mirror of https://github.com/sipwise/jitsi.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1072 lines
40 KiB
1072 lines
40 KiB
/*
|
|
* 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.service.protocol;
|
|
|
|
import java.util.*;
|
|
|
|
import net.java.sip.communicator.service.credentialsstorage.*;
|
|
import net.java.sip.communicator.service.protocol.event.*;
|
|
import net.java.sip.communicator.util.*;
|
|
|
|
import org.jitsi.service.configuration.*;
|
|
import org.osgi.framework.*;
|
|
|
|
/**
|
|
* Represents an implementation of <tt>AccountManager</tt> which loads the
|
|
* accounts in a separate thread.
|
|
*
|
|
* @author Lubomir Marinov
|
|
* @author Yana Stamcheva
|
|
*/
|
|
public class AccountManager
|
|
{
|
|
/**
|
|
* The delay in milliseconds the background <tt>Thread</tt> loading the
|
|
* stored accounts should wait before dying so that it doesn't get recreated
|
|
* for each <tt>ProtocolProviderFactory</tt> registration.
|
|
*/
|
|
private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000;
|
|
|
|
/**
|
|
* The <tt>BundleContext</tt> this service is registered in.
|
|
*/
|
|
private final BundleContext bundleContext;
|
|
|
|
/**
|
|
* The <tt>AccountManagerListener</tt>s currently interested in the
|
|
* events fired by this manager.
|
|
*/
|
|
private final List<AccountManagerListener> listeners =
|
|
new LinkedList<AccountManagerListener>();
|
|
|
|
/**
|
|
* The queue of <tt>ProtocolProviderFactory</tt> services awaiting their
|
|
* stored accounts to be loaded.
|
|
*/
|
|
private final Queue<ProtocolProviderFactory> loadStoredAccountsQueue =
|
|
new LinkedList<ProtocolProviderFactory>();
|
|
|
|
/**
|
|
* The <tt>Thread</tt> loading the stored accounts of the
|
|
* <tt>ProtocolProviderFactory</tt> services waiting in
|
|
* {@link #loadStoredAccountsQueue}.
|
|
*/
|
|
private Thread loadStoredAccountsThread;
|
|
|
|
/**
|
|
* The <tt>Logger</tt> used by this <tt>AccountManagerImpl</tt> instance for
|
|
* logging output.
|
|
*/
|
|
private final Logger logger = Logger.getLogger(AccountManager.class);
|
|
|
|
/**
|
|
* The list of <tt>AccountID</tt>s, corresponding to all stored accounts.
|
|
*/
|
|
private final Vector<AccountID> storedAccounts = new Vector<AccountID>();
|
|
|
|
/**
|
|
* The prefix of the account unique identifier.
|
|
*/
|
|
private static final String ACCOUNT_UID_PREFIX = "acc";
|
|
|
|
/**
|
|
* Initializes a new <tt>AccountManagerImpl</tt> instance loaded in a
|
|
* specific <tt>BundleContext</tt> (in which the caller will usually
|
|
* later register it).
|
|
*
|
|
* @param bundleContext the <tt>BundleContext</tt> in which the new
|
|
* instance is loaded (and in which the caller will usually later
|
|
* register it as a service)
|
|
*/
|
|
public AccountManager(BundleContext bundleContext)
|
|
{
|
|
this.bundleContext = bundleContext;
|
|
|
|
this.bundleContext.addServiceListener(new ServiceListener()
|
|
{
|
|
public void serviceChanged(ServiceEvent serviceEvent)
|
|
{
|
|
AccountManager.this.serviceChanged(serviceEvent);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Implements AccountManager#addListener(AccountManagerListener).
|
|
* @param listener the <tt>AccountManagerListener</tt> to add
|
|
*/
|
|
public void addListener(AccountManagerListener listener)
|
|
{
|
|
synchronized (listeners)
|
|
{
|
|
if (!listeners.contains(listener))
|
|
listeners.add(listener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the accounts stored for a specific
|
|
* <tt>ProtocolProviderFactory</tt>.
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> to load the
|
|
* stored accounts of
|
|
*/
|
|
private void doLoadStoredAccounts(ProtocolProviderFactory factory)
|
|
{
|
|
ConfigurationService configService
|
|
= ProtocolProviderActivator.getConfigurationService();
|
|
String factoryPackage = getFactoryImplPackageName(factory);
|
|
List<String> accounts
|
|
= configService.getPropertyNamesByPrefix(factoryPackage, true);
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Discovered " + accounts.size() + " stored "
|
|
+ factoryPackage + " accounts");
|
|
|
|
for (Iterator<String> storedAccountIter = accounts.iterator();
|
|
storedAccountIter.hasNext();)
|
|
{
|
|
String storedAccount = storedAccountIter.next();
|
|
|
|
// If the property is not related to an account we skip it.
|
|
int dotIndex = storedAccount.lastIndexOf(".");
|
|
if (!storedAccount.substring(dotIndex + 1)
|
|
.startsWith(ACCOUNT_UID_PREFIX))
|
|
continue;
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Loading account " + storedAccount);
|
|
|
|
List<String> storedAccountProperties =
|
|
configService.getPropertyNamesByPrefix(storedAccount, false);
|
|
Map<String, String> accountProperties =
|
|
new Hashtable<String, String>();
|
|
boolean disabled = false;
|
|
CredentialsStorageService credentialsStorage
|
|
= ServiceUtils.getService(
|
|
bundleContext,
|
|
CredentialsStorageService.class);
|
|
|
|
for (Iterator<String> storedAccountPropertyIter
|
|
= storedAccountProperties.iterator();
|
|
storedAccountPropertyIter.hasNext();)
|
|
{
|
|
String property = storedAccountPropertyIter.next();
|
|
String value = configService.getString(property);
|
|
|
|
//strip the package prefix
|
|
property = property.substring(storedAccount.length() + 1);
|
|
|
|
if (ProtocolProviderFactory.IS_ACCOUNT_DISABLED.equals(property))
|
|
disabled = Boolean.parseBoolean(value);
|
|
// Decode passwords.
|
|
else if (ProtocolProviderFactory.PASSWORD.equals(property)
|
|
&& !credentialsStorage.isStoredEncrypted(storedAccount))
|
|
{
|
|
if ((value != null) && value.length() != 0)
|
|
{
|
|
|
|
/*
|
|
* TODO Converting byte[] to String using the platform's
|
|
* default charset may result in an invalid password.
|
|
*/
|
|
value = new String(Base64.decode(value));
|
|
}
|
|
}
|
|
|
|
if (value != null)
|
|
accountProperties.put(property, value);
|
|
}
|
|
|
|
try
|
|
{
|
|
AccountID accountID = factory.createAccount(accountProperties);
|
|
|
|
// If for some reason the account id is not created we move to
|
|
// the next account.
|
|
if (accountID == null)
|
|
continue;
|
|
|
|
synchronized (storedAccounts)
|
|
{
|
|
storedAccounts.add(accountID);
|
|
}
|
|
if (!disabled)
|
|
factory.loadAccount(accountID);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
/*
|
|
* Swallow the exception in order to prevent a single account
|
|
* from halting the loading of subsequent accounts.
|
|
*/
|
|
logger.error("Failed to load account " + accountProperties, ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Notifies the registered {@link #listeners} that the stored accounts of a
|
|
* specific <tt>ProtocolProviderFactory</tt> have just been loaded.
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> which had its
|
|
* stored accounts just loaded
|
|
*/
|
|
private void fireStoredAccountsLoaded(ProtocolProviderFactory factory)
|
|
{
|
|
AccountManagerListener[] listeners;
|
|
synchronized (this.listeners)
|
|
{
|
|
listeners =
|
|
this.listeners
|
|
.toArray(new AccountManagerListener[this.listeners.size()]);
|
|
}
|
|
|
|
int listenerCount = listeners.length;
|
|
if (listenerCount > 0)
|
|
{
|
|
AccountManagerEvent event =
|
|
new AccountManagerEvent(this,
|
|
AccountManagerEvent.STORED_ACCOUNTS_LOADED, factory);
|
|
|
|
for (int listenerIndex = 0;
|
|
listenerIndex < listenerCount; listenerIndex++)
|
|
{
|
|
listeners[listenerIndex].handleAccountManagerEvent(event);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the package name of the <tt>factory</tt>.
|
|
* @param factory the factory which package will be returned.
|
|
* @return the package name of the <tt>factory</tt>.
|
|
*/
|
|
public String getFactoryImplPackageName(ProtocolProviderFactory factory)
|
|
{
|
|
String className = factory.getClass().getName();
|
|
|
|
return className.substring(0, className.lastIndexOf('.'));
|
|
}
|
|
|
|
/**
|
|
* Check for stored accounts for the supplied <tt>protocolName</tt>.
|
|
* @param protocolName the protocol name to check for
|
|
* @param includeHidden whether to include hidden providers
|
|
* @return <tt>true</tt> if there is any account stored in configuration
|
|
* service with <tt>protocolName</tt>, <tt>false</tt> otherwise.
|
|
*/
|
|
public boolean hasStoredAccounts(String protocolName, boolean includeHidden)
|
|
{
|
|
return hasStoredAccount(protocolName, includeHidden, null);
|
|
}
|
|
|
|
/**
|
|
* Checks whether a stored account with <tt>userID</tt> is stored
|
|
* in configuration.
|
|
*
|
|
* @param protocolName the protocol name
|
|
* @param includeHidden whether to check hidden providers
|
|
* @param userID the user id to check.
|
|
* @return <tt>true</tt> if there is any account stored in configuration
|
|
* service with <tt>protocolName</tt> and <tt>userID</tt>,
|
|
* <tt>false</tt> otherwise.
|
|
*/
|
|
public boolean hasStoredAccount(String protocolName,
|
|
boolean includeHidden,
|
|
String userID)
|
|
{
|
|
ServiceReference[] factoryRefs = null;
|
|
boolean hasStoredAccounts = false;
|
|
|
|
try
|
|
{
|
|
factoryRefs
|
|
= bundleContext.getServiceReferences(
|
|
ProtocolProviderFactory.class.getName(), null);
|
|
}
|
|
catch (InvalidSyntaxException ex)
|
|
{
|
|
logger.error(
|
|
"Failed to retrieve the registered ProtocolProviderFactories",
|
|
ex);
|
|
}
|
|
|
|
if ((factoryRefs != null) && (factoryRefs.length > 0))
|
|
{
|
|
ConfigurationService configService
|
|
= ProtocolProviderActivator.getConfigurationService();
|
|
|
|
for (ServiceReference factoryRef : factoryRefs)
|
|
{
|
|
ProtocolProviderFactory factory
|
|
= (ProtocolProviderFactory)
|
|
bundleContext.getService(factoryRef);
|
|
|
|
if ((protocolName != null)
|
|
&& !protocolName.equals(factory.getProtocolName()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
String factoryPackage = getFactoryImplPackageName(factory);
|
|
List<String> storedAccounts
|
|
= configService
|
|
.getPropertyNamesByPrefix(factoryPackage, true);
|
|
|
|
/* Ignore the hidden accounts. */
|
|
for (Iterator<String> storedAccountIter =
|
|
storedAccounts.iterator(); storedAccountIter.hasNext();)
|
|
{
|
|
String storedAccount = storedAccountIter.next();
|
|
List<String> storedAccountProperties =
|
|
configService.getPropertyNamesByPrefix(storedAccount,
|
|
true);
|
|
boolean hidden = false;
|
|
String accountUserID = null;
|
|
|
|
if (!includeHidden || userID != null)
|
|
{
|
|
for (Iterator<String> storedAccountPropertyIter =
|
|
storedAccountProperties.iterator();
|
|
storedAccountPropertyIter.hasNext();)
|
|
{
|
|
String property = storedAccountPropertyIter.next();
|
|
String value = configService.getString(property);
|
|
|
|
property = stripPackagePrefix(property);
|
|
|
|
if (ProtocolProviderFactory.IS_PROTOCOL_HIDDEN
|
|
.equals(property))
|
|
{
|
|
hidden = (value != null);
|
|
}
|
|
else if (ProtocolProviderFactory.USER_ID
|
|
.equals(property))
|
|
{
|
|
accountUserID = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (includeHidden || !hidden)
|
|
{
|
|
if(accountUserID != null
|
|
&& userID != null
|
|
&& userID.equals(accountUserID))
|
|
{
|
|
hasStoredAccounts = true;
|
|
break;
|
|
}
|
|
else if(userID == null)
|
|
{
|
|
hasStoredAccounts = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasStoredAccounts || (protocolName != null))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hasStoredAccounts;
|
|
}
|
|
|
|
/**
|
|
* Searches for stored account with <tt>uid</tt> in stored
|
|
* configuration. The <tt>uid</tt> is the one generated when creating
|
|
* accounts with prefix <tt>ACCOUNT_UID_PREFIX</tt>.
|
|
*
|
|
* @return <tt>AccountID</tt> if there is any account stored in configuration
|
|
* service with <tt>uid</tt>,
|
|
* <tt>null</tt> otherwise.
|
|
*/
|
|
public AccountID findAccountID(String uid)
|
|
{
|
|
ServiceReference[] factoryRefs = null;
|
|
|
|
try
|
|
{
|
|
factoryRefs
|
|
= bundleContext.getServiceReferences(
|
|
ProtocolProviderFactory.class.getName(), null);
|
|
}
|
|
catch (InvalidSyntaxException ex)
|
|
{
|
|
logger.error(
|
|
"Failed to retrieve the registered ProtocolProviderFactories",
|
|
ex);
|
|
}
|
|
|
|
if ((factoryRefs != null) && (factoryRefs.length > 0))
|
|
{
|
|
ConfigurationService configService
|
|
= ProtocolProviderActivator.getConfigurationService();
|
|
|
|
for (ServiceReference factoryRef : factoryRefs)
|
|
{
|
|
ProtocolProviderFactory factory
|
|
= (ProtocolProviderFactory)
|
|
bundleContext.getService(factoryRef);
|
|
|
|
String factoryPackage = getFactoryImplPackageName(factory);
|
|
List<String> storedAccountsProps
|
|
= configService
|
|
.getPropertyNamesByPrefix(factoryPackage, true);
|
|
|
|
for (Iterator<String> storedAccountIter =
|
|
storedAccountsProps.iterator();
|
|
storedAccountIter.hasNext();)
|
|
{
|
|
String storedAccount = storedAccountIter.next();
|
|
|
|
if(!storedAccount.endsWith(uid))
|
|
continue;
|
|
|
|
String accountUID = configService.getString(
|
|
storedAccount //node id
|
|
+ "." + ProtocolProviderFactory.ACCOUNT_UID);// propname
|
|
|
|
for(AccountID acc : storedAccounts)
|
|
{
|
|
if(acc.getAccountUniqueID().equals(accountUID))
|
|
return acc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Loads the accounts stored for a specific
|
|
* <tt>ProtocolProviderFactory</tt> and notifies the registered
|
|
* {@link #listeners} that the stored accounts of the specified
|
|
* <tt>factory</tt> have just been loaded
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> to load the
|
|
* stored accounts of
|
|
*/
|
|
private void loadStoredAccounts(ProtocolProviderFactory factory)
|
|
{
|
|
doLoadStoredAccounts(factory);
|
|
|
|
fireStoredAccountsLoaded(factory);
|
|
}
|
|
|
|
/**
|
|
* Notifies this manager that a specific
|
|
* <tt>ProtocolProviderFactory</tt> has been registered as a service.
|
|
* The current implementation queues the specified <tt>factory</tt> to
|
|
* have its stored accounts as soon as possible.
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> which has been
|
|
* registered as a service.
|
|
*/
|
|
private void protocolProviderFactoryRegistered(
|
|
ProtocolProviderFactory factory)
|
|
{
|
|
queueLoadStoredAccounts(factory);
|
|
}
|
|
|
|
/**
|
|
* Queues a specific <tt>ProtocolProviderFactory</tt> to have its stored
|
|
* accounts loaded as soon as possible.
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> to be queued for
|
|
* loading its stored accounts as soon as possible
|
|
*/
|
|
private void queueLoadStoredAccounts(ProtocolProviderFactory factory)
|
|
{
|
|
synchronized (loadStoredAccountsQueue)
|
|
{
|
|
loadStoredAccountsQueue.add(factory);
|
|
loadStoredAccountsQueue.notifyAll();
|
|
|
|
if (loadStoredAccountsThread == null)
|
|
{
|
|
loadStoredAccountsThread = new Thread()
|
|
{
|
|
@Override
|
|
public void run()
|
|
{
|
|
runInLoadStoredAccountsThread();
|
|
}
|
|
};
|
|
loadStoredAccountsThread.setDaemon(true);
|
|
loadStoredAccountsThread
|
|
.setName("AccountManager.loadStoredAccounts");
|
|
loadStoredAccountsThread.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements AccountManager#removeListener(AccountManagerListener).
|
|
* @param listener the <tt>AccountManagerListener</tt> to remove
|
|
*/
|
|
public void removeListener(AccountManagerListener listener)
|
|
{
|
|
synchronized (listeners)
|
|
{
|
|
listeners.remove(listener);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Running in {@link #loadStoredAccountsThread}, loads the stored accounts
|
|
* of the <tt>ProtocolProviderFactory</tt> services waiting in
|
|
* {@link #loadStoredAccountsQueue}
|
|
*/
|
|
private void runInLoadStoredAccountsThread()
|
|
{
|
|
boolean interrupted = false;
|
|
while (!interrupted)
|
|
{
|
|
try
|
|
{
|
|
ProtocolProviderFactory factory;
|
|
|
|
synchronized (loadStoredAccountsQueue)
|
|
{
|
|
factory = loadStoredAccountsQueue.poll();
|
|
if (factory == null)
|
|
{
|
|
/*
|
|
* Technically, we should be handing spurious wakeups.
|
|
* However, we cannot check the condition in a queue.
|
|
* Anyway, we just want to keep this Thread alive long
|
|
* enough to allow it to not be re-created multiple
|
|
* times and not handing a spurious wakeup will just
|
|
* cause such an inconvenience.
|
|
*/
|
|
try
|
|
{
|
|
loadStoredAccountsQueue
|
|
.wait(LOAD_STORED_ACCOUNTS_TIMEOUT);
|
|
}
|
|
catch (InterruptedException ex)
|
|
{
|
|
logger
|
|
.warn(
|
|
"The loading of the stored accounts has"
|
|
+ " been interrupted",
|
|
ex);
|
|
interrupted = true;
|
|
break;
|
|
}
|
|
factory = loadStoredAccountsQueue.poll();
|
|
}
|
|
if (factory != null)
|
|
loadStoredAccountsQueue.notifyAll();
|
|
}
|
|
|
|
if (factory != null)
|
|
{
|
|
try
|
|
{
|
|
loadStoredAccounts(factory);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
/*
|
|
* Swallow the exception in order to prevent a single
|
|
* factory from halting the loading of subsequent
|
|
* factories.
|
|
*/
|
|
logger.error("Failed to load accounts for " + factory,
|
|
ex);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
synchronized (loadStoredAccountsQueue)
|
|
{
|
|
if (!interrupted && (loadStoredAccountsQueue.size() <= 0))
|
|
{
|
|
if (loadStoredAccountsThread == Thread.currentThread())
|
|
{
|
|
loadStoredAccountsThread = null;
|
|
loadStoredAccountsQueue.notifyAll();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Notifies this manager that an OSGi service has changed. The current
|
|
* implementation tracks the registrations of
|
|
* <tt>ProtocolProviderFactory</tt> services in order to queue them for
|
|
* loading their stored accounts.
|
|
*
|
|
* @param serviceEvent the <tt>ServiceEvent</tt> containing the event
|
|
* data
|
|
*/
|
|
private void serviceChanged(ServiceEvent serviceEvent)
|
|
{
|
|
switch (serviceEvent.getType())
|
|
{
|
|
case ServiceEvent.REGISTERED:
|
|
Object service
|
|
= bundleContext.getService(serviceEvent.getServiceReference());
|
|
|
|
if (service instanceof ProtocolProviderFactory)
|
|
{
|
|
protocolProviderFactoryRegistered(
|
|
(ProtocolProviderFactory) service);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stores an account represented in the form of an <tt>AccountID</tt>
|
|
* created by a specific <tt>ProtocolProviderFactory</tt>.
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> which created the
|
|
* account to be stored
|
|
* @param accountID the account in the form of <tt>AccountID</tt> to be
|
|
* stored
|
|
* @throws OperationFailedException if anything goes wrong while storing the
|
|
* account
|
|
*/
|
|
public void storeAccount(
|
|
ProtocolProviderFactory factory,
|
|
AccountID accountID)
|
|
throws OperationFailedException
|
|
{
|
|
synchronized (storedAccounts)
|
|
{
|
|
if (!storedAccounts.contains(accountID))
|
|
storedAccounts.add(accountID);
|
|
}
|
|
|
|
ConfigurationService configurationService
|
|
= ProtocolProviderActivator.getConfigurationService();
|
|
String factoryPackage = getFactoryImplPackageName(factory);
|
|
|
|
String accountNodeName = getAccountNodeName(factory, accountID);
|
|
|
|
Map<String, Object> configurationProperties
|
|
= new HashMap<String, Object>();
|
|
|
|
// Create a unique node name of the properties node that will contain
|
|
// this account's properties.
|
|
if (accountNodeName == null)
|
|
{
|
|
accountNodeName
|
|
= ACCOUNT_UID_PREFIX + Long.toString(System.currentTimeMillis());
|
|
|
|
// set a value for the persistent node so that we could later
|
|
// retrieve it as a property
|
|
configurationProperties.put(
|
|
factoryPackage /* prefix */ + "." + accountNodeName,
|
|
accountNodeName);
|
|
|
|
// register the account in the configuration service.
|
|
// we register all the properties in the following hierarchy
|
|
//net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
|
|
configurationProperties.put(factoryPackage// prefix
|
|
+ "." + accountNodeName // node name for the account id
|
|
+ "." + ProtocolProviderFactory.ACCOUNT_UID, // propname
|
|
accountID.getAccountUniqueID()); // value
|
|
}
|
|
|
|
// store the rest of the properties
|
|
Map<String, String> accountProperties = accountID.getAccountProperties();
|
|
|
|
for (Map.Entry<String, String> entry : accountProperties.entrySet())
|
|
{
|
|
String property = entry.getKey();
|
|
String value = entry.getValue();
|
|
String secureStorePrefix = null;
|
|
|
|
// If the property is a password, store it securely.
|
|
if (property.equals(ProtocolProviderFactory.PASSWORD))
|
|
{
|
|
String accountPrefix = factoryPackage + "." + accountNodeName;
|
|
secureStorePrefix = accountPrefix;
|
|
}
|
|
else if(property.endsWith("." + ProtocolProviderFactory.PASSWORD))
|
|
{
|
|
secureStorePrefix = factoryPackage + "." + accountNodeName +
|
|
"." + property.substring(0, property.lastIndexOf("."));
|
|
}
|
|
|
|
if(secureStorePrefix != null)
|
|
{
|
|
CredentialsStorageService credentialsStorage
|
|
= ServiceUtils.getService(
|
|
bundleContext,
|
|
CredentialsStorageService.class);
|
|
|
|
// encrypt and store
|
|
if ((value != null)
|
|
&& (value.length() != 0)
|
|
&& !credentialsStorage.storePassword(
|
|
secureStorePrefix,
|
|
value))
|
|
{
|
|
throw
|
|
new OperationFailedException(
|
|
"CredentialsStorageService failed to"
|
|
+ " storePassword",
|
|
OperationFailedException.GENERAL_ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
configurationProperties.put(
|
|
factoryPackage // prefix
|
|
+ "." + accountNodeName // a unique node name for the account id
|
|
+ "." + property, // propname
|
|
value); // value
|
|
}
|
|
}
|
|
|
|
// clear the password if missing property, modification can request
|
|
// password delete
|
|
if(!accountProperties.containsKey(ProtocolProviderFactory.PASSWORD))
|
|
{
|
|
CredentialsStorageService credentialsStorage
|
|
= ServiceUtils.getService(
|
|
bundleContext,
|
|
CredentialsStorageService.class);
|
|
credentialsStorage.removePassword(
|
|
factoryPackage + "." + accountNodeName);
|
|
}
|
|
|
|
if (configurationProperties.size() > 0)
|
|
configurationService.setProperties(configurationProperties);
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Stored account for id " + accountID.getAccountUniqueID()
|
|
+ " for package " + factoryPackage);
|
|
}
|
|
|
|
/**
|
|
* Gets account node name under which account configuration properties are
|
|
* stored.
|
|
*
|
|
* @param factory account's protocol provider factory
|
|
* @param accountID account for which the prefix will be returned
|
|
* @return configuration prefix for given <tt>accountID</tt> if exists or
|
|
* <tt>null</tt> otherwise
|
|
*/
|
|
public String getAccountNodeName( ProtocolProviderFactory factory,
|
|
AccountID accountID )
|
|
{
|
|
ConfigurationService configurationService
|
|
= ProtocolProviderActivator.getConfigurationService();
|
|
String factoryPackage = getFactoryImplPackageName(factory);
|
|
|
|
// First check if such accountID already exists in the configuration.
|
|
List<String> storedAccounts =
|
|
configurationService.getPropertyNamesByPrefix(factoryPackage, true);
|
|
String accountUID = accountID.getAccountUniqueID();
|
|
String accountNodeName = null;
|
|
|
|
for (Iterator<String> storedAccountIter = storedAccounts.iterator();
|
|
storedAccountIter.hasNext();)
|
|
{
|
|
String storedAccount = storedAccountIter.next();
|
|
|
|
// If the property is not related to an account we skip it.
|
|
int dotIndex = storedAccount.lastIndexOf(".");
|
|
if (!storedAccount.substring(dotIndex + 1)
|
|
.startsWith(ACCOUNT_UID_PREFIX))
|
|
continue;
|
|
|
|
String storedAccountUID
|
|
= configurationService.getString(
|
|
storedAccount + "." + ProtocolProviderFactory.ACCOUNT_UID);
|
|
|
|
if (storedAccountUID.equals(accountUID))
|
|
accountNodeName = configurationService.getString(storedAccount);
|
|
}
|
|
return accountNodeName;
|
|
}
|
|
|
|
/**
|
|
* Removes the account with <tt>accountID</tt> from the set of accounts
|
|
* that are persistently stored inside the configuration service.
|
|
*
|
|
* @param factory the <tt>ProtocolProviderFactory</tt> which created the
|
|
* account to be stored
|
|
* @param accountID the AccountID of the account to remove.
|
|
* @return true if an account has been removed and false otherwise.
|
|
*/
|
|
public boolean removeStoredAccount(ProtocolProviderFactory factory,
|
|
AccountID accountID)
|
|
{
|
|
synchronized (storedAccounts)
|
|
{
|
|
if (storedAccounts.contains(accountID))
|
|
storedAccounts.remove(accountID);
|
|
}
|
|
|
|
/*
|
|
* We're already doing it in #unloadAccount(AccountID) - we're figuring
|
|
* out the ProtocolProviderFactory by the AccountID.
|
|
*/
|
|
if (factory == null)
|
|
{
|
|
factory
|
|
= ProtocolProviderActivator.getProtocolProviderFactory(
|
|
accountID.getProtocolName());
|
|
}
|
|
|
|
String factoryPackage = getFactoryImplPackageName(factory);
|
|
|
|
// remove the stored password explicitly using credentials service
|
|
CredentialsStorageService credentialsStorage
|
|
= ServiceUtils.getService(
|
|
bundleContext,
|
|
CredentialsStorageService.class);
|
|
String accountPrefix =
|
|
ProtocolProviderFactory.findAccountPrefix(bundleContext, accountID,
|
|
factoryPackage);
|
|
|
|
credentialsStorage.removePassword(accountPrefix);
|
|
|
|
ConfigurationService configurationService
|
|
= ServiceUtils.getService(
|
|
bundleContext,
|
|
ConfigurationService.class);
|
|
//first retrieve all accounts that we've registered
|
|
List<String> storedAccounts
|
|
= configurationService.getPropertyNamesByPrefix(
|
|
factoryPackage, true);
|
|
|
|
//find an account with the corresponding id.
|
|
for (String accountRootPropertyName : storedAccounts)
|
|
{
|
|
//unregister the account in the configuration service.
|
|
//all the properties must have been registered in the following
|
|
//hierarchy:
|
|
//net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
|
|
String accountUID = configurationService.getString(
|
|
accountRootPropertyName //node id
|
|
+ "." + ProtocolProviderFactory.ACCOUNT_UID); // propname
|
|
|
|
if (accountUID.equals(accountID.getAccountUniqueID()))
|
|
{
|
|
//retrieve the names of all properties registered for the
|
|
//current account.
|
|
List<String> accountPropertyNames
|
|
= configurationService.getPropertyNamesByPrefix(
|
|
accountRootPropertyName, false);
|
|
|
|
//set all account properties to null in order to remove them.
|
|
for (String propName : accountPropertyNames)
|
|
configurationService.setProperty(propName, null);
|
|
|
|
//and now remove the parent too.
|
|
configurationService.setProperty(accountRootPropertyName, null);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Removes all accounts which have been persistently stored.
|
|
*
|
|
* @see #removeStoredAccount(ProtocolProviderFactory, AccountID)
|
|
*/
|
|
public void removeStoredAccounts()
|
|
{
|
|
synchronized (loadStoredAccountsQueue)
|
|
{
|
|
/*
|
|
* Wait for the Thread which loads the stored account to complete so
|
|
* that we can be sure later on that it will not load a stored
|
|
* account while we are deleting it or another one for that matter.
|
|
*/
|
|
boolean interrupted = false;
|
|
|
|
while (loadStoredAccountsThread != null)
|
|
try
|
|
{
|
|
loadStoredAccountsQueue.wait(LOAD_STORED_ACCOUNTS_TIMEOUT);
|
|
}
|
|
catch (InterruptedException ie)
|
|
{
|
|
interrupted = true;
|
|
}
|
|
if (interrupted)
|
|
Thread.currentThread().interrupt();
|
|
|
|
synchronized (this.storedAccounts)
|
|
{
|
|
AccountID[] storedAccounts
|
|
= this.storedAccounts.toArray(
|
|
new AccountID[this.storedAccounts.size()]);
|
|
|
|
for (AccountID storedAccount : storedAccounts)
|
|
{
|
|
ProtocolProviderFactory ppf
|
|
= ProtocolProviderActivator.getProtocolProviderFactory(
|
|
storedAccount.getProtocolName());
|
|
|
|
if (ppf != null)
|
|
ppf.uninstallAccount(storedAccount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns an <tt>Iterator</tt> over a list of all stored
|
|
* <tt>AccountID</tt>s. The list of stored accounts include all registered
|
|
* accounts and all disabled accounts. In other words in this list we could
|
|
* find accounts that aren't loaded.
|
|
* <p>
|
|
* In order to check if an account is already loaded please use the
|
|
* #isAccountLoaded(AccountID accountID) method. To load an account use the
|
|
* #loadAccount(AccountID accountID) method.
|
|
*
|
|
* @return an <tt>Iterator</tt> over a list of all stored
|
|
* <tt>AccountID</tt>s
|
|
*/
|
|
public Collection<AccountID> getStoredAccounts()
|
|
{
|
|
synchronized (storedAccounts)
|
|
{
|
|
return new Vector<AccountID>(storedAccounts);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads the account corresponding to the given <tt>AccountID</tt>. An
|
|
* account is loaded when its <tt>ProtocolProviderService</tt> is registered
|
|
* in the bundle context. This method is meant to load the account through
|
|
* the corresponding <tt>ProtocolProviderFactory</tt>.
|
|
*
|
|
* @param accountID the identifier of the account to load
|
|
* @throws OperationFailedException if anything goes wrong while loading the
|
|
* account corresponding to the specified <tt>accountID</tt>
|
|
*/
|
|
public void loadAccount(AccountID accountID)
|
|
throws OperationFailedException
|
|
{
|
|
// If the account with the given id is already loaded we have nothing
|
|
// to do here.
|
|
if (isAccountLoaded(accountID))
|
|
return;
|
|
|
|
ProtocolProviderFactory providerFactory
|
|
= ProtocolProviderActivator.getProtocolProviderFactory(
|
|
accountID.getProtocolName());
|
|
|
|
if(providerFactory.loadAccount(accountID))
|
|
{
|
|
accountID.putAccountProperty(
|
|
ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
|
|
String.valueOf(false));
|
|
// Finally store the modified properties.
|
|
storeAccount(providerFactory, accountID);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unloads the account corresponding to the given <tt>AccountID</tt>. An
|
|
* account is unloaded when its <tt>ProtocolProviderService</tt> is
|
|
* unregistered in the bundle context. This method is meant to unload the
|
|
* account through the corresponding <tt>ProtocolProviderFactory</tt>.
|
|
*
|
|
* @param accountID the identifier of the account to load
|
|
* @throws OperationFailedException if anything goes wrong while unloading
|
|
* the account corresponding to the specified <tt>accountID</tt>
|
|
*/
|
|
public void unloadAccount(AccountID accountID)
|
|
throws OperationFailedException
|
|
{
|
|
// If the account with the given id is already unloaded we have nothing
|
|
// to do here.
|
|
if (!isAccountLoaded(accountID))
|
|
return;
|
|
|
|
ProtocolProviderFactory providerFactory
|
|
= ProtocolProviderActivator.getProtocolProviderFactory(
|
|
accountID.getProtocolName());
|
|
|
|
// Obtain the protocol provider.
|
|
ServiceReference serRef
|
|
= providerFactory.getProviderForAccount(accountID);
|
|
|
|
// If there's no such provider we have nothing to do here.
|
|
if (serRef == null)
|
|
return;
|
|
|
|
ProtocolProviderService protocolProvider =
|
|
(ProtocolProviderService) bundleContext.getService(serRef);
|
|
|
|
// Set the account icon path for unloaded accounts.
|
|
String iconPathProperty = accountID.getAccountPropertyString(
|
|
ProtocolProviderFactory.ACCOUNT_ICON_PATH);
|
|
|
|
if (iconPathProperty == null)
|
|
{
|
|
accountID.putAccountProperty(
|
|
ProtocolProviderFactory.ACCOUNT_ICON_PATH,
|
|
protocolProvider.getProtocolIcon()
|
|
.getIconPath(ProtocolIcon.ICON_SIZE_32x32));
|
|
}
|
|
|
|
accountID.putAccountProperty(
|
|
ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
|
|
String.valueOf(true));
|
|
|
|
if (!providerFactory.unloadAccount(accountID))
|
|
{
|
|
accountID.putAccountProperty(
|
|
ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
|
|
String.valueOf(false));
|
|
}
|
|
// Finally store the modified properties.
|
|
storeAccount(providerFactory, accountID);
|
|
}
|
|
|
|
/**
|
|
* Checks if the account corresponding to the given <tt>accountID</tt> is
|
|
* loaded. An account is loaded if its <tt>ProtocolProviderService</tt> is
|
|
* registered in the bundle context. By default all accounts are loaded.
|
|
* However the user could manually unload an account, which would be
|
|
* unregistered from the bundle context, but would remain in the
|
|
* configuration file.
|
|
*
|
|
* @param accountID the identifier of the account to load
|
|
* @return <tt>true</tt> to indicate that the account with the given
|
|
* <tt>accountID</tt> is loaded, <tt>false</tt> - otherwise
|
|
*/
|
|
public boolean isAccountLoaded(AccountID accountID)
|
|
{
|
|
return storedAccounts.contains(accountID) && accountID.isEnabled();
|
|
}
|
|
|
|
private String stripPackagePrefix(String property)
|
|
{
|
|
int packageEndIndex = property.lastIndexOf('.');
|
|
|
|
if (packageEndIndex != -1)
|
|
property = property.substring(packageEndIndex + 1);
|
|
return property;
|
|
}
|
|
}
|