Commit 487f8d54 authored by Ricki Hirner's avatar Ricki Hirner

Upload / download VCards

* support PUT
* support REPORT addressbook-multiget
* support CTag
parent a6975918
......@@ -35,11 +35,15 @@ public class DavAddressBook extends DavResource {
}
public void queryMemberETags() throws IOException, HttpException, DavException {
// build XML request body
/* <!ELEMENT addressbook-query ((DAV:allprop |
DAV:propname |
DAV:prop)?, filter, limit?)>
*/
XmlSerializer serializer = XmlUtils.newSerializer();
StringWriter writer = new StringWriter();
serializer.setOutput(writer);
serializer.startDocument("UTF-8", null);
serializer.setPrefix("", XmlUtils.NS_WEBDAV);
serializer.startTag(XmlUtils.NS_CARDDAV, "addressbook-query");
serializer.startTag(XmlUtils.NS_WEBDAV, "prop");
serializer.startTag(XmlUtils.NS_WEBDAV, "getetag");
......@@ -64,4 +68,52 @@ public class DavAddressBook extends DavResource {
processMultiStatus(response.body().charStream());
}
public void multiget(HttpUrl[] urls, boolean vCard4) throws IOException, HttpException, DavException {
/* <!ELEMENT addressbook-multiget ((DAV:allprop |
DAV:propname |
DAV:prop)?,
DAV:href+)>
*/
XmlSerializer serializer = XmlUtils.newSerializer();
StringWriter writer = new StringWriter();
serializer.setOutput(writer);
serializer.startDocument("UTF-8", null);
serializer.setPrefix("", XmlUtils.NS_WEBDAV);
serializer.startTag(XmlUtils.NS_CARDDAV, "addressbook-multiget");
serializer.startTag(XmlUtils.NS_WEBDAV, "prop");
serializer.startTag(XmlUtils.NS_WEBDAV, "getcontenttype");
serializer.endTag(XmlUtils.NS_WEBDAV, "getcontenttype");
serializer.startTag(XmlUtils.NS_WEBDAV, "getetag");
serializer.endTag(XmlUtils.NS_WEBDAV, "getetag");
serializer.startTag(XmlUtils.NS_CARDDAV, "address-data");
if (vCard4) {
serializer.attribute(null, "content-type", "text/vcard");
serializer.attribute(null, "version", "4.0");
}
serializer.endTag(XmlUtils.NS_CARDDAV, "address-data");
serializer.endTag(XmlUtils.NS_WEBDAV, "prop");
for (HttpUrl url : urls) {
serializer.startTag(XmlUtils.NS_WEBDAV, "href");
serializer.text(url.encodedPath());
serializer.endTag(XmlUtils.NS_WEBDAV, "href");
}
serializer.endTag(XmlUtils.NS_CARDDAV, "addressbook-multiget");
serializer.endDocument();
// redirects must not followed automatically (as it may rewrite REPORT requests to GET requests)
httpClient.setFollowRedirects(false);
Response response = httpClient.newCall(new Request.Builder()
.url(location)
.method("REPORT", RequestBody.create(MIME_XML, writer.toString()))
.header("Depth", "0") // "The request MUST include a Depth: 0 header [...]"
.build()).execute();
checkStatus(response);
assertMultiStatus(response);
members.clear();
processMultiStatus(response.body().charStream());
}
}
......@@ -39,6 +39,7 @@ import at.bitfire.dav4android.exception.InvalidDavResponseException;
import at.bitfire.dav4android.exception.UnsupportedDavException;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.dav4android.property.ResourceType;
import lombok.Cleanup;
import lombok.NonNull;
public class DavResource {
......@@ -65,6 +66,11 @@ public class DavResource {
return pathSegments.get(pathSegments.size()-1);
}
@Override
public String toString() {
return location.toString();
}
public ResponseBody get(String accept) throws IOException, HttpException, DavException {
Response response = httpClient.newCall(new Request.Builder()
......@@ -88,7 +94,7 @@ public class DavResource {
return body;
}
public void put(@NonNull RequestBody body, String ifMatchETag, String ifNoneMatchETag) throws IOException {
public void put(@NonNull RequestBody body, String ifMatchETag, String ifNoneMatchETag) throws IOException, HttpException {
Request.Builder builder = new Request.Builder()
.put(body)
.url(location);
......@@ -96,15 +102,23 @@ public class DavResource {
builder.header("If-Match", ifMatchETag);
if (ifNoneMatchETag != null)
builder.header("If-None-Match", ifNoneMatchETag);
httpClient.newCall(builder.build()).execute();
Response response = httpClient.newCall(builder.build()).execute();
checkStatus(response);
String eTag = response.header("ETag");
if (TextUtils.isEmpty(eTag))
properties.remove(GetETag.NAME);
else
properties.put(GetETag.NAME, new GetETag(eTag));
}
public void delete(@NonNull String ifMatchETag) throws IOException, HttpException {
httpClient.newCall(new Request.Builder()
Response response = httpClient.newCall(new Request.Builder()
.delete()
.url(location)
.header("If-Match", ifMatchETag)
.build()).execute();
checkStatus(response);
}
......@@ -113,7 +127,9 @@ public class DavResource {
XmlSerializer serializer = XmlUtils.newSerializer();
StringWriter writer = new StringWriter();
serializer.setOutput(writer);
serializer.setPrefix("", XmlUtils.NS_WEBDAV);
serializer.startDocument("UTF-8", null);
serializer.setPrefix("", XmlUtils.NS_WEBDAV);
serializer.startTag(XmlUtils.NS_WEBDAV, "propfind");
serializer.startTag(XmlUtils.NS_WEBDAV, "prop");
for (Property.Name prop : reqProp) {
......@@ -155,7 +171,8 @@ public class DavResource {
// collection listing requested, drop old member information
members.clear();
processMultiStatus(response.body().charStream());
@Cleanup Reader reader = response.body().charStream();
processMultiStatus(reader);
}
......
......@@ -59,6 +59,15 @@ public class PropertyCollection {
nsProperties.put(name.name, property);
}
public void remove(Property.Name name) {
if (properties == null)
return;
Map<String, Property> nsProperties = properties.get(name.namespace);
if (nsProperties != null)
nsProperties.remove(name.name);
}
public int size() {
if (properties == null)
return 0;
......
......@@ -5,6 +5,7 @@ import org.xmlpull.v1.XmlPullParser;
import java.util.HashMap;
import java.util.Map;
import at.bitfire.dav4android.property.AddressData;
import at.bitfire.dav4android.property.AddressbookDescription;
import at.bitfire.dav4android.property.AddressbookHomeSet;
import at.bitfire.dav4android.property.CalendarColor;
......@@ -14,6 +15,8 @@ import at.bitfire.dav4android.property.CalendarTimezone;
import at.bitfire.dav4android.property.CurrentUserPrincipal;
import at.bitfire.dav4android.property.CurrentUserPrivilegeSet;
import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.dav4android.property.ResourceType;
import at.bitfire.dav4android.property.SupportedAddressData;
......@@ -27,7 +30,9 @@ public class PropertyRegistry {
static {
DEFAULT.register(new ResourceType.Factory());
DEFAULT.register(new DisplayName.Factory());
DEFAULT.register(new GetCTag.Factory());
DEFAULT.register(new GetETag.Factory());
DEFAULT.register(new GetContentType.Factory());
DEFAULT.register(new CurrentUserPrincipal.Factory());
DEFAULT.register(new CurrentUserPrivilegeSet.Factory());
......@@ -35,6 +40,7 @@ public class PropertyRegistry {
DEFAULT.register(new AddressbookHomeSet.Factory());
DEFAULT.register(new AddressbookDescription.Factory());
DEFAULT.register(new SupportedAddressData.Factory());
DEFAULT.register(new AddressData.Factory());
// CalDAV
DEFAULT.register(new CalendarHomeSet.Factory());
......
......@@ -13,7 +13,8 @@ public class XmlUtils {
NS_WEBDAV = "DAV:",
NS_CALDAV = "urn:ietf:params:xml:ns:caldav",
NS_CARDDAV = "urn:ietf:params:xml:ns:carddav",
NS_APPLE_ICAL = "http://apple.com/ns/ical/";
NS_APPLE_ICAL = "http://apple.com/ns/ical/",
NS_CALENDARSERVER = "http://calendarserver.org/ns/";
private static final XmlPullParserFactory factory;
static {
......
/*
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.dav4android.property;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import at.bitfire.dav4android.Constants;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.PropertyFactory;
import at.bitfire.dav4android.XmlUtils;
import lombok.ToString;
@ToString
public class AddressData implements Property {
public static final Name NAME = new Name(XmlUtils.NS_CARDDAV, "address-data");
public String vCard;
private AddressData() {}
public static class Factory implements PropertyFactory {
@Override
public Name getName() {
return NAME;
}
@Override
public AddressData create(XmlPullParser parser) {
AddressData addressData = new AddressData();
try {
int eventType = parser.getEventType();
addressData.vCard = parser.nextText();
} catch(XmlPullParserException |IOException e) {
Log.e(Constants.LOG_TAG, "Couldn't parse <address-data>", e);
return null;
}
return addressData;
}
}
}
/*
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.dav4android.property;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import at.bitfire.dav4android.Constants;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.PropertyFactory;
import at.bitfire.dav4android.XmlUtils;
import lombok.ToString;
@ToString
public class GetCTag implements Property {
public static final Name NAME = new Name(XmlUtils.NS_CALENDARSERVER, "getctag");
public String cTag;
public static class Factory implements PropertyFactory {
@Override
public Name getName() {
return NAME;
}
@Override
public GetCTag create(XmlPullParser parser) {
GetCTag getCTag = new GetCTag();
try {
// <!ELEMENT getctag #PCDATA>
final int depth = parser.getDepth();
int eventType = parser.getEventType();
while (!(eventType == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
if (eventType == XmlPullParser.TEXT && parser.getDepth() == depth)
getCTag.cTag = parser.getText();
eventType = parser.next();
}
} catch(XmlPullParserException |IOException e) {
Log.e(Constants.LOG_TAG, "Couldn't parse <getctag>", e);
return null;
}
return getCTag;
}
}
}
/*
* Copyright © 2013 – 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.dav4android.property;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import at.bitfire.dav4android.Constants;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.PropertyFactory;
import at.bitfire.dav4android.XmlUtils;
import lombok.ToString;
@ToString
public class GetContentType implements Property {
public static final Name NAME = new Name(XmlUtils.NS_WEBDAV, "getcontenttype");
public String type;
private GetContentType() {}
public static class Factory implements PropertyFactory {
@Override
public Name getName() {
return NAME;
}
@Override
public GetContentType create(XmlPullParser parser) {
GetContentType getContentType = new GetContentType();
try {
int eventType = parser.getEventType();
getContentType.type = parser.nextText();
} catch(XmlPullParserException |IOException e) {
Log.e(Constants.LOG_TAG, "Couldn't parse <getcontenttype>", e);
return null;
}
return getContentType;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment