org.gege.caldavsyncadapter.caldav.CaldavFacade.java Source code

Java tutorial

Introduction

Here is the source code for org.gege.caldavsyncadapter.caldav.CaldavFacade.java

Source

/**
 * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger
 * 
 * This file is part of Andoid Caldav Sync Adapter Free.
 *
 * Andoid Caldav Sync Adapter Free is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the 
 * License, or at your option any later version.
 *
 * Andoid Caldav Sync Adapter Free is distributed in the hope that 
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Andoid Caldav Sync Adapter Free.  
 * If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package org.gege.caldavsyncadapter.caldav;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.conn.params.ConnManagerPNames;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.AbstractHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.gege.caldavsyncadapter.BuildConfig;
import org.gege.caldavsyncadapter.caldav.entities.DavCalendar;
import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource;
import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent;
import org.gege.caldavsyncadapter.caldav.entities.CalendarList;
import org.gege.caldavsyncadapter.caldav.http.HttpPropFind;
import org.gege.caldavsyncadapter.caldav.http.HttpReport;
import org.gege.caldavsyncadapter.caldav.xml.CalendarHomeHandler;
import org.gege.caldavsyncadapter.caldav.xml.CalendarsHandler;
import org.gege.caldavsyncadapter.caldav.xml.ServerInfoHandler;
import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.Context;
import android.util.Log;

public class CaldavFacade {
    private static final String TAG = "CaldavFacade";

    private final static String XML_VERSION = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";

    private String USER_AGENT = "CalDAV Sync Adapter (Android) https://github.com/gggard/AndroidCaldavSyncAdapater";
    private String VERSION = "";

    private static HttpClient httpClient;
    private HttpContext mContext = null;
    private AuthState mLastAuthState = null;
    private AuthScope mLastAuthScope = null;

    private boolean trustAll = true;

    private URL url;

    private static HttpHost targetHost;

    private int lastStatusCode;
    private String lastETag;
    private String lastDav;

    private String mstrcHeaderIfMatch = "If-Match";
    private String mstrcHeaderIfNoneMatch = "If-None-Match";

    private Account mAccount = null;
    private ContentProviderClient mProvider;

    protected HttpClient getHttpClient() {

        HttpParams params = new BasicHttpParams();
        params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
        params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
        params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("http", new PlainSocketFactory(), 80));
        registry.register(new Scheme("https",
                (trustAll ? EasySSLSocketFactory.getSocketFactory() : SSLSocketFactory.getSocketFactory()), 443));
        DefaultHttpClient client = new DefaultHttpClient(new ThreadSafeClientConnManager(params, registry), params);

        return client;
    }

    public CaldavFacade(String mUser, String mPassword, String mURL) throws MalformedURLException {
        url = new URL(mURL);

        httpClient = getHttpClient();
        UsernamePasswordCredentials upc = new UsernamePasswordCredentials(mUser, mPassword);

        AuthScope as = null;
        as = new AuthScope(url.getHost(), -1);
        ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(as, upc);

        mContext = new BasicHttpContext();
        CredentialsProvider credProvider = ((AbstractHttpClient) httpClient).getCredentialsProvider();
        mContext.setAttribute(ClientContext.CREDS_PROVIDER, credProvider);

        //http://dlinsin.blogspot.de/2009/08/http-basic-authentication-with-android.html
        ((AbstractHttpClient) httpClient).addRequestInterceptor(preemptiveAuth, 0);

        String proto = "http";
        int port = 80;

        if (url.getProtocol().equalsIgnoreCase("https")) {
            proto = "https";
            if (url.getPort() == -1)
                port = 443;
            else
                port = url.getPort();
        }

        if (url.getProtocol().equalsIgnoreCase("http")) {
            proto = "http";
            if (url.getPort() == -1)
                port = 80;
            else
                port = url.getPort();
        }
        targetHost = new HttpHost(url.getHost(), port, proto);
    }

    //http://dlinsin.blogspot.de/2009/08/http-basic-authentication-with-android.html
    HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
        @Override
        public void process(final HttpRequest request, final HttpContext context)
                throws HttpException, IOException {
            AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);

            if (authState.getAuthScheme() == null) {
                if (mLastAuthState != null) {
                    Log.d(TAG, "LastAuthState: restored with user "
                            + mLastAuthState.getCredentials().getUserPrincipal().getName());
                    authState.setAuthScheme(mLastAuthState.getAuthScheme());
                    authState.setCredentials(mLastAuthState.getCredentials());
                } else {
                    Log.d(TAG, "LastAuthState: nothing to do");
                }
                if (mLastAuthScope != null) {
                    authState.setAuthScope(mLastAuthScope);
                    Log.d(TAG, "LastAuthScope: restored");
                } else {
                    Log.d(TAG, "LastAuthScope: nothing to do");
                }
            } else {
                //AuthState and AuthScope have to be saved separate because of the AuthScope within AuthState gets lost, so we save it in a separate var.
                mLastAuthState = authState;
                Log.d(TAG, "LastAuthState: new with user "
                        + mLastAuthState.getCredentials().getUserPrincipal().getName());
                if (authState.getAuthScope() != null) {
                    mLastAuthScope = authState.getAuthScope();
                    Log.d(TAG, "LastAuthScope: new");
                }
            }
        }
    };

    public enum TestConnectionResult {
        WRONG_CREDENTIAL, WRONG_URL, WRONG_SERVER_STATUS, WRONG_ANSWER, SUCCESS
    }

    /**
     * TODO: testConnection should return only an instance of
     * TestConnectionResult without throwing an exception or only throw checked
     * exceptions so you don't have to check the result of this function AND
     * handle the exceptions
     * @param context 
     * 
     * @return {@link TestConnectionResult}
     * @throws HttpHostConnectException
     * @throws IOException
     * @throws URISyntaxException
     * @throws ParserConfigurationException
     * @throws SAXException
     */
    public TestConnectionResult testConnection() throws HttpHostConnectException, IOException, URISyntaxException,
            ParserConfigurationException, SAXException {
        Log.d(TAG, "start testConnection ");
        try {
            List<DavCalendar> calendars = new ArrayList<DavCalendar>();
            calendars = forceGetCalendarsFromUri(null, url.toURI());
            if (calendars.size() != 0) {
                return TestConnectionResult.SUCCESS;
            }

            URI userPrincipal = getUserPrincipal();
            List<URI> calendarSets = getCalendarHomes(userPrincipal);
            for (URI calendarSet : calendarSets) {
                List<DavCalendar> calendarSetCalendars = getCalendarsFromSet(calendarSet);
                calendars.addAll(calendarSetCalendars);
            }
            if (calendarSets.size() == 0) {
                return TestConnectionResult.WRONG_ANSWER;
            }
        } catch (FileNotFoundException e) {
            return TestConnectionResult.WRONG_URL;
        } catch (SocketException e) {
            return TestConnectionResult.WRONG_URL;
        } catch (AuthenticationException e) {
            return TestConnectionResult.WRONG_CREDENTIAL;
        } catch (ClientProtocolException e) {
            return TestConnectionResult.WRONG_SERVER_STATUS;
        } catch (CaldavProtocolException e) {
            return TestConnectionResult.WRONG_ANSWER;
        }
        return TestConnectionResult.SUCCESS;
    }

    /**
     * @param context May be null if no notification is needed
     * @param uri
     * @return
     * @throws AuthenticationException
     * @throws FileNotFoundException
     */
    private List<DavCalendar> forceGetCalendarsFromUri(Context context, URI uri)
            throws AuthenticationException, FileNotFoundException {
        List<DavCalendar> calendars = new ArrayList<DavCalendar>();
        Exception exception = null;
        try {
            calendars = getCalendarsFromSet(uri);
        } catch (ClientProtocolException e) {
            if (context != null) {
                NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
                //NotificationsHelper.getCurrentSyncLog().addException(e);
            }
            exception = e;
        } catch (FileNotFoundException e) {
            if (context != null) {
                NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
                //NotificationsHelper.getCurrentSyncLog().addException(e);
            }
            throw e;
        } catch (IOException e) {
            if (context != null) {
                NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
                //NotificationsHelper.getCurrentSyncLog().addException(e);
            }
            exception = e;
        } catch (CaldavProtocolException e) {

            if (context != null) {
                NotificationsHelper.signalSyncErrors(context, "Caldav sync problem", e.getMessage());
                //NotificationsHelper.getCurrentSyncLog().addException(e);
            }
            exception = e;
        }
        if (exception != null && BuildConfig.DEBUG) {
            Log.e(TAG, "Force get calendars from '" + uri.toString() + "' failed "
                    + exception.getClass().getCanonicalName() + ": " + exception.getMessage());
        }
        return calendars;
    }

    private final static String PROPFIND_USER_PRINCIPAL = XML_VERSION
            + "<d:propfind xmlns:d=\"DAV:\"><d:prop><d:current-user-principal /><d:principal-URL /></d:prop></d:propfind>";

    private URI getUserPrincipal() throws SocketException, ClientProtocolException, AuthenticationException,
            FileNotFoundException, IOException, CaldavProtocolException, URISyntaxException {
        URI uri = this.url.toURI();
        HttpPropFind request = createPropFindRequest(uri, PROPFIND_USER_PRINCIPAL, 0);
        HttpResponse response = httpClient.execute(targetHost, request, mContext);
        checkStatus(response);
        ServerInfoHandler serverInfoHandler = new ServerInfoHandler();
        parseXML(response, serverInfoHandler);
        String userPrincipal = null;
        if (serverInfoHandler.currentUserPrincipal != null) {
            userPrincipal = serverInfoHandler.currentUserPrincipal;
        } else if (serverInfoHandler.principalUrl != null) {
            userPrincipal = serverInfoHandler.principalUrl;
        } else {
            throw new CaldavProtocolException("no principal url found");
        }
        try {
            URI userPrincipalUri = new URI(userPrincipal);
            userPrincipalUri = uri.resolve(userPrincipalUri);
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "Found userPrincipal: " + userPrincipalUri.toString());
            }
            return userPrincipalUri;
        } catch (URISyntaxException e) {
            throw new CaldavProtocolException("principal url '" + userPrincipal + "' malformed");
        }
    }

    private final static String PROPFIND_CALENDAR_HOME_SET = XML_VERSION
            + "<d:propfind xmlns:d=\"DAV:\" xmlns:c=\"urn:ietf:params:xml:ns:caldav\"><d:prop><c:calendar-home-set/></d:prop></d:propfind>";

    private List<URI> getCalendarHomes(URI userPrincipal) throws ClientProtocolException, IOException,
            AuthenticationException, FileNotFoundException, CaldavProtocolException {
        HttpPropFind request = createPropFindRequest(userPrincipal, PROPFIND_CALENDAR_HOME_SET, 0);
        HttpResponse response = httpClient.execute(targetHost, request, mContext);
        checkStatus(response);
        CalendarHomeHandler calendarHomeHandler = new CalendarHomeHandler(userPrincipal);
        parseXML(response, calendarHomeHandler);
        List<URI> result = calendarHomeHandler.calendarHomeSet;
        if (BuildConfig.DEBUG) {
            Log.d(TAG, result.size() + " calendar-home-set found in " + userPrincipal.toString());
        }
        return result;
    }

    private final static String PROPFIND_CALENDER_LIST = XML_VERSION
            + "<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/\">"
            + "<d:prop><d:displayname /><d:resourcetype />"
            // +
            // "<d:supported-method-set /><d:supported-report-set /><c:supported-calendar-component-set />"
            // +
            // "<c:calendar-description /><c:calendar-timezone /><c:calendar-free-busy-set />
            + "<ic:calendar-color />"
            //<ic:calendar-order />"
            + "<cs:getctag /></d:prop></d:propfind>";

    private List<DavCalendar> getCalendarsFromSet(URI calendarSet) throws ClientProtocolException, IOException,
            CaldavProtocolException, AuthenticationException, FileNotFoundException {
        HttpPropFind request = createPropFindRequest(calendarSet, PROPFIND_CALENDER_LIST, 1);
        HttpResponse response = httpClient.execute(targetHost, request, mContext);
        checkStatus(response);
        CalendarsHandler calendarsHandler = new CalendarsHandler(calendarSet);
        parseXML(response, calendarsHandler);
        List<DavCalendar> result = calendarsHandler.calendars;
        if (BuildConfig.DEBUG) {
            Log.i(TAG, result.size() + " calendars found in set " + calendarSet.toString());
        }
        return result;
    }

    /**
     * Discover CalDAV Calendars Mentioned in
     * http://tools.ietf.org/html/draft-daboo-srv-caldav-10#section-6 and
     * http://code.google.com/p/sabredav/wiki/BuildingACalDAVClient#Discovery
     * <ol>
     * <li>PROPFIND calendar-home-set on url
     * <li>PROPFIND DAV:current-user-principal or principal-URL on url
     * <li>PROPFIND calendar-home-set on current-user-principal or principal-URL
     * <li>PROPFIND displayname, resourcetype, getctag on CalendarHomeSets
     * </ol>
     * @param context 
     * 
     * @return List of {@link DavCalendar}
     * @throws ClientProtocolException
     *             http protocol error
     * @throws IOException
     *             Connection lost
     * @throws URISyntaxException
     *             url in Constructor malformed
     * @throws CaldavProtocolException
     *             caldav protocol error
     */
    //public Iterable<Calendar> getCalendarList(Context context) throws ClientProtocolException,
    public CalendarList getCalendarList(Context context) throws ClientProtocolException, IOException,
            URISyntaxException, ParserConfigurationException, CaldavProtocolException {
        try {
            CalendarList Result = new CalendarList(this.mAccount, this.mProvider, CalendarSource.CalDAV,
                    this.url.toString());
            List<DavCalendar> calendars = new ArrayList<DavCalendar>();

            calendars = forceGetCalendarsFromUri(context, this.url.toURI());

            if (calendars.size() == 0) {
                // no calendars found, try the home-set
                URI userPrincipal = getUserPrincipal();
                List<URI> calendarSets = getCalendarHomes(userPrincipal);
                for (URI calendarSet : calendarSets) {
                    List<DavCalendar> calendarSetCalendars = getCalendarsFromSet(calendarSet);
                    calendars.addAll(calendarSetCalendars);
                }
            }
            for (DavCalendar cal : calendars) {
                Result.addCalendar(cal);
            }

            //return calendars;
            return Result;
        } catch (AuthenticationException e) {
            throw new IOException(e);
        }
    }

    //public Iterable<CalendarEvent> getCalendarEvents(DavCalendar calendar)
    public ArrayList<CalendarEvent> getCalendarEvents(DavCalendar calendar) throws URISyntaxException,
            ClientProtocolException, IOException, ParserConfigurationException, SAXException {

        ArrayList<CalendarEvent> calendarEventList = new ArrayList<CalendarEvent>();

        String requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<D:propfind xmlns:D=\"DAV:\">"
                + "<D:prop>" + "<D:getetag/>" + "</D:prop>" + "</D:propfind>";

        HttpPropFind request = null;

        /*request = new HttpPropFind();
        request.setURI(calendar.getURI());
        request.setHeader("Host", targetHost.getHostName());
        request.setHeader("Depth", "1");
        request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
            
        try {
           request.setEntity(new StringEntity(requestBody, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
           throw new AssertionError("UTF-8 is unknown");
        }*/
        request = this.createPropFindRequest(calendar.getURI(), requestBody, 1);

        Log.d(TAG, "Getting eTag by PROPFIND at " + request.getURI());

        HttpResponse response = httpClient.execute(targetHost, request, mContext);

        BufferedReader reader = new BufferedReader(
                new InputStreamReader(response.getEntity().getContent(), "UTF-8"));

        String line;
        String body = "";
        do {
            line = reader.readLine();
            if (line != null)
                body += line;
        } while (line != null);

        Log.d(TAG, "HttpResponse status=" + response.getStatusLine() + " body= " + body);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document dom = builder.parse(new InputSource(new ByteArrayInputStream(body.getBytes("utf-8"))));
        Element root = dom.getDocumentElement();
        NodeList items = root.getElementsByTagNameNS("*", "getetag");

        for (int i = 0; i < items.getLength(); i++) {
            CalendarEvent calendarEvent = new CalendarEvent(this.mAccount, this.mProvider);

            Node node = items.item(i);

            if (node.getTextContent().trim().length() == 0)
                continue; // not an event

            calendarEvent.setETag(node.getTextContent().trim());
            //calendarEvent.calendarURL = this.url;
            calendarEvent.calendarURL = calendar.getURI().toURL();

            node = node.getParentNode(); // prop
            node = node.getParentNode(); // propstat
            node = node.getParentNode(); // response

            NodeList children = node.getChildNodes();
            for (int j = 0; j < children.getLength(); j++) {
                Node childNode = children.item(j);
                if ((childNode.getLocalName() != null) && (childNode.getLocalName().equalsIgnoreCase("href"))) {
                    calendarEvent.setUri(new URI(childNode.getTextContent().trim()));
                }
            }

            calendarEventList.add(calendarEvent);

        }

        return calendarEventList;
    }

    private void parseXML(HttpResponse response, ContentHandler contentHandler)
            throws IOException, CaldavProtocolException {
        InputStream is = response.getEntity().getContent();
        /*BufferedReader bReader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
        String Content = "";
        String Line = bReader.readLine();
            
        while (Line != null) {
           Content += Line;
           Line = bReader.readLine();
        }*/

        SAXParserFactory factory = SAXParserFactory.newInstance();
        try {
            SAXParser parser = factory.newSAXParser();
            XMLReader reader = parser.getXMLReader();
            reader.setContentHandler(contentHandler);
            reader.parse(new InputSource(is));
        } catch (ParserConfigurationException e) {
            throw new AssertionError("ParserConfigurationException " + e.getMessage());
        } catch (IllegalStateException e) {
            throw new CaldavProtocolException(e.getMessage());
        } catch (SAXException e) {
            throw new CaldavProtocolException(e.getMessage());
        }
    }

    private void checkStatus(HttpResponse response)
            throws AuthenticationException, FileNotFoundException, ClientProtocolException {
        final int statusCode = response.getStatusLine().getStatusCode();
        lastStatusCode = statusCode;
        if (response.containsHeader("ETag"))
            lastETag = response.getFirstHeader("ETag").getValue();
        else
            lastETag = "";
        if (response.containsHeader("DAV"))
            lastDav = response.getFirstHeader("DAV").getValue();
        else
            lastDav = "";

        switch (statusCode) {
        case 401:
            throw new AuthenticationException();
        case 404:
            throw new FileNotFoundException();
        case 409: //Conflict
        case 412:
        case 200:
        case 201:
        case 204:
        case 207:
            return;
        default:
            throw new ClientProtocolException("StatusCode: " + statusCode);
        }
    }

    private HttpPropFind createPropFindRequest(URI uri, String data, int depth) {
        HttpPropFind request = new HttpPropFind();

        request.setURI(uri);
        request.setHeader("Host", targetHost.getHostName());
        request.setHeader("Depth", Integer.toString(depth));
        request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
        try {
            request.setEntity(new StringEntity(data, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            throw new AssertionError("UTF-8 is unknown");
        }
        return request;
    }

    private HttpDelete createDeleteRequest(URI uri) {
        HttpDelete request = new HttpDelete();
        request.setURI(uri);
        request.setHeader("Host", targetHost.getHostName());
        request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
        return request;
    }

    private HttpPut createPutRequest(URI uri, String data, int depth) {
        HttpPut request = new HttpPut();
        request.setURI(uri);
        request.setHeader("Host", targetHost.getHostName());
        //request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
        request.setHeader("Content-Type", "text/calendar; charset=UTF-8");
        try {
            //request.setEntity(new StringEntity(data, "UTF-8"));
            request.setEntity(new StringEntity(data));
        } catch (UnsupportedEncodingException e) {
            throw new AssertionError("UTF-8 is unknown");
        }
        return request;
    }

    private static HttpReport createReportRequest(URI uri, String data, int depth) {
        HttpReport request = new HttpReport();
        request.setURI(uri);
        request.setHeader("Host", targetHost.getHostName());
        request.setHeader("Depth", Integer.toString(depth));
        request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");
        //request.setHeader("Content-Type", "text/xml;charset=\"UTF-8\"");
        try {
            request.setEntity(new StringEntity(data));
        } catch (UnsupportedEncodingException e) {
            throw new AssertionError("UTF-8 is unknown");
        }
        return request;
    }

    public static void fetchEvent_old(CalendarEvent calendarEvent) throws ClientProtocolException, IOException {
        HttpGet request = null;

        request = new HttpGet();
        request.setURI(calendarEvent.getUri());
        request.setHeader("Host", targetHost.getHostName());
        request.setHeader("Content-Type", "application/xml;charset=\"UTF-8\"");

        HttpResponse response = httpClient.execute(targetHost, request);

        BufferedReader reader = new BufferedReader(
                new InputStreamReader(response.getEntity().getContent(), "UTF-8"));

        String line;
        String body = "";
        do {
            line = reader.readLine();
            if (line != null)
                body += line + "\n";
        } while (line != null);

        calendarEvent.setICSasString(body);

        Log.d(TAG, "HttpResponse GET event status=" + response.getStatusLine() + " body= " + body);
    }

    public static boolean getEvent(CalendarEvent calendarEvent) throws ClientProtocolException, IOException {
        boolean Result = false;
        HttpReport request = null;

        //HINT: bugfix for google calendar
        String data = XML_VERSION
                + "<C:calendar-multiget xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" + "<D:prop>"
                + "<D:getetag/>" + "<C:calendar-data/>" + "</D:prop>" + "<D:href>"
                + calendarEvent.getUri().getPath().replace("@", "%40") + "</D:href>" + "</C:calendar-multiget>";

        URI calendarURI = null;
        try {
            calendarURI = calendarEvent.calendarURL.toURI();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        //request = createReportRequest(calendarEvent.getUri(), data, 1);
        request = createReportRequest(calendarURI, data, 1);

        HttpResponse response = httpClient.execute(targetHost, request);

        BufferedReader reader = new BufferedReader(
                new InputStreamReader(response.getEntity().getContent(), "UTF-8"));

        String line;
        String body = "";
        do {
            line = reader.readLine();
            if (line != null)
                body += line + "\n";
        } while (line != null);

        if (calendarEvent.setICSasMultiStatus(body))
            Result = true;

        return Result;
    }

    /**
     * sends a update event request to the server 
     * @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
     * @param data the full ical-data for the event
     * @param ETag the ETAG of this event is send within the "If-Match" Parameter to tell the server only to update this version
     * @return
     */
    public boolean updateEvent(URI uri, String data, String ETag) {
        boolean Result = false;

        try {
            HttpPut request = createPutRequest(uri, data, 1);
            request.addHeader(mstrcHeaderIfMatch, ETag);
            HttpResponse response = httpClient.execute(targetHost, request, mContext);
            checkStatus(response);
            if ((lastStatusCode == 200) || (lastStatusCode == 201) || (lastStatusCode == 204)) {
                Result = true;
            } else if (lastStatusCode == 412) {
                //Precondition failed
                Result = false;
            } else if (lastStatusCode == 409) {
                //Conflict
                Result = false;
            } else {
                Log.w(TAG, "Unkown StatusCode during creation of an event");
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return Result;
    }

    /**
     * sends a create event request to server
     * @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
     * @param data the full ical-data for the new event
     * @return success of this function
     */
    public boolean createEvent(URI uri, String data) {
        boolean Result = false;

        try {
            HttpPut request = createPutRequest(uri, data, 1);
            request.addHeader(mstrcHeaderIfNoneMatch, "*");
            HttpResponse response = httpClient.execute(targetHost, request, mContext);
            checkStatus(response);
            if (lastStatusCode == 201) {
                Result = true;
            } else {
                Log.w(TAG, "Unkown StatusCode during creation of an event");
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        return Result;
    }

    /**
     * sends a delete event request to the server
     * @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
     * @param ETag the ETAG of this event is send within the "If-Match" Parameter to tell the server only to delete this version
     * @return success of this function
     */
    public boolean deleteEvent(URI calendarEventUri, String ETag) {
        boolean Result = false;

        try {
            HttpDelete request = createDeleteRequest(calendarEventUri);
            request.addHeader(mstrcHeaderIfMatch, ETag);
            HttpResponse response = httpClient.execute(targetHost, request, mContext);
            checkStatus(response);
            if ((lastStatusCode == 204) || (lastStatusCode == 200)) {
                Result = true;
            } else {
                Log.w(TAG, "Unkown StatusCode during deletion of an event");
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            if (lastStatusCode == 404) {
                //the event has already been deleted on server side. no action needed
                Result = true;
            } else {
                e.printStackTrace();
            }
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }

        return Result;
    }

    /**
     * returns the ETAG send by the last server response.
     * @return the ETAG
     */
    public String getLastETag() {
        return lastETag;
    }

    /**
     * returns the DAV-Options send by the last server response.
     * @return the DAV-Options
     */
    public String getLastDav() {
        return lastDav;
    }

    public void setVersion(String version) {
        VERSION = version;
        ((AbstractHttpClient) httpClient).getParams().setParameter(CoreProtocolPNames.USER_AGENT,
                this.USER_AGENT + " Version:" + VERSION);
    }

    public void setAccount(Account account) {
        this.mAccount = account;
    }

    public void setProvider(ContentProviderClient provider) {
        this.mProvider = provider;
    }
}