package ch.cyberduck.core.dav; /* * Copyright 2001-2006 The Apache Software Foundation. * * 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. */ import ch.cyberduck.core.Host; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PutMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.util.URIUtil; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.webdav.lib.ResponseEntity; import org.apache.webdav.lib.WebdavResource; //import org.apache.webdav.lib.util.WebdavStatus; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.zip.GZIPInputStream; /** * Webdav Resource adding support for Gzipped streams * * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a> * @version $Id$ */ public class DAVResource extends WebdavResource { private static Logger log = Logger.getLogger(DAVResource.class); public DAVResource(Host host) throws IOException { super(host.getProtocol().isSecure() ? new HttpsURL(host.getHostname(), host.getPort(), host.getDefaultPath()) : new HttpURL(host.getHostname(), host.getPort(), host.getDefaultPath())); } /** * Overwritten to make sure the client and its properties are not overwritten when * the credentials change. See #2974. * * @return true if the given httpURL is the client for this resource. */ @Override protected synchronized boolean isTheClient() throws URIException { final HostConfiguration hostConfig = client.getHostConfiguration(); // Hack to enable preemptive authentication client.getState().setCredentials(null, httpURL.getHost(), hostCredentials); return httpURL.getHost().equalsIgnoreCase(hostConfig.getHost()) && httpURL.getPort() == hostConfig.getProtocol().resolvePort(hostConfig.getPort()); } /** * */ public void clearHeaders() { // headers.clear(); } // /** // * Add all additionals headers that have been previously registered // * with addRequestHeader to the method // */ // @Override // protected void generateAdditionalHeaders(HttpMethod method) { // for(Object o : headers.keySet()) { // String header = (String) o; // method.setRequestHeader(header, (String) headers.get(header)); // } // } /** * @uml.property name="resume" */ private boolean resume; /** * @return * @uml.property name="resume" */ public boolean isResume() { return resume; } @Override protected void setStatusCode(int statusCode, String message) { latestStatusCode = statusCode; // final String statusText = WebdavStatus.getStatusText(statusCode); // StringBuilder text = new StringBuilder(); // text.append(statusCode).append(" ").append(StringUtils.isNotBlank(statusText) ? statusText : ""). // append(" ").append(StringUtils.isNotBlank(message) ? message : ""); // latestStatusMessage = text.toString(); } /** * Get InputStream for the GET method for the given path. * * @param path the server relative path of the resource to get * @return InputStream * @throws IOException */ @Override public InputStream getMethodData(String path) throws IOException { setClient(); GetMethod method = new GetMethod(URIUtil.encodePathQuery(path)); // method.setFollowRedirects(super.followRedirects); // generateTransactionHeader(method); // generateAdditionalHeaders(method); final int statusCode = client.executeMethod(method); Header contentRange = method.getResponseHeader("Content-Range"); resume = contentRange != null; setStatusCode(statusCode, method.getStatusText()); if(isHttpSuccess(statusCode)) { Header contentEncoding = method.getResponseHeader("Content-Encoding"); boolean zipped = contentEncoding != null && "gzip".equalsIgnoreCase(contentEncoding.getValue()); if(zipped) { return new GZIPInputStream(method.getResponseBodyAsStream()); } return method.getResponseBodyAsStream(); } else { throw new IOException("Couldn't get file"); } } /** * Execute the PUT method for the given path. * * @param path the server relative path to put the data * @param requestEntity The input stream. * @return true if the method is succeeded. * @throws IOException */ public boolean putMethod(String path, RequestEntity requestEntity) throws IOException { setClient(); PutMethod method = new PutMethod(URIUtil.encodePathQuery(path)); // Activates 'Expect: 100-Continue' handshake. The purpose of // the 'Expect: 100-Continue' handshake to allow a client that is // sending a request message with a request body to determine if // the origin server is willing to accept the request (based on // the request headers) before the client sends the request body. // // Otherwise, upload will fail when using digest authentication. // Fix #2268 method.setUseExpectHeader(true); generateIfHeader(method); method.setRequestEntity(requestEntity); // generateTransactionHeader(method); // generateAdditionalHeaders(method); int statusCode = client.executeMethod(method); setStatusCode(statusCode, method.getStatusText()); return isHttpSuccess(statusCode); } /** * Check if the http status code passed as argument is a success * * @param statusCode * @return true if code represents a HTTP success */ private boolean isHttpSuccess(int statusCode) { return (statusCode >= HttpStatus.SC_OK && statusCode < HttpStatus.SC_MULTIPLE_CHOICES); } /** * Verify whether a given string is escaped or not * * @param original given characters * @return true if the given character array is 7 bit ASCII-compatible. */ public static boolean verifyEscaped(char[] original) { for(int i = 0; i < original.length; i++) { int c = original[i]; if(c > 128) { return false; } else if(c == '%') { if(Character.digit(original[++i], 16) == -1 || Character.digit(original[++i], 16) == -1) { return false; } } } return true; } @Override protected void setWebdavProperties(final Enumeration responses) throws HttpException, IOException { super.setWebdavProperties(new Enumeration() { public boolean hasMoreElements() { return responses.hasMoreElements(); } public Object nextElement() { final ResponseEntity response = (ResponseEntity) responses.nextElement(); return new ResponseEntity() { public int getStatusCode() { return response.getStatusCode(); } public Enumeration getProperties() { return response.getProperties(); } public Enumeration getHistories() { return response.getHistories(); } public Enumeration getWorkspaces() { return response.getWorkspaces(); } public String getHref() { if(StringUtils.isNotBlank(response.getHref())) { // http://trac.cyberduck.ch/ticket/2223 final String escaped = StringUtils.replace(response.getHref(), " ", "%20"); if(!verifyEscaped(escaped.toCharArray())) { try { return URIUtil.encodePath(response.getHref()); } catch(URIException e) { log.error(e.getMessage(), e); } } return escaped; } return response.getHref(); } }; } }); } }