mirror of
https://github.com/moparisthebest/davmail
synced 2024-12-13 19:22:22 -05:00
Caldav: failover for 404 not found on items containing '+' in url, search item by urlcompname to get permanenturl
git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@1295 3d1905a2-6b24-0410-a738-b14d5a86fcbd
This commit is contained in:
parent
60066db7be
commit
2a9c62ec14
@ -131,7 +131,7 @@ public class CaldavConnection extends AbstractConnection {
|
||||
tokens = new StringTokenizer(line);
|
||||
String command = tokens.nextToken();
|
||||
Map<String, String> headers = parseHeaders();
|
||||
String encodedPath = encodePlusSign(tokens.nextToken());
|
||||
String encodedPath = StringUtil.encodePlusSign(tokens.nextToken());
|
||||
String path = URIUtil.decode(encodedPath);
|
||||
String content = getContent(headers.get("content-length"));
|
||||
setSocketTimeout(headers.get("keep-alive"));
|
||||
@ -1186,21 +1186,6 @@ public class CaldavConnection extends AbstractConnection {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure + sign in URL is encoded.
|
||||
*
|
||||
* @param path URL path
|
||||
* @return reencoded path
|
||||
*/
|
||||
protected String encodePlusSign(String path) {
|
||||
// make sure + sign in URL is encoded
|
||||
if (path.indexOf('+') >= 0) {
|
||||
return path.replaceAll("\\+", "%2B");
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
protected class CaldavRequest {
|
||||
protected final String command;
|
||||
protected final String path;
|
||||
@ -1332,7 +1317,7 @@ public class CaldavConnection extends AbstractConnection {
|
||||
}
|
||||
|
||||
protected boolean isBrokenHrefEncoding() {
|
||||
return isUserAgent("DAVKit/3") || isUserAgent("eM Client/") || isUserAgent("Lightning/1.0b2");
|
||||
return isUserAgent("DAVKit/3") || isUserAgent("eM Client/")/* || isUserAgent("Lightning/1.0b2")*/;
|
||||
}
|
||||
|
||||
protected boolean isLightning() {
|
||||
@ -1400,7 +1385,7 @@ public class CaldavConnection extends AbstractConnection {
|
||||
if (isBrokenHrefEncoding()) {
|
||||
hrefs.add(streamReader.getElementText());
|
||||
} else {
|
||||
hrefs.add(URIUtil.decode(encodePlusSign(streamReader.getElementText())));
|
||||
hrefs.add(URIUtil.decode(StringUtil.encodePlusSign(streamReader.getElementText())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ import org.w3c.dom.Node;
|
||||
|
||||
import javax.mail.MessagingException;
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
@ -611,15 +610,8 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
return propertyValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update contact
|
||||
*
|
||||
* @return action result
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public ItemResult createOrUpdate() throws IOException {
|
||||
int status = 0;
|
||||
ExchangePropPatchMethod propPatchMethod = new ExchangePropPatchMethod(URIUtil.encodePath(getHref()), buildProperties());
|
||||
protected ExchangePropPatchMethod internalCreateOrUpdate(String encodedHref) throws IOException {
|
||||
ExchangePropPatchMethod propPatchMethod = new ExchangePropPatchMethod(encodedHref, buildProperties());
|
||||
propPatchMethod.setRequestHeader("Translate", "f");
|
||||
if (etag != null) {
|
||||
propPatchMethod.setRequestHeader("If-Match", etag);
|
||||
@ -628,21 +620,51 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
propPatchMethod.setRequestHeader("If-None-Match", noneMatch);
|
||||
}
|
||||
try {
|
||||
status = httpClient.executeMethod(propPatchMethod);
|
||||
if (status == HttpStatus.SC_MULTI_STATUS) {
|
||||
status = propPatchMethod.getResponseStatusCode();
|
||||
//noinspection VariableNotUsedInsideIf
|
||||
if (status == HttpStatus.SC_CREATED) {
|
||||
LOGGER.debug("Created contact " + getHref());
|
||||
} else {
|
||||
LOGGER.debug("Updated contact " + getHref());
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Unable to create or update contact " + status + ' ' + propPatchMethod.getStatusLine());
|
||||
}
|
||||
httpClient.executeMethod(propPatchMethod);
|
||||
} finally {
|
||||
propPatchMethod.releaseConnection();
|
||||
}
|
||||
return propPatchMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or update contact
|
||||
*
|
||||
* @return action result
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public ItemResult createOrUpdate() throws IOException {
|
||||
String encodedHref = URIUtil.encodePath(getHref());
|
||||
ExchangePropPatchMethod propPatchMethod = internalCreateOrUpdate(encodedHref);
|
||||
int status = propPatchMethod.getStatusCode();
|
||||
if (status == HttpStatus.SC_MULTI_STATUS) {
|
||||
status = propPatchMethod.getResponseStatusCode();
|
||||
//noinspection VariableNotUsedInsideIf
|
||||
if (status == HttpStatus.SC_CREATED) {
|
||||
LOGGER.debug("Created contact " + encodedHref);
|
||||
} else {
|
||||
LOGGER.debug("Updated contact " + encodedHref);
|
||||
}
|
||||
} else if (status == HttpStatus.SC_NOT_FOUND) {
|
||||
LOGGER.debug("Contact not found at " + encodedHref + ", searching permanenturl by urlcompname");
|
||||
// failover, search item by urlcompname
|
||||
MultiStatusResponse[] responses = searchItems(folderPath, EVENT_REQUEST_PROPERTIES, DavExchangeSession.this.equals("urlcompname", convertItemNameToEML(itemName)), FolderQueryTraversal.Shallow);
|
||||
if (responses.length == 1) {
|
||||
encodedHref = getPropertyIfExists(responses[0].getProperties(HttpStatus.SC_OK), "permanenturl");
|
||||
LOGGER.warn("Contact found, permanenturl is " + encodedHref);
|
||||
propPatchMethod = internalCreateOrUpdate(encodedHref);
|
||||
status = propPatchMethod.getStatusCode();
|
||||
if (status == HttpStatus.SC_MULTI_STATUS) {
|
||||
status = propPatchMethod.getResponseStatusCode();
|
||||
LOGGER.debug("Updated contact " + encodedHref);
|
||||
} else {
|
||||
LOGGER.warn("Unable to create or update contact " + status + ' ' + propPatchMethod.getStatusLine());
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
LOGGER.warn("Unable to create or update contact " + status + ' ' + propPatchMethod.getStatusLine());
|
||||
}
|
||||
ItemResult itemResult = new ItemResult();
|
||||
// 440 means forbidden on Exchange
|
||||
if (status == 440) {
|
||||
@ -803,12 +825,8 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected ItemResult createOrUpdate(byte[] mimeContent) throws IOException {
|
||||
PutMethod putmethod = new PutMethod(URIUtil.encodePath(getHref()));
|
||||
protected PutMethod internalCreateOrUpdate(String encodedHref, byte[] mimeContent) throws IOException {
|
||||
PutMethod putmethod = new PutMethod(encodedHref);
|
||||
putmethod.setRequestHeader("Translate", "f");
|
||||
putmethod.setRequestHeader("Overwrite", "f");
|
||||
if (etag != null) {
|
||||
@ -819,27 +837,54 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
}
|
||||
putmethod.setRequestHeader("Content-Type", "message/rfc822");
|
||||
putmethod.setRequestEntity(new ByteArrayRequestEntity(mimeContent, "message/rfc822"));
|
||||
int status;
|
||||
try {
|
||||
status = httpClient.executeMethod(putmethod);
|
||||
if (status == HttpURLConnection.HTTP_OK) {
|
||||
LOGGER.debug("Updated event " + getHref());
|
||||
} else if (status == HttpURLConnection.HTTP_CREATED) {
|
||||
LOGGER.warn("Overwritten event " + getHref());
|
||||
} else {
|
||||
LOGGER.warn("Unable to create or update message " + status + ' ' + putmethod.getStatusLine());
|
||||
}
|
||||
httpClient.executeMethod(putmethod);
|
||||
} finally {
|
||||
putmethod.releaseConnection();
|
||||
}
|
||||
return putmethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected ItemResult createOrUpdate(byte[] mimeContent) throws IOException {
|
||||
String encodedHref = URIUtil.encodePath(getHref());
|
||||
PutMethod putMethod = internalCreateOrUpdate(encodedHref, mimeContent);
|
||||
int status = putMethod.getStatusCode();
|
||||
|
||||
if (status == HttpStatus.SC_OK) {
|
||||
LOGGER.debug("Updated event " + encodedHref);
|
||||
} else if (status == HttpStatus.SC_CREATED) {
|
||||
LOGGER.warn("Overwritten event " + encodedHref);
|
||||
} else if (status == HttpStatus.SC_NOT_FOUND) {
|
||||
LOGGER.debug("Event not found at " + encodedHref + ", searching permanenturl by urlcompname");
|
||||
// failover, search item by urlcompname
|
||||
MultiStatusResponse[] responses = searchItems(folderPath, EVENT_REQUEST_PROPERTIES, DavExchangeSession.this.equals("urlcompname", convertItemNameToEML(itemName)), FolderQueryTraversal.Shallow);
|
||||
if (responses.length == 1) {
|
||||
encodedHref = getPropertyIfExists(responses[0].getProperties(HttpStatus.SC_OK), "permanenturl");
|
||||
LOGGER.warn("Event found, permanenturl is " + encodedHref);
|
||||
putMethod = internalCreateOrUpdate(encodedHref, mimeContent);
|
||||
status = putMethod.getStatusCode();
|
||||
if (status == HttpStatus.SC_OK) {
|
||||
LOGGER.debug("Updated event " + encodedHref);
|
||||
} else {
|
||||
LOGGER.warn("Unable to create or update event " + status + ' ' + putMethod.getStatusLine());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.warn("Unable to create or update event " + status + ' ' + putMethod.getStatusLine());
|
||||
}
|
||||
|
||||
ItemResult itemResult = new ItemResult();
|
||||
// 440 means forbidden on Exchange
|
||||
if (status == 440) {
|
||||
status = HttpStatus.SC_FORBIDDEN;
|
||||
}
|
||||
itemResult.status = status;
|
||||
if (putmethod.getResponseHeader("GetETag") != null) {
|
||||
itemResult.etag = putmethod.getResponseHeader("GetETag").getValue();
|
||||
if (putMethod.getResponseHeader("GetETag") != null) {
|
||||
itemResult.etag = putMethod.getResponseHeader("GetETag").getValue();
|
||||
}
|
||||
|
||||
// trigger activeSync push event, only if davmail.forceActiveSyncUpdate setting is true
|
||||
@ -850,7 +895,7 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
propertyList.add(Field.createDavProperty("contentclass", contentClass));
|
||||
// ... but also set PR_INTERNET_CONTENT to preserve custom properties
|
||||
propertyList.add(Field.createDavProperty("internetContent", new String(Base64.encodeBase64(mimeContent))));
|
||||
PropPatchMethod propPatchMethod = new PropPatchMethod(URIUtil.encodePath(getHref()), propertyList);
|
||||
PropPatchMethod propPatchMethod = new PropPatchMethod(encodedHref, propertyList);
|
||||
int patchStatus = DavGatewayHttpClientFacade.executeHttpMethod(httpClient, propPatchMethod);
|
||||
if (patchStatus != HttpStatus.SC_MULTI_STATUS) {
|
||||
LOGGER.warn("Unable to patch event to trigger activeSync push");
|
||||
@ -1201,6 +1246,7 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
}
|
||||
|
||||
protected static final DavPropertyNameSet EVENT_REQUEST_PROPERTIES_NAME_SET = new DavPropertyNameSet();
|
||||
|
||||
static {
|
||||
for (String attribute : EVENT_REQUEST_PROPERTIES) {
|
||||
EVENT_REQUEST_PROPERTIES_NAME_SET.add(Field.getPropertyName(attribute));
|
||||
@ -1216,14 +1262,14 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
try {
|
||||
responses = DavGatewayHttpClientFacade.executePropFindMethod(httpClient, URIUtil.encodePath(itemPath), 0, EVENT_REQUEST_PROPERTIES_NAME_SET);
|
||||
if (responses.length == 0) {
|
||||
throw new HttpNotFoundException(itemPath+" not found");
|
||||
throw new HttpNotFoundException(itemPath + " not found");
|
||||
}
|
||||
} catch (HttpNotFoundException e) {
|
||||
LOGGER.debug(itemPath +" not found, searching by urlcompname");
|
||||
LOGGER.debug(itemPath + " not found, searching by urlcompname");
|
||||
// failover: try to get event by displayname
|
||||
responses = searchItems(folderPath, EVENT_REQUEST_PROPERTIES, equals("urlcompname", emlItemName), FolderQueryTraversal.Shallow);
|
||||
if (responses.length == 0) {
|
||||
throw new HttpNotFoundException(itemPath+" not found");
|
||||
throw new HttpNotFoundException(itemPath + " not found");
|
||||
}
|
||||
}
|
||||
// build item
|
||||
@ -1234,14 +1280,14 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
List<ExchangeSession.Contact> contacts = searchContacts(folderPath, CONTACT_ATTRIBUTES, equals("urlcompname", urlcompname));
|
||||
if (contacts.isEmpty()) {
|
||||
LOGGER.warn("Item found, but unable to build contact");
|
||||
throw new HttpNotFoundException(itemPath+" not found");
|
||||
throw new HttpNotFoundException(itemPath + " not found");
|
||||
}
|
||||
return contacts.get(0);
|
||||
} else if ("urn:content-classes:appointment".equals(contentClass)
|
||||
|| "urn:content-classes:calendarmessage".equals(contentClass)) {
|
||||
return new Event(responses[0]);
|
||||
} else {
|
||||
LOGGER.warn("wrong contentclass on item "+itemPath+": "+contentClass);
|
||||
LOGGER.warn("wrong contentclass on item " + itemPath + ": " + contentClass);
|
||||
// return item anyway
|
||||
return new Event(responses[0]);
|
||||
}
|
||||
@ -1362,7 +1408,7 @@ public class DavExchangeSession extends ExchangeSession {
|
||||
// get timezoneid from OWA settings
|
||||
userTimezone.timezoneId = getTimezoneIdFromExchange();
|
||||
}
|
||||
// without a timezoneId, use Exchange timezone
|
||||
// without a timezoneId, use Exchange timezone
|
||||
if (userTimezone.timezoneId != null) {
|
||||
propertyList.add(Field.createDavProperty("timezoneid", userTimezone.timezoneId));
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ public final class StringUtil {
|
||||
public static String urlEncodeSlash(String name) {
|
||||
String result = name;
|
||||
if (name.indexOf("_xF8FF_") >= 0) {
|
||||
result = F8FF_PATTERN.matcher(result).replaceAll(String.valueOf((char)0xF8FF));
|
||||
result = F8FF_PATTERN.matcher(result).replaceAll(String.valueOf((char) 0xF8FF));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -244,10 +244,16 @@ public final class StringUtil {
|
||||
return base64Value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode item name to get actual value stored in urlcompname MAPI property.
|
||||
*
|
||||
* @param value decoded value
|
||||
* @return urlcompname encoded value
|
||||
*/
|
||||
public static String encodeUrlcompname(String value) {
|
||||
String result = value;
|
||||
if (result.indexOf("_xF8FF_") >= 0) {
|
||||
result = F8FF_PATTERN.matcher(result).replaceAll(String.valueOf((char)0xF8FF));
|
||||
result = F8FF_PATTERN.matcher(result).replaceAll(String.valueOf((char) 0xF8FF));
|
||||
}
|
||||
if (result.indexOf('&') >= 0) {
|
||||
result = AMP_PATTERN.matcher(result).replaceAll("%26");
|
||||
@ -257,4 +263,20 @@ public final class StringUtil {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Urlencode plus sign in encoded href.
|
||||
* '+' is decoded as ' ' by URIUtil.decode, the workaround is to force urlencoding to '%2B' first
|
||||
*
|
||||
* @param value encoded href
|
||||
* @return encoded href
|
||||
*/
|
||||
public static String encodePlusSign(String value) {
|
||||
String result = value;
|
||||
if (result.indexOf('+') >= 0) {
|
||||
result = PLUS_PATTERN.matcher(result).replaceAll("%2B");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user