package ch.cyberduck.core.dav; /* * Copyright (c) 2008 David Kocher. All rights reserved. * http://cyberduck.ch/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * Bug fixes, suggestions and comments should be sent to: * dkocher@cyberduck.ch */ import ch.cyberduck.core.*; import ch.cyberduck.core.i18n.Locale; import ch.cyberduck.core.io.service.BandwidthThrottleService; import ch.cyberduck.core.io.IOResumeException; import java.io.FileInputStream; import org.apache.commons.httpclient.methods.InputStreamRequestEntity; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.apache.webdav.lib.WebdavResource; import org.apache.webdav.lib.methods.DepthSupport; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.text.MessageFormat; /** * @version $Id: $ */ public class DAVPath extends Path { private static Logger log = Logger.getLogger(DAVPath.class); static { PathFactory.addFactory(Protocol.WEBDAV, new Factory()); } private static class Factory extends PathFactory<DAVSession> { @Override protected Path create(DAVSession session, String path, int type) { return new DAVPath(session, path, type); } @Override protected Path create(DAVSession session, String parent, String name, int type) { return new DAVPath(session, parent, name, type); } @Override protected Path create(DAVSession session, String path, Local file) { return new DAVPath(session, path, file); } @Override protected <T> Path create(DAVSession session, T dict) { return new DAVPath(session, dict); } } /** * @uml.property name="session" * @uml.associationEnd */ private final DAVSession session; protected DAVPath(DAVSession s, String parent, String name, int type) { super(parent, name, type); this.session = s; } protected DAVPath(DAVSession s, String path, int type) { super(path, type); this.session = s; } protected DAVPath(DAVSession s, String parent, Local file) { super(parent, file); this.session = s; } protected <T> DAVPath(DAVSession s, T dict) { super(dict); this.session = s; } /** * @return * @throws ConnectionCanceledException * @uml.property name="session" */ @Override public DAVSession getSession() throws ConnectionCanceledException { if(null == session) { throw new ConnectionCanceledException(); } return session; } @Override public void readSize() { try { this.getSession().check(); // this.getSession().message(MessageFormat.format(Locale.localizedString("Getting size of {0}", "Status"), // this.getName())); this.getSession().getClient().setPath(this.attributes.isDirectory() ? this.getAbsolute() + Path.DELIMITER : this.getAbsolute()); this.getSession().getClient().setProperties(WebdavResource.BASIC, DepthSupport.DEPTH_1); attributes.setSize(this.getSession().getClient().getGetContentLength()); } catch(IOException e) { this.error("Cannot read file attributes", e); } } @Override public void readTimestamp() { try { this.getSession().check(); // this.getSession().message(MessageFormat.format(Locale.localizedString("Getting timestamp of {0}", "Status"), // this.getName())); this.getSession().getClient().setPath(this.attributes.isDirectory() ? this.getAbsolute() + Path.DELIMITER : this.getAbsolute()); this.getSession().getClient().setProperties(WebdavResource.BASIC, DepthSupport.DEPTH_1); attributes.setModificationDate(this.getSession().getClient().getGetLastModified()); } catch(IOException e) { this.error("Cannot read file attributes", e); } } @Override public void readPermission() { ; } @Override public void delete() { log.debug("delete:" + this.toString()); try { this.getSession().check(); // this.getSession().message(MessageFormat.format(Locale.localizedString("Deleting {0}", "Status"), // this.getName())); if(!this.getSession().getClient().deleteMethod(this.getAbsolute())) { throw new IOException(this.getSession().getClient().getStatusMessage()); } } catch(IOException e) { if(this.attributes.isFile()) { this.error("Cannot delete file", e); } if(this.attributes.isDirectory()) { this.error("Cannot delete folder", e); } } } @Override public AttributedList<Path> list() { final AttributedList<Path> childs = new AttributedList<Path>(); try { this.getSession().check(); this.getSession().message(MessageFormat.format(Locale.localizedString("Listing directory {0}", "Status"), this.getName())); this.getSession().setWorkdir(this); this.getSession().getClient().setContentType("text/xml"); WebdavResource[] resources = this.getSession().getClient().listWebdavResources(); for(final WebdavResource resource : resources) { boolean collection = false; if(null != resource.getResourceType()) { collection = resource.getResourceType().isCollection(); } Path p = PathFactory.createPath(this.getSession(), resource.getPath(), collection ? Path.DIRECTORY_TYPE : Path.FILE_TYPE); p.setParent(this); p.attributes.setOwner(resource.getOwner()); if(resource.getGetLastModified() > 0) { p.attributes.setModificationDate(resource.getGetLastModified()); } if(resource.getCreationDate() > 0) { p.attributes.setCreationDate(resource.getCreationDate()); } p.attributes.setSize(resource.getGetContentLength()); childs.add(p); } } catch(IOException e) { childs.attributes().setReadable(false); this.error("Listing directory failed", e); } return childs; } @Override public void mkdir(boolean recursive) { log.debug("mkdir:" + this.getName()); try { if(recursive) { if(!this.getParent().exists()) { this.getParent().mkdir(recursive); } } this.getSession().check(); // this.getSession().message(MessageFormat.format(Locale.localizedString("Making directory {0}", "Status"), // this.getName())); this.getSession().getClient().setContentType("text/xml"); if(!this.getSession().getClient().mkcolMethod(this.getAbsolute())) { throw new IOException(this.getSession().getClient().getStatusMessage()); } } catch(IOException e) { this.error("Cannot create folder", e); } } @Override public boolean isWritePermissionsSupported() { return false; } @Override public void writePermissions(Permission perm, boolean recursive) { // log.debug("changePermissions:" + perm); // try { // this.getSession().check(); // this.getSession().message(Locale.localizedString("Changing permission of {0} to {1}", "Status", "") + " " + perm.getOctalString() + " (" + this.getName() + ")"); // this.getSession().getClient().aclMethod(this.getAbsolute(), new Ace[]{}); // } // catch(IOException e) { // this.error("Cannot change permissions", e); // } } @Override public boolean isWriteModificationDateSupported() { return false; } @Override public void writeModificationDate(long millis) { ; } @Override public void rename(AbstractPath renamed) { log.debug("rename:" + renamed); try { this.getSession().check(); // this.getSession().message(MessageFormat.format(Locale.localizedString("Renaming {0} to {1}", "Status"), // this.getName(), renamed.getName())); if(!this.getSession().getClient().moveMethod(this.getAbsolute(), renamed.getAbsolute())) { throw new IOException(this.getSession().getClient().getStatusMessage()); } this.setPath(renamed.getAbsolute()); } catch(IOException e) { if(attributes.isFile()) { this.error("Cannot rename file", e); } if(attributes.isDirectory()) { this.error("Cannot rename folder", e); } } } @Override public void copy(AbstractPath copy) { try { this.getSession().check(); // this.getSession().message(MessageFormat.format(Locale.localizedString("Copying {0} to {1}", "Status"), // this.getName(), copy)); if(!this.getSession().getClient().copyMethod(this.getAbsolute(), copy.getAbsolute())) { throw new IOException(this.getSession().getClient().getStatusMessage()); } } catch(IOException e) { if(this.attributes.isFile()) { this.error("Cannot copy file", e); } if(this.attributes.isDirectory()) { this.error("Cannot copy folder", e); } } } @Override public void download(final BandwidthThrottleService throttle, final StreamListener listener, final boolean check) { if(attributes.isFile()) { OutputStream out = null; InputStream in = null; try { if(check) { this.getSession().check(); } if(this.getStatus().isResume()) { // this.getSession().getClient().addRequestHeader("Range", "bytes=" + this.getStatus().getCurrent() + "-"); } // this.getSession().getClient().addRequestHeader("Accept-Encoding", "gzip"); in = this.getSession().getClient().getMethodData(this.getAbsolute()); if(null == in) { throw new IOException("Unable opening data stream"); } if(!this.getSession().getClient().isResume()) { getStatus().setCurrent(0); } out = new Local.OutputStream(this.getLocal(), this.getStatus().isResume()); this.download(in, out, throttle, listener); } catch(IOException e) { this.error("Download failed", e); } finally { IOUtils.closeQuietly(in); IOUtils.closeQuietly(out); } } if(attributes.isDirectory()) { this.getLocal().mkdir(true); } } @Override public void upload(final BandwidthThrottleService throttle, final StreamListener listener, final Permission p, final boolean check) { try { if(check) { this.getSession().check(); } if(attributes.isFile()) { // this.getSession().message(MessageFormat.format(Locale.localizedString("Uploading {0}", "Status"), // this.getName())); // final InputStream in = new Local.InputStream(this.getLocal()); final InputStream in = new FileInputStream(this.getLocal().getAbsolute()); try { final Status stat = this.getStatus(); if(stat.isResume()) { // this.getSession().getClient().addRequestHeader("Content-Range", "bytes " // + stat.getCurrent() // + "-" + (this.getLocal().attributes.getSize() - 1) // + "/" + this.getLocal().attributes.getSize() // ); long skipped = in.skip(stat.getCurrent()); log.info("Skipping " + skipped + " bytes"); if(skipped < stat.getCurrent()) { throw new IOResumeException("Skipped " + skipped + " bytes instead of " + stat.getCurrent()); } } if(!this.getSession().getClient().putMethod(this.getAbsolute(), new InputStreamRequestEntity(in, this.getLocal().attributes.getSize() - stat.getCurrent(), this.getLocal().getMimeType()) { boolean requested = false; @Override public void writeRequest(OutputStream out) throws IOException { if(requested) { in.reset(); stat.reset(); stat.setCurrent(0); } try { DAVPath.this.upload(out, in, throttle, listener); } finally { requested = true; } } @Override public boolean isRepeatable() { return true; } })) { // Upload failed throw new IOException(this.getSession().getClient().getStatusMessage()); } } finally { IOUtils.closeQuietly(in); } } if(attributes.isDirectory()) { this.mkdir(); } } catch(IOException e) { this.error("Upload failed", e); } } @Override public String toHttpURL() { return this.toURL(); } }