/*
* SIP Communicator, 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.lang.reflect.*;
import java.util.*;
import net.java.sip.communicator.service.configuration.*;
import net.java.sip.communicator.service.credentialsstorage.*;
import net.java.sip.communicator.util.*;
import org.osgi.framework.*;
/**
* The ProtocolProviderFactory is what actually creates instances of a
* ProtocolProviderService implementation. A provider factory would register,
* persistently store, and remove when necessary, ProtocolProviders. The way
* things are in the SIP Communicator, a user account is represented (in a 1:1
* relationship) by an AccountID and a ProtocolProvider. In other words - one
* would have as many protocol providers installed in a given moment as they
* would user account registered through the various services.
*
* @author Emil Ivov
* @author Lubomir Marinov
*/
public abstract class ProtocolProviderFactory
{
/**
* The Logger used by the ProtocolProviderFactory class
* and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(ProtocolProviderFactory.class);
/**
* Then name of a property which represents a password.
*/
public static final String PASSWORD = "PASSWORD";
/**
* The name of a property representing the name of the protocol for an
* ProtocolProviderFactory.
*/
public static final String PROTOCOL = "PROTOCOL_NAME";
/**
* The name of a property representing the path to protocol icons.
*/
public static final String PROTOCOL_ICON_PATH = "PROTOCOL_ICON_PATH";
/**
* The name of a property representing the path to the account icon to
* be used in the user interface, when the protocol provider service is not
* available.
*/
public static final String ACCOUNT_ICON_PATH = "ACCOUNT_ICON_PATH";
/**
* The name of a property which represents the AccountID of a
* ProtocolProvider and that, together with a password is used to login
* on the protocol network..
*/
public static final String USER_ID = "USER_ID";
/**
* The name that should be displayed to others when we are calling or
* writing them.
*/
public static final String DISPLAY_NAME = "DISPLAY_NAME";
/**
* The name of the property under which we store protocol AccountID-s.
*/
public static final String ACCOUNT_UID = "ACCOUNT_UID";
/**
* The name of the property under which we store protocol the address of
* a protocol centric entity (any protocol server).
*/
public static final String SERVER_ADDRESS = "SERVER_ADDRESS";
/**
* The name of the property under which we store the number of the port
* where the server stored against the SERVER_ADDRESS property is expecting
* connections to be made via this protocol.
*/
public static final String SERVER_PORT = "SERVER_PORT";
/**
* The name of the property under which we store the name of the transport
* protocol that needs to be used to access the server.
*/
public static final String SERVER_TRANSPORT = "SERVER_TRANSPORT";
/**
* The name of the property under which we store protocol the address of
* a protocol proxy.
*/
public static final String PROXY_ADDRESS = "PROXY_ADDRESS";
/**
* The name of the property under which we store the number of the port
* where the proxy stored against the PROXY_ADDRESS property is expecting
* connections to be made via this protocol.
*/
public static final String PROXY_PORT = "PROXY_PORT";
/**
* The name of the property which defines whether proxy is auto configured
* by the protocol by using known methods such as specific DNS queries.
*/
public static final String PROXY_AUTO_CONFIG = "PROXY_AUTO_CONFIG";
/**
* The property indicating the preferred UDP and TCP
* port to bind to for clear communications.
*/
public static final String PREFERRED_CLEAR_PORT_PROPERTY_NAME
= "net.java.sip.communicator.SIP_PREFERRED_CLEAR_PORT";
/**
* The property indicating the preferred TLS (TCP)
* port to bind to for secure communications.
*/
public static final String PREFERRED_SECURE_PORT_PROPERTY_NAME
= "net.java.sip.communicator.SIP_PREFERRED_SECURE_PORT";
/**
* The name of the property under which we store the the type of the proxy
* stored against the PROXY_ADDRESS property. Exact type values depend on
* protocols and among them are socks4, socks5, http and possibly others.
*/
public static final String PROXY_TYPE = "PROXY_TYPE";
/**
* The name of the property under which we store the the username for the
* proxy stored against the PROXY_ADDRESS property.
*/
public static final String PROXY_USERNAME = "PROXY_USERNAME";
/**
* The name of the property under which we store the the authorization name
* for the proxy stored against the PROXY_ADDRESS property.
*/
public static final String AUTHORIZATION_NAME = "AUTHORIZATION_NAME";
/**
* The name of the property under which we store the password for the proxy
* stored against the PROXY_ADDRESS property.
*/
public static final String PROXY_PASSWORD = "PROXY_PASSWORD";
/**
* The name of the property under which we store the name of the transport
* protocol that needs to be used to access the proxy.
*/
public static final String PROXY_TRANSPORT = "PROXY_TRANSPORT";
/**
* The name of the property under which we store the user preference for a
* transport protocol to use (i.e. tcp or udp).
*/
public static final String PREFERRED_TRANSPORT = "PREFERRED_TRANSPORT";
/**
* The name of the property under which we store resources such as the
* jabber resource property.
*/
public static final String RESOURCE = "RESOURCE";
/**
* The name of the property under which we store resource priority.
*/
public static final String RESOURCE_PRIORITY = "RESOURCE_PRIORITY";
/**
* The name of the property which defines that the call is encrypted by
* default
*/
public static final String DEFAULT_ENCRYPTION = "DEFAULT_ENCRYPTION";
/**
* The name of the property which defines if to include the ZRTP attribute
* to SIP/SDP
*/
public static final String DEFAULT_SIPZRTP_ATTRIBUTE =
"DEFAULT_SIPZRTP_ATTRIBUTE";
/**
* The name of the property under which we store the boolean value
* indicating if the user name should be automatically changed if the
* specified name already exists. This property is meant to be used by IRC
* implementations.
*/
public static final String AUTO_CHANGE_USER_NAME = "AUTO_CHANGE_USER_NAME";
/**
* The name of the property under which we store the boolean value
* indicating if a password is required. Initially this property is meant to
* be used by IRC implementations.
*/
public static final String NO_PASSWORD_REQUIRED = "NO_PASSWORD_REQUIRED";
/**
* The name of the property under which we store if the presence is enabled.
*/
public static final String IS_PRESENCE_ENABLED = "IS_PRESENCE_ENABLED";
/**
* The name of the property under which we store if the p2p mode for SIMPLE
* should be forced.
*/
public static final String FORCE_P2P_MODE = "FORCE_P2P_MODE";
/**
* The name of the property under which we store the offline contact polling
* period for SIMPLE.
*/
public static final String POLLING_PERIOD = "POLLING_PERIOD";
/**
* The name of the property under which we store the chosen default
* subscription expiration value for SIMPLE.
*/
public static final String SUBSCRIPTION_EXPIRATION
= "SUBSCRIPTION_EXPIRATION";
/**
* Indicates if the server address has been validated.
*/
public static final String SERVER_ADDRESS_VALIDATED
= "SERVER_ADDRESS_VALIDATED";
/**
* Indicates if the server settings are over
*/
public static final String IS_SERVER_OVERRIDDEN
= "IS_SERVER_OVERRIDDEN";
/**
* Indicates if the proxy address has been validated.
*/
public static final String PROXY_ADDRESS_VALIDATED
= "PROXY_ADDRESS_VALIDATED";
/**
* Indicates the search strategy chosen for the DICT protocole.
*/
public static final String STRATEGY = "STRATEGY";
/**
* Indicates a protocol that would not be shown in the user interface as an
* account.
*/
public static final String IS_PROTOCOL_HIDDEN = "IS_PROTOCOL_HIDDEN";
/**
* The name of the property that would indicate if a given account is
* currently enabled or disabled.
*/
public static final String IS_ACCOUNT_DISABLED = "IS_ACCOUNT_DISABLED";
/**
* Indicates if ICE should be used.
*/
public static final String IS_USE_ICE = "ICE_ENABLED";
/**
* Indicates if STUN server should be automatically discovered.
*/
public static final String AUTO_DISCOVER_STUN = "AUTO_DISCOVER_STUN";
/**
* Indicates if default STUN server would be used if no other STUN/TURN
* server are available.
*/
public static final String USE_DEFAULT_STUN_SERVER =
"USE_DEFAULT_STUN_SERVER";
/**
* The property name prefix for all stun server properties. We generally use
* this prefix in conjunction with an index which is how we store multiple
* servers.
*/
public static final String STUN_PREFIX = "STUN";
/**
* The base property name for address of additional STUN servers specified.
*/
public static final String STUN_ADDRESS = "ADDRESS";
/**
* The base property name for port of additional STUN servers specified.
*/
public static final String STUN_PORT = "PORT";
/**
* The base property name for username of additional STUN servers specified.
*/
public static final String STUN_USERNAME = "USERNAME";
/**
* The base property name for password of additional STUN servers specified.
*/
public static final String STUN_PASSWORD = "PASSWORD";
/**
* The base property name for the turn supported property of additional
* STUN servers specified.
*/
public static final String STUN_IS_TURN_SUPPORTED = "IS_TURN_SUPPORTED";
/**
* Indicates if JingleNodes should be used with ICE.
*/
public static final String IS_USE_JINGLE_NODES = "JINGLE_NODES_ENABLED";
/**
* Indicates if JingleNodes should be used with ICE.
*/
public static final String AUTO_DISCOVER_JINGLE_NODES
= "AUTO_DISCOVER_JINGLE_NODES";
/**
* Address used to reach voicemail box, by services able to
* subscribe for voicemail new messages notifications.
*/
public static final String VOICEMAIL_URI = "VOICEMAIL_URI";
/**
* The BundleContext
containing (or to contain) the service
* registration of this factory.
*/
private final BundleContext bundleContext;
/**
* The name of the protocol this factory registers its
* ProtocolProviderService
s with and to be placed in the
* properties of the accounts created by this factory.
*/
private final String protocolName;
/**
* The table that we store our accounts in.
*
* TODO Synchronize the access to the field which may in turn be better * achieved by also hiding it from protected into private access. *
*/ protected final HashtableBundleContext
containing (or to contain) the
* service registration of this factory.
*
* @return the BundleContext
containing (or to contain) the
* service registration of this factory
*/
public BundleContext getBundleContext()
{
return bundleContext;
}
/**
* Initializes and creates an account corresponding to the specified
* accountProperties and registers the resulting ProtocolProvider in the
* context BundleContext parameter. Note that account
* registration is persistent and accounts that are registered during
* a particular sip-communicator session would be automatically reloaded
* during all following sessions until they are removed through the
* removeAccount method.
*
* @param userID the user identifier uniquely representing the newly
* created account within the protocol namespace.
* @param accountProperties a set of protocol (or implementation) specific
* properties defining the new account.
* @return the AccountID of the newly created account.
* @throws java.lang.IllegalArgumentException if userID does not correspond
* to an identifier in the context of the underlying protocol or if
* accountProperties does not contain a complete set of account installation
* properties.
* @throws java.lang.IllegalStateException if the account has already been
* installed.
* @throws java.lang.NullPointerException if any of the arguments is null.
*/
public abstract AccountID installAccount(String userID,
Map* In order to store all account properties, the method would create an * entry in the configuration service corresponding (beginning with) the * sourceFactory's package name and add to it a unique identifier * (e.g. the current miliseconds.) *
* * @param accountID the AccountID corresponding to the account that we would * like to store. */ protected void storeAccount(AccountID accountID) { this.storeAccount(accountID, true); } /** * The method stores the specified account in the configuration service * under the package name of the source factory. The restore and remove * account methods are to be used to obtain access to and control the stored * accounts. ** In order to store all account properties, the method would create an * entry in the configuration service corresponding (beginning with) the * sourceFactory's package name and add to it a unique identifier * (e.g. the current miliseconds.) *
* * @param accountID the AccountID corresponding to the account that we would * like to store. * @param isModification if false there must be no such already * loaded account, it true ist modification of an existing account. * Usually we use this method with false in method installAccount * and with true or the overridden method in method * modifyAccount. */ protected void storeAccount(AccountID accountID, boolean isModification) { if(!isModification && getAccountManager().getStoredAccounts().contains(accountID)) { throw new IllegalStateException( "An account for id " + accountID.getUserID() + " was already loaded!"); } try { getAccountManager().storeAccount(this, accountID); } catch (OperationFailedException ofex) { throw new UndeclaredThrowableException(ofex); } } /** * Saves the password for the specified account after scrambling it a bit so * that it is not visible from first sight. (The method remains highly * insecure). * * @param accountID the AccountID for the account whose password we're * storing * @param password the password itself * * @throws IllegalArgumentException if no account corresponding to *accountID
has been previously stored
*/
public void storePassword(AccountID accountID, String password)
throws IllegalArgumentException
{
try
{
storePassword(getBundleContext(), accountID, password);
}
catch (OperationFailedException ofex)
{
throw new UndeclaredThrowableException(ofex);
}
}
/**
* Saves the password for the specified account after scrambling it a bit
* so that it is not visible from first sight (Method remains highly
* insecure).
* * TODO Delegate the implementation to {@link AccountManager} because it * knows the format in which the password (among the other account * properties) is to be saved. *
* * @param bundleContext a currently valid bundle context. * @param accountID the AccountID of the account whose password is * to be stored * @param password the password to be stored * * @throws IllegalArgumentException if no account corresponding to * accountID has been previously stored. * @throws OperationFailedException if anything goes wrong while storing the * specified password */ protected void storePassword(BundleContext bundleContext, AccountID accountID, String password) throws IllegalArgumentException, OperationFailedException { String accountPrefix = findAccountPrefix( bundleContext, accountID, getFactoryImplPackageName()); if (accountPrefix == null) { throw new IllegalArgumentException( "No previous records found for account ID: " + accountID.getAccountUniqueID() + " in package" + getFactoryImplPackageName()); } CredentialsStorageService credentialsStorage = ServiceUtils.getService( bundleContext, CredentialsStorageService.class); if (!credentialsStorage.storePassword(accountPrefix, password)) { throw new OperationFailedException( "CredentialsStorageService failed to storePassword", OperationFailedException.GENERAL_ERROR); } } /** * Returns the password last saved for the specified account. * * @param accountID the AccountID for the account whose password we're * looking for * * @return a String containing the password for the specified accountID */ public String loadPassword(AccountID accountID) { return loadPassword(getBundleContext(), accountID); } /** * Returns the password last saved for the specified account. ** TODO Delegate the implementation to {@link AccountManager} because it * knows the format in which the password (among the other account * properties) was saved. *
* * @param bundleContext a currently valid bundle context. * @param accountID the AccountID for the account whose password we're * looking for.. * * @return a String containing the password for the specified accountID. */ protected String loadPassword(BundleContext bundleContext, AccountID accountID) { String accountPrefix = findAccountPrefix( bundleContext, accountID, getFactoryImplPackageName()); if (accountPrefix == null) return null; CredentialsStorageService credentialsStorage = ServiceUtils.getService( bundleContext, CredentialsStorageService.class); return credentialsStorage.loadPassword(accountPrefix); } /** * Initializes and creates an account corresponding to the specified * accountProperties and registers the resulting ProtocolProvider in the * context BundleContext parameter. This method has a persistent * effect. Once created the resulting account will remain installed until * removed through the uninstallAccount method. * * @param accountProperties a set of protocol (or implementation) specific * properties defining the new account. * @return the AccountID of the newly loaded account */ public AccountID loadAccount(MapAccountID
instance with a specific user ID to
* represent a given set of account properties.
*
* The method is a pure factory allowing implementers to specify the runtime
* type of the created AccountID
and customize the instance.
* The returned AccountID
will later be associated with a
* ProtocolProviderService
by the caller (e.g. using
* {@link #createService(String, AccountID)}).
*
AccountID
instance with the specified user ID
* representing the given set of account properties
*/
protected abstract AccountID createAccountID(
String userID, MapProtocolProviderService
s with and to be placed in the
* properties of the accounts created by this factory.
*
* @return the name of the protocol this factory registers its
* ProtocolProviderService
s with and to be placed in
* the properties of the accounts created by this factory
*/
public String getProtocolName()
{
return protocolName;
}
/**
* Initializes a new ProtocolProviderService
instance with a
* specific user ID to represent a specific AccountID
.
*
* The method is a pure factory allowing implementers to specify the runtime
* type of the created ProtocolProviderService
and customize
* the instance. The caller will later register the returned service with
* the BundleContext
of this factory.
*
AccountID
to be represented by the new
* instance
* @return a new ProtocolProviderService
instance with the
* specific user ID representing the specified
* AccountID
*/
protected abstract ProtocolProviderService createService(String userID,
AccountID accountID);
/**
* Removes the account with accountID from the set of accounts
* that are persistently stored inside the configuration service.
*
* @param accountID the AccountID of the account to remove.
*
* @return true if an account has been removed and false otherwise.
*/
protected boolean removeStoredAccount(AccountID accountID)
{
return getAccountManager().removeStoredAccount(this, accountID);
}
/**
* Returns the prefix for all persistently stored properties of the account
* with the specified id.
* @param bundleContext a currently valid bundle context.
* @param accountID the AccountID of the account whose properties we're
* looking for.
* @param sourcePackageName a String containing the package name of the
* concrete factory class that extends us.
* @return a String indicating the ConfigurationService property name
* prefix under which all account properties are stored or null if no
* account corresponding to the specified id was found.
*/
public static String findAccountPrefix(BundleContext bundleContext,
AccountID accountID,
String sourcePackageName)
{
ServiceReference confReference
= bundleContext.getServiceReference(
ConfigurationService.class.getName());
ConfigurationService configurationService
= (ConfigurationService) bundleContext.getService(confReference);
//first retrieve all accounts that we've registered
ListProtocolProviderService
representing an
* account registered with this factory.
*
* @param registeredAccount the ServiceRegistration
of the
* ProtocolProviderService
representing an account
* registered with this factory
*/
protected void stop(ServiceRegistration registeredAccount)
{
ProtocolProviderService protocolProviderService =
(ProtocolProviderService) getBundleContext().getService(
registeredAccount.getReference());
protocolProviderService.shutdown();
}
/**
* Get the AccountManager of the protocol.
*
* @return AccountManager of the protocol
*/
private AccountManager getAccountManager()
{
BundleContext bundleContext = getBundleContext();
ServiceReference serviceReference =
bundleContext.getServiceReference(AccountManager.class.getName());
return (AccountManager) bundleContext.getService(serviceReference);
}
}