2008-12-03 06:38:35 -05:00
|
|
|
package davmail.ldap;
|
|
|
|
|
|
|
|
import com.sun.jndi.ldap.Ber;
|
|
|
|
import com.sun.jndi.ldap.BerDecoder;
|
|
|
|
import com.sun.jndi.ldap.BerEncoder;
|
|
|
|
import davmail.AbstractConnection;
|
2008-12-04 10:02:53 -05:00
|
|
|
import davmail.Settings;
|
2008-12-03 06:38:35 -05:00
|
|
|
import davmail.exchange.ExchangeSessionFactory;
|
2009-04-03 03:38:31 -04:00
|
|
|
import davmail.ui.tray.DavGatewayTray;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
import java.io.BufferedInputStream;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
|
|
import java.io.IOException;
|
2008-12-17 10:25:35 -05:00
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.lang.reflect.Method;
|
2008-12-03 06:38:35 -05:00
|
|
|
import java.net.Socket;
|
2008-12-05 03:48:54 -05:00
|
|
|
import java.net.SocketException;
|
2008-12-17 10:25:35 -05:00
|
|
|
import java.net.SocketTimeoutException;
|
|
|
|
import java.util.*;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a caldav connection.
|
|
|
|
*/
|
|
|
|
public class LdapConnection extends AbstractConnection {
|
2008-12-04 07:52:30 -05:00
|
|
|
/**
|
|
|
|
* Davmail base context
|
|
|
|
*/
|
|
|
|
static final String BASE_CONTEXT = "ou=people";
|
2009-03-17 03:46:38 -04:00
|
|
|
static final String OD_BASE_CONTEXT = "o=od";
|
|
|
|
static final String OD_USER_CONTEXT = "cn=users, o=od";
|
|
|
|
static final String COMPUTER_CONTEXT = "cn=computers, o=od";
|
|
|
|
|
|
|
|
static final List<String> NAMING_CONTEXTS = new ArrayList<String>();
|
|
|
|
static {
|
|
|
|
NAMING_CONTEXTS.add(BASE_CONTEXT);
|
|
|
|
NAMING_CONTEXTS.add(OD_BASE_CONTEXT);
|
|
|
|
}
|
2008-12-05 18:40:53 -05:00
|
|
|
|
|
|
|
static final List<String> PERSON_OBJECT_CLASSES = new ArrayList<String>();
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-05 18:40:53 -05:00
|
|
|
static {
|
|
|
|
PERSON_OBJECT_CLASSES.add("top");
|
|
|
|
PERSON_OBJECT_CLASSES.add("person");
|
|
|
|
PERSON_OBJECT_CLASSES.add("organizationalPerson");
|
|
|
|
PERSON_OBJECT_CLASSES.add("inetOrgPerson");
|
2009-03-17 03:46:38 -04:00
|
|
|
// OpenDirectory class for iCal
|
2009-03-13 07:14:29 -04:00
|
|
|
PERSON_OBJECT_CLASSES.add("apple-user");
|
2008-12-05 18:40:53 -05:00
|
|
|
}
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
/**
|
|
|
|
* Exchange to LDAP attribute map
|
|
|
|
*/
|
|
|
|
static final HashMap<String, String> ATTRIBUTE_MAP = new HashMap<String, String>();
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
static {
|
2009-03-13 07:14:29 -04:00
|
|
|
ATTRIBUTE_MAP.put("apple-generateduid", "AN");
|
2008-12-04 10:02:53 -05:00
|
|
|
ATTRIBUTE_MAP.put("uid", "AN");
|
|
|
|
ATTRIBUTE_MAP.put("mail", "EM");
|
2008-12-05 18:40:53 -05:00
|
|
|
ATTRIBUTE_MAP.put("cn", "DN");
|
2008-12-04 10:02:53 -05:00
|
|
|
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("givenName", "first");
|
2008-12-04 07:52:30 -05:00
|
|
|
ATTRIBUTE_MAP.put("initials", "initials");
|
2008-12-04 10:02:53 -05:00
|
|
|
ATTRIBUTE_MAP.put("sn", "last");
|
2008-12-04 07:52:30 -05:00
|
|
|
ATTRIBUTE_MAP.put("street", "street");
|
2008-12-04 10:02:53 -05:00
|
|
|
ATTRIBUTE_MAP.put("st", "state");
|
|
|
|
ATTRIBUTE_MAP.put("postalCode", "zip");
|
|
|
|
ATTRIBUTE_MAP.put("c", "country");
|
2008-12-04 07:52:30 -05:00
|
|
|
ATTRIBUTE_MAP.put("departement", "department");
|
|
|
|
ATTRIBUTE_MAP.put("mobile", "mobile");
|
|
|
|
}
|
|
|
|
|
2009-03-13 07:14:29 -04:00
|
|
|
static final HashMap<String, String> STATIC_ATTRIBUTE_MAP = new HashMap<String, String>();
|
|
|
|
|
|
|
|
static final String COMPUTER_GUID = "52486C30-F0AB-48E3-9C37-37E9B28CDD7B";
|
2009-03-17 03:43:37 -04:00
|
|
|
static final String VIRTUALHOST_GUID = "D6DD8A10-1098-11DE-8C30-0800200C9A66";
|
2009-03-13 07:14:29 -04:00
|
|
|
|
|
|
|
static final String SERVICEINFO =
|
|
|
|
"<?xml version='1.0' encoding='UTF-8'?>" +
|
|
|
|
"<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>" +
|
|
|
|
"<plist version='1.0'>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>com.apple.macosxserver.host</key>" +
|
|
|
|
"<array>" +
|
|
|
|
"<string>localhost</string>" + // NOTE: Will be replaced by real hostname
|
|
|
|
"</array>" +
|
|
|
|
"<key>com.apple.macosxserver.virtualhosts</key>" +
|
|
|
|
"<dict>" +
|
2009-03-17 03:46:38 -04:00
|
|
|
"<key>" + VIRTUALHOST_GUID + "</key>" +
|
2009-03-13 07:14:29 -04:00
|
|
|
"<dict>" +
|
|
|
|
"<key>hostDetails</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>http</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>enabled</key>" +
|
|
|
|
"<true/>" +
|
|
|
|
"<key>port</key>" +
|
|
|
|
"<integer>9999</integer>" + // NOTE: Will be replaced by real port number
|
|
|
|
"</dict>" +
|
|
|
|
"<key>https</key>" +
|
|
|
|
"<dict>" +
|
2009-03-19 07:23:04 -04:00
|
|
|
"<key>disabled</key>" +
|
2009-03-13 07:14:29 -04:00
|
|
|
"<false/>" +
|
|
|
|
"<key>port</key>" +
|
|
|
|
"<integer>0</integer>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"<key>hostname</key>" +
|
|
|
|
"<string>localhost</string>" + // NOTE: Will be replaced by real hostname
|
|
|
|
"<key>serviceInfo</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>calendar</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>enabled</key>" +
|
|
|
|
"<true/>" +
|
|
|
|
"<key>templates</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>calendarUserAddresses</key>" +
|
|
|
|
"<array>" +
|
|
|
|
"<string>%(principaluri)s</string>" +
|
|
|
|
"<string>mailto:%(email)s</string>" +
|
|
|
|
"<string>urn:uuid:%(guid)s</string>" +
|
|
|
|
"</array>" +
|
|
|
|
"<key>principalPath</key>" +
|
|
|
|
"<string>/principals/users/%(email)s/</string>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"<key>serviceType</key>" +
|
|
|
|
"<array>" +
|
|
|
|
"<string>calendar</string>" +
|
|
|
|
"</array>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</plist>";
|
|
|
|
|
|
|
|
static {
|
2009-03-17 03:46:38 -04:00
|
|
|
STATIC_ATTRIBUTE_MAP.put("apple-serviceslocator", COMPUTER_GUID + ":" + VIRTUALHOST_GUID + ":calendar");
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
|
2008-12-05 18:40:53 -05:00
|
|
|
static final HashSet<String> EXTENDED_ATTRIBUTES = new HashSet<String>();
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-05 18:40:53 -05:00
|
|
|
static {
|
|
|
|
EXTENDED_ATTRIBUTES.add("givenname");
|
|
|
|
EXTENDED_ATTRIBUTES.add("initials");
|
|
|
|
EXTENDED_ATTRIBUTES.add("sn");
|
|
|
|
EXTENDED_ATTRIBUTES.add("street");
|
|
|
|
EXTENDED_ATTRIBUTES.add("st");
|
|
|
|
EXTENDED_ATTRIBUTES.add("postalcode");
|
|
|
|
EXTENDED_ATTRIBUTES.add("c");
|
|
|
|
EXTENDED_ATTRIBUTES.add("departement");
|
|
|
|
EXTENDED_ATTRIBUTES.add("mobile");
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
/**
|
|
|
|
* LDAP to Exchange Criteria Map
|
|
|
|
*/
|
|
|
|
static final HashMap<String, String> CRITERIA_MAP = new HashMap<String, String>();
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
static {
|
|
|
|
// assume mail starts with firstname
|
2009-02-09 17:40:55 -05:00
|
|
|
CRITERIA_MAP.put("uid", "AN");
|
2008-12-04 10:02:53 -05:00
|
|
|
CRITERIA_MAP.put("mail", "FN");
|
|
|
|
CRITERIA_MAP.put("displayname", "DN");
|
|
|
|
CRITERIA_MAP.put("cn", "DN");
|
|
|
|
CRITERIA_MAP.put("givenname", "FN");
|
|
|
|
CRITERIA_MAP.put("sn", "LN");
|
|
|
|
CRITERIA_MAP.put("title", "TL");
|
|
|
|
CRITERIA_MAP.put("company", "CP");
|
|
|
|
CRITERIA_MAP.put("o", "CP");
|
|
|
|
CRITERIA_MAP.put("l", "OF");
|
|
|
|
CRITERIA_MAP.put("department", "DP");
|
2009-03-13 07:14:29 -04:00
|
|
|
CRITERIA_MAP.put("apple-group-realname", "DP");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LDAP to Exchange Criteria Map
|
|
|
|
*/
|
|
|
|
static final HashSet<String> IGNORE_MAP = new HashSet<String>();
|
|
|
|
|
|
|
|
static {
|
|
|
|
IGNORE_MAP.add("objectclass");
|
|
|
|
IGNORE_MAP.add("apple-generateduid");
|
|
|
|
IGNORE_MAP.add("augmentconfiguration");
|
|
|
|
IGNORE_MAP.add("ou");
|
|
|
|
IGNORE_MAP.add("apple-realname");
|
2009-03-19 11:04:30 -04:00
|
|
|
IGNORE_MAP.add("apple-group-nestedgroup");
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// LDAP version
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_VERSION2 = 0x02;
|
2008-12-04 07:52:30 -05:00
|
|
|
static final int LDAP_VERSION3 = 0x03;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// LDAP request operations
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_REQ_BIND = 0x60;
|
2008-12-08 07:57:28 -05:00
|
|
|
static final int LDAP_REQ_SEARCH = 0x63;
|
2008-12-04 07:52:30 -05:00
|
|
|
static final int LDAP_REQ_UNBIND = 0x42;
|
2008-12-08 07:57:28 -05:00
|
|
|
static final int LDAP_REQ_ABANDON = 0x50;
|
2008-12-04 07:52:30 -05:00
|
|
|
|
|
|
|
// LDAP response operations
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_REP_BIND = 0x61;
|
|
|
|
static final int LDAP_REP_SEARCH = 0x64;
|
|
|
|
static final int LDAP_REP_RESULT = 0x65;
|
2008-12-04 07:52:30 -05:00
|
|
|
|
|
|
|
// LDAP return codes
|
|
|
|
static final int LDAP_OTHER = 80;
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_SUCCESS = 0;
|
|
|
|
static final int LDAP_SIZE_LIMIT_EXCEEDED = 4;
|
|
|
|
static final int LDAP_INVALID_CREDENTIALS = 49;
|
|
|
|
|
2009-02-23 05:15:44 -05:00
|
|
|
static final int LDAP_FILTER_AND = 0xa0;
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_FILTER_OR = 0xa1;
|
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// LDAP filter operators (only LDAP_FILTER_SUBSTRINGS is supported)
|
2008-12-03 06:38:35 -05:00
|
|
|
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;
|
2009-02-09 17:40:55 -05:00
|
|
|
static final int LDAP_FILTER_EQUALITY = 0xa3;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// LDAP filter mode (only startsWith supported by galfind)
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_SUBSTRING_INITIAL = 0x80;
|
|
|
|
static final int LDAP_SUBSTRING_ANY = 0x81;
|
|
|
|
static final int LDAP_SUBSTRING_FINAL = 0x82;
|
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// BER data types
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LBER_ENUMERATED = 0x0a;
|
|
|
|
static final int LBER_SET = 0x31;
|
|
|
|
static final int LBER_SEQUENCE = 0x30;
|
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// LDAP search scope
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int SCOPE_BASE_OBJECT = 0;
|
|
|
|
static final int SCOPE_ONE_LEVEL = 1;
|
|
|
|
static final int SCOPE_SUBTREE = 2;
|
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
/**
|
|
|
|
* For some unknow reaseon parseIntWithTag is private !
|
|
|
|
*/
|
2009-02-24 06:53:02 -05:00
|
|
|
static final Method parseIntWithTag;
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
static {
|
|
|
|
try {
|
|
|
|
parseIntWithTag = BerDecoder.class.getDeclaredMethod("parseIntWithTag", int.class);
|
|
|
|
parseIntWithTag.setAccessible(true);
|
|
|
|
} catch (NoSuchMethodException e) {
|
|
|
|
DavGatewayTray.error("Unable to get BerDecoder.parseIntWithTag method");
|
2009-02-24 06:53:02 -05:00
|
|
|
throw new RuntimeException(e);
|
2008-12-08 07:57:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
/**
|
|
|
|
* raw connection inputStream
|
|
|
|
*/
|
2008-12-08 07:57:28 -05:00
|
|
|
protected BufferedInputStream is;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
/**
|
|
|
|
* reusable BER encoder
|
|
|
|
*/
|
2008-12-17 10:25:35 -05:00
|
|
|
protected final BerEncoder responseBer = new BerEncoder();
|
2008-12-04 07:52:30 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
/**
|
|
|
|
* Current LDAP version (used for String encoding)
|
|
|
|
*/
|
2008-12-26 09:02:26 -05:00
|
|
|
int ldapVersion = LDAP_VERSION3;
|
2008-12-04 10:02:53 -05:00
|
|
|
|
2008-12-03 06:38:35 -05:00
|
|
|
// Initialize the streams and start the thread
|
2008-12-26 08:21:39 -05:00
|
|
|
public LdapConnection(Socket clientSocket) {
|
|
|
|
super("LdapConnection-" + clientSocket.getPort(), clientSocket);
|
2008-12-03 06:38:35 -05:00
|
|
|
try {
|
|
|
|
is = new BufferedInputStream(client.getInputStream());
|
|
|
|
os = new BufferedOutputStream(client.getOutputStream());
|
|
|
|
} catch (IOException e) {
|
|
|
|
close();
|
|
|
|
DavGatewayTray.error("Exception while getting socket streams", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
protected boolean isLdapV3() {
|
|
|
|
return ldapVersion == LDAP_VERSION3;
|
|
|
|
}
|
|
|
|
|
2008-12-03 06:38:35 -05:00
|
|
|
public void run() {
|
2008-12-17 10:25:35 -05:00
|
|
|
byte[] inbuf = new byte[2048]; // Buffer for reading incoming bytes
|
2008-12-03 06:38:35 -05:00
|
|
|
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
|
|
|
|
boolean eos; // End of stream
|
|
|
|
|
|
|
|
try {
|
2008-12-17 10:25:35 -05:00
|
|
|
ExchangeSessionFactory.checkConfig();
|
2008-12-03 06:38:35 -05:00
|
|
|
while (true) {
|
|
|
|
offset = 0;
|
|
|
|
|
|
|
|
// check that it is the beginning of a sequence
|
|
|
|
bytesread = is.read(inbuf, offset, 1);
|
|
|
|
if (bytesread < 0) {
|
|
|
|
break; // EOF
|
|
|
|
}
|
|
|
|
|
2008-12-17 10:25:35 -05:00
|
|
|
if (inbuf[offset++] != (Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR)) {
|
2008-12-03 06:38:35 -05:00
|
|
|
continue;
|
2008-12-17 10:25:35 -05:00
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
// get length of sequence
|
|
|
|
bytesread = is.read(inbuf, offset, 1);
|
2008-12-17 10:25:35 -05:00
|
|
|
if (bytesread < 0) {
|
2008-12-03 06:38:35 -05:00
|
|
|
break; // EOF
|
2008-12-17 10:25:35 -05:00
|
|
|
}
|
2008-12-05 18:40:53 -05:00
|
|
|
int seqlen = inbuf[offset++]; // Length of ASN sequence
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
// 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) {
|
2008-12-05 18:40:53 -05:00
|
|
|
int seqlenlen = seqlen & 0x7f; // number of length bytes
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
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) {
|
2008-12-17 10:25:35 -05:00
|
|
|
byte[] nbuf = new byte[offset + bytesleft];
|
2008-12-03 06:38:35 -05:00
|
|
|
System.arraycopy(inbuf, 0, nbuf, 0, offset);
|
|
|
|
inbuf = nbuf;
|
|
|
|
}
|
|
|
|
while (bytesleft > 0) {
|
|
|
|
bytesread = is.read(inbuf, offset, bytesleft);
|
2008-12-17 10:25:35 -05:00
|
|
|
if (bytesread < 0) {
|
2008-12-03 06:38:35 -05:00
|
|
|
break; // EOF
|
2008-12-17 10:25:35 -05:00
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
offset += bytesread;
|
|
|
|
bytesleft -= bytesread;
|
|
|
|
}
|
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
//Ber.dumpBER(System.out, "request\n", inbuf, 0, offset);
|
2008-12-04 10:02:53 -05:00
|
|
|
handleRequest(new BerDecoder(inbuf, 0, offset));
|
|
|
|
}
|
2008-12-05 03:48:54 -05:00
|
|
|
|
|
|
|
} catch (SocketException e) {
|
|
|
|
DavGatewayTray.debug("Connection closed");
|
2008-12-04 10:02:53 -05:00
|
|
|
} catch (SocketTimeoutException e) {
|
|
|
|
DavGatewayTray.debug("Closing connection on timeout");
|
2009-04-15 11:20:06 -04:00
|
|
|
} catch (Exception e) {
|
2008-12-04 10:02:53 -05:00
|
|
|
DavGatewayTray.error(e);
|
2008-12-17 10:25:35 -05:00
|
|
|
try {
|
|
|
|
sendErr(0, LDAP_REP_BIND, e);
|
|
|
|
} catch (IOException e2) {
|
|
|
|
DavGatewayTray.warn("Exception sending error to client", e2);
|
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
} finally {
|
|
|
|
close();
|
|
|
|
}
|
|
|
|
DavGatewayTray.resetIcon();
|
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
protected void handleRequest(BerDecoder reqBer) throws IOException {
|
|
|
|
int currentMessageId = 0;
|
|
|
|
try {
|
|
|
|
reqBer.parseSeq(null);
|
|
|
|
currentMessageId = reqBer.parseInt();
|
2008-12-08 07:57:28 -05:00
|
|
|
int requestOperation = reqBer.peekByte();
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
if (requestOperation == LDAP_REQ_BIND) {
|
2008-12-08 07:57:28 -05:00
|
|
|
reqBer.parseSeq(null);
|
2008-12-26 09:02:26 -05:00
|
|
|
ldapVersion = reqBer.parseInt();
|
2009-03-13 07:14:29 -04:00
|
|
|
userName = reqBer.parseString(isLdapV3());
|
|
|
|
password = reqBer.parseStringWithTag(Ber.ASN_CONTEXT, isLdapV3(), null);
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
if (userName.length() > 0 && password.length() > 0) {
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_BIND " + currentMessageId + " " + userName);
|
2008-12-04 10:02:53 -05:00
|
|
|
try {
|
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
2008-12-04 07:52:30 -05:00
|
|
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
2008-12-04 10:02:53 -05:00
|
|
|
} catch (IOException e) {
|
|
|
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, "");
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
} else {
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_BIND " + currentMessageId + " anonymous" + userName);
|
2008-12-04 10:02:53 -05:00
|
|
|
// anonymous bind
|
|
|
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
} else if (requestOperation == LDAP_REQ_UNBIND) {
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_UNBIND " + currentMessageId);
|
2008-12-04 10:02:53 -05:00
|
|
|
if (session != null) {
|
|
|
|
session = null;
|
|
|
|
}
|
|
|
|
} else if (requestOperation == LDAP_REQ_SEARCH) {
|
2008-12-08 07:57:28 -05:00
|
|
|
reqBer.parseSeq(null);
|
2008-12-04 10:02:53 -05:00
|
|
|
String dn = reqBer.parseString(isLdapV3());
|
|
|
|
int scope = reqBer.parseEnumeration();
|
2008-12-17 10:25:35 -05:00
|
|
|
/*int derefAliases =*/
|
|
|
|
reqBer.parseEnumeration();
|
2008-12-04 10:02:53 -05:00
|
|
|
int sizeLimit = reqBer.parseInt();
|
|
|
|
if (sizeLimit > 100 || sizeLimit == 0) {
|
|
|
|
sizeLimit = 100;
|
|
|
|
}
|
2008-12-05 18:40:53 -05:00
|
|
|
int timelimit = reqBer.parseInt();
|
2008-12-17 10:25:35 -05:00
|
|
|
/*boolean typesOnly =*/
|
|
|
|
reqBer.parseBoolean();
|
2009-03-12 09:56:37 -04:00
|
|
|
LdapFilter ldapFilter = parseFilter(reqBer);
|
2008-12-05 18:40:53 -05:00
|
|
|
Set<String> returningAttributes = parseReturningAttributes(reqBer);
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
int size = 0;
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " base=" + dn + " scope: " + scope + " sizelimit: " + sizeLimit + " timelimit: " + timelimit +
|
2009-03-12 09:56:37 -04:00
|
|
|
" filter: " + ldapFilter.toString() + " returning attributes: " + returningAttributes);
|
2008-12-04 10:02:53 -05:00
|
|
|
|
|
|
|
if (scope == SCOPE_BASE_OBJECT) {
|
|
|
|
if ("".equals(dn)) {
|
|
|
|
size = 1;
|
|
|
|
sendRootDSE(currentMessageId);
|
2009-03-12 09:56:37 -04:00
|
|
|
} else if (BASE_CONTEXT.equals(dn)) {
|
2008-12-04 10:02:53 -05:00
|
|
|
size = 1;
|
|
|
|
// root
|
|
|
|
sendBaseContext(currentMessageId);
|
|
|
|
} else if (dn.startsWith("uid=") && dn.indexOf(',') > 0 && session != null) {
|
|
|
|
// single user request
|
|
|
|
String uid = dn.substring("uid=".length(), dn.indexOf(','));
|
|
|
|
Map<String, Map<String, String>> persons = session.galFind("AN", uid);
|
2009-03-12 09:56:37 -04:00
|
|
|
Map<String, String> person = persons.get(uid.toLowerCase());
|
|
|
|
// filter out non exact results
|
|
|
|
if (persons.size() > 1 || person == null) {
|
|
|
|
persons = new HashMap<String, Map<String, String>>();
|
|
|
|
if (person != null) {
|
|
|
|
persons.put(uid.toLowerCase(), person);
|
|
|
|
}
|
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
size = persons.size();
|
2009-03-17 03:46:38 -04:00
|
|
|
sendPersons(currentMessageId, dn.substring(dn.indexOf(',')), persons, returningAttributes);
|
2009-03-12 09:56:37 -04:00
|
|
|
} else {
|
|
|
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " unrecognized dn " + dn);
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
2009-03-13 07:14:29 -04:00
|
|
|
} else if (COMPUTER_CONTEXT.equals(dn)) {
|
|
|
|
size = 1;
|
|
|
|
// computer context for iCal
|
|
|
|
sendComputerContext(currentMessageId, returningAttributes);
|
2009-03-17 03:46:38 -04:00
|
|
|
} else if ((BASE_CONTEXT.equalsIgnoreCase(dn) || OD_USER_CONTEXT.equalsIgnoreCase(dn)) && (session != null)) {
|
2008-12-04 10:02:53 -05:00
|
|
|
Map<String, Map<String, String>> persons = new HashMap<String, Map<String, String>>();
|
2009-03-12 09:56:37 -04:00
|
|
|
if (ldapFilter.isFullSearch()) {
|
2008-12-04 10:02:53 -05:00
|
|
|
// full search
|
|
|
|
for (char c = 'A'; c < 'Z'; c++) {
|
|
|
|
if (persons.size() < sizeLimit) {
|
|
|
|
for (Map<String, String> person : session.galFind("AN", String.valueOf(c)).values()) {
|
2008-12-03 06:38:35 -05:00
|
|
|
persons.put(person.get("AN"), person);
|
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
} else {
|
2009-03-12 09:56:37 -04:00
|
|
|
for (Map.Entry<String, SimpleFilter> entry : ldapFilter.getOrFilterEntrySet()) {
|
2008-12-04 10:02:53 -05:00
|
|
|
if (persons.size() < sizeLimit) {
|
2009-02-09 17:40:55 -05:00
|
|
|
for (Map<String, String> person : session.galFind(entry.getKey(), entry.getValue().value).values()) {
|
|
|
|
if ((entry.getValue().operator == LDAP_FILTER_SUBSTRINGS)
|
2009-03-12 09:56:37 -04:00
|
|
|
|| (entry.getValue().operator == LDAP_FILTER_EQUALITY &&
|
2009-02-09 17:40:55 -05:00
|
|
|
entry.getValue().value.equalsIgnoreCase(person.get(entry.getKey())))) {
|
|
|
|
persons.put(person.get("AN"), person);
|
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
size = persons.size();
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " found " + size + " results");
|
2009-03-17 03:46:38 -04:00
|
|
|
sendPersons(currentMessageId, ", "+dn, persons, returningAttributes);
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " end");
|
2009-03-12 09:56:37 -04:00
|
|
|
} else {
|
2009-03-13 07:14:29 -04:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " unrecognized dn " + dn);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size == sizeLimit) {
|
|
|
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SIZE_LIMIT_EXCEEDED, "");
|
2008-12-03 06:38:35 -05:00
|
|
|
} else {
|
2008-12-04 10:02:53 -05:00
|
|
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SUCCESS, "");
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
2008-12-08 07:57:28 -05:00
|
|
|
} else if (requestOperation == LDAP_REQ_ABANDON) {
|
|
|
|
int canceledMessageId = 0;
|
|
|
|
try {
|
|
|
|
canceledMessageId = (Integer) parseIntWithTag.invoke(reqBer, LDAP_REQ_ABANDON);
|
|
|
|
} catch (IllegalAccessException e) {
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.error(e);
|
2008-12-08 07:57:28 -05:00
|
|
|
} catch (InvocationTargetException e) {
|
|
|
|
DavGatewayTray.error(e);
|
|
|
|
}
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_ABANDON " + currentMessageId + " for search " + canceledMessageId + ", too late !");
|
2008-12-04 10:02:53 -05:00
|
|
|
} else {
|
2008-12-17 10:25:35 -05:00
|
|
|
DavGatewayTray.debug("Unsupported operation: " + requestOperation);
|
2008-12-04 10:02:53 -05:00
|
|
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_OTHER, "Unsupported operation");
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
|
|
try {
|
2008-12-04 07:52:30 -05:00
|
|
|
sendErr(currentMessageId, LDAP_REP_RESULT, e);
|
2008-12-03 06:38:35 -05:00
|
|
|
} catch (IOException e2) {
|
|
|
|
DavGatewayTray.debug("Exception sending error to client", e2);
|
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-12 09:56:37 -04:00
|
|
|
protected LdapFilter parseFilter(BerDecoder reqBer) throws IOException {
|
|
|
|
LdapFilter ldapFilter = new LdapFilter();
|
2008-12-04 10:02:53 -05:00
|
|
|
if (reqBer.peekByte() == LDAP_FILTER_PRESENT) {
|
|
|
|
String attributeName = reqBer.parseStringWithTag(LDAP_FILTER_PRESENT, isLdapV3(), null).toLowerCase();
|
|
|
|
if ("objectclass".equals(attributeName)) {
|
2009-03-12 09:56:37 -04:00
|
|
|
ldapFilter.addFilter(attributeName, new SimpleFilter());
|
2008-12-04 10:02:53 -05:00
|
|
|
} else {
|
|
|
|
DavGatewayTray.warn("Unsupported filter");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int[] seqSize = new int[1];
|
|
|
|
int ldapFilterType = reqBer.parseSeq(seqSize);
|
|
|
|
int end = reqBer.getParsePosition() + seqSize[0];
|
2009-03-12 09:56:37 -04:00
|
|
|
|
|
|
|
parseNestedFilter(reqBer, ldapFilter, ldapFilterType, end);
|
|
|
|
}
|
|
|
|
return ldapFilter;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void parseNestedFilter(BerDecoder reqBer, LdapFilter ldapFilter, int ldapFilterType, int end) throws IOException {
|
|
|
|
if (ldapFilterType == LDAP_FILTER_OR) {
|
|
|
|
ldapFilter.startFilter(LDAP_FILTER_OR);
|
|
|
|
// OR filter
|
|
|
|
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
|
|
|
int ldapFilterOperator = reqBer.parseSeq(null);
|
|
|
|
parseNestedFilter(reqBer, ldapFilter, ldapFilterOperator, end);
|
|
|
|
}
|
|
|
|
ldapFilter.endFilter();
|
|
|
|
} else if (ldapFilterType == LDAP_FILTER_AND) {
|
|
|
|
ldapFilter.startFilter(LDAP_FILTER_AND);
|
|
|
|
// AND filter
|
|
|
|
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
|
|
|
int ldapFilterOperator = reqBer.parseSeq(null);
|
|
|
|
parseNestedFilter(reqBer, ldapFilter, ldapFilterOperator, end);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
2009-03-12 09:56:37 -04:00
|
|
|
ldapFilter.endFilter();
|
|
|
|
} else {
|
|
|
|
// simple filter
|
|
|
|
parseSimpleFilter(reqBer, ldapFilter, ldapFilterType);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-12 09:56:37 -04:00
|
|
|
protected void parseSimpleFilter(BerDecoder reqBer, LdapFilter ldapFilter, int ldapFilterOperator) throws IOException {
|
2008-12-04 10:02:53 -05:00
|
|
|
String attributeName = reqBer.parseString(isLdapV3()).toLowerCase();
|
2008-12-05 13:25:09 -05:00
|
|
|
|
|
|
|
StringBuilder value = new StringBuilder();
|
2009-02-09 17:40:55 -05:00
|
|
|
if (ldapFilterOperator == LDAP_FILTER_SUBSTRINGS) {
|
|
|
|
// Thunderbird sends values with space as separate strings, rebuild value
|
|
|
|
int[] seqSize = new int[1];
|
|
|
|
/*LBER_SEQUENCE*/
|
|
|
|
reqBer.parseSeq(seqSize);
|
|
|
|
int end = reqBer.getParsePosition() + seqSize[0];
|
|
|
|
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
|
|
|
int ldapFilterMode = reqBer.peekByte();
|
|
|
|
if (value.length() > 0) {
|
|
|
|
value.append(' ');
|
|
|
|
}
|
|
|
|
value.append(reqBer.parseStringWithTag(ldapFilterMode, isLdapV3(), null));
|
2008-12-05 13:25:09 -05:00
|
|
|
}
|
2009-02-09 17:40:55 -05:00
|
|
|
} else if (ldapFilterOperator == LDAP_FILTER_EQUALITY) {
|
|
|
|
value.append(reqBer.parseString(isLdapV3()));
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.warn("Unsupported filter value");
|
2008-12-05 13:25:09 -05:00
|
|
|
}
|
2009-03-12 09:56:37 -04:00
|
|
|
|
2009-03-13 07:14:29 -04:00
|
|
|
String sValue = value.toString();
|
|
|
|
|
|
|
|
if (attributeName.equalsIgnoreCase("uid") && sValue.equals(userName)) {
|
|
|
|
// replace with actual alias instead of login name search
|
|
|
|
if (sValue.equals(userName)) {
|
|
|
|
sValue = session.getAlias();
|
2009-03-19 11:04:30 -04:00
|
|
|
DavGatewayTray.debug("Replaced " +userName+ " with " + sValue+" in uid filter");
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ldapFilter.addFilter(attributeName, new SimpleFilter(sValue, ldapFilterOperator));
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
|
|
|
|
2008-12-05 18:40:53 -05:00
|
|
|
protected Set<String> parseReturningAttributes(BerDecoder reqBer) throws IOException {
|
|
|
|
Set<String> returningAttributes = new HashSet<String>();
|
|
|
|
int[] seqSize = new int[1];
|
|
|
|
reqBer.parseSeq(seqSize);
|
|
|
|
int end = reqBer.getParsePosition() + seqSize[0];
|
|
|
|
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
2008-12-17 10:25:35 -05:00
|
|
|
returningAttributes.add(reqBer.parseString(isLdapV3()).toLowerCase());
|
2008-12-05 18:40:53 -05:00
|
|
|
}
|
|
|
|
return returningAttributes;
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
/**
|
|
|
|
* Convert to LDAP attributes and send entry
|
2008-12-17 10:25:35 -05:00
|
|
|
*
|
|
|
|
* @param currentMessageId current Message Id
|
2009-03-17 03:46:38 -04:00
|
|
|
* @param baseContext request base context (BASE_CONTEXT or OD_BASE_CONTEXT)
|
2008-12-17 10:25:35 -05:00
|
|
|
* @param persons persons Map
|
|
|
|
* @param returningAttributes returning attributes
|
2008-12-04 10:02:53 -05:00
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2009-03-17 03:46:38 -04:00
|
|
|
protected void sendPersons(int currentMessageId, String baseContext, Map<String, Map<String, String>> persons, Set<String> returningAttributes) throws IOException {
|
2009-03-12 09:56:37 -04:00
|
|
|
boolean needObjectClasses = returningAttributes.contains("objectclass") || returningAttributes.size() == 0;
|
2009-03-19 07:40:22 -04:00
|
|
|
boolean iCalSearch = returningAttributes.contains("apple-serviceslocator");
|
2008-12-05 18:40:53 -05:00
|
|
|
boolean returnAllAttributes = returningAttributes.size() == 0;
|
2008-12-04 10:02:53 -05:00
|
|
|
for (Map<String, String> person : persons.values()) {
|
2008-12-05 18:40:53 -05:00
|
|
|
boolean needDetails = returnAllAttributes;
|
|
|
|
if (!needDetails) {
|
|
|
|
for (String attributeName : EXTENDED_ATTRIBUTES) {
|
|
|
|
if (returningAttributes.contains(attributeName)) {
|
|
|
|
needDetails = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-03-19 07:40:22 -04:00
|
|
|
// iCal search, do not lookup details
|
|
|
|
if (iCalSearch) {
|
|
|
|
needDetails = false;
|
|
|
|
}
|
2008-12-05 18:40:53 -05:00
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
// add detailed information
|
|
|
|
if (needDetails) {
|
2008-12-05 18:40:53 -05:00
|
|
|
session.galLookup(person);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
2008-12-05 18:40:53 -05:00
|
|
|
returningAttributes.add("uid");
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
Map<String, Object> ldapPerson = new HashMap<String, Object>();
|
2009-03-12 09:56:37 -04:00
|
|
|
|
|
|
|
// Process all attributes that are mapped from exchange
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
for (Map.Entry<String, String> entry : ATTRIBUTE_MAP.entrySet()) {
|
|
|
|
String ldapAttribute = entry.getKey();
|
|
|
|
String exchangeAttribute = entry.getValue();
|
|
|
|
String value = person.get(exchangeAttribute);
|
2008-12-05 18:40:53 -05:00
|
|
|
if (value != null
|
|
|
|
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
2008-12-04 10:02:53 -05:00
|
|
|
ldapPerson.put(ldapAttribute, value);
|
|
|
|
}
|
|
|
|
}
|
2009-03-19 07:40:22 -04:00
|
|
|
// iCal: copy cn to sn
|
|
|
|
if (iCalSearch && ldapPerson.get("cn") != null) {
|
|
|
|
ldapPerson.put("sn", ldapPerson.get("cn"));
|
|
|
|
}
|
|
|
|
|
2009-03-13 07:14:29 -04:00
|
|
|
|
|
|
|
// Process all attributes which have static mappings
|
|
|
|
for (Map.Entry<String, String> entry : STATIC_ATTRIBUTE_MAP.entrySet()) {
|
|
|
|
String ldapAttribute = entry.getKey();
|
|
|
|
String value = entry.getValue();
|
|
|
|
|
|
|
|
if (value != null
|
|
|
|
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute.toLowerCase()))) {
|
|
|
|
ldapPerson.put(ldapAttribute, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-05 18:40:53 -05:00
|
|
|
if (needObjectClasses) {
|
|
|
|
ldapPerson.put("objectClass", PERSON_OBJECT_CLASSES);
|
|
|
|
}
|
2009-03-19 07:42:28 -04:00
|
|
|
// iCal: replace current user alias with login name
|
2009-03-19 07:23:04 -04:00
|
|
|
if (session.getAlias().equals(ldapPerson.get("uid"))) {
|
|
|
|
if (returningAttributes.contains("uidnumber")) {
|
|
|
|
ldapPerson.put("uidnumber", userName);
|
|
|
|
}
|
|
|
|
if (returningAttributes.contains("apple-generateduid")) {
|
|
|
|
ldapPerson.put("apple-generateduid", userName);
|
|
|
|
ldapPerson.put("uid", userName);
|
|
|
|
}
|
|
|
|
}
|
2009-03-17 03:46:38 -04:00
|
|
|
DavGatewayTray.debug("LDAP_REQ_SEARCH " + currentMessageId + " send uid=" + ldapPerson.get("uid") + baseContext + " " + ldapPerson);
|
|
|
|
sendEntry(currentMessageId, "uid=" + ldapPerson.get("uid") + baseContext, ldapPerson);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2008-12-05 18:40:53 -05:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
/**
|
|
|
|
* Send Root DSE
|
2008-12-17 10:25:35 -05:00
|
|
|
*
|
2008-12-04 10:02:53 -05:00
|
|
|
* @param currentMessageId current message id
|
|
|
|
* @throws IOException on error
|
2008-12-04 07:52:30 -05:00
|
|
|
*/
|
2008-12-04 10:02:53 -05:00
|
|
|
protected void sendRootDSE(int currentMessageId) throws IOException {
|
2009-03-12 09:56:37 -04:00
|
|
|
DavGatewayTray.debug("Sending root DSE");
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
Map<String, Object> attributes = new HashMap<String, Object>();
|
|
|
|
attributes.put("objectClass", "top");
|
2009-03-17 03:46:38 -04:00
|
|
|
attributes.put("namingContexts", NAMING_CONTEXTS);
|
2009-03-12 09:56:37 -04:00
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
sendEntry(currentMessageId, "Root DSE", attributes);
|
|
|
|
}
|
|
|
|
|
2009-03-13 07:14:29 -04:00
|
|
|
protected void addIf(Map<String, Object> attributes, Set<String> returningAttributes, String name, Object value) {
|
|
|
|
if ((returningAttributes.size() == 0) || returningAttributes.contains(name)) {
|
|
|
|
attributes.put(name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected String hostName() {
|
2009-03-17 03:43:37 -04:00
|
|
|
try {
|
2009-03-19 07:23:04 -04:00
|
|
|
return java.net.InetAddress.getLocalHost().getCanonicalHostName();
|
2009-03-17 03:43:37 -04:00
|
|
|
} catch (java.net.UnknownHostException ex) {
|
|
|
|
DavGatewayTray.debug("Couldn't get hostname");
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
|
2009-03-17 03:46:38 -04:00
|
|
|
// If we can't get the hostname, just return localhost.
|
2009-03-13 07:14:29 -04:00
|
|
|
|
|
|
|
return "localhost";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send ComputerContext
|
|
|
|
*
|
|
|
|
* @param currentMessageId current message id
|
|
|
|
* @param returningAttributes attributes to return
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
protected void sendComputerContext(int currentMessageId, Set<String> returningAttributes) throws IOException {
|
|
|
|
String customServiceInfo = SERVICEINFO.replaceAll("localhost", hostName());
|
|
|
|
customServiceInfo = customServiceInfo.replaceAll("9999", Settings.getProperty("davmail.caldavPort"));
|
|
|
|
|
|
|
|
List<String> objectClasses = new ArrayList<String>();
|
|
|
|
objectClasses.add("top");
|
|
|
|
objectClasses.add("apple-computer");
|
|
|
|
Map<String, Object> attributes = new HashMap<String, Object>();
|
|
|
|
addIf(attributes, returningAttributes, "objectClass", objectClasses);
|
|
|
|
addIf(attributes, returningAttributes, "apple-generateduid", COMPUTER_GUID);
|
|
|
|
addIf(attributes, returningAttributes, "apple-serviceinfo", customServiceInfo);
|
|
|
|
addIf(attributes, returningAttributes, "apple-serviceslocator", "::anyService");
|
|
|
|
addIf(attributes, returningAttributes, "cn", hostName());
|
|
|
|
|
2009-03-17 03:46:38 -04:00
|
|
|
String dn = "cn=" + hostName() + ", " + COMPUTER_CONTEXT;
|
2009-03-19 07:23:04 -04:00
|
|
|
DavGatewayTray.debug("Sending computer context " + dn+" "+attributes);
|
2009-03-17 03:43:37 -04:00
|
|
|
|
|
|
|
sendEntry(currentMessageId, dn, attributes);
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
/**
|
|
|
|
* Send Base Context
|
2008-12-17 10:25:35 -05:00
|
|
|
*
|
2008-12-04 10:02:53 -05:00
|
|
|
* @param currentMessageId current message id
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
|
|
|
protected void sendBaseContext(int currentMessageId) throws IOException {
|
|
|
|
List<String> objectClasses = new ArrayList<String>();
|
|
|
|
objectClasses.add("top");
|
|
|
|
objectClasses.add("organizationalUnit");
|
|
|
|
Map<String, Object> attributes = new HashMap<String, Object>();
|
|
|
|
attributes.put("objectClass", objectClasses);
|
|
|
|
attributes.put("description", "DavMail Gateway LDAP for " + Settings.getProperty("davmail.url"));
|
|
|
|
sendEntry(currentMessageId, BASE_CONTEXT, attributes);
|
|
|
|
}
|
|
|
|
|
2008-12-17 10:25:35 -05:00
|
|
|
protected void sendEntry(int currentMessageId, String dn, Map<String, Object> attributes) throws IOException {
|
2008-12-04 07:52:30 -05:00
|
|
|
responseBer.reset();
|
|
|
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
2009-02-09 17:40:55 -05:00
|
|
|
responseBer.encodeInt(currentMessageId);
|
|
|
|
responseBer.beginSeq(LDAP_REP_SEARCH);
|
|
|
|
responseBer.encodeString(dn, isLdapV3());
|
|
|
|
responseBer.beginSeq(LBER_SEQUENCE);
|
|
|
|
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
|
|
|
|
responseBer.beginSeq(LBER_SEQUENCE);
|
|
|
|
responseBer.encodeString(entry.getKey(), isLdapV3());
|
|
|
|
responseBer.beginSeq(LBER_SET);
|
|
|
|
Object values = entry.getValue();
|
|
|
|
if (values instanceof String) {
|
|
|
|
responseBer.encodeString((String) values, isLdapV3());
|
|
|
|
} else if (values instanceof List) {
|
|
|
|
for (Object value : (List) values) {
|
|
|
|
responseBer.encodeString((String) value, isLdapV3());
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
2008-12-04 07:52:30 -05:00
|
|
|
responseBer.endSeq();
|
2009-02-09 17:40:55 -05:00
|
|
|
responseBer.endSeq();
|
|
|
|
}
|
|
|
|
responseBer.endSeq();
|
|
|
|
responseBer.endSeq();
|
2008-12-04 07:52:30 -05:00
|
|
|
responseBer.endSeq();
|
|
|
|
sendResponse();
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
protected void sendErr(int currentMessageId, int responseOperation, Exception e) throws IOException {
|
2008-12-03 06:38:35 -05:00
|
|
|
String message = e.getMessage();
|
|
|
|
if (message == null) {
|
|
|
|
message = e.toString();
|
|
|
|
}
|
2008-12-04 07:52:30 -05:00
|
|
|
sendClient(currentMessageId, responseOperation, LDAP_OTHER, message);
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
protected void sendClient(int currentMessageId, int responseOperation, int status, String message) throws IOException {
|
2008-12-04 07:52:30 -05:00
|
|
|
responseBer.reset();
|
|
|
|
|
|
|
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
2008-12-17 10:25:35 -05:00
|
|
|
responseBer.encodeInt(currentMessageId);
|
|
|
|
responseBer.beginSeq(responseOperation);
|
|
|
|
responseBer.encodeInt(status, LBER_ENUMERATED);
|
|
|
|
// dn
|
|
|
|
responseBer.encodeString("", isLdapV3());
|
|
|
|
// error message
|
|
|
|
responseBer.encodeString(message, isLdapV3());
|
|
|
|
responseBer.endSeq();
|
2008-12-04 07:52:30 -05:00
|
|
|
responseBer.endSeq();
|
2008-12-17 10:25:35 -05:00
|
|
|
sendResponse();
|
2008-12-04 07:52:30 -05:00
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
protected void sendResponse() throws IOException {
|
2008-12-05 18:42:54 -05:00
|
|
|
//Ber.dumpBER(System.out, ">\n", responseBer.getBuf(), 0, responseBer.getDataLen());
|
2008-12-04 07:52:30 -05:00
|
|
|
os.write(responseBer.getBuf(), 0, responseBer.getDataLen());
|
2008-12-03 06:38:35 -05:00
|
|
|
os.flush();
|
|
|
|
}
|
|
|
|
|
2009-03-27 09:00:47 -04:00
|
|
|
static class LdapFilter {
|
2009-03-26 12:07:05 -04:00
|
|
|
final StringBuilder filterString = new StringBuilder();
|
2009-03-12 09:56:37 -04:00
|
|
|
int ldapFilterType = 0;
|
|
|
|
boolean isFullSearch = true;
|
2009-03-26 12:07:05 -04:00
|
|
|
final Map<String, SimpleFilter> orCriteria = new HashMap<String, SimpleFilter>();
|
|
|
|
final Map<String, SimpleFilter> andCriteria = new HashMap<String, SimpleFilter>();
|
2009-03-12 09:56:37 -04:00
|
|
|
|
|
|
|
public void addFilter(String attributeName, SimpleFilter simpleFilter) {
|
2009-04-10 16:07:49 -04:00
|
|
|
filterString.append('(').append(attributeName).append('=').append(simpleFilter.toString()).append(')');
|
2009-03-12 09:56:37 -04:00
|
|
|
|
|
|
|
String exchangeAttributeName = CRITERIA_MAP.get(attributeName);
|
|
|
|
if (exchangeAttributeName != null) {
|
|
|
|
isFullSearch = false;
|
|
|
|
if (ldapFilterType == 0 || ldapFilterType == LDAP_FILTER_OR) {
|
|
|
|
orCriteria.put(exchangeAttributeName, simpleFilter);
|
|
|
|
} else if (ldapFilterType == LDAP_FILTER_AND) {
|
|
|
|
andCriteria.put(exchangeAttributeName, simpleFilter);
|
|
|
|
}
|
|
|
|
} else if ("objectclass".equals(attributeName) && SimpleFilter.STAR.equals(simpleFilter.value)) {
|
|
|
|
isFullSearch = true;
|
|
|
|
} else {
|
2009-03-13 07:14:29 -04:00
|
|
|
if (IGNORE_MAP.contains(attributeName)) {
|
|
|
|
DavGatewayTray.debug("Ignoring filter attribute: " + attributeName + " = " + simpleFilter.value);
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.warn("Unsupported filter attribute: " + attributeName + " = " + simpleFilter.value);
|
|
|
|
}
|
2009-03-12 09:56:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return filterString.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void startFilter(int ldapFilterType) {
|
|
|
|
this.ldapFilterType = ldapFilterType;
|
|
|
|
filterString.append('(');
|
|
|
|
if (ldapFilterType == LDAP_FILTER_OR) {
|
|
|
|
filterString.append('|');
|
|
|
|
} else if (ldapFilterType == LDAP_FILTER_AND) {
|
|
|
|
filterString.append('&');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void endFilter() {
|
|
|
|
ldapFilterType = 0;
|
|
|
|
filterString.append(')');
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isFullSearch() {
|
|
|
|
return isFullSearch;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Set<Map.Entry<String, SimpleFilter>> getOrFilterEntrySet() {
|
|
|
|
return orCriteria.entrySet();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-27 09:00:47 -04:00
|
|
|
static class SimpleFilter {
|
2009-02-24 06:53:02 -05:00
|
|
|
static final String STAR = "*";
|
|
|
|
final String value;
|
|
|
|
final int operator;
|
2009-02-09 17:40:55 -05:00
|
|
|
|
2009-02-24 06:53:02 -05:00
|
|
|
SimpleFilter() {
|
|
|
|
this.value = SimpleFilter.STAR;
|
2009-02-09 17:40:55 -05:00
|
|
|
this.operator = LDAP_FILTER_SUBSTRINGS;
|
|
|
|
}
|
|
|
|
|
|
|
|
SimpleFilter(String value, int ldapFilterOperator) {
|
|
|
|
this.value = value;
|
|
|
|
this.operator = ldapFilterOperator;
|
|
|
|
}
|
2009-04-10 16:07:49 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
if (SimpleFilter.STAR.equals(value)) {
|
|
|
|
return SimpleFilter.STAR;
|
|
|
|
} else if (operator == LDAP_FILTER_SUBSTRINGS) {
|
|
|
|
return SimpleFilter.STAR + value + SimpleFilter.STAR;
|
|
|
|
} else {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
|
|
|
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|