/* * Constellation - An open source and standard compliant SDI * http://www.constellation-sdi.org * * Copyright 2014 Geomatys. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.constellation.webdav; import com.bradmcevoy.http.Auth; import com.bradmcevoy.http.CollectionResource; import com.bradmcevoy.http.CopyableResource; import com.bradmcevoy.http.DeletableResource; import com.bradmcevoy.http.GetableResource; import com.bradmcevoy.http.HttpManager; import com.bradmcevoy.http.LockInfo; import com.bradmcevoy.http.LockResult; import com.bradmcevoy.http.LockTimeout; import com.bradmcevoy.http.LockToken; import com.bradmcevoy.http.LockingCollectionResource; import com.bradmcevoy.http.MakeCollectionableResource; import com.bradmcevoy.http.MoveableResource; import com.bradmcevoy.http.PropFindableResource; import com.bradmcevoy.http.PutableResource; import com.bradmcevoy.http.Range; import com.bradmcevoy.http.Request; import com.bradmcevoy.http.Resource; import com.bradmcevoy.http.XmlWriter; import com.bradmcevoy.http.exceptions.NotAuthorizedException; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.constellation.configuration.WebdavContext; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; /** * Represents a directory in a physical file system. * */ public class FsDirectoryResource extends FsResource implements MakeCollectionableResource, PutableResource, CopyableResource, DeletableResource, MoveableResource, PropFindableResource, LockingCollectionResource, GetableResource { private static final DateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy hh:mm"); private final String defaultPage; private final File root; private final String contextPath; private final boolean hideDotFile; private final String id; public FsDirectoryResource(final String host, final File dir, final WebdavContext context) { super(host, dir, context); this.defaultPage = context.getDefaultPage(); this.root = context.getRootFile(); this.contextPath = context.getContextPath(); this.hideDotFile = context.isHideDotFile(); this.id = context.getId(); if (!dir.exists()) { throw new IllegalArgumentException("Directory does not exist: " + dir.getAbsolutePath()); } if (!dir.isDirectory()) { throw new IllegalArgumentException("Is not a directory: " + dir.getAbsolutePath()); } } @Override public CollectionResource createCollection(final String name) { final File fnew = new File(file, name); if (!fnew.mkdir()) { throw new RuntimeException("Failed to create: " + fnew.getAbsolutePath()); } return new FsDirectoryResource(host, fnew, context); } @Override public Resource child(final String name) { final File fchild = new File(file, name); return resolveFile(this.host, fchild); } @Override public List<? extends Resource> getChildren() { final List<FsResource> list = new ArrayList<FsResource>(); for (File fchild : this.file.listFiles()) { final FsResource res = resolveFile(this.host, fchild); if (res != null) { list.add(res); } else { LOGGER.log(Level.SEVERE, "Couldnt resolve file {}", fchild.getAbsolutePath()); } } return list; } /** * Will redirect if a default page has been specified on the factory * * @param request * @return */ @Override public String checkRedirect(final Request request) { if (defaultPage != null) { return request.getAbsoluteUrl() + "/" + defaultPage; } else { return null; } } @Override public Resource createNew(final String name, final InputStream in, final Long length, final String contentType) throws IOException { final File dest = new File(this.getFile(), name); FileOutputStream out = null; try { out = new FileOutputStream(dest); IOUtils.copy(in, out); } finally { IOUtils.closeQuietly(out); } // todo: ignores contentType return resolveFile(this.host, dest); } @Override protected void doCopy(final File dest) { try { FileUtils.copyDirectory(this.getFile(), dest); } catch (IOException ex) { throw new RuntimeException("Failed to copy to:" + dest.getAbsolutePath(), ex); } } @Override public LockToken createAndLock(final String name, final LockTimeout timeout, final LockInfo lockInfo) throws NotAuthorizedException { final File dest = new File(this.getFile(), name); createEmptyFile(dest); final FsFileResource newRes = new FsFileResource(host, dest, context); final LockResult res = newRes.lock(timeout, lockInfo); return res.getLockToken(); } private void createEmptyFile(File file) { FileOutputStream fout = null; try { fout = new FileOutputStream(file); } catch (IOException e) { throw new RuntimeException(e); } finally { IOUtils.closeQuietly(fout); } } /** * Will generate a listing of the contents of this directory, unless the * factory's allowDirectoryBrowsing has been set to false. * * If so it will just output a message saying that access has been disabled. * * @param out * @param range * @param params * @param contentType * @throws IOException * @throws NotAuthorizedException */ @Override public void sendContent(OutputStream out, Range range, Map<String, String> params, String contentType) throws IOException, NotAuthorizedException { final File currentFile = getFile(); final String subpath = currentFile.getCanonicalPath().substring(root.getCanonicalPath().length()).replace('\\', '/'); final String baseURL = getURL(); //String uri = "/" + factory.getContextPath() + subpath; XmlWriter w = new XmlWriter(out); w.open("html"); w.open("head"); w.open("title"); w.writeText("Constellation webdav repository"); w.close("title"); w.close("head"); w.open("body"); w.begin("h1").open().writeText("Constellation webdav repository").close(); w.open("table"); //<tr><th><img src="/icons/blank.gif" alt="[ICO]"></th><th><a href="?C=N;O=D">Name</a></th><th><a href="?C=M;O=A">Last modified</a></th></tr> w.open("tr"); w.open("th"); w.begin("img").writeAtt("src", baseURL + "/icons/blank.gif").writeAtt("alt", "[ICO]").close(); w.close("th"); w.open("th"); w.begin("a").writeAtt("href", "#").open(); //?C=N;O=D w.writeText("Name"); w.close("a"); w.close("th"); w.open("th"); w.begin("a").writeAtt("href", "#").open(); //?C=M;O=A w.writeText("Last modified"); w.close("a"); w.close("th"); w.close("tr"); // separator <tr><th colspan="5"><hr></th></tr> w.open("tr"); w.begin("th").writeAtt("colspan", "5").open(); w.begin("hr").open().close(); w.close("th"); w.close("tr"); //if not root we write the ".." link if (!root.getPath().equals(currentFile.getPath())) { w.open("tr"); w.begin("td").writeAtt("valign", "top").open(); w.begin("img").writeAtt("src", baseURL + "/icons/folder.gif").writeAtt("alt", "[DIR]").close(); w.close("td"); w.open("td"); final String path = buildHref(subpath, null); w.begin("a").writeAtt("href", path).open().writeText("Parent Directory").close(); w.close("td"); w.begin("td").open().writeText("-").close(); w.close("tr"); } for (Resource r : getChildren()) { if (!r.getName().startsWith(".") || !hideDotFile) { w.open("tr"); // icons <td valign="top"><img src="/icons/folder.gif" alt="[DIR]"></td> if (r instanceof FsDirectoryResource) { w.begin("td").writeAtt("valign", "top").open(); w.begin("img").writeAtt("src", baseURL + "/icons/folder.gif").writeAtt("alt", "[DIR]").close(); w.close("td"); } else { w.begin("td").writeAtt("valign", "top").open(); w.begin("img").writeAtt("src", baseURL + "/icons/unknown.gif").writeAtt("alt", " ").close(); w.close("td"); } w.open("td"); final String path = buildHref(subpath, r.getName()); w.begin("a").writeAtt("href", path).open().writeText(r.getName()).close(); w.close("td"); w.begin("td").open().writeText(formatter.format(r.getModifiedDate())).close(); w.close("tr"); } } // separator <tr><th colspan="5"><hr></th></tr> w.open("tr"); w.begin("th").writeAtt("colspan", "5").open(); w.begin("hr").open().close(); w.close("th"); w.close("tr"); w.close("table"); w.close("body"); w.close("html"); w.flush(); } @Override public Long getMaxAgeSeconds(final Auth auth) { return null; } @Override public String getContentType(final String accepts) { return "text/html"; } @Override public Long getContentLength() { return null; } private String getURL() { final String abUrl = HttpManager.request().getAbsoluteUrl(); final int i = abUrl.indexOf(contextPath); if (i != -1) { return abUrl.substring(0, i); } throw new IllegalArgumentException("Error in getURL"); } private String buildHref(final String subPath, final String name) { final String abUrl = getURL() + contextPath + '/'+ id + subPath; if (ssoPrefix == null) { if (name == null) { final String s = abUrl.substring(0, abUrl.lastIndexOf(getName())); return s; } else { return abUrl + '/' + name; } } else { // This is to match up with the prefix set on SimpleSSOSessionProvider in MyCompanyDavServlet String s = insertSsoPrefix(abUrl, ssoPrefix); return s += name; } } public static String insertSsoPrefix(final String abUrl, final String prefix) { // need to insert the ssoPrefix immediately after the host and port int pos = abUrl.indexOf("/", 8); String s = abUrl.substring(0, pos) + "/" + prefix; s += abUrl.substring(pos); return s; } public FsResource resolveFile(String host, File file) { FsResource r; if (!file.exists()) { LOGGER.log(Level.INFO, "file not found: {0}", file.getAbsolutePath()); return null; } else if (file.isDirectory()) { r = new FsDirectoryResource(host, file, context); } else { r = new FsFileResource(host, file, context); } return r; } }