diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 8914e7b8..3cd2f489 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -2020,6 +2020,8 @@ public String getBody() { writer.writeLine("PHOTO;TYPE=" + contactPhoto.contentType + ";ENCODING=BASE64:"); writer.writeLine(contactPhoto.content, true); } + + writer.appendProperty("KEY;X509;ENCODING=BASE64", get("msexchangecertificate")); writer.endCard(); return writer.toString(); @@ -3012,6 +3014,7 @@ public String getAlias() { CONTACT_ATTRIBUTES.add("private"); CONTACT_ATTRIBUTES.add("sensitivity"); CONTACT_ATTRIBUTES.add("fburl"); + CONTACT_ATTRIBUTES.add("msexchangecertificate"); } protected static final Set DISTRIBUTION_LIST_ATTRIBUTES = new HashSet<>(); diff --git a/src/java/davmail/exchange/ews/ContactDataShape.java b/src/java/davmail/exchange/ews/ContactDataShape.java new file mode 100644 index 00000000..e649a41b --- /dev/null +++ b/src/java/davmail/exchange/ews/ContactDataShape.java @@ -0,0 +1,34 @@ +/* + * 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; + +/** + * ResolveNames contact data shape. + */ +@SuppressWarnings({"UnusedDeclaration"}) +public class ContactDataShape extends AttributeOption { + private ContactDataShape(String value) { + super("ContactDataShape", value); + } + + public static final ContactDataShape IdOnly = new ContactDataShape("IdOnly"); + public static final ContactDataShape Default = new ContactDataShape("Default"); + public static final ContactDataShape AllProperties = new ContactDataShape("AllProperties"); + +} diff --git a/src/java/davmail/exchange/ews/EwsExchangeSession.java b/src/java/davmail/exchange/ews/EwsExchangeSession.java index 329db2ee..a3c0510a 100644 --- a/src/java/davmail/exchange/ews/EwsExchangeSession.java +++ b/src/java/davmail/exchange/ews/EwsExchangeSession.java @@ -3030,6 +3030,7 @@ protected void internalExecuteMethod(EWSMethod ewsMethod) throws IOException { GALFIND_ATTRIBUTE_MAP.put("homePhone", "HomePhone"); GALFIND_ATTRIBUTE_MAP.put("pager", "Pager"); + GALFIND_ATTRIBUTE_MAP.put("msexchangecertificate", "MSExchangeCertificate"); } protected static final HashSet IGNORE_ATTRIBUTE_SET = new HashSet<>(); diff --git a/src/java/davmail/exchange/ews/Field.java b/src/java/davmail/exchange/ews/Field.java index 1fab30a9..b21dc764 100644 --- a/src/java/davmail/exchange/ews/Field.java +++ b/src/java/davmail/exchange/ews/Field.java @@ -272,6 +272,8 @@ private Field() { // attachments FIELD_MAP.put("attachments", new UnindexedFieldURI("item:Attachments")); + // user certificate + FIELD_MAP.put("msexchangecertificate", new UnindexedFieldURI("contacts:MSExchangeCertificate")); } /** diff --git a/src/java/davmail/exchange/ews/ResolveNamesMethod.java b/src/java/davmail/exchange/ews/ResolveNamesMethod.java index 58f65340..20891d67 100644 --- a/src/java/davmail/exchange/ews/ResolveNamesMethod.java +++ b/src/java/davmail/exchange/ews/ResolveNamesMethod.java @@ -1,156 +1,204 @@ -/* - * 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 davmail.exchange.XMLStreamUtil; - -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamReader; - -/** - * Resolve Names method. - */ -public class ResolveNamesMethod extends EWSMethod { - protected static final AttributeOption RETURN_FULL_CONTACT_DATA = new AttributeOption("ReturnFullContactData", "true"); - - /** - * Build Resolve Names method - * - * @param value search value - */ - public ResolveNamesMethod(String value) { - super("Contact", "ResolveNames", "ResolutionSet"); - addMethodOption(SearchScope.ActiveDirectory); - addMethodOption(RETURN_FULL_CONTACT_DATA); - unresolvedEntry = new ElementOption("m:UnresolvedEntry", value); - } - - @Override - protected Item handleItem(XMLStreamReader reader) throws XMLStreamException { - Item responseItem = new Item(); - responseItem.type = "Contact"; - // skip to Contact - while (reader.hasNext() && !XMLStreamUtil.isStartTag(reader, "Resolution")) { - reader.next(); - } - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Resolution")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - if ("Mailbox".equals(tagLocalName)) { - handleMailbox(reader, responseItem); - } else if ("Contact".equals(tagLocalName)) { - handleContact(reader, responseItem); - } - } - } - return responseItem; - } - - protected void handleMailbox(XMLStreamReader reader, Item responseItem) throws XMLStreamException { - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Mailbox")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - if ("Name".equals(tagLocalName)) { - responseItem.put(tagLocalName, XMLStreamUtil.getElementText(reader)); - } else if ("EmailAddress".equals(tagLocalName)) { - responseItem.put(tagLocalName, XMLStreamUtil.getElementText(reader)); - } - } - } - } - - protected void handleContact(XMLStreamReader reader, Item responseItem) throws XMLStreamException { - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Contact")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - if ("EmailAddresses".equals(tagLocalName)) { - handleEmailAddresses(reader, responseItem); - } else if ("PhysicalAddresses".equals(tagLocalName)) { - handlePhysicalAddresses(reader, responseItem); - } else if ("PhoneNumbers".equals(tagLocalName)) { - handlePhoneNumbers(reader, responseItem); - } else { - responseItem.put(tagLocalName, XMLStreamUtil.getElementText(reader)); - } - } - } - } - - protected void handlePhysicalAddress(XMLStreamReader reader, Item responseItem, String addressType) throws XMLStreamException { - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Entry")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - String value = XMLStreamUtil.getElementText(reader); - responseItem.put(addressType + tagLocalName, value); - } - } - } - - protected void handlePhysicalAddresses(XMLStreamReader reader, Item responseItem) throws XMLStreamException { - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "PhysicalAddresses")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - if ("Entry".equals(tagLocalName)) { - String key = getAttributeValue(reader, "Key"); - handlePhysicalAddress(reader, responseItem, key); - } - } - } - } - - protected void handlePhoneNumbers(XMLStreamReader reader, Item responseItem) throws XMLStreamException { - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "PhoneNumbers")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - if ("Entry".equals(tagLocalName)) { - String key = getAttributeValue(reader, "Key"); - String value = XMLStreamUtil.getElementText(reader); - responseItem.put(key, value); - } - } - } - } - - @Override - protected void handleEmailAddresses(XMLStreamReader reader, Item responseItem) throws XMLStreamException { - while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "EmailAddresses")) { - reader.next(); - if (XMLStreamUtil.isStartTag(reader)) { - String tagLocalName = reader.getLocalName(); - if ("Entry".equals(tagLocalName)) { - String value = XMLStreamUtil.getElementText(reader); - if (value != null) { - if (value.startsWith("smtp:") || value.startsWith("SMTP:")) { - value = value.substring(5); - // get smtp address only if not already available through Mailbox info - responseItem.putIfAbsent("EmailAddress", value); - } - } - } - } - } - } - -} +/* + * 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 davmail.exchange.XMLStreamUtil; + +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +/** + * Resolve Names method. + */ +public class ResolveNamesMethod extends EWSMethod { + protected static final AttributeOption RETURN_FULL_CONTACT_DATA = new AttributeOption("ReturnFullContactData", "true"); + + /** + * Build Resolve Names method + * + * @param value search value + */ + public ResolveNamesMethod(String value) { + super("Contact", "ResolveNames", "ResolutionSet"); + addMethodOption(SearchScope.ActiveDirectory); + addMethodOption(RETURN_FULL_CONTACT_DATA); + addMethodOption(ContactDataShape.AllProperties); + unresolvedEntry = new ElementOption("m:UnresolvedEntry", value); + } + + @Override + protected Item handleItem(XMLStreamReader reader) throws XMLStreamException { + Item responseItem = new Item(); + responseItem.type = "Contact"; + // skip to Contact + while (reader.hasNext() && !XMLStreamUtil.isStartTag(reader, "Resolution")) { + reader.next(); + } + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Resolution")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Mailbox".equals(tagLocalName)) { + handleMailbox(reader, responseItem); + } else if ("Contact".equals(tagLocalName)) { + handleContact(reader, responseItem); + } + } + } + return responseItem; + } + + protected void handleMailbox(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Mailbox")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Name".equals(tagLocalName)) { + responseItem.put(tagLocalName, XMLStreamUtil.getElementText(reader)); + } else if ("EmailAddress".equals(tagLocalName)) { + responseItem.put(tagLocalName, XMLStreamUtil.getElementText(reader)); + } + } + } + } + + protected void handleContact(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Contact")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("EmailAddresses".equals(tagLocalName)) { + handleEmailAddresses(reader, responseItem); + } else if ("PhysicalAddresses".equals(tagLocalName)) { + handlePhysicalAddresses(reader, responseItem); + } else if ("PhoneNumbers".equals(tagLocalName)) { + handlePhoneNumbers(reader, responseItem); + } else if ("MSExchangeCertificate".equals(tagLocalName) + || "UserSMIMECertificate".equals(tagLocalName)) { + handleUserCertificate(reader, responseItem, tagLocalName); + } else if ("ManagerMailbox".equals(tagLocalName) + || "Attachments".equals(tagLocalName) + || "Categories".equals(tagLocalName) + || "InternetMessageHeaders".equals(tagLocalName) + || "ResponseObjects".equals(tagLocalName) + || "ExtendedProperty".equals(tagLocalName) + || "EffectiveRights".equals(tagLocalName) + || "CompleteName".equals(tagLocalName) + || "Children".equals(tagLocalName) + || "Companies".equals(tagLocalName) + || "ImAddresses".equals(tagLocalName) + || "DirectReports".equals(tagLocalName)) { + skipTag(reader, tagLocalName); + } else { + responseItem.put(tagLocalName, XMLStreamUtil.getElementText(reader)); + } + } + } + } + + protected void handlePhysicalAddress(XMLStreamReader reader, Item responseItem, String addressType) throws XMLStreamException { + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "Entry")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + String value = XMLStreamUtil.getElementText(reader); + responseItem.put(addressType + tagLocalName, value); + } + } + } + + protected void handlePhysicalAddresses(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "PhysicalAddresses")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Entry".equals(tagLocalName)) { + String key = getAttributeValue(reader, "Key"); + handlePhysicalAddress(reader, responseItem, key); + } + } + } + } + + protected void handlePhoneNumbers(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "PhoneNumbers")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Entry".equals(tagLocalName)) { + String key = getAttributeValue(reader, "Key"); + String value = XMLStreamUtil.getElementText(reader); + responseItem.put(key, value); + } + } + } + } + + protected void handleUserCertificate(XMLStreamReader reader, Item responseItem, String contextTagLocalName) throws XMLStreamException { + boolean firstValueRead = false; + String certificate = ""; + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, contextTagLocalName)) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Base64Binary".equals(tagLocalName)) { + String value = reader.getElementText(); + if ((value != null) && !value.isEmpty()) { + if (!firstValueRead) { + // Only first certificate value will be read + certificate = value; + } else { + LOGGER.debug("ResolveNames multiple certificates found, tagLocaleName=" + + contextTagLocalName + " Certificate [" + value + "] ignored"); + } + } + } + } + } + responseItem.put(contextTagLocalName, certificate); + } + + protected void skipTag(XMLStreamReader reader, String tagLocalName) throws XMLStreamException { + LOGGER.debug("ResolveNames tag parsing skipped. tagLocalName=" + tagLocalName); + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, tagLocalName)) { + reader.next(); + } + } + + @Override + protected void handleEmailAddresses(XMLStreamReader reader, Item responseItem) throws XMLStreamException { + while (reader.hasNext() && !XMLStreamUtil.isEndTag(reader, "EmailAddresses")) { + reader.next(); + if (XMLStreamUtil.isStartTag(reader)) { + String tagLocalName = reader.getLocalName(); + if ("Entry".equals(tagLocalName)) { + String value = XMLStreamUtil.getElementText(reader); + if (value != null) { + if (value.startsWith("smtp:") || value.startsWith("SMTP:")) { + value = value.substring(5); + // get smtp address only if not already available through Mailbox info + responseItem.putIfAbsent("EmailAddress", value); + } + } + } + } + } + } + +} diff --git a/src/java/davmail/ldap/LdapConnection.java b/src/java/davmail/ldap/LdapConnection.java index aab33e0b..1dc18569 100644 --- a/src/java/davmail/ldap/LdapConnection.java +++ b/src/java/davmail/ldap/LdapConnection.java @@ -26,6 +26,8 @@ import davmail.exchange.ExchangeSessionFactory; import davmail.exchange.dav.DavExchangeSession; import davmail.ui.tray.DavGatewayTray; +import davmail.util.IOUtil; + import org.apache.log4j.Logger; import javax.naming.InvalidNameException; @@ -122,6 +124,7 @@ public class LdapConnection extends AbstractConnection { CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("homeStreet", "mozillahomestreet"); CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("businesshomepage", "mozillaworkurl"); CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("nickname", "mozillanickname"); + CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("msexchangecertificate", "usercertificate;binary"); } /** @@ -287,6 +290,9 @@ public class LdapConnection extends AbstractConnection { // iCal search attribute LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-serviceslocator", "apple-serviceslocator"); + + LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("usercertificate;binary", "msexchangecertificate"); + } /** @@ -967,6 +973,8 @@ protected void sendEntry(int currentMessageId, String dn, Map at for (Object value : (Iterable) values) { responseBer.encodeString((String) value, isLdapV3()); } + } else if (values instanceof byte[]) { + responseBer.encodeOctetString((byte[])values, BerEncoder.ASN_OCTET_STR); } else { throw new DavMailException("EXCEPTION_UNSUPPORTED_VALUE", values); } @@ -1019,7 +1027,7 @@ interface LdapFilter { boolean isFullSearch(); - boolean isMatch(Map person); + boolean isMatch(ExchangeSession.Contact person); } class CompoundFilter implements LdapFilter { @@ -1104,7 +1112,7 @@ public ExchangeSession.Condition getContactSearchFilter() { * @param person person attributes map * @return true if filter match */ - public boolean isMatch(Map person) { + public boolean isMatch(ExchangeSession.Contact person) { if (type == LDAP_FILTER_OR) { for (LdapFilter child : criteria) { if (!child.isFullSearch()) { @@ -1279,13 +1287,14 @@ public ExchangeSession.Condition getContactSearchFilter() { return condition; } - public boolean isMatch(Map person) { + public boolean isMatch(ExchangeSession.Contact person) { if (canIgnore) { // Ignore this filter return true; } + String contactAttributeName = getContactAttributeName(attributeName); - String personAttributeValue = person.get(attributeName); + String personAttributeValue = person.get(contactAttributeName); if (personAttributeValue == null) { // No value to allow for filter match @@ -1688,6 +1697,10 @@ protected void sendPersons(int currentMessageId, String baseContext, Map