/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Distributable under LGPL license. * See terms of license at gnu.org. */ package net.java.sip.communicator.impl.ldap; import java.util.*; import java.util.regex.*; import net.java.sip.communicator.plugin.desktoputil.*; import net.java.sip.communicator.service.contactsource.*; import net.java.sip.communicator.service.ldap.*; import net.java.sip.communicator.service.ldap.event.*; import net.java.sip.communicator.service.protocol.*; /** * Implements ContactQuery for LDAP. *

* In contrast to other contact source implementations like AddressBook and * Outlook the LDAP contact source implementation is explicitly moved to the * "impl.ldap" package in order to allow us to create LDAP contact sources * for ldap directories through the LdapService. *

* * @author Sebastien Vincent * @author Yana Stamcheva */ public class LdapContactQuery extends AsyncContactQuery { /** * Maximum results for LDAP query. */ public static final int LDAP_MAX_RESULTS = 40; /** * Maximum number of results for this instance. */ private final int count; /** * LDAP query. */ private LdapQuery ldapQuery = null; /** * Object lock. */ private final Object objLock = new Object(); /** * Initializes a new LdapContactQuery instance which is to perform * a specific query on behalf of a specific contactSource. * * @param contactSource the ContactSourceService which is to * perform the new ContactQuery instance * @param query the Pattern for which contactSource is * being queried * @param count maximum number of results */ protected LdapContactQuery(LdapContactSourceService contactSource, Pattern query, int count) { super(contactSource, query); this.count = count; } /** * Performs this AsyncContactQuery in a background Thread. * * @see AsyncContactQuery#run() */ @Override protected void run() { /* query we get is delimited by \Q and \E * and we should not query LDAP server with a too small number of * characters */ String queryStr = query.toString(); if(queryStr.length() < (4)) { return; } /* remove \Q and \E from the Pattern */ String queryString = queryStr.substring(2, queryStr.length() - 2); LdapService ldapService = LdapActivator.getLdapService(); LdapFactory factory = ldapService.getFactory(); ldapQuery = factory.createQuery(queryString); LdapSearchSettings settings = factory.createSearchSettings(); settings.setDelay(250); settings.setMaxResults(count); LdapListener caller = new LdapListener() { public void ldapEventReceived(LdapEvent evt) { processLdapResponse(evt); } }; LdapDirectory ldapDir = getContactSource().getLdapDirectory(); if(ldapDir == null) { return; } ldapDir.searchPerson(ldapQuery, caller, settings); synchronized(objLock) { try { objLock.wait(); } catch(InterruptedException e) { } } } /** * Gets the contactDetails to be set on a SourceContact. * * @param person LDAP person * @return the contactDetails to be set on a SourceContact */ private List getContactDetails(LdapPersonFound person) { List ret = new LinkedList(); Set mailAddresses = person.getMail(); Set mobilePhones = person.getMobilePhone(); Set homePhones = person.getHomePhone(); Set workPhones = person.getWorkPhone(); ContactDetail detail = null; for(String mail : mailAddresses) { detail = new ContactDetail(mail, ContactDetail.Category.Email); // can be added as contacts detail.addSupportedOpSet(OperationSetPersistentPresence.class); ret.add(detail); } for(String homePhone : homePhones) { homePhone = PhoneNumberI18nService.normalize(homePhone); detail = new ContactDetail(homePhone, ContactDetail.Category.Phone, new ContactDetail.SubCategory[]{ ContactDetail.SubCategory.Home}); detail.addSupportedOpSet(OperationSetBasicTelephony.class); // can be added as contacts detail.addSupportedOpSet(OperationSetPersistentPresence.class); ret.add(detail); } for(String workPhone : workPhones) { workPhone = PhoneNumberI18nService.normalize(workPhone); detail = new ContactDetail(workPhone, ContactDetail.Category.Phone, new ContactDetail.SubCategory[]{ ContactDetail.SubCategory.Work}); detail.addSupportedOpSet(OperationSetBasicTelephony.class); // can be added as contacts detail.addSupportedOpSet(OperationSetPersistentPresence.class); ret.add(detail); } for(String mobilePhone : mobilePhones) { mobilePhone = PhoneNumberI18nService.normalize(mobilePhone); detail = new ContactDetail(mobilePhone, ContactDetail.Category.Phone, new ContactDetail.SubCategory[]{ ContactDetail.SubCategory.Mobile}); detail.addSupportedOpSet(OperationSetBasicTelephony.class); // can be added as contacts detail.addSupportedOpSet(OperationSetPersistentPresence.class); ret.add(detail); } return ret; } /** * Process LDAP event. * * @param evt LDAP event */ private void processLdapResponse(LdapEvent evt) { if(evt.getCause() == LdapEvent.LdapEventCause.SEARCH_ACHIEVED || evt.getCause() == LdapEvent.LdapEventCause.SEARCH_CANCELLED) { synchronized(objLock) { objLock.notify(); } } if (evt.getCause() == LdapEvent.LdapEventCause.SEARCH_ERROR) { // The status must be set to QUERY_ERROR and the thread allowed to // continue, otherwise the query will still appear to be in // progress. setStatus(ContactQuery.QUERY_ERROR); synchronized(objLock) { objLock.notify(); } } if(evt.getCause() == LdapEvent.LdapEventCause.NEW_SEARCH_RESULT) { LdapPersonFound person = (LdapPersonFound) evt.getContent(); String displayName = null; if(person == null) { return; } if(person.getDisplayName() != null) { displayName = person.getDisplayName(); } else { displayName = person.getFirstName() + " " + person.getSurname(); } List contactDetails = getContactDetails(person); if (!contactDetails.isEmpty()) { GenericSourceContact sourceContact = new GenericSourceContact( getContactSource(), displayName, contactDetails); try { sourceContact.setImage(person.fetchPhoto()); } catch (OutOfMemoryError oome) { // Ignore it, the image is not vital. } if (person.getOrganization() != null) { sourceContact.setDisplayDetails(person.getOrganization()); } addQueryResult(sourceContact); } } else if(evt.getCause() == LdapEvent.LdapEventCause.SEARCH_AUTH_ERROR) { synchronized(objLock) { objLock.notify(); } /* show authentication window to obtain new credentials */ new Thread() { @Override public void run() { LdapDirectorySettingsImpl ldapSettings = (LdapDirectorySettingsImpl) getContactSource() .getLdapDirectory().getSettings(); AuthenticationWindow authWindow = new AuthenticationWindow(ldapSettings.getUserName(), ldapSettings.getPassword().toCharArray(), ldapSettings.getName(), false, LdapActivator.getResourceService().getImage( "service.gui.icons.AUTHORIZATION_ICON"), LdapActivator.getResourceService().getI18NString( "impl.ldap.WRONG_CREDENTIALS", new String[]{ldapSettings.getName()})); authWindow.setVisible(true); if(!authWindow.isCanceled()) { LdapDirectorySettings newSettings = new LdapDirectorySettingsImpl(ldapSettings); // Remove old server. LdapService ldapService = LdapActivator.getLdapService(); LdapFactory factory = ldapService.getFactory(); LdapDirectory ldapDir = getContactSource().getLdapDirectory(); LdapActivator.unregisterContactSource(ldapDir); ldapService.getServerSet().removeServerWithName( ldapSettings.getName()); // Add new server. newSettings.setPassword( new String(authWindow.getPassword())); ldapDir = factory.createServer(newSettings); ldapService.getServerSet().addServer(ldapDir); LdapActivator.registerContactSource(ldapDir); } } }.start(); } } /** * Notifies this LdapContactQuery that it has stopped performing * in the associated background Thread. * * @param completed true if this ContactQuery has * successfully completed, false if an error has been encountered * during its execution * @see AsyncContactQuery#stopped(boolean) */ @Override protected void stopped(boolean completed) { try { super.stopped(completed); } finally { getContactSource().stopped(this); } } /** * Cancels this ContactQuery. * * @see ContactQuery#cancel() */ @Override public void cancel() { if(ldapQuery != null) { ldapQuery.setState(LdapQuery.State.CANCELLED); } synchronized(objLock) { objLock.notify(); } super.cancel(); } }