2009-07-21 04:39:18 -04:00
|
|
|
/*
|
2009-08-29 19:49:45 -04:00
|
|
|
* DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
|
2009-07-21 04:39:18 -04:00
|
|
|
* Copyright (C) 2009 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.
|
|
|
|
*/
|
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;
|
2009-04-17 09:32:26 -04:00
|
|
|
import davmail.BundleMessage;
|
2009-10-06 17:13:41 -04:00
|
|
|
import davmail.Settings;
|
2009-04-27 19:03:58 -04:00
|
|
|
import davmail.exception.DavMailException;
|
2009-10-06 17:13:41 -04:00
|
|
|
import davmail.exchange.ExchangeSession;
|
2008-12-03 06:38:35 -05:00
|
|
|
import davmail.exchange.ExchangeSessionFactory;
|
2011-09-28 05:04:48 -04:00
|
|
|
import davmail.exchange.dav.DavExchangeSession;
|
2009-04-03 03:38:31 -04:00
|
|
|
import davmail.ui.tray.DavGatewayTray;
|
2010-08-24 04:16:32 -04:00
|
|
|
import org.apache.log4j.Logger;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2011-09-15 04:47:16 -04:00
|
|
|
import javax.security.auth.callback.*;
|
|
|
|
import javax.security.sasl.AuthorizeCallback;
|
|
|
|
import javax.security.sasl.Sasl;
|
|
|
|
import javax.security.sasl.SaslServer;
|
2011-02-17 12:23:30 -05:00
|
|
|
import java.io.*;
|
2008-12-17 10:25:35 -05:00
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.lang.reflect.Method;
|
2009-10-06 17:13:41 -04:00
|
|
|
import java.net.*;
|
2010-07-19 10:53:00 -04:00
|
|
|
import java.text.ParseException;
|
|
|
|
import java.text.SimpleDateFormat;
|
2008-12-17 10:25:35 -05:00
|
|
|
import java.util.*;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle a caldav connection.
|
|
|
|
*/
|
|
|
|
public class LdapConnection extends AbstractConnection {
|
2010-08-24 04:16:32 -04:00
|
|
|
private static final Logger LOGGER = Logger.getLogger(LdapConnection.class);
|
2008-12-04 07:52:30 -05:00
|
|
|
/**
|
|
|
|
* Davmail base context
|
|
|
|
*/
|
|
|
|
static final String BASE_CONTEXT = "ou=people";
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* OSX server (OpenDirectory) base context
|
|
|
|
*/
|
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";
|
2010-08-17 05:02:33 -04:00
|
|
|
static final String OD_CONFIG_CONTEXT = "cn=config, o=od";
|
2009-03-17 03:46:38 -04:00
|
|
|
static final String COMPUTER_CONTEXT = "cn=computers, o=od";
|
2010-11-22 04:38:37 -05:00
|
|
|
static final String OD_GROUP_CONTEXT = "cn=groups, o=od";
|
2009-03-17 03:46:38 -04:00
|
|
|
|
2011-09-28 05:04:48 -04:00
|
|
|
// TODO: adjust Directory Utility settings
|
|
|
|
static final String COMPUTER_CONTEXT_LION = "cn=computers,o=od";
|
|
|
|
static final String OD_USER_CONTEXT_LION = "cn=users, ou=people";
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* Root DSE naming contexts (default and OpenDirectory)
|
|
|
|
*/
|
2009-03-17 03:46:38 -04:00
|
|
|
static final List<String> NAMING_CONTEXTS = new ArrayList<String>();
|
2009-04-17 09:32:26 -04:00
|
|
|
|
2009-03-17 03:46:38 -04:00
|
|
|
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
|
|
|
/**
|
2010-08-16 05:41:37 -04:00
|
|
|
* Map Exchange contact attribute names to LDAP attributes.
|
|
|
|
* Used only when returningAttributes is empty in LDAP request (return all available attributes)
|
2008-12-04 07:52:30 -05:00
|
|
|
*/
|
2010-07-12 18:35:11 -04:00
|
|
|
static final HashMap<String, String> CONTACT_TO_LDAP_ATTRIBUTE_MAP = new HashMap<String, String>();
|
2009-09-03 19:08:35 -04:00
|
|
|
|
|
|
|
static {
|
2010-07-12 18:35:11 -04:00
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("imapUid", "uid");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("co", "countryname");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("extensionattribute1", "custom1");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("extensionattribute2", "custom2");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("extensionattribute3", "custom3");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("extensionattribute4", "custom4");
|
2010-08-09 16:15:28 -04:00
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("smtpemail1", "mail");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("smtpemail2", "xmozillasecondemail");
|
2010-07-12 18:35:11 -04:00
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("homeCountry", "mozillahomecountryname");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("homeCity", "mozillahomelocalityname");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("homePostalCode", "mozillahomepostalcode");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("homeState", "mozillahomestate");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("homeStreet", "mozillahomestreet");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("businesshomepage", "mozillaworkurl");
|
|
|
|
CONTACT_TO_LDAP_ATTRIBUTE_MAP.put("nickname", "mozillanickname");
|
2009-09-03 19:08:35 -04:00
|
|
|
}
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* OSX constant computer guid (used by iCal attendee completion)
|
|
|
|
*/
|
2009-03-13 07:14:29 -04:00
|
|
|
static final String COMPUTER_GUID = "52486C30-F0AB-48E3-9C37-37E9B28CDD7B";
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* OSX constant virtual host guid (used by iCal attendee completion)
|
|
|
|
*/
|
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
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* OSX constant value for attribute apple-serviceslocator
|
|
|
|
*/
|
|
|
|
static final HashMap<String, String> STATIC_ATTRIBUTE_MAP = new HashMap<String, String>();
|
2009-03-13 07:14:29 -04:00
|
|
|
|
|
|
|
static {
|
2009-04-23 16:53:22 -04:00
|
|
|
STATIC_ATTRIBUTE_MAP.put("apple-serviceslocator", COMPUTER_GUID + ':' + VIRTUALHOST_GUID + ":calendar");
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
/**
|
|
|
|
* LDAP to Exchange Criteria Map
|
|
|
|
*/
|
2010-08-16 05:41:37 -04:00
|
|
|
// TODO: remove
|
2008-12-04 10:02:53 -05:00
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* LDAP to Exchange contact attribute map.
|
|
|
|
*/
|
2010-07-12 18:35:11 -04:00
|
|
|
static final HashMap<String, String> LDAP_TO_CONTACT_ATTRIBUTE_MAP = new HashMap<String, String>();
|
2009-08-29 19:49:45 -04:00
|
|
|
|
|
|
|
static {
|
2010-07-12 18:35:11 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("uid", "imapUid");
|
2010-08-09 16:15:28 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mail", "smtpemail1");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
2010-07-12 18:35:11 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("displayname", "cn");
|
2010-07-19 10:07:46 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("commonname", "cn");
|
|
|
|
|
2010-07-12 18:35:11 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("givenname", "givenName");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("surname", "sn");
|
|
|
|
|
2010-07-12 18:35:11 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("company", "o");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
2010-07-12 18:35:11 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-group-realname", "department");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomelocalityname", "homeCity");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("c", "co");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("countryname", "co");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("custom1", "extensionattribute1");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("custom2", "extensionattribute2");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("custom3", "extensionattribute3");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("custom4", "extensionattribute4");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillacustom1", "extensionattribute1");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillacustom2", "extensionattribute2");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillacustom3", "extensionattribute3");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillacustom4", "extensionattribute4");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("telephonenumber", "telephoneNumber");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("orgunit", "department");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("departmentnumber", "department");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("ou", "department");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillaworkstreet2", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomestreet", "homeStreet");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("xmozillanickname", "nickname");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillanickname", "nickname");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("cellphone", "mobile");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("homeurl", "personalHomePage");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomeurl", "personalHomePage");
|
2010-09-20 17:53:53 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-user-homeurl", "personalHomePage");
|
2010-07-19 10:07:46 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomepostalcode", "homePostalCode");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("fax", "facsimiletelephonenumber");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomecountryname", "homeCountry");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("streetaddress", "street");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillaworkurl", "businesshomepage");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("workurl", "businesshomepage");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("region", "st");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("birthmonth", "bday");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("birthday", "bday");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("birthyear", "bday");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("carphone", "othermobile");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("nsaimid", "im");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("nscpaimscreenname", "im");
|
2010-09-20 17:53:53 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-imhandle", "im");
|
2010-11-03 18:43:18 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("imhandle", "im");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
2010-09-06 18:55:29 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("xmozillasecondemail", "smtpemail2");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("notes", "description");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("pagerphone", "pager");
|
2010-11-03 18:43:18 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("pager", "pager");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("locality", "l");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("homephone", "homePhone");
|
2010-09-06 18:55:29 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillasecondemail", "smtpemail2");
|
2010-07-19 10:07:46 -04:00
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("zip", "postalcode");
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomestate", "homeState");
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("modifytimestamp", "lastmodified");
|
|
|
|
|
|
|
|
// ignore attribute
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("objectclass", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillausehtmlmail", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("xmozillausehtmlmail", null);
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("mozillahomestreet2", null);
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("labeleduri", null);
|
2010-08-13 17:52:05 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-generateduid", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("uidnumber", null);
|
2010-09-20 16:52:35 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("gidnumber", null);
|
2010-09-20 17:02:16 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("jpegphoto", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-emailcontacts", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-user-picture", null);
|
2010-09-20 17:53:53 -04:00
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers_usercertificate", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers_realname", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers_jpegphoto", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_guest", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers_linkedidentity", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_defaultlanguage", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers_hint", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers__defaultlanguage", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("_writers_picture", null);
|
|
|
|
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-user-authenticationhint", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("external", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("userpassword", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("linkedidentity", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("homedirectory", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("authauthority", null);
|
2010-08-18 18:54:23 -04:00
|
|
|
|
2010-11-03 18:43:18 -04:00
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("applefloor", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("buildingname", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("destinationindicator", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("postaladdress", null);
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("homepostaladdress", null);
|
|
|
|
|
2010-08-18 18:54:23 -04:00
|
|
|
// iCal search attribute
|
|
|
|
LDAP_TO_CONTACT_ATTRIBUTE_MAP.put("apple-serviceslocator", "apple-serviceslocator");
|
2009-08-29 19:49:45 -04:00
|
|
|
}
|
|
|
|
|
2009-03-13 07:14:29 -04:00
|
|
|
/**
|
2009-08-29 19:49:45 -04:00
|
|
|
* LDAP filter attributes ignore map
|
2009-03-13 07:14:29 -04:00
|
|
|
*/
|
2010-08-16 05:41:37 -04:00
|
|
|
// TODO remove
|
2009-03-13 07:14:29 -04:00
|
|
|
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-04-15 12:16:33 -04:00
|
|
|
IGNORE_MAP.add("apple-group-nestedgroup");
|
2009-04-17 09:32:26 -04:00
|
|
|
IGNORE_MAP.add("apple-group-memberguid");
|
2009-04-15 12:16:33 -04:00
|
|
|
IGNORE_MAP.add("macaddress");
|
2009-04-17 09:32:26 -04:00
|
|
|
IGNORE_MAP.add("memberuid");
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
2008-12-04 07:52:30 -05:00
|
|
|
// LDAP version
|
2010-07-18 15:56:47 -04: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
|
|
|
|
2011-09-15 04:47:16 -04:00
|
|
|
static final int LDAP_SASL_BIND_IN_PROGRESS = 0x0E;
|
|
|
|
|
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;
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
// LDAP filter code
|
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;
|
2011-02-07 04:23:12 -05:00
|
|
|
static final int LDAP_FILTER_NOT = 0xa2;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
// LDAP filter operators
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_FILTER_SUBSTRINGS = 0xa4;
|
2010-07-18 15:56:47 -04:00
|
|
|
//static final int LDAP_FILTER_GE = 0xa5;
|
|
|
|
//static final int LDAP_FILTER_LE = 0xa6;
|
2008-12-03 06:38:35 -05:00
|
|
|
static final int LDAP_FILTER_PRESENT = 0x87;
|
2010-07-18 15:56:47 -04:00
|
|
|
//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
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
// LDAP filter mode
|
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;
|
2010-07-18 15:56:47 -04:00
|
|
|
//static final int SCOPE_ONE_LEVEL = 1;
|
|
|
|
//static final int SCOPE_SUBTREE = 2;
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
/**
|
2010-08-16 05:41:37 -04:00
|
|
|
* For some unknown reason parseIntWithTag is private !
|
2008-12-08 07:57:28 -05:00
|
|
|
*/
|
2009-09-21 17:34:13 -04:00
|
|
|
static final Method PARSE_INT_WITH_TAG_METHOD;
|
2008-12-17 10:25:35 -05:00
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
static {
|
|
|
|
try {
|
2009-09-21 17:34:13 -04:00
|
|
|
PARSE_INT_WITH_TAG_METHOD = BerDecoder.class.getDeclaredMethod("parseIntWithTag", int.class);
|
|
|
|
PARSE_INT_WITH_TAG_METHOD.setAccessible(true);
|
2008-12-08 07:57:28 -05:00
|
|
|
} catch (NoSuchMethodException e) {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.error(new BundleMessage("LOG_UNABLE_TO_GET_PARSEINTWITHTAG"));
|
2009-02-24 06:53:02 -05:00
|
|
|
throw new RuntimeException(e);
|
2008-12-08 07:57:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-15 04:47:16 -04:00
|
|
|
/**
|
|
|
|
* Sasl server for DIGEST-MD5 authentication
|
|
|
|
*/
|
|
|
|
protected SaslServer saslServer;
|
|
|
|
|
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
|
|
|
|
2009-10-29 06:20:23 -04:00
|
|
|
/**
|
|
|
|
* Search threads map
|
|
|
|
*/
|
2010-08-17 05:02:33 -04:00
|
|
|
protected final HashMap<Integer, SearchRunnable> searchThreadMap = new HashMap<Integer, SearchRunnable>();
|
2009-10-29 06:20:23 -04:00
|
|
|
|
2009-08-21 05:58:19 -04:00
|
|
|
/**
|
|
|
|
* Initialize the streams and start the thread.
|
|
|
|
*
|
|
|
|
* @param clientSocket LDAP client socket
|
|
|
|
*/
|
2008-12-26 08:21:39 -05:00
|
|
|
public LdapConnection(Socket clientSocket) {
|
2009-05-04 18:31:06 -04:00
|
|
|
super(LdapConnection.class.getSimpleName(), clientSocket);
|
2008-12-03 06:38:35 -05:00
|
|
|
try {
|
|
|
|
is = new BufferedInputStream(client.getInputStream());
|
|
|
|
os = new BufferedOutputStream(client.getOutputStream());
|
|
|
|
} catch (IOException e) {
|
|
|
|
close();
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.error(new BundleMessage("LOG_EXCEPTION_GETTING_SOCKET_STREAMS"), e);
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
protected boolean isLdapV3() {
|
|
|
|
return ldapVersion == LDAP_VERSION3;
|
|
|
|
}
|
|
|
|
|
2009-04-17 09:32:26 -04:00
|
|
|
@Override
|
|
|
|
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
|
2009-04-16 17:52:17 -04:00
|
|
|
if (eos) {
|
2008-12-03 06:38:35 -05:00
|
|
|
break; // EOF
|
2009-04-16 17:52:17 -04:00
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2010-02-08 15:50:47 -05:00
|
|
|
DavGatewayTray.switchIcon();
|
2011-06-08 15:58:29 -04:00
|
|
|
|
2011-02-17 12:23:30 -05:00
|
|
|
handleRequest(inbuf, offset);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
2008-12-05 03:48:54 -05:00
|
|
|
|
|
|
|
} catch (SocketException e) {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_CONNECTION_CLOSED"));
|
2008-12-04 10:02:53 -05:00
|
|
|
} catch (SocketTimeoutException e) {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_CLOSE_CONNECTION_ON_TIMEOUT"));
|
2009-04-15 11:20:06 -04:00
|
|
|
} catch (Exception e) {
|
2009-07-15 18:47:31 -04:00
|
|
|
DavGatewayTray.log(e);
|
2008-12-17 10:25:35 -05:00
|
|
|
try {
|
|
|
|
sendErr(0, LDAP_REP_BIND, e);
|
|
|
|
} catch (IOException e2) {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.warn(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
2008-12-17 10:25:35 -05:00
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
} finally {
|
2011-05-11 08:14:05 -04:00
|
|
|
// cancel all search threads
|
|
|
|
synchronized (searchThreadMap) {
|
2011-06-08 15:58:29 -04:00
|
|
|
for (SearchRunnable searchRunnable : searchThreadMap.values()) {
|
2011-05-11 08:14:05 -04:00
|
|
|
searchRunnable.abandon();
|
|
|
|
}
|
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
close();
|
|
|
|
}
|
|
|
|
DavGatewayTray.resetIcon();
|
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2012-03-15 09:41:16 -04:00
|
|
|
protected static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
2011-09-15 04:47:16 -04:00
|
|
|
|
2011-02-17 12:23:30 -05:00
|
|
|
protected void handleRequest(byte[] inbuf, int offset) throws IOException {
|
2011-09-26 19:45:17 -04:00
|
|
|
//dumpBer(inbuf, offset);
|
2011-02-17 12:23:30 -05:00
|
|
|
BerDecoder reqBer = new BerDecoder(inbuf, 0, offset);
|
2008-12-04 10:02:53 -05:00
|
|
|
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());
|
2011-08-03 15:44:22 -04:00
|
|
|
if (reqBer.peekByte() == (Ber.ASN_CONTEXT | Ber.ASN_CONSTRUCTOR | 3)) {
|
2011-09-15 04:47:16 -04:00
|
|
|
// SASL authentication
|
2011-08-03 15:44:22 -04:00
|
|
|
reqBer.parseSeq(null);
|
2011-09-15 04:47:16 -04:00
|
|
|
// Get mechanism, usually DIGEST-MD5
|
2011-08-03 15:44:22 -04:00
|
|
|
String mechanism = reqBer.parseString(isLdapV3());
|
2011-09-15 04:47:16 -04:00
|
|
|
|
|
|
|
byte[] serverResponse;
|
|
|
|
CallbackHandler callbackHandler = new CallbackHandler() {
|
|
|
|
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
|
|
|
|
// look for username in callbacks
|
|
|
|
for (Callback callback : callbacks) {
|
|
|
|
if (callback instanceof NameCallback) {
|
|
|
|
userName = ((NameCallback) callback).getDefaultName();
|
|
|
|
// get password from session pool
|
|
|
|
password = ExchangeSessionFactory.getUserPassword(userName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// handle other callbacks
|
2012-03-15 09:41:16 -04:00
|
|
|
for (Callback callback : callbacks) {
|
2011-09-15 04:47:16 -04:00
|
|
|
if (callback instanceof AuthorizeCallback) {
|
2012-03-15 09:41:16 -04:00
|
|
|
((AuthorizeCallback) callback).setAuthorized(true);
|
2011-09-15 04:47:16 -04:00
|
|
|
} else if (callback instanceof PasswordCallback) {
|
|
|
|
if (password != null) {
|
2012-03-15 09:41:16 -04:00
|
|
|
((PasswordCallback) callback).setPassword(password.toCharArray());
|
2011-09-15 04:47:16 -04:00
|
|
|
}
|
|
|
|
}
|
2012-03-15 09:41:16 -04:00
|
|
|
}
|
2011-09-15 04:47:16 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
int status;
|
2011-09-28 05:04:48 -04:00
|
|
|
if (reqBer.bytesLeft() > 0 && saslServer != null) {
|
2012-03-15 09:41:16 -04:00
|
|
|
byte[] clientResponse = reqBer.parseOctetString(Ber.ASN_OCTET_STR, null);
|
|
|
|
serverResponse = saslServer.evaluateResponse(clientResponse);
|
|
|
|
status = LDAP_SUCCESS;
|
2011-09-15 04:47:16 -04:00
|
|
|
|
2012-03-15 09:41:16 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_USER", currentMessageId, userName));
|
|
|
|
try {
|
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_SUCCESS"));
|
|
|
|
} catch (IOException e) {
|
|
|
|
serverResponse = EMPTY_BYTE_ARRAY;
|
|
|
|
status = LDAP_INVALID_CREDENTIALS;
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_INVALID_CREDENTIALS"));
|
|
|
|
}
|
2011-09-15 04:47:16 -04:00
|
|
|
|
|
|
|
} else {
|
|
|
|
Map<String, String> properties = new HashMap<String, String>();
|
|
|
|
properties.put("javax.security.sasl.qop", "auth,auth-int");
|
2011-09-26 19:45:17 -04:00
|
|
|
saslServer = Sasl.createSaslServer(mechanism, "ldap", client.getLocalAddress().getHostAddress(), properties, callbackHandler);
|
2011-09-15 04:47:16 -04:00
|
|
|
serverResponse = saslServer.evaluateResponse(EMPTY_BYTE_ARRAY);
|
|
|
|
status = LDAP_SASL_BIND_IN_PROGRESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
|
|
|
responseBer.encodeInt(currentMessageId);
|
|
|
|
responseBer.beginSeq(LDAP_REP_BIND);
|
|
|
|
responseBer.encodeInt(status, LBER_ENUMERATED);
|
|
|
|
// server credentials
|
|
|
|
responseBer.encodeString("", isLdapV3());
|
|
|
|
responseBer.encodeString("", isLdapV3());
|
|
|
|
// challenge or response
|
2011-11-02 17:16:37 -04:00
|
|
|
if (serverResponse != null) {
|
|
|
|
responseBer.encodeOctetString(serverResponse, 0x87);
|
|
|
|
}
|
2011-09-15 04:47:16 -04:00
|
|
|
responseBer.endSeq();
|
|
|
|
responseBer.endSeq();
|
|
|
|
sendResponse();
|
|
|
|
|
2011-08-03 15:44:22 -04:00
|
|
|
} else {
|
|
|
|
password = reqBer.parseStringWithTag(Ber.ASN_CONTEXT, isLdapV3(), null);
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2011-09-15 04:47:16 -04:00
|
|
|
if (userName.length() > 0 && password.length() > 0) {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_USER", currentMessageId, userName));
|
|
|
|
try {
|
|
|
|
session = ExchangeSessionFactory.getInstance(userName, password);
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_SUCCESS"));
|
|
|
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
|
|
|
} catch (IOException e) {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_INVALID_CREDENTIALS"));
|
|
|
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_INVALID_CREDENTIALS, "");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_BIND_ANONYMOUS", currentMessageId));
|
|
|
|
// anonymous bind
|
2008-12-04 07:52:30 -05:00
|
|
|
sendClient(currentMessageId, LDAP_REP_BIND, LDAP_SUCCESS, "");
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
|
2008-12-04 10:02:53 -05:00
|
|
|
} else if (requestOperation == LDAP_REQ_UNBIND) {
|
2009-09-15 09:18:03 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_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);
|
2010-08-17 05:02:33 -04:00
|
|
|
SearchRunnable searchRunnable = new SearchRunnable(currentMessageId, dn, scope, sizeLimit, timelimit, ldapFilter, returningAttributes);
|
2011-09-28 05:04:48 -04:00
|
|
|
if (BASE_CONTEXT.equalsIgnoreCase(dn) || OD_USER_CONTEXT.equalsIgnoreCase(dn) || OD_USER_CONTEXT_LION.equalsIgnoreCase(dn)) {
|
2010-08-17 05:02:33 -04:00
|
|
|
// launch search in a separate thread
|
|
|
|
synchronized (searchThreadMap) {
|
|
|
|
searchThreadMap.put(currentMessageId, searchRunnable);
|
|
|
|
}
|
|
|
|
Thread searchThread = new Thread(searchRunnable);
|
|
|
|
searchThread.setName(getName() + "-Search-" + currentMessageId);
|
|
|
|
searchThread.start();
|
|
|
|
} else {
|
|
|
|
// no need to create a separate thread, just run
|
|
|
|
searchRunnable.run();
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
2008-12-08 07:57:28 -05:00
|
|
|
} else if (requestOperation == LDAP_REQ_ABANDON) {
|
2009-10-29 06:20:23 -04:00
|
|
|
int abandonMessageId = 0;
|
2008-12-08 07:57:28 -05:00
|
|
|
try {
|
2009-10-29 06:20:23 -04:00
|
|
|
abandonMessageId = (Integer) PARSE_INT_WITH_TAG_METHOD.invoke(reqBer, LDAP_REQ_ABANDON);
|
|
|
|
synchronized (searchThreadMap) {
|
2010-08-17 05:02:33 -04:00
|
|
|
SearchRunnable searchRunnable = searchThreadMap.get(abandonMessageId);
|
|
|
|
if (searchRunnable != null) {
|
|
|
|
searchRunnable.abandon();
|
2009-10-29 06:20:23 -04:00
|
|
|
searchThreadMap.remove(currentMessageId);
|
|
|
|
}
|
|
|
|
}
|
2008-12-08 07:57:28 -05:00
|
|
|
} 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);
|
|
|
|
}
|
2009-10-29 06:20:23 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_ABANDON_SEARCH", currentMessageId, abandonMessageId));
|
2008-12-04 10:02:53 -05:00
|
|
|
} else {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_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) {
|
2011-02-17 12:23:30 -05:00
|
|
|
dumpBer(inbuf, offset);
|
2008-12-03 06:38:35 -05:00
|
|
|
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) {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-17 12:23:30 -05:00
|
|
|
protected void dumpBer(byte[] inbuf, int offset) {
|
|
|
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
|
Ber.dumpBER(baos, "LDAP request buffer\n", inbuf, 0, offset);
|
|
|
|
try {
|
|
|
|
LOGGER.debug(new String(baos.toByteArray(), "UTF-8"));
|
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
|
// should not happen
|
|
|
|
LOGGER.error(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-12 09:56:37 -04:00
|
|
|
protected LdapFilter parseFilter(BerDecoder reqBer) throws IOException {
|
2009-10-06 17:13:41 -04:00
|
|
|
LdapFilter 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();
|
2009-10-06 17:13:41 -04:00
|
|
|
ldapFilter = new SimpleFilter(attributeName);
|
2008-12-04 10:02:53 -05:00
|
|
|
} 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
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
ldapFilter = parseNestedFilter(reqBer, ldapFilterType, end);
|
2009-03-12 09:56:37 -04:00
|
|
|
}
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2009-03-12 09:56:37 -04:00
|
|
|
return ldapFilter;
|
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
protected LdapFilter parseNestedFilter(BerDecoder reqBer, int ldapFilterType, int end) throws IOException {
|
|
|
|
LdapFilter nestedFilter;
|
|
|
|
|
2011-02-07 04:23:12 -05:00
|
|
|
if ((ldapFilterType == LDAP_FILTER_OR) || (ldapFilterType == LDAP_FILTER_AND)
|
|
|
|
|| ldapFilterType == LDAP_FILTER_NOT) {
|
2009-10-06 17:13:41 -04:00
|
|
|
nestedFilter = new CompoundFilter(ldapFilterType);
|
|
|
|
|
2009-03-12 09:56:37 -04:00
|
|
|
while (reqBer.getParsePosition() < end && reqBer.bytesLeft() > 0) {
|
2009-12-22 08:18:28 -05:00
|
|
|
if (reqBer.peekByte() == LDAP_FILTER_PRESENT) {
|
|
|
|
String attributeName = reqBer.parseStringWithTag(LDAP_FILTER_PRESENT, isLdapV3(), null).toLowerCase();
|
|
|
|
nestedFilter.add(new SimpleFilter(attributeName));
|
|
|
|
} else {
|
2010-06-17 18:37:33 -04:00
|
|
|
int[] seqSize = new int[1];
|
|
|
|
int ldapFilterOperator = reqBer.parseSeq(seqSize);
|
|
|
|
int subEnd = reqBer.getParsePosition() + seqSize[0];
|
|
|
|
nestedFilter.add(parseNestedFilter(reqBer, ldapFilterOperator, subEnd));
|
2009-12-22 08:18:28 -05:00
|
|
|
}
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
2009-03-12 09:56:37 -04:00
|
|
|
} else {
|
|
|
|
// simple filter
|
2009-10-06 17:13:41 -04:00
|
|
|
nestedFilter = parseSimpleFilter(reqBer, ldapFilterType);
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
return nestedFilter;
|
2008-12-04 10:02:53 -05:00
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
protected LdapFilter parseSimpleFilter(BerDecoder reqBer, int ldapFilterOperator) throws IOException {
|
2008-12-04 10:02:53 -05:00
|
|
|
String attributeName = reqBer.parseString(isLdapV3()).toLowerCase();
|
2009-10-12 17:33:21 -04:00
|
|
|
int ldapFilterMode = 0;
|
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) {
|
2009-10-12 17:33:21 -04:00
|
|
|
ldapFilterMode = reqBer.peekByte();
|
2009-02-09 17:40:55 -05:00
|
|
|
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 {
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.warn(new BundleMessage("LOG_LDAP_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();
|
|
|
|
|
2009-04-16 17:52:17 -04:00
|
|
|
if ("uid".equalsIgnoreCase(attributeName) && sValue.equals(userName)) {
|
2011-09-28 05:04:48 -04:00
|
|
|
// replace with actual alias instead of login name search, only in Dav mode
|
|
|
|
if (sValue.equals(userName) && session instanceof DavExchangeSession) {
|
2009-03-13 07:14:29 -04:00
|
|
|
sValue = session.getAlias();
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REPLACED_UID_FILTER", userName, sValue));
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-12 17:33:21 -04:00
|
|
|
return new SimpleFilter(attributeName, sValue, ldapFilterOperator, ldapFilterMode);
|
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 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-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_SEND_ROOT_DSE"));
|
2009-03-12 09:56:37 -04:00
|
|
|
|
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);
|
2011-09-26 19:45:17 -04:00
|
|
|
//attributes.put("supportedsaslmechanisms", "PLAIN");
|
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) {
|
2009-04-16 17:16:40 -04:00
|
|
|
if ((returningAttributes.isEmpty()) || returningAttributes.contains(name)) {
|
2009-03-13 07:14:29 -04:00
|
|
|
attributes.put(name, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-20 17:30:15 -04:00
|
|
|
protected String currentHostName;
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
protected String getCurrentHostName() throws UnknownHostException {
|
2010-09-20 17:30:15 -04:00
|
|
|
if (currentHostName == null) {
|
|
|
|
if (client.getInetAddress().isLoopbackAddress()) {
|
|
|
|
// local address, probably using localhost in iCal URL
|
|
|
|
currentHostName = "localhost";
|
|
|
|
} else {
|
|
|
|
// remote address, send fully qualified domain name
|
|
|
|
currentHostName = InetAddress.getLocalHost().getCanonicalHostName();
|
|
|
|
}
|
2009-10-07 18:08:05 -04:00
|
|
|
}
|
2010-09-20 17:30:15 -04:00
|
|
|
return currentHostName;
|
2009-03-13 07:14:29 -04:00
|
|
|
}
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
/**
|
|
|
|
* Cache serviceInfo string value
|
|
|
|
*/
|
|
|
|
protected String serviceInfo;
|
|
|
|
|
|
|
|
protected String getServiceInfo() throws UnknownHostException {
|
|
|
|
if (serviceInfo == null) {
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
buffer.append("<?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>" +
|
|
|
|
"<key>" + VIRTUALHOST_GUID + "</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>hostDetails</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>http</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>enabled</key>" +
|
|
|
|
"<true/>" +
|
|
|
|
"<key>port</key>" +
|
|
|
|
"<integer>");
|
|
|
|
buffer.append(Settings.getProperty("davmail.caldavPort"));
|
|
|
|
buffer.append("</integer>" +
|
|
|
|
"</dict>" +
|
|
|
|
"<key>https</key>" +
|
|
|
|
"<dict>" +
|
|
|
|
"<key>disabled</key>" +
|
|
|
|
"<false/>" +
|
|
|
|
"<key>port</key>" +
|
|
|
|
"<integer>0</integer>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"<key>hostname</key>" +
|
|
|
|
"<string>");
|
|
|
|
buffer.append(getCurrentHostName());
|
|
|
|
buffer.append("</string>" +
|
|
|
|
"<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/__uuids__/%(guid)s/</string>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"<key>serviceType</key>" +
|
|
|
|
"<array>" +
|
|
|
|
"<string>calendar</string>" +
|
|
|
|
"</array>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</dict>" +
|
|
|
|
"</plist>");
|
|
|
|
serviceInfo = buffer.toString();
|
|
|
|
}
|
|
|
|
return serviceInfo;
|
|
|
|
}
|
|
|
|
|
2009-03-13 07:14:29 -04:00
|
|
|
/**
|
|
|
|
* 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 {
|
|
|
|
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);
|
2010-08-16 05:41:37 -04:00
|
|
|
addIf(attributes, returningAttributes, "apple-serviceinfo", getServiceInfo());
|
2011-09-28 05:04:48 -04:00
|
|
|
// TODO: remove ?
|
|
|
|
addIf(attributes, returningAttributes, "apple-xmlplist", getServiceInfo());
|
2009-03-13 07:14:29 -04:00
|
|
|
addIf(attributes, returningAttributes, "apple-serviceslocator", "::anyService");
|
2010-08-16 05:41:37 -04:00
|
|
|
addIf(attributes, returningAttributes, "cn", getCurrentHostName());
|
2009-03-13 07:14:29 -04:00
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
String dn = "cn=" + getCurrentHostName() + ", " + COMPUTER_CONTEXT;
|
2009-04-17 09:32:26 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_SEND_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 {
|
2009-10-29 06:20:23 -04:00
|
|
|
// synchronize on responseBer
|
|
|
|
synchronized (responseBer) {
|
|
|
|
responseBer.reset();
|
|
|
|
responseBer.beginSeq(Ber.ASN_SEQUENCE | Ber.ASN_CONSTRUCTOR);
|
|
|
|
responseBer.encodeInt(currentMessageId);
|
|
|
|
responseBer.beginSeq(LDAP_REP_SEARCH);
|
|
|
|
responseBer.encodeString(dn, isLdapV3());
|
2009-02-09 17:40:55 -05:00
|
|
|
responseBer.beginSeq(LBER_SEQUENCE);
|
2009-10-29 06:20:23 -04:00
|
|
|
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 DavMailException("EXCEPTION_UNSUPPORTED_VALUE", values);
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
2009-10-29 06:20:23 -04:00
|
|
|
responseBer.endSeq();
|
|
|
|
responseBer.endSeq();
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
2008-12-04 07:52:30 -05:00
|
|
|
responseBer.endSeq();
|
2009-02-09 17:40:55 -05:00
|
|
|
responseBer.endSeq();
|
2009-10-29 06:20:23 -04:00
|
|
|
responseBer.endSeq();
|
|
|
|
sendResponse();
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
2008-12-04 07:52:30 -05:00
|
|
|
}
|
|
|
|
|
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-10-06 17:13:41 -04:00
|
|
|
static interface LdapFilter {
|
2010-06-17 18:37:33 -04:00
|
|
|
ExchangeSession.Condition getContactSearchFilter();
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2010-08-09 18:19:15 -04:00
|
|
|
Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session, Set<String> returningAttributes, int sizeLimit) throws IOException;
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2009-10-12 16:27:50 -04:00
|
|
|
void add(LdapFilter filter);
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2009-10-12 16:27:50 -04:00
|
|
|
boolean isFullSearch();
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2009-10-12 16:27:50 -04:00
|
|
|
boolean isMatch(Map<String, String> person);
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
|
|
|
|
2010-06-17 18:37:33 -04:00
|
|
|
class CompoundFilter implements LdapFilter {
|
2009-10-06 17:13:41 -04:00
|
|
|
final Set<LdapFilter> criteria = new HashSet<LdapFilter>();
|
2009-10-19 05:00:22 -04:00
|
|
|
final int type;
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
CompoundFilter(int filterType) {
|
|
|
|
type = filterType;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
|
|
|
|
if (type == LDAP_FILTER_OR) {
|
|
|
|
buffer.append("(|");
|
2011-02-07 04:23:12 -05:00
|
|
|
} else if (type == LDAP_FILTER_AND) {
|
2009-10-06 17:13:41 -04:00
|
|
|
buffer.append("(&");
|
2011-02-07 04:23:12 -05:00
|
|
|
} else {
|
|
|
|
buffer.append("(!");
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (LdapFilter child : criteria) {
|
|
|
|
buffer.append(child.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer.append(')');
|
|
|
|
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add child filter
|
|
|
|
*
|
|
|
|
* @param filter inner filter
|
|
|
|
*/
|
|
|
|
public void add(LdapFilter filter) {
|
|
|
|
criteria.add(filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is only a full search if every child
|
|
|
|
* is also a full search
|
|
|
|
*
|
|
|
|
* @return true if full search filter
|
|
|
|
*/
|
|
|
|
public boolean isFullSearch() {
|
|
|
|
for (LdapFilter child : criteria) {
|
|
|
|
if (!child.isFullSearch()) {
|
|
|
|
return false;
|
2009-09-04 18:30:03 -04:00
|
|
|
}
|
2009-08-29 19:49:45 -04:00
|
|
|
}
|
2009-09-04 18:30:03 -04:00
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
return true;
|
2009-08-29 19:49:45 -04:00
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
/**
|
|
|
|
* Build search filter for Contacts folder search.
|
|
|
|
* Use Exchange SEARCH syntax
|
|
|
|
*
|
|
|
|
* @return contact search filter
|
|
|
|
*/
|
2010-06-17 18:37:33 -04:00
|
|
|
public ExchangeSession.Condition getContactSearchFilter() {
|
|
|
|
ExchangeSession.MultiCondition condition;
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
if (type == LDAP_FILTER_OR) {
|
2010-06-17 18:37:33 -04:00
|
|
|
condition = session.or();
|
2009-08-29 19:49:45 -04:00
|
|
|
} else {
|
2010-06-17 18:37:33 -04:00
|
|
|
condition = session.and();
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (LdapFilter child : criteria) {
|
2010-06-17 18:37:33 -04:00
|
|
|
condition.add(child.getContactSearchFilter());
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
|
|
|
|
2010-06-17 18:37:33 -04:00
|
|
|
return condition;
|
2009-03-12 09:56:37 -04:00
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
/**
|
|
|
|
* Test if person matches the current filter.
|
|
|
|
*
|
|
|
|
* @param person person attributes map
|
|
|
|
* @return true if filter match
|
|
|
|
*/
|
|
|
|
public boolean isMatch(Map<String, String> person) {
|
|
|
|
if (type == LDAP_FILTER_OR) {
|
|
|
|
for (LdapFilter child : criteria) {
|
|
|
|
if (!child.isFullSearch()) {
|
|
|
|
if (child.isMatch(person)) {
|
|
|
|
// We've found a match
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// No subconditions are met
|
|
|
|
return false;
|
|
|
|
} else if (type == LDAP_FILTER_AND) {
|
|
|
|
for (LdapFilter child : criteria) {
|
|
|
|
if (!child.isFullSearch()) {
|
|
|
|
if (!child.isMatch(person)) {
|
|
|
|
// We've found a miss
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// All subconditions are met
|
|
|
|
return true;
|
2009-03-12 09:56:37 -04:00
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
return false;
|
2009-03-12 09:56:37 -04:00
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
/**
|
|
|
|
* Find persons in Exchange GAL matching filter.
|
|
|
|
* Iterate over child filters to build results.
|
|
|
|
*
|
|
|
|
* @param session Exchange session
|
|
|
|
* @return persons map
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-08-09 18:19:15 -04:00
|
|
|
public Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session, Set<String> returningAttributes, int sizeLimit) throws IOException {
|
2010-08-09 16:15:28 -04:00
|
|
|
Map<String, ExchangeSession.Contact> persons = null;
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
for (LdapFilter child : criteria) {
|
2010-08-13 17:52:05 -04:00
|
|
|
int currentSizeLimit = sizeLimit;
|
|
|
|
if (persons != null) {
|
|
|
|
currentSizeLimit -= persons.size();
|
|
|
|
}
|
|
|
|
Map<String, ExchangeSession.Contact> childFind = child.findInGAL(session, returningAttributes, currentSizeLimit);
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
if (childFind != null) {
|
|
|
|
if (persons == null) {
|
|
|
|
persons = childFind;
|
|
|
|
} else if (type == LDAP_FILTER_OR) {
|
|
|
|
// Create the union of the existing results and the child found results
|
|
|
|
persons.putAll(childFind);
|
|
|
|
} else if (type == LDAP_FILTER_AND) {
|
|
|
|
// Append current child filter results that match all child filters to persons.
|
|
|
|
// The hard part is that, due to the 100-item-returned galFind limit
|
|
|
|
// we may catch new items that match all child filters in each child search.
|
|
|
|
// Thus, instead of building the intersection, we check each result against
|
|
|
|
// all filters.
|
|
|
|
|
2010-08-09 16:15:28 -04:00
|
|
|
for (ExchangeSession.Contact result : childFind.values()) {
|
2009-10-06 17:13:41 -04:00
|
|
|
if (isMatch(result)) {
|
|
|
|
// This item from the child result set matches all sub-criteria, add it
|
2010-08-09 16:15:28 -04:00
|
|
|
persons.put(result.get("uid"), result);
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((persons == null) && !isFullSearch()) {
|
|
|
|
// return an empty map (indicating no results were found)
|
2010-08-09 16:15:28 -04:00
|
|
|
return new HashMap<String, ExchangeSession.Contact>();
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
2009-03-12 09:56:37 -04:00
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
return persons;
|
2009-03-12 09:56:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-17 18:37:33 -04:00
|
|
|
class SimpleFilter implements LdapFilter {
|
2009-02-24 06:53:02 -05:00
|
|
|
static final String STAR = "*";
|
2009-08-29 19:49:45 -04:00
|
|
|
final String attributeName;
|
2009-02-24 06:53:02 -05:00
|
|
|
final String value;
|
2009-10-12 17:33:21 -04:00
|
|
|
final int mode;
|
2009-02-24 06:53:02 -05:00
|
|
|
final int operator;
|
2009-10-19 05:00:22 -04:00
|
|
|
final boolean canIgnore;
|
2009-02-09 17:40:55 -05:00
|
|
|
|
2009-08-29 19:49:45 -04:00
|
|
|
SimpleFilter(String attributeName) {
|
|
|
|
this.attributeName = attributeName;
|
2009-02-24 06:53:02 -05:00
|
|
|
this.value = SimpleFilter.STAR;
|
2009-02-09 17:40:55 -05:00
|
|
|
this.operator = LDAP_FILTER_SUBSTRINGS;
|
2009-10-12 17:33:21 -04:00
|
|
|
this.mode = 0;
|
2009-10-06 17:13:41 -04:00
|
|
|
this.canIgnore = checkIgnore();
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
|
|
|
|
2009-10-12 17:33:21 -04:00
|
|
|
SimpleFilter(String attributeName, String value, int ldapFilterOperator, int ldapFilterMode) {
|
2009-08-29 19:49:45 -04:00
|
|
|
this.attributeName = attributeName;
|
2009-02-09 17:40:55 -05:00
|
|
|
this.value = value;
|
|
|
|
this.operator = ldapFilterOperator;
|
2009-10-12 17:33:21 -04:00
|
|
|
this.mode = ldapFilterMode;
|
2009-10-06 17:13:41 -04:00
|
|
|
this.canIgnore = checkIgnore();
|
|
|
|
}
|
|
|
|
|
|
|
|
private boolean checkIgnore() {
|
|
|
|
if ("objectclass".equals(attributeName) && STAR.equals(value)) {
|
|
|
|
// ignore cases where any object class can match
|
|
|
|
return true;
|
|
|
|
} else if (IGNORE_MAP.contains(attributeName)) {
|
|
|
|
// Ignore this specific attribute
|
|
|
|
return true;
|
2010-07-19 10:07:46 -04:00
|
|
|
} else if (CRITERIA_MAP.get(attributeName) == null && getContactAttributeName(attributeName) == null) {
|
2009-10-06 17:13:41 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_UNSUPPORTED_FILTER_ATTRIBUTE",
|
|
|
|
attributeName, value));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isFullSearch() {
|
2009-10-12 17:33:21 -04:00
|
|
|
// only (objectclass=*) is a full search
|
|
|
|
return "objectclass".equals(attributeName) && STAR.equals(value);
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
2009-04-10 16:07:49 -04:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
2009-08-29 19:49:45 -04:00
|
|
|
StringBuilder buffer = new StringBuilder();
|
2009-10-06 17:13:41 -04:00
|
|
|
buffer.append('(');
|
2009-08-29 19:49:45 -04:00
|
|
|
buffer.append(attributeName);
|
|
|
|
buffer.append('=');
|
2009-04-10 16:07:49 -04:00
|
|
|
if (SimpleFilter.STAR.equals(value)) {
|
2009-08-29 19:49:45 -04:00
|
|
|
buffer.append(SimpleFilter.STAR);
|
2009-04-10 16:07:49 -04:00
|
|
|
} else if (operator == LDAP_FILTER_SUBSTRINGS) {
|
2009-10-12 17:33:21 -04:00
|
|
|
if (mode == LDAP_SUBSTRING_FINAL || mode == LDAP_SUBSTRING_ANY) {
|
|
|
|
buffer.append(SimpleFilter.STAR);
|
|
|
|
}
|
|
|
|
buffer.append(value);
|
|
|
|
if (mode == LDAP_SUBSTRING_INITIAL || mode == LDAP_SUBSTRING_ANY) {
|
|
|
|
buffer.append(SimpleFilter.STAR);
|
|
|
|
}
|
2009-04-10 16:07:49 -04:00
|
|
|
} else {
|
2009-08-29 19:49:45 -04:00
|
|
|
buffer.append(value);
|
2009-04-10 16:07:49 -04:00
|
|
|
}
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
buffer.append(')');
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
2010-06-17 18:37:33 -04:00
|
|
|
public ExchangeSession.Condition getContactSearchFilter() {
|
2010-07-19 10:07:46 -04:00
|
|
|
String contactAttributeName = getContactAttributeName(attributeName);
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
if (canIgnore || (contactAttributeName == null)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2010-07-26 19:45:18 -04:00
|
|
|
ExchangeSession.Condition condition = null;
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
if (operator == LDAP_FILTER_EQUALITY) {
|
2010-08-09 18:19:15 -04:00
|
|
|
condition = session.isEqualTo(contactAttributeName, value);
|
2010-07-06 05:49:26 -04:00
|
|
|
} else if ("*".equals(value)) {
|
2010-06-17 18:37:33 -04:00
|
|
|
condition = session.not(session.isNull(contactAttributeName));
|
2010-07-27 17:11:23 -04:00
|
|
|
// do not allow substring search on integer field imapUid
|
|
|
|
} else if (!"imapUid".equals(contactAttributeName)) {
|
2010-06-17 18:37:33 -04:00
|
|
|
// endsWith not supported by exchange, convert to contains
|
2009-10-12 17:33:21 -04:00
|
|
|
if (mode == LDAP_SUBSTRING_FINAL || mode == LDAP_SUBSTRING_ANY) {
|
2010-07-06 05:49:26 -04:00
|
|
|
condition = session.contains(contactAttributeName, value);
|
2010-06-17 18:37:33 -04:00
|
|
|
} else {
|
2010-07-06 05:49:26 -04:00
|
|
|
condition = session.startsWith(contactAttributeName, value);
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
2009-10-12 17:33:21 -04:00
|
|
|
}
|
2010-06-17 18:37:33 -04:00
|
|
|
return condition;
|
2009-08-29 19:49:45 -04:00
|
|
|
}
|
|
|
|
|
2009-10-06 17:13:41 -04:00
|
|
|
public boolean isMatch(Map<String, String> person) {
|
|
|
|
if (canIgnore) {
|
|
|
|
// Ignore this filter
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-08-09 18:19:15 -04:00
|
|
|
String personAttributeValue = person.get(attributeName);
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
if (personAttributeValue == null) {
|
|
|
|
// No value to allow for filter match
|
|
|
|
return false;
|
|
|
|
} else if (value == null) {
|
|
|
|
// This is a presence filter: found
|
|
|
|
return true;
|
|
|
|
} else if ((operator == LDAP_FILTER_EQUALITY) && personAttributeValue.equalsIgnoreCase(value)) {
|
|
|
|
// Found an exact match
|
|
|
|
return true;
|
|
|
|
} else if ((operator == LDAP_FILTER_SUBSTRINGS) && (personAttributeValue.toLowerCase().indexOf(value.toLowerCase()) >= 0)) {
|
|
|
|
// Found a substring match
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-09 18:19:15 -04:00
|
|
|
public Map<String, ExchangeSession.Contact> findInGAL(ExchangeSession session, Set<String> returningAttributes, int sizeLimit) throws IOException {
|
2009-10-06 17:13:41 -04:00
|
|
|
if (canIgnore) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
String contactAttributeName = getContactAttributeName(attributeName);
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
if (contactAttributeName != null) {
|
2009-11-04 19:11:38 -05:00
|
|
|
// quick fix for cn=* filter
|
2010-08-16 06:07:56 -04:00
|
|
|
Map<String, ExchangeSession.Contact> galPersons = session.galFind(session.startsWith(contactAttributeName, "*".equals(value) ? "A" : value),
|
|
|
|
convertLdapToContactReturningAttributes(returningAttributes), sizeLimit);
|
2009-10-06 17:13:41 -04:00
|
|
|
|
|
|
|
if (operator == LDAP_FILTER_EQUALITY) {
|
|
|
|
// Make sure only exact matches are returned
|
|
|
|
|
2010-08-09 16:15:28 -04:00
|
|
|
Map<String, ExchangeSession.Contact> results = new HashMap<String, ExchangeSession.Contact>();
|
2009-10-06 17:13:41 -04:00
|
|
|
|
2010-08-09 16:15:28 -04:00
|
|
|
for (ExchangeSession.Contact person : galPersons.values()) {
|
2009-10-06 17:13:41 -04:00
|
|
|
if (isMatch(person)) {
|
|
|
|
// Found an exact match
|
2010-08-09 16:15:28 -04:00
|
|
|
results.put(person.get("uid"), person);
|
2009-10-06 17:13:41 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
} else {
|
|
|
|
return galPersons;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void add(LdapFilter filter) {
|
|
|
|
// Should never be called
|
|
|
|
DavGatewayTray.error(new BundleMessage("LOG_LDAP_UNSUPPORTED_FILTER", "nested simple filters"));
|
|
|
|
}
|
2010-07-19 10:07:46 -04:00
|
|
|
}
|
|
|
|
|
2010-07-19 10:53:00 -04:00
|
|
|
/**
|
|
|
|
* Convert contact attribute name to LDAP attribute name.
|
|
|
|
*
|
|
|
|
* @param ldapAttributeName ldap attribute name
|
|
|
|
* @return contact attribute name
|
|
|
|
*/
|
|
|
|
protected static String getContactAttributeName(String ldapAttributeName) {
|
2010-07-19 10:07:46 -04:00
|
|
|
String contactAttributeName = null;
|
|
|
|
// first look in contact attributes
|
2010-07-19 10:53:00 -04:00
|
|
|
if (ExchangeSession.CONTACT_ATTRIBUTES.contains(ldapAttributeName)) {
|
|
|
|
contactAttributeName = ldapAttributeName;
|
|
|
|
} else if (LDAP_TO_CONTACT_ATTRIBUTE_MAP.containsKey(ldapAttributeName)) {
|
|
|
|
String mappedAttribute = LDAP_TO_CONTACT_ATTRIBUTE_MAP.get(ldapAttributeName);
|
2010-07-19 10:07:46 -04:00
|
|
|
if (mappedAttribute != null) {
|
|
|
|
contactAttributeName = mappedAttribute;
|
|
|
|
}
|
|
|
|
} else {
|
2010-07-19 10:53:00 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("UNKNOWN_ATTRIBUTE", ldapAttributeName));
|
2009-04-10 16:07:49 -04:00
|
|
|
}
|
2010-07-19 10:07:46 -04:00
|
|
|
return contactAttributeName;
|
2009-02-09 17:40:55 -05:00
|
|
|
}
|
|
|
|
|
2010-07-19 10:53:00 -04:00
|
|
|
/**
|
|
|
|
* Convert LDAP attribute name to contact attribute name.
|
|
|
|
*
|
|
|
|
* @param contactAttributeName ldap attribute name
|
|
|
|
* @return contact attribute name
|
|
|
|
*/
|
|
|
|
protected static String getLdapAttributeName(String contactAttributeName) {
|
|
|
|
String mappedAttributeName = CONTACT_TO_LDAP_ATTRIBUTE_MAP.get(contactAttributeName);
|
|
|
|
if (mappedAttributeName != null) {
|
|
|
|
return mappedAttributeName;
|
2010-08-09 16:15:28 -04:00
|
|
|
} else {
|
|
|
|
return contactAttributeName;
|
2010-07-19 10:53:00 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-16 06:07:56 -04:00
|
|
|
protected Set<String> convertLdapToContactReturningAttributes(Set<String> returningAttributes) {
|
|
|
|
Set<String> contactReturningAttributes;
|
|
|
|
if (returningAttributes != null && !returningAttributes.isEmpty()) {
|
|
|
|
contactReturningAttributes = new HashSet<String>();
|
|
|
|
// always return uid
|
|
|
|
contactReturningAttributes.add("imapUid");
|
|
|
|
for (String attribute : returningAttributes) {
|
|
|
|
String contactAttributeName = getContactAttributeName(attribute);
|
|
|
|
if (contactAttributeName != null) {
|
|
|
|
contactReturningAttributes.add(contactAttributeName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
contactReturningAttributes = ExchangeSession.CONTACT_ATTRIBUTES;
|
|
|
|
}
|
|
|
|
return contactReturningAttributes;
|
|
|
|
}
|
|
|
|
|
2010-08-17 05:02:33 -04:00
|
|
|
protected class SearchRunnable implements Runnable {
|
2009-10-29 06:20:23 -04:00
|
|
|
private final int currentMessageId;
|
|
|
|
private final String dn;
|
|
|
|
private final int scope;
|
|
|
|
private final int sizeLimit;
|
|
|
|
private final int timelimit;
|
|
|
|
private final LdapFilter ldapFilter;
|
|
|
|
private final Set<String> returningAttributes;
|
|
|
|
private boolean abandon;
|
|
|
|
|
2010-08-17 05:02:33 -04:00
|
|
|
protected SearchRunnable(int currentMessageId, String dn, int scope, int sizeLimit, int timelimit, LdapFilter ldapFilter, Set<String> returningAttributes) {
|
2009-10-29 06:20:23 -04:00
|
|
|
this.currentMessageId = currentMessageId;
|
|
|
|
this.dn = dn;
|
|
|
|
this.scope = scope;
|
|
|
|
this.sizeLimit = sizeLimit;
|
|
|
|
this.timelimit = timelimit;
|
|
|
|
this.ldapFilter = ldapFilter;
|
|
|
|
this.returningAttributes = returningAttributes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abandon search.
|
|
|
|
*/
|
|
|
|
protected void abandon() {
|
|
|
|
abandon = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
int size = 0;
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH", currentMessageId, dn, scope, sizeLimit, timelimit, ldapFilter.toString(), returningAttributes));
|
|
|
|
|
|
|
|
if (scope == SCOPE_BASE_OBJECT) {
|
|
|
|
if ("".equals(dn)) {
|
|
|
|
size = 1;
|
|
|
|
sendRootDSE(currentMessageId);
|
|
|
|
} else if (BASE_CONTEXT.equals(dn)) {
|
|
|
|
size = 1;
|
|
|
|
// root
|
|
|
|
sendBaseContext(currentMessageId);
|
|
|
|
} else if (dn.startsWith("uid=") && dn.indexOf(',') > 0) {
|
|
|
|
if (session != null) {
|
|
|
|
// single user request
|
|
|
|
String uid = dn.substring("uid=".length(), dn.indexOf(','));
|
2010-08-09 16:15:28 -04:00
|
|
|
Map<String, ExchangeSession.Contact> persons = null;
|
2010-07-01 08:47:57 -04:00
|
|
|
|
2009-10-29 06:20:23 -04:00
|
|
|
// first search in contact
|
2010-07-01 08:47:57 -04:00
|
|
|
try {
|
2010-07-06 05:49:26 -04:00
|
|
|
// check if this is a contact uid
|
|
|
|
Integer.parseInt(uid);
|
2010-07-27 16:46:49 -04:00
|
|
|
persons = contactFind(session.isEqualTo("imapUid", uid), returningAttributes, sizeLimit);
|
2010-07-06 05:49:26 -04:00
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
// ignore, this is not a contact uid
|
2010-07-01 08:47:57 -04:00
|
|
|
}
|
2009-10-29 06:20:23 -04:00
|
|
|
|
|
|
|
// then in GAL
|
2010-07-01 08:47:57 -04:00
|
|
|
if (persons == null || persons.isEmpty()) {
|
2010-08-17 05:02:33 -04:00
|
|
|
persons = session.galFind(session.isEqualTo("imapUid", uid),
|
2010-08-16 06:07:56 -04:00
|
|
|
convertLdapToContactReturningAttributes(returningAttributes), sizeLimit);
|
2009-10-29 06:20:23 -04:00
|
|
|
|
2010-08-09 16:15:28 -04:00
|
|
|
ExchangeSession.Contact person = persons.get(uid.toLowerCase());
|
2009-10-29 06:20:23 -04:00
|
|
|
// filter out non exact results
|
|
|
|
if (persons.size() > 1 || person == null) {
|
2010-08-09 16:15:28 -04:00
|
|
|
persons = new HashMap<String, ExchangeSession.Contact>();
|
2009-10-29 06:20:23 -04:00
|
|
|
if (person != null) {
|
|
|
|
persons.put(uid.toLowerCase(), person);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
size = persons.size();
|
|
|
|
sendPersons(currentMessageId, dn.substring(dn.indexOf(',')), persons, returningAttributes);
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_ANONYMOUS_ACCESS_FORBIDDEN", currentMessageId, dn));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_INVALID_DN", currentMessageId, dn));
|
|
|
|
}
|
2011-09-28 05:04:48 -04:00
|
|
|
} else if (COMPUTER_CONTEXT.equals(dn) || COMPUTER_CONTEXT_LION.equals(dn)) {
|
2009-10-29 06:20:23 -04:00
|
|
|
size = 1;
|
|
|
|
// computer context for iCal
|
|
|
|
sendComputerContext(currentMessageId, returningAttributes);
|
2011-09-28 05:04:48 -04:00
|
|
|
} else if ((BASE_CONTEXT.equalsIgnoreCase(dn) || OD_USER_CONTEXT.equalsIgnoreCase(dn)) || OD_USER_CONTEXT_LION.equalsIgnoreCase(dn)) {
|
2009-10-29 06:20:23 -04:00
|
|
|
if (session != null) {
|
2010-08-09 16:15:28 -04:00
|
|
|
Map<String, ExchangeSession.Contact> persons = new HashMap<String, ExchangeSession.Contact>();
|
2009-10-29 06:20:23 -04:00
|
|
|
if (ldapFilter.isFullSearch()) {
|
|
|
|
// append personal contacts first
|
2010-08-09 16:15:28 -04:00
|
|
|
for (ExchangeSession.Contact person : contactFind(null, returningAttributes, sizeLimit).values()) {
|
2010-07-06 05:49:26 -04:00
|
|
|
persons.put(person.get("imapUid"), person);
|
2009-10-29 06:20:23 -04:00
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// full search
|
2010-08-13 18:34:26 -04:00
|
|
|
for (char c = 'A'; c <= 'Z'; c++) {
|
2009-10-29 06:20:23 -04:00
|
|
|
if (!abandon && persons.size() < sizeLimit) {
|
2010-11-07 17:38:27 -05:00
|
|
|
for (ExchangeSession.Contact person : session.galFind(session.startsWith("cn", String.valueOf(c)),
|
2010-08-16 06:07:56 -04:00
|
|
|
convertLdapToContactReturningAttributes(returningAttributes), sizeLimit).values()) {
|
2010-08-09 16:15:28 -04:00
|
|
|
persons.put(person.get("uid"), person);
|
2009-10-29 06:20:23 -04:00
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// append personal contacts first
|
2010-06-17 18:37:33 -04:00
|
|
|
ExchangeSession.Condition filter = ldapFilter.getContactSearchFilter();
|
2009-10-29 06:20:23 -04:00
|
|
|
|
|
|
|
// if ldapfilter is not a full search and filter is null,
|
|
|
|
// ignored all attribute filters => return empty results
|
|
|
|
if (ldapFilter.isFullSearch() || filter != null) {
|
2010-08-09 16:15:28 -04:00
|
|
|
for (ExchangeSession.Contact person : contactFind(filter, returningAttributes, sizeLimit).values()) {
|
2010-07-06 05:49:26 -04:00
|
|
|
persons.put(person.get("imapUid"), person);
|
2009-10-29 06:20:23 -04:00
|
|
|
|
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!abandon && persons.size() < sizeLimit) {
|
2010-08-13 17:52:05 -04:00
|
|
|
for (ExchangeSession.Contact person : ldapFilter.findInGAL(session, returningAttributes, sizeLimit - persons.size()).values()) {
|
2009-10-29 06:20:23 -04:00
|
|
|
if (persons.size() == sizeLimit) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2010-08-09 16:15:28 -04:00
|
|
|
persons.put(person.get("uid"), person);
|
2009-10-29 06:20:23 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size = persons.size();
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_FOUND_RESULTS", currentMessageId, size));
|
|
|
|
sendPersons(currentMessageId, ", " + dn, persons, returningAttributes);
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_END", currentMessageId));
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_ANONYMOUS_ACCESS_FORBIDDEN", currentMessageId, dn));
|
|
|
|
}
|
2010-11-22 04:38:37 -05:00
|
|
|
} else if (dn != null && dn.length() > 0 && !OD_CONFIG_CONTEXT.equals(dn) && !OD_GROUP_CONTEXT.equals(dn)) {
|
2009-10-29 06:20:23 -04:00
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_INVALID_DN", currentMessageId, dn));
|
|
|
|
}
|
|
|
|
|
|
|
|
// iCal: do not send LDAP_SIZE_LIMIT_EXCEEDED on apple-computer search by cn with sizelimit 1
|
|
|
|
if (size > 1 && size == sizeLimit) {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SIZE_LIMIT_EXCEEDED", currentMessageId));
|
|
|
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SIZE_LIMIT_EXCEEDED, "");
|
|
|
|
} else {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SUCCESS", currentMessageId));
|
|
|
|
sendClient(currentMessageId, LDAP_REP_RESULT, LDAP_SUCCESS, "");
|
|
|
|
}
|
2010-01-24 15:41:02 -05:00
|
|
|
} catch (SocketException e) {
|
|
|
|
// client closed connection
|
2010-08-24 04:16:32 -04:00
|
|
|
LOGGER.warn(BundleMessage.formatLog("LOG_CLIENT_CLOSED_CONNECTION"));
|
2009-10-29 06:20:23 -04:00
|
|
|
} catch (IOException e) {
|
|
|
|
DavGatewayTray.log(e);
|
|
|
|
try {
|
|
|
|
sendErr(currentMessageId, LDAP_REP_RESULT, e);
|
|
|
|
} catch (IOException e2) {
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_EXCEPTION_SENDING_ERROR_TO_CLIENT"), e2);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
synchronized (searchThreadMap) {
|
|
|
|
searchThreadMap.remove(currentMessageId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-07-01 08:47:57 -04:00
|
|
|
/**
|
|
|
|
* Search users in contacts folder
|
|
|
|
*
|
2010-07-18 15:56:47 -04:00
|
|
|
* @param condition search filter
|
|
|
|
* @param returningAttributes requested attributes
|
2010-07-27 17:11:23 -04:00
|
|
|
* @param maxCount maximum item count
|
2010-07-01 08:47:57 -04:00
|
|
|
* @return List of users
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-08-09 16:15:28 -04:00
|
|
|
public Map<String, ExchangeSession.Contact> contactFind(ExchangeSession.Condition condition, Set<String> returningAttributes, int maxCount) throws IOException {
|
|
|
|
Map<String, ExchangeSession.Contact> results = new HashMap<String, ExchangeSession.Contact>();
|
2010-07-01 08:47:57 -04:00
|
|
|
|
2010-08-16 06:07:56 -04:00
|
|
|
Set<String> contactReturningAttributes = convertLdapToContactReturningAttributes(returningAttributes);
|
2010-08-18 18:54:23 -04:00
|
|
|
contactReturningAttributes.remove("apple-serviceslocator");
|
2010-07-27 16:46:49 -04:00
|
|
|
List<ExchangeSession.Contact> contacts = session.searchContacts(ExchangeSession.CONTACTS, contactReturningAttributes, condition, maxCount);
|
2010-07-01 08:47:57 -04:00
|
|
|
|
|
|
|
for (ExchangeSession.Contact contact : contacts) {
|
2010-08-09 16:15:28 -04:00
|
|
|
// use imapUid as uid
|
|
|
|
String imapUid = contact.get("imapUid");
|
|
|
|
if (imapUid != null) {
|
|
|
|
results.put(imapUid, contact);
|
2010-07-01 08:47:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-29 06:20:23 -04:00
|
|
|
/**
|
|
|
|
* Convert to LDAP attributes and send entry
|
|
|
|
*
|
|
|
|
* @param currentMessageId current Message Id
|
|
|
|
* @param baseContext request base context (BASE_CONTEXT or OD_BASE_CONTEXT)
|
|
|
|
* @param persons persons Map
|
|
|
|
* @param returningAttributes returning attributes
|
|
|
|
* @throws IOException on error
|
|
|
|
*/
|
2010-08-09 16:15:28 -04:00
|
|
|
protected void sendPersons(int currentMessageId, String baseContext, Map<String, ExchangeSession.Contact> persons, Set<String> returningAttributes) throws IOException {
|
2009-10-29 06:20:23 -04:00
|
|
|
boolean needObjectClasses = returningAttributes.contains("objectclass") || returningAttributes.isEmpty();
|
|
|
|
boolean returnAllAttributes = returningAttributes.isEmpty();
|
|
|
|
|
2010-08-09 16:15:28 -04:00
|
|
|
for (ExchangeSession.Contact person : persons.values()) {
|
2009-10-29 06:20:23 -04:00
|
|
|
if (abandon) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
Map<String, Object> ldapPerson = new HashMap<String, Object>();
|
|
|
|
|
2010-08-16 05:41:37 -04:00
|
|
|
// convert Contact entries
|
|
|
|
if (returnAllAttributes) {
|
|
|
|
// just convert contact attributes to default ldap names
|
|
|
|
for (Map.Entry<String, String> entry : person.entrySet()) {
|
|
|
|
String ldapAttribute = getLdapAttributeName(entry.getKey());
|
|
|
|
String value = entry.getValue();
|
|
|
|
if (value != null) {
|
2009-10-29 06:20:23 -04:00
|
|
|
ldapPerson.put(ldapAttribute, value);
|
|
|
|
}
|
|
|
|
}
|
2010-08-16 05:41:37 -04:00
|
|
|
} else {
|
|
|
|
// always map uid
|
2010-08-16 06:07:56 -04:00
|
|
|
ldapPerson.put("uid", person.get("imapUid"));
|
2010-08-16 05:41:37 -04:00
|
|
|
// iterate over requested attributes
|
|
|
|
for (String ldapAttribute : returningAttributes) {
|
|
|
|
String contactAttribute = getContactAttributeName(ldapAttribute);
|
|
|
|
String value = person.get(contactAttribute);
|
|
|
|
if (value != null) {
|
|
|
|
if (ldapAttribute.startsWith("birth")) {
|
|
|
|
SimpleDateFormat parser = ExchangeSession.getZuluDateFormat();
|
|
|
|
Calendar calendar = Calendar.getInstance();
|
|
|
|
try {
|
|
|
|
calendar.setTime(parser.parse(value));
|
|
|
|
} catch (ParseException e) {
|
2012-03-15 09:41:16 -04:00
|
|
|
throw new IOException(e + " " + e.getMessage());
|
2010-08-16 05:41:37 -04:00
|
|
|
}
|
|
|
|
if ("birthday".equals(ldapAttribute)) {
|
|
|
|
value = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
|
|
|
|
} else if ("birthmonth".equals(ldapAttribute)) {
|
|
|
|
value = String.valueOf(calendar.get(Calendar.MONTH) + 1);
|
|
|
|
} else if ("birthyear".equals(ldapAttribute)) {
|
|
|
|
value = String.valueOf(calendar.get(Calendar.YEAR));
|
2010-07-19 10:53:00 -04:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 05:41:37 -04:00
|
|
|
ldapPerson.put(ldapAttribute, value);
|
2009-10-29 06:20:23 -04:00
|
|
|
}
|
|
|
|
}
|
2010-08-16 05:41:37 -04:00
|
|
|
}
|
2009-10-29 06:20:23 -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
|
2010-08-16 05:41:37 -04:00
|
|
|
&& (returnAllAttributes || returningAttributes.contains(ldapAttribute))) {
|
2009-10-29 06:20:23 -04:00
|
|
|
ldapPerson.put(ldapAttribute, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needObjectClasses) {
|
|
|
|
ldapPerson.put("objectClass", PERSON_OBJECT_CLASSES);
|
|
|
|
}
|
|
|
|
|
|
|
|
// iCal: copy email to apple-generateduid, encode @
|
|
|
|
if (returnAllAttributes || returningAttributes.contains("apple-generateduid")) {
|
|
|
|
String mail = (String) ldapPerson.get("mail");
|
|
|
|
if (mail != null) {
|
|
|
|
ldapPerson.put("apple-generateduid", mail.replaceAll("@", "__AT__"));
|
|
|
|
} else {
|
|
|
|
// failover, should not happen
|
|
|
|
ldapPerson.put("apple-generateduid", ldapPerson.get("uid"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// iCal: replace current user alias with login name
|
|
|
|
if (session.getAlias().equals(ldapPerson.get("uid"))) {
|
|
|
|
if (returningAttributes.contains("uidnumber")) {
|
|
|
|
ldapPerson.put("uidnumber", userName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DavGatewayTray.debug(new BundleMessage("LOG_LDAP_REQ_SEARCH_SEND_PERSON", currentMessageId, ldapPerson.get("uid"), baseContext, ldapPerson));
|
|
|
|
sendEntry(currentMessageId, "uid=" + ldapPerson.get("uid") + baseContext, ldapPerson);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2008-12-03 06:38:35 -05:00
|
|
|
}
|