IwcmFsVolume.java

package sk.iway.iwcm.system.elfinder;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import cn.bluejoe.elfinder.controller.executor.FsItemEx;
import cn.bluejoe.elfinder.service.FsItem;
import cn.bluejoe.elfinder.service.FsVolume;
import cn.bluejoe.elfinder.util.MimeTypesUtils;
import sk.iway.iwcm.Adminlog;
import sk.iway.iwcm.Constants;
import sk.iway.iwcm.DB;
import sk.iway.iwcm.FileTools;
import sk.iway.iwcm.Identity;
import sk.iway.iwcm.InitServlet;
import sk.iway.iwcm.Logger;
import sk.iway.iwcm.Tools;
import sk.iway.iwcm.common.AdminTools;
import sk.iway.iwcm.common.DocTools;
import sk.iway.iwcm.common.FileBrowserTools;
import sk.iway.iwcm.common.FileIndexerTools;
import sk.iway.iwcm.io.IwcmFile;
import sk.iway.iwcm.io.IwcmFileFilter;
import sk.iway.iwcm.io.IwcmFsDB;
import sk.iway.iwcm.io.IwcmInputStream;
import sk.iway.iwcm.io.IwcmOutputStream;
import sk.iway.iwcm.users.UsersDB;

/**
 *  IwcmFsVolume.java - volume objekt pre elFinder (cn.bluejoe.elfinder)
 *
 *@Title        webjet8
 *@Company      Interway s.r.o. (www.interway.sk)
 *@Copyright    Interway s.r.o. (c) 2001-2015
 *@author       $Author: jeeff jeeff $
 *@version      $Revision: 1.3 $
 *@created      Date: 19.2.2015 19:09:46
 *@modified     $Date: 2004/08/16 06:26:11 $
 */
public class IwcmFsVolume implements FsVolume
{
	public static final String VOLUME_ID_ACTUAL_PAGE = "iwcm_fs_ap_volume";

	protected String _name; //NOSONAR

	protected IwcmFile _rootDir; //NOSONAR

	public IwcmFsVolume(String name, String rootUrl)
	{
		this._name = name;
		this._rootDir = new IwcmFile(Tools.getRealPath(rootUrl));
	}

	public IwcmFsVolume(String name, IwcmFile rootDir)
	{
		this._name = name;
		this._rootDir = rootDir;
	}

	protected IwcmFile asFile(FsItem fsi)
	{
		return ((IwcmFsItem) fsi).getFile();
	}

	@Override
	public void createFile(FsItem fsi) throws IOException
	{
		IwcmFile file = asFile(fsi);

		if (canWrite(file.getParentFile()))
		{
			if (file.getParentFile().exists() == false) file.getParentFile().mkdirs();
			file.createNewFile();

			//kvoli FSDB musime do suboru nieco zapisat
			byte[] buf = new byte[0];
			ByteArrayInputStream bis = new ByteArrayInputStream(buf);
			IwcmFsDB.writeFiletoDest(bis, new File(file.getAbsolutePath()), 0, true);

			Adminlog.add(Adminlog.TYPE_FILE_CREATE, "elfinder createFile, path=" + file.getVirtualPath(), -1, -1);
		}
	}

	@Override
	public void createFolder(FsItem fsi) throws IOException
	{
		IwcmFsItem fsii = (IwcmFsItem) fsi;
		IwcmFile f = asFile(fsi);

		if (canWrite(f))
		{
			//odstran diakritiku
			if (f.getVirtualPath().startsWith("/files") || f.getVirtualPath().startsWith("/images"))
			{
				String newDir = DB.internationalToEnglish(DocTools.removeCharsDir(f.getName(), true).toLowerCase());
				IwcmFile f2 = new IwcmFile(f.getParentFile(), newDir);
				fsii.setFile(f2);
			}

			fsii.getFile().mkdirs();

			Adminlog.add(Adminlog.TYPE_FILE_CREATE, "elfinder createFolder, path=" + fsii.getFile().getVirtualPath(), -1, -1);
		}
	}

	@Override
	public boolean deleteFile(FsItem fsi) throws IOException
	{
		boolean deleted = false;
		IwcmFile file = asFile(fsi);
		if (!file.isDirectory() && canWrite(file))
		{
			deleted = file.delete();
			//zmaz z indexu (ak existuje)
			String virtualPath = file.getVirtualPath();
			if (virtualPath.startsWith("/files"))
			{
				//vymazanie full text indexu
				FileIndexerTools.deleteIndexedFile(virtualPath);
			}

			Adminlog.add(Adminlog.TYPE_FILE_DELETE, "elfinder deleteFile, path="+virtualPath, -1, -1);
		}
		return deleted;
	}

	@Override
	public boolean deleteFolder(FsItem fsi) throws IOException
	{
		boolean deleted = false;

		Identity user = sk.iway.iwcm.system.elfinder.FsService.getCurrentUser();
		IwcmFile file = asFile(fsi);
		if (user == null || user.isDisabledItem("fbrowser_delete_directory")) {
			//tempfolder vznika pri zipovani suborov, ten je teda povolene vzdy zmazat bez ohladu na prava
			if (file.getName().contains("tempfolder")==false) {
				return false;
			}
		}

		//adresar / nedovolime zmazat, to je nezmysel zmazat cely web
		if (file.isDirectory() && canWrite(file) && "/".equals(file.getVirtualPath())==false)
		{
			//JEEFF FileUtils.deleteDirectory(file);
			deleted = FileTools.deleteDirTree(file);
			Adminlog.add(Adminlog.TYPE_FILE_DELETE, "elfinder deleteFolder, path="+file.getVirtualPath(), -1, -1);
		}

		return deleted;
	}

	private boolean canWrite(IwcmFile file)
	{
		Identity user = sk.iway.iwcm.system.elfinder.FsService.getCurrentUser();
		if (user == null) return false;

		return UsersDB.isFolderWritable(user.getWritableFolders(), file.getVirtualPath());
	}

	@Override
	public boolean exists(FsItem newFile)
	{
		return asFile(newFile).exists();
	}

	protected IwcmFsItem fromFile(IwcmFile file)
	{
		file = new IwcmFile(Tools.getRealPath(file.getVirtualPath()));

		return new IwcmFsItem(this, file);
	}

	@Override
	public FsItem fromPath(String relativePath)
	{
		return fromFile(new IwcmFile(_rootDir, relativePath));
	}

	@Override
	public String getDimensions(FsItem fsi)
	{
		return null;
	}

	@Override
	public long getLastModified(FsItem fsi)
	{
		if(!Constants.getBoolean("elfinderUseFastLoading"))
		{
			long lastModified = asFile(fsi).lastModified();
			//prepocet na JavaScript timestamp
			lastModified = Math.round((double)lastModified / 1000);
			return lastModified;
		}
		return 0;
	}

	@Override
	public String getMimeType(FsItem fsi)
	{
		IwcmFile file = asFile(fsi);
		if (file.isDirectory())
			return "directory";

		//JEEFF String ext = FileUtils.getExtension(file.getName());
		String ext = FileTools.getFileExtension(file.getName());
		if (ext != null && !ext.isEmpty())
		{
			String mimeType = MimeTypesUtils.getMimeType(ext);
			return mimeType == null ? MimeTypesUtils.UNKNOWN_MIME_TYPE : mimeType;
		}

		return MimeTypesUtils.UNKNOWN_MIME_TYPE;
	}

	public String getName()
	{
		return _name;
	}

	@Override
	public String getName(FsItem fsi)
	{
		IwcmFile fsiFile = asFile(fsi);
		return fsiFile.getName();
	}

	@Override
	public FsItem getParent(FsItem fsi)
	{
		IwcmFile fsiFile = asFile(fsi);
		if ("images".equals(fsiFile.getName()))
		{
			//Logger.debug(IwcmFsVolume.class, "Som images");
		}
		IwcmFile parentFile = fsiFile.getParentFile();
		String virtualPath = parentFile.getVirtualPath();
		//Logger.debug(IwcmFsVolume.class, "getParent, file=" + fsiFile.getAbsolutePath() + " parent=" + parentFile.getAbsolutePath()+" virtual="+virtualPath);
		if ("/".equals(virtualPath))
		{
			parentFile = new IwcmFile(Tools.getRealPath("/"));
		}

		return fromFile(parentFile);
	}

	@Override
	public String getPath(FsItem fsi) throws IOException
	{
		String relativePath = "";
		if (InitServlet.isTypeCloud() || Constants.getBoolean("enableStaticFilesExternalDir"))
		{
			//pre istotu je to takto lebo az pri cloude sa na to prislo ale takto by to malo fungovat dobre aj pre standardny WJ
			IwcmFile file = asFile(fsi);
			relativePath = file.getVirtualPath();
			if ("/".equals(relativePath)) relativePath = "";
		}
		else
		{
			String fullPath = asFile(fsi).getAbsolutePath();
			//String rootPath = _rootDir.getCanonicalPath();
			//if (fullPath.endsWith(File.separator)==false) fullPath = fullPath+File.separator;

			String rootPath = _rootDir.getAbsolutePath();
			if (rootPath.endsWith("/"))
			{
				rootPath = rootPath.substring(0, rootPath.length()-1);
			}

			try
			{
				relativePath = fullPath.substring(rootPath.length());
			}
			catch (Exception e)
			{
				Logger.debug(IwcmFsVolume.class, "fullPath="+fullPath+" rootPath="+rootPath);
				sk.iway.iwcm.Logger.error(e);
			}
		}

		//nepovolime ../
		if (FileBrowserTools.hasForbiddenSymbol(relativePath))
		{
			Adminlog.add(Adminlog.TYPE_XSS, "Pokus o XSS utok - volanie elfinder nepovolenej cesty - "+relativePath, -1, -1);
			return null;
		}

		return relativePath.replace('\\', '/');
	}

	@Override
	public FsItem getRoot()
	{
		return fromFile(_rootDir);
	}

	public IwcmFile getRootDir()
	{
		return _rootDir;
	}

	@Override
	public long getSize(FsItem fsi)
	{
		if(Constants.getBoolean("elfinderUseFastLoading")) return 0;
		IwcmFile file = asFile(fsi);
		long size = file.getLength();
		if (file.isDirectory())
		{
			size = 0;
			//iterate over files in directory and sum their sizes
			IwcmFile[] files = file.listFiles();
			for (IwcmFile f : files)
			{
				size += f.getLength();
			}
		}
		return size;
	}

	@Override
	public String getThumbnailFileName(FsItem fsi)
	{
		return null;
	}

	@Override
	public boolean hasChildFolder(FsItem fsi)
	{
		if(!Constants.getBoolean("elfinderUseFastLoading"))
		{
			return asFile(fsi).isDirectory() && asFile(fsi).listFiles(new IwcmFileFilter()
			{
				@Override
				public boolean accept(IwcmFile arg0)
				{
					return arg0.isDirectory();
				}
			}).length > 0;
		}
		else
			return true;
	}

	@Override
	public boolean isFolder(FsItem fsi)
	{
		return asFile(fsi).isDirectory();
	}

	@Override
	public boolean isRoot(FsItem fsi)
	{
		//boolean isRoot = (_rootDir == asFile(fsi));
		//Logger.debug(IwcmFsVolume.class, "isRoot: " + _rootDir + " " + asFile(fsi) + " " + asFile(fsi).getVirtualPath() + " is="+isRoot);
		//return isRoot;

		return "/".equals(asFile(fsi).getVirtualPath());
	}

	@Override
	public FsItem[] listChildren(FsItem fsi)
	{
		List<FsItem> list = new ArrayList<>();

		IwcmFile fsiFile = asFile(fsi);

		Logger.debug(IwcmFsVolume.class, "listChildrens, virtualPath="+fsiFile.getVirtualPath()+" realPath="+fsiFile.getAbsolutePath());

		IwcmFile[] cs = fsiFile.listFiles();
		if (cs == null)
		{
			Logger.debug(IwcmFsVolume.class, "listChildren - cs is null: "+cs);
			return new FsItem[0];
		}

		boolean fbrowserShowOnlyWritableFolders = Constants.getBoolean("fbrowserShowOnlyWritableFolders");

		Identity user = sk.iway.iwcm.system.elfinder.FsService.getCurrentUser();
		Logger.debug(IwcmFsVolume.class, "listChildren, user="+user+" cs.length="+cs.length+" fbrowserShowOnlyWritableFolders="+fbrowserShowOnlyWritableFolders);
		if (user!=null)
		{
			String path = fsiFile.getVirtualPath();
			if (path.endsWith("/")==false) path = path+"/"; //NOSONAR

			List<IwcmFile> writableFolders = user.getWritableFoldersList();
			String fbrowserAlwaysShowFolders = Constants.getStringExecuteMacro("fbrowserAlwaysShowFolders");
			if (Tools.isNotEmpty(fbrowserAlwaysShowFolders))
			{
				String[] fbrowserAlwaysShowFoldersList = fbrowserAlwaysShowFolders.split("\n");
				if (fbrowserAlwaysShowFoldersList.length > 0) {
					for (String fbrowserAlwaysShowFolder : fbrowserAlwaysShowFoldersList) {
						writableFolders.add(new IwcmFile(Tools.getRealPath(fbrowserAlwaysShowFolder)));
					}
				}
			}

			for (IwcmFile c : cs)
			{
				if (FileBrowserTools.hasForbiddenSymbol(c.getName())) {
					continue;
				}

				if (fbrowserShowOnlyWritableFolders && !isFolderWritable(writableFolders, c.getVirtualPath())) {
						continue;
				}

				list.add(fromFile(c));
			}
		}

		return list.toArray(new FsItem[0]);
	}

	private boolean isFolderWritable(List<IwcmFile> writableFolders, String path) {
		IwcmFile pathFile = new IwcmFile(Tools.getRealPath(path));
		String pathVirtualPath = pathFile.getVirtualPath();
		if (!pathVirtualPath.endsWith("/") && pathFile.isDirectory()) {
			pathVirtualPath += "/";
		}

		for (IwcmFile folder : writableFolders) {
			IwcmFile actualFolder = folder;
			if (!actualFolder.exists()) {
				actualFolder = new IwcmFile(Tools.replace(folder.getAbsolutePath(), "*", ""));
			}

			if (!actualFolder.exists()) {
				continue;
			}

			String folderVirtualPath = actualFolder.getVirtualPath();
			if (!folderVirtualPath.endsWith("/")) {
				folderVirtualPath += "/";
			}

			if (pathFile.isDirectory() && pathVirtualPath.startsWith(folderVirtualPath) || folderVirtualPath.startsWith(pathVirtualPath)) {
					return true;
			}

			if (pathFile.isFile() && pathVirtualPath.startsWith(folderVirtualPath)) {
					return true;
			}
		}
		return false;
	}

	@Override
	public IwcmInputStream openInputStream(FsItem fsi) throws IOException
	{
		return new IwcmInputStream(asFile(fsi));
	}

	@Override
	public IwcmOutputStream openOutputStream(FsItem fsi) throws IOException
	{
		Adminlog.add(Adminlog.TYPE_FILE_SAVE, "elfinder saveFile, path="+asFile(fsi).getVirtualPath(), -1, -1);

		return new IwcmOutputStream(asFile(fsi));
	}

	@Override
	public void rename(FsItem src, FsItem dst) throws IOException
	{
		asFile(src).renameTo(asFile(dst));
		Adminlog.add(Adminlog.TYPE_FILE_EDIT, "elfinder rename, old="+asFile(src).getVirtualPath()+" new="+asFile(dst).getVirtualPath(), -1, -1);
	}

	public void setName(String name)
	{
		_name = name;
	}

	public void setRootDir(IwcmFile rootDir)
	{
		if (!rootDir.exists())
		{
			rootDir.mkdirs();
		}

		_rootDir = rootDir;
	}

	/**
	 * Vrati URL adresu pre subor
	 * @param item
	 * @return
	 */
	public String getVirtualPath(FsItemEx item) throws IOException
	{
		IwcmFile file = asFile(fromPath(item.getPath()));

		return file.getVirtualPath();
	}

	/**
	 * Ak je pre domenu definovany alias suborov vrati /alias pre jednoduche vlozenie do cesty
	 * @return
	 * @deprecated use AdminTools.getDomainNameFileAliasAppend()
	 */
	@Deprecated
	public static String getDomainNameFileAliasAppend()
	{
		return AdminTools.getDomainNameFileAliasAppend();
	}

	/**
	 * For files/dir in /files and /images directories, remove diacritics and convert to lowercase
	 * @param name
	 * @param fsi
	 * @return
	 * @throws IOException
	 */
	public static String removeSpecialChars(String name, FsItemEx fsi) throws IOException {
		if (fsi.getPath().startsWith("/files") || fsi.getPath().startsWith("/images"))
		{
			name = DB.internationalToEnglish(name);
			name = DocTools.removeCharsDir(name, true).toLowerCase();
		}
		return name;
	}
}