1 package net
.bionicmessage
.groupdav
;
3 import java
.io
.ByteArrayInputStream
;
4 import java
.io
.IOException
;
5 import java
.io
.UnsupportedEncodingException
;
9 * bionicmessage jgroupdav library
10 * CalDAVExtensions.java created Friday May 23rd 2008, (C) 2008 Mathew McBride
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
19 * The above copyright notice and this permission notice shall be included in
20 * all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
32 * Implements some CalDAV queries to supplement GroupDAV
35 import java
.util
.ArrayList
;
36 import java
.util
.HashMap
;
37 import java
.util
.Hashtable
;
38 import java
.util
.Iterator
;
39 import java
.util
.List
;
41 import java
.util
.Stack
;
42 import java
.util
.logging
.Logger
;
43 import javax
.xml
.namespace
.NamespaceContext
;
44 import javax
.xml
.parsers
.DocumentBuilder
;
45 import javax
.xml
.parsers
.DocumentBuilderFactory
;
46 import javax
.xml
.xpath
.XPath
;
47 import javax
.xml
.xpath
.XPathConstants
;
48 import javax
.xml
.xpath
.XPathExpression
;
49 import javax
.xml
.xpath
.XPathExpressionException
;
50 import javax
.xml
.xpath
.XPathFactory
;
51 import net
.bionicmessage
.extutils
.Base64
;
52 import org
.w3c
.dom
.Document
;
53 import org
.w3c
.dom
.Node
;
54 import org
.w3c
.dom
.NodeList
;
55 import org
.xml
.sax
.InputSource
;
56 import org
.xml
.sax
.SAXException
;
59 public class CalDAVExtensions
implements IDAVHandler
,ServerDriver
{
61 private URL davServer
;
62 private String base64cache
= null;
63 private Logger log
= null;
65 private DocumentBuilderFactory dbf
= null;
66 private DocumentBuilder dbuilder
= null;
67 private boolean ready
= false;
68 private Exception retException
= null;
70 private groupDAV parent
= null;
71 private static final NamespaceContext nsctx
= new NamespaceContext() {
73 public String
getNamespaceURI(String prefix
) {
74 if ("D".equals(prefix
)) {
80 public Iterator
getPrefixes(String val
) {
84 public String
getPrefix(String uri
) {
88 private XPathFactory xpf
= null;
89 private XPath xp
= null;
90 private XPathExpression pull_hrefs
= null;
91 private XPathExpression pull_etags
= null;
92 /** Constant string with CalDAV calendar filter syntax. Use
93 * with String.format(..)
94 * Parameters: Meta component type, iCal component type, iCal param,
95 * ical Value one, iCal Value 2
96 * See http://www.apps.ietf.org/rfc/rfc4791.html#sec-7.8.5
98 public static final String CALDAV_DISCOVER_BY_TIME
=
99 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
100 "<C:calendar-query xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +
101 "<D:prop xmlns:D=\"DAV:\">\r\n<D:getetag/>\r\n" +
102 "</D:prop>\r\n<C:filter><C:prop-filter name=\"%s\">\r\n" +
103 "<C:prop-filter name=\"%s\">\r\n" +
104 "<C:time-range start=\"%s\" end=\"%s\"/>\r\n" +
105 "</C:prop-filter></C:comp-filter></C:prop-filter></C:filter>" +
106 "</C:calendar-query>";
107 public static final String CALDAV_DISCOVER_BY_TIMERANGE
=
108 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
109 "<C:calendar-query xmlns:D=\"DAV:\"" +
110 " xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +
111 "<D:prop><D:getetag/></D:prop><C:filter><C:comp-filter name=\"VCALENDAR\">" +
112 "<C:comp-filter name=\"%s\"><C:time-range start=\"%s\"" +
113 " end=\"%s\"/></C:comp-filter>" +
115 "</C:filter></C:calendar-query>";
117 public static final String CALDAV_PULL_EVERYTHING
=
118 "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
119 "<C:calendar-multiget xmlns:D=\"DAV:\" xmlns:C=\"urn:ietf:params:xml:ns:caldav\">" +
120 "<D:prop><D:getetag/><C:calendar-data/></D:prop></C:calendar-multiget>";
122 /* public CalDAVExtensions(URL server, Logger l, String user, String pass) {
123 String up = user + ":" + pass;
124 String up64 = Base64.encodeBytes(up.getBytes());
125 String header = "Authorization: Basic" + up64;
126 init(server, header, l);
129 public CalDAVExtensions(URL server
, groupDAV parent
, Logger l
) {
130 init(server
, parent
, l
);
133 public void init(URL server
, groupDAV parent
, Logger l
) {
135 this.parent
= parent
;
137 dbf
= DocumentBuilderFactory
.newInstance();
138 dbf
.setNamespaceAware(true);
140 dbuilder
= dbf
.newDocumentBuilder();
141 xpf
= XPathFactory
.newInstance();
143 xp
.setNamespaceContext(nsctx
);
144 pull_hrefs
= xp
.compile("//D:href/text()");
145 pull_etags
= xp
.compile("//D:getetag/text()");
146 } catch (Exception e
) {
147 l
.info("Exception: " + e
.getMessage());
151 public boolean doesSupportCalDAV(URL fullURL
)
153 // Build an OPTIONS query
154 byte[] query
= Common
.buildQuery("OPTIONS", fullURL
, new byte[0], null, parent
);
155 String response
= Common
.sendNonKeepAliveRequest(davServer
, query
, parent
, log
);
156 GroupDAVObject rbo
= new GroupDAVObject(response
, GroupDAVObject
.OBJECT_GET
);
157 parent
.addToCookieJar(rbo
.getCookies());
158 String davHeader
= (String
) rbo
.getHeaders().get("dav");
159 if (davHeader
== null)
161 return davHeader
.contains("calendar-access");
164 public void doBasicReport(URL fullURL
)
166 byte[] query
= Common
.buildQuery("REPORT", fullURL
, new byte[0], null, parent
);
167 String response
= Common
.sendNonKeepAliveRequest(davServer
,query
, parent
, log
);
168 GroupDAVObject rbo
= new GroupDAVObject(response
, GroupDAVObject
.OBJECT_GET
);
171 /** Queries the server for events specified by a particular time range
173 * @param fullURL The URL to the path being queried
174 * @param iComponent The type of component to query, i.e VCALENDAR or VTODO
175 * @param type The iCalendar parameter to compare, i.e DTSTART
176 * @param param1 The first date-time parameter
177 * @param param2 The second date time parameter
178 * @return a Map in the format in {url, etag}
179 * @throws java.io.UnsupportedEncodingException Rare - if your JRE can't do UTF-8
180 * @throws java.io.IOException due to network error
181 * @throws org.xml.sax.SAXException due to error parsing XML propfind
182 * @throws javax.xml.xpath.XPathExpressionException Due to error querying propfind.
184 public Map
<String
, String
> getByTimeRange(URL fullURL
,
190 /* String query = String.format(CALDAV_DISCOVER_BY_TIME,
195 String query
= String
.format(CALDAV_DISCOVER_BY_TIMERANGE
,
199 Hashtable headers
= new Hashtable();
200 headers
.put("Depth", "1");
201 headers
.put("Content-Type", "text/xml;charset=utf-8");
202 byte[] request
= Common
.buildQuery("REPORT",
204 query
.getBytes("UTF-8"),
207 String response
= Common
.sendNonKeepAliveRequest(davServer
, request
,parent
, log
);
208 GroupDAVObject gbo
= new GroupDAVObject(response
, GroupDAVObject
.OBJECT_GET
);
209 parent
.addToCookieJar(gbo
.getCookies());
210 Document report
= dbuilder
.parse(
211 new ByteArrayInputStream(gbo
.getContent().getBytes("UTF-8")));
212 NodeList hrefs
= (NodeList
) pull_hrefs
.evaluate(report
, XPathConstants
.NODESET
);
213 NodeList etags
= (NodeList
) pull_etags
.evaluate(report
, XPathConstants
.NODESET
);
214 HashMap
<String
, String
> result_map
= new HashMap(hrefs
.getLength());
215 // Pool the above results together
216 for (int i
= 0; i
< hrefs
.getLength(); i
++) {
217 Node href
= hrefs
.item(i
);
218 URL fullHref
= Common
.createURL(href
.getNodeValue(), davServer
);
219 Node etag
= etags
.item(i
);
220 result_map
.put(fullHref
.toString(), etag
.getNodeValue());
225 public void setLog(Logger log
) {
229 public List
<GroupDAVObject
> pullCalendarData(URL root
) throws Exception
{
230 HashMap headers
= new HashMap();
231 headers
.put("Depth","1");
232 headers
.put("Content-Type", "text/xml;charset=utf-8");
233 byte[] data
= CALDAV_PULL_EVERYTHING
.getBytes("UTF-8");
234 byte[] req
= Common
.buildQuery("REPORT", root
, data
, headers
,parent
);
235 HTTPInputStream istr
= Common
.sendNonKeepAliveRequest(root
, req
);
236 ArrayList
<GroupDAVObject
> addTo
= new ArrayList(500);
237 return extractFromReport(istr
,addTo
);
239 public List
<GroupDAVObject
> doMultiget(URL root
, List
<String
> toDownload
, List
<GroupDAVObject
> addTo
) throws Exception
{
240 StringBuffer request
= new StringBuffer();
241 request
.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
242 request
.append("\r\n<C:calendar-multiget xmlns:D=\"DAV:\" \r\n");
243 request
.append("xmlns:C=\"urn:ietf:params:xml:ns:caldav\"> ");
244 request
.append("\r\n<D:prop>\r\n<D:getetag/>\r\n<C:calendar-data/>");
245 request
.append("\r\n</D:prop>");
246 for(String s
: toDownload
) {
247 request
.append("<D:href>");
249 request
.append("</D:href>");
251 request
.append("</C:calendar-multiget>");
252 HashMap headers
= new HashMap();
253 headers
.put("Depth","1");
254 headers
.put("Content-Type", "text/xml;charset=utf-8");
255 byte[] data
= request
.toString().getBytes("UTF-8");
256 byte[] req
= Common
.buildQuery("REPORT", root
, data
,headers
, parent
);
257 HTTPInputStream istr
= Common
.sendNonKeepAliveRequest(root
, req
);
258 return extractFromReport(istr
, addTo
);
261 public List
<GroupDAVObject
> extractFromReport(HTTPInputStream istr
, List
<GroupDAVObject
> addTo
) throws Exception
{
262 InputSource is
= new InputSource(istr
);
264 SAXDAVHandler sdh
= new SAXDAVHandler(this, is
);
268 if(retException
!= null) {
271 Stack
<String
> hrefStack
= sdh
.getHrefs();
272 Stack
<String
> etagStack
= sdh
.getEtags();
273 Stack
<String
> contentsStack
= sdh
.getContents();
274 while(!hrefStack
.isEmpty()) {
275 String href
= hrefStack
.pop();
276 String etag
= etagStack
.pop();
277 String contents
= contentsStack
.pop();
278 GroupDAVObject child
= new GroupDAVObject(istr
, contents
, etag
, href
);
285 public boolean getReady() {
289 public void setReady(boolean ready
) {
293 public void setRetException(Exception e
) {
294 this.retException
= e
;