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.LoginController;
import ch.cyberduck.core.*;
import ch.cyberduck.core.Credentials;
import ch.cyberduck.core.http.HTTPSession;
import ch.cyberduck.core.i18n.Locale;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.auth.*;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.methods.DepthSupport;
import java.io.IOException;
import java.net.InetAddress;
import java.text.MessageFormat;
/**
* @version $Id$
*/
public class DAVSession extends HTTPSession {
private static Logger log = Logger.getLogger(DAVSession.class);
static {
SessionFactory.addFactory(Protocol.WEBDAV, new Factory());
}
private static class Factory extends SessionFactory {
@Override
protected Session create(Host h) {
return new DAVSession(h);
}
}
/**
* @uml.property name="dAV"
* @uml.associationEnd
*/
private DAVResource DAV;
protected DAVSession(Host h) {
super(h);
}
@Override
public void check() throws IOException {
super.check();
if(this.isConnected()) {
this.getClient().clearHeaders();
}
}
@Override
protected DAVResource getClient() throws ConnectionCanceledException {
if(null == DAV) {
throw new ConnectionCanceledException();
}
return DAV;
}
protected void configure() throws IOException {
final HttpClient client = this.getClient().getSessionInstance(this.getClient().getHttpURL(), false);
client.getHostConfiguration().getParams().setParameter(
"http.useragent", this.getUserAgent()
);
// final Proxy proxy = ProxyFactory.instance();
// if(proxy.isHTTPProxyEnabled()) {
// this.getClient().setProxy(proxy.getHTTPProxyHost(), proxy.getHTTPProxyPort());
// //this.getClient().setProxyCredentials(new UsernamePasswordCredentials(null, null));
// }
// else {
// this.getClient().setProxy(null, -1);
// this.getClient().setProxyCredentials(null);
// }
// this.getClient().setFollowRedirects(Preferences.instance().getBoolean("webdav.followRedirects"));
}
@Override
protected void connect() throws IOException, LoginCanceledException {
if(this.isConnected()) {
return;
}
this.fireConnectionWillOpenEvent();
// this.message(MessageFormat.format(Locale.localizedString("Opening {0} connection to {1}", "Status"),
// host.getProtocol().getName(), host.getHostname()));
WebdavResource.setDefaultAction(WebdavResource.NOACTION);
this.DAV = new DAVResource(host);
final String workdir = host.getDefaultPath();
if(StringUtils.isNotBlank(workdir)) {
this.getClient().setPath(workdir.startsWith(Path.DELIMITER) ? workdir : Path.DELIMITER + workdir);
}
this.configure();
this.login();
WebdavResource.setDefaultAction(WebdavResource.BASIC);
// this.message(MessageFormat.format(Locale.localizedString("{0} connection opened", "Status"),
// host.getProtocol().getName()));
if(null == this.getClient().getResourceType() || !this.getClient().getResourceType().isCollection()) {
throw new IOException("Listing directory failed");
}
this.fireConnectionDidOpenEvent();
}
@Override
public void setLoginController(final LoginController c) {
this.login = new LoginController() {
public void check(Host host) throws LoginCanceledException {
final Credentials credentials = host.getCredentials();
if(!credentials.isValid()) {
if(Preferences.instance().getBoolean("connection.login.useKeychain")) {
credentials.setPassword(((AbstractLoginController) c).find(host));
}
}
// Do not prompt for credentials yet but in the credentials provider
// below upon request with the given authentication scheme realm
}
public void check(Host host, String reason) throws LoginCanceledException {
c.check(host, reason);
}
public void success(Host host) {
c.success(host);
}
public void fail(Host host, String reason) throws LoginCanceledException {
c.fail(host, reason);
}
public void prompt(Host host, String reason, String message) throws LoginCanceledException {
c.prompt(host, reason, message);
}
};
}
@Override
protected void login(final Credentials credentials) throws IOException, LoginCanceledException {
try {
final HttpClient client = this.getClient().getSessionInstance(this.getClient().getHttpURL(), false);
if(credentials.isValid()) {
// Enable preemptive authentication. See HttpState#setAuthenticationPreemptive
this.getClient().setCredentials(
new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword()));
}
client.getParams().setParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true);
client.getParams().setParameter(CredentialsProvider.PROVIDER, new CredentialsProvider() {
int retry = 0;
public org.apache.commons.httpclient.Credentials getCredentials(AuthScheme authscheme, String hostname, int port, boolean proxy) throws CredentialsNotAvailableException {
if(null == authscheme) {
return null;
}
try {
final StringBuilder realm = new StringBuilder(hostname);
realm.append(":").append(port).append(".");
if(StringUtils.isNotBlank(authscheme.getRealm())) {
realm.append(" ").append(authscheme.getRealm());
}
if(0 == retry) {
login.check(host, realm.toString());
}
else {
// authstate.isAuthAttempted() && authscheme.isComplete()
// Already tried and failed.
login.fail(DAVSession.this.getHost(), realm.toString());
}
// message(MessageFormat.format(Locale.localizedString("Authenticating as {0}", "Status"),
// credentials.getUsername()));
retry++;
if(authscheme instanceof RFC2617Scheme) {
return new UsernamePasswordCredentials(credentials.getUsername(), credentials.getPassword());
}
if(authscheme instanceof NTLMScheme) {
return new NTCredentials(credentials.getUsername(),
credentials.getPassword(), InetAddress.getLocalHost().getHostName(),
Preferences.instance().getProperty("webdav.ntlm.domain"));
}
throw new CredentialsNotAvailableException("Unsupported authentication scheme: " +
authscheme.getSchemeName());
}
catch(LoginCanceledException e) {
throw new CredentialsNotAvailableException();
}
catch(IOException e) {
throw new CredentialsNotAvailableException();
}
}
});
// Try to get basic properties fo this resource using these credentials
this.getClient().setProperties(WebdavResource.BASIC, DepthSupport.DEPTH_0);
// this.message(Locale.localizedString("Login successful", "Credentials"));
}
catch(HttpException e) {
if(e.getReasonCode() == HttpStatus.SC_UNAUTHORIZED) {
throw new LoginCanceledException();
}
throw e;
}
}
@Override
public void close() {
try {
if(this.isConnected()) {
this.fireConnectionWillCloseEvent();
this.getClient().close();
}
}
catch(IOException e) {
log.error("IO Error: " + e.getMessage());
}
finally {
DAV = null;
this.fireConnectionDidCloseEvent();
}
}
@Override
public void setWorkdir(Path workdir) throws IOException {
if(!this.isConnected()) {
throw new ConnectionCanceledException();
}
this.getClient().setPath(workdir.isRoot() ? Path.DELIMITER : workdir.getAbsolute() + Path.DELIMITER);
super.setWorkdir(workdir);
}
@Override
protected void noop() throws IOException {
;
}
@Override
public void sendCommand(String command) {
throw new UnsupportedOperationException();
}
@Override
public void error(Path path, String message, Throwable e) {
if(e instanceof HttpException) {
super.error(path, message, new HttpException(
HttpStatus.getStatusText(((HttpException) e).getReasonCode())));
}
super.error(path, message, e);
}
}