/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.service.protocol; import java.util.*; import net.java.sip.communicator.util.*; import net.java.sip.communicator.service.credentialsstorage.*; import org.jitsi.service.neomedia.*; import org.osgi.framework.*; /** * The AccountID is an account identifier that, uniquely represents a specific * user account over a specific protocol. The class needs to be extended by * every protocol implementation because of its protected * constructor. The reason why this constructor is protected is mostly avoiding * confusion and letting people (using the protocol provider service) believe * that they are the ones who are supposed to instantiate the accountid class. * <p> * Every instance of the <tt>ProtocolProviderService</tt>, created through the * ProtocolProviderFactory is assigned an AccountID instance, that uniquely * represents it and whose string representation (obtained through the * getAccountUID() method) can be used for identification of persistently stored * account details. * <p> * Account id's are guaranteed to be different for different accounts and in the * same time are bound to be equal for multiple installations of the same * account. * * @author Emil Ivov * @author Lubomir Marinov * @author Pawel Domas */ public abstract class AccountID { /** * The <tt>Logger</tt> used by the <tt>AccountID</tt> class and its * instances for logging output. */ private static final Logger logger = Logger.getLogger(AccountID.class); /** * The default properties key prefix used in lib/jitsi-defaults.properties */ protected static final String DEFAULTS_PREFIX = "net.java.sip.communicator.service.protocol."; /** * The protocol display name. In the case of overridden protocol name this * would be the new name. */ private final String protocolDisplayName; /** * The real protocol name. */ private final String protocolName; /** * Allows a specific set of account properties to override a given default * protocol name (e.g. account registration wizards which want to present a * well-known protocol name associated with the account that is different * from the name of the effective protocol). * <p> * Note: The logic of the SIP protocol implementation at the time of this * writing modifies <tt>accountProperties</tt> to contain the default * protocol name if an override hasn't been defined. Since the desire is to * enable all account registration wizards to override the protocol name, * the current implementation places the specified * <tt>defaultProtocolName</tt> in a similar fashion. * </p> * * @param accountProperties a Map containing any other protocol and * implementation specific account initialization properties * @param defaultProtocolName the protocol name to be used in case * <tt>accountProperties</tt> doesn't provide an overriding value * @return the protocol name */ private static final String getOverriddenProtocolName( Map<String, String> accountProperties, String defaultProtocolName) { String key = ProtocolProviderFactory.PROTOCOL; String protocolName = accountProperties.get(key); if ((protocolName == null) && (defaultProtocolName != null)) { protocolName = defaultProtocolName; accountProperties.put(key, protocolName); } return protocolName; } /** * Contains all implementation specific properties that define the account. * The exact names of the keys are protocol (and sometimes implementation) * specific. * Currently, only String property keys and values will get properly stored. * If you need something else, please consider converting it through custom * accessors (get/set) in your implementation. */ protected Map<String, String> accountProperties = null; /** * A String uniquely identifying the user for this particular account. */ private final String userID; /** * A String uniquely identifying this account, that can also be used for * storing and unambiguously retrieving details concerning it. */ private final String accountUID; /** * The name of the service that defines the context for this account. */ private final String serviceName; /** * Creates an account id for the specified provider userid and * accountProperties. * If account uid exists in account properties, we are loading the account * and so load its value from there, prevent changing account uid * when server changed (serviceName has changed). * @param userID a String that uniquely identifies the user. * @param accountProperties a Map containing any other protocol and * implementation specific account initialization properties * @param protocolName the name of the protocol implemented by the provider * that this id is meant for. * @param serviceName the name of the service (e.g. iptel.org, jabber.org, * icq.com) that this account is registered with. */ protected AccountID( String userID, Map<String, String> accountProperties, String protocolName, String serviceName) { /* * Allow account registration wizards to override the default protocol * name through accountProperties for the purposes of presenting a * well-known protocol name associated with the account that is * different from the name of the effective protocol. */ this.protocolDisplayName = getOverriddenProtocolName(accountProperties, protocolName); this.protocolName = protocolName; this.userID = userID; this.accountProperties = new HashMap<String, String>(accountProperties); this.serviceName = serviceName; String existingAccountUID = accountProperties.get(ProtocolProviderFactory.ACCOUNT_UID); if(existingAccountUID == null) { //create a unique identifier string this.accountUID = protocolDisplayName + ":" + userID + "@" + ((serviceName == null) ? "" : serviceName); } else { this.accountUID = existingAccountUID; } } /** * Returns the user id associated with this account. * * @return A String identifying the user inside this particular service. */ public String getUserID() { return userID; } /** * Returns a name that can be displayed to the user when referring to this * account. WARNING: This property actually refers to * <code>ACCOUNT_DISPLAY_NAME</code>! * * @return A String identifying the user inside this particular service. */ public String getDisplayName() { // If the ACCOUNT_DISPLAY_NAME property has been set for this account // we'll be using it as a display name. String key = ProtocolProviderFactory.ACCOUNT_DISPLAY_NAME; String accountDisplayName = accountProperties.get(key); if (accountDisplayName != null && accountDisplayName.length() > 0) { return accountDisplayName; } // Otherwise construct a display name. String returnValue = getUserID(); String protocolName = getProtocolDisplayName(); if (protocolName != null && protocolName.trim().length() > 0) returnValue += " (" + protocolName + ")"; return returnValue; } /** * Sets {@link ProtocolProviderFactory#DISPLAY_NAME} property value. * * @param displayName the display name value to set. */ public void setDisplayName(String displayName) { setOrRemoveIfEmpty(ProtocolProviderFactory.DISPLAY_NAME, displayName); } /** * Returns the display name of the protocol. * * @return the display name of the protocol */ public String getProtocolDisplayName() { return protocolDisplayName; } /** * Returns the name of the protocol. * * @return the name of the protocol */ public String getProtocolName() { return protocolName; } /** * Returns a String uniquely identifying this account, guaranteed to remain * the same across multiple installations of the same account and to always * be unique for differing accounts. * @return String */ public String getAccountUniqueID() { return accountUID; } /** * Returns a Map containing protocol and implementation account * initialization properties. * @return a Map containing protocol and implementation account * initialization properties. */ public Map<String, String> getAccountProperties() { return new HashMap<String, String>(accountProperties); } /** * Returns the specific account property. * * @param key property key * @param defaultValue default value if the property does not exist * @return property value corresponding to property key */ public boolean getAccountPropertyBoolean(Object key, boolean defaultValue) { String value = getAccountPropertyString(key); if(value == null) value = getDefaultString(key.toString()); return (value == null) ? defaultValue : Boolean.parseBoolean(value); } /** * Gets the value of a specific property as a signed decimal integer. If the * specified property key is associated with a value in this * <tt>AccountID</tt>, the string representation of the value is parsed into * a signed decimal integer according to the rules of * {@link Integer#parseInt(String)} . If parsing the value as a signed * decimal integer fails or there is no value associated with the specified * property key, <tt>defaultValue</tt> is returned. * * @param key the key of the property to get the value of as a * signed decimal integer * @param defaultValue the value to be returned if parsing the value of the * specified property key as a signed decimal integer fails or there is no * value associated with the specified property key in this * <tt>AccountID</tt> * @return the value of the property with the specified key in this * <tt>AccountID</tt> as a signed decimal integer; <tt>defaultValue</tt> if * parsing the value of the specified property key fails or no value is * associated in this <tt>AccountID</tt> with the specified property name */ public int getAccountPropertyInt(Object key, int defaultValue) { String stringValue = getAccountPropertyString(key); int intValue = defaultValue; if ((stringValue == null) || (stringValue.isEmpty())) { stringValue = getDefaultString(key.toString()); } if ((stringValue != null) && (stringValue.length() > 0)) { try { intValue = Integer.parseInt(stringValue); } catch (NumberFormatException ex) { logger.error("Failed to parse account property " + key + " value " + stringValue + " as an integer", ex); } } return intValue; } /** * Returns the account property string corresponding to the given key. * * @param key the key, corresponding to the property string we're looking * for * @return the account property string corresponding to the given key */ public String getAccountPropertyString(Object key) { return getAccountPropertyString(key, null); } /** * Returns the account property string corresponding to the given key. * * @param key the key, corresponding to the property string we're looking * for * @param defValue the default value returned when given <tt>key</tt> * is not present * @return the account property string corresponding to the given key */ public String getAccountPropertyString(Object key, String defValue) { String value = accountProperties.get(key); if(value == null) value = getDefaultString(key.toString()); return (value == null) ? defValue : value; } /** * Adds a property to the map of properties for this account identifier. * * @param key the key of the property * @param value the property value */ public void putAccountProperty(String key, String value) { accountProperties.put(key, value); } /** * Adds property to the map of properties for this account * identifier. * @param key the key of the property * @param value the property value */ public void putAccountProperty(String key, Object value) { accountProperties.put(key, String.valueOf(value)); } /** * Removes specified account property. * @param key the key to remove. */ public void removeAccountProperty(String key) { accountProperties.remove(key); } /** * Returns a hash code value for the object. This method is * supported for the benefit of hashtables such as those provided by * <tt>java.util.Hashtable</tt>. * <p> * @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.util.Hashtable */ @Override public int hashCode() { return (accountUID == null)? 0 : accountUID.hashCode(); } /** * Indicates whether some other object is "equal to" this account id. * <p> * @param obj the reference object with which to compare. * @return <tt>true</tt> if this object is the same as the obj * argument; <tt>false</tt> otherwise. * @see #hashCode() * @see java.util.Hashtable */ @Override public boolean equals(Object obj) { if (this == obj) return true; return (obj != null) && getClass().isInstance(obj) && userID.equals(((AccountID)obj).userID); } /** * Returns a string representation of this account id (same as calling * getAccountUniqueID()). * * @return a string representation of this account id. */ @Override public String toString() { return getAccountUniqueID(); } /** * Returns the name of the service that defines the context for this * account. Often this name would be an sqdn or even an ipaddress but this * would not always be the case (e.g. p2p providers may return a name that * does not directly correspond to an IP address or host name). * <p> * @return the name of the service that defines the context for this * account. */ public String getService() { return this.serviceName; } /** * Returns a string that could be directly used (or easily converted to) an * address that other users of the protocol can use to communicate with us. * By default this string is set to userid@servicename. Protocol * implementors should override it if they'd need it to respect a different * syntax. * * @return a String in the form of userid@service that other protocol users * should be able to parse into a meaningful address and use it to * communicate with us. */ public String getAccountAddress() { String userID = getUserID(); return (userID.indexOf('@') > 0) ? userID : (userID + "@" + getService()); } /** * Indicates if this account is currently enabled. * @return <tt>true</tt> if this account is enabled, <tt>false</tt> - * otherwise. */ public boolean isEnabled() { return !getAccountPropertyBoolean( ProtocolProviderFactory.IS_ACCOUNT_DISABLED, false); } /** * The address of the server we will use for this account * * @return String */ public String getServerAddress() { return getAccountPropertyString(ProtocolProviderFactory.SERVER_ADDRESS); } /** * Returns the password of the account. * * @return the password of the account. */ public String getPassword() { return getAccountPropertyString(ProtocolProviderFactory.PASSWORD); } /** * Sets the password of the account. * * @param password the password of the account. */ public void setPassword(String password) { setOrRemoveIfEmpty(ProtocolProviderFactory.PASSWORD, password); } /** * The authorization name * * @return String auth name */ public String getAuthorizationName() { return getAccountPropertyString( ProtocolProviderFactory.AUTHORIZATION_NAME); } /** * Sets authorization name. * * @param authName String */ public void setAuthorizationName(String authName) { setOrRemoveIfEmpty( ProtocolProviderFactory.AUTHORIZATION_NAME, authName); } /** * The port on the specified server * * @return int */ public String getServerPort() { return getAccountPropertyString(ProtocolProviderFactory.SERVER_PORT); } /** * Sets the server port. * * @param port int */ public void setServerPort(String port) { setOrRemoveIfEmpty(ProtocolProviderFactory.SERVER_PORT, port); } /** * Sets the server * * @param serverAddress String */ public void setServerAddress(String serverAddress) { setOrRemoveIfEmpty(ProtocolProviderFactory.SERVER_ADDRESS, serverAddress); } /** * Returns <tt>true</tt> if server was overriden. * @return <tt>true</tt> if server was overriden. */ public boolean isServerOverridden() { return getAccountPropertyBoolean( ProtocolProviderFactory.IS_SERVER_OVERRIDDEN, false); } /** * Sets <tt>isServerOverridden</tt> property. * @param isServerOverridden indicates if the server is overridden */ public void setServerOverridden(boolean isServerOverridden) { putAccountProperty( ProtocolProviderFactory.IS_SERVER_OVERRIDDEN, isServerOverridden); } /** * Returns the protocol icon path stored under * {@link ProtocolProviderFactory#PROTOCOL_ICON_PATH} key. * * @return the protocol icon path. */ public String getProtocolIconPath() { return getAccountPropertyString( ProtocolProviderFactory.PROTOCOL_ICON_PATH); } /** * Sets the protocl icon path that will be held under * {@link ProtocolProviderFactory#PROTOCOL_ICON_PATH} key. * * @param iconPath a path to the protocol icon to set. */ public void setProtocolIconPath(String iconPath) { putAccountProperty( ProtocolProviderFactory.PROTOCOL_ICON_PATH, iconPath); } /** * Returns the protocol icon path stored under * {@link ProtocolProviderFactory#ACCOUNT_ICON_PATH} key. * * @return the protocol icon path. */ public String getAccountIconPath() { return getAccountPropertyString( ProtocolProviderFactory.ACCOUNT_ICON_PATH); } /** * Sets the account icon path that will be held under * {@link ProtocolProviderFactory#ACCOUNT_ICON_PATH} key. * * @param iconPath a path to the account icon to set. */ public void setAccountIconPath(String iconPath) { putAccountProperty( ProtocolProviderFactory.ACCOUNT_ICON_PATH, iconPath); } /** * Returns the DTMF method. * * @return the DTMF method. */ public String getDTMFMethod() { return getAccountPropertyString(ProtocolProviderFactory.DTMF_METHOD); } /** * Sets the DTMF method. * * @param dtmfMethod the DTMF method to set */ public void setDTMFMethod(String dtmfMethod) { putAccountProperty(ProtocolProviderFactory.DTMF_METHOD, dtmfMethod); } /** * Returns the minimal DTMF tone duration. * * @return The minimal DTMF tone duration. */ public String getDtmfMinimalToneDuration() { return getAccountPropertyString( ProtocolProviderFactory.DTMF_MINIMAL_TONE_DURATION); } /** * Sets the minimal DTMF tone duration. * * @param dtmfMinimalToneDuration The minimal DTMF tone duration to set. */ public void setDtmfMinimalToneDuration(String dtmfMinimalToneDuration) { putAccountProperty( ProtocolProviderFactory.DTMF_MINIMAL_TONE_DURATION, dtmfMinimalToneDuration ); } /** * Gets the ID of the client certificate configuration. * @return the ID of the client certificate configuration. */ public String getTlsClientCertificate() { return getAccountPropertyString( ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE); } /** * Sets the ID of the client certificate configuration. * @param id the client certificate configuration template ID. */ public void setTlsClientCertificate(String id) { setOrRemoveIfEmpty(ProtocolProviderFactory.CLIENT_TLS_CERTIFICATE, id); } /** * Checks if the account is hidden. * @return <tt>true</tt> if this account is hidden or <tt>false</tt> * otherwise. */ public boolean isHidden() { return getAccountPropertyString( ProtocolProviderFactory.IS_PROTOCOL_HIDDEN) != null; } /** * Checks if the account config is hidden. * @return <tt>true</tt> if the account config is hidden or <tt>false</tt> * otherwise. */ public boolean isConfigHidden() { return getAccountPropertyString( ProtocolProviderFactory.IS_ACCOUNT_CONFIG_HIDDEN) != null; } /** * Checks if the account status menu is hidden. * @return <tt>true</tt> if the account status menu is hidden or * <tt>false</tt> otherwise. */ public boolean isStatusMenuHidden() { return getAccountPropertyString( ProtocolProviderFactory.IS_ACCOUNT_STATUS_MENU_HIDDEN) != null; } /** * Checks if the account is marked as readonly. * @return <tt>true</tt> if the account is marked as readonly or * <tt>false</tt> otherwise. */ public boolean isReadOnly() { return getAccountPropertyString( ProtocolProviderFactory.IS_ACCOUNT_READ_ONLY) != null; } /** * Returns the first <tt>ProtocolProviderService</tt> implementation * corresponding to the preferred protocol * * @return the <tt>ProtocolProviderService</tt> corresponding to the * preferred protocol */ public boolean isPreferredProvider() { String preferredProtocolProp = getAccountPropertyString( ProtocolProviderFactory.IS_PREFERRED_PROTOCOL); if (preferredProtocolProp != null && preferredProtocolProp.length() > 0 && Boolean.parseBoolean(preferredProtocolProp)) { return true; } return false; } /** * Set the account properties. * * @param accountProperties the properties of the account */ public void setAccountProperties(Map<String, String> accountProperties) { this.accountProperties = accountProperties; } /** * Returns if the encryption protocol given in parameter is enabled. * * @param encryptionProtocolName The name of the encryption protocol * ("ZRTP", "SDES" or "MIKEY"). */ public boolean isEncryptionProtocolEnabled(SrtpControlType type) { // The default value is false, except for ZRTP. boolean defaultValue = type == SrtpControlType.ZRTP; return getAccountPropertyBoolean( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + "." + type.toString(), defaultValue); } /** * Returns the list of STUN servers that this account is currently * configured to use. * * @return the list of STUN servers that this account is currently * configured to use. */ public List<StunServerDescriptor> getStunServers( BundleContext bundleContext) { Map<String, String> accountProperties = getAccountProperties(); List<StunServerDescriptor> stunServerList = new ArrayList<StunServerDescriptor>(); for (int i = 0; i < StunServerDescriptor.MAX_STUN_SERVER_COUNT; i ++) { StunServerDescriptor stunServer = StunServerDescriptor.loadDescriptor( accountProperties, ProtocolProviderFactory.STUN_PREFIX + i); // If we don't find a stun server with the given index, it means // there are no more servers left in the table so we've nothing // more to do here. if (stunServer == null) break; String password = loadStunPassword( bundleContext, this, ProtocolProviderFactory.STUN_PREFIX + i); if(password != null) stunServer.setPassword(password); stunServerList.add(stunServer); } return stunServerList; } /** * Returns the password for the STUN server with the specified prefix. * * @param bundleContext the OSGi bundle context that we are currently * running in. * @param accountID account ID * @param namePrefix name prefix * * @return password or null if empty */ protected static String loadStunPassword(BundleContext bundleContext, AccountID accountID, String namePrefix) { ProtocolProviderFactory providerFactory = ProtocolProviderFactory.getProtocolProviderFactory( bundleContext, accountID.getSystemProtocolName()); String password = null; String className = providerFactory.getClass().getName(); String packageSourceName = className.substring(0, className.lastIndexOf('.')); String accountPrefix = ProtocolProviderFactory.findAccountPrefix( bundleContext, accountID, packageSourceName); CredentialsStorageService credentialsService = ServiceUtils.getService( bundleContext, CredentialsStorageService.class); try { password = credentialsService. loadPassword(accountPrefix + "." + namePrefix); } catch(Exception e) { return null; } return password; } /** * Determines whether this account's provider is supposed to auto discover * STUN and TURN servers. * * @return <tt>true</tt> if this provider would need to discover STUN/TURN * servers and false otherwise. */ public boolean isStunServerDiscoveryEnabled() { return getAccountPropertyBoolean( ProtocolProviderFactory.AUTO_DISCOVER_STUN, true); } /** * Determines whether this account's provider uses UPnP (if available). * * @return <tt>true</tt> if this provider would use UPnP (if available), * <tt>false</tt> otherwise */ public boolean isUPNPEnabled() { return getAccountPropertyBoolean( ProtocolProviderFactory.IS_USE_UPNP, true); } /** * Determines whether this account's provider uses the default STUN server * provided by Jitsi (stun.jitsi.net) if there is no other STUN/TURN server * discovered/configured. * * @return <tt>true</tt> if this provider would use the default STUN server, * <tt>false</tt> otherwise */ public boolean isUseDefaultStunServer() { return getAccountPropertyBoolean( ProtocolProviderFactory.USE_DEFAULT_STUN_SERVER, true); } /** * Returns the actual name of the protocol used rather than a branded * variant. The method is primarily meant for open protocols such as SIP * or XMPP so that it would always return SIP or XMPP even in branded * protocols who otherwise return things like GTalk and ippi for * PROTOCOL_NAME. * * @return the real non-branded name of the protocol. */ public String getSystemProtocolName() { return getProtocolName(); } /** * Sorts the enabled encryption protocol list given in parameter to match * the preferences set for this account. * * @return Sorts the enabled encryption protocol list given in parameter to * match the preferences set for this account. */ public List<SrtpControlType> getSortedEnabledEncryptionProtocolList() { Map<String, Integer> encryptionProtocols = getIntegerPropertiesByPrefix( ProtocolProviderFactory.ENCRYPTION_PROTOCOL, true); Map<String, Boolean> encryptionProtocolStatus = getBooleanPropertiesByPrefix( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS, true, false); // If the account is not yet configured, then ZRTP is activated by // default. if(encryptionProtocols.size() == 0) { encryptionProtocols.put( ProtocolProviderFactory.ENCRYPTION_PROTOCOL + ".ZRTP", 0); encryptionProtocolStatus.put( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + ".ZRTP", true); } List<SrtpControlType> sortedEncryptionProtocols = new ArrayList<SrtpControlType>(encryptionProtocols.size()); // First: add all protocol in the right order. for (Map.Entry<String, Integer> e : encryptionProtocols.entrySet()) { int index = e.getValue(); // If the key is set. if (index != -1) { if (index > sortedEncryptionProtocols.size()) index = sortedEncryptionProtocols.size(); String name = e.getKey() .substring( ProtocolProviderFactory.ENCRYPTION_PROTOCOL .length() + 1); try { sortedEncryptionProtocols.add(index, SrtpControlType.fromString(name)); } catch(IllegalArgumentException exc) { logger.error( "Failed to get SRTP control type for name: '" + name + "', key: '" + e.getKey() + "'", exc); } } } // Second: remove all disabled protocols. for (Iterator<SrtpControlType> i = sortedEncryptionProtocols.iterator(); i.hasNext();) { String name = i.next().toString(); if (!encryptionProtocolStatus.get( ProtocolProviderFactory.ENCRYPTION_PROTOCOL_STATUS + "." + name)) { i.remove(); } } return sortedEncryptionProtocols; } /** * Returns a <tt>java.util.Map</tt> of <tt>String</tt>s containing the * all property names that have the specified prefix and <tt>Boolean</tt> * containing the value for each property selected. Depending on the value * of the <tt>exactPrefixMatch</tt> parameter the method will (when false) * or will not (when exactPrefixMatch is true) include property names that * have prefixes longer than the specified <tt>prefix</tt> param. * <p> * Example: * <p> * Imagine a configuration service instance containing 2 properties * only:<br> * <code> * net.java.sip.communicator.PROP1=value1<br> * net.java.sip.communicator.service.protocol.PROP1=value2 * </code> * <p> * A call to this method with a prefix="net.java.sip.communicator" and * exactPrefixMatch=true would only return the first property - * net.java.sip.communicator.PROP1, whereas the same call with * exactPrefixMatch=false would return both properties as the second prefix * includes the requested prefix string. * <p> * @param prefix a String containing the prefix (the non dotted non-caps * part of a property name) that we're looking for. * @param exactPrefixMatch a boolean indicating whether the returned * property names should all have a prefix that is an exact match of the * the <tt>prefix</tt> param or whether properties with prefixes that * contain it but are longer than it are also accepted. * @param defaultValue the default value if the key is not set. * @return a <tt>java.util.Map</tt> containing all property name String-s * matching the specified conditions and the corresponding values as * Boolean. */ public Map<String, Boolean> getBooleanPropertiesByPrefix( String prefix, boolean exactPrefixMatch, boolean defaultValue) { String propertyName; List<String> propertyNames = getPropertyNamesByPrefix(prefix, exactPrefixMatch); Map<String, Boolean> properties = new HashMap<String, Boolean>(propertyNames.size()); for(int i = 0; i < propertyNames.size(); ++i) { propertyName = propertyNames.get(i); properties.put( propertyName, getAccountPropertyBoolean(propertyName, defaultValue)); } return properties; } /** * Returns a <tt>java.util.Map</tt> of <tt>String</tt>s containing the * all property names that have the specified prefix and <tt>Integer</tt> * containing the value for each property selected. Depending on the value * of the <tt>exactPrefixMatch</tt> parameter the method will (when false) * or will not (when exactPrefixMatch is true) include property names that * have prefixes longer than the specified <tt>prefix</tt> param. * <p> * Example: * <p> * Imagine a configuration service instance containing 2 properties * only:<br> * <code> * net.java.sip.communicator.PROP1=value1<br> * net.java.sip.communicator.service.protocol.PROP1=value2 * </code> * <p> * A call to this method with a prefix="net.java.sip.communicator" and * exactPrefixMatch=true would only return the first property - * net.java.sip.communicator.PROP1, whereas the same call with * exactPrefixMatch=false would return both properties as the second prefix * includes the requested prefix string. * <p> * @param prefix a String containing the prefix (the non dotted non-caps * part of a property name) that we're looking for. * @param exactPrefixMatch a boolean indicating whether the returned * property names should all have a prefix that is an exact match of the * the <tt>prefix</tt> param or whether properties with prefixes that * contain it but are longer than it are also accepted. * @return a <tt>java.util.Map</tt> containing all property name String-s * matching the specified conditions and the corresponding values as * Integer. */ public Map<String, Integer> getIntegerPropertiesByPrefix( String prefix, boolean exactPrefixMatch) { String propertyName; List<String> propertyNames = getPropertyNamesByPrefix(prefix, exactPrefixMatch); Map<String, Integer> properties = new HashMap<String, Integer>(propertyNames.size()); for(int i = 0; i < propertyNames.size(); ++i) { propertyName = propertyNames.get(i); properties.put( propertyName, getAccountPropertyInt(propertyName, -1)); } return properties; } /** * Returns a <tt>java.util.List</tt> of <tt>String</tt>s containing the * all property names that have the specified prefix. Depending on the value * of the <tt>exactPrefixMatch</tt> parameter the method will (when false) * or will not (when exactPrefixMatch is true) include property names that * have prefixes longer than the specified <tt>prefix</tt> param. * <p> * Example: * <p> * Imagine a configuration service instance containing 2 properties * only:<br> * <code> * net.java.sip.communicator.PROP1=value1<br> * net.java.sip.communicator.service.protocol.PROP1=value2 * </code> * <p> * A call to this method with a prefix="net.java.sip.communicator" and * exactPrefixMatch=true would only return the first property - * net.java.sip.communicator.PROP1, whereas the same call with * exactPrefixMatch=false would return both properties as the second prefix * includes the requested prefix string. * <p> * @param prefix a String containing the prefix (the non dotted non-caps * part of a property name) that we're looking for. * @param exactPrefixMatch a boolean indicating whether the returned * property names should all have a prefix that is an exact match of the * the <tt>prefix</tt> param or whether properties with prefixes that * contain it but are longer than it are also accepted. * @return a <tt>java.util.List</tt>containing all property name String-s * matching the specified conditions. */ public List<String> getPropertyNamesByPrefix( String prefix, boolean exactPrefixMatch) { List<String> resultKeySet = new LinkedList<String>(); for (String key : accountProperties.keySet()) { int ix = key.lastIndexOf('.'); if(ix == -1) continue; String keyPrefix = key.substring(0, ix); if(exactPrefixMatch) { if(prefix.equals(keyPrefix)) resultKeySet.add(key); } else { if(keyPrefix.startsWith(prefix)) resultKeySet.add(key); } } return resultKeySet; } /** * Sets the property a new value, but only if it's not <tt>null</tt> or * the property is removed from the map. * * @param key the property key * @param value the property value */ public void setOrRemoveIfNull(String key, String value) { if(value != null) { putAccountProperty(key, value); } else { removeAccountProperty(key); } } /** * Puts the new property value if it's not <tt>null</tt> nor empty. * @param key the property key * @param value the property value */ public void setOrRemoveIfEmpty(String key, String value) { setOrRemoveIfEmpty(key, value, false); } /** * Puts the new property value if it's not <tt>null</tt> nor empty. If * <tt>trim</tt> parameter is set to <tt>true</tt> the string will be * trimmed, before checked for emptiness. * * @param key the property key * @param value the property value * @param trim <tt>true</tt> if the value will be trimmed, before * <tt>isEmpty()</tt> is called. */ public void setOrRemoveIfEmpty(String key, String value, boolean trim) { if( value != null && (trim ? !value.trim().isEmpty() : !value.isEmpty()) ) { putAccountProperty(key, value); } else { removeAccountProperty(key); } } /** * Stores configuration properties held by this object into given * <tt>accountProperties</tt> map. * * @param protocolIconPath the path to the protocol icon is used * @param accountIconPath the path to the account icon if used * @param accountProperties output properties map */ public void storeProperties( String protocolIconPath, String accountIconPath, Map<String, String> accountProperties ) { if(protocolIconPath != null) setProtocolIconPath(protocolIconPath); if(accountIconPath != null) setAccountIconPath(accountIconPath); mergeProperties(this.accountProperties, accountProperties); // Removes encrypted password property, as it will be restored during // account storage, but only if the password property is present. accountProperties.remove("ENCRYPTED_PASSWORD"); } /** * Gets default property value for given <tt>key</tt>. * * @param key the property key * @return default property value for given<tt>key</tt> */ protected String getDefaultString(String key) { return getDefaultStr(key); } /** * Gets default property value for given <tt>key</tt>. * * @param key the property key * @return default property value for given<tt>key</tt> */ public static String getDefaultStr(String key) { return ProtocolProviderActivator .getConfigurationService() .getString(DEFAULTS_PREFIX +key); } /** * Copies all properties from <tt>input</tt> map to <tt>output</tt> map. * @param input source properties map * @param output destination properties map */ public static void mergeProperties( Map<String, String> input, Map<String, String> output ) { for(String key : input.keySet()) { output.put(key, input.get(key)); } } }