mirror of
https://github.com/etesync/android
synced 2025-02-25 13:52:30 +00:00
Bug fixes, refactoring, version bump
* don't manually decode URLs for comparing (because we now always use encoded URLs) * .ics: VERSION before PRODID * refactoring: move WebDavResource.httpClient to new singleton DavHttpClient * close input streams in propfind/multi-get * version bumpt to 0.4.1
This commit is contained in:
parent
66d6ec34d1
commit
1fc2679bd4
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="at.bitfire.davdroid"
|
||||
android:versionCode="13"
|
||||
android:versionName="0.4-alpha" >
|
||||
android:versionCode="14"
|
||||
android:versionName="0.4.1-alpha" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="14"
|
||||
|
@ -9,7 +9,7 @@ package at.bitfire.davdroid;
|
||||
|
||||
public class Constants {
|
||||
public static final String
|
||||
APP_VERSION = "0.4-alpha",
|
||||
APP_VERSION = "0.4.1-alpha",
|
||||
|
||||
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
||||
|
||||
|
@ -10,15 +10,6 @@ import android.util.Log;
|
||||
public class URIUtils {
|
||||
private static final String TAG = "davdroid.URIUtils";
|
||||
|
||||
public static boolean isSame(URI a, URI b) {
|
||||
try {
|
||||
a = new URI(a.getScheme(), null, a.getHost(), a.getPort(), sanitize(a.getPath()), sanitize(a.getQuery()), null);
|
||||
b = new URI(b.getScheme(), null, b.getHost(), b.getPort(), sanitize(b.getPath()), sanitize(b.getQuery()), null);
|
||||
return a.equals(b);
|
||||
} catch (URISyntaxException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// handles invalid URLs/paths as good as possible
|
||||
public static String sanitize(String original) {
|
||||
|
@ -148,8 +148,8 @@ public class Event extends Resource {
|
||||
@Override
|
||||
public String toEntity() {
|
||||
net.fortuna.ical4j.model.Calendar ical = new net.fortuna.ical4j.model.Calendar();
|
||||
ical.getProperties().add(new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + "//EN"));
|
||||
ical.getProperties().add(Version.VERSION_2_0);
|
||||
ical.getProperties().add(new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + "//EN"));
|
||||
|
||||
VEvent event = new VEvent();
|
||||
PropertyList props = event.getProperties();
|
||||
|
58
src/at/bitfire/davdroid/webdav/DavHttpClient.java
Normal file
58
src/at/bitfire/davdroid/webdav/DavHttpClient.java
Normal file
@ -0,0 +1,58 @@
|
||||
package at.bitfire.davdroid.webdav;
|
||||
|
||||
import org.apache.http.client.params.HttpClientParams;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.conn.scheme.PlainSocketFactory;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.CoreProtocolPNames;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.webdav.GzipDecompressingEntity;
|
||||
import at.bitfire.davdroid.webdav.TlsSniSocketFactory;
|
||||
|
||||
// see AndroidHttpClient
|
||||
|
||||
|
||||
public class DavHttpClient extends DefaultHttpClient {
|
||||
private static DavHttpClient httpClient = null;
|
||||
|
||||
private DavHttpClient(ClientConnectionManager connectionManager, HttpParams params) {
|
||||
super(connectionManager, params);
|
||||
}
|
||||
|
||||
|
||||
public static synchronized DefaultHttpClient getInstance() {
|
||||
if (httpClient == null) {
|
||||
HttpParams params = new BasicHttpParams();
|
||||
params.setParameter(CoreProtocolPNames.USER_AGENT, "DAVdroid/" + Constants.APP_VERSION);
|
||||
|
||||
// use defaults of AndroidHttpClient
|
||||
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
|
||||
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
|
||||
HttpConnectionParams.setSocketBufferSize(params, 8192);
|
||||
HttpConnectionParams.setStaleCheckingEnabled(params, false);
|
||||
|
||||
// don't allow redirections
|
||||
HttpClientParams.setRedirecting(params, false);
|
||||
|
||||
// use our own, SNI-capable LayeredSocketFactory for https://
|
||||
SchemeRegistry schemeRegistry = new SchemeRegistry();
|
||||
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
|
||||
schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), 443));
|
||||
|
||||
httpClient = new DavHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params);
|
||||
|
||||
// allow gzip compression
|
||||
GzipDecompressingEntity.enable(httpClient);
|
||||
}
|
||||
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,7 @@ import org.apache.http.HttpException;
|
||||
public class InvalidDavResponseException extends HttpException {
|
||||
private static final long serialVersionUID = -2118919144443165706L;
|
||||
|
||||
public InvalidDavResponseException() {
|
||||
super("Invalid DAV response");
|
||||
public InvalidDavResponseException(String message) {
|
||||
super("Invalid DAV response: " + message);
|
||||
}
|
||||
}
|
||||
|
@ -39,18 +39,13 @@ import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpOptions;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.params.ClientPNames;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.message.BasicLineParser;
|
||||
import org.apache.http.params.CoreProtocolPNames;
|
||||
import org.simpleframework.xml.Serializer;
|
||||
import org.simpleframework.xml.core.Persister;
|
||||
|
||||
import android.util.Log;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.URIUtils;
|
||||
|
||||
|
||||
@ -91,7 +86,7 @@ public class WebDavResource {
|
||||
// content (available after GET)
|
||||
@Getter protected InputStream content;
|
||||
|
||||
protected DefaultHttpClient client;
|
||||
protected DefaultHttpClient client = DavHttpClient.getInstance();
|
||||
|
||||
|
||||
public WebDavResource(URI baseURL, boolean trailingSlash) throws URISyntaxException {
|
||||
@ -99,20 +94,6 @@ public class WebDavResource {
|
||||
|
||||
if (trailingSlash && !location.getRawPath().endsWith("/"))
|
||||
location = new URI(location.getScheme(), location.getSchemeSpecificPart() + "/", null);
|
||||
|
||||
// create new HTTP client
|
||||
client = new DefaultHttpClient();
|
||||
client.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "DAVdroid/" + Constants.APP_VERSION);
|
||||
|
||||
// use our own, SNI-capable LayeredSocketFactory for https://
|
||||
SchemeRegistry schemeRegistry = client.getConnectionManager().getSchemeRegistry();
|
||||
schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(), 443));
|
||||
|
||||
// allow gzip compression
|
||||
GzipDecompressingEntity.enable(client);
|
||||
|
||||
// redirections
|
||||
client.getParams().setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false);
|
||||
}
|
||||
|
||||
public WebDavResource(URI baseURL, String username, String password, boolean preemptive, boolean trailingSlash) throws URISyntaxException {
|
||||
@ -130,12 +111,10 @@ public class WebDavResource {
|
||||
|
||||
protected WebDavResource(WebDavResource parent, URI uri) {
|
||||
location = uri;
|
||||
client = parent.client;
|
||||
}
|
||||
|
||||
public WebDavResource(WebDavResource parent, String member) {
|
||||
location = parent.location.resolve(URIUtils.sanitize(member));
|
||||
client = parent.client;
|
||||
}
|
||||
|
||||
public WebDavResource(WebDavResource parent, String member, boolean trailingSlash) {
|
||||
@ -261,29 +240,36 @@ public class WebDavResource {
|
||||
|
||||
/* collection operations */
|
||||
|
||||
public boolean propfind(HttpPropfind.Mode mode) throws IOException, InvalidDavResponseException, HttpException {
|
||||
public void propfind(HttpPropfind.Mode mode) throws IOException, InvalidDavResponseException, HttpException {
|
||||
HttpPropfind propfind = new HttpPropfind(location, mode);
|
||||
HttpResponse response = client.execute(propfind);
|
||||
checkResponse(response);
|
||||
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_MULTI_STATUS) {
|
||||
InputStream content = response.getEntity().getContent();
|
||||
if (content == null)
|
||||
throw new InvalidDavResponseException("Multistatus response without content");
|
||||
|
||||
// duplicate content for logging
|
||||
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
||||
InputStream is = new TeeInputStream(content, logStream);
|
||||
|
||||
DavMultistatus multistatus;
|
||||
try {
|
||||
Serializer serializer = new Persister();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
InputStream is = new TeeInputStream(response.getEntity().getContent(), baos);
|
||||
multistatus = serializer.read(DavMultistatus.class, is, false);
|
||||
|
||||
Log.d(TAG, "Received multistatus response: " + baos.toString("UTF-8"));
|
||||
} catch (Exception ex) {
|
||||
Log.w(TAG, "Invalid PROPFIND XML response", ex);
|
||||
throw new InvalidDavResponseException();
|
||||
throw new InvalidDavResponseException("Invalid PROPFIND response");
|
||||
} finally {
|
||||
Log.d(TAG, "Received multistatus response:\n" + logStream.toString("UTF-8"));
|
||||
is.close();
|
||||
content.close();
|
||||
}
|
||||
processMultiStatus(multistatus);
|
||||
return true;
|
||||
|
||||
} else
|
||||
return false;
|
||||
throw new InvalidDavResponseException("Multistatus response expected");
|
||||
}
|
||||
|
||||
public void multiGet(String[] names, MultigetType type) throws IOException, InvalidDavResponseException, HttpException {
|
||||
@ -307,7 +293,7 @@ public class WebDavResource {
|
||||
serializer.write(multiget, writer);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Couldn't create XML multi-get request", ex);
|
||||
throw new InvalidDavResponseException();
|
||||
throw new InvalidDavResponseException("Couldn't create multi-get request");
|
||||
}
|
||||
|
||||
HttpReport report = new HttpReport(location, writer.toString());
|
||||
@ -315,21 +301,30 @@ public class WebDavResource {
|
||||
checkResponse(response);
|
||||
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_MULTI_STATUS) {
|
||||
DavMultistatus multistatus;
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
InputStream is = new TeeInputStream(response.getEntity().getContent(), baos);
|
||||
multistatus = serializer.read(DavMultistatus.class, is, false);
|
||||
InputStream content = response.getEntity().getContent();
|
||||
if (content == null)
|
||||
throw new InvalidDavResponseException("Multistatus response without content");
|
||||
|
||||
Log.d(TAG, "Received multistatus response: " + baos.toString("UTF-8"));
|
||||
DavMultistatus multistatus;
|
||||
|
||||
// duplicate content for logging
|
||||
ByteArrayOutputStream logStream = new ByteArrayOutputStream();
|
||||
InputStream is = new TeeInputStream(content, logStream, true);
|
||||
|
||||
try {
|
||||
multistatus = serializer.read(DavMultistatus.class, is, false);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "Couldn't parse multi-get response", ex);
|
||||
throw new InvalidDavResponseException();
|
||||
throw new InvalidDavResponseException("Invalid multi-get response");
|
||||
} finally {
|
||||
Log.d(TAG, "Received multistatus response:\n" + logStream.toString("UTF-8"));
|
||||
is.close();
|
||||
content.close();
|
||||
}
|
||||
processMultiStatus(multistatus);
|
||||
|
||||
} else
|
||||
throw new InvalidDavResponseException();
|
||||
throw new InvalidDavResponseException("Multistatus response expected");
|
||||
}
|
||||
|
||||
|
||||
@ -393,7 +388,7 @@ public class WebDavResource {
|
||||
|
||||
// about which resource is this response?
|
||||
WebDavResource referenced = null;
|
||||
if (URIUtils.isSame(location, href)) { // -> ourselves
|
||||
if (location.equals(href)) { // -> ourselves
|
||||
referenced = this;
|
||||
|
||||
} else { // -> about a member
|
||||
|
@ -17,11 +17,6 @@ public class URIUtilsTest extends InstrumentationTestCase {
|
||||
baseURI = new URI(BASE_URI);
|
||||
}
|
||||
|
||||
|
||||
public void testIsSame() throws URISyntaxException {
|
||||
assertTrue(URIUtils.isSame(new URI(ROOT_URI + "my@email/"), new URI(ROOT_URI + "my%40email/")));
|
||||
}
|
||||
|
||||
public void testSanitize() {
|
||||
assertEquals("/my%40email.com/dir", URIUtils.sanitize("/my@email.com/dir"));
|
||||
assertEquals("my%3Afile.vcf", URIUtils.sanitize("my:file.vcf"));
|
||||
|
@ -82,10 +82,14 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testPropfindCurrentUserPrincipal() throws IOException, HttpException {
|
||||
assertTrue(davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL));
|
||||
davCollection.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
||||
assertEquals("/dav/principals/users/test", davCollection.getCurrentUserPrincipal());
|
||||
|
||||
assertFalse(simpleFile.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL));
|
||||
try {
|
||||
simpleFile.propfind(HttpPropfind.Mode.CURRENT_USER_PRINCIPAL);
|
||||
fail();
|
||||
} catch(InvalidDavResponseException ex) {
|
||||
}
|
||||
assertNull(simpleFile.getCurrentUserPrincipal());
|
||||
}
|
||||
|
||||
@ -130,10 +134,9 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
WebDavResource redirection = new WebDavResource(new URI(ROBOHYDRA_BASE + "redirect"), false);
|
||||
try {
|
||||
redirection.get();
|
||||
fail();
|
||||
} catch (HttpException e) {
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
public void testGet() throws URISyntaxException, IOException, HttpException {
|
||||
@ -160,10 +163,9 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
// should fail on an existing file
|
||||
try {
|
||||
davExistingFile.put(SAMPLE_CONTENT, PutMode.ADD_DONT_OVERWRITE);
|
||||
fail();
|
||||
} catch(PreconditionFailedException ex) {
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
public void testPutUpdateDontOverwrite() throws IOException, HttpException {
|
||||
@ -173,10 +175,9 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
// should fail on a non-existing file
|
||||
try {
|
||||
davNonExistingFile.put(SAMPLE_CONTENT, PutMode.UPDATE_DONT_OVERWRITE);
|
||||
fail();
|
||||
} catch(PreconditionFailedException ex) {
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
public void testDelete() throws IOException, HttpException {
|
||||
@ -186,10 +187,9 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
||||
// should fail on a non-existing file
|
||||
try {
|
||||
davNonExistingFile.delete();
|
||||
fail();
|
||||
} catch (NotFoundException e) {
|
||||
return;
|
||||
}
|
||||
fail();
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user