michael@0: /** michael@0: * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger michael@0: * michael@0: * This file is part of Andoid Caldav Sync Adapter Free. michael@0: * michael@0: * Andoid Caldav Sync Adapter Free is free software: you can redistribute michael@0: * it and/or modify it under the terms of the GNU General Public License michael@0: * as published by the Free Software Foundation, either version 3 of the michael@0: * License, or at your option any later version. michael@0: * michael@0: * Andoid Caldav Sync Adapter Free is distributed in the hope that michael@0: * it will be useful, but WITHOUT ANY WARRANTY; without even the implied michael@0: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the michael@0: * GNU General Public License for more details. michael@0: * michael@0: * You should have received a copy of the GNU General Public License michael@0: * along with Andoid Caldav Sync Adapter Free. michael@0: * If not, see . michael@0: * michael@0: */ michael@0: michael@0: package org.gege.caldavsyncadapter.caldav; michael@0: michael@0: import java.io.BufferedReader; michael@0: import java.io.ByteArrayInputStream; michael@0: import java.io.FileNotFoundException; michael@0: import java.io.IOException; michael@0: import java.io.InputStream; michael@0: import java.io.InputStreamReader; michael@0: import java.io.UnsupportedEncodingException; michael@0: import java.net.MalformedURLException; michael@0: import java.net.SocketException; michael@0: import java.net.URI; michael@0: import java.net.URISyntaxException; michael@0: import java.net.URL; michael@0: import java.util.ArrayList; michael@0: import java.util.List; michael@0: michael@0: import javax.xml.parsers.DocumentBuilder; michael@0: import javax.xml.parsers.DocumentBuilderFactory; michael@0: import javax.xml.parsers.ParserConfigurationException; michael@0: import javax.xml.parsers.SAXParser; michael@0: import javax.xml.parsers.SAXParserFactory; michael@0: michael@0: import org.apache.http.HttpException; michael@0: import org.apache.http.HttpHost; michael@0: import org.apache.http.HttpRequest; michael@0: import org.apache.http.HttpRequestInterceptor; michael@0: import org.apache.http.HttpResponse; michael@0: import org.apache.http.HttpVersion; michael@0: import org.apache.http.auth.AuthScope; michael@0: import org.apache.http.auth.AuthState; michael@0: import org.apache.http.auth.AuthenticationException; michael@0: import org.apache.http.auth.UsernamePasswordCredentials; michael@0: import org.apache.http.client.ClientProtocolException; michael@0: import org.apache.http.client.CredentialsProvider; michael@0: import org.apache.http.client.HttpClient; michael@0: import org.apache.http.client.methods.HttpDelete; michael@0: import org.apache.http.client.methods.HttpGet; michael@0: import org.apache.http.client.methods.HttpPut; michael@0: import org.apache.http.client.protocol.ClientContext; michael@0: import org.apache.http.conn.HttpHostConnectException; michael@0: import org.apache.http.conn.params.ConnManagerPNames; michael@0: import org.apache.http.conn.params.ConnPerRouteBean; michael@0: import org.apache.http.conn.scheme.PlainSocketFactory; michael@0: import org.apache.http.conn.scheme.Scheme; michael@0: import org.apache.http.conn.scheme.SchemeRegistry; michael@0: import org.apache.http.conn.ssl.SSLSocketFactory; michael@0: import org.apache.http.entity.StringEntity; michael@0: import org.apache.http.impl.client.AbstractHttpClient; michael@0: import org.apache.http.impl.client.DefaultHttpClient; michael@0: import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; michael@0: import org.apache.http.params.BasicHttpParams; michael@0: import org.apache.http.params.CoreProtocolPNames; michael@0: import org.apache.http.params.HttpParams; michael@0: import org.apache.http.params.HttpProtocolParams; michael@0: import org.apache.http.protocol.BasicHttpContext; michael@0: import org.apache.http.protocol.HttpContext; michael@0: import org.gege.caldavsyncadapter.BuildConfig; michael@0: import org.gege.caldavsyncadapter.caldav.entities.DavCalendar; michael@0: import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource; michael@0: import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent; michael@0: import org.gege.caldavsyncadapter.caldav.entities.CalendarList; michael@0: import org.gege.caldavsyncadapter.caldav.http.HttpPropFind; michael@0: import org.gege.caldavsyncadapter.caldav.http.HttpReport; michael@0: import org.gege.caldavsyncadapter.caldav.xml.CalendarHomeHandler; michael@0: import org.gege.caldavsyncadapter.caldav.xml.CalendarsHandler; michael@0: import org.gege.caldavsyncadapter.caldav.xml.ServerInfoHandler; michael@0: import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper; michael@0: import org.w3c.dom.Document; michael@0: import org.w3c.dom.Element; michael@0: import org.w3c.dom.Node; michael@0: import org.w3c.dom.NodeList; michael@0: import org.xml.sax.ContentHandler; michael@0: import org.xml.sax.InputSource; michael@0: import org.xml.sax.SAXException; michael@0: import org.xml.sax.XMLReader; michael@0: michael@0: import android.accounts.Account; michael@0: import android.content.ContentProviderClient; michael@0: import android.content.Context; michael@0: import android.util.Log; michael@0: michael@0: public class CaldavFacade { michael@0: private static final String TAG = "CaldavFacade"; michael@0: michael@0: private final static String XML_VERSION = "\n"; michael@0: michael@0: private String USER_AGENT = "CalDAV Sync Adapter (Android) https://github.com/gggard/AndroidCaldavSyncAdapater"; michael@0: private String VERSION = ""; michael@0: michael@0: private static HttpClient httpClient; michael@0: private HttpContext mContext = null; michael@0: private AuthState mLastAuthState = null; michael@0: private AuthScope mLastAuthScope = null; michael@0: michael@0: private boolean trustAll = true; michael@0: michael@0: private URL url; michael@0: michael@0: private static HttpHost targetHost; michael@0: michael@0: private int lastStatusCode; michael@0: private String lastETag; michael@0: private String lastDav; michael@0: michael@0: private String mstrcHeaderIfMatch = "If-Match"; michael@0: private String mstrcHeaderIfNoneMatch = "If-None-Match"; michael@0: michael@0: private Account mAccount = null; michael@0: private ContentProviderClient mProvider; michael@0: michael@0: protected HttpClient getHttpClient() { michael@0: michael@0: HttpParams params = new BasicHttpParams(); michael@0: params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30); michael@0: params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30)); michael@0: params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false); michael@0: HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); michael@0: michael@0: SchemeRegistry registry = new SchemeRegistry(); michael@0: registry.register(new Scheme("http", new PlainSocketFactory(), 80)); michael@0: registry.register(new Scheme("https", (trustAll ? EasySSLSocketFactory.getSocketFactory() : SSLSocketFactory.getSocketFactory()), 443)); michael@0: DefaultHttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, registry), params); michael@0: michael@0: return client; michael@0: } michael@0: michael@0: public CaldavFacade(String mUser, String mPassword, String mURL) throws MalformedURLException { michael@0: url = new URL(mURL); michael@0: michael@0: httpClient = getHttpClient(); michael@0: UsernamePasswordCredentials upc = new UsernamePasswordCredentials(mUser, mPassword); michael@0: michael@0: AuthScope as = null; michael@0: as = new AuthScope(url.getHost(), -1); michael@0: ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(as, upc); michael@0: michael@0: mContext = new BasicHttpContext(); michael@0: CredentialsProvider credProvider = ((AbstractHttpClient) httpClient).getCredentialsProvider(); michael@0: mContext.setAttribute(ClientContext.CREDS_PROVIDER, credProvider); michael@0: michael@0: //http://dlinsin.blogspot.de/2009/08/http-basic-authentication-with-android.html michael@0: ((AbstractHttpClient) httpClient).addRequestInterceptor(preemptiveAuth, 0); michael@0: michael@0: String proto = "http"; michael@0: int port = 80; michael@0: michael@0: if (url.getProtocol().equalsIgnoreCase("https")) { michael@0: proto = "https"; michael@0: if (url.getPort() == -1) michael@0: port = 443; michael@0: else michael@0: port = url.getPort(); michael@0: } michael@0: michael@0: if (url.getProtocol().equalsIgnoreCase("http")) { michael@0: proto = "http"; michael@0: if (url.getPort() == -1) michael@0: port = 80; michael@0: else michael@0: port = url.getPort(); michael@0: } michael@0: targetHost = new HttpHost(url.getHost(), port, proto); michael@0: } michael@0: michael@0: //http://dlinsin.blogspot.de/2009/08/http-basic-authentication-with-android.html michael@0: HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() { michael@0: @Override michael@0: public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { michael@0: AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); michael@0: michael@0: if (authState.getAuthScheme() == null) { michael@0: if (mLastAuthState != null) { michael@0: Log.d(TAG, "LastAuthState: restored with user " + mLastAuthState.getCredentials().getUserPrincipal().getName()); michael@0: authState.setAuthScheme(mLastAuthState.getAuthScheme()); michael@0: authState.setCredentials(mLastAuthState.getCredentials()); michael@0: } else { michael@0: Log.d(TAG, "LastAuthState: nothing to do"); michael@0: } michael@0: if (mLastAuthScope != null) { michael@0: authState.setAuthScope(mLastAuthScope); michael@0: Log.d(TAG, "LastAuthScope: restored"); michael@0: } else { michael@0: Log.d(TAG, "LastAuthScope: nothing to do"); michael@0: } michael@0: } else { michael@0: //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: mLastAuthState = authState; michael@0: Log.d(TAG, "LastAuthState: new with user " + mLastAuthState.getCredentials().getUserPrincipal().getName()); michael@0: if (authState.getAuthScope() != null) { michael@0: mLastAuthScope = authState.getAuthScope(); michael@0: Log.d(TAG, "LastAuthScope: new"); michael@0: } michael@0: } michael@0: } michael@0: }; michael@0: michael@0: public enum TestConnectionResult { michael@0: WRONG_CREDENTIAL, michael@0: WRONG_URL, michael@0: WRONG_SERVER_STATUS, michael@0: WRONG_ANSWER, michael@0: SUCCESS michael@0: } michael@0: michael@0: /** michael@0: * TODO: testConnection should return only an instance of michael@0: * TestConnectionResult without throwing an exception or only throw checked michael@0: * exceptions so you don't have to check the result of this function AND michael@0: * handle the exceptions michael@0: * @param context michael@0: * michael@0: * @return {@link TestConnectionResult} michael@0: * @throws HttpHostConnectException michael@0: * @throws IOException michael@0: * @throws URISyntaxException michael@0: * @throws ParserConfigurationException michael@0: * @throws SAXException michael@0: */ michael@0: public TestConnectionResult testConnection() throws HttpHostConnectException, IOException, URISyntaxException, ParserConfigurationException, SAXException { michael@0: Log.d(TAG, "start testConnection "); michael@0: try { michael@0: List calendars = new ArrayList(); michael@0: calendars = forceGetCalendarsFromUri(null, url.toURI()); michael@0: if (calendars.size() != 0) { michael@0: return TestConnectionResult.SUCCESS; michael@0: } michael@0: michael@0: URI userPrincipal = getUserPrincipal(); michael@0: List calendarSets = getCalendarHomes(userPrincipal); michael@0: for (URI calendarSet : calendarSets) { michael@0: List calendarSetCalendars = getCalendarsFromSet(calendarSet); michael@0: calendars.addAll(calendarSetCalendars); michael@0: } michael@0: if (calendarSets.size() == 0) { michael@0: return TestConnectionResult.WRONG_ANSWER; michael@0: } michael@0: } catch (FileNotFoundException e) { michael@0: return TestConnectionResult.WRONG_URL; michael@0: } catch (SocketException e) { michael@0: return TestConnectionResult.WRONG_URL; michael@0: } catch (AuthenticationException e) { michael@0: return TestConnectionResult.WRONG_CREDENTIAL; michael@0: } catch (ClientProtocolException e) { michael@0: return TestConnectionResult.WRONG_SERVER_STATUS; michael@0: } catch (CaldavProtocolException e) { michael@0: return TestConnectionResult.WRONG_ANSWER; michael@0: } michael@0: return TestConnectionResult.SUCCESS; michael@0: } michael@0: michael@0: /** michael@0: * @param context May be null if no notification is needed michael@0: * @param uri michael@0: * @return michael@0: * @throws AuthenticationException michael@0: * @throws FileNotFoundException michael@0: */ michael@0: private List forceGetCalendarsFromUri(Context context, URI uri) throws AuthenticationException, FileNotFoundException { michael@0: List calendars = new ArrayList(); michael@0: Exception exception = null; michael@0: try { michael@0: calendars = getCalendarsFromSet(uri); michael@0: } catch (ClientProtocolException e) { michael@0: if (context != null) { michael@0: NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e); michael@0: } michael@0: exception = e; michael@0: } catch (FileNotFoundException e) { michael@0: if (context != null) { michael@0: NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e); michael@0: } michael@0: throw e; michael@0: } catch (IOException e) { michael@0: if (context != null) { michael@0: NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e); michael@0: } michael@0: exception = e; michael@0: } catch (CaldavProtocolException e) { michael@0: michael@0: if (context != null) { michael@0: NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e); michael@0: } michael@0: exception = e; michael@0: } michael@0: if (exception != null && BuildConfig.DEBUG) { michael@0: Log.e(TAG, "Force get calendars from '" + uri.toString() michael@0: + "' failed " + exception.getClass().getCanonicalName() michael@0: + ": " + exception.getMessage()); michael@0: } michael@0: return calendars; michael@0: } michael@0: michael@0: private final static String PROPFIND_USER_PRINCIPAL = XML_VERSION + michael@0: "" + michael@0: "" + michael@0: "" + michael@0: "" + michael@0: "" + michael@0: ""; michael@0: michael@0: private URI getUserPrincipal() throws SocketException, michael@0: ClientProtocolException, AuthenticationException, michael@0: FileNotFoundException, IOException, CaldavProtocolException, michael@0: URISyntaxException { michael@0: URI uri = this.url.toURI(); michael@0: HttpPropFind request = createPropFindRequest(uri, michael@0: PROPFIND_USER_PRINCIPAL, 0); michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: checkStatus(response); michael@0: ServerInfoHandler serverInfoHandler = new ServerInfoHandler(); michael@0: parseXML(response, serverInfoHandler); michael@0: String userPrincipal = null; michael@0: if (serverInfoHandler.currentUserPrincipal != null) { michael@0: userPrincipal = serverInfoHandler.currentUserPrincipal; michael@0: } else if (serverInfoHandler.principalUrl != null) { michael@0: userPrincipal = serverInfoHandler.principalUrl; michael@0: } else { michael@0: throw new CaldavProtocolException("no principal url found"); michael@0: } michael@0: try { michael@0: URI userPrincipalUri = new URI(userPrincipal); michael@0: userPrincipalUri = uri.resolve(userPrincipalUri); michael@0: if (BuildConfig.DEBUG) { michael@0: Log.d(TAG, michael@0: "Found userPrincipal: " + userPrincipalUri.toString()); michael@0: } michael@0: return userPrincipalUri; michael@0: } catch (URISyntaxException e) { michael@0: throw new CaldavProtocolException("principal url '" + userPrincipal michael@0: + "' malformed"); michael@0: } michael@0: } michael@0: michael@0: private final static String PROPFIND_CALENDAR_HOME_SET = XML_VERSION michael@0: + ""; michael@0: michael@0: private List getCalendarHomes(URI userPrincipal) michael@0: throws ClientProtocolException, IOException, michael@0: AuthenticationException, FileNotFoundException, michael@0: CaldavProtocolException { michael@0: HttpPropFind request = createPropFindRequest(userPrincipal, michael@0: PROPFIND_CALENDAR_HOME_SET, 0); michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: checkStatus(response); michael@0: CalendarHomeHandler calendarHomeHandler = new CalendarHomeHandler( michael@0: userPrincipal); michael@0: parseXML(response, calendarHomeHandler); michael@0: List result = calendarHomeHandler.calendarHomeSet; michael@0: if (BuildConfig.DEBUG) { michael@0: Log.d(TAG, result.size() + " calendar-home-set found in " michael@0: + userPrincipal.toString()); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: private final static String PROPFIND_CALENDER_LIST = XML_VERSION michael@0: + "" michael@0: + "" michael@0: // + michael@0: // "" michael@0: // + michael@0: // " michael@0: + "" michael@0: //" michael@0: + ""; michael@0: michael@0: michael@0: private List getCalendarsFromSet(URI calendarSet) michael@0: throws ClientProtocolException, IOException, michael@0: CaldavProtocolException, AuthenticationException, michael@0: FileNotFoundException { michael@0: HttpPropFind request = createPropFindRequest(calendarSet, PROPFIND_CALENDER_LIST, 1); michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: checkStatus(response); michael@0: CalendarsHandler calendarsHandler = new CalendarsHandler(calendarSet); michael@0: parseXML(response, calendarsHandler); michael@0: List result = calendarsHandler.calendars; michael@0: if (BuildConfig.DEBUG) { michael@0: Log.i(TAG, michael@0: result.size() + " calendars found in set " michael@0: + calendarSet.toString()); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: /** michael@0: * Discover CalDAV Calendars Mentioned in michael@0: * http://tools.ietf.org/html/draft-daboo-srv-caldav-10#section-6 and michael@0: * http://code.google.com/p/sabredav/wiki/BuildingACalDAVClient#Discovery michael@0: *
    michael@0: *
  1. PROPFIND calendar-home-set on url michael@0: *
  2. PROPFIND DAV:current-user-principal or principal-URL on url michael@0: *
  3. PROPFIND calendar-home-set on current-user-principal or principal-URL michael@0: *
  4. PROPFIND displayname, resourcetype, getctag on CalendarHomeSets michael@0: *
michael@0: * @param context michael@0: * michael@0: * @return List of {@link DavCalendar} michael@0: * @throws ClientProtocolException michael@0: * http protocol error michael@0: * @throws IOException michael@0: * Connection lost michael@0: * @throws URISyntaxException michael@0: * url in Constructor malformed michael@0: * @throws CaldavProtocolException michael@0: * caldav protocol error michael@0: */ michael@0: //public Iterable getCalendarList(Context context) throws ClientProtocolException, michael@0: public CalendarList getCalendarList(Context context) throws ClientProtocolException, michael@0: IOException, URISyntaxException, ParserConfigurationException, michael@0: CaldavProtocolException { michael@0: try { michael@0: CalendarList Result = new CalendarList(this.mAccount, this.mProvider, CalendarSource.CalDAV, this.url.toString()); michael@0: List calendars = new ArrayList(); michael@0: michael@0: calendars = forceGetCalendarsFromUri(context, this.url.toURI()); michael@0: michael@0: if (calendars.size() == 0) { michael@0: // no calendars found, try the home-set michael@0: URI userPrincipal = getUserPrincipal(); michael@0: List calendarSets = getCalendarHomes(userPrincipal); michael@0: for (URI calendarSet : calendarSets) { michael@0: List calendarSetCalendars = getCalendarsFromSet(calendarSet); michael@0: calendars.addAll(calendarSetCalendars); michael@0: } michael@0: } michael@0: for (DavCalendar cal : calendars) { michael@0: Result.addCalendar(cal); michael@0: } michael@0: michael@0: //return calendars; michael@0: return Result; michael@0: } catch (AuthenticationException e) { michael@0: throw new IOException(e); michael@0: } michael@0: } michael@0: michael@0: //public Iterable getCalendarEvents(DavCalendar calendar) michael@0: public ArrayList getCalendarEvents(DavCalendar calendar) michael@0: throws URISyntaxException, ClientProtocolException, IOException, michael@0: ParserConfigurationException, SAXException { michael@0: michael@0: ArrayList calendarEventList = new ArrayList(); michael@0: michael@0: String requestBody = "" michael@0: + "" + "" + "" michael@0: + "" + ""; michael@0: michael@0: HttpPropFind request = null; michael@0: michael@0: String EventUri; michael@0: michael@0: /*request = new HttpPropFind(); michael@0: request.setURI(calendar.getURI()); michael@0: request.setHeader("Host", targetHost.getHostName()); michael@0: request.setHeader("Depth", "1"); michael@0: request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\""); michael@0: michael@0: try { michael@0: request.setEntity(new StringEntity(requestBody, "UTF-8")); michael@0: } catch (UnsupportedEncodingException e) { michael@0: throw new AssertionError("UTF-8 is unknown"); michael@0: }*/ michael@0: request = this.createPropFindRequest(calendar.getURI(), requestBody, 1); michael@0: michael@0: Log.d(TAG, "Getting eTag by PROPFIND at " + request.getURI()); michael@0: michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: michael@0: BufferedReader reader = new BufferedReader(new InputStreamReader( michael@0: response.getEntity().getContent(), "UTF-8")); michael@0: michael@0: String line; michael@0: String body = ""; michael@0: do { michael@0: line = reader.readLine(); michael@0: if (line != null) michael@0: body += line; michael@0: } while (line != null); michael@0: michael@0: Log.d(TAG, "HttpResponse status=" + response.getStatusLine() michael@0: + " body= " + body); michael@0: michael@0: DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); michael@0: factory.setNamespaceAware(true); michael@0: DocumentBuilder builder = factory.newDocumentBuilder(); michael@0: Document dom = builder.parse(new InputSource(new ByteArrayInputStream( michael@0: body.getBytes("utf-8")))); michael@0: Element root = dom.getDocumentElement(); michael@0: NodeList items = root.getElementsByTagNameNS("*", "getetag"); michael@0: michael@0: for (int i = 0; i < items.getLength(); i++) { michael@0: CalendarEvent calendarEvent = new CalendarEvent(this.mAccount, this.mProvider); michael@0: michael@0: Node node = items.item(i); michael@0: michael@0: if (node.getTextContent().trim().length() == 0) michael@0: continue; // not an event michael@0: michael@0: calendarEvent.setETag(node.getTextContent().trim()); michael@0: //calendarEvent.calendarURL = this.url; michael@0: calendarEvent.calendarURL = calendar.getURI().toURL(); michael@0: michael@0: node = node.getParentNode(); // prop michael@0: node = node.getParentNode(); // propstat michael@0: node = node.getParentNode(); // response michael@0: michael@0: NodeList children = node.getChildNodes(); michael@0: for (int j = 0; j < children.getLength(); j++) { michael@0: Node childNode = children.item(j); michael@0: if ((childNode.getLocalName()!=null) && (childNode.getLocalName().equalsIgnoreCase("href"))) { michael@0: EventUri = childNode.getTextContent().trim(); michael@0: //HINT: bugfix for zimbra calendar: replace("@", "%40") michael@0: EventUri = EventUri.replace("@", "%40"); michael@0: calendarEvent.setUri(new URI(EventUri)); michael@0: } michael@0: } michael@0: michael@0: calendarEventList.add(calendarEvent); michael@0: michael@0: } michael@0: michael@0: return calendarEventList; michael@0: } michael@0: michael@0: private void parseXML(HttpResponse response, ContentHandler contentHandler) michael@0: throws IOException, CaldavProtocolException { michael@0: InputStream is = response.getEntity().getContent(); michael@0: /*BufferedReader bReader = new BufferedReader(new InputStreamReader(is, "UTF-8")); michael@0: String Content = ""; michael@0: String Line = bReader.readLine(); michael@0: michael@0: while (Line != null) { michael@0: Content += Line; michael@0: Line = bReader.readLine(); michael@0: }*/ michael@0: michael@0: SAXParserFactory factory = SAXParserFactory.newInstance(); michael@0: try { michael@0: SAXParser parser = factory.newSAXParser(); michael@0: XMLReader reader = parser.getXMLReader(); michael@0: reader.setContentHandler(contentHandler); michael@0: reader.parse(new InputSource(is)); michael@0: } catch (ParserConfigurationException e) { michael@0: throw new AssertionError("ParserConfigurationException " michael@0: + e.getMessage()); michael@0: } catch (IllegalStateException e) { michael@0: throw new CaldavProtocolException(e.getMessage()); michael@0: } catch (SAXException e) { michael@0: throw new CaldavProtocolException(e.getMessage()); michael@0: } michael@0: } michael@0: michael@0: private void checkStatus(HttpResponse response) michael@0: throws AuthenticationException, FileNotFoundException, michael@0: ClientProtocolException { michael@0: final int statusCode = response.getStatusLine().getStatusCode(); michael@0: lastStatusCode = statusCode; michael@0: if (response.containsHeader("ETag")) michael@0: lastETag = response.getFirstHeader("ETag").getValue(); michael@0: else michael@0: lastETag = ""; michael@0: if (response.containsHeader("DAV")) michael@0: lastDav = response.getFirstHeader("DAV").getValue(); michael@0: else michael@0: lastDav = ""; michael@0: michael@0: switch (statusCode) { michael@0: case 401: michael@0: throw new AuthenticationException(); michael@0: case 404: michael@0: throw new FileNotFoundException(); michael@0: case 409: //Conflict michael@0: case 412: michael@0: case 200: michael@0: case 201: michael@0: case 204: michael@0: case 207: michael@0: return; michael@0: default: michael@0: throw new ClientProtocolException("StatusCode: " + statusCode); michael@0: } michael@0: } michael@0: michael@0: private HttpPropFind createPropFindRequest(URI uri, String data, int depth) { michael@0: HttpPropFind request = new HttpPropFind(); michael@0: michael@0: request.setURI(uri); michael@0: //request.setHeader("Host", targetHost.getHostName()); michael@0: request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort())); michael@0: request.setHeader("Depth", Integer.toString(depth)); michael@0: request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\""); michael@0: try { michael@0: request.setEntity(new StringEntity(data, "UTF-8")); michael@0: } catch (UnsupportedEncodingException e) { michael@0: throw new AssertionError("UTF-8 is unknown"); michael@0: } michael@0: return request; michael@0: } michael@0: michael@0: private HttpDelete createDeleteRequest(URI uri) { michael@0: HttpDelete request = new HttpDelete(); michael@0: request.setURI(uri); michael@0: //request.setHeader("Host", targetHost.getHostName()); michael@0: request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort())); michael@0: request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\""); michael@0: return request; michael@0: } michael@0: michael@0: private HttpPut createPutRequest(URI uri, String data, int depth) { michael@0: HttpPut request = new HttpPut(); michael@0: request.setURI(uri); michael@0: //request.setHeader("Host", targetHost.getHostName()); michael@0: request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort())); michael@0: //request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\""); michael@0: request.setHeader("Content-Type", "text/calendar; charset=UTF-8"); michael@0: try { michael@0: request.setEntity(new StringEntity(data, "UTF-8")); michael@0: //request.setEntity(new StringEntity(data)); michael@0: } catch (UnsupportedEncodingException e) { michael@0: throw new AssertionError("UTF-8 is unknown"); michael@0: } michael@0: return request; michael@0: } michael@0: michael@0: private static HttpReport createReportRequest(URI uri, String data, int depth) { michael@0: HttpReport request = new HttpReport(); michael@0: request.setURI(uri); michael@0: //request.setHeader("Host", targetHost.getHostName()); michael@0: request.setHeader("Host", targetHost.getHostName() + ":" + String.valueOf(targetHost.getPort())); michael@0: request.setHeader("Depth", Integer.toString(depth)); michael@0: request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\""); michael@0: //request.setHeader("Content-Type", "text/xml;charset=\"UTF-8\""); michael@0: try { michael@0: request.setEntity(new StringEntity(data)); michael@0: } catch (UnsupportedEncodingException e) { michael@0: throw new AssertionError("UTF-8 is unknown"); michael@0: } michael@0: return request; michael@0: } michael@0: michael@0: public static void fetchEvent_old(CalendarEvent calendarEvent) michael@0: throws ClientProtocolException, IOException { michael@0: HttpGet request = null; michael@0: michael@0: request = new HttpGet(); michael@0: request.setURI(calendarEvent.getUri()); michael@0: request.setHeader("Host", targetHost.getHostName()); michael@0: request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\""); michael@0: michael@0: HttpResponse response = httpClient.execute(targetHost, request); michael@0: michael@0: BufferedReader reader = new BufferedReader(new InputStreamReader( michael@0: response.getEntity().getContent(), "UTF-8")); michael@0: michael@0: String line; michael@0: String body = ""; michael@0: do { michael@0: line = reader.readLine(); michael@0: if (line != null) michael@0: body += line + "\n"; michael@0: } while (line != null); michael@0: michael@0: calendarEvent.setICSasString(body); michael@0: michael@0: Log.d(TAG, "HttpResponse GET event status=" + response.getStatusLine() michael@0: + " body= " + body); michael@0: } michael@0: michael@0: public static boolean getEvent(CalendarEvent calendarEvent) throws ClientProtocolException, IOException { michael@0: boolean Result = false; michael@0: HttpReport request = null; michael@0: michael@0: //HINT: bugfix for google calendar: replace("@", "%40") michael@0: String data = XML_VERSION + michael@0: "" + michael@0: "" + michael@0: "" + michael@0: "" + michael@0: "" + michael@0: "" + calendarEvent.getUri().getRawPath().replace("@", "%40") + "" + michael@0: ""; michael@0: michael@0: URI calendarURI = null; michael@0: try { michael@0: calendarURI = calendarEvent.calendarURL.toURI(); michael@0: } catch (URISyntaxException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: //request = createReportRequest(calendarEvent.getUri(), data, 1); michael@0: request = createReportRequest(calendarURI, data, 1); michael@0: michael@0: HttpResponse response = httpClient.execute(targetHost, request); michael@0: michael@0: BufferedReader reader = new BufferedReader(new InputStreamReader( michael@0: response.getEntity().getContent(), "UTF-8")); michael@0: michael@0: String line; michael@0: String body = ""; michael@0: do { michael@0: line = reader.readLine(); michael@0: if (line != null) michael@0: body += line + "\n"; michael@0: } while (line != null); michael@0: michael@0: if (calendarEvent.setICSasMultiStatus(body)) michael@0: Result = true; michael@0: michael@0: return Result; michael@0: } michael@0: michael@0: michael@0: /** michael@0: * sends a update event request to the server michael@0: * @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: * @param data the full ical-data for the event michael@0: * @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: * @return michael@0: */ michael@0: public boolean updateEvent(URI uri, String data, String ETag) { michael@0: boolean Result = false; michael@0: michael@0: try { michael@0: HttpPut request = createPutRequest(uri, data, 1); michael@0: request.addHeader(mstrcHeaderIfMatch, ETag); michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: checkStatus(response); michael@0: if ((lastStatusCode == 200) || (lastStatusCode == 201) || (lastStatusCode == 204)) { michael@0: Result = true; michael@0: } else if (lastStatusCode == 412) { michael@0: //Precondition failed michael@0: Result = false; michael@0: } else if (lastStatusCode == 409) { michael@0: //Conflict michael@0: Result = false; michael@0: } else { michael@0: Log.w(TAG, "Unkown StatusCode during creation of an event"); michael@0: } michael@0: } catch (ClientProtocolException e) { michael@0: e.printStackTrace(); michael@0: } catch (IOException e) { michael@0: e.printStackTrace(); michael@0: } catch (AuthenticationException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: return Result; michael@0: } michael@0: michael@0: /** michael@0: * sends a create event request to server michael@0: * @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: * @param data the full ical-data for the new event michael@0: * @return success of this function michael@0: */ michael@0: public boolean createEvent(URI uri, String data) { michael@0: boolean Result = false; michael@0: michael@0: try { michael@0: HttpPut request = createPutRequest(uri, data, 1); michael@0: request.addHeader(mstrcHeaderIfNoneMatch, "*"); michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: checkStatus(response); michael@0: if (lastStatusCode == 201) { michael@0: Result = true; michael@0: } else { michael@0: Log.w(TAG, "Unkown StatusCode during creation of an event"); michael@0: } michael@0: } catch (ClientProtocolException e) { michael@0: e.printStackTrace(); michael@0: } catch (IOException e) { michael@0: e.printStackTrace(); michael@0: } catch (AuthenticationException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: return Result; michael@0: } michael@0: michael@0: /** michael@0: * sends a delete event request to the server michael@0: * @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: * @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: * @return success of this function michael@0: */ michael@0: public boolean deleteEvent(URI calendarEventUri, String ETag) { michael@0: boolean Result = false; michael@0: michael@0: try { michael@0: HttpDelete request = createDeleteRequest(calendarEventUri); michael@0: request.addHeader(mstrcHeaderIfMatch, ETag); michael@0: HttpResponse response = httpClient.execute(targetHost, request, mContext); michael@0: checkStatus(response); michael@0: if ((lastStatusCode == 204) || (lastStatusCode == 200)) { michael@0: Result = true; michael@0: } else { michael@0: Log.w(TAG, "Unkown StatusCode during deletion of an event"); michael@0: } michael@0: } catch (ClientProtocolException e) { michael@0: e.printStackTrace(); michael@0: } catch (IOException e) { michael@0: if (lastStatusCode == 404) { michael@0: //the event has already been deleted on server side. no action needed michael@0: Result = true; michael@0: } else { michael@0: e.printStackTrace(); michael@0: } michael@0: } catch (AuthenticationException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: michael@0: return Result; michael@0: } michael@0: michael@0: /** michael@0: * returns the ETAG send by the last server response. michael@0: * @return the ETAG michael@0: */ michael@0: public String getLastETag() { michael@0: return lastETag; michael@0: } michael@0: michael@0: /** michael@0: * returns the DAV-Options send by the last server response. michael@0: * @return the DAV-Options michael@0: */ michael@0: public String getLastDav() { michael@0: return lastDav; michael@0: } michael@0: michael@0: public void setVersion(String version) { michael@0: VERSION = version; michael@0: ((AbstractHttpClient) httpClient).getParams().setParameter(CoreProtocolPNames.USER_AGENT, this.USER_AGENT + " Version:" + VERSION); michael@0: } michael@0: michael@0: public void setAccount(Account account) { michael@0: this.mAccount = account; michael@0: } michael@0: public void setProvider(ContentProviderClient provider) { michael@0: this.mProvider = provider; michael@0: } michael@0: }