Commit 57e1f34c authored by Ricki Hirner's avatar Ricki Hirner

add new properties and utilities

parent 91876d3f
......@@ -17,7 +17,7 @@ import at.bitfire.dav4android.property.ResourceType;
public class DavResourceTest extends TestCase {
OkHttpClient httpClient = new HttpClient();
OkHttpClient httpClient = new OkHttpClient();
MockWebServer mockServer = new MockWebServer();
@Override
......
/*
* 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;
import android.util.Log;
import com.squareup.okhttp.HttpUrl;
import junit.framework.TestCase;
import java.util.List;
public class HttpUtilsTest extends TestCase {
public void testHttpUrl() {
assertEquals(
HttpUrl.parse("http://example.com/my@dav/"),
HttpUrl.parse("http://example.com/my%40dav/")
);
}
public void testParseWwwAuthenticate() {
List<HttpUtils.AuthScheme> schemes = HttpUtils.parseWwwAuthenticate(new String[]{ "Basic realm=\"test\"" });
assertEquals(1, schemes.size());
HttpUtils.AuthScheme scheme = schemes.get(0);
assertEquals("Basic", scheme.scheme);
assertEquals(1, scheme.params.size());
assertEquals("realm=\"test\"", scheme.params.get(0));
schemes = HttpUtils.parseWwwAuthenticate(new String[]{ " UnknownWithoutParam, Unknown WithParam1, Param2 " });
assertEquals(2, schemes.size());
assertEquals("UnknownWithoutParam", schemes.get(0).scheme);
assertEquals(0, schemes.get(0).params.size());
assertEquals("Unknown", schemes.get(1).scheme);
assertEquals(2, schemes.get(1).params.size());
assertEquals("WithParam1", schemes.get(1).params.get(0));
assertEquals("Param2", schemes.get(1).params.get(1));
// TODO test parameters with quoted strings with commas:
// X-MyScheme param1, param2="a,b,c", MyOtherScheme paramA
}
}
......@@ -43,6 +43,7 @@ public class DavResource {
public final MediaType MEDIA_TYPE_XML = MediaType.parse("application/xml; charset=utf-8");
protected final OkHttpClient httpClient;
protected static final int MAX_REDIRECTS = 5;
public HttpUrl location;
public final PropertyCollection properties = new PropertyCollection();
......@@ -76,13 +77,11 @@ public class DavResource {
httpClient.setFollowRedirects(false);
Response response = null;
for (int attempt = 0; attempt < 3; attempt++) {
Constants.log.info("Attempt " + attempt);
for (int attempt = 0; attempt < MAX_REDIRECTS; attempt++) {
response = httpClient.newCall(new Request.Builder()
.url(location)
.method("PROPFIND", RequestBody.create(MEDIA_TYPE_XML, writer.toString()))
.header("Depth", String.valueOf(depth))
.header("Authorization", Credentials.basic("XXXXXXXXXXXX", "XXXXXXXXx")) // TODO
.build()).execute();
if (response.code()/100 == 3) {
......
/*
* 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;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
import java.util.logging.Level;
import lombok.extern.slf4j.Slf4j;
public class HttpClient extends OkHttpClient {
public HttpClient() {
super();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Constants.log.trace(message);
}
});
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
networkInterceptors().add(logging);
}
}
/*
* 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;
import android.text.TextUtils;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import lombok.RequiredArgsConstructor;
public class HttpUtils {
public static List<AuthScheme> parseWwwAuthenticate(String[] wwwAuths) {
List<AuthScheme> schemes = new LinkedList<>();
for (String wwwAuth : wwwAuths) {
StringTokenizer tok = new StringTokenizer(wwwAuth.trim(), ",");
AuthScheme scheme = null;
while (tok.hasMoreTokens()) {
String token = tok.nextToken().trim();
Constants.log.debug("Token: " + token);
if (token.contains(" ")) {
String parts[] = token.split(" +");
schemes.add(scheme = new AuthScheme(parts[0]));
scheme.params.add(parts[1]);
} else {
if (scheme != null)
scheme.params.add(token);
else
schemes.add(scheme = new AuthScheme(token));
}
}
}
Constants.log.debug("Server authentication schemes: ");
for (AuthScheme scheme : schemes)
Constants.log.debug(" - " + scheme);
return schemes;
}
@RequiredArgsConstructor
public static class AuthScheme {
public final String scheme;
public final List<String> params = new LinkedList<>();
@Override
public String toString() {
return scheme + "(" + TextUtils.join(",", params) + ")";
}
}
}
......@@ -10,10 +10,13 @@ import at.bitfire.dav4android.property.AddressbookHomeSet;
import at.bitfire.dav4android.property.CalendarColor;
import at.bitfire.dav4android.property.CalendarDescription;
import at.bitfire.dav4android.property.CalendarHomeSet;
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.ResourceType;
import at.bitfire.dav4android.property.SupportedAddressData;
import at.bitfire.dav4android.property.SupportedCalendarComponentSet;
public class PropertyRegistry {
......@@ -24,6 +27,7 @@ public class PropertyRegistry {
DEFAULT.register(new ResourceType.Factory());
DEFAULT.register(new DisplayName.Factory());
DEFAULT.register(new CurrentUserPrincipal.Factory());
DEFAULT.register(new CurrentUserPrivilegeSet.Factory());
// CardDAV
DEFAULT.register(new AddressbookHomeSet.Factory());
......@@ -32,8 +36,10 @@ public class PropertyRegistry {
// CalDAV
DEFAULT.register(new CalendarHomeSet.Factory());
DEFAULT.register(new CalendarDescription.Factory());
DEFAULT.register(new CalendarColor.Factory());
DEFAULT.register(new CalendarDescription.Factory());
DEFAULT.register(new CalendarTimezone.Factory());
DEFAULT.register(new SupportedCalendarComponentSet.Factory());
}
......
/*
* 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 CalendarTimezone implements Property {
public static final Name NAME = new Name(XmlUtils.NS_CALDAV, "calendar-timezone");
public String vTimeZone;
public static class Factory implements PropertyFactory {
@Override
public Name getName() {
return NAME;
}
@Override
public CalendarTimezone create(XmlPullParser parser) {
CalendarTimezone timeZone = new CalendarTimezone();
try {
// <!ELEMENT calendar-timezone (#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)
timeZone.vTimeZone = parser.getText();
eventType = parser.next();
}
} catch(XmlPullParserException|IOException e) {
Log.e(Constants.LOG_TAG, "Couldn't parse <calendar-timezone>", e);
return null;
}
return timeZone;
}
}
}
/*
* 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.text.TextUtils;
import android.util.Log;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import at.bitfire.dav4android.Constants;
import at.bitfire.dav4android.Property;
import at.bitfire.dav4android.PropertyFactory;
import at.bitfire.dav4android.XmlUtils;
public class CurrentUserPrivilegeSet implements Property {
public static final Name NAME = new Name(XmlUtils.NS_WEBDAV, "current-user-privilege-set");
// only those privileges which are required for DAVdroid are implemented
public boolean
mayRead,
mayWriteContent;
@Override
public String toString() {
List<String> s = new LinkedList<>();
if (mayRead)
s.add("read");
if (mayWriteContent)
s.add("write");
return "[" + TextUtils.join("/", s) + "]";
}
public static class Factory implements PropertyFactory {
@Override
public Name getName() {
return NAME;
}
@Override
public CurrentUserPrivilegeSet create(XmlPullParser parser) {
CurrentUserPrivilegeSet privs = new CurrentUserPrivilegeSet();
try {
// <!ELEMENT current-user-privilege-set (privilege*)>
final int depth = parser.getDepth();
int eventType = parser.getEventType();
while (!(eventType == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == depth+1 &&
XmlUtils.NS_WEBDAV.equals(parser.getNamespace()) && "privilege".equals(parser.getName()))
parsePrivilege(parser, privs);
eventType = parser.next();
}
} catch(XmlPullParserException|IOException e) {
Log.e(Constants.LOG_TAG, "Couldn't parse <current-user-privilege-set>", e);
return null;
}
return privs;
}
protected void parsePrivilege(XmlPullParser parser, CurrentUserPrivilegeSet privs) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
// <!ELEMENT privilege ANY>
int eventType = parser.getEventType();
while (!(eventType == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == depth+1 && XmlUtils.NS_WEBDAV.equals(parser.getNamespace())) {
String name = parser.getName();
switch (name) {
case "read":
privs.mayRead = true;
break;
case "write":
case "write-content":
privs.mayWriteContent = true;
break;
case "all":
privs.mayRead = privs.mayWriteContent = true;
break;
}
}
eventType = parser.next();
}
}
}
}
......@@ -39,8 +39,8 @@ public class SupportedAddressData implements Property {
while (!(eventType == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == depth+1 &&
XmlUtils.NS_CARDDAV.equals(parser.getNamespace()) && "address-data-type".equals(parser.getName())) {
String contentType = parser.getAttributeValue(XmlUtils.NS_CARDDAV, "content-type"),
version = parser.getAttributeValue(XmlUtils.NS_CARDDAV, "version");
String contentType = parser.getAttributeValue(null, "content-type"),
version = parser.getAttributeValue(null, "version");
if (contentType != null) {
if (version != null)
contentType += "; version=" + version;
......
/*
* 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 SupportedCalendarComponentSet implements Property {
public static final Name NAME = new Name(XmlUtils.NS_CALDAV, "supported-calendar-component-set");
public boolean
supportsEvents = false,
supportsTasks = false;
public static class Factory implements PropertyFactory {
@Override
public Name getName() {
return NAME;
}
@Override
public SupportedCalendarComponentSet create(XmlPullParser parser) {
SupportedCalendarComponentSet components = new SupportedCalendarComponentSet();
try {
/* <!ELEMENT supported-calendar-component-set (comp+)>
<!ELEMENT comp ((allprop | prop*), (allcomp | comp*))>
<!ATTLIST comp name CDATA #REQUIRED>
*/
final int depth = parser.getDepth();
int eventType = parser.getEventType();
while (!(eventType == XmlPullParser.END_TAG && parser.getDepth() == depth)) {
if (eventType == XmlPullParser.START_TAG && parser.getDepth() == depth+1 && XmlUtils.NS_CALDAV.equals(parser.getNamespace())) {
switch (parser.getName()) {
case "allcomp":
components.supportsEvents = components.supportsTasks = true;
break;
case "comp":
String compName = parser.getAttributeValue(null, "name");
if (compName != null)
switch (compName.toUpperCase()) {
case "VEVENT":
components.supportsEvents = true;
break;
case "VTODO":
components.supportsTasks = true;
break;
}
break;
}
}
eventType = parser.next();
}
} catch(XmlPullParserException|IOException e) {
Log.e(Constants.LOG_TAG, "Couldn't parse <supported-calendar-component-set>", e);
return null;
}
return components;
}
}
}
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