mirror of
https://github.com/etesync/android
synced 2024-12-23 23:18:46 +00:00
Support for read-only calendars (closes #126)
* relevant RFCs go into the doc/ directory for reference purposes * read-only calendar collections are set as read-only in Android * HTTP exception refactoring to mark 4xx HTTP errors as hard sync errors (numAuthExcetions/numParseExceptions) for Android sync manager * query current-user-privilege-set for resources, detect read-only resources * show read-only resources as read-only in SelectCollectionsFragment * minor refactoring (DavProp.*)
This commit is contained in:
parent
70973dcc0a
commit
a12942c606
4035
doc/rfc3744-webdav-access-control-protocol.txt
Normal file
4035
doc/rfc3744-webdav-access-control-protocol.txt
Normal file
File diff suppressed because it is too large
Load Diff
5995
doc/rfc4791-caldav.txt
Normal file
5995
doc/rfc4791-caldav.txt
Normal file
File diff suppressed because it is too large
Load Diff
7115
doc/rfc4918-webdav.txt
Normal file
7115
doc/rfc4918-webdav.txt
Normal file
File diff suppressed because it is too large
Load Diff
281
doc/rfc5397-webdav-current-principal-extension.txt
Normal file
281
doc/rfc5397-webdav-current-principal-extension.txt
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Network Working Group W. Sanchez
|
||||||
|
Request for Comments: 5397 C. Daboo
|
||||||
|
Category: Standards Track Apple Inc.
|
||||||
|
December 2008
|
||||||
|
|
||||||
|
|
||||||
|
WebDAV Current Principal Extension
|
||||||
|
|
||||||
|
Status of This Memo
|
||||||
|
|
||||||
|
This document specifies an Internet standards track protocol for the
|
||||||
|
Internet community, and requests discussion and suggestions for
|
||||||
|
improvements. Please refer to the current edition of the "Internet
|
||||||
|
Official Protocol Standards" (STD 1) for the standardization state
|
||||||
|
and status of this protocol. Distribution of this memo is unlimited.
|
||||||
|
|
||||||
|
Copyright Notice
|
||||||
|
|
||||||
|
Copyright (c) 2008 IETF Trust and the persons identified as the
|
||||||
|
document authors. All rights reserved.
|
||||||
|
|
||||||
|
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||||
|
Provisions Relating to IETF Documents
|
||||||
|
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||||
|
publication of this document. Please review these documents
|
||||||
|
carefully, as they describe your rights and restrictions with respect
|
||||||
|
to this document.
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
|
||||||
|
This specification defines a new WebDAV property that allows clients
|
||||||
|
to quickly determine the principal corresponding to the current
|
||||||
|
authenticated user.
|
||||||
|
|
||||||
|
Table of Contents
|
||||||
|
|
||||||
|
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 2
|
||||||
|
2. Conventions Used in This Document . . . . . . . . . . . . . . . 2
|
||||||
|
3. DAV:current-user-principal . . . . . . . . . . . . . . . . . . 3
|
||||||
|
4. Security Considerations . . . . . . . . . . . . . . . . . . . . 4
|
||||||
|
5. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . 4
|
||||||
|
6. Normative References . . . . . . . . . . . . . . . . . . . . . 4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Sanchez & Daboo Standards Track [Page 1]
|
||||||
|
|
||||||
|
RFC 5397 WebDAV Current Principal December 2008
|
||||||
|
|
||||||
|
|
||||||
|
1. Introduction
|
||||||
|
|
||||||
|
WebDAV [RFC4918] is an extension to HTTP [RFC2616] to support
|
||||||
|
improved document authoring capabilities. The WebDAV Access Control
|
||||||
|
Protocol ("WebDAV ACL") [RFC3744] extension adds access control
|
||||||
|
capabilities to WebDAV. It introduces the concept of a "principal"
|
||||||
|
resource, which is used to represent information about authenticated
|
||||||
|
entities on the system.
|
||||||
|
|
||||||
|
Some clients have a need to determine which [RFC3744] principal a
|
||||||
|
server is associating with the currently authenticated HTTP user.
|
||||||
|
While [RFC3744] defines a DAV:current-user-privilege-set property for
|
||||||
|
retrieving the privileges granted to that principal, there is no
|
||||||
|
recommended way to identify the principal in question, which is
|
||||||
|
necessary to perform other useful operations. For example, a client
|
||||||
|
may wish to determine which groups the current user is a member of,
|
||||||
|
or modify a property of the principal resource associated with the
|
||||||
|
current user.
|
||||||
|
|
||||||
|
The DAV:principal-match REPORT provides some useful functionality,
|
||||||
|
but there are common situations where the results from that query can
|
||||||
|
be ambiguous. For example, not only is an individual user principal
|
||||||
|
returned, but also every group principal that the user is a member
|
||||||
|
of, and there is no clear way to distinguish which is which.
|
||||||
|
|
||||||
|
This specification proposes an extension to WebDAV ACL that adds a
|
||||||
|
DAV:current-user-principal property to resources under access control
|
||||||
|
on the server. This property provides a URL to a principal resource
|
||||||
|
corresponding to the currently authenticated user. This allows a
|
||||||
|
client to "bootstrap" itself by performing additional queries on the
|
||||||
|
principal resource to obtain additional information from that
|
||||||
|
resource, which is the purpose of this extension. Note that while it
|
||||||
|
is possible for multiple URLs to refer to the same principal
|
||||||
|
resource, or for multiple principal resources to correspond to a
|
||||||
|
single principal, this specification only allows for a single http(s)
|
||||||
|
URL in the DAV:current-user-principal property. If a client wishes
|
||||||
|
to obtain alternate URLs for the principal, it can query the
|
||||||
|
principal resource for this information; it is not the purpose of
|
||||||
|
this extension to provide a complete list of such URLs, but simply to
|
||||||
|
provide a means to locate a resource which contains that (and other)
|
||||||
|
information.
|
||||||
|
|
||||||
|
2. Conventions Used in This Document
|
||||||
|
|
||||||
|
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||||
|
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||||
|
document are to be interpreted as described in [RFC2119].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Sanchez & Daboo Standards Track [Page 2]
|
||||||
|
|
||||||
|
RFC 5397 WebDAV Current Principal December 2008
|
||||||
|
|
||||||
|
|
||||||
|
When XML element types in the namespace "DAV:" are referenced in this
|
||||||
|
document outside of the context of an XML fragment, the string "DAV:"
|
||||||
|
will be prefixed to the element type names.
|
||||||
|
|
||||||
|
Processing of XML by clients and servers MUST follow the rules
|
||||||
|
defined in Section 17 of WebDAV [RFC4918].
|
||||||
|
|
||||||
|
Some of the declarations refer to XML elements defined by WebDAV
|
||||||
|
[RFC4918].
|
||||||
|
|
||||||
|
3. DAV:current-user-principal
|
||||||
|
|
||||||
|
Name: current-user-principal
|
||||||
|
|
||||||
|
Namespace: DAV:
|
||||||
|
|
||||||
|
Purpose: Indicates a URL for the currently authenticated user's
|
||||||
|
principal resource on the server.
|
||||||
|
|
||||||
|
Value: A single DAV:href or DAV:unauthenticated element.
|
||||||
|
|
||||||
|
Protected: This property is computed on a per-request basis, and
|
||||||
|
therefore is protected.
|
||||||
|
|
||||||
|
Description: The DAV:current-user-principal property contains either
|
||||||
|
a DAV:href or DAV:unauthenticated XML element. The DAV:href
|
||||||
|
element contains a URL to a principal resource corresponding to
|
||||||
|
the currently authenticated user. That URL MUST be one of the
|
||||||
|
URLs in the DAV:principal-URL or DAV:alternate-URI-set properties
|
||||||
|
defined on the principal resource and MUST be an http(s) scheme
|
||||||
|
URL. When authentication has not been done or has failed, this
|
||||||
|
property MUST contain the DAV:unauthenticated pseudo-principal.
|
||||||
|
|
||||||
|
In some cases, there may be multiple principal resources
|
||||||
|
corresponding to the same authenticated principal. In that case,
|
||||||
|
the server is free to choose any one of the principal resource
|
||||||
|
URIs for the value of the DAV:current-user-principal property.
|
||||||
|
However, servers SHOULD be consistent and use the same principal
|
||||||
|
resource URI for each authenticated principal.
|
||||||
|
|
||||||
|
COPY/MOVE behavior: This property is computed on a per-request
|
||||||
|
basis, and is thus never copied or moved.
|
||||||
|
|
||||||
|
Definition:
|
||||||
|
|
||||||
|
<!ELEMENT current-user-principal (unauthenticated | href)>
|
||||||
|
<!-- href value: a URL to a principal resource -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Sanchez & Daboo Standards Track [Page 3]
|
||||||
|
|
||||||
|
RFC 5397 WebDAV Current Principal December 2008
|
||||||
|
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
<D:current-user-principal xmlns:D="DAV:">
|
||||||
|
<D:href>/principals/users/cdaboo</D:href>
|
||||||
|
</D:current-user-principal>
|
||||||
|
|
||||||
|
4. Security Considerations
|
||||||
|
|
||||||
|
This specification does not introduce any additional security issues
|
||||||
|
beyond those defined for HTTP [RFC2616], WebDAV [RFC4918], and WebDAV
|
||||||
|
ACL [RFC3744].
|
||||||
|
|
||||||
|
5. Acknowledgments
|
||||||
|
|
||||||
|
This specification is based on discussions that took place within the
|
||||||
|
Calendaring and Scheduling Consortium's CalDAV Technical Committee.
|
||||||
|
The authors thank the participants of that group for their input.
|
||||||
|
|
||||||
|
The authors thank Julian Reschke for his valuable input via the
|
||||||
|
WebDAV working group mailing list.
|
||||||
|
|
||||||
|
6. Normative References
|
||||||
|
|
||||||
|
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||||
|
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||||
|
|
||||||
|
[RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
|
||||||
|
Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
|
||||||
|
Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.
|
||||||
|
|
||||||
|
[RFC3744] Clemm, G., Reschke, J., Sedlar, E., and J. Whitehead, "Web
|
||||||
|
Distributed Authoring and Versioning (WebDAV)
|
||||||
|
Access Control Protocol", RFC 3744, May 2004.
|
||||||
|
|
||||||
|
[RFC4918] Dusseault, L., "HTTP Extensions for Web Distributed
|
||||||
|
Authoring and Versioning (WebDAV)", RFC 4918, June 2007.
|
||||||
|
|
||||||
|
Authors' Addresses
|
||||||
|
|
||||||
|
Wilfredo Sanchez
|
||||||
|
Apple Inc.
|
||||||
|
1 Infinite Loop
|
||||||
|
Cupertino, CA 95014
|
||||||
|
USA
|
||||||
|
|
||||||
|
EMail: wsanchez@wsanchez.net
|
||||||
|
URI: http://www.apple.com/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Sanchez & Daboo Standards Track [Page 4]
|
||||||
|
|
||||||
|
RFC 5397 WebDAV Current Principal December 2008
|
||||||
|
|
||||||
|
|
||||||
|
Cyrus Daboo
|
||||||
|
Apple Inc.
|
||||||
|
1 Infinite Loop
|
||||||
|
Cupertino, CA 95014
|
||||||
|
USA
|
||||||
|
|
||||||
|
EMail: cyrus@daboo.name
|
||||||
|
URI: http://www.apple.com/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Sanchez & Daboo Standards Track [Page 5]
|
||||||
|
|
||||||
|
|
2691
doc/rfc6352-carddav.txt
Normal file
2691
doc/rfc6352-carddav.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -83,6 +83,7 @@
|
|||||||
<string name="account_name_hint">Mein CalDAV/CardDAV-Konto</string>
|
<string name="account_name_hint">Mein CalDAV/CardDAV-Konto</string>
|
||||||
<string name="email_address">Email-Adresse:</string>
|
<string name="email_address">Email-Adresse:</string>
|
||||||
<string name="organizer_hint">"ORGANIZER der von Ihnen angelegten Termine; notwendig für Teilnehmer-Info"</string>
|
<string name="organizer_hint">"ORGANIZER der von Ihnen angelegten Termine; notwendig für Teilnehmer-Info"</string>
|
||||||
<string name="account_name_info">"Verwenden Sie Ihre Email-Addresse als Kontoname, da Android den Kontonamen als ORGANIZER-Feld in Terminen benutzt. Sie können keine zwei Konten mit dem gleichen Namen anlegen.</string>
|
<string name="account_name_info">"Verwenden Sie Ihre Email-Adresse als Kontoname, da Android den Kontonamen als ORGANIZER-Feld in Terminen benutzt. Sie können keine zwei Konten mit dem gleichen Namen anlegen.</string>
|
||||||
|
<string name="read_only">schreibgeschützt</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -92,5 +92,6 @@
|
|||||||
<string name="email_address">Email address:</string>
|
<string name="email_address">Email address:</string>
|
||||||
<string name="organizer_hint">"ORGANIZER of your events; required if you use attendee info"</string>
|
<string name="organizer_hint">"ORGANIZER of your events; required if you use attendee info"</string>
|
||||||
<string name="account_name_info">"Use your email address as account name because Android will use the account name as ORGANIZER field for events you create. You can't have two accounts with the same name.</string>
|
<string name="account_name_info">"Use your email address as account name because Android will use the account name as ORGANIZER field for events you create. You can't have two accounts with the same name.</string>
|
||||||
|
<string name="read_only">read-only</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -122,13 +122,18 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
values.put(Calendars.NAME, info.getPath());
|
values.put(Calendars.NAME, info.getPath());
|
||||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
||||||
values.put(Calendars.CALENDAR_COLOR, color);
|
values.put(Calendars.CALENDAR_COLOR, color);
|
||||||
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
|
|
||||||
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
|
|
||||||
values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
|
|
||||||
values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1);
|
|
||||||
values.put(Calendars.OWNER_ACCOUNT, account.name);
|
values.put(Calendars.OWNER_ACCOUNT, account.name);
|
||||||
values.put(Calendars.SYNC_EVENTS, 1);
|
values.put(Calendars.SYNC_EVENTS, 1);
|
||||||
values.put(Calendars.VISIBLE, 1);
|
values.put(Calendars.VISIBLE, 1);
|
||||||
|
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
|
||||||
|
|
||||||
|
if (info.isReadOnly())
|
||||||
|
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_READ);
|
||||||
|
else {
|
||||||
|
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
|
||||||
|
values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
|
||||||
|
values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= 15) {
|
if (android.os.Build.VERSION.SDK_INT >= 15) {
|
||||||
values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE);
|
values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE);
|
||||||
|
@ -22,13 +22,11 @@ import java.util.List;
|
|||||||
import lombok.Cleanup;
|
import lombok.Cleanup;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.fortuna.ical4j.model.ValidationException;
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
|
||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import at.bitfire.davdroid.webdav.DavException;
|
import at.bitfire.davdroid.webdav.DavException;
|
||||||
import at.bitfire.davdroid.webdav.DavMultiget;
|
import at.bitfire.davdroid.webdav.DavMultiget;
|
||||||
import at.bitfire.davdroid.webdav.DavNoContentException;
|
import at.bitfire.davdroid.webdav.DavNoContentException;
|
||||||
|
import at.bitfire.davdroid.webdav.HttpException;
|
||||||
import at.bitfire.davdroid.webdav.HttpPropfind;
|
import at.bitfire.davdroid.webdav.HttpPropfind;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
|
import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
|
||||||
@ -112,7 +110,7 @@ public abstract class RemoteCollection<T extends Resource> {
|
|||||||
|
|
||||||
/* internal member operations */
|
/* internal member operations */
|
||||||
|
|
||||||
public Resource get(Resource resource) throws IOException, HttpException, InvalidResourceException {
|
public Resource get(Resource resource) throws IOException, HttpException, DavException, InvalidResourceException {
|
||||||
WebDavResource member = new WebDavResource(collection, resource.getName());
|
WebDavResource member = new WebDavResource(collection, resource.getName());
|
||||||
member.get();
|
member.get();
|
||||||
|
|
||||||
|
@ -13,11 +13,9 @@ package at.bitfire.davdroid.syncadapter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
|
||||||
import org.apache.http.auth.AuthenticationException;
|
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
import android.content.AbstractThreadedSyncAdapter;
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
@ -32,10 +30,12 @@ import at.bitfire.davdroid.resource.LocalCollection;
|
|||||||
import at.bitfire.davdroid.resource.LocalStorageException;
|
import at.bitfire.davdroid.resource.LocalStorageException;
|
||||||
import at.bitfire.davdroid.resource.RemoteCollection;
|
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||||
import at.bitfire.davdroid.webdav.DavException;
|
import at.bitfire.davdroid.webdav.DavException;
|
||||||
|
import at.bitfire.davdroid.webdav.HttpException;
|
||||||
|
|
||||||
public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter {
|
public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
private final static String TAG = "davdroid.DavSyncAdapter";
|
private final static String TAG = "davdroid.DavSyncAdapter";
|
||||||
|
|
||||||
|
protected Context context;
|
||||||
protected AccountManager accountManager;
|
protected AccountManager accountManager;
|
||||||
|
|
||||||
@Getter private static String androidID;
|
@Getter private static String androidID;
|
||||||
@ -49,6 +49,7 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
accountManager = AccountManager.get(context);
|
accountManager = AccountManager.get(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,15 +71,22 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||||||
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 (AuthenticationException ex) {
|
|
||||||
syncResult.stats.numAuthExceptions++;
|
|
||||||
Log.e(TAG, "HTTP authentication failed", ex);
|
|
||||||
} 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) {
|
||||||
|
Log.e(TAG, "HTTP Unauthorized " + ex.getCode(), ex);
|
||||||
|
syncResult.stats.numAuthExceptions++;
|
||||||
|
} else if (ex.isClientError()) {
|
||||||
|
Log.e(TAG, "Hard HTTP error " + ex.getCode(), ex);
|
||||||
|
syncResult.stats.numParseExceptions++;
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Soft HTTP error" + ex.getCode(), ex);
|
||||||
syncResult.stats.numIoExceptions++;
|
syncResult.stats.numIoExceptions++;
|
||||||
Log.e(TAG, "HTTP error", ex);
|
}
|
||||||
|
|
||||||
} 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);
|
||||||
|
@ -31,6 +31,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import at.bitfire.davdroid.R;
|
import at.bitfire.davdroid.R;
|
||||||
|
import at.bitfire.davdroid.webdav.DavException;
|
||||||
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
import at.bitfire.davdroid.webdav.HttpPropfind.Mode;
|
||||||
import at.bitfire.davdroid.webdav.DavIncapableException;
|
import at.bitfire.davdroid.webdav.DavIncapableException;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
@ -165,6 +166,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
Log.i(TAG, "Found address book: " + resource.getLocation().getRawPath());
|
Log.i(TAG, "Found address book: " + resource.getLocation().getRawPath());
|
||||||
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.getLocation().getRawPath(),
|
resource.getLocation().getRawPath(),
|
||||||
resource.getDisplayName(),
|
resource.getDisplayName(),
|
||||||
resource.getDescription(), resource.getColor()
|
resource.getDescription(), resource.getColor()
|
||||||
@ -196,6 +198,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
}
|
}
|
||||||
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
ServerInfo.ResourceInfo info = new ServerInfo.ResourceInfo(
|
||||||
ServerInfo.ResourceInfo.Type.CALENDAR,
|
ServerInfo.ResourceInfo.Type.CALENDAR,
|
||||||
|
resource.isReadOnly(),
|
||||||
resource.getLocation().getRawPath(),
|
resource.getLocation().getRawPath(),
|
||||||
resource.getDisplayName(),
|
resource.getDisplayName(),
|
||||||
resource.getDescription(), resource.getColor()
|
resource.getDescription(), resource.getColor()
|
||||||
@ -211,9 +214,11 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
|||||||
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) {
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage()));
|
||||||
} catch (DavIncapableException e) {
|
} catch (DavException e) {
|
||||||
|
Log.e(TAG, "DAV error while querying server info", e);
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage()));
|
||||||
} catch (HttpException e) {
|
} catch (HttpException e) {
|
||||||
|
Log.e(TAG, "HTTP error while querying server info", e);
|
||||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));
|
serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
package at.bitfire.davdroid.syncadapter;
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import android.content.Context;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -26,11 +27,14 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
|||||||
TYPE_CALENDARS_HEADING = 2,
|
TYPE_CALENDARS_HEADING = 2,
|
||||||
TYPE_CALENDARS_ROW = 3;
|
TYPE_CALENDARS_ROW = 3;
|
||||||
|
|
||||||
|
protected Context context;
|
||||||
protected ServerInfo serverInfo;
|
protected ServerInfo serverInfo;
|
||||||
@Getter protected int nAddressBooks, nCalendars;
|
@Getter protected int nAddressBooks, nCalendars;
|
||||||
|
|
||||||
|
|
||||||
public SelectCollectionsAdapter(ServerInfo serverInfo) {
|
public SelectCollectionsAdapter(Context context, ServerInfo serverInfo) {
|
||||||
|
this.context = context;
|
||||||
|
|
||||||
this.serverInfo = serverInfo;
|
this.serverInfo = serverInfo;
|
||||||
nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size();
|
nAddressBooks = (serverInfo.getAddressBooks() == null) ? 0 : serverInfo.getAddressBooks().size();
|
||||||
nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size();
|
nCalendars = (serverInfo.getCalendars() == null) ? 0 : serverInfo.getCalendars().size();
|
||||||
@ -130,8 +134,12 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
|
|||||||
if (description == null)
|
if (description == null)
|
||||||
description = info.getPath();
|
description = info.getPath();
|
||||||
|
|
||||||
|
String title = "<b>" + info.getTitle() + "</b>";
|
||||||
|
if (info.isReadOnly())
|
||||||
|
title = title + " (" + context.getString(R.string.read_only) + ")";
|
||||||
|
|
||||||
// FIXME escape HTML
|
// FIXME escape HTML
|
||||||
view.setText(Html.fromHtml("<b>" + info.getTitle() + "</b><br/>" + description));
|
view.setText(Html.fromHtml(title + "<br/>" + description));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,7 +51,7 @@ public class SelectCollectionsFragment extends ListFragment {
|
|||||||
listView.addHeaderView(header);
|
listView.addHeaderView(header);
|
||||||
|
|
||||||
final ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
|
final ServerInfo serverInfo = (ServerInfo)getArguments().getSerializable(KEY_SERVER_INFO);
|
||||||
final SelectCollectionsAdapter adapter = new SelectCollectionsAdapter(serverInfo);
|
final SelectCollectionsAdapter adapter = new SelectCollectionsAdapter(view.getContext(), serverInfo);
|
||||||
setListAdapter(adapter);
|
setListAdapter(adapter);
|
||||||
|
|
||||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||||
|
@ -54,6 +54,7 @@ public class ServerInfo implements Serializable {
|
|||||||
boolean enabled = false;
|
boolean enabled = false;
|
||||||
|
|
||||||
final Type type;
|
final Type type;
|
||||||
|
final boolean readOnly;
|
||||||
final String path, title, description, color;
|
final String path, title, description, color;
|
||||||
|
|
||||||
String timezone;
|
String timezone;
|
||||||
|
@ -12,9 +12,6 @@ import java.util.HashSet;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import net.fortuna.ical4j.model.ValidationException;
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
|
||||||
|
|
||||||
import android.content.SyncResult;
|
import android.content.SyncResult;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import at.bitfire.davdroid.ArrayUtils;
|
import at.bitfire.davdroid.ArrayUtils;
|
||||||
@ -23,6 +20,8 @@ import at.bitfire.davdroid.resource.LocalStorageException;
|
|||||||
import at.bitfire.davdroid.resource.RecordNotFoundException;
|
import at.bitfire.davdroid.resource.RecordNotFoundException;
|
||||||
import at.bitfire.davdroid.resource.RemoteCollection;
|
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||||
import at.bitfire.davdroid.resource.Resource;
|
import at.bitfire.davdroid.resource.Resource;
|
||||||
|
import at.bitfire.davdroid.webdav.DavException;
|
||||||
|
import at.bitfire.davdroid.webdav.HttpException;
|
||||||
import at.bitfire.davdroid.webdav.NotFoundException;
|
import at.bitfire.davdroid.webdav.NotFoundException;
|
||||||
import at.bitfire.davdroid.webdav.PreconditionFailedException;
|
import at.bitfire.davdroid.webdav.PreconditionFailedException;
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ public class SyncManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void synchronize(boolean manualSync, SyncResult syncResult) throws LocalStorageException, IOException, HttpException {
|
public void synchronize(boolean manualSync, SyncResult syncResult) throws 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(),
|
||||||
@ -175,7 +174,7 @@ public class SyncManager {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int pullNew(Resource[] resourcesToAdd) throws LocalStorageException, IOException, HttpException {
|
private int pullNew(Resource[] resourcesToAdd) throws 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)");
|
||||||
|
|
||||||
@ -189,7 +188,7 @@ public class SyncManager {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int pullChanged(Resource[] resourcesToUpdate) throws LocalStorageException, IOException, HttpException {
|
private int pullChanged(Resource[] resourcesToUpdate) throws 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)");
|
||||||
|
|
||||||
|
@ -10,13 +10,13 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.webdav;
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
public class DavException extends Exception {
|
||||||
|
|
||||||
public class DavException extends HttpException {
|
|
||||||
private static final long serialVersionUID = -2118919144443165706L;
|
private static final long serialVersionUID = -2118919144443165706L;
|
||||||
|
|
||||||
final private static String prefix = "Invalid DAV response: ";
|
final private static String prefix = "Invalid DAV response: ";
|
||||||
|
|
||||||
|
/* used to indiciate DAV protocol errors */
|
||||||
|
|
||||||
|
|
||||||
public DavException(String message) {
|
public DavException(String message) {
|
||||||
super(prefix + message);
|
super(prefix + message);
|
||||||
|
@ -13,6 +13,8 @@ package at.bitfire.davdroid.webdav;
|
|||||||
public class DavIncapableException extends DavException {
|
public class DavIncapableException extends DavException {
|
||||||
private static final long serialVersionUID = -7199786680939975667L;
|
private static final long serialVersionUID = -7199786680939975667L;
|
||||||
|
|
||||||
|
/* used to indicate that the server doesn't support DAV */
|
||||||
|
|
||||||
public DavIncapableException(String msg) {
|
public DavIncapableException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,8 @@ import org.simpleframework.xml.Text;
|
|||||||
@Namespace(prefix="D",reference="DAV:")
|
@Namespace(prefix="D",reference="DAV:")
|
||||||
@Root(strict=false)
|
@Root(strict=false)
|
||||||
public class DavProp {
|
public class DavProp {
|
||||||
@Element(required=false,name="current-user-principal")
|
|
||||||
DavCurrentUserPrincipal currentUserPrincipal;
|
|
||||||
|
|
||||||
@Element(required=false,name="addressbook-home-set")
|
/* RFC 4918 WebDAV */
|
||||||
DavAddressbookHomeSet addressbookHomeSet;
|
|
||||||
|
|
||||||
@Element(required=false,name="calendar-home-set")
|
|
||||||
DavCalendarHomeSet calendarHomeSet;
|
|
||||||
|
|
||||||
@Element(required=false)
|
@Element(required=false)
|
||||||
DavPropResourceType resourcetype;
|
DavPropResourceType resourcetype;
|
||||||
@ -39,6 +33,80 @@ public class DavProp {
|
|||||||
@Element(required=false)
|
@Element(required=false)
|
||||||
DavPropDisplayName displayname;
|
DavPropDisplayName displayname;
|
||||||
|
|
||||||
|
@Element(required=false)
|
||||||
|
DavPropGetCTag getctag;
|
||||||
|
|
||||||
|
@Element(required=false)
|
||||||
|
DavPropGetETag getetag;
|
||||||
|
|
||||||
|
@Root(strict=false)
|
||||||
|
public static class DavPropResourceType {
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private Addressbook addressbook;
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private Calendar calendar;
|
||||||
|
|
||||||
|
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
||||||
|
public static class Addressbook { }
|
||||||
|
|
||||||
|
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
||||||
|
public static class Calendar { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DavPropDisplayName {
|
||||||
|
@Text(required=false)
|
||||||
|
@Getter private String displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Namespace(prefix="CS",reference="http://calendarserver.org/ns/")
|
||||||
|
public static class DavPropGetCTag {
|
||||||
|
@Text(required=false)
|
||||||
|
@Getter private String CTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DavPropGetETag {
|
||||||
|
@Text(required=false)
|
||||||
|
@Getter private String ETag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* RFC 5397 WebDAV Current Principal Extension */
|
||||||
|
|
||||||
|
@Element(required=false,name="current-user-principal")
|
||||||
|
DavCurrentUserPrincipal currentUserPrincipal;
|
||||||
|
|
||||||
|
public static class DavCurrentUserPrincipal {
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private DavHref href;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* RFC 3744 WebDAV Access Control Protocol */
|
||||||
|
|
||||||
|
@ElementList(required=false,name="current-user-privilege-set",entry="privilege")
|
||||||
|
List<DavPropPrivilege> currentUserPrivilegeSet;
|
||||||
|
|
||||||
|
public static class DavPropPrivilege {
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private PrivAll all;
|
||||||
|
|
||||||
|
@Element(required=false)
|
||||||
|
@Getter private PrivWrite write;
|
||||||
|
|
||||||
|
public static class PrivAll { }
|
||||||
|
public static class PrivWrite { }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* RFC 4791 CalDAV, RFC 6352 CardDAV */
|
||||||
|
|
||||||
|
@Element(required=false,name="addressbook-home-set")
|
||||||
|
DavAddressbookHomeSet addressbookHomeSet;
|
||||||
|
|
||||||
|
@Element(required=false,name="calendar-home-set")
|
||||||
|
DavCalendarHomeSet calendarHomeSet;
|
||||||
|
|
||||||
@Element(required=false,name="addressbook-description")
|
@Element(required=false,name="addressbook-description")
|
||||||
DavPropAddressbookDescription addressbookDescription;
|
DavPropAddressbookDescription addressbookDescription;
|
||||||
|
|
||||||
@ -51,14 +119,9 @@ public class DavProp {
|
|||||||
@Element(required=false,name="calendar-timezone")
|
@Element(required=false,name="calendar-timezone")
|
||||||
DavPropCalendarTimezone calendarTimezone;
|
DavPropCalendarTimezone calendarTimezone;
|
||||||
|
|
||||||
@Element(required=false,name="supported-calendar-component-set")
|
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
||||||
DavPropSupportedCalendarComponentSet supportedCalendarComponentSet;
|
@ElementList(required=false,name="supported-calendar-component-set",entry="comp")
|
||||||
|
List<DavPropComp> supportedCalendarComponentSet;
|
||||||
@Element(required=false)
|
|
||||||
DavPropGetCTag getctag;
|
|
||||||
|
|
||||||
@Element(required=false)
|
|
||||||
DavPropGetETag getetag;
|
|
||||||
|
|
||||||
@Element(name="address-data",required=false)
|
@Element(name="address-data",required=false)
|
||||||
DavPropAddressData addressData;
|
DavPropAddressData addressData;
|
||||||
@ -67,11 +130,6 @@ public class DavProp {
|
|||||||
DavPropCalendarData calendarData;
|
DavPropCalendarData calendarData;
|
||||||
|
|
||||||
|
|
||||||
public static class DavCurrentUserPrincipal {
|
|
||||||
@Element(required=false)
|
|
||||||
@Getter private DavHref href;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
||||||
public static class DavAddressbookHomeSet {
|
public static class DavAddressbookHomeSet {
|
||||||
@Element(required=false)
|
@Element(required=false)
|
||||||
@ -84,25 +142,6 @@ public class DavProp {
|
|||||||
@Getter private DavHref href;
|
@Getter private DavHref href;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Root(strict=false)
|
|
||||||
public static class DavPropResourceType {
|
|
||||||
@Element(required=false)
|
|
||||||
@Getter private DavAddressbook addressbook;
|
|
||||||
@Element(required=false)
|
|
||||||
@Getter private DavCalendar calendar;
|
|
||||||
|
|
||||||
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
|
||||||
public static class DavAddressbook { }
|
|
||||||
|
|
||||||
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
|
||||||
public static class DavCalendar { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DavPropDisplayName {
|
|
||||||
@Text(required=false)
|
|
||||||
@Getter private String displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
||||||
public static class DavPropAddressbookDescription {
|
public static class DavPropAddressbookDescription {
|
||||||
@Text(required=false)
|
@Text(required=false)
|
||||||
@ -127,29 +166,12 @@ public class DavProp {
|
|||||||
@Getter private String timezone;
|
@Getter private String timezone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
|
||||||
public static class DavPropSupportedCalendarComponentSet {
|
|
||||||
@ElementList(inline=true,entry="comp",required=false)
|
|
||||||
List<DavPropComp> components;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
@Namespace(prefix="C",reference="urn:ietf:params:xml:ns:caldav")
|
||||||
public static class DavPropComp {
|
public static class DavPropComp {
|
||||||
@Attribute
|
@Attribute
|
||||||
@Getter String name;
|
@Getter String name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Namespace(prefix="CS",reference="http://calendarserver.org/ns/")
|
|
||||||
public static class DavPropGetCTag {
|
|
||||||
@Text(required=false)
|
|
||||||
@Getter private String CTag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DavPropGetETag {
|
|
||||||
@Text(required=false)
|
|
||||||
@Getter private String ETag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
@Namespace(prefix="CD",reference="urn:ietf:params:xml:ns:carddav")
|
||||||
public static class DavPropAddressData {
|
public static class DavPropAddressData {
|
||||||
@Text(required=false)
|
@Text(required=false)
|
||||||
|
19
src/at/bitfire/davdroid/webdav/HttpException.java
Normal file
19
src/at/bitfire/davdroid/webdav/HttpException.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public class HttpException extends org.apache.http.HttpException {
|
||||||
|
private static final long serialVersionUID = -4805778240079377401L;
|
||||||
|
|
||||||
|
@Getter private int code;
|
||||||
|
|
||||||
|
HttpException(int code, String message) {
|
||||||
|
super(message);
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClientError() {
|
||||||
|
return code/100 == 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,6 +12,7 @@ package at.bitfire.davdroid.webdav;
|
|||||||
|
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
@ -52,11 +53,12 @@ public class HttpPropfind extends HttpEntityEnclosingRequestBase {
|
|||||||
depth = 1;
|
depth = 1;
|
||||||
propfind.prop.displayname = new DavProp.DavPropDisplayName();
|
propfind.prop.displayname = new DavProp.DavPropDisplayName();
|
||||||
propfind.prop.resourcetype = new DavProp.DavPropResourceType();
|
propfind.prop.resourcetype = new DavProp.DavPropResourceType();
|
||||||
|
propfind.prop.currentUserPrivilegeSet = new LinkedList<DavProp.DavPropPrivilege>();
|
||||||
propfind.prop.addressbookDescription = new DavProp.DavPropAddressbookDescription();
|
propfind.prop.addressbookDescription = new DavProp.DavPropAddressbookDescription();
|
||||||
propfind.prop.calendarDescription = new DavProp.DavPropCalendarDescription();
|
propfind.prop.calendarDescription = new DavProp.DavPropCalendarDescription();
|
||||||
propfind.prop.calendarColor = new DavProp.DavPropCalendarColor();
|
propfind.prop.calendarColor = new DavProp.DavPropCalendarColor();
|
||||||
propfind.prop.calendarTimezone = new DavProp.DavPropCalendarTimezone();
|
propfind.prop.calendarTimezone = new DavProp.DavPropCalendarTimezone();
|
||||||
propfind.prop.supportedCalendarComponentSet = new DavProp.DavPropSupportedCalendarComponentSet();
|
propfind.prop.supportedCalendarComponentSet = new LinkedList<DavProp.DavPropComp>();
|
||||||
break;
|
break;
|
||||||
case COLLECTION_CTAG:
|
case COLLECTION_CTAG:
|
||||||
propfind.prop.getctag = new DavProp.DavPropGetCTag();
|
propfind.prop.getctag = new DavProp.DavPropGetCTag();
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.webdav;
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class NotFoundException extends HttpException {
|
public class NotFoundException extends HttpException {
|
||||||
private static final long serialVersionUID = 1565961502781880483L;
|
private static final long serialVersionUID = 1565961502781880483L;
|
||||||
|
|
||||||
public NotFoundException(String reason) {
|
public NotFoundException(String reason) {
|
||||||
super(reason);
|
super(HttpStatus.SC_NOT_FOUND, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package at.bitfire.davdroid.webdav;
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
import org.apache.http.HttpStatus;
|
||||||
|
|
||||||
public class PreconditionFailedException extends HttpException {
|
public class PreconditionFailedException extends HttpException {
|
||||||
private static final long serialVersionUID = 102282229174086113L;
|
private static final long serialVersionUID = 102282229174086113L;
|
||||||
|
|
||||||
public PreconditionFailedException(String reason) {
|
public PreconditionFailedException(String reason) {
|
||||||
super(reason);
|
super(HttpStatus.SC_PRECONDITION_FAILED, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,12 +30,10 @@ import org.apache.commons.io.IOUtils;
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.http.Header;
|
import org.apache.http.Header;
|
||||||
import org.apache.http.HttpEntity;
|
import org.apache.http.HttpEntity;
|
||||||
import org.apache.http.HttpException;
|
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.HttpStatus;
|
import org.apache.http.HttpStatus;
|
||||||
import org.apache.http.StatusLine;
|
import org.apache.http.StatusLine;
|
||||||
import org.apache.http.auth.AuthScope;
|
import org.apache.http.auth.AuthScope;
|
||||||
import org.apache.http.auth.AuthenticationException;
|
|
||||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||||
import org.apache.http.client.methods.HttpDelete;
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
@ -61,6 +59,7 @@ public class WebDavResource {
|
|||||||
|
|
||||||
public enum Property {
|
public enum Property {
|
||||||
CURRENT_USER_PRINCIPAL,
|
CURRENT_USER_PRINCIPAL,
|
||||||
|
READ_ONLY,
|
||||||
DISPLAY_NAME, DESCRIPTION, COLOR,
|
DISPLAY_NAME, DESCRIPTION, COLOR,
|
||||||
TIMEZONE, SUPPORTED_COMPONENTS,
|
TIMEZONE, SUPPORTED_COMPONENTS,
|
||||||
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
ADDRESSBOOK_HOMESET, CALENDAR_HOMESET,
|
||||||
@ -177,6 +176,10 @@ public class WebDavResource {
|
|||||||
return properties.get(Property.CURRENT_USER_PRINCIPAL);
|
return properties.get(Property.CURRENT_USER_PRINCIPAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isReadOnly() {
|
||||||
|
return properties.containsKey(Property.READ_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return properties.get(Property.DISPLAY_NAME);
|
return properties.get(Property.DISPLAY_NAME);
|
||||||
}
|
}
|
||||||
@ -301,7 +304,7 @@ public class WebDavResource {
|
|||||||
|
|
||||||
/* resource operations */
|
/* resource operations */
|
||||||
|
|
||||||
public void get() throws IOException, HttpException {
|
public void get() throws IOException, HttpException, DavException {
|
||||||
HttpGet get = new HttpGet(location);
|
HttpGet get = new HttpGet(location);
|
||||||
HttpResponse response = client.execute(get);
|
HttpResponse response = client.execute(get);
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
@ -370,18 +373,16 @@ public class WebDavResource {
|
|||||||
|
|
||||||
String reason = code + " " + statusLine.getReasonPhrase();
|
String reason = code + " " + statusLine.getReasonPhrase();
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case HttpStatus.SC_UNAUTHORIZED:
|
|
||||||
throw new AuthenticationException(reason);
|
|
||||||
case HttpStatus.SC_NOT_FOUND:
|
case HttpStatus.SC_NOT_FOUND:
|
||||||
throw new NotFoundException(reason);
|
throw new NotFoundException(reason);
|
||||||
case HttpStatus.SC_PRECONDITION_FAILED:
|
case HttpStatus.SC_PRECONDITION_FAILED:
|
||||||
throw new PreconditionFailedException(reason);
|
throw new PreconditionFailedException(reason);
|
||||||
default:
|
default:
|
||||||
throw new HttpException(reason);
|
throw new HttpException(code, reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processMultiStatus(DavMultistatus multistatus) throws HttpException {
|
protected void processMultiStatus(DavMultistatus multistatus) throws HttpException, DavException {
|
||||||
if (multistatus.response == null) // empty response
|
if (multistatus.response == null) // empty response
|
||||||
throw new DavNoContentException();
|
throw new DavNoContentException();
|
||||||
|
|
||||||
@ -421,6 +422,16 @@ public class WebDavResource {
|
|||||||
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
if (prop.currentUserPrincipal != null && prop.currentUserPrincipal.getHref() != null)
|
||||||
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
properties.put(Property.CURRENT_USER_PRINCIPAL, prop.currentUserPrincipal.getHref().href);
|
||||||
|
|
||||||
|
if (prop.currentUserPrivilegeSet != null) {
|
||||||
|
// privilege info available
|
||||||
|
boolean hasWrite = false;
|
||||||
|
for (DavProp.DavPropPrivilege privilege : prop.currentUserPrivilegeSet) {
|
||||||
|
if (privilege.getAll() != null || privilege.getWrite() != null)
|
||||||
|
hasWrite = true;
|
||||||
|
}
|
||||||
|
if (!hasWrite) properties.put(Property.READ_ONLY, "1");
|
||||||
|
}
|
||||||
|
|
||||||
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
if (prop.addressbookHomeSet != null && prop.addressbookHomeSet.getHref() != null)
|
||||||
properties.put(Property.ADDRESSBOOK_HOMESET, prop.addressbookHomeSet.getHref().href);
|
properties.put(Property.ADDRESSBOOK_HOMESET, prop.addressbookHomeSet.getHref().href);
|
||||||
|
|
||||||
@ -449,9 +460,9 @@ public class WebDavResource {
|
|||||||
if (prop.calendarTimezone != null)
|
if (prop.calendarTimezone != null)
|
||||||
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
properties.put(Property.TIMEZONE, Event.TimezoneDefToTzId(prop.calendarTimezone.getTimezone()));
|
||||||
|
|
||||||
if (prop.supportedCalendarComponentSet != null && prop.supportedCalendarComponentSet.components != null) {
|
if (prop.supportedCalendarComponentSet != null) {
|
||||||
referenced.supportedComponents = new LinkedList<String>();
|
referenced.supportedComponents = new LinkedList<String>();
|
||||||
for (DavPropComp component : prop.supportedCalendarComponentSet.components)
|
for (DavPropComp component : prop.supportedCalendarComponentSet)
|
||||||
referenced.supportedComponents.add(component.getName());
|
referenced.supportedComponents.add(component.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user