mirror of
https://github.com/moparisthebest/davmail
synced 2024-11-11 11:55:08 -05:00
Caldav: Implement basic Carddav search requests
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1035 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
4fbb040a35
commit
0faf2497db
@ -273,6 +273,10 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
if (request.path.endsWith("/")) {
|
if (request.path.endsWith("/")) {
|
||||||
// GET request on a folder => build ics content of all folder events
|
// GET request on a folder => build ics content of all folder events
|
||||||
String folderPath = request.getExchangeFolderPath();
|
String folderPath = request.getExchangeFolderPath();
|
||||||
|
ExchangeSession.Folder folder = session.getFolder(folderPath);
|
||||||
|
if (folder.isContact()) {
|
||||||
|
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(session.getFolderResourceTag(folderPath)), "text/vcard", (byte[])null, true);
|
||||||
|
} else {
|
||||||
List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
|
List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
|
||||||
ChunkedResponse response = new ChunkedResponse(HttpStatus.SC_OK, "text/calendar;charset=UTF-8");
|
ChunkedResponse response = new ChunkedResponse(HttpStatus.SC_OK, "text/calendar;charset=UTF-8");
|
||||||
response.append("BEGIN:VCALENDAR\r\n");
|
response.append("BEGIN:VCALENDAR\r\n");
|
||||||
@ -281,12 +285,12 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
response.append("METHOD:PUBLISH\r\n");
|
response.append("METHOD:PUBLISH\r\n");
|
||||||
|
|
||||||
for (ExchangeSession.Event event : events) {
|
for (ExchangeSession.Event event : events) {
|
||||||
String icsContent = StringUtil.getToken(event.getICS(), "BEGIN:VTIMEZONE", "END:VCALENDAR");
|
String icsContent = StringUtil.getToken(event.getBody(), "BEGIN:VTIMEZONE", "END:VCALENDAR");
|
||||||
if (icsContent != null) {
|
if (icsContent != null) {
|
||||||
response.append("BEGIN:VTIMEZONE");
|
response.append("BEGIN:VTIMEZONE");
|
||||||
response.append(icsContent);
|
response.append(icsContent);
|
||||||
} else {
|
} else {
|
||||||
icsContent = StringUtil.getToken(event.getICS(), "BEGIN:VEVENT", "END:VCALENDAR");
|
icsContent = StringUtil.getToken(event.getBody(), "BEGIN:VEVENT", "END:VCALENDAR");
|
||||||
if (icsContent != null) {
|
if (icsContent != null) {
|
||||||
response.append("BEGIN:VEVENT");
|
response.append("BEGIN:VEVENT");
|
||||||
response.append(icsContent);
|
response.append(icsContent);
|
||||||
@ -295,14 +299,15 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
response.append("END:VCALENDAR");
|
response.append("END:VCALENDAR");
|
||||||
response.close();
|
response.close();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ExchangeSession.Event event = session.getEvent(request.getExchangeFolderPath(), lastPath);
|
ExchangeSession.Item item = session.getItem(request.getExchangeFolderPath(), lastPath);
|
||||||
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(event.getEtag()), "text/calendar;charset=UTF-8", event.getICS(), true);
|
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(item.getEtag()), item.getContentType(), item.getBody(), true);
|
||||||
}
|
}
|
||||||
} else if (request.isHead()) {
|
} else if (request.isHead()) {
|
||||||
// test event
|
// test event
|
||||||
ExchangeSession.Event event = session.getEvent(request.getExchangeFolderPath(), lastPath);
|
ExchangeSession.Item item = session.getItem(request.getExchangeFolderPath(), lastPath);
|
||||||
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(event.getEtag()), "text/calendar;charset=UTF-8", (byte[]) null, true);
|
sendHttpResponse(HttpStatus.SC_OK, buildEtagHeader(item.getEtag()), item.getContentType(), (byte[]) null, true);
|
||||||
} else {
|
} else {
|
||||||
sendUnsupported(request);
|
sendUnsupported(request);
|
||||||
}
|
}
|
||||||
@ -319,40 +324,54 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendContactsResponses(CaldavResponse response, CaldavRequest request, List<ExchangeSession.Contact> contacts) throws IOException {
|
||||||
|
int size = contacts.size();
|
||||||
|
int count = 0;
|
||||||
|
for (ExchangeSession.Contact contact : contacts) {
|
||||||
|
DavGatewayTray.debug(new BundleMessage("LOG_LISTING_CONTACT", ++count, size));
|
||||||
|
DavGatewayTray.switchIcon();
|
||||||
|
appendItemResponse(response, request, contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void appendEventsResponses(CaldavResponse response, CaldavRequest request, List<ExchangeSession.Event> events) throws IOException {
|
protected void appendEventsResponses(CaldavResponse response, CaldavRequest request, List<ExchangeSession.Event> events) throws IOException {
|
||||||
int size = events.size();
|
int size = events.size();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (ExchangeSession.Event event : events) {
|
for (ExchangeSession.Event event : events) {
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_LISTING_EVENT", ++count, size));
|
DavGatewayTray.debug(new BundleMessage("LOG_LISTING_EVENT", ++count, size));
|
||||||
DavGatewayTray.switchIcon();
|
DavGatewayTray.switchIcon();
|
||||||
appendEventResponse(response, request, event);
|
appendItemResponse(response, request, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void appendEventResponse(CaldavResponse response, CaldavRequest request, ExchangeSession.Event event) throws IOException {
|
protected void appendItemResponse(CaldavResponse response, CaldavRequest request, ExchangeSession.Item item) throws IOException {
|
||||||
StringBuilder eventPath = new StringBuilder();
|
StringBuilder eventPath = new StringBuilder();
|
||||||
eventPath.append(URIUtil.encodePath(request.getPath()));
|
eventPath.append(URIUtil.encodePath(request.getPath()));
|
||||||
if (!(eventPath.charAt(eventPath.length() - 1) == '/')) {
|
if (!(eventPath.charAt(eventPath.length() - 1) == '/')) {
|
||||||
eventPath.append('/');
|
eventPath.append('/');
|
||||||
}
|
}
|
||||||
String eventName = StringUtil.xmlEncode(event.getName());
|
String itemName = StringUtil.xmlEncode(item.getName());
|
||||||
eventPath.append(URIUtil.encodeWithinQuery(eventName));
|
eventPath.append(URIUtil.encodeWithinQuery(itemName));
|
||||||
response.startResponse(eventPath.toString());
|
response.startResponse(eventPath.toString());
|
||||||
response.startPropstat();
|
response.startPropstat();
|
||||||
if (request.hasProperty("calendar-data")) {
|
if (request.hasProperty("calendar-data") && item instanceof ExchangeSession.Event) {
|
||||||
response.appendCalendarData(event.getICS());
|
response.appendCalendarData(item.getBody());
|
||||||
}
|
}
|
||||||
if (request.hasProperty("getcontenttype")) {
|
if (request.hasProperty("getcontenttype")) {
|
||||||
response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
|
if (item instanceof ExchangeSession.Event) {
|
||||||
|
response.appendProperty("D:getcontenttype", "text/calendar; component=vevent");
|
||||||
|
} else if (item instanceof ExchangeSession.Contact) {
|
||||||
|
response.appendProperty("D:getcontenttype", "text/vcard");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (request.hasProperty("getetag")) {
|
if (request.hasProperty("getetag")) {
|
||||||
response.appendProperty("D:getetag", event.getEtag());
|
response.appendProperty("D:getetag", item.getEtag());
|
||||||
}
|
}
|
||||||
if (request.hasProperty("resourcetype")) {
|
if (request.hasProperty("resourcetype")) {
|
||||||
response.appendProperty("D:resourcetype");
|
response.appendProperty("D:resourcetype");
|
||||||
}
|
}
|
||||||
if (request.hasProperty("displayname")) {
|
if (request.hasProperty("displayname")) {
|
||||||
response.appendProperty("D:displayname", eventName);
|
response.appendProperty("D:displayname", itemName);
|
||||||
}
|
}
|
||||||
response.endPropStatOK();
|
response.endPropStatOK();
|
||||||
response.endResponse();
|
response.endResponse();
|
||||||
@ -365,8 +384,9 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
* @param request Caldav request
|
* @param request Caldav request
|
||||||
* @param subFolder calendar folder path relative to request path
|
* @param subFolder calendar folder path relative to request path
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
|
* @return Exchange folder object
|
||||||
*/
|
*/
|
||||||
public void appendFolder(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
|
public ExchangeSession.Folder appendFolder(CaldavResponse response, CaldavRequest request, String subFolder) throws IOException {
|
||||||
ExchangeSession.Folder folder = session.getFolder(request.getExchangeFolderPath(subFolder));
|
ExchangeSession.Folder folder = session.getFolder(request.getExchangeFolderPath(subFolder));
|
||||||
|
|
||||||
response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
|
response.startResponse(URIUtil.encodePath(request.getPath(subFolder)));
|
||||||
@ -420,6 +440,7 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
|
|
||||||
response.endPropStatOK();
|
response.endPropStatOK();
|
||||||
response.endResponse();
|
response.endResponse();
|
||||||
|
return folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -560,17 +581,21 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
String folderPath = request.getExchangeFolderPath();
|
String folderPath = request.getExchangeFolderPath();
|
||||||
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
CaldavResponse response = new CaldavResponse(HttpStatus.SC_MULTI_STATUS);
|
||||||
response.startMultistatus();
|
response.startMultistatus();
|
||||||
appendFolder(response, request, null);
|
ExchangeSession.Folder folder = appendFolder(response, request, null);
|
||||||
if (request.getDepth() == 1) {
|
if (request.getDepth() == 1) {
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_SEARCHING_CALENDAR_EVENTS", folderPath));
|
if (folder.isContact()) {
|
||||||
List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
|
appendContactsResponses(response, request, session.getAllContacts(folderPath));
|
||||||
DavGatewayTray.debug(new BundleMessage("LOG_FOUND_CALENDAR_EVENTS", events.size()));
|
} else {
|
||||||
appendEventsResponses(response, request, events);
|
DavGatewayTray.debug(new BundleMessage("LOG_SEARCHING_CALENDAR_EVENTS", folderPath));
|
||||||
// Send sub folders for multi-calendar support under iCal, except for public folders
|
List<ExchangeSession.Event> events = session.getAllEvents(folderPath);
|
||||||
if (!folderPath.startsWith("/public")) {
|
DavGatewayTray.debug(new BundleMessage("LOG_FOUND_CALENDAR_EVENTS", events.size()));
|
||||||
List<ExchangeSession.Folder> folderList = session.getSubCalendarFolders(folderPath, false);
|
appendEventsResponses(response, request, events);
|
||||||
for (ExchangeSession.Folder folder : folderList) {
|
// Send sub folders for multi-calendar support under iCal, except for public folders
|
||||||
appendFolder(response, request, folder.folderPath.substring(folder.folderPath.indexOf('/') + 1));
|
if (!folderPath.startsWith("/public")) {
|
||||||
|
List<ExchangeSession.Folder> folderList = session.getSubCalendarFolders(folderPath, false);
|
||||||
|
for (ExchangeSession.Folder subFolder : folderList) {
|
||||||
|
appendFolder(response, request, subFolder.folderPath.substring(subFolder.folderPath.indexOf('/') + 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,9 +655,9 @@ public class CaldavConnection extends AbstractConnection {
|
|||||||
// ignore cases for Sunbird
|
// ignore cases for Sunbird
|
||||||
if (eventName != null && eventName.length() > 0
|
if (eventName != null && eventName.length() > 0
|
||||||
&& !"inbox".equals(eventName) && !"calendar".equals(eventName)) {
|
&& !"inbox".equals(eventName) && !"calendar".equals(eventName)) {
|
||||||
ExchangeSession.Event event;
|
ExchangeSession.Item item;
|
||||||
event = session.getEvent(folderPath, eventName);
|
item = session.getItem(folderPath, eventName);
|
||||||
appendEventResponse(response, request, event);
|
appendItemResponse(response, request, item);
|
||||||
}
|
}
|
||||||
} catch (HttpException e) {
|
} catch (HttpException e) {
|
||||||
DavGatewayTray.warn(new BundleMessage("LOG_EVENT_NOT_AVAILABLE", eventName, href));
|
DavGatewayTray.warn(new BundleMessage("LOG_EVENT_NOT_AVAILABLE", eventName, href));
|
||||||
|
@ -106,12 +106,15 @@ public class ExchangeSession {
|
|||||||
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:");
|
protected static final Namespace URN_SCHEMAS_HTTPMAIL = Namespace.getNamespace("urn:schemas:httpmail:");
|
||||||
protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/");
|
protected static final Namespace SCHEMAS_EXCHANGE = Namespace.getNamespace("http://schemas.microsoft.com/exchange/");
|
||||||
protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace.getNamespace("http://schemas.microsoft.com/mapi/proptag/");
|
protected static final Namespace SCHEMAS_MAPI_PROPTAG = Namespace.getNamespace("http://schemas.microsoft.com/mapi/proptag/");
|
||||||
|
protected static final Namespace URN_SCHEMAS_CONTACTS = Namespace.getNamespace("urn:schemas:contacts:");
|
||||||
|
|
||||||
|
|
||||||
protected static final DavPropertyNameSet EVENT_REQUEST_PROPERTIES = new DavPropertyNameSet();
|
protected static final DavPropertyNameSet EVENT_REQUEST_PROPERTIES = new DavPropertyNameSet();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("permanenturl", SCHEMAS_EXCHANGE));
|
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("permanenturl", SCHEMAS_EXCHANGE));
|
||||||
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.GETETAG);
|
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.GETETAG);
|
||||||
|
EVENT_REQUEST_PROPERTIES.add(DavPropertyName.create("contentclass", Namespace.getNamespace("DAV:")));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static final DavPropertyNameSet WELL_KNOWN_FOLDERS = new DavPropertyNameSet();
|
protected static final DavPropertyNameSet WELL_KNOWN_FOLDERS = new DavPropertyNameSet();
|
||||||
@ -605,6 +608,15 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getPropertyIfExists(DavPropertySet properties, DavPropertyName davPropertyName, String defaultValue) {
|
||||||
|
String value = getPropertyIfExists(properties, davPropertyName);
|
||||||
|
if (value == null) {
|
||||||
|
return defaultValue;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected String getPropertyIfExists(DavPropertySet properties, DavPropertyName davPropertyName) {
|
protected String getPropertyIfExists(DavPropertySet properties, DavPropertyName davPropertyName) {
|
||||||
DavProperty property = properties.get(davPropertyName);
|
DavProperty property = properties.get(davPropertyName);
|
||||||
if (property == null) {
|
if (property == null) {
|
||||||
@ -1876,20 +1888,181 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calendar event object
|
* Generic folder item.
|
||||||
*/
|
*/
|
||||||
public class Event {
|
public abstract class Item {
|
||||||
protected String href;
|
protected String href;
|
||||||
protected String permanentUrl;
|
protected String permanentUrl;
|
||||||
protected String displayName;
|
protected String displayName;
|
||||||
protected String etag;
|
protected String etag;
|
||||||
protected String contentClass;
|
protected String contentClass;
|
||||||
protected String noneMatch;
|
protected String noneMatch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create empty Item.
|
||||||
|
*/
|
||||||
|
public Item() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return item content type
|
||||||
|
* @return content type
|
||||||
|
*/
|
||||||
|
public abstract String getContentType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve item body from Exchange
|
||||||
|
* @return item body
|
||||||
|
* @throws HttpException on error
|
||||||
|
*/
|
||||||
|
public abstract String getBody() throws HttpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Item instance from multistatusResponse info
|
||||||
|
* @param multiStatusResponse response
|
||||||
|
* @throws URIException on error
|
||||||
|
*/
|
||||||
|
public Item(MultiStatusResponse multiStatusResponse) throws URIException {
|
||||||
|
href = URIUtil.decode(multiStatusResponse.getHref());
|
||||||
|
permanentUrl = getPropertyIfExists(multiStatusResponse.getProperties(HttpStatus.SC_OK), "permanenturl", SCHEMAS_EXCHANGE);
|
||||||
|
etag = getPropertyIfExists(multiStatusResponse.getProperties(HttpStatus.SC_OK), "getetag", Namespace.getNamespace("DAV:"));
|
||||||
|
displayName = getPropertyIfExists(multiStatusResponse.getProperties(HttpStatus.SC_OK), "displayname", Namespace.getNamespace("DAV:"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get event name (file name part in URL).
|
||||||
|
*
|
||||||
|
* @return event name
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
int index = href.lastIndexOf('/');
|
||||||
|
if (index >= 0) {
|
||||||
|
return href.substring(index + 1);
|
||||||
|
} else {
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get event etag (last change tag).
|
||||||
|
*
|
||||||
|
* @return event etag
|
||||||
|
*/
|
||||||
|
public String getEtag() {
|
||||||
|
return etag;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpException buildHttpException(Exception e) {
|
||||||
|
String message = "Unable to get event " + getName() + " at " + permanentUrl + ": " + e.getMessage();
|
||||||
|
LOGGER.warn(message);
|
||||||
|
return new HttpException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Calendar event object
|
||||||
|
*/
|
||||||
|
public class Contact extends Item {
|
||||||
|
/**
|
||||||
|
* Build Contact instance from multistatusResponse info
|
||||||
|
* @param multiStatusResponse response
|
||||||
|
* @throws URIException on error
|
||||||
|
*/
|
||||||
|
public Contact(MultiStatusResponse multiStatusResponse) throws URIException {
|
||||||
|
super(multiStatusResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return "text/vcard";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBody() throws HttpException {
|
||||||
|
// first retrieve contact details
|
||||||
|
String result = null;
|
||||||
|
|
||||||
|
PropFindMethod propFindMethod = null;
|
||||||
|
try {
|
||||||
|
propFindMethod = new PropFindMethod(URIUtil.encodePath(permanentUrl));
|
||||||
|
DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propFindMethod);
|
||||||
|
MultiStatus responses = propFindMethod.getResponseBodyAsMultiStatus();
|
||||||
|
if (responses.getResponses().length > 0) {
|
||||||
|
DavPropertySet properties = responses.getResponses()[0].getProperties(HttpStatus.SC_OK);
|
||||||
|
|
||||||
|
ICSBufferedWriter writer = new ICSBufferedWriter();
|
||||||
|
writer.writeLine("BEGIN:VCARD");
|
||||||
|
writer.writeLine("VERSION:3.0");
|
||||||
|
writer.write("UID:");
|
||||||
|
writer.writeLine(URIUtil.encodePath(getName()));
|
||||||
|
writer.write("FN:");
|
||||||
|
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("cn", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
// RFC 2426: Family Name, Given Name, Additional Names, Honorific Prefixes, and Honorific Suffixes
|
||||||
|
writer.write("N:");
|
||||||
|
writer.write(getPropertyIfExists(properties, DavPropertyName.create("sn", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
writer.write(";");
|
||||||
|
writer.write(getPropertyIfExists(properties, DavPropertyName.create("givenName", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
writer.write(";");
|
||||||
|
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("middlename", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
|
||||||
|
writer.write("TEL;TYPE=cell:");
|
||||||
|
writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("mobile", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
//writer.writeLine(getPropertyIfExists(properties, DavPropertyName.create("initials", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
|
||||||
|
// The structured type value corresponds, in sequence, to the post office box; the extended address;
|
||||||
|
// the street address; the locality (e.g., city); the region (e.g., state or province);
|
||||||
|
// the postal code; the country name
|
||||||
|
// ADR;TYPE=dom,home,postal,parcel:;;123 Main Street;Any Town;CA;91921-1234
|
||||||
|
writer.write("ADR;TYPE=home:;;");
|
||||||
|
writer.write(getPropertyIfExists(properties, DavPropertyName.create("homepostaladdress", URN_SCHEMAS_CONTACTS), ""));
|
||||||
|
writer.write(";;;");
|
||||||
|
writer.newLine();
|
||||||
|
writer.writeLine("END:VCARD");
|
||||||
|
result = writer.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (DavException e) {
|
||||||
|
throw buildHttpException(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw buildHttpException(e);
|
||||||
|
} finally {
|
||||||
|
if (propFindMethod != null) {
|
||||||
|
propFindMethod.releaseConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calendar event object
|
||||||
|
*/
|
||||||
|
public class Event extends Item {
|
||||||
/**
|
/**
|
||||||
* ICS content
|
* ICS content
|
||||||
*/
|
*/
|
||||||
protected String icsBody;
|
protected String icsBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Event instance from multistatusResponse info
|
||||||
|
* @param multiStatusResponse response
|
||||||
|
* @throws URIException on error
|
||||||
|
*/
|
||||||
|
public Event(MultiStatusResponse multiStatusResponse) throws URIException {
|
||||||
|
super(multiStatusResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create empty event.
|
||||||
|
*/
|
||||||
|
public Event() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return "text/calendar;charset=UTF-8";
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isCalendarContentType(String contentType) {
|
protected boolean isCalendarContentType(String contentType) {
|
||||||
return contentType.startsWith("text/calendar") || contentType.startsWith("application/ics");
|
return contentType.startsWith("text/calendar") || contentType.startsWith("application/ics");
|
||||||
}
|
}
|
||||||
@ -1977,7 +2150,8 @@ public class ExchangeSession {
|
|||||||
* @return ICS (iCalendar) event
|
* @return ICS (iCalendar) event
|
||||||
* @throws HttpException on error
|
* @throws HttpException on error
|
||||||
*/
|
*/
|
||||||
public String getICS() throws HttpException {
|
@Override
|
||||||
|
public String getBody() throws HttpException {
|
||||||
String result;
|
String result;
|
||||||
LOGGER.debug("Get event: " + permanentUrl);
|
LOGGER.debug("Get event: " + permanentUrl);
|
||||||
// try to get PR_INTERNET_CONTENT
|
// try to get PR_INTERNET_CONTENT
|
||||||
@ -2004,35 +2178,6 @@ public class ExchangeSession {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpException buildHttpException(Exception e) {
|
|
||||||
String message = "Unable to get event " + getName() + " at " + permanentUrl + ": " + e.getMessage();
|
|
||||||
LOGGER.warn(message);
|
|
||||||
return new HttpException(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get event name (file name part in URL).
|
|
||||||
*
|
|
||||||
* @return event name
|
|
||||||
*/
|
|
||||||
public String getName() {
|
|
||||||
int index = href.lastIndexOf('/');
|
|
||||||
if (index >= 0) {
|
|
||||||
return href.substring(index + 1);
|
|
||||||
} else {
|
|
||||||
return href;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get event etag (last change tag).
|
|
||||||
*
|
|
||||||
* @return event etag
|
|
||||||
*/
|
|
||||||
public String getEtag() {
|
|
||||||
return etag;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String fixTimezoneId(String line, String validTimezoneId) {
|
protected String fixTimezoneId(String line, String validTimezoneId) {
|
||||||
return StringUtil.replaceToken(line, "TZID=", ":", validTimezoneId);
|
return StringUtil.replaceToken(line, "TZID=", ":", validTimezoneId);
|
||||||
}
|
}
|
||||||
@ -2630,8 +2775,8 @@ public class ExchangeSession {
|
|||||||
LOGGER.warn("Unable to patch event to trigger activeSync push");
|
LOGGER.warn("Unable to patch event to trigger activeSync push");
|
||||||
} else {
|
} else {
|
||||||
// need to retrieve new etag
|
// need to retrieve new etag
|
||||||
Event newEvent = getEvent(href);
|
Item newItem = getItem(href);
|
||||||
eventResult.etag = newEvent.etag;
|
eventResult.etag = newItem.etag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return eventResult;
|
return eventResult;
|
||||||
@ -2639,6 +2784,38 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search contacts in provided folder.
|
||||||
|
*
|
||||||
|
* @param folderPath Exchange folder path
|
||||||
|
* @return list of contacts
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
public List<Contact> getAllContacts(String folderPath) throws IOException {
|
||||||
|
|
||||||
|
String searchQuery = "Select \"DAV:getetag\", \"http://schemas.microsoft.com/exchange/permanenturl\", \"DAV:displayname\"" +
|
||||||
|
" FROM Scope('SHALLOW TRAVERSAL OF \"" + folderPath + "\"')\n" +
|
||||||
|
" WHERE \"DAV:contentclass\" = 'urn:content-classes:person'\n";
|
||||||
|
return getContacts(folderPath, searchQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search contacts in provided folder matching the search query.
|
||||||
|
*
|
||||||
|
* @param folderPath Exchange folder path
|
||||||
|
* @param searchQuery Exchange search query
|
||||||
|
* @return list of contacts
|
||||||
|
* @throws IOException on error
|
||||||
|
*/
|
||||||
|
protected List<Contact> getContacts(String folderPath, String searchQuery) throws IOException {
|
||||||
|
List<Contact> contacts = new ArrayList<Contact>();
|
||||||
|
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(httpClient, URIUtil.encodePath(folderPath), searchQuery);
|
||||||
|
for (MultiStatusResponse response : responses) {
|
||||||
|
contacts.add(new Contact(response));
|
||||||
|
}
|
||||||
|
return contacts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search calendar messages in provided folder.
|
* Search calendar messages in provided folder.
|
||||||
*
|
*
|
||||||
@ -2718,13 +2895,13 @@ public class ExchangeSession {
|
|||||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(httpClient, URIUtil.encodePath(folderPath), searchQuery);
|
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executeSearchMethod(httpClient, URIUtil.encodePath(folderPath), searchQuery);
|
||||||
for (MultiStatusResponse response : responses) {
|
for (MultiStatusResponse response : responses) {
|
||||||
String instancetype = getPropertyIfExists(response.getProperties(HttpStatus.SC_OK), "instancetype", Namespace.getNamespace("urn:schemas:calendar:"));
|
String instancetype = getPropertyIfExists(response.getProperties(HttpStatus.SC_OK), "instancetype", Namespace.getNamespace("urn:schemas:calendar:"));
|
||||||
Event event = buildEvent(response);
|
Event event = new Event(response);
|
||||||
//noinspection VariableNotUsedInsideIf
|
//noinspection VariableNotUsedInsideIf
|
||||||
if (instancetype == null) {
|
if (instancetype == null) {
|
||||||
// check ics content
|
// check ics content
|
||||||
try {
|
try {
|
||||||
event.getICS();
|
event.getBody();
|
||||||
// getICS success => add event or task
|
// getBody success => add event or task
|
||||||
events.add(event);
|
events.add(event);
|
||||||
} catch (HttpException e) {
|
} catch (HttpException e) {
|
||||||
// invalid event: exclude from list
|
// invalid event: exclude from list
|
||||||
@ -2738,45 +2915,53 @@ public class ExchangeSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get event named eventName in folder
|
* Get item named eventName in folder
|
||||||
*
|
*
|
||||||
* @param folderPath Exchange folder path
|
* @param folderPath Exchange folder path
|
||||||
* @param eventName event name
|
* @param itemName event name
|
||||||
* @return event object
|
* @return event object
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public Event getEvent(String folderPath, String eventName) throws IOException {
|
public Item getItem(String folderPath, String itemName) throws IOException {
|
||||||
String eventPath = folderPath + '/' + eventName;
|
String itemPath = folderPath + '/' + itemName;
|
||||||
Event event;
|
Item item;
|
||||||
try {
|
try {
|
||||||
event = getEvent(eventPath);
|
item = getItem(itemPath);
|
||||||
} catch (HttpNotFoundException hnfe) {
|
} catch (HttpNotFoundException hnfe) {
|
||||||
// failover for Exchange 2007 plus encoding issue
|
// failover for Exchange 2007 plus encoding issue
|
||||||
String decodedEventName = eventName.replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''");
|
String decodedEventName = itemName.replaceAll("_xF8FF_", "/").replaceAll("_x003F_", "?").replaceAll("'", "''");
|
||||||
ExchangeSession.MessageList messages = searchMessages(folderPath, " AND \"DAV:displayname\"='" + decodedEventName + '\'');
|
ExchangeSession.MessageList messages = searchMessages(folderPath, " AND \"DAV:displayname\"='" + decodedEventName + '\'');
|
||||||
if (!messages.isEmpty()) {
|
if (!messages.isEmpty()) {
|
||||||
event = getEvent(messages.get(0).getPermanentUrl());
|
item = getItem(messages.get(0).getPermanentUrl());
|
||||||
} else {
|
} else {
|
||||||
throw hnfe;
|
throw hnfe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return event;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get event by url
|
* Get item by url
|
||||||
*
|
*
|
||||||
* @param eventPath Event path
|
* @param itemPath Event path
|
||||||
* @return event object
|
* @return event object
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public Event getEvent(String eventPath) throws IOException {
|
public Item getItem(String itemPath) throws IOException {
|
||||||
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(eventPath), 0, EVENT_REQUEST_PROPERTIES);
|
MultiStatusResponse[] responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(itemPath), 0, EVENT_REQUEST_PROPERTIES);
|
||||||
if (responses.length == 0) {
|
if (responses.length == 0) {
|
||||||
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
||||||
}
|
}
|
||||||
return buildEvent(responses[0]);
|
String contentClass = getPropertyIfExists( responses[0].getProperties(HttpStatus.SC_OK),
|
||||||
|
"contentclass", Namespace.getNamespace("DAV:"));
|
||||||
|
if ("urn:content-classes:person".equals(contentClass)) {
|
||||||
|
return new Contact(responses[0]);
|
||||||
|
} else if ("urn:content-classes:appointment".equals(contentClass)){
|
||||||
|
return new Event(responses[0]);
|
||||||
|
} else {
|
||||||
|
throw new DavMailException("EXCEPTION_EVENT_NOT_FOUND");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2804,15 +2989,6 @@ public class ExchangeSession {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Event buildEvent(MultiStatusResponse calendarResponse) throws URIException {
|
|
||||||
Event event = new Event();
|
|
||||||
event.href = URIUtil.decode(calendarResponse.getHref());
|
|
||||||
event.permanentUrl = getPropertyIfExists(calendarResponse.getProperties(HttpStatus.SC_OK), "permanenturl", SCHEMAS_EXCHANGE);
|
|
||||||
event.etag = getPropertyIfExists(calendarResponse.getProperties(HttpStatus.SC_OK), "getetag", Namespace.getNamespace("DAV:"));
|
|
||||||
event.displayName = getPropertyIfExists(calendarResponse.getProperties(HttpStatus.SC_OK), "displayname", Namespace.getNamespace("DAV:"));
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int dumpIndex;
|
private static int dumpIndex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,6 +95,7 @@ LOG_LDAP_UNSUPPORTED_FILTER_ATTRIBUTE=Unsupported filter attribute: {0}= {1}
|
|||||||
LOG_LDAP_UNSUPPORTED_FILTER_VALUE=Unsupported filter value
|
LOG_LDAP_UNSUPPORTED_FILTER_VALUE=Unsupported filter value
|
||||||
LOG_LDAP_UNSUPPORTED_OPERATION=Unsupported operation: {0}
|
LOG_LDAP_UNSUPPORTED_OPERATION=Unsupported operation: {0}
|
||||||
LOG_LISTING_EVENT=Listing event {0}/{1}
|
LOG_LISTING_EVENT=Listing event {0}/{1}
|
||||||
|
LOG_LISTING_CONTACT=Listing event {0}/{1}
|
||||||
LOG_MESSAGE={0}
|
LOG_MESSAGE={0}
|
||||||
LOG_NEW_VERSION_AVAILABLE=A new version ({0}) of DavMail Gateway is available !
|
LOG_NEW_VERSION_AVAILABLE=A new version ({0}) of DavMail Gateway is available !
|
||||||
LOG_OPEN_LINK_NOT_SUPPORTED=Open link not supported (tried AWT Desktop and SWT Program)
|
LOG_OPEN_LINK_NOT_SUPPORTED=Open link not supported (tried AWT Desktop and SWT Program)
|
||||||
@ -253,4 +254,4 @@ UI_USE_SYSTEM_PROXIES=Use system proxy settings :
|
|||||||
UI_SHOW_STARTUP_BANNER=Display startup banner
|
UI_SHOW_STARTUP_BANNER=Display startup banner
|
||||||
UI_SHOW_STARTUP_BANNER_HELP=Whether to show the initial startup notification window or not
|
UI_SHOW_STARTUP_BANNER_HELP=Whether to show the initial startup notification window or not
|
||||||
UI_IMAP_IDLE_DELAY=IDLE folder monitor delay (IMAP):
|
UI_IMAP_IDLE_DELAY=IDLE folder monitor delay (IMAP):
|
||||||
UI_IMAP_IDLE_DELAY_HELP=IMAP folder idle monitor delay in minutes, leave empty to disable IDLE support
|
UI_IMAP_IDLE_DELAY_HELP=IMAP folder idle monitor delay in minutes, leave empty to disable IDLE support
|
||||||
|
Loading…
Reference in New Issue
Block a user