/* Dateiverwaltung - File management in your browser Copyright (C) 2017 Ulrich Hilger, http://uhilger.de This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ package de.uhilger.filecms.api; import de.uhilger.filecms.data.FileRef; import de.uhilger.filecms.pub.AbstractComparator; import de.uhilger.filecms.pub.FileNameComparator; import de.uhilger.filecms.pub.ImgFileFilter; import de.uhilger.wbx.Bild; import de.uhilger.wbx.WbxUtils; import static de.uhilger.wbx.WbxUtils.EMPTY_STRING; import static de.uhilger.wbx.WbxUtils.WBX_FILE_BASE; import de.uhilger.wbx.data.Inhalt; import de.uhilger.wbx.web.TNServlet; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.Adler32; import java.util.zip.CheckedOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import javax.servlet.http.HttpServletRequest; import net.coobird.thumbnailator.Thumbnails; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.apache.commons.lang.StringEscapeUtils; /** * Methoden zur Verwaltung von Dateien */ public class FileMgr extends Api { private static final Logger logger = Logger.getLogger(FileMgr.class.getName()); public static final int OP_COPY = 1; public static final int OP_MOVE = 2; public static final String DOT = "."; public String hallo() { return "Hallo Welt!"; } /** * Inhalte der WebBox listen. Hier wird nur der relative Pfad * ausgehend von www oder home ausgegeben sowie zudem ausgehend * von $daten und $basis, sofern der Benutzer die Rolle wbxAdmin hat. * * Andere Inhalte werden nicht ausgegeben. * * @param relPath * @return */ public List list(String relPath) { return listInt(relPath, "name", AbstractComparator.ORDER_ASC); } public List listOrdered(String relPath, String orderBy, String order) { return listInt(relPath, orderBy, order); } /** * Inhalte der WebBox listen. Hier wird nur der relative Pfad * ausgehend von www oder home ausgegeben sowie zudem ausgehend * von $daten und $basis, sofern der Benutzer die Rolle wbxAdmin hat. * * Andere Inhalte werden nicht ausgegeben. * * @param relPath * @param orderBy 'name' * @param order AbstractComparator.ORDER_ASC oder AbstractComparator.ORDER_DESC * @return */ private List listInt(String relPath, String orderBy, String order) { Bild bild = new Bild(); List files = new ArrayList(); if (!relPath.startsWith(".") && !relPath.contains("WEB-INF") && !relPath.contains("META-INF")) { if (relPath.length() == 0) { FileRef namedPublicFolder = new FileRef(PUB_DIR_NAME, true); logger.finer(namedPublicFolder.getAbsolutePath()); FileRef namedHomeFolder = new FileRef(HOME_DIR_NAME, true); logger.finer(namedHomeFolder.getAbsolutePath()); FileRef namedDavFolder = new FileRef(DAV_DIR_NAME, true); logger.finer(namedDavFolder.getAbsolutePath()); files = new ArrayList(); files.add(namedHomeFolder); files.add(namedPublicFolder); files.add(namedDavFolder); if (getRequest().isUserInRole(WBX_ADMIN_ROLE)) { FileRef namedBaseFolder = new FileRef(WBX_BASE, true); FileRef namedDataFolder = new FileRef(WBX_DATA, true); files.add(namedBaseFolder); files.add(namedDataFolder); } } else { String path = getTargetDir(relPath).getAbsolutePath(); logger.fine("listing path: " + path); File dir = new File(path); if (dir.exists()) { File[] fileArray = dir.listFiles(new ImgFileFilter()); if (orderBy != null && orderBy.equalsIgnoreCase("name")) { Arrays.sort(fileArray, new FileNameComparator(order)); } else { Arrays.sort(fileArray, new FileNameComparator(AbstractComparator.ORDER_ASC)); } for (int i = 0; i < fileArray.length; i++) { logger.fine(fileArray[i].toURI().toString()); String fname = fileArray[i].toURI().toString().replace("file:/", ""); if (fileArray[i].isDirectory()) { fname = fname.substring(0, fname.length() - 1); } logger.fine(fname); if(!fname.contains("WEB-INF") && !fname.contains("META-INF")) { long fLen = fileArray[i].length(); long lastMod = fileArray[i].lastModified(); FileRef ref = new FileRef(fname, fileArray[i].isDirectory(), fileArray[i].isHidden(), lastMod, fLen); ref.setMimetype(bild.getMimeType(fileArray[i])); files.add(ref); } } } } } return files; } public List collect(String relativePath, int maxTiefe, int maxAnzahl, int len) { WbxUtils wu = new WbxUtils(); HttpServletRequest req = getRequest(); String requestUrl = req.getRequestURL().toString(); String contextPath = req.getContextPath(); return wu.collectFiles(requestUrl, contextPath, relativePath, maxTiefe, maxAnzahl, len); } public FileRef newFolder(String relPath, String folderName) { if (!relPath.startsWith(".")) { logger.finer(relPath); String targetPath = null; if(relPath.startsWith(PUB_DIR_NAME)) { targetPath = PUB_DIR_PATH + getUserName() + "/" + relPath.substring(PUB_DIR_NAME.length()) + "/" + folderName; } else if(relPath.startsWith(HOME_DIR_NAME)) { targetPath = HOME_DIR_PATH + getUserName() + "/" + relPath.substring(HOME_DIR_NAME.length()) + "/" + folderName; } else { // kann eigentlich nicht sein.. } logger.finer(targetPath); File targetDir = new File(getBase().getAbsolutePath(), targetPath); targetDir.mkdirs(); return new FileRef(targetDir.getAbsolutePath(), true); } else { return null; } } public String getCode(String relPath, String fileName) { String code = null; if (!relPath.startsWith(".")) { Object p = getRequest().getUserPrincipal(); if (p instanceof Principal) { Reader reader = null; try { File targetFile = new File(getTargetDir(relPath), fileName); reader = new InputStreamReader(new FileInputStream(targetFile), Charset.forName("UTF-8").newDecoder()); //reader = new FileReader(targetFile); StringBuffer buf = new StringBuffer(); char[] readBuffer = new char[1024]; int charsRead = reader.read(readBuffer); while (charsRead > -1) { buf.append(readBuffer, 0, charsRead); charsRead = reader.read(readBuffer); } code = buf.toString(); } catch (FileNotFoundException ex) { Logger.getLogger(FileMgr.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(FileMgr.class.getName()).log(Level.SEVERE, null, ex); } finally { try { reader.close(); } catch (IOException ex) { Logger.getLogger(FileMgr.class.getName()).log(Level.SEVERE, null, ex); } } } } return code; } public String renameFile(String relPath, String fname, String newName) { if (!relPath.startsWith(".")) { File targetDir = getTargetDir(relPath); File file = new File(targetDir, fname); if(fname.endsWith(TNServlet.JPEG) || fname.endsWith(TNServlet.JPG) || fname.endsWith(TNServlet.PNG)) { renameImgFiles(targetDir, file, newName); } else { file.renameTo(new File(targetDir, newName)); } return fname + " umbenannt zu " + newName; } else { return "Pfad nicht erlaubt."; } } public void renameImgFiles(File targetDir, File targetFile, String newName) { String alt; String neu; int newdotpos = newName.lastIndexOf(DOT); String newfname = newName.substring(0, newdotpos); String newext = newName.substring(newdotpos); logger.fine("newfname: " + newfname + ", newext: " + newext); String fnameext = targetFile.getName(); int dotpos = fnameext.lastIndexOf(DOT); String fname = fnameext.substring(0, dotpos); String ext = fnameext.substring(dotpos); logger.fine("fname: " + fname + ", ext: " + ext); FileFilter fileFilter = new WildcardFileFilter(fname + "*" + ext); File[] files = targetDir.listFiles(fileFilter); for (int i = 0; i < files.length; i++) { alt = files[i].getName(); logger.fine("alt: " + alt); if(alt.contains(TNServlet.TN)) { neu = newfname + TNServlet.TN + newext; } else if (alt.contains(TNServlet.KL)) { neu = newfname + TNServlet.KL + newext; } else if(alt.contains(TNServlet.GR)) { neu = newfname + TNServlet.GR + newext; } else if(alt.contains(TNServlet.MT)) { neu = newfname + TNServlet.MT + newext; } else if(alt.contains(TNServlet.SM)) { neu = newfname + TNServlet.SM + newext; } else { neu = newName; } files[i].renameTo(new File(targetDir, neu)); logger.fine("neu: " + neu); } } public String deleteFiles(String relPath, List fileNames) { String result = null; try { logger.fine(fileNames.toString()); if (!relPath.startsWith(DOT)) { File targetDir = getTargetDir(relPath); for(int i=0; i < fileNames.size(); i++) { Object o = fileNames.get(i); if(o instanceof ArrayList) { ArrayList al = (ArrayList) o; logger.fine(al.get(0).toString()); File targetFile = new File(targetDir, al.get(0).toString()); logger.fine(targetFile.getAbsolutePath()); if(targetFile.isDirectory()) { FileUtils.deleteDirectory(targetFile); } else { /* Wenn targetFile mit jpg, jpeg oder png endet, muss eine Unterfunktion eine Liste aller Dateien bilden, die so heissen, also z.B. alle [Dateiname]*.jpg */ String fname = targetFile.getName().toLowerCase(); if(fname.endsWith(TNServlet.JPEG) || fname.endsWith(TNServlet.JPG) || fname.endsWith(TNServlet.PNG)) { deleteImgFiles(targetDir, targetFile); } else { targetFile.delete(); } } } } result = "deleted"; } } catch (Throwable ex) { logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } return result; } public void deleteImgFiles(File targetDir, File targetFile) { String fnameext = targetFile.getName(); int dotpos = fnameext.lastIndexOf(DOT); String fname = fnameext.substring(0, dotpos); String ext = fnameext.substring(dotpos); logger.fine("fname: " + fname + ", ext: " + ext); FileFilter fileFilter = new WildcardFileFilter(fname + "*" + ext); File[] files = targetDir.listFiles(fileFilter); for (int i = 0; i < files.length; i++) { logger.fine(files[i].getName()); files[i].delete(); } } public String copyFiles(String fromPath, String toPath, List fileNames) { return copyOrMoveFiles(fromPath, toPath, fileNames, OP_COPY); } public String moveFiles(String fromPath, String toPath, List fileNames) { return copyOrMoveFiles(fromPath, toPath, fileNames, OP_MOVE); } public String duplicateFile(String fromPath, String fileName) { String result = null; try { File srcDir = getTargetDir(fromPath); File srcFile = new File(srcDir, fileName); String fnameext = srcFile.getName(); int dotpos = fnameext.lastIndexOf(DOT); String fname = fnameext.substring(0, dotpos); String ext = fnameext.substring(dotpos); File destFile = new File(srcDir, fname + "-Kopie" + ext); int i = 1; while(destFile.exists()) { destFile = new File(srcDir, fname + "-Kopie-" + Integer.toString(++i) + ext); } FileUtils.copyFile(srcFile, destFile); result = destFile.getName(); } catch (IOException ex) { logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } return result; } private String copyOrMoveFiles(String fromPath, String toPath, List fileNames, int operation) { String result = null; try { if (!fromPath.startsWith(".")) { File srcDir = getTargetDir(fromPath); File targetDir = getTargetDir(toPath); Iterator i = fileNames.iterator(); while(i.hasNext()) { Object o = i.next(); if (o instanceof ArrayList) { ArrayList al = (ArrayList) o; File srcFile = new File(srcDir, al.get(0).toString()); if(srcFile.isDirectory()) { if(operation == OP_MOVE) { FileUtils.moveDirectoryToDirectory(srcFile, targetDir, false); } else { FileUtils.copyDirectoryToDirectory(srcFile, targetDir); } } else { if(operation == OP_MOVE) { String fname = srcFile.getName().toLowerCase(); if(fname.endsWith(TNServlet.JPEG) || fname.endsWith(TNServlet.JPG) || fname.endsWith(TNServlet.PNG)) { moveImgFilesToDirectory(srcFile, srcDir, targetDir, false); } else { FileUtils.moveFileToDirectory(srcFile, targetDir, false); } } else { FileUtils.copyFileToDirectory(srcFile, targetDir); } } } } } } catch (IOException ex) { logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } return result; } private void moveImgFilesToDirectory(File srcFile, File srcDir, File targetDir, boolean createDestDir) throws IOException { String fnameext = srcFile.getName(); int dotpos = fnameext.lastIndexOf(DOT); String fname = fnameext.substring(0, dotpos); String ext = fnameext.substring(dotpos); logger.fine("fname: " + fname + ", ext: " + ext); FileFilter fileFilter = new WildcardFileFilter(fname + "*" + ext); File[] files = srcDir.listFiles(fileFilter); for (int i = 0; i < files.length; i++) { logger.fine(files[i].getName()); FileUtils.moveFileToDirectory(files[i], targetDir, createDestDir); } } public FileRef saveTextFileAs(String relPath, String fileName, String contents) { FileRef savedFile = null; logger.fine(relPath + " " + fileName); if (!relPath.startsWith(".")) { //FileRef datenRef = getBase(); Object p = getRequest().getUserPrincipal(); if(p instanceof Principal) { File targetFile = new File(getTargetDir(relPath), fileName); if(targetFile.exists()) { targetFile = getNewFileName(targetFile); } else { targetFile.getParentFile().mkdirs(); } saveToFile(targetFile, contents); } } return savedFile; } private File getNewFileName(File file) { File dir = file.getParentFile(); String targetName = file.getName(); logger.fine("targetName: " + targetName); String ext = ""; int dotpos = targetName.indexOf("."); if(dotpos > -1) { ext = targetName.substring(dotpos); targetName = targetName.substring(0, dotpos); } logger.fine("targetName: " + targetName + ", ext: " + ext); int i = 1; while(file.exists()) { StringBuffer buf = new StringBuffer(); buf.append(targetName); buf.append("-"); buf.append(i); if(ext.length() > 0) { buf.append(ext); } file = new File(dir, buf.toString()); i++; } logger.fine("new file: " + file.getName()); return file; } private FileRef saveToFile(File targetFile, String contents) { FileRef savedFile = null; try { targetFile.createNewFile(); //FileWriter w = new FileWriter(targetFile, Charset.forName("UTF-8")); //w.write(StringEscapeUtils.unescapeHtml(contents)); Writer w = new OutputStreamWriter(new FileOutputStream(targetFile), "UTF-8"); //w.write(contents); w.write(StringEscapeUtils.unescapeHtml(contents)); w.flush(); w.close(); savedFile = new FileRef( targetFile.getAbsolutePath(), targetFile.isDirectory(), targetFile.isHidden(), targetFile.lastModified(), targetFile.length()); } catch (IOException ex) { logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } return savedFile; } public FileRef saveTextFile(String relPath, String fileName, String contents) { FileRef savedFile = null; logger.fine(relPath + " " + fileName); if (!relPath.startsWith(".")) { //FileRef datenRef = getBase(); Object p = getRequest().getUserPrincipal(); if(p instanceof Principal) { File targetFile = new File(getTargetDir(relPath), fileName); if(targetFile.exists()) { /* muss delete() sein? pruefen: ueberschreibt der FileWriter den alteen Inhalt oder entsteht eine unerwuenschte Mischung aus altem und neuem Inhalt? */ targetFile.delete(); } else { targetFile.getParentFile().mkdirs(); } saveToFile(targetFile, contents); } } return savedFile; } public String bildVerkleinern(String relPath, String bildName) { if (!relPath.startsWith(".")) { File dir = getTargetDir(relPath); File original = new File(dir, bildName); Bild bild = new Bild(); //for (int i = 0; i < Bild.GR.length; i++) { //int gr = bild.getVariantenGroesse(i); String ext = ""; String nurname = bildName; int dotpos = bildName.indexOf("."); if (dotpos > -1) { ext = bildName.substring(dotpos); nurname = bildName.substring(0, dotpos); } // 120, 240, 500, 700, 1200 for (int i = 0; i < Bild.GR.length; i++) { StringBuffer buf = new StringBuffer(); buf.append(nurname); buf.append(bild.getVariantenName(i)); buf.append(ext); File newImgFile = new File(dir, buf.toString()); try { Thumbnails.of(original) .size(bild.getVariantenGroesse(i), bild.getVariantenGroesse(i)) .keepAspectRatio(true) .outputQuality(0.7) .toFile(newImgFile); } catch (IOException ex) { logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } } return "ok"; } else { return "Pfad micht erlaubt."; } } public String bildRotieren(String relPath, String bildName) { if (!relPath.startsWith(".")) { File dir = getTargetDir(relPath); File original = new File(dir, bildName); String ext = ""; String nurname = bildName; int dotpos = bildName.indexOf("."); if (dotpos > -1) { ext = bildName.substring(dotpos); nurname = bildName.substring(0, dotpos); } StringBuffer buf = new StringBuffer(); buf.append(nurname); buf.append("-rot"); buf.append(ext); File newImgFile = new File(dir, buf.toString()); logger.fine("original: " + original.getAbsolutePath() + " newImgFile: " + newImgFile.getAbsolutePath()); try { Thumbnails.of(original) .scale(1) .rotate(90) .toFile(newImgFile); } catch (IOException ex) { logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } return "ok"; } else { return "Pfad micht erlaubt."; } } /* --------- ZIP entpacken ---------------- */ public String extractZipfile(String relPath, String filename) { String result = null; if (!relPath.startsWith(".")) { try { File targetDir = getTargetDir(relPath); File archive = new File(targetDir, filename); if(extract(archive)) { result = "ok"; } else { result = "error while extracting"; } } catch(Exception ex) { result = ex.getLocalizedMessage(); logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex); } } return result; } /** * extract a given ZIP archive to the folder respective archive resides in * @param archive the archive to extract * @throws Exception */ private boolean extract(File archive) throws Exception { ZipFile zipfile = new ZipFile(archive); Enumeration en = zipfile.entries(); while(en.hasMoreElements()) { ZipEntry zipentry = (ZipEntry) en.nextElement(); unzip(zipfile, zipentry, archive.getParent()); } zipfile.close(); return true; } /** * unzip a given entry of a given zip file to a given location * @param zipfile the zip file to read an entry from * @param zipentry the zip entry to read * @param destPath the path to the destination location for the extracted content * @throws IOException */ private void unzip(ZipFile zipfile, ZipEntry zipentry, String destPath) throws IOException { byte buf[] = new byte[1024]; InputStream is = zipfile.getInputStream(zipentry); String outFileName = destPath + File.separator + zipentry.getName(); File file = new File(outFileName); if(!zipentry.isDirectory()) { file.getParentFile().mkdirs(); if(!file.exists()) file.createNewFile(); FileOutputStream fos = new FileOutputStream(file); int i = is.read(buf, 0, 1024); while(i > -1) { fos.write(buf, 0, i); i = is.read(buf, 0, 1024); } fos.close(); is.close(); } else { file.mkdirs(); } } /* ------------- Ornder als ZIP packen --------------- */ public String packFolder(String relPath) { if (!relPath.startsWith(".")) { try { File targetDir = getTargetDir(relPath); File parentDir = targetDir.getParentFile(); StringBuffer fname = new StringBuffer(); fname.append(targetDir.getName()); fname.append(".zip"); File archiveFile = new File(parentDir, fname.toString()); FileRef folderToPack = new FileRef(targetDir.getAbsolutePath()); FileRef archive = new FileRef(archiveFile.getAbsolutePath()); pack(folderToPack, archive); return "ok"; } catch(Exception ex) { String result = ex.getLocalizedMessage(); logger.log(Level.SEVERE, result, ex); return result; } } else { return "Falsche relative Pfadangabe"; } } /** * pack the contents of a given folder into a new ZIP compressed archive * @param folder the folder to pack * @param archive the archive to create from the given files * @throws Exception */ private boolean pack(FileRef folder, FileRef archive) throws Exception { File file = new File(archive.getAbsolutePath()); FileOutputStream fos = new FileOutputStream(file); CheckedOutputStream checksum = new CheckedOutputStream(fos, new Adler32()); ZipOutputStream zos = new ZipOutputStream(checksum); pack(zos, folder.getAbsolutePath(), ""); zos.flush(); zos.finish(); zos.close(); fos.flush(); fos.close(); return true; } /** * go through the given file structure recursively * @param zipFile the ZIP file to write to * @param srcDir the directory to pack during this cycle * @param subDir the subdirectory to append to names of file entries inside the archive * @throws IOException */ private void pack(ZipOutputStream zipFile, String srcDir, String subDir) throws IOException { File[] files = new File(srcDir).listFiles(); for(int i = 0; i < files.length; i++) { if(files[i].isDirectory()) { pack(zipFile, files[i].getAbsolutePath(), subDir + File.separator + files[i].getName()); } else { packFile(zipFile, subDir, files[i]); } } } /** * pack a given file * @param zipFile the ZIP archive to pack to * @param dir the directory name to append to name of file entry inside archive * @param file the file to pack * @throws IOException */ private void packFile(ZipOutputStream zipFile, String dir, File file) throws IOException { FileInputStream fileinputstream = new FileInputStream(file); byte buf[] = new byte[fileinputstream.available()]; fileinputstream.read(buf); String dirWithSlashes = dir.replace('\\', '/'); //System.out.println("zipping " + dirWithSlashes + "/" + file.getName()); ZipEntry ze = new ZipEntry(dirWithSlashes + "/" + file.getName()); ze.setMethod(ZipEntry.DEFLATED); zipFile.putNextEntry(ze); zipFile.write(buf, 0, buf.length); zipFile.closeEntry(); fileinputstream.close(); } /* ---- Hilfsfunktionen ---- */ }