1 /* GroupDAV2Server.java
2 * Copyright 2010 Mathew McBride
4 * Redistribution and use in source and binary forms, with or without modification, are
5 * permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice, this list of
8 * conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 * of conditions and the following disclaimer in the documentation and/or other materials
12 * provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY Mathew McBride ``AS IS'' AND ANY EXPRESS OR IMPLIED
15 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 * The views and conclusions contained in the software and documentation are those of the
25 * authors and should not be interpreted as representing official policies, either expressed
26 * or implied, of Mathew McBride.
28 package net
.bionicmessage
.groupdav
;
31 import java
.net
.URISyntaxException
;
32 import java
.util
.ArrayList
;
33 import java
.util
.Iterator
;
34 import java
.util
.List
;
36 import net
.bionicmessage
.extutils
.Base64
;
37 import net
.bionicmessage
.groupdav
.http
.HttpDelete
;
38 import net
.bionicmessage
.groupdav
.http
.HttpPropfind
;
39 import org
.apache
.http
.Header
;
40 import org
.apache
.http
.HttpEntity
;
41 import org
.apache
.http
.HttpHost
;
42 import org
.apache
.http
.HttpResponse
;
43 import org
.apache
.http
.auth
.AuthScope
;
44 import org
.apache
.http
.auth
.UsernamePasswordCredentials
;
45 import org
.apache
.http
.client
.AuthCache
;
46 import org
.apache
.http
.client
.methods
.HttpGet
;
47 import org
.apache
.http
.client
.methods
.HttpPut
;
48 import org
.apache
.http
.entity
.ByteArrayEntity
;
49 import org
.apache
.http
.entity
.StringEntity
;
50 import org
.apache
.http
.impl
.auth
.BasicScheme
;
51 import org
.apache
.http
.impl
.client
.BasicAuthCache
;
52 import org
.apache
.http
.impl
.client
.DefaultHttpClient
;
53 import org
.apache
.http
.protocol
.BasicHttpContext
;
54 import org
.apache
.http
.util
.EntityUtils
;
55 import org
.xml
.sax
.InputSource
;
57 public class GroupDAV2Server
58 implements DAVServer
, IDAVHandler
{
60 private static final String DAV_PROPFIND
= "<?xml version=\"1.0\" " +
61 "encoding=\"utf-8\"?>\n<propfind xmlns=\"DAV:\"\n xmlns:I=\"urn:ietf:params:xml:ns:caldav\"\n" +
62 " xmlns:C=\"urn:ietf:params:xml:ns:carddav\"\n" +
63 " xmlns:G=\"http://groupdav.org/\"\n" +
64 " xmlns:A=\"http://calendarserver.org/ns/\">\n\t" +
65 "<prop>\n\t\t<displayname/>\n\t\t<resourcetype/>\n\t\t" +
66 "<getetag/>\n\t\t<getcontenttype/>\n\t\t<current-user-privilege-set/>" +
67 "\n\t\t<G:component-set/>\n\t\t<I:supported-calendar-component-set/>" +
68 "\n\t\t<I:supported-calendar-data/>\n\t\t<I:calendar-description/>" +
69 "\n\t\t<C:supported-address-data/>\n\t\t<C:addressbook-description/>" +
70 "\n\t\t<A:getctag/>\n\t</prop>\n</propfind>\n";
71 /* private static final String DAV_PROPFIND = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
72 + "<propfind xmlns=\"DAV:\"><prop><getetag/></prop></propfind>"; */
74 private URI serverURI
= null;
75 private DefaultHttpClient httpClient
= null;
76 private BasicHttpContext httpContext
= null;
77 private HttpHost httpHost
= null;
78 private SAXDAVHandler handler
= null;
79 boolean ready
= false;
80 private Exception exception
= null;
82 public GroupDAV2Server() {
83 this.httpClient
= new DefaultHttpClient();
84 this.httpContext
= new BasicHttpContext();
87 public Map
<String
, String
> listObjects(String url
) throws Exception
{
88 URI pathURI
= this.serverURI
.resolve(url
);
89 HttpPropfind pf
= new HttpPropfind(pathURI
);
90 StringEntity he
= new StringEntity(DAV_PROPFIND
);
91 he
.setContentType("text/xml");
93 HttpResponse hr
= this.httpClient
.execute(this.httpHost
, pf
, this.httpContext
);
94 InputSource is
= new InputSource(hr
.getEntity().getContent());
95 this.handler
= new SAXDAVHandler(this, is
);
96 while (!(this.ready
));
98 hr
.getEntity().consumeContent();
99 return this.handler
.getEtagMap();
102 public List
<GroupDAVObject
> getObjects(final List
<String
> urls
) {
103 ArrayList
<GroupDAVObject
> downloaded
= new ArrayList(urls
.size());
104 ArrayList
<String
> downloadBatch
= getBatch(urls
, 500);
105 while (downloadBatch
.size() != 0) {
106 for (String s
: downloadBatch
) {
108 GroupDAVObject gdo
= downloadSingleObject(s
);
110 } catch (Exception e
) {
114 downloadBatch
= getBatch(urls
, 500);
119 public GroupDAVObject
modifyObject(String url
, String etag
, String ctype
, byte[] newContents
)
121 URI pathURI
= this.serverURI
.resolve(url
);
122 HttpPut put
= new HttpPut(pathURI
);
123 put
.addHeader("If-Match", etag
);
124 ByteArrayEntity entity
= new ByteArrayEntity(newContents
);
125 entity
.setContentType(ctype
);
126 entity
.setContentEncoding("UTF-8");
127 put
.setEntity(entity
);
128 HttpResponse hr
= this.httpClient
.execute(this.httpHost
, put
, this.httpContext
);
129 if (hr
.getStatusLine().getStatusCode() != 204) {
132 if (hr
.getEntity() != null) {
133 hr
.getEntity().consumeContent();
135 GroupDAVObject gbo
= new GroupDAVObject();
136 gbo
.setStatus(hr
.getStatusLine().getStatusCode());
140 public GroupDAVObject
putObject(String url
, String ctype
, byte[] content
) throws Exception
{
141 URI pathURI
= this.serverURI
.resolve(url
);
142 HttpPut put
= new HttpPut(pathURI
);
143 ByteArrayEntity entity
= new ByteArrayEntity(content
);
144 entity
.setContentType(ctype
);
145 entity
.setContentEncoding("UTF-8");
146 put
.setEntity(entity
);
147 HttpResponse hr
= this.httpClient
.execute(this.httpHost
, put
, this.httpContext
);
148 if (hr
.getEntity() != null) {
149 hr
.getEntity().consumeContent();
151 GroupDAVObject obj
= new GroupDAVObject();
152 Header loc
= hr
.getFirstHeader("Location");
154 obj
.setLocation(loc
.getValue());
156 obj
.setLocation(url
);
161 public boolean deleteObject(String url
, String etag
) throws Exception
{
162 URI pathURI
= this.serverURI
.resolve(url
);
163 HttpDelete delete
= new HttpDelete(pathURI
);
164 delete
.addHeader("If-Match", etag
);
165 HttpResponse hr
= this.httpClient
.execute(this.httpHost
, delete
, this.httpContext
);
166 if (hr
.getEntity() != null) {
167 hr
.getEntity().consumeContent();
170 return (hr
.getStatusLine().getStatusCode() == 204);
173 public void setProperty(String name
, Object value
) {
174 throw new UnsupportedOperationException("Not supported yet.");
177 public void setAuthenticationBasic(String b64
) {
178 byte[] decoded
= Base64
.decode(b64
);
179 String up
= new String(decoded
);
180 AuthScope as
= new AuthScope(this.serverURI
.getHost(), this.serverURI
.getPort());
181 UsernamePasswordCredentials upc
= new UsernamePasswordCredentials(up
);
183 this.httpClient
.getCredentialsProvider().setCredentials(as
, upc
);
186 public void setAuthentication(String user
, String pass
) {
187 AuthScope as
= new AuthScope(this.serverURI
.getHost(), this.serverURI
.getPort());
188 UsernamePasswordCredentials upc
= new UsernamePasswordCredentials(user
, pass
);
190 this.httpClient
.getCredentialsProvider().setCredentials(as
, upc
);
193 public void setServer(String server
) throws URISyntaxException
{
194 this.serverURI
= new URI(server
).parseServerAuthority();
196 if (this.serverURI
.getPort() != -1) {
197 port
= this.serverURI
.getPort();
199 if (("https".equals(this.serverURI
.getScheme())) && (port
== 80)) {
203 this.httpHost
= new HttpHost(this.serverURI
.getHost(), port
, this.serverURI
.getScheme());
204 AuthCache authCache
= new BasicAuthCache();
206 BasicScheme basicAuth
= new BasicScheme();
207 authCache
.put(this.httpHost
, basicAuth
);
209 BasicHttpContext localcontext
= new BasicHttpContext();
210 localcontext
.setAttribute("http.auth.auth-cache", authCache
);
213 public boolean getReady() {
217 public void setReady(boolean ready
) {
221 public void setRetException(Exception e
) {
225 private ArrayList
<String
> getBatch(List
<String
> collection
, int size
) {
226 ArrayList batch
= new ArrayList(size
);
228 Iterator iterator
= collection
.iterator();
229 while ((iterator
.hasNext()) && (i
!= size
)) {
230 String url
= (String
) iterator
.next();
238 public GroupDAVObject
downloadSingleObject(String url
) throws Exception
{
239 URI pathURI
= this.serverURI
.resolve(url
);
240 HttpGet hg
= new HttpGet(pathURI
);
241 HttpResponse hr
= this.httpClient
.execute(this.httpHost
, hg
, this.httpContext
);
242 HttpEntity he
= hr
.getEntity();
243 byte[] data
= EntityUtils
.toByteArray(he
);
244 Header etag
= hr
.getFirstHeader("ETag");
245 Header loc
= hr
.getFirstHeader("Location");
246 GroupDAVObject gdo
= new GroupDAVObject();
248 URI newLocation
= this.serverURI
.resolve(loc
.getValue());
249 gdo
.setLocation(newLocation
.getPath());
251 gdo
.setLocation(pathURI
.getPath());
253 gdo
.setEtag(etag
.getValue());
254 gdo
.setContent(data
);
255 hr
.getEntity().consumeContent();