package org.apache.slide.projector.repository.webdav;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Logger;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.slide.projector.Projector;
import org.apache.slide.projector.URI;
import org.apache.slide.projector.i18n.ErrorMessage;
import org.apache.slide.projector.repository.Configurable;
import org.apache.slide.projector.repository.Repository;
import org.apache.slide.projector.repository.RoleExistsException;
import org.apache.slide.projector.repository.UserExistsException;
import org.apache.slide.projector.value.ArrayValue;
import org.apache.slide.projector.value.ElementValue;
import org.apache.slide.projector.value.InputStreamValue;
import org.apache.slide.projector.value.MapValue;
import org.apache.slide.projector.value.NullValue;
import org.apache.slide.projector.value.StreamableValue;
import org.apache.slide.projector.value.StringValue;
import org.apache.slide.projector.value.URIValue;
import org.apache.slide.projector.value.Value;
import org.apache.slide.projector.value.XMLValue;
import org.apache.webdav.lib.NotificationListener;
import org.apache.webdav.lib.Property;
import org.apache.webdav.lib.PropertyName;
import org.apache.webdav.lib.Subscriber;
import org.apache.webdav.lib.methods.MkcolMethod;
import org.apache.webdav.lib.methods.PropFindMethod;
import org.apache.webdav.lib.methods.PropPatchMethod;
import org.apache.webdav.lib.methods.SearchMethod;
import org.jdom.Element;
import org.jdom.input.DOMBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
public class WebdavRepository implements Repository, Configurable {
private static Logger logger = Logger.getLogger(WebdavRepository.class.getName());
private static final String SLIDE_NAMESPACE = "http://jakarta.apache.org/slide/";
private static final String DAV_NAMESPACE = "DAV:";
private Protocol protocol;
private String host, domain, notificationHost, users, roles;
private int port, notificationPort, notificationDelay, pollInterval, subscriptionLifetime;
private static WebdavRepository repository = new WebdavRepository();
private NotificationListener dispatcher;
private WebdavRepository() {}
public static Repository getInstance() {
return repository;
}
public void configure(Element configuration) {
protocol = Protocol.getProtocol(configuration.getChildText("protocol"));
host = configuration.getChildText("host");
port = Integer.valueOf(configuration.getChildText("port")).intValue();
domain = configuration.getChildText("domain");
users = configuration.getChildText("users");
roles = configuration.getChildText("roles");
Element notifications = configuration.getChild("notifications");
notificationHost = notifications.getChildText("host");
notificationPort = Integer.valueOf(notifications.getChildText("port")).intValue();
pollInterval = Integer.valueOf(notifications.getChildText("poll-interval")).intValue();
subscriptionLifetime = Integer.valueOf(notifications.getChildText("subscription-lifetime")).intValue();
notificationDelay = Integer.valueOf(notifications.getChildText("notification-delay")).intValue();
dispatcher = new NotificationListener(notificationHost, notificationPort,
host, port, protocol, Projector.getCredentials(), domain, pollInterval, true);
}
public Credentials login(String username, String password) throws IOException {
String url = domain+Projector.getProjectorDir();
GetMethod getMethod = new GetMethod(url);
getMethod.setDoAuthentication(true);
Credentials credentials = new UsernamePasswordCredentials(username, password);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = getMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state == HttpStatus.SC_OK ) {
return credentials;
}
return null;
}
public URI createUser(String username, String password, Credentials credentials) throws UserExistsException, IOException {
Value userExists = getResource(new URIValue(users+username), credentials);
if ( userExists == NullValue.NULL || userExists != null ) {
throw new UserExistsException(new ErrorMessage("userExists", new String[] { username }));
} else {
URI userUri = new URIValue(users+username);
MkcolMethod mkcolMethod = new MkcolMethod(domain+userUri.toString());
mkcolMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = mkcolMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state == HttpStatus.SC_CREATED ) {
changePassword(userUri, null, password, credentials);
return userUri;
}
return null;
}
}
public URI createRole(String rolename, Credentials credentials) throws RoleExistsException, IOException {
Value userExists = getResource(new URIValue(domain+roles+rolename), credentials);
if ( userExists == NullValue.NULL || userExists != null ) {
throw new RoleExistsException(new ErrorMessage("roleExists", new String[] { rolename }));
} else {
URI roleUri = new URIValue(roles+rolename);
MkcolMethod mkcolMethod = new MkcolMethod(domain+roleUri.toString());
mkcolMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = mkcolMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state == HttpStatus.SC_CREATED ) {
return roleUri;
}
return null;
}
}
public void deleteRole(URI role, Credentials credentials) throws IOException {
removeResource(role, credentials);
}
public void deleteUser(URI user, Credentials credentials) throws IOException {
removeResource(user, credentials);
}
public void addRole(URI user, URI role, Credentials credentials) throws IOException {
XMLValue roleElement = getPropertyAsXMLValue(role, DAV_NAMESPACE, "group-member-set", credentials);
Element newUserElement = new Element("href", "DAV:");
newUserElement.addContent(user.toString());
roleElement.getRootElement().addContent(newUserElement);
XMLOutputter xout = new XMLOutputter(Format.getCompactFormat());
String groupMemberSet = xout.outputString(roleElement.getRootElement().getContent());
setProperty(role, DAV_NAMESPACE, "group-member-set", groupMemberSet, credentials);
}
public void removeRole(URI user, URI role, Credentials credentials) throws IOException {
XMLValue membersElement = getPropertyAsXMLValue(role, DAV_NAMESPACE, "group-member-set", credentials);
List members = membersElement.getRootElement().getChildren();
for ( Iterator i = members.iterator(); i.hasNext(); ) {
Element memberElement = (Element)i.next();
String uri = memberElement.getTextTrim();
if ( uri.equals(user.toString()) ) {
i.remove();
break;
}
}
XMLOutputter xout = new XMLOutputter(Format.getCompactFormat());
String groupMemberSet = xout.outputString(membersElement.getRootElement().getContent());
setProperty(role, DAV_NAMESPACE, "group-member-set", groupMemberSet, credentials);
}
public ArrayValue listRoles(URI user, Credentials credentials) throws IOException {
XMLValue membership = getPropertyAsXMLValue(user, DAV_NAMESPACE, "group-membership", credentials);
List roles = membership.getRootElement().getChildren();
List roleList = new ArrayList();
for ( Iterator i = roles.iterator(); i.hasNext(); ) {
Map map = new HashMap();
Element roleElement = (Element)i.next();
String uri = roleElement.getTextTrim();
if ( uri.indexOf(domain) != -1 ) {
uri = uri.substring(uri.indexOf(domain)+domain.length());
}
map.put("uri", uri);
roleList.add(new MapValue(map));
}
return new ArrayValue((Value [])roleList.toArray(new Value[roleList.size()]));
}
public void changePassword(URI uri, String oldPassword, String newPassword, Credentials credentials) throws IOException {
setProperty(uri, SLIDE_NAMESPACE, "password", newPassword, credentials);
}
public Value getResource(URI uri, Credentials credentials) throws IOException {
String url = domain+uri.toString();
GetMethod getMethod = new GetMethod(url);
getMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = getMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state != HttpStatus.SC_OK ) {
return null;
}
InputStream stream = getMethod.getResponseBodyAsStream();
String contentType = getMethod.getResponseHeader("Content-type").getValue();
String characterEncoding = getMethod.getResponseCharSet();
Header contentLengthHeader = getMethod.getResponseHeader("Content-length");
if ( contentLengthHeader == null ) return NullValue.NULL;
int contentLength = Integer.parseInt(getMethod.getResponseHeader("Content-length").getValue());
return new InputStreamValue(stream, contentType, characterEncoding, contentLength, true);
}
public void setProperty(URI uri, String namespace, String name, String value, Credentials credentials) throws IOException {
PropPatchMethod proppatchMethod = new PropPatchMethod(domain+uri.toString());
proppatchMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
proppatchMethod.addPropertyToSet(name, value, "S", namespace);
int state = proppatchMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state != HttpStatus.SC_MULTI_STATUS ) {
throw new IOException("Received status code "+state+" when doing PROPPATH on URI="+uri);
}
}
public Value getPropertyAsStringValue(URI uri, String namespace, String name, Credentials credentials) throws IOException {
Property property = getProperty(uri, namespace, name, credentials);
if ( property == null ) return null;
return new StringValue(property.getPropertyAsString());
}
public XMLValue getPropertyAsXMLValue(URI uri, String namespace, String name, Credentials credentials) throws IOException {
Property property = getProperty(uri, namespace, name, credentials);
if ( property == null ) return null;
DOMBuilder builder = new DOMBuilder();
Element element = builder.build(property.getElement());
return new ElementValue(element);
}
private Property getProperty(URI uri, String namespace, String name, Credentials credentials) throws IOException {
String url = domain+uri.toString();
Vector props = new Vector();
props.add(new PropertyName(namespace, name));
PropFindMethod propfindMethod = new PropFindMethod(url, 0, props.elements());
propfindMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = propfindMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state != HttpStatus.SC_MULTI_STATUS ) {
throw new IOException("Received status code "+state+" when doing PROPFIND on URI="+url);
}
Enumeration propertyEnumeration = propfindMethod.getResponseProperties(url);
if ( propertyEnumeration.hasMoreElements() ) {
return (Property)propertyEnumeration.nextElement();
}
return null;
}
public ArrayValue getProperties(URI uri, Credentials credentials) throws IOException {
String url = domain+uri.toString();
PropFindMethod propfindMethod = new PropFindMethod(url, 0);
propfindMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = propfindMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state != HttpStatus.SC_MULTI_STATUS ) {
throw new IOException("Received status code "+state+" when doing PROPFIND on URI="+url);
}
List arrayList = new ArrayList();
for ( Enumeration propertyEnumeration = propfindMethod.getResponseProperties(url); propertyEnumeration.hasMoreElements(); ) {
Map properties = new HashMap();
Property property = (Property)propertyEnumeration.nextElement();
properties.put("name", new StringValue(property.getLocalName()));
properties.put("value", new StringValue(property.getPropertyAsString()));
arrayList.add(new MapValue(properties));
}
Value[] resource = new Value[arrayList.size()];
return new ArrayValue((Value[])arrayList.toArray(resource));
}
public void setResource(URI uri, StreamableValue resource, Credentials credentials) throws IOException {
String url = domain+uri.toString();
PutMethod putMethod = new PutMethod(url);
putMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
putMethod.setRequestBody(resource.getInputStream());
putMethod.setRequestHeader("Content-type", resource.getContentType());
putMethod.setRequestHeader("Content-length", String.valueOf(resource.getContentLength()));
putMethod.execute(httpState, new HttpConnection(host, port, protocol));
}
public void removeResource(URI uri, Credentials credentials) throws IOException {
String url = domain+uri.toString();
DeleteMethod deleteMethod = new DeleteMethod(url);
deleteMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
deleteMethod.execute(httpState, new HttpConnection(host, port, protocol));
}
public ArrayValue getChildren(URI uri, Credentials credentials) throws IOException {
String url = domain+uri.toString();
if ( url.charAt(url.length()-1) == '/') {
url = url.substring(0, url.length()-1);
}
PropFindMethod propfindMethod = new PropFindMethod(url, 0);
propfindMethod.setDepth(1);
propfindMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = propfindMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state != HttpStatus.SC_MULTI_STATUS ) {
throw new IOException("Received status code "+state+" when doing PROPFIND on URI="+url);
}
List children = new ArrayList();
for ( Enumeration propertyEnumeration = propfindMethod.getAllResponseURLs(); propertyEnumeration.hasMoreElements(); ) {
String childUrl = (String)propertyEnumeration.nextElement();
if ( !childUrl.equals(url) ) {
if ( childUrl.indexOf(domain) != -1 ) {
childUrl = childUrl.substring(childUrl.indexOf(domain)+domain.length());
}
children.add(new URIValue(childUrl));
}
}
return new ArrayValue((Value[])children.toArray(new Value[children.size()]));
}
public void subscribe(String method, URI uri, int depth, Subscriber listener, Credentials credentials) {
dispatcher.subscribe(method, uri.toString(), depth, subscriptionLifetime, notificationDelay, listener, credentials);
}
public void subscribe(String method, URI uri, int depth, int lifetime, int notificationDelay, Subscriber listener, Credentials credentials) {
dispatcher.subscribe(method, uri.toString(), depth, lifetime, notificationDelay, listener, credentials);
}
public void unsubscribe(URI uri, Subscriber listener, Credentials credentials) {
dispatcher.unsubscribe(uri.toString(), listener, credentials);
}
public void fireEvent(Map information, Credentials credentials) throws IOException {
dispatcher.fireEvent(information, credentials);
}
public void fireVetoableEvent(Map information, Credentials credentials) throws IOException {
dispatcher.fireVetoableEvent(information, credentials);
}
public Value[] search(String query, Credentials credentials) throws IOException {
String url = domain+"/";
SearchMethod searchMethod = new SearchMethod(url, query);
searchMethod.setDoAuthentication(true);
HttpState httpState = new HttpState();
httpState.setCredentials(null, host, credentials);
int state = searchMethod.execute(httpState, new HttpConnection(host, port, protocol));
if ( state != HttpStatus.SC_MULTI_STATUS ) {
throw new IOException("Received status code "+state+" when doing SEARCH with query="+query);
}
List values = new ArrayList();
for ( Enumeration e = searchMethod.getAllResponseURLs(); e.hasMoreElements(); ) {
Map searchResults = new HashMap();
String uri = (String)e.nextElement();
for ( Enumeration pe = searchMethod.getResponseProperties(uri); pe.hasMoreElements(); ) {
Property property = (Property)pe.nextElement();
searchResults.put(property.getLocalName(), property.getPropertyAsString());
}
if ( uri.indexOf(domain) != -1 ) {
uri = uri.substring(uri.indexOf(domain)+domain.length());
}
searchResults.put("uri", uri);
values.add(new MapValue(searchResults));
}
return (Value [])values.toArray(new Value[values.size()]);
}
}