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