src/org/gege/caldavsyncadapter/caldav/CaldavFacade.java

Tue, 10 Feb 2015 18:12:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 10 Feb 2015 18:12:00 +0100
changeset 0
fb9019fb1bf7
child 8
ec8af0e3fbc2
permissions
-rw-r--r--

Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.

michael@0 1 /**
michael@0 2 * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger
michael@0 3 *
michael@0 4 * This file is part of Andoid Caldav Sync Adapter Free.
michael@0 5 *
michael@0 6 * Andoid Caldav Sync Adapter Free is free software: you can redistribute
michael@0 7 * it and/or modify it under the terms of the GNU General Public License
michael@0 8 * as published by the Free Software Foundation, either version 3 of the
michael@0 9 * License, or at your option any later version.
michael@0 10 *
michael@0 11 * Andoid Caldav Sync Adapter Free is distributed in the hope that
michael@0 12 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
michael@0 13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
michael@0 14 * GNU General Public License for more details.
michael@0 15 *
michael@0 16 * You should have received a copy of the GNU General Public License
michael@0 17 * along with Andoid Caldav Sync Adapter Free.
michael@0 18 * If not, see <http://www.gnu.org/licenses/>.
michael@0 19 *
michael@0 20 */
michael@0 21
michael@0 22 package org.gege.caldavsyncadapter.caldav;
michael@0 23
michael@0 24 import java.io.BufferedReader;
michael@0 25 import java.io.ByteArrayInputStream;
michael@0 26 import java.io.FileNotFoundException;
michael@0 27 import java.io.IOException;
michael@0 28 import java.io.InputStream;
michael@0 29 import java.io.InputStreamReader;
michael@0 30 import java.io.UnsupportedEncodingException;
michael@0 31 import java.net.MalformedURLException;
michael@0 32 import java.net.SocketException;
michael@0 33 import java.net.URI;
michael@0 34 import java.net.URISyntaxException;
michael@0 35 import java.net.URL;
michael@0 36 import java.util.ArrayList;
michael@0 37 import java.util.List;
michael@0 38
michael@0 39 import javax.xml.parsers.DocumentBuilder;
michael@0 40 import javax.xml.parsers.DocumentBuilderFactory;
michael@0 41 import javax.xml.parsers.ParserConfigurationException;
michael@0 42 import javax.xml.parsers.SAXParser;
michael@0 43 import javax.xml.parsers.SAXParserFactory;
michael@0 44
michael@0 45 import org.apache.http.HttpException;
michael@0 46 import org.apache.http.HttpHost;
michael@0 47 import org.apache.http.HttpRequest;
michael@0 48 import org.apache.http.HttpRequestInterceptor;
michael@0 49 import org.apache.http.HttpResponse;
michael@0 50 import org.apache.http.HttpVersion;
michael@0 51 import org.apache.http.auth.AuthScope;
michael@0 52 import org.apache.http.auth.AuthState;
michael@0 53 import org.apache.http.auth.AuthenticationException;
michael@0 54 import org.apache.http.auth.UsernamePasswordCredentials;
michael@0 55 import org.apache.http.client.ClientProtocolException;
michael@0 56 import org.apache.http.client.CredentialsProvider;
michael@0 57 import org.apache.http.client.HttpClient;
michael@0 58 import org.apache.http.client.methods.HttpDelete;
michael@0 59 import org.apache.http.client.methods.HttpGet;
michael@0 60 import org.apache.http.client.methods.HttpPut;
michael@0 61 import org.apache.http.client.protocol.ClientContext;
michael@0 62 import org.apache.http.conn.HttpHostConnectException;
michael@0 63 import org.apache.http.conn.params.ConnManagerPNames;
michael@0 64 import org.apache.http.conn.params.ConnPerRouteBean;
michael@0 65 import org.apache.http.conn.scheme.PlainSocketFactory;
michael@0 66 import org.apache.http.conn.scheme.Scheme;
michael@0 67 import org.apache.http.conn.scheme.SchemeRegistry;
michael@0 68 import org.apache.http.conn.ssl.SSLSocketFactory;
michael@0 69 import org.apache.http.entity.StringEntity;
michael@0 70 import org.apache.http.impl.client.AbstractHttpClient;
michael@0 71 import org.apache.http.impl.client.DefaultHttpClient;
michael@0 72 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
michael@0 73 import org.apache.http.params.BasicHttpParams;
michael@0 74 import org.apache.http.params.CoreProtocolPNames;
michael@0 75 import org.apache.http.params.HttpParams;
michael@0 76 import org.apache.http.params.HttpProtocolParams;
michael@0 77 import org.apache.http.protocol.BasicHttpContext;
michael@0 78 import org.apache.http.protocol.HttpContext;
michael@0 79 import org.gege.caldavsyncadapter.BuildConfig;
michael@0 80 import org.gege.caldavsyncadapter.caldav.entities.DavCalendar;
michael@0 81 import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource;
michael@0 82 import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent;
michael@0 83 import org.gege.caldavsyncadapter.caldav.entities.CalendarList;
michael@0 84 import org.gege.caldavsyncadapter.caldav.http.HttpPropFind;
michael@0 85 import org.gege.caldavsyncadapter.caldav.http.HttpReport;
michael@0 86 import org.gege.caldavsyncadapter.caldav.xml.CalendarHomeHandler;
michael@0 87 import org.gege.caldavsyncadapter.caldav.xml.CalendarsHandler;
michael@0 88 import org.gege.caldavsyncadapter.caldav.xml.ServerInfoHandler;
michael@0 89 import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper;
michael@0 90 import org.w3c.dom.Document;
michael@0 91 import org.w3c.dom.Element;
michael@0 92 import org.w3c.dom.Node;
michael@0 93 import org.w3c.dom.NodeList;
michael@0 94 import org.xml.sax.ContentHandler;
michael@0 95 import org.xml.sax.InputSource;
michael@0 96 import org.xml.sax.SAXException;
michael@0 97 import org.xml.sax.XMLReader;
michael@0 98
michael@0 99 import android.accounts.Account;
michael@0 100 import android.content.ContentProviderClient;
michael@0 101 import android.content.Context;
michael@0 102 import android.util.Log;
michael@0 103
michael@0 104 public class CaldavFacade {
michael@0 105 private static final String TAG = "CaldavFacade";
michael@0 106
michael@0 107 private final static String XML_VERSION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
michael@0 108
michael@0 109 private String USER_AGENT = "CalDAV Sync Adapter (Android) https://github.com/gggard/AndroidCaldavSyncAdapater";
michael@0 110 private String VERSION = "";
michael@0 111
michael@0 112 private static HttpClient httpClient;
michael@0 113 private HttpContext mContext = null;
michael@0 114 private AuthState mLastAuthState = null;
michael@0 115 private AuthScope mLastAuthScope = null;
michael@0 116
michael@0 117 private boolean trustAll = true;
michael@0 118
michael@0 119 private URL url;
michael@0 120
michael@0 121 private static HttpHost targetHost;
michael@0 122
michael@0 123 private int lastStatusCode;
michael@0 124 private String lastETag;
michael@0 125 private String lastDav;
michael@0 126
michael@0 127 private String mstrcHeaderIfMatch = "If-Match";
michael@0 128 private String mstrcHeaderIfNoneMatch = "If-None-Match";
michael@0 129
michael@0 130 private Account mAccount = null;
michael@0 131 private ContentProviderClient mProvider;
michael@0 132
michael@0 133 protected HttpClient getHttpClient() {
michael@0 134
michael@0 135 HttpParams params = new BasicHttpParams();
michael@0 136 params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
michael@0 137 params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
michael@0 138 params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
michael@0 139 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
michael@0 140
michael@0 141 SchemeRegistry registry = new SchemeRegistry();
michael@0 142 registry.register(new Scheme("http", new PlainSocketFactory(), 80));
michael@0 143 registry.register(new Scheme("https", (trustAll ? EasySSLSocketFactory.getSocketFactory() : SSLSocketFactory.getSocketFactory()), 443));
michael@0 144 DefaultHttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, registry), params);
michael@0 145
michael@0 146 return client;
michael@0 147 }
michael@0 148
michael@0 149 public CaldavFacade(String mUser, String mPassword, String mURL) throws MalformedURLException {
michael@0 150 url = new URL(mURL);
michael@0 151
michael@0 152 httpClient = getHttpClient();
michael@0 153 UsernamePasswordCredentials upc = new UsernamePasswordCredentials(mUser, mPassword);
michael@0 154
michael@0 155 AuthScope as = null;
michael@0 156 as = new AuthScope(url.getHost(), -1);
michael@0 157 ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(as, upc);
michael@0 158
michael@0 159 mContext = new BasicHttpContext();
michael@0 160 CredentialsProvider credProvider = ((AbstractHttpClient) httpClient).getCredentialsProvider();
michael@0 161 mContext.setAttribute(ClientContext.CREDS_PROVIDER, credProvider);
michael@0 162
michael@0 163 //http://dlinsin.blogspot.de/2009/08/http-basic-authentication-with-android.html
michael@0 164 ((AbstractHttpClient) httpClient).addRequestInterceptor(preemptiveAuth, 0);
michael@0 165
michael@0 166 String proto = "http";
michael@0 167 int port = 80;
michael@0 168
michael@0 169 if (url.getProtocol().equalsIgnoreCase("https")) {
michael@0 170 proto = "https";
michael@0 171 if (url.getPort() == -1)
michael@0 172 port = 443;
michael@0 173 else
michael@0 174 port = url.getPort();
michael@0 175 }
michael@0 176
michael@0 177 if (url.getProtocol().equalsIgnoreCase("http")) {
michael@0 178 proto = "http";
michael@0 179 if (url.getPort() == -1)
michael@0 180 port = 80;
michael@0 181 else
michael@0 182 port = url.getPort();
michael@0 183 }
michael@0 184 targetHost = new HttpHost(url.getHost(), port, proto);
michael@0 185 }
michael@0 186
michael@0 187 //http://dlinsin.blogspot.de/2009/08/http-basic-authentication-with-android.html
michael@0 188 HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
michael@0 189 @Override
michael@0 190 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
michael@0 191 AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
michael@0 192
michael@0 193 if (authState.getAuthScheme() == null) {
michael@0 194 if (mLastAuthState != null) {
michael@0 195 Log.d(TAG, "LastAuthState: restored with user " + mLastAuthState.getCredentials().getUserPrincipal().getName());
michael@0 196 authState.setAuthScheme(mLastAuthState.getAuthScheme());
michael@0 197 authState.setCredentials(mLastAuthState.getCredentials());
michael@0 198 } else {
michael@0 199 Log.d(TAG, "LastAuthState: nothing to do");
michael@0 200 }
michael@0 201 if (mLastAuthScope != null) {
michael@0 202 authState.setAuthScope(mLastAuthScope);
michael@0 203 Log.d(TAG, "LastAuthScope: restored");
michael@0 204 } else {
michael@0 205 Log.d(TAG, "LastAuthScope: nothing to do");
michael@0 206 }
michael@0 207 } else {
michael@0 208 //AuthState and AuthScope have to be saved separate because of the AuthScope within AuthState gets lost, so we save it in a separate var.
michael@0 209 mLastAuthState = authState;
michael@0 210 Log.d(TAG, "LastAuthState: new with user " + mLastAuthState.getCredentials().getUserPrincipal().getName());
michael@0 211 if (authState.getAuthScope() != null) {
michael@0 212 mLastAuthScope = authState.getAuthScope();
michael@0 213 Log.d(TAG, "LastAuthScope: new");
michael@0 214 }
michael@0 215 }
michael@0 216 }
michael@0 217 };
michael@0 218
michael@0 219 public enum TestConnectionResult {
michael@0 220 WRONG_CREDENTIAL,
michael@0 221 WRONG_URL,
michael@0 222 WRONG_SERVER_STATUS,
michael@0 223 WRONG_ANSWER,
michael@0 224 SUCCESS
michael@0 225 }
michael@0 226
michael@0 227 /**
michael@0 228 * TODO: testConnection should return only an instance of
michael@0 229 * TestConnectionResult without throwing an exception or only throw checked
michael@0 230 * exceptions so you don't have to check the result of this function AND
michael@0 231 * handle the exceptions
michael@0 232 * @param context
michael@0 233 *
michael@0 234 * @return {@link TestConnectionResult}
michael@0 235 * @throws HttpHostConnectException
michael@0 236 * @throws IOException
michael@0 237 * @throws URISyntaxException
michael@0 238 * @throws ParserConfigurationException
michael@0 239 * @throws SAXException
michael@0 240 */
michael@0 241 public TestConnectionResult testConnection() throws HttpHostConnectException, IOException, URISyntaxException, ParserConfigurationException, SAXException {
michael@0 242 Log.d(TAG, "start testConnection ");
michael@0 243 try {
michael@0 244 List<DavCalendar> calendars = new ArrayList<DavCalendar>();
michael@0 245 calendars = forceGetCalendarsFromUri(null, url.toURI());
michael@0 246 if (calendars.size() != 0) {
michael@0 247 return TestConnectionResult.SUCCESS;
michael@0 248 }
michael@0 249
michael@0 250 URI userPrincipal = getUserPrincipal();
michael@0 251 List<URI> calendarSets = getCalendarHomes(userPrincipal);
michael@0 252 for (URI calendarSet : calendarSets) {
michael@0 253 List<DavCalendar> calendarSetCalendars = getCalendarsFromSet(calendarSet);
michael@0 254 calendars.addAll(calendarSetCalendars);
michael@0 255 }
michael@0 256 if (calendarSets.size() == 0) {
michael@0 257 return TestConnectionResult.WRONG_ANSWER;
michael@0 258 }
michael@0 259 } catch (FileNotFoundException e) {
michael@0 260 return TestConnectionResult.WRONG_URL;
michael@0 261 } catch (SocketException e) {
michael@0 262 return TestConnectionResult.WRONG_URL;
michael@0 263 } catch (AuthenticationException e) {
michael@0 264 return TestConnectionResult.WRONG_CREDENTIAL;
michael@0 265 } catch (ClientProtocolException e) {
michael@0 266 return TestConnectionResult.WRONG_SERVER_STATUS;
michael@0 267 } catch (CaldavProtocolException e) {
michael@0 268 return TestConnectionResult.WRONG_ANSWER;
michael@0 269 }
michael@0 270 return TestConnectionResult.SUCCESS;
michael@0 271 }
michael@0 272
michael@0 273 /**
michael@0 274 * @param context May be null if no notification is needed
michael@0 275 * @param uri
michael@0 276 * @return
michael@0 277 * @throws AuthenticationException
michael@0 278 * @throws FileNotFoundException
michael@0 279 */
michael@0 280 private List<DavCalendar> forceGetCalendarsFromUri(Context context, URI uri) throws AuthenticationException, FileNotFoundException {
michael@0 281 List<DavCalendar> calendars = new ArrayList<DavCalendar>();
michael@0 282 Exception exception = null;
michael@0 283 try {
michael@0 284 calendars = getCalendarsFromSet(uri);
michael@0 285 } catch (ClientProtocolException e) {
michael@0 286 if (context != null) {
michael@0 287 NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
michael@0 288 //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@0 289 }
michael@0 290 exception = e;
michael@0 291 } catch (FileNotFoundException e) {
michael@0 292 if (context != null) {
michael@0 293 NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
michael@0 294 //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@0 295 }
michael@0 296 throw e;
michael@0 297 } catch (IOException e) {
michael@0 298 if (context != null) {
michael@0 299 NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
michael@0 300 //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@0 301 }
michael@0 302 exception = e;
michael@0 303 } catch (CaldavProtocolException e) {
michael@0 304
michael@0 305 if (context != null) {
michael@0 306 NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
michael@0 307 //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@0 308 }
michael@0 309 exception = e;
michael@0 310 }
michael@0 311 if (exception != null && BuildConfig.DEBUG) {
michael@0 312 Log.e(TAG, "Force get calendars from '" + uri.toString()
michael@0 313 + "' failed " + exception.getClass().getCanonicalName()
michael@0 314 + ": " + exception.getMessage());
michael@0 315 }
michael@0 316 return calendars;
michael@0 317 }
michael@0 318
michael@0 319 private final static String PROPFIND_USER_PRINCIPAL = XML_VERSION +
michael@0 320 "<d:propfind xmlns:d=\"DAV:\">" +
michael@0 321 "<d:prop>" +
michael@0 322 "<d:current-user-principal />" +
michael@0 323 "<d:principal-URL />" +
michael@0 324 "</d:prop>" +
michael@0 325 "</d:propfind>";
michael@0 326
michael@0 327 private URI getUserPrincipal() throws SocketException,
michael@0 328 ClientProtocolException, AuthenticationException,
michael@0 329 FileNotFoundException, IOException, CaldavProtocolException,
michael@0 330 URISyntaxException {
michael@0 331 URI uri = this.url.toURI();
michael@0 332 HttpPropFind request = createPropFindRequest(uri,
michael@0 333 PROPFIND_USER_PRINCIPAL, 0);
michael@0 334 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 335 checkStatus(response);
michael@0 336 ServerInfoHandler serverInfoHandler = new ServerInfoHandler();
michael@0 337 parseXML(response, serverInfoHandler);
michael@0 338 String userPrincipal = null;
michael@0 339 if (serverInfoHandler.currentUserPrincipal != null) {
michael@0 340 userPrincipal = serverInfoHandler.currentUserPrincipal;
michael@0 341 } else if (serverInfoHandler.principalUrl != null) {
michael@0 342 userPrincipal = serverInfoHandler.principalUrl;
michael@0 343 } else {
michael@0 344 throw new CaldavProtocolException("no principal url found");
michael@0 345 }
michael@0 346 try {
michael@0 347 URI userPrincipalUri = new URI(userPrincipal);
michael@0 348 userPrincipalUri = uri.resolve(userPrincipalUri);
michael@0 349 if (BuildConfig.DEBUG) {
michael@0 350 Log.d(TAG,
michael@0 351 "Found userPrincipal: " + userPrincipalUri.toString());
michael@0 352 }
michael@0 353 return userPrincipalUri;
michael@0 354 } catch (URISyntaxException e) {
michael@0 355 throw new CaldavProtocolException("principal url '" + userPrincipal
michael@0 356 + "' malformed");
michael@0 357 }
michael@0 358 }
michael@0 359
michael@0 360 private final static String PROPFIND_CALENDAR_HOME_SET = XML_VERSION
michael@0 361 + "<d:propfind xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\"><d:prop><c:calendar-home-set/></d:prop></d:propfind>";
michael@0 362
michael@0 363 private List<URI> getCalendarHomes(URI userPrincipal)
michael@0 364 throws ClientProtocolException, IOException,
michael@0 365 AuthenticationException, FileNotFoundException,
michael@0 366 CaldavProtocolException {
michael@0 367 HttpPropFind request = createPropFindRequest(userPrincipal,
michael@0 368 PROPFIND_CALENDAR_HOME_SET, 0);
michael@0 369 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 370 checkStatus(response);
michael@0 371 CalendarHomeHandler calendarHomeHandler = new CalendarHomeHandler(
michael@0 372 userPrincipal);
michael@0 373 parseXML(response, calendarHomeHandler);
michael@0 374 List<URI> result = calendarHomeHandler.calendarHomeSet;
michael@0 375 if (BuildConfig.DEBUG) {
michael@0 376 Log.d(TAG, result.size() + " calendar-home-set found in "
michael@0 377 + userPrincipal.toString());
michael@0 378 }
michael@0 379 return result;
michael@0 380 }
michael@0 381
michael@0 382 private final static String PROPFIND_CALENDER_LIST = XML_VERSION
michael@0 383 + "<d:propfind xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\" xmlns:cs=\"http://calendarserver.org/ns/\" xmlns:ic=\"http://apple.com/ns/ical/\">"
michael@0 384 + "<d:prop><d:displayname /><d:resourcetype />"
michael@0 385 // +
michael@0 386 // "<d:supported-method-set /><d:supported-report-set /><c:supported-calendar-component-set />"
michael@0 387 // +
michael@0 388 // "<c:calendar-description /><c:calendar-timezone /><c:calendar-free-busy-set />
michael@0 389 + "<ic:calendar-color />"
michael@0 390 //<ic:calendar-order />"
michael@0 391 + "<cs:getctag /></d:prop></d:propfind>";
michael@0 392
michael@0 393
michael@0 394 private List<DavCalendar> getCalendarsFromSet(URI calendarSet)
michael@0 395 throws ClientProtocolException, IOException,
michael@0 396 CaldavProtocolException, AuthenticationException,
michael@0 397 FileNotFoundException {
michael@0 398 HttpPropFind request = createPropFindRequest(calendarSet, PROPFIND_CALENDER_LIST, 1);
michael@0 399 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 400 checkStatus(response);
michael@0 401 CalendarsHandler calendarsHandler = new CalendarsHandler(calendarSet);
michael@0 402 parseXML(response, calendarsHandler);
michael@0 403 List<DavCalendar> result = calendarsHandler.calendars;
michael@0 404 if (BuildConfig.DEBUG) {
michael@0 405 Log.i(TAG,
michael@0 406 result.size() + " calendars found in set "
michael@0 407 + calendarSet.toString());
michael@0 408 }
michael@0 409 return result;
michael@0 410 }
michael@0 411
michael@0 412 /**
michael@0 413 * Discover CalDAV Calendars Mentioned in
michael@0 414 * http://tools.ietf.org/html/draft-daboo-srv-caldav-10#section-6 and
michael@0 415 * http://code.google.com/p/sabredav/wiki/BuildingACalDAVClient#Discovery
michael@0 416 * <ol>
michael@0 417 * <li>PROPFIND calendar-home-set on url
michael@0 418 * <li>PROPFIND DAV:current-user-principal or principal-URL on url
michael@0 419 * <li>PROPFIND calendar-home-set on current-user-principal or principal-URL
michael@0 420 * <li>PROPFIND displayname, resourcetype, getctag on CalendarHomeSets
michael@0 421 * </ol>
michael@0 422 * @param context
michael@0 423 *
michael@0 424 * @return List of {@link DavCalendar}
michael@0 425 * @throws ClientProtocolException
michael@0 426 * http protocol error
michael@0 427 * @throws IOException
michael@0 428 * Connection lost
michael@0 429 * @throws URISyntaxException
michael@0 430 * url in Constructor malformed
michael@0 431 * @throws CaldavProtocolException
michael@0 432 * caldav protocol error
michael@0 433 */
michael@0 434 //public Iterable<Calendar> getCalendarList(Context context) throws ClientProtocolException,
michael@0 435 public CalendarList getCalendarList(Context context) throws ClientProtocolException,
michael@0 436 IOException, URISyntaxException, ParserConfigurationException,
michael@0 437 CaldavProtocolException {
michael@0 438 try {
michael@0 439 CalendarList Result = new CalendarList(this.mAccount, this.mProvider, CalendarSource.CalDAV, this.url.toString());
michael@0 440 List<DavCalendar> calendars = new ArrayList<DavCalendar>();
michael@0 441
michael@0 442 calendars = forceGetCalendarsFromUri(context, this.url.toURI());
michael@0 443
michael@0 444 if (calendars.size() == 0) {
michael@0 445 // no calendars found, try the home-set
michael@0 446 URI userPrincipal = getUserPrincipal();
michael@0 447 List<URI> calendarSets = getCalendarHomes(userPrincipal);
michael@0 448 for (URI calendarSet : calendarSets) {
michael@0 449 List<DavCalendar> calendarSetCalendars = getCalendarsFromSet(calendarSet);
michael@0 450 calendars.addAll(calendarSetCalendars);
michael@0 451 }
michael@0 452 }
michael@0 453 for (DavCalendar cal : calendars) {
michael@0 454 Result.addCalendar(cal);
michael@0 455 }
michael@0 456
michael@0 457 //return calendars;
michael@0 458 return Result;
michael@0 459 } catch (AuthenticationException e) {
michael@0 460 throw new IOException(e);
michael@0 461 }
michael@0 462 }
michael@0 463
michael@0 464 //public Iterable<CalendarEvent> getCalendarEvents(DavCalendar calendar)
michael@0 465 public ArrayList<CalendarEvent> getCalendarEvents(DavCalendar calendar)
michael@0 466 throws URISyntaxException, ClientProtocolException, IOException,
michael@0 467 ParserConfigurationException, SAXException {
michael@0 468
michael@0 469 ArrayList<CalendarEvent> calendarEventList = new ArrayList<CalendarEvent>();
michael@0 470
michael@0 471 String requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
michael@0 472 + "<D:propfind xmlns:D=\"DAV:\">" + "<D:prop>" + "<D:getetag/>"
michael@0 473 + "</D:prop>" + "</D:propfind>";
michael@0 474
michael@0 475 HttpPropFind request = null;
michael@0 476
michael@0 477 String EventUri;
michael@0 478
michael@0 479 /*request = new HttpPropFind();
michael@0 480 request.setURI(calendar.getURI());
michael@0 481 request.setHeader("Host", targetHost.getHostName());
michael@0 482 request.setHeader("Depth", "1");
michael@0 483 request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
michael@0 484
michael@0 485 try {
michael@0 486 request.setEntity(new StringEntity(requestBody, "UTF-8"));
michael@0 487 } catch (UnsupportedEncodingException e) {
michael@0 488 throw new AssertionError("UTF-8 is unknown");
michael@0 489 }*/
michael@0 490 request = this.createPropFindRequest(calendar.getURI(), requestBody, 1);
michael@0 491
michael@0 492 Log.d(TAG, "Getting eTag by PROPFIND at " + request.getURI());
michael@0 493
michael@0 494 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 495
michael@0 496 BufferedReader reader = new BufferedReader(new InputStreamReader(
michael@0 497 response.getEntity().getContent(), "UTF-8"));
michael@0 498
michael@0 499 String line;
michael@0 500 String body = "";
michael@0 501 do {
michael@0 502 line = reader.readLine();
michael@0 503 if (line != null)
michael@0 504 body += line;
michael@0 505 } while (line != null);
michael@0 506
michael@0 507 Log.d(TAG, "HttpResponse status=" + response.getStatusLine()
michael@0 508 + " body= " + body);
michael@0 509
michael@0 510 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
michael@0 511 factory.setNamespaceAware(true);
michael@0 512 DocumentBuilder builder = factory.newDocumentBuilder();
michael@0 513 Document dom = builder.parse(new InputSource(new ByteArrayInputStream(
michael@0 514 body.getBytes("utf-8"))));
michael@0 515 Element root = dom.getDocumentElement();
michael@0 516 NodeList items = root.getElementsByTagNameNS("*", "getetag");
michael@0 517
michael@0 518 for (int i = 0; i < items.getLength(); i++) {
michael@0 519 CalendarEvent calendarEvent = new CalendarEvent(this.mAccount, this.mProvider);
michael@0 520
michael@0 521 Node node = items.item(i);
michael@0 522
michael@0 523 if (node.getTextContent().trim().length() == 0)
michael@0 524 continue; // not an event
michael@0 525
michael@0 526 calendarEvent.setETag(node.getTextContent().trim());
michael@0 527 //calendarEvent.calendarURL = this.url;
michael@0 528 calendarEvent.calendarURL = calendar.getURI().toURL();
michael@0 529
michael@0 530 node = node.getParentNode(); // prop
michael@0 531 node = node.getParentNode(); // propstat
michael@0 532 node = node.getParentNode(); // response
michael@0 533
michael@0 534 NodeList children = node.getChildNodes();
michael@0 535 for (int j = 0; j < children.getLength(); j++) {
michael@0 536 Node childNode = children.item(j);
michael@0 537 if ((childNode.getLocalName()!=null) && (childNode.getLocalName().equalsIgnoreCase("href"))) {
michael@0 538 EventUri = childNode.getTextContent().trim();
michael@0 539 //HINT: bugfix for zimbra calendar: replace("@", "%40")
michael@0 540 EventUri = EventUri.replace("@", "%40");
michael@0 541 calendarEvent.setUri(new URI(EventUri));
michael@0 542 }
michael@0 543 }
michael@0 544
michael@0 545 calendarEventList.add(calendarEvent);
michael@0 546
michael@0 547 }
michael@0 548
michael@0 549 return calendarEventList;
michael@0 550 }
michael@0 551
michael@0 552 private void parseXML(HttpResponse response, ContentHandler contentHandler)
michael@0 553 throws IOException, CaldavProtocolException {
michael@0 554 InputStream is = response.getEntity().getContent();
michael@0 555 /*BufferedReader bReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
michael@0 556 String Content = "";
michael@0 557 String Line = bReader.readLine();
michael@0 558
michael@0 559 while (Line != null) {
michael@0 560 Content += Line;
michael@0 561 Line = bReader.readLine();
michael@0 562 }*/
michael@0 563
michael@0 564 SAXParserFactory factory = SAXParserFactory.newInstance();
michael@0 565 try {
michael@0 566 SAXParser parser = factory.newSAXParser();
michael@0 567 XMLReader reader = parser.getXMLReader();
michael@0 568 reader.setContentHandler(contentHandler);
michael@0 569 reader.parse(new InputSource(is));
michael@0 570 } catch (ParserConfigurationException e) {
michael@0 571 throw new AssertionError("ParserConfigurationException "
michael@0 572 + e.getMessage());
michael@0 573 } catch (IllegalStateException e) {
michael@0 574 throw new CaldavProtocolException(e.getMessage());
michael@0 575 } catch (SAXException e) {
michael@0 576 throw new CaldavProtocolException(e.getMessage());
michael@0 577 }
michael@0 578 }
michael@0 579
michael@0 580 private void checkStatus(HttpResponse response)
michael@0 581 throws AuthenticationException, FileNotFoundException,
michael@0 582 ClientProtocolException {
michael@0 583 final int statusCode = response.getStatusLine().getStatusCode();
michael@0 584 lastStatusCode = statusCode;
michael@0 585 if (response.containsHeader("ETag"))
michael@0 586 lastETag = response.getFirstHeader("ETag").getValue();
michael@0 587 else
michael@0 588 lastETag = "";
michael@0 589 if (response.containsHeader("DAV"))
michael@0 590 lastDav = response.getFirstHeader("DAV").getValue();
michael@0 591 else
michael@0 592 lastDav = "";
michael@0 593
michael@0 594 switch (statusCode) {
michael@0 595 case 401:
michael@0 596 throw new AuthenticationException();
michael@0 597 case 404:
michael@0 598 throw new FileNotFoundException();
michael@0 599 case 409: //Conflict
michael@0 600 case 412:
michael@0 601 case 200:
michael@0 602 case 201:
michael@0 603 case 204:
michael@0 604 case 207:
michael@0 605 return;
michael@0 606 default:
michael@0 607 throw new ClientProtocolException("StatusCode: " + statusCode);
michael@0 608 }
michael@0 609 }
michael@0 610
michael@0 611 private HttpPropFind createPropFindRequest(URI uri, String data, int depth) {
michael@0 612 HttpPropFind request = new HttpPropFind();
michael@0 613
michael@0 614 request.setURI(uri);
michael@0 615 //request.setHeader("Host", targetHost.getHostName());
michael@0 616 request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort()));
michael@0 617 request.setHeader("Depth", Integer.toString(depth));
michael@0 618 request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
michael@0 619 try {
michael@0 620 request.setEntity(new StringEntity(data, "UTF-8"));
michael@0 621 } catch (UnsupportedEncodingException e) {
michael@0 622 throw new AssertionError("UTF-8 is unknown");
michael@0 623 }
michael@0 624 return request;
michael@0 625 }
michael@0 626
michael@0 627 private HttpDelete createDeleteRequest(URI uri) {
michael@0 628 HttpDelete request = new HttpDelete();
michael@0 629 request.setURI(uri);
michael@0 630 //request.setHeader("Host", targetHost.getHostName());
michael@0 631 request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort()));
michael@0 632 request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
michael@0 633 return request;
michael@0 634 }
michael@0 635
michael@0 636 private HttpPut createPutRequest(URI uri, String data, int depth) {
michael@0 637 HttpPut request = new HttpPut();
michael@0 638 request.setURI(uri);
michael@0 639 //request.setHeader("Host", targetHost.getHostName());
michael@0 640 request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort()));
michael@0 641 //request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
michael@0 642 request.setHeader("Content-Type", "text/calendar; charset=UTF-8");
michael@0 643 try {
michael@0 644 request.setEntity(new StringEntity(data, "UTF-8"));
michael@0 645 //request.setEntity(new StringEntity(data));
michael@0 646 } catch (UnsupportedEncodingException e) {
michael@0 647 throw new AssertionError("UTF-8 is unknown");
michael@0 648 }
michael@0 649 return request;
michael@0 650 }
michael@0 651
michael@0 652 private static HttpReport createReportRequest(URI uri, String data, int depth) {
michael@0 653 HttpReport request = new HttpReport();
michael@0 654 request.setURI(uri);
michael@0 655 //request.setHeader("Host", targetHost.getHostName());
michael@0 656 request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort()));
michael@0 657 request.setHeader("Depth", Integer.toString(depth));
michael@0 658 request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
michael@0 659 //request.setHeader("Content-Type", "text/xml;charset=\"UTF-8\"");
michael@0 660 try {
michael@0 661 request.setEntity(new StringEntity(data));
michael@0 662 } catch (UnsupportedEncodingException e) {
michael@0 663 throw new AssertionError("UTF-8 is unknown");
michael@0 664 }
michael@0 665 return request;
michael@0 666 }
michael@0 667
michael@0 668 public static void fetchEvent_old(CalendarEvent calendarEvent)
michael@0 669 throws ClientProtocolException, IOException {
michael@0 670 HttpGet request = null;
michael@0 671
michael@0 672 request = new HttpGet();
michael@0 673 request.setURI(calendarEvent.getUri());
michael@0 674 request.setHeader("Host", targetHost.getHostName());
michael@0 675 request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
michael@0 676
michael@0 677 HttpResponse response = httpClient.execute(targetHost, request);
michael@0 678
michael@0 679 BufferedReader reader = new BufferedReader(new InputStreamReader(
michael@0 680 response.getEntity().getContent(), "UTF-8"));
michael@0 681
michael@0 682 String line;
michael@0 683 String body = "";
michael@0 684 do {
michael@0 685 line = reader.readLine();
michael@0 686 if (line != null)
michael@0 687 body += line + "\n";
michael@0 688 } while (line != null);
michael@0 689
michael@0 690 calendarEvent.setICSasString(body);
michael@0 691
michael@0 692 Log.d(TAG, "HttpResponse GET event status=" + response.getStatusLine()
michael@0 693 + " body= " + body);
michael@0 694 }
michael@0 695
michael@0 696 public static boolean getEvent(CalendarEvent calendarEvent) throws ClientProtocolException, IOException {
michael@0 697 boolean Result = false;
michael@0 698 HttpReport request = null;
michael@0 699
michael@0 700 //HINT: bugfix for google calendar: replace("@", "%40")
michael@0 701 String data = XML_VERSION +
michael@0 702 "<C:calendar-multiget xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +
michael@0 703 "<D:prop>" +
michael@0 704 "<D:getetag />" +
michael@0 705 "<C:calendar-data />" +
michael@0 706 "</D:prop>" +
michael@0 707 "<D:href>" + calendarEvent.getUri().getRawPath().replace("@", "%40") + "</D:href>" +
michael@0 708 "</C:calendar-multiget>";
michael@0 709
michael@0 710 URI calendarURI = null;
michael@0 711 try {
michael@0 712 calendarURI = calendarEvent.calendarURL.toURI();
michael@0 713 } catch (URISyntaxException e) {
michael@0 714 e.printStackTrace();
michael@0 715 }
michael@0 716 //request = createReportRequest(calendarEvent.getUri(), data, 1);
michael@0 717 request = createReportRequest(calendarURI, data, 1);
michael@0 718
michael@0 719 HttpResponse response = httpClient.execute(targetHost, request);
michael@0 720
michael@0 721 BufferedReader reader = new BufferedReader(new InputStreamReader(
michael@0 722 response.getEntity().getContent(), "UTF-8"));
michael@0 723
michael@0 724 String line;
michael@0 725 String body = "";
michael@0 726 do {
michael@0 727 line = reader.readLine();
michael@0 728 if (line != null)
michael@0 729 body += line + "\n";
michael@0 730 } while (line != null);
michael@0 731
michael@0 732 if (calendarEvent.setICSasMultiStatus(body))
michael@0 733 Result = true;
michael@0 734
michael@0 735 return Result;
michael@0 736 }
michael@0 737
michael@0 738
michael@0 739 /**
michael@0 740 * sends a update event request to the server
michael@0 741 * @param uri the full URI of the event on server side. example: http://caldav.example.com/principal/user/calendar/e6be67c6-eff0-44f8-a1a0-6c2cb1029944-caldavsyncadapter.ics
michael@0 742 * @param data the full ical-data for the event
michael@0 743 * @param ETag the ETAG of this event is send within the "If-Match" Parameter to tell the server only to update this version
michael@0 744 * @return
michael@0 745 */
michael@0 746 public boolean updateEvent(URI uri, String data, String ETag) {
michael@0 747 boolean Result = false;
michael@0 748
michael@0 749 try {
michael@0 750 HttpPut request = createPutRequest(uri, data, 1);
michael@0 751 request.addHeader(mstrcHeaderIfMatch, ETag);
michael@0 752 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 753 checkStatus(response);
michael@0 754 if ((lastStatusCode == 200) || (lastStatusCode == 201) || (lastStatusCode == 204)) {
michael@0 755 Result = true;
michael@0 756 } else if (lastStatusCode == 412) {
michael@0 757 //Precondition failed
michael@0 758 Result = false;
michael@0 759 } else if (lastStatusCode == 409) {
michael@0 760 //Conflict
michael@0 761 Result = false;
michael@0 762 } else {
michael@0 763 Log.w(TAG, "Unkown StatusCode during creation of an event");
michael@0 764 }
michael@0 765 } catch (ClientProtocolException e) {
michael@0 766 e.printStackTrace();
michael@0 767 } catch (IOException e) {
michael@0 768 e.printStackTrace();
michael@0 769 } catch (AuthenticationException e) {
michael@0 770 e.printStackTrace();
michael@0 771 }
michael@0 772 return Result;
michael@0 773 }
michael@0 774
michael@0 775 /**
michael@0 776 * sends a create event request to server
michael@0 777 * @param uri the full URI of the new event on server side. example: http://caldav.example.com/principal/user/calendar/e6be67c6-eff0-44f8-a1a0-6c2cb1029944-caldavsyncadapter.ics
michael@0 778 * @param data the full ical-data for the new event
michael@0 779 * @return success of this function
michael@0 780 */
michael@0 781 public boolean createEvent(URI uri, String data) {
michael@0 782 boolean Result = false;
michael@0 783
michael@0 784 try {
michael@0 785 HttpPut request = createPutRequest(uri, data, 1);
michael@0 786 request.addHeader(mstrcHeaderIfNoneMatch, "*");
michael@0 787 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 788 checkStatus(response);
michael@0 789 if (lastStatusCode == 201) {
michael@0 790 Result = true;
michael@0 791 } else {
michael@0 792 Log.w(TAG, "Unkown StatusCode during creation of an event");
michael@0 793 }
michael@0 794 } catch (ClientProtocolException e) {
michael@0 795 e.printStackTrace();
michael@0 796 } catch (IOException e) {
michael@0 797 e.printStackTrace();
michael@0 798 } catch (AuthenticationException e) {
michael@0 799 e.printStackTrace();
michael@0 800 }
michael@0 801 return Result;
michael@0 802 }
michael@0 803
michael@0 804 /**
michael@0 805 * sends a delete event request to the server
michael@0 806 * @param calendarEventUri the full URI of the event on server side. example: http://caldav.example.com/principal/user/calendar/e6be67c6-eff0-44f8-a1a0-6c2cb1029944-caldavsyncadapter.ics
michael@0 807 * @param ETag the ETAG of this event is send within the "If-Match" Parameter to tell the server only to delete this version
michael@0 808 * @return success of this function
michael@0 809 */
michael@0 810 public boolean deleteEvent(URI calendarEventUri, String ETag) {
michael@0 811 boolean Result = false;
michael@0 812
michael@0 813 try {
michael@0 814 HttpDelete request = createDeleteRequest(calendarEventUri);
michael@0 815 request.addHeader(mstrcHeaderIfMatch, ETag);
michael@0 816 HttpResponse response = httpClient.execute(targetHost, request, mContext);
michael@0 817 checkStatus(response);
michael@0 818 if ((lastStatusCode == 204) || (lastStatusCode == 200)) {
michael@0 819 Result = true;
michael@0 820 } else {
michael@0 821 Log.w(TAG, "Unkown StatusCode during deletion of an event");
michael@0 822 }
michael@0 823 } catch (ClientProtocolException e) {
michael@0 824 e.printStackTrace();
michael@0 825 } catch (IOException e) {
michael@0 826 if (lastStatusCode == 404) {
michael@0 827 //the event has already been deleted on server side. no action needed
michael@0 828 Result = true;
michael@0 829 } else {
michael@0 830 e.printStackTrace();
michael@0 831 }
michael@0 832 } catch (AuthenticationException e) {
michael@0 833 e.printStackTrace();
michael@0 834 }
michael@0 835
michael@0 836 return Result;
michael@0 837 }
michael@0 838
michael@0 839 /**
michael@0 840 * returns the ETAG send by the last server response.
michael@0 841 * @return the ETAG
michael@0 842 */
michael@0 843 public String getLastETag() {
michael@0 844 return lastETag;
michael@0 845 }
michael@0 846
michael@0 847 /**
michael@0 848 * returns the DAV-Options send by the last server response.
michael@0 849 * @return the DAV-Options
michael@0 850 */
michael@0 851 public String getLastDav() {
michael@0 852 return lastDav;
michael@0 853 }
michael@0 854
michael@0 855 public void setVersion(String version) {
michael@0 856 VERSION = version;
michael@0 857 ((AbstractHttpClient) httpClient).getParams().setParameter(CoreProtocolPNames.USER_AGENT, this.USER_AGENT + " Version:" + VERSION);
michael@0 858 }
michael@0 859
michael@0 860 public void setAccount(Account account) {
michael@0 861 this.mAccount = account;
michael@0 862 }
michael@0 863 public void setProvider(ContentProviderClient provider) {
michael@0 864 this.mProvider = provider;
michael@0 865 }
michael@0 866 }

mercurial