From 939c7567b7fd38594e044605904cdf171610cec0 Mon Sep 17 00:00:00 2001 From: mguessan Date: Wed, 3 Dec 2008 11:38:35 +0000 Subject: [PATCH] First commit of the LDAP to GAL address book listener git-svn-id: http://svn.code.sf.net/p/davmail/code/trunk@191 3d1905a2-6b24-0410-a738-b14d5a86fcbd --- src/java/davmail/AbstractConnection.java | 22 +- src/java/davmail/DavGateway.java | 17 + .../davmail/exchange/ExchangeSession.java | 69 ++- src/java/davmail/exchange/XMLStreamUtil.java | 48 +- src/java/davmail/ldap/LdapConnection.java | 558 ++++++++++++++++++ src/java/davmail/ldap/LdapServer.java | 29 + 6 files changed, 713 insertions(+), 30 deletions(-) create mode 100644 src/java/davmail/ldap/LdapConnection.java create mode 100644 src/java/davmail/ldap/LdapServer.java diff --git a/src/java/davmail/AbstractConnection.java b/src/java/davmail/AbstractConnection.java index 39f20548..602f26f5 100644 --- a/src/java/davmail/AbstractConnection.java +++ b/src/java/davmail/AbstractConnection.java @@ -1,17 +1,13 @@ package davmail; import davmail.exchange.ExchangeSession; -import davmail.tray.DavGatewayTray; import davmail.smtp.SmtpConnection; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.Socket; - +import davmail.tray.DavGatewayTray; import org.apache.commons.httpclient.util.Base64; +import java.io.*; +import java.net.Socket; + /** * Generic connection common to pop3 and smtp implementations */ @@ -28,7 +24,13 @@ public class AbstractConnection extends Thread { // Exchange session proxy protected ExchangeSession session; - // Initialize the streams and start the thread + // only set the thread name and socket + public AbstractConnection(String name, Socket clientSocket) { + super(name); + this.client = clientSocket; + } + + // Initialize the streams and set thread name public AbstractConnection(String name, Socket clientSocket, String encoding) { super(name + "-" + clientSocket.getPort()); this.client = clientSocket; @@ -39,7 +41,7 @@ public class AbstractConnection extends Thread { } else { in = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")); } - os = client.getOutputStream(); + os = new BufferedOutputStream(client.getOutputStream()); } catch (IOException e) { close(); DavGatewayTray.error("Exception while getting socket streams", e); diff --git a/src/java/davmail/DavGateway.java b/src/java/davmail/DavGateway.java index 35c8b9f4..404b8e6d 100644 --- a/src/java/davmail/DavGateway.java +++ b/src/java/davmail/DavGateway.java @@ -6,6 +6,7 @@ import davmail.http.DavGatewaySSLProtocolSocketFactory; import davmail.pop.PopServer; import davmail.smtp.SmtpServer; import davmail.tray.DavGatewayTray; +import davmail.ldap.LdapServer; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; @@ -24,6 +25,7 @@ public class DavGateway { private static SmtpServer smtpServer; private static PopServer popServer; private static CaldavServer caldavServer; + private static LdapServer ldapServer; /** * Start the gateway, listen on spécified smtp and pop3 ports @@ -57,17 +59,24 @@ public class DavGateway { if (caldavPort == 0) { caldavPort = CaldavServer.DEFAULT_PORT; } + int ldapPort = Settings.getIntProperty("davmail.ldapPort"); + if (ldapPort == 0) { + ldapPort = LdapServer.DEFAULT_PORT; + } try { smtpServer = new SmtpServer(smtpPort); popServer = new PopServer(popPort); caldavServer = new CaldavServer(caldavPort); + ldapServer = new LdapServer(ldapPort); smtpServer.start(); popServer.start(); caldavServer.start(); + ldapServer.start(); String message = "DavMail gateway listening on SMTP port " + smtpPort + ", Caldav port " + caldavPort + + ", LDAP port " + ldapPort + " and POP port " + popPort; String releasedVersion = getReleasedVersion(); String currentVersion = getCurrentVersion(); @@ -109,6 +118,14 @@ public class DavGateway { DavGatewayTray.warn("Exception waiting for listener to die", e); } } + if (ldapServer != null) { + ldapServer.close(); + try { + ldapServer.join(); + } catch (InterruptedException e) { + DavGatewayTray.warn("Exception waiting for listener to die", e); + } + } } public static String getCurrentVersion() { diff --git a/src/java/davmail/exchange/ExchangeSession.java b/src/java/davmail/exchange/ExchangeSession.java index 9aa67af0..9b15d1f1 100644 --- a/src/java/davmail/exchange/ExchangeSession.java +++ b/src/java/davmail/exchange/ExchangeSession.java @@ -17,10 +17,6 @@ import org.htmlcleaner.HtmlCleaner; import org.htmlcleaner.TagNode; import javax.mail.internet.MimeUtility; -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.net.HttpURLConnection; import java.net.URL; @@ -338,17 +334,6 @@ public class ExchangeSession { } else { message.append(exc); } - try { - message.append("\nWebdav status:"); - message.append(wdr.getStatusCode()); - - String webdavStatusMessage = wdr.getStatusMessage(); - if (webdavStatusMessage != null) { - message.append(webdavStatusMessage); - } - } catch (Exception e) { - LOGGER.error("Exception getting status from " + wdr); - } LOGGER.error(message.toString()); throw new IOException(message.toString()); @@ -948,7 +933,6 @@ public class ExchangeSession { public String getEmail() throws IOException { String email = null; GetMethod getMethod = new GetMethod("/public/?Cmd=galfind&AN=" + getUserName()); - XMLStreamReader reader = null; try { int status = wdr.retrieveSessionInstance().executeMethod(getMethod); if (status != HttpStatus.SC_OK) { @@ -965,6 +949,59 @@ public class ExchangeSession { return email; } + /** + * Search users in global address book + * + * @param searchValue + * @return List of users + */ + public Map> galFind(String searchAttribute, String searchValue) throws IOException { + Map> results = new HashMap>(); + GetMethod getMethod = new GetMethod(URIUtil.encodePathQuery("/public/?Cmd=galfind&" + searchAttribute + "=" + searchValue)); + try { + int status = wdr.retrieveSessionInstance().executeMethod(getMethod); + if (status != HttpStatus.SC_OK) { + throw new IOException(status + "Unable to find users from: " + getMethod.getURI()); + } + results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "item", "AN"); + // add detailed information, only if few results + if (results.size() <=10) { + for (Map person : results.values()) { + galLookup(person); + } + } + } finally { + getMethod.releaseConnection(); + } + + return results; + } + + public void galLookup(Map person) { + GetMethod getMethod = null; + try { + getMethod = new GetMethod(URIUtil.encodePathQuery("/public/?Cmd=gallookup&ADDR=" + person.get("EM"))); + int status = wdr.retrieveSessionInstance().executeMethod(getMethod); + if (status != HttpStatus.SC_OK) { + throw new IOException(status + "Unable to find users from: " + getMethod.getURI()); + } + Map> results = XMLStreamUtil.getElementContentsAsMap(getMethod.getResponseBodyAsStream(), "person", "alias"); + // add detailed information + if (results.size() > 0) { + Map fullperson = results.get(person.get("AN")); + for (Map.Entry entry : fullperson.entrySet()) { + person.put(entry.getKey(), entry.getValue()); + } + } + } catch (IOException e) { + LOGGER.warn("Unable to gallookup person: " + person); + } finally { + if (getMethod != null) { + getMethod.releaseConnection(); + } + } + } + public String getFreebusy(Map valueMap) throws IOException { String result = null; diff --git a/src/java/davmail/exchange/XMLStreamUtil.java b/src/java/davmail/exchange/XMLStreamUtil.java index b0ecd5dd..b11d9d4e 100644 --- a/src/java/davmail/exchange/XMLStreamUtil.java +++ b/src/java/davmail/exchange/XMLStreamUtil.java @@ -6,6 +6,10 @@ import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import java.io.IOException; import java.io.InputStream; +import java.util.List; +import java.util.ArrayList; +import java.util.Map; +import java.util.HashMap; /** * XmlStreamReader utility methods @@ -15,15 +19,15 @@ public class XMLStreamUtil { } public static XMLInputFactory getXmlInputFactory() { - XMLInputFactory inputFactory = XMLInputFactory.newInstance(); - inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); - inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE); + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); + inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.TRUE); return inputFactory; } public static String getElementContentByLocalName(InputStream inputStream, String localName) throws IOException { String elementContent = null; - XMLStreamReader reader = null; + XMLStreamReader reader = null; try { XMLInputFactory inputFactory = getXmlInputFactory(); @@ -51,4 +55,40 @@ public class XMLStreamUtil { } return elementContent; } + + public static Map> getElementContentsAsMap(InputStream inputStream, String rowName, String idName) throws IOException { + Map> results = new HashMap>(); + Map item = null; + String currentElement = null; + XMLStreamReader reader = null; + try { + XMLInputFactory inputFactory = getXmlInputFactory(); + reader = inputFactory.createXMLStreamReader(inputStream); + while (reader.hasNext()) { + int event = reader.next(); + if (event == XMLStreamConstants.START_ELEMENT && rowName.equals(reader.getLocalName())) { + item = new HashMap(); + } else if (event == XMLStreamConstants.END_ELEMENT && rowName.equals(reader.getLocalName())) { + results.put(item.get(idName),item); + item = null; + } else if (event == XMLStreamConstants.START_ELEMENT && item != null) { + currentElement = reader.getLocalName(); + } else if (event == XMLStreamConstants.CHARACTERS && currentElement != null) { + item.put(currentElement, reader.getText()); + currentElement = null; + } + } + } catch (XMLStreamException e) { + throw new IOException(e.getMessage()); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (XMLStreamException e) { + ExchangeSession.LOGGER.error(e); + } + } + return results; + } } diff --git a/src/java/davmail/ldap/LdapConnection.java b/src/java/davmail/ldap/LdapConnection.java new file mode 100644 index 00000000..e881d9f0 --- /dev/null +++ b/src/java/davmail/ldap/LdapConnection.java @@ -0,0 +1,558 @@ +package davmail.ldap; + +import com.sun.jndi.ldap.Ber; +import com.sun.jndi.ldap.BerDecoder; +import com.sun.jndi.ldap.BerEncoder; +import davmail.AbstractConnection; +import davmail.exchange.ExchangeSessionFactory; +import davmail.tray.DavGatewayTray; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.Socket; +import java.util.Map; +import java.util.List; +import java.util.HashMap; + +/** + * Handle a caldav connection. + */ +public class LdapConnection extends AbstractConnection { + static final int LDAP_VERSION2 = 0x02; + static final int LDAP_VERSION3 = 0x03; // LDAPv3 + + static final int LDAP_OTHER = 80; + static final int LDAP_REQ_BIND = 0x60; + static final int LDAP_REQ_SEARCH = 99; + static final int LDAP_REP_BIND = 0x61; + static final int LDAP_REP_SEARCH = 0x64; + static final int LDAP_REP_RESULT = 0x65; + static final int LDAP_SUCCESS = 0; + static final int LDAP_SIZE_LIMIT_EXCEEDED = 4; + static final int LDAP_INVALID_CREDENTIALS = 49; + + + static final int LDAP_FILTER_OR = 0xa1; + + static final int LDAP_FILTER_SUBSTRINGS = 0xa4; + static final int LDAP_FILTER_GE = 0xa5; + static final int LDAP_FILTER_LE = 0xa6; + static final int LDAP_FILTER_PRESENT = 0x87; + static final int LDAP_FILTER_APPROX = 0xa8; + + static final int LDAP_SUBSTRING_INITIAL = 0x80; + static final int LDAP_SUBSTRING_ANY = 0x81; + static final int LDAP_SUBSTRING_FINAL = 0x82; + + static final int LBER_ENUMERATED = 0x0a; + static final int LBER_SET = 0x31; + static final int LBER_SEQUENCE = 0x30; + static final int LDAP_REQ_UNBIND = 0x42; + + static final int SCOPE_BASE_OBJECT = 0; + static final int SCOPE_ONE_LEVEL = 1; + static final int SCOPE_SUBTREE = 2; + + protected InputStream is; + + // Initialize the streams and start the thread + public LdapConnection(String name, Socket clientSocket) { + super(name + "-" + clientSocket.getPort(), clientSocket); + try { + is = new BufferedInputStream(client.getInputStream()); + os = new BufferedOutputStream(client.getOutputStream()); + } catch (IOException e) { + close(); + DavGatewayTray.error("Exception while getting socket streams", e); + } + } + + public void run() { + byte inbuf[] = new byte[2048]; // Buffer for reading incoming bytes + int inMsgId = 0; // Message id of incoming response + int bytesread; // Number of bytes in inbuf + int bytesleft; // Number of bytes that need to read for completing resp + int br; // Temp; number of bytes read from stream + int offset; // Offset of where to store bytes in inbuf + int seqlen; // Length of ASN sequence + int seqlenlen; // Number of sequence length bytes + int operation = 0; + boolean eos; // End of stream + BerDecoder reqBer; // Decoder for ASN.1 BER data from inbuf + + try { + while (true) { + offset = 0; + seqlen = 0; + seqlenlen = 0; + + + // check that it is the beginning of a sequence + bytesread = is.read(inbuf, offset, 1); + if (bytesread < 0) { + break; // EOF + } + + if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) + continue; + + // get length of sequence + bytesread = is.read(inbuf, offset, 1); + if (bytesread < 0) + break; // EOF + seqlen = inbuf[offset++]; + + // if high bit is on, length is encoded in the + // subsequent length bytes and the number of length bytes + // is equal to & 0x80 (i.e. length byte with high bit off). + if ((seqlen & 0x80) == 0x80) { + seqlenlen = seqlen & 0x7f; // number of length bytes + + bytesread = 0; + eos = false; + + // Read all length bytes + while (bytesread < seqlenlen) { + br = is.read(inbuf, offset + bytesread, + seqlenlen - bytesread); + if (br < 0) { + eos = true; + break; // EOF + } + bytesread += br; + } + + // end-of-stream reached before length bytes are read + if (eos) + break; // EOF + + // Add contents of length bytes to determine length + seqlen = 0; + for (int i = 0; i < seqlenlen; i++) { + seqlen = (seqlen << 8) + (inbuf[offset + i] & 0xff); + } + offset += bytesread; + } + + // read in seqlen bytes + bytesleft = seqlen; + if ((offset + bytesleft) > inbuf.length) { + byte nbuf[] = new byte[offset + bytesleft]; + System.arraycopy(inbuf, 0, nbuf, 0, offset); + inbuf = nbuf; + } + while (bytesleft > 0) { + bytesread = is.read(inbuf, offset, bytesleft); + if (bytesread < 0) + break; // EOF + offset += bytesread; + bytesleft -= bytesread; + } + + reqBer = new BerDecoder(inbuf, 0, offset); + + reqBer.parseSeq(null); + inMsgId = reqBer.parseInt(); + operation = reqBer.parseSeq(null); + + Ber.dumpBER(System.out, "request\n", inbuf, 0, offset); + + if (operation == LDAP_REQ_BIND) { + int ldapVersion = reqBer.parseInt(); + String userName = reqBer.parseString(ldapVersion == LDAP_VERSION3); + String password = reqBer.parseStringWithTag(0x80, ldapVersion == LDAP_VERSION3, null); + + if (userName.length() > 0 && password.length() > 0) { + try { + session = ExchangeSessionFactory.getInstance(userName, password); + sendClient(inMsgId, LDAP_REP_BIND, LDAP_SUCCESS, ""); + } catch (IOException e) { + sendClient(inMsgId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, ""); + } + } else { + // anonymous bind + sendClient(inMsgId, LDAP_REP_BIND, LDAP_SUCCESS, ""); + } + + } else if (operation == LDAP_REQ_UNBIND) { + if (session != null) { + session.close(); + session = null; + } + } else if (operation == LDAP_REQ_SEARCH) { + String dn = reqBer.parseString(true); + int scope = reqBer.parseEnumeration(); + int deref = reqBer.parseEnumeration(); + int sizeLimit = reqBer.parseInt(); + if (sizeLimit > 100 || sizeLimit == 0) { + sizeLimit = 100; + } + int timelimit = reqBer.parseInt(); + boolean attrsOnly = reqBer.parseBoolean(); + int size = 0; + + BerEncoder retBer = new BerEncoder(2048); + + if (scope == SCOPE_BASE_OBJECT) { + if ("".equals(dn)) { + // Root DSE + size = 1; + // root + retBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + retBer.encodeInt(inMsgId); + retBer.beginSeq(LDAP_REP_SEARCH); + + retBer.encodeString("Root DSE", true); + retBer.beginSeq(LBER_SEQUENCE); + retBer.beginSeq(LBER_SEQUENCE); + retBer.encodeString("namingContexts", true); + retBer.beginSeq(LBER_SET); + retBer.encodeString("ou=people", true); + retBer.endSeq(); + retBer.endSeq(); + retBer.endSeq(); + + retBer.endSeq(); + retBer.endSeq(); + } if ("ou=people".equals(dn)) { + size = 1; + // root + retBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + retBer.encodeInt(inMsgId); + retBer.beginSeq(LDAP_REP_SEARCH); + + retBer.encodeString("ou=people", true); + retBer.beginSeq(LBER_SEQUENCE); + retBer.beginSeq(LBER_SEQUENCE); + retBer.encodeString("description", true); + retBer.beginSeq(LBER_SET); + retBer.encodeString("people", true); + retBer.endSeq(); + retBer.endSeq(); + retBer.endSeq(); + + retBer.endSeq(); + retBer.endSeq(); + } else if (dn.startsWith("uid=") && session != null) { + String uid = dn.substring(4, dn.indexOf(',')); + Map> persons = session.galFind("AN", uid); + size = persons.size(); + // TODO refactor + for (Map person : persons.values()) { + HashMap ATTRIBUTE_MAP = new HashMap(); + ATTRIBUTE_MAP.put("uid", "AN"); + ATTRIBUTE_MAP.put("mail", "EM"); + ATTRIBUTE_MAP.put("displayName", "DN"); + ATTRIBUTE_MAP.put("telephoneNumber", "PH"); + ATTRIBUTE_MAP.put("l", "OFFICE"); + ATTRIBUTE_MAP.put("company", "CP"); + ATTRIBUTE_MAP.put("title", "TL"); + + ATTRIBUTE_MAP.put("cn", "DN"); + ATTRIBUTE_MAP.put("givenName", "first"); + ATTRIBUTE_MAP.put("initials", "initials"); + ATTRIBUTE_MAP.put("sn", "last"); + ATTRIBUTE_MAP.put("street", "street"); + ATTRIBUTE_MAP.put("st", "state"); + ATTRIBUTE_MAP.put("postalCode", "zip"); + ATTRIBUTE_MAP.put("c", "country"); + ATTRIBUTE_MAP.put("departement", "department"); + ATTRIBUTE_MAP.put("mobile", "mobile"); + + Map ldapPerson = new HashMap(); + + for (Map.Entry entry : ATTRIBUTE_MAP.entrySet()) { + String ldapAttribute = entry.getKey(); + String exchangeAttribute = entry.getValue(); + String value = person.get(exchangeAttribute); + if (value != null) { + ldapPerson.put(ldapAttribute, value); + } + } + + retBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + retBer.encodeInt(inMsgId); + retBer.beginSeq(LDAP_REP_SEARCH); + + retBer.encodeString("uid=" + ldapPerson.get("uid") + ",ou=people", true); + retBer.beginSeq(LBER_SEQUENCE); + + for (Map.Entry entry : ldapPerson.entrySet()) { + retBer.beginSeq(LBER_SEQUENCE); + retBer.encodeString(entry.getKey(), true); + retBer.beginSeq(LBER_SET); + retBer.encodeString(entry.getValue(), true); + retBer.endSeq(); + retBer.endSeq(); + } + retBer.endSeq(); + + retBer.endSeq(); + retBer.endSeq(); + + } + //end TODO + } + + } else if ("ou=people".equals(dn) && session != null) { + // filter + Map criteria = new HashMap(); + try { + int[] seqSize = new int[1]; + int ldapFilterType = reqBer.parseSeq(seqSize); + int end = reqBer.getParsePosition() + seqSize[0]; + if (ldapFilterType == LDAP_FILTER_OR) { + System.out.print("(|"); + while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) { + int ldapFilterOperator = reqBer.parseSeq(null); + if (ldapFilterOperator == LDAP_FILTER_SUBSTRINGS) { + String attributeName = reqBer.parseString(true).toLowerCase(); + /*LBER_SEQUENCE*/ + reqBer.parseSeq(null); + int ldapFilterMode = reqBer.peekByte(); + String value = reqBer.parseStringWithTag(ldapFilterMode, true, null); + if (ldapFilterMode == LDAP_SUBSTRING_ANY) { + System.out.print("(" + attributeName + "=*" + value + "*)"); + } else if (ldapFilterMode == LDAP_SUBSTRING_INITIAL) { + System.out.print("(" + attributeName + "=" + value + "*)"); + } else if (ldapFilterMode == LDAP_SUBSTRING_FINAL) { + System.out.print("(" + attributeName + "=*" + value + ")"); + } + criteria.put(attributeName, value); + } + } + System.out.println(")"); + // simple filter + } else if (ldapFilterType == LDAP_FILTER_SUBSTRINGS) { + // TODO refactor + String attributeName = reqBer.parseString(true).toLowerCase(); + /*LBER_SEQUENCE*/ + reqBer.parseSeq(null); + int ldapFilterMode = reqBer.peekByte(); + String value = reqBer.parseStringWithTag(ldapFilterMode, true, null); + if (ldapFilterMode == LDAP_SUBSTRING_ANY) { + System.out.print("(" + attributeName + "=*" + value + "*)"); + } else if (ldapFilterMode == LDAP_SUBSTRING_INITIAL) { + System.out.print("(" + attributeName + "=" + value + "*)"); + } else if (ldapFilterMode == LDAP_SUBSTRING_FINAL) { + System.out.print("(" + attributeName + "=*" + value + ")"); + } + criteria.put(attributeName, value); + } + } catch (Exception e) { + // unsupported filter + System.out.println(e.getMessage()); + } + + + Map> persons = new HashMap>(); + if (criteria.size() > 0) { + if (criteria.containsKey("displayname")) { + for (Map person : session.galFind("DN", criteria.get("displayname")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } else if (criteria.containsKey("cn")) { + for (Map person : session.galFind("DN", criteria.get("cn")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("givenname") && persons.size() < sizeLimit) { + for (Map person : session.galFind("FN", criteria.get("givenname")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("sn") && persons.size() < sizeLimit) { + for (Map person : session.galFind("LN", criteria.get("sn")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("title") && persons.size() < sizeLimit) { + for (Map person : session.galFind("TL", criteria.get("title")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("company") && persons.size() < sizeLimit) { + for (Map person : session.galFind("CP", criteria.get("company")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("o") && persons.size() < sizeLimit) { + for (Map person : session.galFind("CP", criteria.get("o")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("department") && persons.size() < sizeLimit) { + for (Map person : session.galFind("DP", criteria.get("department")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("l") && persons.size() < sizeLimit) { + for (Map person : session.galFind("OF", criteria.get("l")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (criteria.containsKey("l") && persons.size() < sizeLimit) { + for (Map person : session.galFind("OF", criteria.get("l")).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + } else { + // full list + for (char c = 'A'; c < 'Z'; c++) { + if (persons.size() < sizeLimit) { + for (Map person : session.galFind("AN", String.valueOf(c)).values()) { + persons.put(person.get("AN"), person); + if (persons.size() == sizeLimit) { + break; + } + } + } + if (persons.size() == sizeLimit) { + break; + } + } + } + + size = persons.size(); + for (Map person : persons.values()) { + HashMap ATTRIBUTE_MAP = new HashMap(); + ATTRIBUTE_MAP.put("AN", "uid"); + ATTRIBUTE_MAP.put("EM", "mail"); + ATTRIBUTE_MAP.put("DN", "displayName"); + ATTRIBUTE_MAP.put("PH", "telephoneNumber"); + ATTRIBUTE_MAP.put("OFFICE", "l"); + ATTRIBUTE_MAP.put("CP", "company"); + ATTRIBUTE_MAP.put("TL", "title"); + + ATTRIBUTE_MAP.put("first", "givenName"); + ATTRIBUTE_MAP.put("initials", "initials"); + ATTRIBUTE_MAP.put("last", "sn"); + ATTRIBUTE_MAP.put("street", "street"); + ATTRIBUTE_MAP.put("state", "st"); + ATTRIBUTE_MAP.put("zip", "postalCode"); + ATTRIBUTE_MAP.put("country", "c"); + ATTRIBUTE_MAP.put("departement", "department"); + ATTRIBUTE_MAP.put("mobile", "mobile"); + + Map ldapPerson = new HashMap(); + for (Map.Entry entry : person.entrySet()) { + String ldapAttribute = ATTRIBUTE_MAP.get(entry.getKey()); + if (ldapAttribute != null) { + ldapPerson.put(ldapAttribute, entry.getValue()); + } + } + + + retBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + retBer.encodeInt(inMsgId); + retBer.beginSeq(LDAP_REP_SEARCH); + + retBer.encodeString("uid=" + ldapPerson.get("uid") + ",ou=people", true); + retBer.beginSeq(LBER_SEQUENCE); + + for (Map.Entry entry : ldapPerson.entrySet()) { + retBer.beginSeq(LBER_SEQUENCE); + retBer.encodeString(entry.getKey(), true); + retBer.beginSeq(LBER_SET); + retBer.encodeString(entry.getValue(), true); + retBer.endSeq(); + retBer.endSeq(); + } + retBer.endSeq(); + + retBer.endSeq(); + retBer.endSeq(); + + } + } + + Ber.dumpBER(System.out, "response\n", retBer.getBuf(), 0, retBer.getDataLen()); + + os.write(retBer.getBuf(), 0, retBer.getDataLen()); + os.flush(); + if (size == sizeLimit) { + sendClient(inMsgId, LDAP_REP_RESULT, LDAP_SIZE_LIMIT_EXCEEDED, ""); + } else { + sendClient(inMsgId, LDAP_REP_RESULT, LDAP_SUCCESS, ""); + } + } else { + sendClient(inMsgId, 0, LDAP_OTHER, "Unsupported operation"); + } + + + } + } catch (IOException e) { + DavGatewayTray.error(e); + try { + sendErr(inMsgId, operation, e); + } catch (IOException e2) { + DavGatewayTray.debug("Exception sending error to client", e2); + } + } finally { + close(); + } + DavGatewayTray.resetIcon(); + } + + public void sendErr(int inMsgId, int operation, Exception e) throws IOException { + String message = e.getMessage(); + if (message == null) { + message = e.toString(); + } + sendClient(inMsgId, operation, LDAP_OTHER, message); + } + + public void sendClient(int inMsgId, int operation, int status, String message) throws IOException { + BerEncoder retBer = new BerEncoder(2048); + + retBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR); + retBer.encodeInt(inMsgId); + retBer.beginSeq(operation); + retBer.encodeInt(status, LBER_ENUMERATED); + // dn + retBer.encodeString("", true); + // error message + retBer.encodeString(message, true); + retBer.endSeq(); + + retBer.endSeq(); + + os.write(retBer.getBuf(), 0, retBer.getDataLen()); + os.flush(); + } + + +} + diff --git a/src/java/davmail/ldap/LdapServer.java b/src/java/davmail/ldap/LdapServer.java new file mode 100644 index 00000000..e17390b1 --- /dev/null +++ b/src/java/davmail/ldap/LdapServer.java @@ -0,0 +1,29 @@ +package davmail.ldap; + +import davmail.AbstractConnection; +import davmail.AbstractServer; + +import java.io.IOException; +import java.net.Socket; + +/** + * LDAP server, handle LDAP directory requests. + */ +public class LdapServer extends AbstractServer { + public static final int DEFAULT_PORT = 389; + + /** + * Create a ServerSocket to listen for connections. + * Start the thread. + * + * @param port pop listen port, 389 if not defined (0) + * @throws java.io.IOException on error + */ + public LdapServer(int port) throws IOException { + super("LdapServer", port, LdapServer.DEFAULT_PORT); + } + + public AbstractConnection createConnectionHandler(Socket clientSocket) { + return new LdapConnection("LdapServer", clientSocket); + } +} \ No newline at end of file