2010-05-18 07:50:00 -04:00
|
|
|
/*
|
|
|
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
|
|
|
* Copyright (C) 2010 Mickael Guessant
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
package davmail.exchange.ews;
|
|
|
|
|
|
|
|
import org.apache.commons.httpclient.Header;
|
|
|
|
import org.apache.commons.httpclient.HttpConnection;
|
|
|
|
import org.apache.commons.httpclient.HttpState;
|
|
|
|
import org.apache.commons.httpclient.methods.PostMethod;
|
|
|
|
import org.apache.commons.httpclient.methods.RequestEntity;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
|
|
|
|
import javax.xml.stream.XMLInputFactory;
|
|
|
|
import javax.xml.stream.XMLStreamConstants;
|
|
|
|
import javax.xml.stream.XMLStreamException;
|
|
|
|
import javax.xml.stream.XMLStreamReader;
|
|
|
|
import java.io.*;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* EWS SOAP method.
|
|
|
|
*/
|
|
|
|
public abstract class EWSMethod extends PostMethod {
|
|
|
|
protected static final Logger logger = Logger.getLogger(EWSMethod.class);
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected FolderQueryTraversalType traversal;
|
|
|
|
protected BaseShapeType baseShape;
|
|
|
|
protected FolderIdType folderId;
|
|
|
|
protected FolderIdType parentFolderId;
|
2010-05-18 07:50:00 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Build EWS method
|
|
|
|
*/
|
|
|
|
public EWSMethod() {
|
|
|
|
super("/ews/exchange.asmx");
|
|
|
|
setRequestEntity(new RequestEntity() {
|
|
|
|
byte[] content;
|
|
|
|
|
|
|
|
public boolean isRepeatable() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void writeRequest(OutputStream outputStream) throws IOException {
|
|
|
|
if (content == null) {
|
|
|
|
content = generateSoapEnvelope();
|
|
|
|
}
|
|
|
|
outputStream.write(content);
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getContentLength() {
|
|
|
|
if (content == null) {
|
|
|
|
content = generateSoapEnvelope();
|
|
|
|
}
|
|
|
|
return content.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getContentType() {
|
|
|
|
return "text/xml;charset=UTF-8";
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getName() {
|
|
|
|
return "POST";
|
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected void setBaseShape(BaseShapeType baseShapeType) {
|
|
|
|
this.baseShape = baseShapeType;
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected void setFolderId(FolderIdType folderId) {
|
|
|
|
this.folderId = folderId;
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected void setParentFolderId(FolderIdType folderId) {
|
|
|
|
this.parentFolderId = folderId;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void writeShape(Writer writer) throws IOException {
|
2010-05-18 07:50:00 -04:00
|
|
|
if (baseShape != null) {
|
|
|
|
writer.write("<m:");
|
|
|
|
writer.write(getResponseItemName());
|
|
|
|
writer.write("Shape>");
|
|
|
|
baseShape.write(writer);
|
|
|
|
writer.write("</m:");
|
|
|
|
writer.write(getResponseItemName());
|
|
|
|
writer.write("Shape>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected void writeFolderId(Writer writer) throws IOException {
|
|
|
|
if (folderId != null) {
|
|
|
|
writer.write("<m:FolderIds>");
|
|
|
|
folderId.write(writer);
|
|
|
|
writer.write("</m:FolderIds>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void writeParentFolderId(Writer writer) throws IOException {
|
|
|
|
if (parentFolderId != null) {
|
|
|
|
writer.write("<m:ParentFolderIds>");
|
|
|
|
parentFolderId.write(writer);
|
|
|
|
writer.write("</m:ParentFolderIds>");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-18 07:50:00 -04:00
|
|
|
protected byte[] generateSoapEnvelope() {
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
try {
|
|
|
|
OutputStreamWriter writer = new OutputStreamWriter(baos, "UTF-8");
|
|
|
|
writer.write("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
|
|
|
|
"xmlns:t=\"http://schemas.microsoft.com/exchange/services/2006/types\" " +
|
|
|
|
"xmlns:m=\"http://schemas.microsoft.com/exchange/services/2006/messages\">" +
|
|
|
|
"<soap:Body>");
|
|
|
|
writer.write("<m:");
|
|
|
|
writer.write(getMethodName());
|
|
|
|
if (traversal != null) {
|
|
|
|
traversal.write(writer);
|
|
|
|
}
|
|
|
|
writer.write(">");
|
2010-05-18 13:05:12 -04:00
|
|
|
writeSoapBody(writer);
|
2010-05-18 07:50:00 -04:00
|
|
|
writer.write("</m:");
|
|
|
|
writer.write(getMethodName());
|
|
|
|
writer.write(">");
|
|
|
|
writer.write("</soap:Body>" +
|
|
|
|
"</soap:Envelope>");
|
|
|
|
writer.flush();
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new RuntimeException(e);
|
|
|
|
}
|
|
|
|
return baos.toByteArray();
|
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected void writeSoapBody(Writer writer) throws IOException {
|
|
|
|
writeShape(writer);
|
|
|
|
writeParentFolderId(writer);
|
|
|
|
writeFolderId(writer);
|
|
|
|
}
|
2010-05-18 07:50:00 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Build a new XMLInputFactory.
|
|
|
|
*
|
|
|
|
* @return XML input factory
|
|
|
|
*/
|
|
|
|
public static XMLInputFactory getXmlInputFactory() {
|
|
|
|
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
|
|
|
|
inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
|
|
|
|
inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE);
|
|
|
|
return inputFactory;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Item {
|
|
|
|
public String id;
|
|
|
|
public String changeKey;
|
|
|
|
public String displayName;
|
2010-05-18 13:05:12 -04:00
|
|
|
public String type;
|
2010-05-18 07:50:00 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2010-05-18 13:05:12 -04:00
|
|
|
return "type: " + type + " id: " + id + " changeKey:" + changeKey + " displayName:" + displayName;
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected List<Item> responseItems;
|
|
|
|
protected String errorDetail;
|
|
|
|
|
|
|
|
protected abstract String getMethodName();
|
|
|
|
|
|
|
|
protected abstract String getResponseItemName();
|
|
|
|
|
|
|
|
protected abstract String getResponseItemId();
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected abstract String getResponseCollectionName();
|
|
|
|
|
2010-05-18 07:50:00 -04:00
|
|
|
public List<Item> getResponseItems() {
|
|
|
|
return responseItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String handleTag(XMLStreamReader reader, String localName) throws XMLStreamException {
|
|
|
|
String result = null;
|
|
|
|
int event = reader.getEventType();
|
|
|
|
if (event == XMLStreamConstants.START_ELEMENT && localName.equals(reader.getLocalName())) {
|
|
|
|
while (reader.hasNext() &&
|
|
|
|
!((event == XMLStreamConstants.END_ELEMENT && localName.equals(reader.getLocalName())))) {
|
|
|
|
event = reader.next();
|
|
|
|
if (event == XMLStreamConstants.CHARACTERS) {
|
|
|
|
result = reader.getText();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void handleErrors(XMLStreamReader reader) throws XMLStreamException {
|
|
|
|
String result = handleTag(reader, "ResponseCode");
|
|
|
|
if (result != null && !"NoError".equals(result)) {
|
|
|
|
errorDetail = result;
|
|
|
|
}
|
|
|
|
result = handleTag(reader, "faultstring");
|
|
|
|
if (result != null) {
|
|
|
|
errorDetail = result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
protected boolean isStartTag(XMLStreamReader reader) {
|
|
|
|
return (reader.getEventType() == XMLStreamConstants.START_ELEMENT);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean isStartTag(XMLStreamReader reader, String tagLocalName) {
|
|
|
|
return (reader.getEventType() == XMLStreamConstants.START_ELEMENT) && (reader.getLocalName().equals(tagLocalName));
|
|
|
|
}
|
|
|
|
|
|
|
|
protected boolean isEndTag(XMLStreamReader reader, String tagLocalName) {
|
|
|
|
return (reader.getEventType() == XMLStreamConstants.END_ELEMENT) && (reader.getLocalName().equals(tagLocalName));
|
|
|
|
}
|
|
|
|
|
2010-05-18 07:50:00 -04:00
|
|
|
protected Item handleItem(XMLStreamReader reader) throws XMLStreamException {
|
2010-05-18 13:05:12 -04:00
|
|
|
Item item = new Item();
|
|
|
|
item.type = reader.getLocalName();
|
2010-05-18 07:50:00 -04:00
|
|
|
int event = reader.getEventType();
|
2010-05-18 13:05:12 -04:00
|
|
|
while (reader.hasNext() && !isEndTag(reader, item.type)) {
|
|
|
|
event = reader.next();
|
|
|
|
if (event == XMLStreamConstants.START_ELEMENT && getResponseItemId().equals(reader.getLocalName())) {
|
|
|
|
for (int i = 0; i < reader.getAttributeCount(); i++) {
|
|
|
|
if ("Id".equals(reader.getAttributeLocalName(i))) {
|
|
|
|
item.id = reader.getAttributeValue(i);
|
|
|
|
} else if ("ChangeKey".equals(reader.getAttributeLocalName(i))) {
|
|
|
|
item.changeKey = reader.getAttributeValue(i);
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
}
|
2010-05-18 13:05:12 -04:00
|
|
|
} else {
|
|
|
|
String displayName = handleTag(reader, "DisplayName");
|
|
|
|
if (displayName != null) {
|
|
|
|
item.displayName = displayName;
|
|
|
|
}
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
}
|
2010-05-18 13:05:12 -04:00
|
|
|
return item;
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void processResponseBody(HttpState httpState, HttpConnection httpConnection) {
|
|
|
|
Header contentTypeHeader = getResponseHeader("Content-Type");
|
|
|
|
if (contentTypeHeader != null && "text/xml; charset=utf-8".equals(contentTypeHeader.getValue())) {
|
|
|
|
responseItems = new ArrayList<Item>();
|
|
|
|
XMLStreamReader reader = null;
|
|
|
|
try {
|
|
|
|
XMLInputFactory xmlInputFactory = getXmlInputFactory();
|
|
|
|
reader = xmlInputFactory.createXMLStreamReader(getResponseBodyAsStream());
|
|
|
|
while (reader.hasNext()) {
|
2010-05-18 13:05:12 -04:00
|
|
|
reader.next();
|
2010-05-18 07:50:00 -04:00
|
|
|
handleErrors(reader);
|
2010-05-18 13:05:12 -04:00
|
|
|
if (isStartTag(reader, getResponseCollectionName())) {
|
|
|
|
handleItems(reader);
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
logger.error("Error while parsing soap response: " + e);
|
|
|
|
} catch (XMLStreamException e) {
|
|
|
|
logger.error("Error while parsing soap response: " + e);
|
|
|
|
}
|
|
|
|
if (errorDetail != null) {
|
|
|
|
logger.error(errorDetail);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-18 13:05:12 -04:00
|
|
|
private void handleItems(XMLStreamReader reader) throws XMLStreamException {
|
|
|
|
while (reader.hasNext() && !isEndTag(reader, getResponseCollectionName())) {
|
|
|
|
reader.next();
|
|
|
|
if (isStartTag(reader)) {
|
|
|
|
responseItems.add(handleItem(reader));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-05-18 07:50:00 -04:00
|
|
|
}
|