/* * 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.googlecontacts; import java.io.*; import java.net.*; import java.util.*; import java.util.regex.*; import net.java.sip.communicator.impl.googlecontacts.configform.*; import net.java.sip.communicator.service.credentialsstorage.*; import net.java.sip.communicator.service.googlecontacts.*; import net.java.sip.communicator.util.*; import org.jitsi.service.configuration.*; import com.google.gdata.client.Service.GDataRequest; import com.google.gdata.client.contacts.*; import com.google.gdata.data.*; import com.google.gdata.data.contacts.ContactEntry; import com.google.gdata.data.contacts.ContactFeed; import com.google.gdata.data.extensions.*; /** * Implementation of Google Contacts service. * We first get {@link #MAX_RESULT} contacts from Google Contacts then * we filter it to get {@link #MAX_RESULT} that matched our query. * If {@link #MAX_RESULT} is not reach, we try with additional * contacts (if there are more than {@link #MAX_RESULT} contacts). * * @author Sebastien Vincent */ public class GoogleContactsServiceImpl implements GoogleContactsService { /** * Logger. */ private static final Logger logger = Logger.getLogger(GoogleContactsServiceImpl.class); /** * Google Contacts feed URL string. */ private static final String feedURL = "https://www.google.com/m8/feeds/contacts/default/full"; /** * Maximum number of results for a query. */ public static final int MAX_RESULT = 20; /** * Maximum number of contacts retrieved for a query. */ public static final int MAX_NUMBER = 1000; /** * List of Google Contacts account. */ private final List accounts = new ArrayList(); /** * Path where to store the account settings */ private final static String CONFIGURATION_PATH = "net.java.sip.communicator.impl.googlecontacts"; /** * Constructor. */ public GoogleContactsServiceImpl() { new Thread() { public void run() { loadConfig(); } }.start(); } /** * Get list of stored connections. * * @return list of connections */ public List getAccounts() { return accounts; } /** * Loads configuration. */ private void loadConfig() { ConfigurationService configService = GoogleContactsActivator.getConfigService(); CredentialsStorageService credentialsService = GoogleContactsActivator.getCredentialsService(); List list = configService.getPropertyNamesByPrefix( CONFIGURATION_PATH, true); for(Object configEntry : list) { String path = configEntry.toString(); Object oen = configService.getProperty(path + ".enabled"); boolean enabled = Boolean.parseBoolean((String)oen); String login = (String)configService.getProperty(path + ".account"); String prefix = (String)configService.getProperty(path + ".prefix"); // If this property doesn't exist, like for old stored accounts, we // just need to initialize it to the empty string. if(prefix == null) prefix = ""; String password = credentialsService.loadPassword(path); GoogleContactsConnectionImpl cnx = (GoogleContactsConnectionImpl) getConnection(login, password); cnx.setEnabled(enabled); cnx.setPrefix(prefix); if(cnx != null) { if(cnx.connect() == GoogleContactsConnection.ConnectionStatus. ERROR_INVALID_CREDENTIALS) { cnx.setEnabled(false); AccountSettingsForm settings = new AccountSettingsForm(); settings.setModal(true); settings.loadData(cnx); int ret = settings.showDialog(); if(ret == 1) { cnx = (GoogleContactsConnectionImpl) settings.getConnection(); // set the enabled state as before cnx.setEnabled(enabled); cnx.setPrefix(prefix); saveConfig(cnx); } } accounts.add(cnx); /* register contact source */ if(cnx.isEnabled()) { addContactSource(cnx, true); } } } } /** * Remove a connection. * * @param cnx connection to save */ public void removeConfig(GoogleContactsConnection cnx) { ConfigurationService configService = GoogleContactsActivator.getConfigService(); configService.removeProperty(CONFIGURATION_PATH + ".acc" + Math.abs(cnx.getLogin().hashCode())); } /** * Save configuration. * * @param cnx connection to save */ public void saveConfig(GoogleContactsConnection cnx) { ConfigurationService configService = GoogleContactsActivator.getConfigService(); CredentialsStorageService credentialsService = GoogleContactsActivator.getCredentialsService(); String login = cnx.getLogin(); String path = CONFIGURATION_PATH + ".acc" + Math.abs(login.hashCode()); configService.setProperty( path, login); configService.setProperty( path + ".account", login); configService.setProperty( path + ".enabled", ((GoogleContactsConnectionImpl)cnx).isEnabled()); configService.setProperty( path + ".prefix", ((GoogleContactsConnectionImpl)cnx).getPrefix()); credentialsService.storePassword(path, cnx.getPassword()); } /** * Perform a search for a contact using regular expression. * * @param cnx GoogleContactsConnection to perform the query * @param gQuery Google query * @param count maximum number of matched contacts * @param callback object that will be notified for each new * GoogleContactsEntry found * @return list of GoogleContactsEntry */ public List searchContact( GoogleContactsConnection cnx, GoogleQuery gQuery, int count, GoogleEntryCallback callback) { URL url = null; ContactFeed contactFeed = null; ContactQuery query = null; List ret = new ArrayList(); boolean endOfContacts = false; int matchedContacts = 0; int index = 1; GoogleContactsConnectionImpl cnxImpl = (GoogleContactsConnectionImpl)cnx; if(count <= 0) { count = MAX_RESULT; } try { url = new URL(feedURL); } catch(MalformedURLException e) { logger.info("Malformed URL", e); return ret; } if(gQuery.isCancelled()) { return ret; } while(matchedContacts < count || endOfContacts) { query = new ContactQuery(url); query.setStartIndex(index); query.setMaxResults(MAX_NUMBER); query.setSortOrder(ContactQuery.SortOrder.DESCENDING); if(gQuery.isCancelled()) { return ret; } try { contactFeed = cnxImpl.getGoogleService().query( query, ContactFeed.class); } catch(Exception e) { logger.info( "Problem occurred during Google Contacts retrievment", e); return ret; } if(contactFeed.getEntries().size() == 0) { endOfContacts = true; break; } for (int i = 0; i < contactFeed.getEntries().size(); i++) { if(gQuery.isCancelled()) { return ret; } ContactEntry entry = contactFeed.getEntries().get(i); if(filter(entry, gQuery.getQueryPattern())) { GoogleContactsEntry gcEntry = null; gcEntry = getGoogleContactsEntry(entry); matchedContacts++; ret.add(gcEntry); if(callback != null) { callback.callback(gcEntry); } if(matchedContacts >= count) { break; } } } index += contactFeed.getEntries().size(); } return ret; } /** * Filter according to filter. * * @param entry ContactEntry * @param filter regular expression * @return true if entry match the filter, false otherwise */ private boolean filter(ContactEntry entry, Pattern filter) { Name name = entry.getName(); /* try to see if name, mail or phone match */ if(name != null) { if(name.hasFamilyName()) { Matcher m = filter.matcher(name.getFamilyName().getValue()); if(m.matches()) { return true; } } if(name.hasGivenName()) { Matcher m = filter.matcher(name.getGivenName().getValue()); if(m.find()) { return true; } } if(name.hasFullName()) { Matcher m = filter.matcher(name.getFullName().getValue()); if(m.find()) { return true; } } } for(Email mail : entry.getEmailAddresses()) { Matcher m = filter.matcher(mail.getAddress()); if(m.find()) { return true; } } for(PhoneNumber phone : entry.getPhoneNumbers()) { Matcher m = filter.matcher(phone.getPhoneNumber()); if(m.find()) { return true; } } return false; } /** * Get a GoogleContactsEntry from a ContactEntry * * @param entry ContactEntry * @return GoogleContactsEntry */ private GoogleContactsEntry getGoogleContactsEntry(ContactEntry entry) { GoogleContactsEntryImpl ret = new GoogleContactsEntryImpl(); ret.setField(entry); return ret; } /** * Get the full contacts list. * * @return list of GoogleContactsEntry */ public List getContacts() { return null; } /** * Get a GoogleContactsConnection. * * @param login login to connect to the service * @param password password to connect to the service * @return GoogleContactsConnection. */ public GoogleContactsConnection getConnection(String login, String password) { try { return new GoogleContactsConnectionImpl(login, password); } catch(Exception e) { logger.info("Failed to obtain Google Contacts connection", e); return null; } } /** * Add a contact source service with the specified * GoogleContactsConnection. * * @param cnx GoogleContactsConnection. * @param googleTalk if the contact source has been created as GoogleTalk * account or via external Google Contacts */ public void addContactSource(GoogleContactsConnection cnx, boolean googleTalk) { GoogleContactsActivator.enableContactSource(cnx, googleTalk); } /** * Add a contact source service with the specified * GoogleContactsConnection. * * @param login login * @param password password */ public void addContactSource(String login, String password) { GoogleContactsActivator.enableContactSource(login, password, false); } /** * Add a contact source service with the specified. * * GoogleContactsConnection. * @param cnx GoogleContactsConnection. */ public void removeContactSource(GoogleContactsConnection cnx) { GoogleContactsActivator.disableContactSource(cnx); } /** * Remove a contact source service with the specified * GoogleContactsConnection. * * @param login login */ public void removeContactSource(String login) { GoogleContactsActivator.disableContactSource(login); } /** * Retrieve photo of a contact. Adapted from Google sample. * * @param photoLink photo link * @param service * @return byte array containing image photo or null if problem happened */ public static byte[] downloadPhoto(ILink photoLink, ContactsService service) { try { if (photoLink != null) { GDataRequest request = service.createLinkQueryRequest(photoLink); request.execute(); InputStream in = request.getResponseStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; for (int read = 0 ; (read = in.read(buffer)) != -1; out.write(buffer, 0, read)); return out.toByteArray(); } } catch(Exception e) { logger.debug("Failed to retrieve photo of the contact", e); } return null; } }