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

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

mercurial