mirror of
https://github.com/etesync/android
synced 2025-01-26 15:40:55 +00:00
Initial support for SRV/TXT service discovery
This commit is contained in:
parent
e3a7c7092e
commit
8d4c353d8c
BIN
libs/org.xbill.dns_2.1.6.jar
Normal file
BIN
libs/org.xbill.dns_2.1.6.jar
Normal file
Binary file not shown.
@ -18,15 +18,14 @@
|
|||||||
android:text="@string/root_url" />
|
android:text="@string/root_url" />
|
||||||
|
|
||||||
<Spinner
|
<Spinner
|
||||||
android:id="@+id/select_protocol"
|
android:id="@+id/login_scheme"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_gravity="left"
|
android:layout_gravity="left"
|
||||||
android:entries="@array/http_protocols" />
|
android:entries="@array/uri_scheme" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/baseURL"
|
android:id="@+id/login_authority_path"
|
||||||
android:layout_gravity="fill_horizontal"
|
android:layout_gravity="fill_horizontal"
|
||||||
android:hint="my.server.com"
|
|
||||||
android:imeOptions="flagForceAscii|actionNext"
|
android:imeOptions="flagForceAscii|actionNext"
|
||||||
android:inputType="textUri"
|
android:inputType="textUri"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
<string name="app_name">DAVdroid</string>
|
<string name="app_name">DAVdroid</string>
|
||||||
<string name="menu_settings">Settings</string>
|
<string name="menu_settings">Settings</string>
|
||||||
|
|
||||||
<string-array name="http_protocols">
|
<string-array name="uri_scheme">
|
||||||
<item>http://</item>
|
<item>http://</item>
|
||||||
<item>https://</item>
|
<item>https://</item>
|
||||||
|
<item>mailto:</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string name="http_warning">"If you don't use encryption (HTTPS), other people may easily intercept your login details, contacts and events."</string>
|
<string name="http_warning">"If you don't use encryption (HTTPS), other people may easily intercept your login details, contacts and events."</string>
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid;
|
package at.bitfire.davdroid;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URL;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
public class URIUtils {
|
public class URLUtils {
|
||||||
private static final String TAG = "davdroid.URIUtils";
|
private static final String TAG = "davdroid.URIUtils";
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +28,23 @@ public class URIUtils {
|
|||||||
return href;
|
return href;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URI ensureTrailingSlash(URI href) {
|
public static URL ensureTrailingSlash(URL href) {
|
||||||
|
if (!href.getPath().endsWith("/"))
|
||||||
|
try {
|
||||||
|
URL newURL = new URL(href, href.getPath() + "/");
|
||||||
|
|
||||||
|
// "@" is the only character that is not encoded
|
||||||
|
//newURL = new URI(newURI.toString().replaceAll("@", "%40"));
|
||||||
|
|
||||||
|
Log.d(TAG, "Implicitly appending trailing slash to collection " + href + " -> " + newURL);
|
||||||
|
return newURL;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
Log.e(TAG, "Couldn't append trailing slash to collection URI", e);
|
||||||
|
}
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public static URI ensureTrailingSlash(URI href) {
|
||||||
if (!href.getPath().endsWith("/"))
|
if (!href.getPath().endsWith("/"))
|
||||||
try {
|
try {
|
||||||
URI newURI = new URI(href.getScheme(), href.getAuthority(), href.getPath() + "/", href.getQuery(), null);
|
URI newURI = new URI(href.getScheme(), href.getAuthority(), href.getPath() + "/", href.getQuery(), null);
|
||||||
@ -42,7 +58,7 @@ public class URIUtils {
|
|||||||
Log.e(TAG, "Couldn't append trailing slash to collection URI", e);
|
Log.e(TAG, "Couldn't append trailing slash to collection URI", e);
|
||||||
}
|
}
|
||||||
return href;
|
return href;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
|
||||||
/** handles invalid URLs/paths as good as possible **/
|
/** handles invalid URLs/paths as good as possible **/
|
@ -7,7 +7,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.resource;
|
package at.bitfire.davdroid.resource;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
import at.bitfire.davdroid.webdav.DavMultiget;
|
import at.bitfire.davdroid.webdav.DavMultiget;
|
||||||
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
||||||
@ -31,7 +31,7 @@ public class CalDavCalendar extends RemoteCollection<Event> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CalDavCalendar(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
public CalDavCalendar(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException {
|
||||||
super(httpClient, baseURL, user, password, preemptiveAuth);
|
super(httpClient, baseURL, user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.resource;
|
package at.bitfire.davdroid.resource;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
|
||||||
import at.bitfire.davdroid.webdav.DavMultiget;
|
import at.bitfire.davdroid.webdav.DavMultiget;
|
||||||
|
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
||||||
|
|
||||||
public class CardDavAddressBook extends RemoteCollection<Contact> {
|
public class CardDavAddressBook extends RemoteCollection<Contact> {
|
||||||
//private final static String TAG = "davdroid.CardDavAddressBook";
|
//private final static String TAG = "davdroid.CardDavAddressBook";
|
||||||
@ -31,7 +31,7 @@ public class CardDavAddressBook extends RemoteCollection<Contact> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CardDavAddressBook(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
public CardDavAddressBook(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException {
|
||||||
super(httpClient, baseURL, user, password, preemptiveAuth);
|
super(httpClient, baseURL, user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,57 @@
|
|||||||
package at.bitfire.davdroid.resource;
|
package at.bitfire.davdroid.resource;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ch.boye.httpclientandroidlib.HttpException;
|
import org.xbill.DNS.Lookup;
|
||||||
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
import org.xbill.DNS.Record;
|
||||||
import ezvcard.VCardVersion;
|
import org.xbill.DNS.SRVRecord;
|
||||||
|
import org.xbill.DNS.TXTRecord;
|
||||||
|
import org.xbill.DNS.TextParseException;
|
||||||
|
import org.xbill.DNS.Type;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import at.bitfire.davdroid.R;
|
import at.bitfire.davdroid.R;
|
||||||
import at.bitfire.davdroid.URIUtils;
|
|
||||||
import at.bitfire.davdroid.webdav.DavException;
|
import at.bitfire.davdroid.webdav.DavException;
|
||||||
import at.bitfire.davdroid.webdav.DavHttpClient;
|
import at.bitfire.davdroid.webdav.DavHttpClient;
|
||||||
import at.bitfire.davdroid.webdav.DavIncapableException;
|
import at.bitfire.davdroid.webdav.DavIncapableException;
|
||||||
|
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
||||||
import at.bitfire.davdroid.webdav.NotAuthorizedException;
|
import at.bitfire.davdroid.webdav.NotAuthorizedException;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
import ch.boye.httpclientandroidlib.HttpException;
|
||||||
|
import ch.boye.httpclientandroidlib.impl.client.CloseableHttpClient;
|
||||||
|
import ezvcard.VCardVersion;
|
||||||
|
|
||||||
public class DavResourceFinder {
|
public class DavResourceFinder implements Closeable {
|
||||||
private final static String TAG = "davdroid.DavResourceFinder";
|
private final static String TAG = "davdroid.DavResourceFinder";
|
||||||
|
|
||||||
|
protected Context context;
|
||||||
|
protected CloseableHttpClient httpClient;
|
||||||
|
|
||||||
|
|
||||||
|
public DavResourceFinder(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
public static void findResources(Context context, ServerInfo serverInfo) throws URISyntaxException, DavException, HttpException, IOException {
|
|
||||||
// disable compression and enable network logging for debugging purposes
|
// disable compression and enable network logging for debugging purposes
|
||||||
CloseableHttpClient httpClient = DavHttpClient.create(true, true);
|
httpClient = DavHttpClient.create(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
WebDavResource base = new WebDavResource(httpClient,
|
@Override
|
||||||
new URI(URIUtils.ensureTrailingSlash(serverInfo.getProvidedURL())),
|
public void close() throws IOException {
|
||||||
serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.isAuthPreemptive());
|
httpClient.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void findResources(ServerInfo serverInfo) throws URISyntaxException, DavException, HttpException, IOException {
|
||||||
// CardDAV
|
// CardDAV
|
||||||
WebDavResource principal = getCurrentUserPrincipal(base, "carddav");
|
WebDavResource principal = getCurrentUserPrincipal(serverInfo, "carddav");
|
||||||
if (principal != null) {
|
if (principal != null) {
|
||||||
serverInfo.setCardDAV(true);
|
serverInfo.setCardDAV(true);
|
||||||
|
|
||||||
@ -50,11 +68,11 @@ public class DavResourceFinder {
|
|||||||
if (homeSetAddressBooks.getMembers() != null)
|
if (homeSetAddressBooks.getMembers() != null)
|
||||||
for (WebDavResource resource : homeSetAddressBooks.getMembers())
|
for (WebDavResource resource : homeSetAddressBooks.getMembers())
|
||||||
if (resource.isAddressBook()) {
|
if (resource.isAddressBook()) {
|
||||||
Log.i(TAG, "Found address book: " + resource.getLocation().getRawPath());
|
Log.i(TAG, "Found address book: " + resource.getLocation().getPath());
|
||||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||||
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
ServerInfo.ResourceInfo.Type.ADDRESS_BOOK,
|
||||||
resource.isReadOnly(),
|
resource.isReadOnly(),
|
||||||
resource.getLocation().toASCIIString(),
|
resource.getLocation().toString(),
|
||||||
resource.getDisplayName(),
|
resource.getDisplayName(),
|
||||||
resource.getDescription(), resource.getColor()
|
resource.getDescription(), resource.getColor()
|
||||||
);
|
);
|
||||||
@ -73,7 +91,7 @@ public class DavResourceFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CalDAV
|
// CalDAV
|
||||||
principal = getCurrentUserPrincipal(base, "caldav");
|
principal = getCurrentUserPrincipal(serverInfo, "caldav");
|
||||||
if (principal != null) {
|
if (principal != null) {
|
||||||
serverInfo.setCalDAV(true);
|
serverInfo.setCalDAV(true);
|
||||||
|
|
||||||
@ -105,7 +123,7 @@ public class DavResourceFinder {
|
|||||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||||
resource.isReadOnly(),
|
resource.isReadOnly(),
|
||||||
resource.getLocation().toASCIIString(),
|
resource.getLocation().toString(),
|
||||||
resource.getDisplayName(),
|
resource.getDisplayName(),
|
||||||
resource.getDescription(), resource.getColor()
|
resource.getDescription(), resource.getColor()
|
||||||
);
|
);
|
||||||
@ -124,6 +142,75 @@ public class DavResourceFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the initial service URL from a given base URI (HTTP[S] or mailto URI, user name, password)
|
||||||
|
* @param serverInfo User-given service information (including base URI, i.e. HTTP[S] URL+user name+password or mailto URI and password)
|
||||||
|
* @param serviceName Service name ("carddav" or "caldav")
|
||||||
|
* @return Initial service URL (HTTP/HTTPS), without user credentials
|
||||||
|
* @throws URISyntaxException when the user-given URI is invalid
|
||||||
|
* @throws MalformedURLException when the user-given URI is invalid
|
||||||
|
* @throws UnknownServiceURLException when no intial service URL could be determined
|
||||||
|
*/
|
||||||
|
URL getInitialURL(ServerInfo serverInfo, String serviceName) throws URISyntaxException, MalformedURLException {
|
||||||
|
String scheme = null,
|
||||||
|
domain = null;
|
||||||
|
int port = -1;
|
||||||
|
String path = "/";
|
||||||
|
|
||||||
|
URI baseURI = serverInfo.getBaseURI();
|
||||||
|
if ("mailto".equalsIgnoreCase(baseURI.getScheme())) {
|
||||||
|
// mailto URIs
|
||||||
|
String mailbox = serverInfo.getBaseURI().getSchemeSpecificPart();
|
||||||
|
|
||||||
|
// determine service FQDN
|
||||||
|
int pos = mailbox.lastIndexOf("@");
|
||||||
|
if (pos == -1)
|
||||||
|
throw new URISyntaxException(mailbox, "Email address doesn't contain @");
|
||||||
|
domain = mailbox.substring(pos + 1);
|
||||||
|
} else {
|
||||||
|
// HTTP(S) URLs
|
||||||
|
scheme = baseURI.getScheme();
|
||||||
|
domain = baseURI.getHost();
|
||||||
|
port = baseURI.getPort();
|
||||||
|
path = baseURI.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to determine FQDN and port number using SRV records
|
||||||
|
try {
|
||||||
|
String name = "_" + serviceName + "s._tcp." + domain;
|
||||||
|
Log.d(TAG, "Looking up SRV records for " + name);
|
||||||
|
Record[] records = new Lookup(name, Type.SRV).run();
|
||||||
|
if (records != null && records.length >= 1) {
|
||||||
|
SRVRecord srv = selectSRVRecord(records);
|
||||||
|
|
||||||
|
scheme = "https";
|
||||||
|
domain = srv.getTarget().toString(true);
|
||||||
|
port = srv.getPort();
|
||||||
|
Log.d(TAG, "Found " + serviceName + "s service for " + domain + " -> " + domain + ":" + port);
|
||||||
|
|
||||||
|
// SRV record found, look for TXT record too (for initial context path)
|
||||||
|
records = new Lookup(name, Type.TXT).run();
|
||||||
|
if (records != null && records.length >= 1) {
|
||||||
|
TXTRecord txt = (TXTRecord)records[0];
|
||||||
|
for (String segment : (String[])txt.getStrings().toArray())
|
||||||
|
if (segment.startsWith("path=")) {
|
||||||
|
path = segment.substring(5);
|
||||||
|
Log.d(TAG, "Found initial context path for " + serviceName + " at " + domain + " -> " + path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (TextParseException e) {
|
||||||
|
throw new URISyntaxException(domain, "Invalid domain name");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port != -1)
|
||||||
|
return new URL(scheme, domain, port, path);
|
||||||
|
else
|
||||||
|
return new URL(scheme, domain, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detects the current-user-principal for a given WebDavResource. At first, /.well-known/ is tried. Only
|
* Detects the current-user-principal for a given WebDavResource. At first, /.well-known/ is tried. Only
|
||||||
* if no current-user-principal can be detected for the .well-known location, the given location of the resource
|
* if no current-user-principal can be detected for the .well-known location, the given location of the resource
|
||||||
@ -132,10 +219,18 @@ public class DavResourceFinder {
|
|||||||
* @param serviceName Well-known service name ("carddav", "caldav")
|
* @param serviceName Well-known service name ("carddav", "caldav")
|
||||||
* @return WebDavResource of current-user-principal for the given service, or null if it can't be found
|
* @return WebDavResource of current-user-principal for the given service, or null if it can't be found
|
||||||
*/
|
*/
|
||||||
private static WebDavResource getCurrentUserPrincipal(WebDavResource resource, String serviceName) throws IOException, NotAuthorizedException {
|
WebDavResource getCurrentUserPrincipal(ServerInfo serverInfo, String serviceName) throws URISyntaxException, IOException, NotAuthorizedException {
|
||||||
|
URL initialURL = getInitialURL(serverInfo, serviceName);
|
||||||
|
|
||||||
|
// determine base URL (host name and initial context path)
|
||||||
|
WebDavResource base = new WebDavResource(httpClient,
|
||||||
|
//new URI(URIUtils.ensureTrailingSlash(serverInfo.getBaseURI())),
|
||||||
|
initialURL,
|
||||||
|
serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.isAuthPreemptive());
|
||||||
|
|
||||||
// look for well-known service (RFC 5785)
|
// look for well-known service (RFC 5785)
|
||||||
try {
|
try {
|
||||||
WebDavResource wellKnown = new WebDavResource(resource, "/.well-known/" + serviceName);
|
WebDavResource wellKnown = new WebDavResource(base, "/.well-known/" + serviceName);
|
||||||
wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
wellKnown.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
if (wellKnown.getCurrentUserPrincipal() != null)
|
if (wellKnown.getCurrentUserPrincipal() != null)
|
||||||
return new WebDavResource(wellKnown, wellKnown.getCurrentUserPrincipal());
|
return new WebDavResource(wellKnown, wellKnown.getCurrentUserPrincipal());
|
||||||
@ -150,9 +245,9 @@ public class DavResourceFinder {
|
|||||||
|
|
||||||
// fall back to user-given initial context path
|
// fall back to user-given initial context path
|
||||||
try {
|
try {
|
||||||
resource.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
base.propfind(Mode.CURRENT_USER_PRINCIPAL);
|
||||||
if (resource.getCurrentUserPrincipal() != null)
|
if (base.getCurrentUserPrincipal() != null)
|
||||||
return new WebDavResource(resource, resource.getCurrentUserPrincipal());
|
return new WebDavResource(base, base.getCurrentUserPrincipal());
|
||||||
} catch (NotAuthorizedException e) {
|
} catch (NotAuthorizedException e) {
|
||||||
Log.d(TAG, "Not authorized for querying principal for " + serviceName + " service", e);
|
Log.d(TAG, "Not authorized for querying principal for " + serviceName + " service", e);
|
||||||
throw e;
|
throw e;
|
||||||
@ -165,7 +260,7 @@ public class DavResourceFinder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean checkHomesetCapabilities(WebDavResource resource, String davCapability) throws IOException {
|
private static boolean checkHomesetCapabilities(WebDavResource resource, String davCapability) throws URISyntaxException, IOException {
|
||||||
// check for necessary capabilities
|
// check for necessary capabilities
|
||||||
try {
|
try {
|
||||||
resource.options();
|
resource.options();
|
||||||
@ -178,4 +273,11 @@ public class DavResourceFinder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SRVRecord selectSRVRecord(Record[] records) {
|
||||||
|
if (records.length > 1)
|
||||||
|
Log.w(TAG, "Multiple SRV records not supported yet; using first one");
|
||||||
|
return (SRVRecord)records[0];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,9 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -46,16 +47,16 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
abstract protected DavMultiget.Type multiGetType();
|
abstract protected DavMultiget.Type multiGetType();
|
||||||
abstract protected T newResourceSkeleton(String name, String ETag);
|
abstract protected T newResourceSkeleton(String name, String ETag);
|
||||||
|
|
||||||
public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws MalformedURLException {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
|
|
||||||
collection = new WebDavResource(httpClient, new URI(baseURL), user, password, preemptiveAuth);
|
collection = new WebDavResource(httpClient, new URL(baseURL), user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* collection operations */
|
/* collection operations */
|
||||||
|
|
||||||
public String getCTag() throws IOException, HttpException {
|
public String getCTag() throws URISyntaxException, IOException, HttpException {
|
||||||
try {
|
try {
|
||||||
if (collection.getCTag() == null && collection.getMembers() == null) // not already fetched
|
if (collection.getCTag() == null && collection.getMembers() == null) // not already fetched
|
||||||
collection.propfind(HttpPropfind.Mode.COLLECTION_CTAG);
|
collection.propfind(HttpPropfind.Mode.COLLECTION_CTAG);
|
||||||
@ -65,7 +66,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
return collection.getCTag();
|
return collection.getCTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource[] getMemberETags() throws IOException, DavException, HttpException {
|
public Resource[] getMemberETags() throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG);
|
collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG);
|
||||||
|
|
||||||
List<T> resources = new LinkedList<T>();
|
List<T> resources = new LinkedList<T>();
|
||||||
@ -77,7 +78,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Resource[] multiGet(Resource[] resources) throws IOException, DavException, HttpException {
|
public Resource[] multiGet(Resource[] resources) throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
try {
|
try {
|
||||||
if (resources.length == 1)
|
if (resources.length == 1)
|
||||||
return (T[]) new Resource[] { get(resources[0]) };
|
return (T[]) new Resource[] { get(resources[0]) };
|
||||||
@ -118,7 +119,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
|
|
||||||
/* internal member operations */
|
/* internal member operations */
|
||||||
|
|
||||||
public Resource get(Resource resource) throws IOException, HttpException, DavException, InvalidResourceException {
|
public Resource get(Resource resource) throws URISyntaxException, IOException, HttpException, DavException, InvalidResourceException {
|
||||||
WebDavResource member = new WebDavResource(collection, resource.getName());
|
WebDavResource member = new WebDavResource(collection, resource.getName());
|
||||||
|
|
||||||
if (resource instanceof Contact)
|
if (resource instanceof Contact)
|
||||||
@ -144,7 +145,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns ETag of the created resource, if returned by server
|
// returns ETag of the created resource, if returned by server
|
||||||
public String add(Resource res) throws IOException, HttpException, ValidationException {
|
public String add(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException {
|
||||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||||
member.setContentType(memberContentType());
|
member.setContentType(memberContentType());
|
||||||
|
|
||||||
@ -157,7 +158,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
return eTag;
|
return eTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete(Resource res) throws IOException, HttpException {
|
public void delete(Resource res) throws URISyntaxException, IOException, HttpException {
|
||||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||||
member.delete();
|
member.delete();
|
||||||
|
|
||||||
@ -165,7 +166,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns ETag of the updated resource, if returned by server
|
// returns ETag of the updated resource, if returned by server
|
||||||
public String update(Resource res) throws IOException, HttpException, ValidationException {
|
public String update(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException {
|
||||||
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
|
||||||
member.setContentType(memberContentType());
|
member.setContentType(memberContentType());
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
package at.bitfire.davdroid.resource;
|
package at.bitfire.davdroid.resource;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -20,7 +21,11 @@ import lombok.RequiredArgsConstructor;
|
|||||||
public class ServerInfo implements Serializable {
|
public class ServerInfo implements Serializable {
|
||||||
private static final long serialVersionUID = 6744847358282980437L;
|
private static final long serialVersionUID = 6744847358282980437L;
|
||||||
|
|
||||||
final private String providedURL;
|
enum Scheme {
|
||||||
|
HTTP, HTTPS, MAILTO
|
||||||
|
}
|
||||||
|
|
||||||
|
final private URI baseURI;
|
||||||
final private String userName, password;
|
final private String userName, password;
|
||||||
final boolean authPreemptive;
|
final boolean authPreemptive;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.MalformedURLException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ public class CalendarsSyncAdapterService extends Service {
|
|||||||
return map;
|
return map;
|
||||||
} catch (RemoteException ex) {
|
} catch (RemoteException ex) {
|
||||||
Log.e(TAG, "Couldn't find local calendars", ex);
|
Log.e(TAG, "Couldn't find local calendars", ex);
|
||||||
} catch (URISyntaxException ex) {
|
} catch (MalformedURLException ex) {
|
||||||
Log.e(TAG, "Couldn't build calendar URI", ex);
|
Log.e(TAG, "Couldn't build calendar URI", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
import java.net.URISyntaxException;
|
import java.net.MalformedURLException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ public class ContactsSyncAdapterService extends Service {
|
|||||||
map.put(database, dav);
|
map.put(database, dav);
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
} catch (URISyntaxException ex) {
|
} catch (MalformedURLException ex) {
|
||||||
Log.e(TAG, "Couldn't build address book URI", ex);
|
Log.e(TAG, "Couldn't build address book URI", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ package at.bitfire.davdroid.syncadapter;
|
|||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
@ -131,11 +132,9 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
|
|||||||
try {
|
try {
|
||||||
for (Map.Entry<LocalCollection<?>, RemoteCollection<?>> entry : syncCollections.entrySet())
|
for (Map.Entry<LocalCollection<?>, RemoteCollection<?>> entry : syncCollections.entrySet())
|
||||||
new SyncManager(entry.getKey(), entry.getValue()).synchronize(extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult);
|
new SyncManager(entry.getKey(), entry.getValue()).synchronize(extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult);
|
||||||
|
|
||||||
} catch (DavException ex) {
|
} catch (DavException ex) {
|
||||||
syncResult.stats.numParseExceptions++;
|
syncResult.stats.numParseExceptions++;
|
||||||
Log.e(TAG, "Invalid DAV response", ex);
|
Log.e(TAG, "Invalid DAV response", ex);
|
||||||
|
|
||||||
} catch (HttpException ex) {
|
} catch (HttpException ex) {
|
||||||
if (ex.getCode() == HttpStatus.SC_UNAUTHORIZED) {
|
if (ex.getCode() == HttpStatus.SC_UNAUTHORIZED) {
|
||||||
Log.e(TAG, "HTTP Unauthorized " + ex.getCode(), ex);
|
Log.e(TAG, "HTTP Unauthorized " + ex.getCode(), ex);
|
||||||
@ -147,13 +146,14 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
|
|||||||
Log.w(TAG, "Soft HTTP error " + ex.getCode() + " (Android will try again later)", ex);
|
Log.w(TAG, "Soft HTTP error " + ex.getCode() + " (Android will try again later)", ex);
|
||||||
syncResult.stats.numIoExceptions++;
|
syncResult.stats.numIoExceptions++;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (LocalStorageException ex) {
|
} catch (LocalStorageException ex) {
|
||||||
syncResult.databaseError = true;
|
syncResult.databaseError = true;
|
||||||
Log.e(TAG, "Local storage (content provider) exception", ex);
|
Log.e(TAG, "Local storage (content provider) exception", ex);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
syncResult.stats.numIoExceptions++;
|
syncResult.stats.numIoExceptions++;
|
||||||
Log.e(TAG, "I/O error (Android will try again later)", ex);
|
Log.e(TAG, "I/O error (Android will try again later)", ex);
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
Log.e(TAG, "Invalid URI (file name) syntax", ex);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// allow httpClient shutdown
|
// allow httpClient shutdown
|
||||||
|
@ -7,11 +7,6 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
|
||||||
|
|
||||||
import android.app.DialogFragment;
|
import android.app.DialogFragment;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
@ -32,13 +27,12 @@ import android.widget.EditText;
|
|||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import at.bitfire.davdroid.R;
|
import at.bitfire.davdroid.R;
|
||||||
import at.bitfire.davdroid.URIUtils;
|
|
||||||
|
|
||||||
public class EnterCredentialsFragment extends Fragment implements TextWatcher {
|
public class EnterCredentialsFragment extends Fragment implements TextWatcher {
|
||||||
String protocol;
|
String scheme;
|
||||||
|
|
||||||
TextView textHttpWarning;
|
TextView textHttpWarning;
|
||||||
EditText editBaseURL, editUserName, editPassword;
|
EditText editBaseURI, editUserName, editPassword;
|
||||||
CheckBox checkboxPreemptive;
|
CheckBox checkboxPreemptive;
|
||||||
Button btnNext;
|
Button btnNext;
|
||||||
|
|
||||||
@ -50,24 +44,24 @@ public class EnterCredentialsFragment extends Fragment implements TextWatcher {
|
|||||||
// protocol selection spinner
|
// protocol selection spinner
|
||||||
textHttpWarning = (TextView) v.findViewById(R.id.http_warning);
|
textHttpWarning = (TextView) v.findViewById(R.id.http_warning);
|
||||||
|
|
||||||
Spinner spnrProtocol = (Spinner) v.findViewById(R.id.select_protocol);
|
Spinner spnrScheme = (Spinner) v.findViewById(R.id.login_scheme);
|
||||||
spnrProtocol.setOnItemSelectedListener(new OnItemSelectedListener() {
|
spnrScheme.setOnItemSelectedListener(new OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
protocol = parent.getAdapter().getItem(position).toString();
|
scheme = parent.getAdapter().getItem(position).toString();
|
||||||
textHttpWarning.setVisibility(protocol.equals("https://") ? View.GONE : View.VISIBLE);
|
textHttpWarning.setVisibility(scheme.equals("https://") ? View.GONE : View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNothingSelected(AdapterView<?> parent) {
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
protocol = null;
|
scheme = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
spnrProtocol.setSelection(1); // HTTPS
|
spnrScheme.setSelection(1); // HTTPS
|
||||||
|
|
||||||
// other input fields
|
// other input fields
|
||||||
editBaseURL = (EditText) v.findViewById(R.id.baseURL);
|
editBaseURI = (EditText) v.findViewById(R.id.login_authority_path);
|
||||||
editBaseURL.addTextChangedListener(this);
|
editBaseURI.addTextChangedListener(this);
|
||||||
|
|
||||||
editUserName = (EditText) v.findViewById(R.id.userName);
|
editUserName = (EditText) v.findViewById(R.id.userName);
|
||||||
editUserName.addTextChangedListener(this);
|
editUserName.addTextChangedListener(this);
|
||||||
@ -105,8 +99,9 @@ public class EnterCredentialsFragment extends Fragment implements TextWatcher {
|
|||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
|
|
||||||
String host_path = editBaseURL.getText().toString();
|
String authority_path = editBaseURI.getText().toString();
|
||||||
args.putString(QueryServerDialogFragment.EXTRA_BASE_URL, URIUtils.sanitize(protocol + host_path));
|
//args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, URIUtils.sanitize(scheme + host_path));
|
||||||
|
args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, scheme + authority_path);
|
||||||
args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, editUserName.getText().toString());
|
args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, editUserName.getText().toString());
|
||||||
args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString());
|
args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString());
|
||||||
args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, checkboxPreemptive.isChecked());
|
args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, checkboxPreemptive.isChecked());
|
||||||
@ -125,15 +120,15 @@ public class EnterCredentialsFragment extends Fragment implements TextWatcher {
|
|||||||
editUserName.getText().length() > 0 &&
|
editUserName.getText().length() > 0 &&
|
||||||
editPassword.getText().length() > 0;
|
editPassword.getText().length() > 0;
|
||||||
|
|
||||||
if (ok)
|
/*if (ok)
|
||||||
// check host name
|
// check host name
|
||||||
try {
|
try {
|
||||||
URI uri = new URI(URIUtils.sanitize(protocol + editBaseURL.getText().toString()));
|
URI uri = new URI(URIUtils.sanitize(scheme + editBaseURI.getText().toString()));
|
||||||
if (StringUtils.isBlank(uri.getHost()))
|
if (StringUtils.isBlank(uri.getHost()))
|
||||||
ok = false;
|
ok = false;
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
MenuItem item = menu.findItem(R.id.next);
|
MenuItem item = menu.findItem(R.id.next);
|
||||||
item.setEnabled(ok);
|
item.setEnabled(ok);
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import lombok.Cleanup;
|
||||||
import android.app.DialogFragment;
|
import android.app.DialogFragment;
|
||||||
import android.app.LoaderManager.LoaderCallbacks;
|
import android.app.LoaderManager.LoaderCallbacks;
|
||||||
import android.content.AsyncTaskLoader;
|
import android.content.AsyncTaskLoader;
|
||||||
@ -31,7 +33,7 @@ import ch.boye.httpclientandroidlib.HttpException;
|
|||||||
public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks<ServerInfo> {
|
public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks<ServerInfo> {
|
||||||
private static final String TAG = "davdroid.QueryServerDialogFragment";
|
private static final String TAG = "davdroid.QueryServerDialogFragment";
|
||||||
public static final String
|
public static final String
|
||||||
EXTRA_BASE_URL = "base_uri",
|
EXTRA_BASE_URI = "base_uri",
|
||||||
EXTRA_USER_NAME = "user_name",
|
EXTRA_USER_NAME = "user_name",
|
||||||
EXTRA_PASSWORD = "password",
|
EXTRA_PASSWORD = "password",
|
||||||
EXTRA_AUTH_PREEMPTIVE = "auth_preemptive";
|
EXTRA_AUTH_PREEMPTIVE = "auth_preemptive";
|
||||||
@ -99,14 +101,15 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
@Override
|
@Override
|
||||||
public ServerInfo loadInBackground() {
|
public ServerInfo loadInBackground() {
|
||||||
ServerInfo serverInfo = new ServerInfo(
|
ServerInfo serverInfo = new ServerInfo(
|
||||||
args.getString(EXTRA_BASE_URL),
|
URI.create(args.getString(EXTRA_BASE_URI)),
|
||||||
args.getString(EXTRA_USER_NAME),
|
args.getString(EXTRA_USER_NAME),
|
||||||
args.getString(EXTRA_PASSWORD),
|
args.getString(EXTRA_PASSWORD),
|
||||||
args.getBoolean(EXTRA_AUTH_PREEMPTIVE)
|
args.getBoolean(EXTRA_AUTH_PREEMPTIVE)
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DavResourceFinder.findResources(context, serverInfo);
|
@Cleanup DavResourceFinder finder = new DavResourceFinder(context);
|
||||||
|
finder.findResources(serverInfo);
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -40,7 +41,7 @@ public class SyncManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void synchronize(boolean manualSync, SyncResult syncResult) throws LocalStorageException, IOException, HttpException, DavException {
|
public void synchronize(boolean manualSync, SyncResult syncResult) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
|
||||||
// PHASE 1: push local changes to server
|
// PHASE 1: push local changes to server
|
||||||
int deletedRemotely = pushDeleted(),
|
int deletedRemotely = pushDeleted(),
|
||||||
addedRemotely = pushNew(),
|
addedRemotely = pushNew(),
|
||||||
@ -98,7 +99,7 @@ public class SyncManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int pushDeleted() throws LocalStorageException, IOException, HttpException {
|
private int pushDeleted() throws URISyntaxException, LocalStorageException, IOException, HttpException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
long[] deletedIDs = local.findDeleted();
|
long[] deletedIDs = local.findDeleted();
|
||||||
|
|
||||||
@ -129,7 +130,7 @@ public class SyncManager {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int pushNew() throws LocalStorageException, IOException, HttpException {
|
private int pushNew() throws URISyntaxException, LocalStorageException, IOException, HttpException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
long[] newIDs = local.findNew();
|
long[] newIDs = local.findNew();
|
||||||
Log.i(TAG, "Uploading " + newIDs.length + " new resource(s) (if not existing)");
|
Log.i(TAG, "Uploading " + newIDs.length + " new resource(s) (if not existing)");
|
||||||
@ -155,7 +156,7 @@ public class SyncManager {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int pushDirty() throws LocalStorageException, IOException, HttpException {
|
private int pushDirty() throws URISyntaxException, LocalStorageException, IOException, HttpException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
long[] dirtyIDs = local.findUpdated();
|
long[] dirtyIDs = local.findUpdated();
|
||||||
Log.i(TAG, "Uploading " + dirtyIDs.length + " modified resource(s) (if not changed)");
|
Log.i(TAG, "Uploading " + dirtyIDs.length + " modified resource(s) (if not changed)");
|
||||||
@ -182,7 +183,7 @@ public class SyncManager {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int pullNew(Resource[] resourcesToAdd) throws LocalStorageException, IOException, HttpException, DavException {
|
private int pullNew(Resource[] resourcesToAdd) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Log.i(TAG, "Fetching " + resourcesToAdd.length + " new remote resource(s)");
|
Log.i(TAG, "Fetching " + resourcesToAdd.length + " new remote resource(s)");
|
||||||
|
|
||||||
@ -196,7 +197,7 @@ public class SyncManager {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int pullChanged(Resource[] resourcesToUpdate) throws LocalStorageException, IOException, HttpException, DavException {
|
private int pullChanged(Resource[] resourcesToUpdate) throws URISyntaxException, LocalStorageException, IOException, HttpException, DavException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Log.i(TAG, "Fetching " + resourcesToUpdate.length + " updated remote resource(s)");
|
Log.i(TAG, "Fetching " + resourcesToUpdate.length + " updated remote resource(s)");
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package at.bitfire.davdroid.webdav;
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
|
import java.net.MalformedURLException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import ch.boye.httpclientandroidlib.Header;
|
import ch.boye.httpclientandroidlib.Header;
|
||||||
@ -66,7 +68,7 @@ public class DavRedirectStrategy implements RedirectStrategy {
|
|||||||
* Gets the destination of a redirection
|
* Gets the destination of a redirection
|
||||||
* @return absolute URL of new location; null if not available
|
* @return absolute URL of new location; null if not available
|
||||||
*/
|
*/
|
||||||
static URI getLocation(HttpRequest request, HttpResponse response, HttpContext context) {
|
static URL getLocation(HttpRequest request, HttpResponse response, HttpContext context) {
|
||||||
Header locationHdr = response.getFirstHeader("Location");
|
Header locationHdr = response.getFirstHeader("Location");
|
||||||
if (locationHdr == null) {
|
if (locationHdr == null) {
|
||||||
Log.e(TAG, "Received redirection without Location header, ignoring");
|
Log.e(TAG, "Received redirection without Location header, ignoring");
|
||||||
@ -86,10 +88,12 @@ public class DavRedirectStrategy implements RedirectStrategy {
|
|||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
location = originalURI.resolve(location);
|
return new URL(originalURI.toURL(), location.toString());
|
||||||
}
|
}
|
||||||
return location;
|
return location.toURL();
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
|
Log.e(TAG, "Received redirection from/to invalid URI, ignoring", e);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
Log.e(TAG, "Received redirection from/to invalid URL, ignoring", e);
|
Log.e(TAG, "Received redirection from/to invalid URL, ignoring", e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -120,7 +120,7 @@ public class TlsSniSocketFactory implements LayeredConnectionSocketFactory {
|
|||||||
if (!hostnameVerifier.verify(host, session))
|
if (!hostnameVerifier.verify(host, session))
|
||||||
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
|
throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
|
||||||
|
|
||||||
Log.i(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
|
Log.d(TAG, "Established " + session.getProtocol() + " connection with " + session.getPeerHost() +
|
||||||
" using " + session.getCipherSuite());
|
" using " + session.getCipherSuite());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ package at.bitfire.davdroid.webdav;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.net.URI;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -28,7 +29,7 @@ import org.simpleframework.xml.Serializer;
|
|||||||
import org.simpleframework.xml.core.Persister;
|
import org.simpleframework.xml.core.Persister;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import at.bitfire.davdroid.URIUtils;
|
import at.bitfire.davdroid.URLUtils;
|
||||||
import at.bitfire.davdroid.resource.Event;
|
import at.bitfire.davdroid.resource.Event;
|
||||||
import at.bitfire.davdroid.webdav.DavProp.Comp;
|
import at.bitfire.davdroid.webdav.DavProp.Comp;
|
||||||
import ch.boye.httpclientandroidlib.Header;
|
import ch.boye.httpclientandroidlib.Header;
|
||||||
@ -79,7 +80,7 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// location of this resource
|
// location of this resource
|
||||||
@Getter protected URI location;
|
@Getter protected URL 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<String>(),
|
protected Set<String> capabilities = new HashSet<String>(),
|
||||||
@ -99,18 +100,18 @@ public class WebDavResource {
|
|||||||
protected HttpClientContext context;
|
protected HttpClientContext context;
|
||||||
|
|
||||||
|
|
||||||
public WebDavResource(CloseableHttpClient httpClient, URI baseURL) throws URISyntaxException {
|
public WebDavResource(CloseableHttpClient httpClient, URL baseURL) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
location = baseURL.normalize();
|
location = baseURL;
|
||||||
|
|
||||||
context = HttpClientContext.create();
|
context = HttpClientContext.create();
|
||||||
context.setCredentialsProvider(new BasicCredentialsProvider());
|
context.setCredentialsProvider(new BasicCredentialsProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(CloseableHttpClient httpClient, URI baseURL, String username, String password, boolean preemptive) throws URISyntaxException {
|
public WebDavResource(CloseableHttpClient httpClient, URL baseURL, String username, String password, boolean preemptive) {
|
||||||
this(httpClient, baseURL);
|
this(httpClient, baseURL);
|
||||||
|
|
||||||
HttpHost host = new HttpHost(baseURL.getHost(), baseURL.getPort(), baseURL.getScheme());
|
HttpHost host = new HttpHost(baseURL.getHost(), baseURL.getPort(), baseURL.getProtocol());
|
||||||
context.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
|
context.getCredentialsProvider().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
|
||||||
|
|
||||||
if (preemptive) {
|
if (preemptive) {
|
||||||
@ -129,17 +130,17 @@ public class WebDavResource {
|
|||||||
location = parent.location;
|
location = parent.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected WebDavResource(WebDavResource parent, URI uri) {
|
protected WebDavResource(WebDavResource parent, URL url) {
|
||||||
this(parent);
|
this(parent);
|
||||||
location = uri;
|
location = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(WebDavResource parent, String member) {
|
public WebDavResource(WebDavResource parent, String member) throws MalformedURLException {
|
||||||
this(parent);
|
this(parent);
|
||||||
location = parent.location.resolve(URIUtils.sanitize(member));
|
location = new URL(parent.location, URLUtils.sanitize(member));
|
||||||
}
|
}
|
||||||
|
|
||||||
public WebDavResource(WebDavResource parent, String member, String ETag) {
|
public WebDavResource(WebDavResource parent, String member, String ETag) throws MalformedURLException {
|
||||||
this(parent, member);
|
this(parent, member);
|
||||||
properties.put(Property.ETAG, ETag);
|
properties.put(Property.ETAG, ETag);
|
||||||
}
|
}
|
||||||
@ -148,8 +149,8 @@ public class WebDavResource {
|
|||||||
|
|
||||||
/* feature detection */
|
/* feature detection */
|
||||||
|
|
||||||
public void options() throws IOException, HttpException {
|
public void options() throws URISyntaxException, IOException, HttpException {
|
||||||
HttpOptions options = new HttpOptions(location);
|
HttpOptions options = new HttpOptions(location.toURI());
|
||||||
CloseableHttpResponse response = httpClient.execute(options, context);
|
CloseableHttpResponse response = httpClient.execute(options, context);
|
||||||
try {
|
try {
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
@ -178,7 +179,7 @@ public class WebDavResource {
|
|||||||
/* file hierarchy methods */
|
/* file hierarchy methods */
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
String[] names = StringUtils.split(location.getRawPath(), "/");
|
String[] names = StringUtils.split(location.getPath(), "/");
|
||||||
return names[names.length - 1];
|
return names[names.length - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,13 +253,13 @@ public class WebDavResource {
|
|||||||
|
|
||||||
/* collection operations */
|
/* collection operations */
|
||||||
|
|
||||||
public void propfind(HttpPropfind.Mode mode) throws IOException, DavException, HttpException {
|
public void propfind(HttpPropfind.Mode mode) throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
CloseableHttpResponse response = null;
|
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.toURI(), mode);
|
||||||
response = httpClient.execute(propfind, context);
|
response = httpClient.execute(propfind, context);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
@ -281,7 +282,7 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void multiGet(DavMultiget.Type type, String[] names) throws IOException, DavException, HttpException {
|
public void multiGet(DavMultiget.Type type, String[] names) throws URISyntaxException, IOException, DavException, HttpException {
|
||||||
CloseableHttpResponse response = null;
|
CloseableHttpResponse response = null;
|
||||||
|
|
||||||
// processMultiStatus() requires knowledge of the actual content location,
|
// processMultiStatus() requires knowledge of the actual content location,
|
||||||
@ -290,7 +291,7 @@ public class WebDavResource {
|
|||||||
// build multi-get XML request
|
// build multi-get XML request
|
||||||
List<String> hrefs = new LinkedList<String>();
|
List<String> hrefs = new LinkedList<String>();
|
||||||
for (String name : names)
|
for (String name : names)
|
||||||
hrefs.add(location.resolve(name).getRawPath());
|
hrefs.add(new URL(location, name).getPath());
|
||||||
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0]));
|
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0]));
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
@ -303,7 +304,7 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// submit REPORT request
|
// submit REPORT request
|
||||||
HttpReport report = new HttpReport(location, writer.toString());
|
HttpReport report = new HttpReport(location.toURI(), writer.toString());
|
||||||
response = httpClient.execute(report, context);
|
response = httpClient.execute(report, context);
|
||||||
|
|
||||||
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
if (response.getStatusLine().getStatusCode()/100 == 3) {
|
||||||
@ -330,8 +331,8 @@ public class WebDavResource {
|
|||||||
|
|
||||||
/* resource operations */
|
/* resource operations */
|
||||||
|
|
||||||
public void get(String acceptedType) throws IOException, HttpException, DavException {
|
public void get(String acceptedType) throws URISyntaxException, IOException, HttpException, DavException {
|
||||||
HttpGet get = new HttpGet(location);
|
HttpGet get = new HttpGet(location.toURI());
|
||||||
get.addHeader("Accept", acceptedType);
|
get.addHeader("Accept", acceptedType);
|
||||||
|
|
||||||
CloseableHttpResponse response = httpClient.execute(get, context);
|
CloseableHttpResponse response = httpClient.execute(get, context);
|
||||||
@ -349,8 +350,8 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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 IOException, HttpException {
|
public String put(byte[] data, PutMode mode) throws URISyntaxException, IOException, HttpException {
|
||||||
HttpPut put = new HttpPut(location);
|
HttpPut put = new HttpPut(location.toURI());
|
||||||
put.setEntity(new ByteArrayEntity(data));
|
put.setEntity(new ByteArrayEntity(data));
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
@ -379,8 +380,8 @@ public class WebDavResource {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() throws IOException, HttpException {
|
public void delete() throws URISyntaxException, IOException, HttpException {
|
||||||
HttpDelete delete = new HttpDelete(location);
|
HttpDelete delete = new HttpDelete(location.toURI());
|
||||||
|
|
||||||
if (getETag() != null)
|
if (getETag() != null)
|
||||||
delete.addHeader("If-Match", getETag());
|
delete.addHeader("If-Match", getETag());
|
||||||
@ -404,9 +405,9 @@ public class WebDavResource {
|
|||||||
if (contentLocationHdr != null)
|
if (contentLocationHdr != null)
|
||||||
try {
|
try {
|
||||||
// Content-Location was set, update location correspondingly
|
// Content-Location was set, update location correspondingly
|
||||||
location = location.resolve(new URI(contentLocationHdr.getValue()));
|
location = new URL(location, contentLocationHdr.getValue());
|
||||||
Log.d(TAG, "Set Content-Location to " + location);
|
Log.d(TAG, "Set Content-Location to " + location);
|
||||||
} catch (URISyntaxException e) {
|
} catch (MalformedURLException e) {
|
||||||
Log.w(TAG, "Ignoring invalid Content-Location", e);
|
Log.w(TAG, "Ignoring invalid Content-Location", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,9 +456,9 @@ public class WebDavResource {
|
|||||||
|
|
||||||
// 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;
|
URL href;
|
||||||
try {
|
try {
|
||||||
href = location.resolve(URIUtils.sanitize(singleResponse.getHref().href));
|
href = new URL(location, URLUtils.sanitize(singleResponse.getHref().href));
|
||||||
} catch(IllegalArgumentException ex) {
|
} catch(IllegalArgumentException ex) {
|
||||||
Log.w(TAG, "Ignoring illegal member URI in multi-status response", ex);
|
Log.w(TAG, "Ignoring illegal member URI in multi-status response", ex);
|
||||||
continue;
|
continue;
|
||||||
@ -499,10 +500,10 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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, URLUtils.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, URLUtils.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());
|
||||||
@ -511,7 +512,7 @@ public class WebDavResource {
|
|||||||
if (prop.resourcetype.getCollection() != null) {
|
if (prop.resourcetype.getCollection() != null) {
|
||||||
properties.put(Property.IS_COLLECTION, "1");
|
properties.put(Property.IS_COLLECTION, "1");
|
||||||
// is a collection, ensure trailing slash
|
// is a collection, ensure trailing slash
|
||||||
href = URIUtils.ensureTrailingSlash(href);
|
href = URLUtils.ensureTrailingSlash(href);
|
||||||
}
|
}
|
||||||
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");
|
||||||
@ -558,7 +559,7 @@ public class WebDavResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) || URLUtils.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;
|
||||||
|
@ -1,19 +1,34 @@
|
|||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ezvcard.VCardVersion;
|
|
||||||
import android.test.InstrumentationTestCase;
|
import android.test.InstrumentationTestCase;
|
||||||
import at.bitfire.davdroid.resource.DavResourceFinder;
|
import at.bitfire.davdroid.resource.DavResourceFinder;
|
||||||
import at.bitfire.davdroid.resource.ServerInfo;
|
import at.bitfire.davdroid.resource.ServerInfo;
|
||||||
import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo;
|
import at.bitfire.davdroid.resource.ServerInfo.ResourceInfo;
|
||||||
import at.bitfire.davdroid.test.Constants;
|
import at.bitfire.davdroid.test.Constants;
|
||||||
|
import ezvcard.VCardVersion;
|
||||||
|
|
||||||
public class DavResourceFinderTest extends InstrumentationTestCase {
|
public class DavResourceFinderTest extends InstrumentationTestCase {
|
||||||
|
|
||||||
public void testFindResources() throws Exception {
|
DavResourceFinder finder;
|
||||||
ServerInfo info = new ServerInfo(Constants.ROBOHYDRA_BASE, "test", "test", true);
|
|
||||||
DavResourceFinder.findResources(getInstrumentation().getContext(), info);
|
@Override
|
||||||
|
protected void setUp() {
|
||||||
|
finder = new DavResourceFinder(getInstrumentation().getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws IOException {
|
||||||
|
finder.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void testFindResourcesRobohydra() throws Exception {
|
||||||
|
ServerInfo info = new ServerInfo(new URI(Constants.ROBOHYDRA_BASE), "test", "test", true);
|
||||||
|
finder.findResources(info);
|
||||||
|
|
||||||
// CardDAV
|
// CardDAV
|
||||||
assertTrue(info.isCardDAV());
|
assertTrue(info.isCardDAV());
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
package at.bitfire.davdroid.test;
|
package at.bitfire.davdroid.test;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URL;
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
public static final String ROBOHYDRA_BASE = "http://10.0.0.11:3000/";
|
public static final String ROBOHYDRA_BASE = "http://10.0.0.11:3000/";
|
||||||
|
|
||||||
public static URI roboHydra;
|
public static URL roboHydra;
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
roboHydra = new URI(ROBOHYDRA_BASE);
|
roboHydra = new URL(ROBOHYDRA_BASE);
|
||||||
} catch(URISyntaxException e) {
|
} catch(MalformedURLException e) {
|
||||||
Log.wtf("davdroid.test.Constants", "Invalid RoboHydra base URL");
|
Log.wtf("davdroid.test.Constants", "Invalid RoboHydra base URL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* Copyright (c) 2014 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.davdroid.test;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
import at.bitfire.davdroid.URIUtils;
|
|
||||||
|
|
||||||
public class URIUtilsTest extends TestCase {
|
|
||||||
|
|
||||||
public void testEnsureTrailingSlash() throws URISyntaxException {
|
|
||||||
assertEquals("/test/", URIUtils.ensureTrailingSlash("/test"));
|
|
||||||
assertEquals("/test/", URIUtils.ensureTrailingSlash("/test/"));
|
|
||||||
|
|
||||||
String withoutSlash = "http://www.test.at/dav/collection",
|
|
||||||
withSlash = withoutSlash + "/";
|
|
||||||
assertEquals(new URI(withSlash), URIUtils.ensureTrailingSlash(new URI(withoutSlash)));
|
|
||||||
assertEquals(new URI(withSlash), URIUtils.ensureTrailingSlash(new URI(withSlash)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testSanitize() {
|
|
||||||
assertNull(URIUtils.sanitize(null));
|
|
||||||
|
|
||||||
// escape "@"
|
|
||||||
assertEquals("https://my%40server/my%40email.com/dir", URIUtils.sanitize("https://my@server/my@email.com/dir"));
|
|
||||||
assertEquals("http://my%40server/my%40email.com/dir", URIUtils.sanitize("http://my@server/my@email.com/dir"));
|
|
||||||
assertEquals("//my%40server/my%40email.com/dir", URIUtils.sanitize("//my@server/my@email.com/dir"));
|
|
||||||
assertEquals("/my%40email.com/dir", URIUtils.sanitize("/my@email.com/dir"));
|
|
||||||
assertEquals("my%40email.com/dir", URIUtils.sanitize("my@email.com/dir"));
|
|
||||||
|
|
||||||
// escape ":" in path but not as port separator
|
|
||||||
assertEquals("https://www.test.at:80/my%3afile.vcf", URIUtils.sanitize("https://www.test.at:80/my:file.vcf"));
|
|
||||||
assertEquals("http://www.test.at:80/my%3afile.vcf", URIUtils.sanitize("http://www.test.at:80/my:file.vcf"));
|
|
||||||
assertEquals("//www.test.at:80/my%3afile.vcf", URIUtils.sanitize("//www.test.at:80/my:file.vcf"));
|
|
||||||
assertEquals("/my%3afile.vcf", URIUtils.sanitize("/my:file.vcf"));
|
|
||||||
assertEquals("my%3afile.vcf", URIUtils.sanitize("my:file.vcf"));
|
|
||||||
|
|
||||||
// keep literal IPv6 addresses (only in host name)
|
|
||||||
assertEquals("https://[1:2::1]/", URIUtils.sanitize("https://[1:2::1]/"));
|
|
||||||
assertEquals("/%5b1%3a2%3a%3a1%5d/", URIUtils.sanitize("/[1:2::1]/"));
|
|
||||||
}
|
|
||||||
}
|
|
48
test/src/at/bitfire/davdroid/test/URLUtilsTest.java
Normal file
48
test/src/at/bitfire/davdroid/test/URLUtilsTest.java
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2014 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.davdroid.test;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import at.bitfire.davdroid.URLUtils;
|
||||||
|
|
||||||
|
public class URLUtilsTest extends TestCase {
|
||||||
|
|
||||||
|
public void testEnsureTrailingSlash() throws Exception {
|
||||||
|
assertEquals("/test/", URLUtils.ensureTrailingSlash("/test"));
|
||||||
|
assertEquals("/test/", URLUtils.ensureTrailingSlash("/test/"));
|
||||||
|
|
||||||
|
String withoutSlash = "http://www.test.at/dav/collection",
|
||||||
|
withSlash = withoutSlash + "/";
|
||||||
|
assertEquals(new URL(withSlash), URLUtils.ensureTrailingSlash(new URL(withoutSlash)));
|
||||||
|
assertEquals(new URL(withSlash), URLUtils.ensureTrailingSlash(new URL(withSlash)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSanitize() {
|
||||||
|
assertNull(URLUtils.sanitize(null));
|
||||||
|
|
||||||
|
// escape "@"
|
||||||
|
assertEquals("https://my%40server/my%40email.com/dir", URLUtils.sanitize("https://my@server/my@email.com/dir"));
|
||||||
|
assertEquals("http://my%40server/my%40email.com/dir", URLUtils.sanitize("http://my@server/my@email.com/dir"));
|
||||||
|
assertEquals("//my%40server/my%40email.com/dir", URLUtils.sanitize("//my@server/my@email.com/dir"));
|
||||||
|
assertEquals("/my%40email.com/dir", URLUtils.sanitize("/my@email.com/dir"));
|
||||||
|
assertEquals("my%40email.com/dir", URLUtils.sanitize("my@email.com/dir"));
|
||||||
|
|
||||||
|
// escape ":" in path but not as port separator
|
||||||
|
assertEquals("https://www.test.at:80/my%3afile.vcf", URLUtils.sanitize("https://www.test.at:80/my:file.vcf"));
|
||||||
|
assertEquals("http://www.test.at:80/my%3afile.vcf", URLUtils.sanitize("http://www.test.at:80/my:file.vcf"));
|
||||||
|
assertEquals("//www.test.at:80/my%3afile.vcf", URLUtils.sanitize("//www.test.at:80/my:file.vcf"));
|
||||||
|
assertEquals("/my%3afile.vcf", URLUtils.sanitize("/my:file.vcf"));
|
||||||
|
assertEquals("my%3afile.vcf", URLUtils.sanitize("my:file.vcf"));
|
||||||
|
|
||||||
|
// keep literal IPv6 addresses (only in host name)
|
||||||
|
assertEquals("https://[1:2::1]/", URLUtils.sanitize("https://[1:2::1]/"));
|
||||||
|
assertEquals("/%5b1%3a2%3a%3a1%5d/", URLUtils.sanitize("/[1:2::1]/"));
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
package at.bitfire.davdroid.webdav;
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import at.bitfire.davdroid.test.Constants;
|
import at.bitfire.davdroid.test.Constants;
|
||||||
@ -34,7 +35,7 @@ public class DavRedirectStrategyTest extends TestCase {
|
|||||||
// happy cases
|
// happy cases
|
||||||
|
|
||||||
public void testNonRedirection() throws Exception {
|
public void testNonRedirection() throws Exception {
|
||||||
HttpUriRequest request = new HttpOptions(Constants.roboHydra);
|
HttpUriRequest request = new HttpOptions(Constants.roboHydra.toURI());
|
||||||
HttpResponse response = httpClient.execute(request);
|
HttpResponse response = httpClient.execute(request);
|
||||||
assertFalse(strategy.isRedirected(request, response, null));
|
assertFalse(strategy.isRedirected(request, response, null));
|
||||||
}
|
}
|
||||||
@ -43,12 +44,12 @@ public class DavRedirectStrategyTest extends TestCase {
|
|||||||
final String newLocation = "/new-location";
|
final String newLocation = "/new-location";
|
||||||
|
|
||||||
HttpContext context = HttpClientContext.create();
|
HttpContext context = HttpClientContext.create();
|
||||||
HttpUriRequest request = new HttpOptions(Constants.roboHydra.resolve("redirect/301?to=" + newLocation));
|
HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/301?to=" + newLocation).toURI());
|
||||||
HttpResponse response = httpClient.execute(request, context);
|
HttpResponse response = httpClient.execute(request, context);
|
||||||
assertTrue(strategy.isRedirected(request, response, context));
|
assertTrue(strategy.isRedirected(request, response, context));
|
||||||
|
|
||||||
HttpUriRequest redirected = strategy.getRedirect(request, response, context);
|
HttpUriRequest redirected = strategy.getRedirect(request, response, context);
|
||||||
assertEquals(Constants.roboHydra.resolve(newLocation), redirected.getURI());
|
assertEquals(new URL(Constants.roboHydra, newLocation).toURI(), redirected.getURI());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -56,18 +57,18 @@ public class DavRedirectStrategyTest extends TestCase {
|
|||||||
|
|
||||||
public void testMissingLocation() throws Exception {
|
public void testMissingLocation() throws Exception {
|
||||||
HttpContext context = HttpClientContext.create();
|
HttpContext context = HttpClientContext.create();
|
||||||
HttpUriRequest request = new HttpOptions(Constants.roboHydra.resolve("redirect/without-location"));
|
HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/without-location").toURI());
|
||||||
HttpResponse response = httpClient.execute(request, context);
|
HttpResponse response = httpClient.execute(request, context);
|
||||||
assertFalse(strategy.isRedirected(request, response, context));
|
assertFalse(strategy.isRedirected(request, response, context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRelativeLocation() throws Exception {
|
public void testRelativeLocation() throws Exception {
|
||||||
HttpContext context = HttpClientContext.create();
|
HttpContext context = HttpClientContext.create();
|
||||||
HttpUriRequest request = new HttpOptions(Constants.roboHydra.resolve("redirect/relative"));
|
HttpUriRequest request = new HttpOptions(new URL(Constants.roboHydra, "redirect/relative").toURI());
|
||||||
HttpResponse response = httpClient.execute(request, context);
|
HttpResponse response = httpClient.execute(request, context);
|
||||||
assertTrue(strategy.isRedirected(request, response, context));
|
assertTrue(strategy.isRedirected(request, response, context));
|
||||||
|
|
||||||
HttpUriRequest redirected = strategy.getRedirect(request, response, context);
|
HttpUriRequest redirected = strategy.getRedirect(request, response, context);
|
||||||
assertEquals(Constants.roboHydra.resolve("/new/location"), redirected.getURI());
|
assertEquals(new URL(Constants.roboHydra, "/new/location").toURI(), redirected.getURI());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,8 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.webdav;
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URL;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -48,26 +46,26 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
assetMgr = getInstrumentation().getContext().getResources().getAssets();
|
||||||
|
|
||||||
baseDAV = new WebDavResource(httpClient, Constants.roboHydra.resolve("/dav/"));
|
baseDAV = new WebDavResource(httpClient, new URL(Constants.roboHydra, "/dav/"));
|
||||||
|
|
||||||
simpleFile = new WebDavResource(httpClient, new URI(Constants.ROBOHYDRA_BASE + "assets/test.random"));
|
simpleFile = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "assets/test.random"));
|
||||||
|
|
||||||
davCollection = new WebDavResource(httpClient, new URI(Constants.ROBOHYDRA_BASE + "dav/"));
|
davCollection = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "dav/"));
|
||||||
davNonExistingFile = new WebDavResource(davCollection, "collection/new.file");
|
davNonExistingFile = new WebDavResource(davCollection, "collection/new.file");
|
||||||
davExistingFile = new WebDavResource(davCollection, "collection/existing.file");
|
davExistingFile = new WebDavResource(davCollection, "collection/existing.file");
|
||||||
|
|
||||||
davInvalid = new WebDavResource(httpClient, new URI(Constants.ROBOHYDRA_BASE + "dav-invalid/"));
|
davInvalid = new WebDavResource(httpClient, new URL(Constants.ROBOHYDRA_BASE + "dav-invalid/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void tearDown() throws IOException {
|
protected void tearDown() throws Exception {
|
||||||
httpClient.close();
|
httpClient.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* test feature detection */
|
/* test feature detection */
|
||||||
|
|
||||||
public void testOptions() throws URISyntaxException, IOException, HttpException {
|
public void testOptions() throws Exception {
|
||||||
String[] davMethods = new String[] { "PROPFIND", "GET", "PUT", "DELETE", "REPORT" },
|
String[] davMethods = new String[] { "PROPFIND", "GET", "PUT", "DELETE", "REPORT" },
|
||||||
davCapabilities = new String[] { "addressbook", "calendar-access" };
|
davCapabilities = new String[] { "addressbook", "calendar-access" };
|
||||||
|
|
||||||
@ -79,7 +77,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
assert(capable.supportsDAV(capability));
|
assert(capable.supportsDAV(capability));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindCurrentUserPrincipal() throws IOException, HttpException, DavException {
|
public void testPropfindCurrentUserPrincipal() throws Exception {
|
||||||
davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
||||||
assertEquals("/dav/principals/users/test", davCollection.getCurrentUserPrincipal());
|
assertEquals("/dav/principals/users/test", davCollection.getCurrentUserPrincipal());
|
||||||
|
|
||||||
@ -92,14 +90,14 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
assertNull(simpleFile.getCurrentUserPrincipal());
|
assertNull(simpleFile.getCurrentUserPrincipal());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindHomeSets() throws IOException, HttpException, DavException {
|
public void testPropfindHomeSets() throws Exception {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
WebDavResource dav = new WebDavResource(davCollection, "principals/users/test");
|
||||||
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
dav.propfind(HttpPropfind.Mode.HOME_SETS);
|
||||||
assertEquals("/dav/addressbooks/test/", dav.getAddressbookHomeSet());
|
assertEquals("/dav/addressbooks/test/", dav.getAddressbookHomeSet());
|
||||||
assertEquals("/dav/calendars/test/", dav.getCalendarHomeSet());
|
assertEquals("/dav/calendars/test/", dav.getCalendarHomeSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindAddressBooks() throws IOException, HttpException, DavException {
|
public void testPropfindAddressBooks() throws Exception {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test");
|
WebDavResource dav = new WebDavResource(davCollection, "addressbooks/test");
|
||||||
dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
|
dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
|
||||||
assertEquals(2, dav.getMembers().size());
|
assertEquals(2, dav.getMembers().size());
|
||||||
@ -112,7 +110,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindCalendars() throws IOException, HttpException, DavException {
|
public void testPropfindCalendars() throws Exception {
|
||||||
WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
|
WebDavResource dav = new WebDavResource(davCollection, "calendars/test");
|
||||||
dav.propfind(Mode.CALDAV_COLLECTIONS);
|
dav.propfind(Mode.CALDAV_COLLECTIONS);
|
||||||
assertEquals(3, dav.getMembers().size());
|
assertEquals(3, dav.getMembers().size());
|
||||||
@ -126,7 +124,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPropfindTrailingSlashes() throws IOException, HttpException, DavException {
|
public void testPropfindTrailingSlashes() throws Exception {
|
||||||
final String principalOK = "/principals/ok";
|
final String principalOK = "/principals/ok";
|
||||||
|
|
||||||
String requestPaths[] = {
|
String requestPaths[] = {
|
||||||
@ -146,22 +144,22 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
/* test normal HTTP/WebDAV */
|
/* test normal HTTP/WebDAV */
|
||||||
|
|
||||||
public void testPropfindRedirection() throws URISyntaxException, IOException, DavException, HttpException {
|
public void testPropfindRedirection() throws Exception {
|
||||||
// PROPFIND redirection
|
// PROPFIND redirection
|
||||||
WebDavResource redirected = new WebDavResource(baseDAV, "/redirect/301?to=/dav/");
|
WebDavResource redirected = new WebDavResource(baseDAV, "/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());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGet() throws URISyntaxException, IOException, HttpException, DavException {
|
public void testGet() throws Exception {
|
||||||
simpleFile.get("*/*");
|
simpleFile.get("*/*");
|
||||||
@Cleanup InputStream is = assetMgr.open("test.random", AssetManager.ACCESS_STREAMING);
|
@Cleanup InputStream is = assetMgr.open("test.random", AssetManager.ACCESS_STREAMING);
|
||||||
byte[] expected = IOUtils.toByteArray(is);
|
byte[] expected = IOUtils.toByteArray(is);
|
||||||
assertTrue(Arrays.equals(expected, simpleFile.getContent()));
|
assertTrue(Arrays.equals(expected, simpleFile.getContent()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetHttpsWithSni() throws URISyntaxException, HttpException, IOException, DavException {
|
public void testGetHttpsWithSni() throws Exception {
|
||||||
WebDavResource file = new WebDavResource(httpClient, new URI("https://sni.velox.ch"));
|
WebDavResource file = new WebDavResource(httpClient, new URL("https://sni.velox.ch"));
|
||||||
|
|
||||||
boolean sniWorking = false;
|
boolean sniWorking = false;
|
||||||
try {
|
try {
|
||||||
@ -173,7 +171,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
assertTrue(sniWorking);
|
assertTrue(sniWorking);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMultiGet() throws DavException, IOException, HttpException {
|
public void testMultiGet() throws Exception {
|
||||||
WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf");
|
WebDavResource davAddressBook = new WebDavResource(davCollection, "addressbooks/default.vcf");
|
||||||
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2.vcf" });
|
davAddressBook.multiGet(DavMultiget.Type.ADDRESS_BOOK, new String[] { "1.vcf", "2.vcf" });
|
||||||
assertEquals(2, davAddressBook.getMembers().size());
|
assertEquals(2, davAddressBook.getMembers().size());
|
||||||
@ -182,7 +180,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPutAddDontOverwrite() throws IOException, HttpException {
|
public void testPutAddDontOverwrite() throws Exception {
|
||||||
// should succeed on a non-existing file
|
// should succeed on a non-existing file
|
||||||
assertEquals("has-just-been-created", davNonExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE));
|
assertEquals("has-just-been-created", davNonExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE));
|
||||||
|
|
||||||
@ -194,7 +192,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testPutUpdateDontOverwrite() throws IOException, HttpException {
|
public void testPutUpdateDontOverwrite() throws Exception {
|
||||||
// should succeed on an existing file
|
// should succeed on an existing file
|
||||||
assertEquals("has-just-been-updated", davExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE));
|
assertEquals("has-just-been-updated", davExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE));
|
||||||
|
|
||||||
@ -206,7 +204,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testDelete() throws IOException, HttpException {
|
public void testDelete() throws Exception {
|
||||||
// should succeed on an existing file
|
// should succeed on an existing file
|
||||||
davExistingFile.delete();
|
davExistingFile.delete();
|
||||||
|
|
||||||
@ -224,13 +222,13 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
|
|
||||||
/* special test */
|
/* special test */
|
||||||
|
|
||||||
public void testInvalidURLs() throws IOException, HttpException, DavException {
|
public void testInvalidURLs() throws Exception {
|
||||||
WebDavResource dav = new WebDavResource(davInvalid, "addressbooks/user%40domain/");
|
WebDavResource dav = new WebDavResource(davInvalid, "addressbooks/user%40domain/");
|
||||||
dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
|
dav.propfind(HttpPropfind.Mode.CARDDAV_COLLECTIONS);
|
||||||
List<WebDavResource> members = dav.getMembers();
|
List<WebDavResource> members = dav.getMembers();
|
||||||
assertEquals(2, members.size());
|
assertEquals(2, members.size());
|
||||||
assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());
|
assertEquals(Constants.ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());
|
||||||
assertEquals("HTTPS://example.com/user%40domain/absolute-url.vcf/", members.get(1).getLocation().toString());
|
assertEquals("https://example.com/user%40domain/absolute-url.vcf/", members.get(1).getLocation().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user