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

Tue, 10 Feb 2015 22:40:00 +0100

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

Merge https://github.com/gggard/AndroidCaldavSyncAdapater/pull/206/

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

mercurial