io.milton.http.webdav.WebDavProtocol Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of milton-server-ce Show documentation
Show all versions of milton-server-ce Show documentation
Milton Community Edition: Supports DAV level 1 and is available on Apache2 license
The newest version!
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 io.milton.http.webdav;
import io.milton.common.NameSpace;
import io.milton.http.*;
import io.milton.http.exceptions.BadRequestException;
import io.milton.http.exceptions.NotAuthorizedException;
import io.milton.http.exceptions.NotFoundException;
import io.milton.http.http11.CustomPostHandler;
import io.milton.http.http11.ETagGenerator;
import io.milton.http.quota.QuotaDataAccessor;
import io.milton.http.report.QualifiedReport;
import io.milton.http.report.Report;
import io.milton.http.report.ReportHandler;
import io.milton.http.values.SupportedReportSetList;
import io.milton.http.values.ValueWriters;
import io.milton.http.webdav.PropertyMap.StandardProperty;
import io.milton.http.webdav.PropertyMap.WritableStandardProperty;
import io.milton.property.PropertyAuthoriser;
import io.milton.property.PropertySource;
import io.milton.resource.CollectionResource;
import io.milton.resource.DisplayNameResource;
import io.milton.resource.GetableResource;
import io.milton.resource.PropFindableResource;
import io.milton.resource.PutableResource;
import io.milton.resource.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Defines the methods and properties that make up the webdav protocol.
*
* We've been a little pragmatic about what the webdav protocol actually is. It
* generally doesnt include things defined in subsequent protocols (RFC's), but
* where something is frequently used by other protocols (like REPORT) or is
* very tightly couple with normal webdav operations (like quota checking)
* you'll find it here
*
*
* @author brad
*/
public class WebDavProtocol implements HttpExtension, PropertySource {
private static final Logger log = LoggerFactory.getLogger(WebDavProtocol.class);
public static final String DAV_URI = "DAV:";
public static final String DAV_PREFIX = "d";
public static final NameSpace NS_DAV = new NameSpace(DAV_URI, DAV_PREFIX);
private final Set handlers;
private final Map reports;
private final ResourceTypeHelper resourceTypeHelper;
private final QuotaDataAccessor quotaDataAccessor;
private final PropertyMap propertyMap;
private final List propertySources;
private final ETagGenerator eTagGenerator;
private final HandlerHelper handlerHelper;
private final UserAgentHelper userAgentHelper;
private final DisplayNameFormatter displayNameFormatter;
private final MkColHandler mkColHandler;
private final PropPatchHandler propPatchHandler;
private List customPostHandlers;
public WebDavProtocol(HandlerHelper handlerHelper, ResourceTypeHelper resourceTypeHelper, WebDavResponseHandler responseHandler, List propertySources, QuotaDataAccessor quotaDataAccessor, PropPatchSetter patchSetter, PropertyAuthoriser propertyAuthoriser, ETagGenerator eTagGenerator, UrlAdapter urlAdapter, ResourceHandlerHelper resourceHandlerHelper, UserAgentHelper userAgentHelper, PropFindRequestFieldParser requestFieldParser, PropFindPropertyBuilder propertyBuilder, DisplayNameFormatter displayNameFormatter, boolean enableTextContentProperty) {
this.displayNameFormatter = displayNameFormatter;
this.userAgentHelper = userAgentHelper;
this.handlerHelper = handlerHelper;
this.eTagGenerator = eTagGenerator;
handlers = new HashSet<>();
this.resourceTypeHelper = resourceTypeHelper;
this.quotaDataAccessor = quotaDataAccessor;
this.propertyMap = new PropertyMap(WebDavProtocol.NS_DAV.getName());
propertyMap.add(new ContentLengthPropertyWriter());
propertyMap.add(new ContentTypePropertyWriter());
propertyMap.add(new CreationDatePropertyWriter("getcreated"));
propertyMap.add(new CreationDatePropertyWriter("creationdate"));
propertyMap.add(new DisplayNamePropertyWriter());
propertyMap.add(new LastModifiedDatePropertyWriter());
propertyMap.add(new ResourceTypePropertyWriter());
propertyMap.add(new EtagPropertyWriter());
propertyMap.add(new MSIsCollectionPropertyWriter());
propertyMap.add(new MSIsReadOnlyPropertyWriter());
propertyMap.add(new MSNamePropertyWriter());
log.info("resourceTypeHelper: " + resourceTypeHelper.getClass());
if (quotaDataAccessor == null) {
log.info("no quota data");
} else {
log.info("quotaDataAccessor: " + quotaDataAccessor.getClass());
propertyMap.add(new QuotaAvailableBytesPropertyWriter());
propertyMap.add(new QuotaUsedBytesPropertyWriter());
}
propertyMap.add(new SupportedReportSetProperty());
if (enableTextContentProperty) {
propertyMap.add(new MiltonExtTextContentProperty());
}
// note valuewriters is also used in DefaultWebDavResponseHandler
// if using non-default configuration you should inject the same instance into there
// and here
ValueWriters valueWriters = new ValueWriters();
if (propertySources == null) {
propertySources = new ArrayList<>();
}
log.debug("provided property sources: " + propertySources.size());
this.propertySources = propertySources;
log.debug("adding webdav as a property source to: " + this.propertySources.getClass() + " hashCode: " + this.propertySources.hashCode());
addPropertySource(this);
if (patchSetter == null) {
log.info("creating default patcheSetter: " + PropertySourcePatchSetter.class);
patchSetter = new PropertySourcePatchSetter(propertySources, valueWriters);
}
//handlers.add(new PropFindHandler(resourceHandlerHelper, resourceTypeHelper, responseHandler, propertySources));
PropFindHandler propFindHandler = new PropFindHandler(resourceHandlerHelper, requestFieldParser, responseHandler, propertyBuilder);
handlers.add(propFindHandler);
mkColHandler = new MkColHandler(responseHandler, handlerHelper);
handlers.add(mkColHandler);
propPatchHandler = new PropPatchHandler(resourceHandlerHelper, new DefaultPropPatchParser(), patchSetter, responseHandler, propertyAuthoriser);
handlers.add(propPatchHandler);
handlers.add(new CopyHandler(responseHandler, handlerHelper, resourceHandlerHelper, userAgentHelper));
handlers.add(new MoveHandler(responseHandler, handlerHelper, resourceHandlerHelper, userAgentHelper));
// Reports are added by other protocols via addReport
reports = new HashMap<>();
handlers.add(new ReportHandler(responseHandler, resourceHandlerHelper, reports));
}
@Override
public List getCustomPostHandlers() {
return customPostHandlers;
}
public void setCustomPostHandlers(List customPostHandlers) {
this.customPostHandlers = customPostHandlers;
}
public List getPropertySources() {
return Collections.unmodifiableList(propertySources);
}
public void addPropertySource(PropertySource ps) {
propertySources.add(ps);
log.debug("adding property source: " + ps.getClass() + " new size: " + propertySources.size());
}
public void addReport(Report report) {
this.reports.put(report.getName(), report);
}
@Override
public Set getHandlers() {
return Collections.unmodifiableSet(handlers);
}
@Override
public Object getProperty(QName name, Resource r) {
return propertyMap.getProperty(name, r);
}
@Override
public void setProperty(QName name, Object value, Resource r) {
throw new UnsupportedOperationException("Not supported. Standard webdav properties are not writable");
}
@Override
public PropertyMetaData getPropertyMetaData(QName name, Resource r) {
PropertyMetaData propertyMetaData = propertyMap.getPropertyMetaData(name, r);
if (propertyMetaData != null) {
// Nautilus (at least on Ubuntu 12) doesnt like empty properties
if (userAgentHelper.isNautilus(HttpManager.request())) {
Object v = getProperty(name, r);
if (v == null) {
return PropertyMetaData.UNKNOWN;
} else if (v instanceof String) {
String s = (String) v;
if (s.trim().length() == 0) {
return PropertyMetaData.UNKNOWN;
}
}
}
}
return propertyMetaData;
}
@Override
public void clearProperty(QName name, Resource r) {
throw new UnsupportedOperationException("Not supported. Standard webdav properties are not writable");
}
@Override
public List getAllPropertyNames(Resource r) {
return propertyMap.getAllPropertyNames(r);
}
/**
* Generates the displayname element text. By default is a
* CdataDisplayNameFormatter wrapping a DefaultDisplayNameFormatter so that
* extended character sets are supported
*
* @return
*/
public DisplayNameFormatter getDisplayNameFormatter() {
return displayNameFormatter;
}
class DisplayNamePropertyWriter implements WritableStandardProperty {
@Override
public String getValue(PropFindableResource res) {
if( res instanceof DisplayNameResource) {
DisplayNameResource dnr = (DisplayNameResource) res;
return dnr.getDisplayName();
}
return displayNameFormatter.formatDisplayName(res);
}
@Override
public String fieldName() {
return "displayname";
}
@Override
public Class getValueClass() {
return String.class;
}
@Override
public void setValue(PropFindableResource res, String value) {
if( res instanceof DisplayNameResource) {
DisplayNameResource dnr = (DisplayNameResource) res;
dnr.setDisplayName(value);
} else {
log.warn("Attempt to set displayname property, but resource is not compatible: " + res.getClass());
}
}
}
static class CreationDatePropertyWriter implements StandardProperty {
private final String fieldName;
public CreationDatePropertyWriter(String fieldName) {
this.fieldName = fieldName;
}
@Override
public String fieldName() {
return fieldName;
}
@Override
public Date getValue(PropFindableResource res) {
// BM: was getModifiedDate(), presume that was wrong??
return res.getCreateDate();
}
@Override
public Class getValueClass() {
return Date.class;
}
}
static class LastModifiedDatePropertyWriter implements StandardProperty {
@Override
public String fieldName() {
return "getlastmodified";
}
@Override
public Date getValue(PropFindableResource res) {
return res.getModifiedDate();
}
@Override
public Class getValueClass() {
return Date.class;
}
}
class ResourceTypePropertyWriter implements StandardProperty> {
@Override
public List getValue(PropFindableResource res) {
return resourceTypeHelper.getResourceTypes(res);
}
@Override
public String fieldName() {
return "resourcetype";
}
@Override
public Class getValueClass() {
return List.class;
}
}
static class ContentTypePropertyWriter implements StandardProperty {
@Override
public String getValue(PropFindableResource res) {
if (res instanceof GetableResource) {
GetableResource getable = (GetableResource) res;
return getable.getContentType(null);
} else {
return "";
}
}
@Override
public String fieldName() {
return "getcontenttype";
}
@Override
public Class getValueClass() {
return String.class;
}
}
static class ContentLengthPropertyWriter implements StandardProperty {
@Override
public Long getValue(PropFindableResource res) {
if (res instanceof GetableResource) {
GetableResource getable = (GetableResource) res;
return getable.getContentLength();
} else {
return null;
}
}
@Override
public String fieldName() {
return "getcontentlength";
}
@Override
public Class getValueClass() {
return Long.class;
}
}
class QuotaUsedBytesPropertyWriter implements StandardProperty {
@Override
public Long getValue(PropFindableResource res) {
if (quotaDataAccessor != null) {
return quotaDataAccessor.getQuotaUsed(res);
} else {
return null;
}
}
@Override
public String fieldName() {
return "quota-used-bytes";
}
@Override
public Class getValueClass() {
return Long.class;
}
}
class QuotaAvailableBytesPropertyWriter implements StandardProperty {
@Override
public Long getValue(PropFindableResource res) {
if (quotaDataAccessor != null) {
return quotaDataAccessor.getQuotaAvailable(res);
} else {
return null;
}
}
@Override
public String fieldName() {
return "quota-available-bytes";
}
@Override
public Class getValueClass() {
return Long.class;
}
}
class EtagPropertyWriter implements StandardProperty {
@Override
public String getValue(PropFindableResource res) {
return eTagGenerator.generateEtag(res);
}
@Override
public String fieldName() {
return "getetag";
}
@Override
public Class getValueClass() {
return String.class;
}
}
// MS specific fields
class MSNamePropertyWriter extends DisplayNamePropertyWriter {
@Override
public String fieldName() {
return "name";
}
}
static class MSIsCollectionPropertyWriter implements StandardProperty {
@Override
public String fieldName() {
return "iscollection";
}
@Override
public Boolean getValue(PropFindableResource res) {
return (res instanceof CollectionResource);
}
@Override
public Class getValueClass() {
return Boolean.class;
}
}
static class MSIsReadOnlyPropertyWriter implements StandardProperty {
@Override
public String fieldName() {
return "isreadonly";
}
@Override
public Boolean getValue(PropFindableResource res) {
return !(res instanceof PutableResource);
}
@Override
public Class getValueClass() {
return Boolean.class;
}
}
class SupportedReportSetProperty implements StandardProperty {
@Override
public String fieldName() {
return "supported-report-set";
}
@Override
public SupportedReportSetList getValue(PropFindableResource res) {
SupportedReportSetList reportSet = new SupportedReportSetList();
for (Report report: reports.values()) {
if(report instanceof QualifiedReport)
reportSet.add(((QualifiedReport) report).getQualifiedName());
else
reportSet.add(new QName(DAV_URI, report.getName()));
}
return reportSet;
}
@Override
public Class getValueClass() {
return SupportedReportSetList.class;
}
}
static class MiltonExtTextContentProperty implements StandardProperty {
@Override
public String fieldName() {
return "textcontent";
}
@Override
public String getValue(PropFindableResource res) {
if (res instanceof GetableResource) {
GetableResource gr = (GetableResource) res;
String ct = gr.getContentType("text");
if (ct != null && ct.startsWith("text")) {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try {
gr.sendContent(bout, null, Collections.EMPTY_MAP, ct);
return bout.toString("UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NotAuthorizedException | NotFoundException | BadRequestException e) {
return null;
}
}
}
return null;
}
@Override
public Class getValueClass() {
return String.class;
}
}
protected void sendStringProp(XmlWriter writer, String name, String value) {
if (value == null) {
writer.writeProperty(null, name);
} else {
writer.writeProperty(null, name, value);
}
}
void sendDateProp(XmlWriter writer, String name, Date date) {
sendStringProp(writer, name, (date == null ? null : DateUtils.formatDate(date)));
}
public HandlerHelper getHandlerHelper() {
return handlerHelper;
}
public QuotaDataAccessor getQuotaDataAccessor() {
return quotaDataAccessor;
}
public Map getReports() {
return reports;
}
public ResourceTypeHelper getResourceTypeHelper() {
return resourceTypeHelper;
}
public ETagGenerator geteTagGenerator() {
return eTagGenerator;
}
public PropertyMap getPropertyMap() {
return propertyMap;
}
public MkColHandler getMkColHandler() {
return mkColHandler;
}
public PropPatchHandler getPropPatchHandler() {
return propPatchHandler;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy