mirror of
https://github.com/etesync/android
synced 2025-06-01 21:58:47 +00:00
Specify encoding details of member names passed to WebDavResource (fixes #482)
This commit is contained in:
parent
c6950b1c16
commit
ed2a0419ad
@ -145,18 +145,24 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (String path : requestPaths) {
|
for (String path : requestPaths) {
|
||||||
WebDavResource davSlash = new WebDavResource(davCollection, path);
|
WebDavResource davSlash = new WebDavResource(davCollection, new URI(path));
|
||||||
davSlash.propfind(Mode.CARDDAV_COLLECTIONS);
|
davSlash.propfind(Mode.CARDDAV_COLLECTIONS);
|
||||||
assertEquals(new URI(principalOK), davSlash.getCurrentUserPrincipal());
|
assertEquals(new URI(principalOK), davSlash.getCurrentUserPrincipal());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testStrangeMemberNames() throws Exception {
|
||||||
|
// construct a WebDavResource from a base collection and a member which is an encoded URL (see https://github.com/bitfireAT/davdroid/issues/482)
|
||||||
|
WebDavResource dav = new WebDavResource(davCollection, "http%3A%2F%2Fwww.invalid.example%2Fm8%2Ffeeds%2Fcontacts%2Fmaria.mueller%2540gmail.com%2Fbase%2F5528abc5720cecc.vcf");
|
||||||
|
dav.get("text/vcard");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* test normal HTTP/WebDAV */
|
/* test normal HTTP/WebDAV */
|
||||||
|
|
||||||
public void testPropfindRedirection() throws Exception {
|
public void testPropfindRedirection() throws Exception {
|
||||||
// PROPFIND redirection
|
// PROPFIND redirection
|
||||||
WebDavResource redirected = new WebDavResource(baseDAV, "/redirect/301?to=/dav/");
|
WebDavResource redirected = new WebDavResource(baseDAV, new URI("/redirect/301?to=/dav/"));
|
||||||
redirected.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
redirected.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
assertEquals("/dav/", redirected.getLocation().getPath());
|
assertEquals("/dav/", redirected.getLocation().getPath());
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ exports.getBodyParts = function(conf) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/* non-existing file */
|
/* non-existing member */
|
||||||
new RoboHydraHeadDAV({
|
new RoboHydraHeadDAV({
|
||||||
path: "/dav/collection/new.file",
|
path: "/dav/collection/new.file",
|
||||||
handler: function(req,res,next) {
|
handler: function(req,res,next) {
|
||||||
@ -238,7 +238,7 @@ exports.getBodyParts = function(conf) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/* existing file */
|
/* existing member */
|
||||||
new RoboHydraHeadDAV({
|
new RoboHydraHeadDAV({
|
||||||
path: "/dav/collection/existing.file",
|
path: "/dav/collection/existing.file",
|
||||||
handler: function(req,res,next) {
|
handler: function(req,res,next) {
|
||||||
@ -255,6 +255,15 @@ exports.getBodyParts = function(conf) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
/* existing member with encoded URL as file name */
|
||||||
|
new RoboHydraHeadDAV({
|
||||||
|
path: "/dav/http%253A%252F%252Fwww.invalid.example%252Fm8%252Ffeeds%252Fcontacts%252Fmaria.mueller%252540gmail.com%252Fbase%252F5528abc5720cecc.vcf",
|
||||||
|
handler: function(req,res,next) {
|
||||||
|
if (req.method == "GET")
|
||||||
|
res.statusCode = 200;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
/* address-book multiget */
|
/* address-book multiget */
|
||||||
new RoboHydraHeadDAV({
|
new RoboHydraHeadDAV({
|
||||||
path: "/dav/addressbooks/default.vcf/",
|
path: "/dav/addressbooks/default.vcf/",
|
||||||
|
@ -39,7 +39,7 @@ public class URIUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a received absolute/relative URL and generate a normalized URI that can be compared.
|
* Parse a received absolute/relative URL and generate a normalized URI that can be compared.
|
||||||
* @param original URI to be parsed, may be absolute or relative
|
* @param original URI to be parsed, may be absolute or relative. Encoded characters will be decoded!
|
||||||
* @param mustBePath true if it's known that original is a path (may contain ":") and not an URI, i.e. ":" is not the scheme separator
|
* @param mustBePath true if it's known that original is a path (may contain ":") and not an URI, i.e. ":" is not the scheme separator
|
||||||
* @return normalized URI
|
* @return normalized URI
|
||||||
* @throws URISyntaxException
|
* @throws URISyntaxException
|
||||||
@ -72,7 +72,8 @@ public class URIUtils {
|
|||||||
|
|
||||||
URI uri = new URI(repaired);
|
URI uri = new URI(repaired);
|
||||||
URI normalized = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment());
|
URI normalized = new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), uri.getQuery(), uri.getFragment());
|
||||||
Log.v(TAG, "Normalized URL " + original + " -> " + normalized.toASCIIString());
|
Log.v(TAG, "Normalized URI " + original + " -> " + normalized.toASCIIString() + " assuming that it was " +
|
||||||
|
(mustBePath ? "a path name" : "an URI or path name"));
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ import lombok.ToString;
|
|||||||
@ToString
|
@ToString
|
||||||
public class WebDavResource {
|
public class WebDavResource {
|
||||||
private static final String TAG = "davdroid.WebDavResource";
|
private static final String TAG = "davdroid.WebDavResource";
|
||||||
|
|
||||||
public enum Property {
|
public enum Property {
|
||||||
CURRENT_USER_PRINCIPAL, // resource detection
|
CURRENT_USER_PRINCIPAL, // resource detection
|
||||||
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
||||||
@ -80,15 +80,15 @@ public class WebDavResource {
|
|||||||
|
|
||||||
// location of this resource
|
// location of this resource
|
||||||
@Getter protected URI location;
|
@Getter protected URI location;
|
||||||
|
|
||||||
// DAV capabilities (DAV: header) and allowed DAV methods (set for OPTIONS request)
|
// DAV capabilities (DAV: header) and allowed DAV methods (set for OPTIONS request)
|
||||||
protected Set<String> capabilities = new HashSet<>(),
|
protected Set<String> capabilities = new HashSet<>(),
|
||||||
methods = new HashSet<>();
|
methods = new HashSet<>();
|
||||||
|
|
||||||
// DAV properties
|
// DAV properties
|
||||||
protected HashMap<Property, String> properties = new HashMap<>();
|
protected HashMap<Property, String> properties = new HashMap<>();
|
||||||
@Getter protected List<String> supportedComponents;
|
@Getter protected List<String> supportedComponents;
|
||||||
|
|
||||||
// list of members (only for collections)
|
// list of members (only for collections)
|
||||||
@Getter protected List<WebDavResource> members;
|
@Getter protected List<WebDavResource> members;
|
||||||
|
|
||||||
@ -97,16 +97,16 @@ public class WebDavResource {
|
|||||||
|
|
||||||
protected CloseableHttpClient httpClient;
|
protected CloseableHttpClient httpClient;
|
||||||
protected HttpClientContext context;
|
protected HttpClientContext context;
|
||||||
|
|
||||||
|
|
||||||
public WebDavResource(CloseableHttpClient httpClient, URI baseURI) {
|
public WebDavResource(CloseableHttpClient httpClient, URI baseURI) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
location = baseURI;
|
location = baseURI;
|
||||||
|
|
||||||
context = HttpClientContext.create();
|
context = HttpClientContext.create();
|
||||||
context.setCredentialsProvider(new BasicCredentialsProviderHC4());
|
context.setCredentialsProvider(new BasicCredentialsProviderHC4());
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(CloseableHttpClient httpClient, URI baseURI, String username, String password, boolean preemptive) {
|
public WebDavResource(CloseableHttpClient httpClient, URI baseURI, String username, String password, boolean preemptive) {
|
||||||
this(httpClient, baseURI);
|
this(httpClient, baseURI);
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ public class WebDavResource {
|
|||||||
context.setAuthCache(authCache);
|
context.setAuthCache(authCache);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(WebDavResource parent) { // copy constructor: based on existing WebDavResource, reuse settings
|
public WebDavResource(WebDavResource parent) { // copy constructor: based on existing WebDavResource, reuse settings
|
||||||
// reuse httpClient, context and location (no deep copy)
|
// reuse httpClient, context and location (no deep copy)
|
||||||
httpClient = parent.httpClient;
|
httpClient = parent.httpClient;
|
||||||
@ -141,14 +141,16 @@ public class WebDavResource {
|
|||||||
/**
|
/**
|
||||||
* Creates a WebDavResource representing a member of the parent collection.
|
* Creates a WebDavResource representing a member of the parent collection.
|
||||||
* @param parent Parent collection
|
* @param parent Parent collection
|
||||||
* @param member File name of the member. This may contain ":" without leading "./"!
|
* @param member File name of the member, unescaped. This may contain ":" without leading "./"!
|
||||||
|
* To create a new collection with a relative path that may not be a member, use the
|
||||||
|
* WebDavResource(WebDavResource parent, URI url) constructor.
|
||||||
* @throws URISyntaxException
|
* @throws URISyntaxException
|
||||||
*/
|
*/
|
||||||
public WebDavResource(WebDavResource parent, String member) throws URISyntaxException {
|
public WebDavResource(WebDavResource parent, String member) throws URISyntaxException {
|
||||||
this(parent);
|
this(parent);
|
||||||
location = parent.location.resolve(URIUtils.parseURI(member, true));
|
location = parent.location.resolve(new URI(null, null, "./" + member, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(WebDavResource parent, String member, String ETag) throws URISyntaxException {
|
public WebDavResource(WebDavResource parent, String member, String ETag) throws URISyntaxException {
|
||||||
this(parent, member);
|
this(parent, member);
|
||||||
properties.put(Property.ETAG, ETag);
|
properties.put(Property.ETAG, ETag);
|
||||||
@ -179,37 +181,37 @@ public class WebDavResource {
|
|||||||
public boolean supportsMethod(String method) {
|
public boolean supportsMethod(String method) {
|
||||||
return methods.contains(method);
|
return methods.contains(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* file hierarchy methods */
|
/* file hierarchy methods */
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
String[] names = StringUtils.split(location.getPath(), "/");
|
String[] names = StringUtils.split(location.getPath(), "/");
|
||||||
return names[names.length - 1];
|
return names[names.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* property methods */
|
/* property methods */
|
||||||
|
|
||||||
public URI getCurrentUserPrincipal() throws URISyntaxException {
|
public URI getCurrentUserPrincipal() throws URISyntaxException {
|
||||||
String principal = properties.get(Property.CURRENT_USER_PRINCIPAL);
|
String principal = properties.get(Property.CURRENT_USER_PRINCIPAL);
|
||||||
return principal != null ? URIUtils.parseURI(principal, false) : null;
|
return principal != null ? URIUtils.parseURI(principal, false) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getAddressbookHomeSet() throws URISyntaxException {
|
public URI getAddressbookHomeSet() throws URISyntaxException {
|
||||||
String homeset = properties.get(Property.ADDRESSBOOK_HOMESET);
|
String homeset = properties.get(Property.ADDRESSBOOK_HOMESET);
|
||||||
return homeset != null ? URIUtils.parseURI(homeset, false) : null;
|
return homeset != null ? URIUtils.parseURI(homeset, false) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getCalendarHomeSet() throws URISyntaxException {
|
public URI getCalendarHomeSet() throws URISyntaxException {
|
||||||
String homeset = properties.get(Property.CALENDAR_HOMESET);
|
String homeset = properties.get(Property.CALENDAR_HOMESET);
|
||||||
return homeset != null ? URIUtils.parseURI(homeset, false) : null;
|
return homeset != null ? URIUtils.parseURI(homeset, false) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
return properties.get(Property.CONTENT_TYPE);
|
return properties.get(Property.CONTENT_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContentType(String mimeType) {
|
public void setContentType(String mimeType) {
|
||||||
properties.put(Property.CONTENT_TYPE, mimeType);
|
properties.put(Property.CONTENT_TYPE, mimeType);
|
||||||
}
|
}
|
||||||
@ -217,22 +219,22 @@ public class WebDavResource {
|
|||||||
public boolean isReadOnly() {
|
public boolean isReadOnly() {
|
||||||
return properties.containsKey(Property.READ_ONLY);
|
return properties.containsKey(Property.READ_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return properties.get(Property.DISPLAY_NAME);
|
return properties.get(Property.DISPLAY_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return properties.get(Property.DESCRIPTION);
|
return properties.get(Property.DESCRIPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCTag() {
|
public String getCTag() {
|
||||||
return properties.get(Property.CTAG);
|
return properties.get(Property.CTAG);
|
||||||
}
|
}
|
||||||
public void invalidateCTag() {
|
public void invalidateCTag() {
|
||||||
properties.remove(Property.CTAG);
|
properties.remove(Property.CTAG);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getETag() {
|
public String getETag() {
|
||||||
return properties.get(Property.ETAG);
|
return properties.get(Property.ETAG);
|
||||||
}
|
}
|
||||||
@ -240,36 +242,36 @@ public class WebDavResource {
|
|||||||
public boolean isCalendar() {
|
public boolean isCalendar() {
|
||||||
return properties.containsKey(Property.IS_CALENDAR);
|
return properties.containsKey(Property.IS_CALENDAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getColor() {
|
public String getColor() {
|
||||||
return properties.get(Property.COLOR);
|
return properties.get(Property.COLOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTimezone() {
|
public String getTimezone() {
|
||||||
return properties.get(Property.TIMEZONE);
|
return properties.get(Property.TIMEZONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAddressBook() {
|
public boolean isAddressBook() {
|
||||||
return properties.containsKey(Property.IS_ADDRESSBOOK);
|
return properties.containsKey(Property.IS_ADDRESSBOOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VCardVersion getVCardVersion() {
|
public VCardVersion getVCardVersion() {
|
||||||
String versionStr = properties.get(Property.VCARD_VERSION);
|
String versionStr = properties.get(Property.VCARD_VERSION);
|
||||||
return (versionStr != null) ? VCardVersion.valueOfByStr(versionStr) : null;
|
return (versionStr != null) ? VCardVersion.valueOfByStr(versionStr) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* collection operations */
|
/* collection operations */
|
||||||
|
|
||||||
public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
|
public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
@Cleanup CloseableHttpResponse response = null;
|
@Cleanup CloseableHttpResponse response = null;
|
||||||
|
|
||||||
// processMultiStatus() requires knowledge of the actual content location,
|
// processMultiStatus() requires knowledge of the actual content location,
|
||||||
// so we have to handle redirections manually and create a new request for the new location
|
// so we have to handle redirections manually and create a new request for the new location
|
||||||
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
|
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
|
||||||
HttpPropfind propfind = new HttpPropfind(location, mode);
|
HttpPropfind propfind = new HttpPropfind(location, mode);
|
||||||
response = httpClient.execute(propfind, context);
|
response = httpClient.execute(propfind, context);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
location = DavRedirectStrategy.getLocation(propfind, response, context);
|
location = DavRedirectStrategy.getLocation(propfind, response, context);
|
||||||
Log.i(TAG, "Redirection on PROPFIND; trying again at new content URL: " + location);
|
Log.i(TAG, "Redirection on PROPFIND; trying again at new content URL: " + location);
|
||||||
@ -281,18 +283,18 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
if (response == null)
|
if (response == null)
|
||||||
throw new DavNoContentException();
|
throw new DavNoContentException();
|
||||||
|
|
||||||
checkResponse(response); // will also handle Content-Location
|
checkResponse(response); // will also handle Content-Location
|
||||||
processMultiStatus(response);
|
processMultiStatus(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void multiGet(DavMultiget.Type type, String[] names) throws URISyntaxException, IOException, DavException, HttpException {
|
public void multiGet(DavMultiget.Type type, String[] names) throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
@Cleanup CloseableHttpResponse response = null;
|
@Cleanup CloseableHttpResponse response = null;
|
||||||
|
|
||||||
// processMultiStatus() requires knowledge of the actual content location,
|
// processMultiStatus() requires knowledge of the actual content location,
|
||||||
// so we have to handle redirections manually and create a new request for the new location
|
// so we have to handle redirections manually and create a new request for the new location
|
||||||
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
|
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
|
||||||
// build multi-get XML request
|
// build multi-get XML request
|
||||||
List<String> hrefs = new LinkedList<>();
|
List<String> hrefs = new LinkedList<>();
|
||||||
for (String name : names)
|
for (String name : names)
|
||||||
// name may contain "%" which have to be encoded → use non-quoting URI constructor and getRawPath()
|
// name may contain "%" which have to be encoded → use non-quoting URI constructor and getRawPath()
|
||||||
@ -300,7 +302,7 @@ public class WebDavResource {
|
|||||||
// DAVdroid ensures that collections always have a trailing slash, so "./" won't go down in directory hierarchy
|
// DAVdroid ensures that collections always have a trailing slash, so "./" won't go down in directory hierarchy
|
||||||
hrefs.add(location.resolve(new URI(null, null, "./" + name, null)).getRawPath());
|
hrefs.add(location.resolve(new URI(null, null, "./" + name, null)).getRawPath());
|
||||||
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[hrefs.size()]));
|
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[hrefs.size()]));
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
try {
|
try {
|
||||||
Serializer serializer = new Persister();
|
Serializer serializer = new Persister();
|
||||||
@ -309,15 +311,15 @@ public class WebDavResource {
|
|||||||
Log.e(TAG, "Couldn't create XML multi-get request", ex);
|
Log.e(TAG, "Couldn't create XML multi-get request", ex);
|
||||||
throw new DavException("Couldn't create multi-get request");
|
throw new DavException("Couldn't create multi-get request");
|
||||||
}
|
}
|
||||||
|
|
||||||
// submit REPORT request
|
// submit REPORT request
|
||||||
HttpReport report = new HttpReport(location, writer.toString());
|
HttpReport report = new HttpReport(location, writer.toString());
|
||||||
response = httpClient.execute(report, context);
|
response = httpClient.execute(report, context);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
location = DavRedirectStrategy.getLocation(report, response, context);
|
location = DavRedirectStrategy.getLocation(report, response, context);
|
||||||
Log.i(TAG, "Redirection on REPORT multi-get; trying again at new content URL: " + location);
|
Log.i(TAG, "Redirection on REPORT multi-get; trying again at new content URL: " + location);
|
||||||
|
|
||||||
// don't forget to throw away the unneeded response content
|
// don't forget to throw away the unneeded response content
|
||||||
HttpEntity entity = response.getEntity();
|
HttpEntity entity = response.getEntity();
|
||||||
if (entity != null) { @Cleanup InputStream content = entity.getContent(); }
|
if (entity != null) { @Cleanup InputStream content = entity.getContent(); }
|
||||||
@ -326,7 +328,7 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
if (response == null)
|
if (response == null)
|
||||||
throw new DavNoContentException();
|
throw new DavNoContentException();
|
||||||
|
|
||||||
checkResponse(response); // will also handle Content-Location
|
checkResponse(response); // will also handle Content-Location
|
||||||
processMultiStatus(response);
|
processMultiStatus(response);
|
||||||
}
|
}
|
||||||
@ -343,13 +345,13 @@ public class WebDavResource {
|
|||||||
processMultiStatus(response);
|
processMultiStatus(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* resource operations */
|
/* resource operations */
|
||||||
|
|
||||||
public void get(String acceptedMimeTypes) throws URISyntaxException, IOException, HttpException, DavException {
|
public void get(String acceptedMimeTypes) throws URISyntaxException, IOException, HttpException, DavException {
|
||||||
HttpGetHC4 get = new HttpGetHC4(location);
|
HttpGetHC4 get = new HttpGetHC4(location);
|
||||||
get.addHeader("Accept", acceptedMimeTypes);
|
get.addHeader("Accept", acceptedMimeTypes);
|
||||||
|
|
||||||
@Cleanup CloseableHttpResponse response = httpClient.execute(get, context);
|
@Cleanup CloseableHttpResponse response = httpClient.execute(get, context);
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
|
|
||||||
@ -359,7 +361,7 @@ public class WebDavResource {
|
|||||||
|
|
||||||
content = EntityUtilsHC4.toByteArray(entity);
|
content = EntityUtilsHC4.toByteArray(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the ETag of the created/updated resource, if available (null otherwise)
|
// returns the ETag of the created/updated resource, if available (null otherwise)
|
||||||
public String put(byte[] data, PutMode mode) throws URISyntaxException, IOException, HttpException {
|
public String put(byte[] data, PutMode mode) throws URISyntaxException, IOException, HttpException {
|
||||||
HttpPutHC4 put = new HttpPutHC4(location);
|
HttpPutHC4 put = new HttpPutHC4(location);
|
||||||
@ -373,7 +375,7 @@ public class WebDavResource {
|
|||||||
put.addHeader("If-Match", (getETag() != null) ? getETag() : "*");
|
put.addHeader("If-Match", (getETag() != null) ? getETag() : "*");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getContentType() != null)
|
if (getContentType() != null)
|
||||||
put.addHeader("Content-Type", getContentType());
|
put.addHeader("Content-Type", getContentType());
|
||||||
|
|
||||||
@ -386,23 +388,23 @@ public class WebDavResource {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() throws URISyntaxException, IOException, HttpException {
|
public void delete() throws URISyntaxException, IOException, HttpException {
|
||||||
HttpDeleteHC4 delete = new HttpDeleteHC4(location);
|
HttpDeleteHC4 delete = new HttpDeleteHC4(location);
|
||||||
|
|
||||||
if (getETag() != null)
|
if (getETag() != null)
|
||||||
delete.addHeader("If-Match", getETag());
|
delete.addHeader("If-Match", getETag());
|
||||||
|
|
||||||
@Cleanup CloseableHttpResponse response = httpClient.execute(delete, context);
|
@Cleanup CloseableHttpResponse response = httpClient.execute(delete, context);
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* helpers */
|
/* helpers */
|
||||||
|
|
||||||
protected void checkResponse(HttpResponse response) throws HttpException {
|
protected void checkResponse(HttpResponse response) throws HttpException {
|
||||||
checkResponse(response.getStatusLine());
|
checkResponse(response.getStatusLine());
|
||||||
|
|
||||||
// handle Content-Location header (see RFC 4918 5.2 Collection Resources)
|
// handle Content-Location header (see RFC 4918 5.2 Collection Resources)
|
||||||
Header contentLocationHdr = response.getFirstHeader("Content-Location");
|
Header contentLocationHdr = response.getFirstHeader("Content-Location");
|
||||||
if (contentLocationHdr != null) {
|
if (contentLocationHdr != null) {
|
||||||
@ -411,13 +413,13 @@ public class WebDavResource {
|
|||||||
Log.d(TAG, "Set Content-Location to " + location);
|
Log.d(TAG, "Set Content-Location to " + location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static void checkResponse(StatusLine statusLine) throws HttpException {
|
protected static void checkResponse(StatusLine statusLine) throws HttpException {
|
||||||
int code = statusLine.getStatusCode();
|
int code = statusLine.getStatusCode();
|
||||||
|
|
||||||
if (code/100 == 1 || code/100 == 2) // everything OK
|
if (code/100 == 1 || code/100 == 2) // everything OK
|
||||||
return;
|
return;
|
||||||
|
|
||||||
String reason = code + " " + statusLine.getReasonPhrase();
|
String reason = code + " " + statusLine.getReasonPhrase();
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case HttpStatus.SC_UNAUTHORIZED:
|
case HttpStatus.SC_UNAUTHORIZED:
|
||||||
@ -437,12 +439,12 @@ public class WebDavResource {
|
|||||||
protected void processMultiStatus(HttpResponse response) throws IOException, HttpException, DavException {
|
protected void processMultiStatus(HttpResponse response) throws IOException, HttpException, DavException {
|
||||||
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS)
|
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_MULTI_STATUS)
|
||||||
throw new DavNoMultiStatusException();
|
throw new DavNoMultiStatusException();
|
||||||
|
|
||||||
HttpEntity entity = response.getEntity();
|
HttpEntity entity = response.getEntity();
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
throw new DavNoContentException();
|
throw new DavNoContentException();
|
||||||
@Cleanup InputStream content = entity.getContent();
|
@Cleanup InputStream content = entity.getContent();
|
||||||
|
|
||||||
DavMultistatus multiStatus;
|
DavMultistatus multiStatus;
|
||||||
try {
|
try {
|
||||||
Serializer serializer = new Persister();
|
Serializer serializer = new Persister();
|
||||||
@ -453,10 +455,10 @@ public class WebDavResource {
|
|||||||
|
|
||||||
if (multiStatus.response == null) // empty response
|
if (multiStatus.response == null) // empty response
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// member list will be built from response
|
// member list will be built from response
|
||||||
List<WebDavResource> members = new LinkedList<>();
|
List<WebDavResource> members = new LinkedList<>();
|
||||||
|
|
||||||
// iterate through all resources (either ourselves or member)
|
// iterate through all resources (either ourselves or member)
|
||||||
for (DavResponse singleResponse : multiStatus.response) {
|
for (DavResponse singleResponse : multiStatus.response) {
|
||||||
URI href;
|
URI href;
|
||||||
@ -480,7 +482,7 @@ public class WebDavResource {
|
|||||||
|
|
||||||
} else for (DavPropstat singlePropstat : singleResponse.propstat) { // method 2 (propstat)
|
} else for (DavPropstat singlePropstat : singleResponse.propstat) { // method 2 (propstat)
|
||||||
StatusLine status = BasicLineParserHC4.parseStatusLine(singlePropstat.status, new BasicLineParserHC4());
|
StatusLine status = BasicLineParserHC4.parseStatusLine(singlePropstat.status, new BasicLineParserHC4());
|
||||||
|
|
||||||
// ignore information about missing properties etc.
|
// ignore information about missing properties etc.
|
||||||
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
if (status.getStatusCode()/100 != 1 && status.getStatusCode()/100 != 2)
|
||||||
continue;
|
continue;
|
||||||
@ -488,7 +490,7 @@ public class WebDavResource {
|
|||||||
|
|
||||||
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
||||||
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
||||||
|
|
||||||
if (prop.currentUserPrivilegeSet != null) {
|
if (prop.currentUserPrivilegeSet != null) {
|
||||||
// privilege info available
|
// privilege info available
|
||||||
boolean mayAll = false,
|
boolean mayAll = false,
|
||||||
@ -506,16 +508,16 @@ public class WebDavResource {
|
|||||||
if (!mayAll && !mayWrite && !(mayWriteContent && mayBind && mayUnbind))
|
if (!mayAll && !mayWrite && !(mayWriteContent && mayBind && mayUnbind))
|
||||||
properties.put(Property.READ_ONLY, "1");
|
properties.put(Property.READ_ONLY, "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
||||||
properties.put(Property.ADDRESSBOOK_HOMESET, URIUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href));
|
properties.put(Property.ADDRESSBOOK_HOMESET, URIUtils.ensureTrailingSlash(prop.addressbookHomeSet.getHref().href));
|
||||||
|
|
||||||
if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
|
if (prop.calendarHomeSet != null && prop.calendarHomeSet.getHref() != null)
|
||||||
properties.put(Property.CALENDAR_HOMESET, URIUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href));
|
properties.put(Property.CALENDAR_HOMESET, URIUtils.ensureTrailingSlash(prop.calendarHomeSet.getHref().href));
|
||||||
|
|
||||||
if (prop.displayname != null)
|
if (prop.displayname != null)
|
||||||
properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
|
properties.put(Property.DISPLAY_NAME, prop.displayname.getDisplayName());
|
||||||
|
|
||||||
if (prop.resourcetype != null) {
|
if (prop.resourcetype != null) {
|
||||||
if (prop.resourcetype.getCollection() != null) {
|
if (prop.resourcetype.getCollection() != null) {
|
||||||
properties.put(Property.IS_COLLECTION, "1");
|
properties.put(Property.IS_COLLECTION, "1");
|
||||||
@ -524,7 +526,7 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
|
if (prop.resourcetype.getAddressbook() != null) { // CardDAV collection properties
|
||||||
properties.put(Property.IS_ADDRESSBOOK, "1");
|
properties.put(Property.IS_ADDRESSBOOK, "1");
|
||||||
|
|
||||||
if (prop.addressbookDescription != null)
|
if (prop.addressbookDescription != null)
|
||||||
properties.put(Property.DESCRIPTION, prop.addressbookDescription.getDescription());
|
properties.put(Property.DESCRIPTION, prop.addressbookDescription.getDescription());
|
||||||
if (prop.supportedAddressData != null)
|
if (prop.supportedAddressData != null)
|
||||||
@ -536,19 +538,19 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
if (prop.resourcetype.getCalendar() != null) { // CalDAV collection propertioes
|
if (prop.resourcetype.getCalendar() != null) { // CalDAV collection propertioes
|
||||||
properties.put(Property.IS_CALENDAR, "1");
|
properties.put(Property.IS_CALENDAR, "1");
|
||||||
|
|
||||||
if (prop.calendarDescription != null)
|
if (prop.calendarDescription != null)
|
||||||
properties.put(Property.DESCRIPTION, prop.calendarDescription.getDescription());
|
properties.put(Property.DESCRIPTION, prop.calendarDescription.getDescription());
|
||||||
|
|
||||||
if (prop.calendarColor != null)
|
if (prop.calendarColor != null)
|
||||||
properties.put(Property.COLOR, prop.calendarColor.getColor());
|
properties.put(Property.COLOR, prop.calendarColor.getColor());
|
||||||
|
|
||||||
if (prop.calendarTimezone != null)
|
if (prop.calendarTimezone != null)
|
||||||
try {
|
try {
|
||||||
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
||||||
} catch(IllegalArgumentException e) {
|
} catch(IllegalArgumentException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prop.supportedCalendarComponentSet != null) {
|
if (prop.supportedCalendarComponentSet != null) {
|
||||||
supportedComponents = new LinkedList<>();
|
supportedComponents = new LinkedList<>();
|
||||||
for (Comp component : prop.supportedCalendarComponentSet)
|
for (Comp component : prop.supportedCalendarComponentSet)
|
||||||
@ -556,36 +558,36 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prop.getctag != null)
|
if (prop.getctag != null)
|
||||||
properties.put(Property.CTAG, prop.getctag.getCTag());
|
properties.put(Property.CTAG, prop.getctag.getCTag());
|
||||||
|
|
||||||
if (prop.getetag != null)
|
if (prop.getetag != null)
|
||||||
properties.put(Property.ETAG, prop.getetag.getETag());
|
properties.put(Property.ETAG, prop.getetag.getETag());
|
||||||
|
|
||||||
if (prop.calendarData != null && prop.calendarData.ical != null)
|
if (prop.calendarData != null && prop.calendarData.ical != null)
|
||||||
data = prop.calendarData.ical.getBytes();
|
data = prop.calendarData.ical.getBytes();
|
||||||
else if (prop.addressData != null && prop.addressData.vcard != null)
|
else if (prop.addressData != null && prop.addressData.vcard != null)
|
||||||
data = prop.addressData.vcard.getBytes();
|
data = prop.addressData.vcard.getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
// about which resource is this response?
|
// about which resource is this response?
|
||||||
if (location.equals(href) || URIUtils.ensureTrailingSlash(location).equals(href)) { // about ourselves
|
if (location.equals(href) || URIUtils.ensureTrailingSlash(location).equals(href)) { // about ourselves
|
||||||
this.properties.putAll(properties);
|
this.properties.putAll(properties);
|
||||||
if (supportedComponents != null)
|
if (supportedComponents != null)
|
||||||
this.supportedComponents = supportedComponents;
|
this.supportedComponents = supportedComponents;
|
||||||
this.content = data;
|
this.content = data;
|
||||||
|
|
||||||
} else { // about a member
|
} else { // about a member
|
||||||
WebDavResource member = new WebDavResource(this, href);
|
WebDavResource member = new WebDavResource(this, href);
|
||||||
member.properties = properties;
|
member.properties = properties;
|
||||||
member.supportedComponents = supportedComponents;
|
member.supportedComponents = supportedComponents;
|
||||||
member.content = data;
|
member.content = data;
|
||||||
|
|
||||||
members.add(member);
|
members.add(member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.members = members;
|
this.members = members;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user