From be0540e2b75cc30d437daad88cb3467580eff0cc Mon Sep 17 00:00:00 2001 From: Ingo Bauersachs Date: Sun, 28 Feb 2016 21:54:06 +0100 Subject: Normalize line endings See also #227 --- .../impl/protocol/sip/net/ProxyConnection.java | 360 +-- .../impl/protocol/sip/net/RFC5922Matcher.java | 418 +-- .../sysactivity/SystemActivityNotifications.java | 502 ++-- .../plugin/contactinfo/ContactInfoActivator.java | 416 +-- .../plugin/desktoputil/X509CertificatePanel.java | 976 +++---- .../communicator/plugin/otr/ScOtrEngineImpl.java | 2654 ++++++++++---------- .../muc/ChatRoomProviderWrapperListener.java | 80 +- .../service/protocol/AccountManager.java | 2166 ++++++++-------- 8 files changed, 3786 insertions(+), 3786 deletions(-) (limited to 'src') diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java index 99fcfb2..f7079f4 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/ProxyConnection.java @@ -1,180 +1,180 @@ -/* - * 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.impl.protocol.sip.net; - -import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG; - -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.impl.protocol.sip.*; -import net.java.sip.communicator.service.dns.*; - -/** - * Abstract class for the determining the address for the SIP proxy. - * - * @author Ingo Bauersachs - */ -public abstract class ProxyConnection -{ - private List returnedAddresses = new LinkedList(); - - protected String transport; - protected InetSocketAddress socketAddress; - protected final SipAccountIDImpl account; - - /** - * Creates a new instance of this class. - * @param account the account of this SIP protocol instance - */ - protected ProxyConnection(SipAccountIDImpl account) - { - this.account = account; - } - - /** - * Gets the address to use for the next connection attempt. - * @return the address of the last lookup. - */ - public final InetSocketAddress getAddress() - { - return socketAddress; - } - - /** - * Gets the transport to use for the next connection attempt. - * @return the transport of the last lookup. - */ - public final String getTransport() - { - return transport; - } - - /** - * In case we are using an outbound proxy this method returns - * a suitable string for use with Router. - * The method returns null otherwise. - * - * @return the string of our outbound proxy if we are using one and - * null otherwise. - */ - public final String getOutboundProxyString() - { - if(socketAddress == null) - return null; - - InetAddress proxyAddress = socketAddress.getAddress(); - StringBuilder proxyStringBuffer - = new StringBuilder(proxyAddress.getHostAddress()); - - if(proxyAddress instanceof Inet6Address) - { - proxyStringBuffer.insert(0, '['); - proxyStringBuffer.append(']'); - } - - proxyStringBuffer.append(':'); - proxyStringBuffer.append(socketAddress.getPort()); - proxyStringBuffer.append('/'); - proxyStringBuffer.append(transport); - - return proxyStringBuffer.toString(); - } - - /** - * Compares an InetAddress against the active outbound proxy. The comparison - * is by reference, not equals. - * - * @param addressToTest The addres to test. - * @return True when the InetAddress is the same as the outbound proxy. - */ - public final boolean isSameInetAddress(InetAddress addressToTest) - { - // if the proxy is not yet initialized then this is not the provider - // that caused this comparison - if(socketAddress == null) - return false; - return addressToTest == socketAddress.getAddress(); - } - - /** - * Retrieves the next address to use from DNS. Duplicate results are - * suppressed. - * - * @return True if a new address is available through {@link #getAddress()}, - * false if the last address was reached. A new lookup from scratch - * can be started by calling {@link #reset()}. - * @throws DnssecException if there is a problem related to DNSSEC - */ - public final boolean getNextAddress() throws DnssecException - { - boolean result; - String key = null; - do - { - result = getNextAddressFromDns(); - if(result && socketAddress != null) - { - key = getOutboundProxyString(); - if(!returnedAddresses.contains(key)) - { - returnedAddresses.add(key); - break; - } - } - } - while(result && returnedAddresses.contains(key)); - return result; - } - - /** - * Implementations must use this method to get the next address, but do not - * have to care about duplicate addresses. - * - * @return True when a further address was available. - * @throws DnssecException when a DNSSEC validation failure occured. - */ - protected abstract boolean getNextAddressFromDns() - throws DnssecException; - - /** - * Resets the lookup to it's initial state. Overriders methods have to call - * this method through a super-call. - */ - public void reset() - { - returnedAddresses.clear(); - } - - /** - * Factory method to create a proxy connection based on the account settings - * of the protocol provider. - * - * @param pps the protocol provider that needs a SIP server connection. - * @return An instance of a derived class. - */ - public static ProxyConnection create(ProtocolProviderServiceSipImpl pps) - { - if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG, - true)) - return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(), - pps.getDefaultTransport()); - else - return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID()); - } -} +/* + * 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.impl.protocol.sip.net; + +import static net.java.sip.communicator.service.protocol.ProtocolProviderFactory.PROXY_AUTO_CONFIG; + +import java.net.*; +import java.util.*; + +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.service.dns.*; + +/** + * Abstract class for the determining the address for the SIP proxy. + * + * @author Ingo Bauersachs + */ +public abstract class ProxyConnection +{ + private List returnedAddresses = new LinkedList(); + + protected String transport; + protected InetSocketAddress socketAddress; + protected final SipAccountIDImpl account; + + /** + * Creates a new instance of this class. + * @param account the account of this SIP protocol instance + */ + protected ProxyConnection(SipAccountIDImpl account) + { + this.account = account; + } + + /** + * Gets the address to use for the next connection attempt. + * @return the address of the last lookup. + */ + public final InetSocketAddress getAddress() + { + return socketAddress; + } + + /** + * Gets the transport to use for the next connection attempt. + * @return the transport of the last lookup. + */ + public final String getTransport() + { + return transport; + } + + /** + * In case we are using an outbound proxy this method returns + * a suitable string for use with Router. + * The method returns null otherwise. + * + * @return the string of our outbound proxy if we are using one and + * null otherwise. + */ + public final String getOutboundProxyString() + { + if(socketAddress == null) + return null; + + InetAddress proxyAddress = socketAddress.getAddress(); + StringBuilder proxyStringBuffer + = new StringBuilder(proxyAddress.getHostAddress()); + + if(proxyAddress instanceof Inet6Address) + { + proxyStringBuffer.insert(0, '['); + proxyStringBuffer.append(']'); + } + + proxyStringBuffer.append(':'); + proxyStringBuffer.append(socketAddress.getPort()); + proxyStringBuffer.append('/'); + proxyStringBuffer.append(transport); + + return proxyStringBuffer.toString(); + } + + /** + * Compares an InetAddress against the active outbound proxy. The comparison + * is by reference, not equals. + * + * @param addressToTest The addres to test. + * @return True when the InetAddress is the same as the outbound proxy. + */ + public final boolean isSameInetAddress(InetAddress addressToTest) + { + // if the proxy is not yet initialized then this is not the provider + // that caused this comparison + if(socketAddress == null) + return false; + return addressToTest == socketAddress.getAddress(); + } + + /** + * Retrieves the next address to use from DNS. Duplicate results are + * suppressed. + * + * @return True if a new address is available through {@link #getAddress()}, + * false if the last address was reached. A new lookup from scratch + * can be started by calling {@link #reset()}. + * @throws DnssecException if there is a problem related to DNSSEC + */ + public final boolean getNextAddress() throws DnssecException + { + boolean result; + String key = null; + do + { + result = getNextAddressFromDns(); + if(result && socketAddress != null) + { + key = getOutboundProxyString(); + if(!returnedAddresses.contains(key)) + { + returnedAddresses.add(key); + break; + } + } + } + while(result && returnedAddresses.contains(key)); + return result; + } + + /** + * Implementations must use this method to get the next address, but do not + * have to care about duplicate addresses. + * + * @return True when a further address was available. + * @throws DnssecException when a DNSSEC validation failure occured. + */ + protected abstract boolean getNextAddressFromDns() + throws DnssecException; + + /** + * Resets the lookup to it's initial state. Overriders methods have to call + * this method through a super-call. + */ + public void reset() + { + returnedAddresses.clear(); + } + + /** + * Factory method to create a proxy connection based on the account settings + * of the protocol provider. + * + * @param pps the protocol provider that needs a SIP server connection. + * @return An instance of a derived class. + */ + public static ProxyConnection create(ProtocolProviderServiceSipImpl pps) + { + if (pps.getAccountID().getAccountPropertyBoolean(PROXY_AUTO_CONFIG, + true)) + return new AutoProxyConnection((SipAccountIDImpl) pps.getAccountID(), + pps.getDefaultTransport()); + else + return new ManualProxyConnection((SipAccountIDImpl) pps.getAccountID()); + } +} diff --git a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java index 0027ec5..262b717 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/net/RFC5922Matcher.java @@ -1,4 +1,4 @@ -/* +/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,211 +15,211 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.impl.protocol.sip.net; - -import java.security.cert.*; -import java.text.*; -import java.util.*; -import java.util.regex.*; - -import javax.sip.address.*; - -import net.java.sip.communicator.impl.protocol.sip.*; -import net.java.sip.communicator.service.certificate.*; -import net.java.sip.communicator.util.*; - -/** - * Matcher that extracts certificate identities according to RFC5922, Section - * 7.1 and compares them with the rules from Section 7.2 and 7.3. - * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false - * - * @author Ingo Bauersachs - */ -public class RFC5922Matcher - implements CertificateMatcher -{ - /** - * When set to true, enables strict validation of the hostname according to - * RFC5922 Section - * 7.2 - */ - public final static String PNAME_STRICT_RFC5922 = - "net.java.sip.communicator.sip.tls.STRICT_RFC5922"; - - private ProtocolProviderServiceSipImpl provider; - - /** - * Creates a new instance of this class. - * @param provider The SIP Provider to which this matcher belongs. - */ - public RFC5922Matcher(ProtocolProviderServiceSipImpl provider) - { - this.provider = provider; - } - - /** Our class logger. */ - private static final Logger logger = Logger - .getLogger(CertificateMatcher.class); - - /* - * (non-Javadoc) - * - * @see - * net.java.sip.communicator.service.certificate.CertificateMatcher#verify - * (java.lang.Iterable, java.security.cert.X509Certificate) - */ - public void verify(Iterable identitiesToTest, X509Certificate cert) - throws CertificateException - { - boolean strict = SipActivator.getConfigurationService() - .getBoolean(PNAME_STRICT_RFC5922, false); - - // if any of the identities is contained in the certificate we're good - boolean oneMatched = false; - Iterable certIdentities = extractCertIdentities(cert); - for (String identity : identitiesToTest) - { - // check if the intended hostname is contained in one of the - // hostnames of the certificate according to - // http://tools.ietf.org/html/rfc5922#section-7.2 - for(String dnsName : certIdentities) - { - try - { - if(NetworkUtils.compareDnsNames(dnsName, identity) == 0) - { - // one of the hostnames matched, we're good to go - return; - } - - if(!strict - // is a wildcard name - && dnsName.startsWith("*.") - // contains at least two dots (*.example.com) - && identity.indexOf(".") < identity.lastIndexOf(".") - // compare *.example.com stripped to example.com with - // - foo.example.com stripped to example.com - // - foo.bar.example.com to bar.example.com - && NetworkUtils.compareDnsNames( - dnsName.substring(2), - identity.substring(identity.indexOf(".")+1)) == 0) - { - // the wildcard matched, we're good to go - return; - } - } - catch (ParseException e) - {} // we don't care - this hostname did not match - } - } - if (!oneMatched) - throw new CertificateException("None of <" + identitiesToTest - + "> matched by the rules of RFC5922 to the cert with CN=" - + cert.getSubjectDN()); - } - - private Iterable extractCertIdentities(X509Certificate cert) - { - List certIdentities = new ArrayList(); - Collection> subjAltNames = null; - try - { - subjAltNames = cert.getSubjectAlternativeNames(); - } - catch (CertificateParsingException ex) - { - logger.error("Error parsing TLS certificate", ex); - } - // subjAltName types are defined in rfc2459 - final Integer dnsNameType = 2; - final Integer uriNameType = 6; - if (subjAltNames != null) - { - if (logger.isDebugEnabled()) - logger.debug("found subjAltNames: " + subjAltNames); - - // First look for a URI in the subjectAltName field - for (List altName : subjAltNames) - { - // 0th position is the alt name type - // 1st position is the alt name data - if (altName.get(0).equals(uriNameType)) - { - SipURI altNameUri; - try - { - altNameUri = - provider.getAddressFactory().createSipURI( - (String) altName.get(1)); - // only sip URIs are allowed - if (!"sip".equals(altNameUri.getScheme())) - continue; - // user certificates are not allowed - if (altNameUri.getUser() != null) - continue; - String altHostName = altNameUri.getHost(); - if (logger.isDebugEnabled()) - { - logger.debug("found uri " + altName.get(1) - + ", hostName " + altHostName); - } - certIdentities.add(altHostName); - } - catch (ParseException e) - { - logger.error("certificate contains invalid uri: " - + altName.get(1)); - } - } - - } - // DNS An implementation MUST accept a domain name system - // identifier as a SIP domain identity if and only if no other - // identity is found that matches the "sip" URI type described - // above. - if (certIdentities.isEmpty()) - { - for (List altName : subjAltNames) - { - if (altName.get(0).equals(dnsNameType)) - { - if (logger.isDebugEnabled()) - logger.debug("found dns " + altName.get(1)); - certIdentities.add(altName.get(1).toString()); - } - } - } - } - else - { - // If and only if the subjectAltName does not appear in the - // certificate, the implementation MAY examine the CN field of the - // certificate. If a valid DNS name is found there, the - // implementation MAY accept this value as a SIP domain identity. - String dname = cert.getSubjectDN().getName(); - String cname = ""; - try - { - Pattern EXTRACT_CN = - Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*"); - Matcher matcher = EXTRACT_CN.matcher(dname); - if (matcher.matches()) - { - cname = matcher.group(1); - if (logger.isDebugEnabled()) - { - logger.debug("found CN: " + cname + " from DN: " - + dname); - } - certIdentities.add(cname); - } - } - catch (Exception ex) - { - logger.error("exception while extracting CN", ex); - } - } - return certIdentities; - } -} +package net.java.sip.communicator.impl.protocol.sip.net; + +import java.security.cert.*; +import java.text.*; +import java.util.*; +import java.util.regex.*; + +import javax.sip.address.*; + +import net.java.sip.communicator.impl.protocol.sip.*; +import net.java.sip.communicator.service.certificate.*; +import net.java.sip.communicator.util.*; + +/** + * Matcher that extracts certificate identities according to RFC5922, Section + * 7.1 and compares them with the rules from Section 7.2 and 7.3. + * @see #PNAME_STRICT_RFC5922 for wildcard handling; the default is false + * + * @author Ingo Bauersachs + */ +public class RFC5922Matcher + implements CertificateMatcher +{ + /** + * When set to true, enables strict validation of the hostname according to + * RFC5922 Section + * 7.2 + */ + public final static String PNAME_STRICT_RFC5922 = + "net.java.sip.communicator.sip.tls.STRICT_RFC5922"; + + private ProtocolProviderServiceSipImpl provider; + + /** + * Creates a new instance of this class. + * @param provider The SIP Provider to which this matcher belongs. + */ + public RFC5922Matcher(ProtocolProviderServiceSipImpl provider) + { + this.provider = provider; + } + + /** Our class logger. */ + private static final Logger logger = Logger + .getLogger(CertificateMatcher.class); + + /* + * (non-Javadoc) + * + * @see + * net.java.sip.communicator.service.certificate.CertificateMatcher#verify + * (java.lang.Iterable, java.security.cert.X509Certificate) + */ + public void verify(Iterable identitiesToTest, X509Certificate cert) + throws CertificateException + { + boolean strict = SipActivator.getConfigurationService() + .getBoolean(PNAME_STRICT_RFC5922, false); + + // if any of the identities is contained in the certificate we're good + boolean oneMatched = false; + Iterable certIdentities = extractCertIdentities(cert); + for (String identity : identitiesToTest) + { + // check if the intended hostname is contained in one of the + // hostnames of the certificate according to + // http://tools.ietf.org/html/rfc5922#section-7.2 + for(String dnsName : certIdentities) + { + try + { + if(NetworkUtils.compareDnsNames(dnsName, identity) == 0) + { + // one of the hostnames matched, we're good to go + return; + } + + if(!strict + // is a wildcard name + && dnsName.startsWith("*.") + // contains at least two dots (*.example.com) + && identity.indexOf(".") < identity.lastIndexOf(".") + // compare *.example.com stripped to example.com with + // - foo.example.com stripped to example.com + // - foo.bar.example.com to bar.example.com + && NetworkUtils.compareDnsNames( + dnsName.substring(2), + identity.substring(identity.indexOf(".")+1)) == 0) + { + // the wildcard matched, we're good to go + return; + } + } + catch (ParseException e) + {} // we don't care - this hostname did not match + } + } + if (!oneMatched) + throw new CertificateException("None of <" + identitiesToTest + + "> matched by the rules of RFC5922 to the cert with CN=" + + cert.getSubjectDN()); + } + + private Iterable extractCertIdentities(X509Certificate cert) + { + List certIdentities = new ArrayList(); + Collection> subjAltNames = null; + try + { + subjAltNames = cert.getSubjectAlternativeNames(); + } + catch (CertificateParsingException ex) + { + logger.error("Error parsing TLS certificate", ex); + } + // subjAltName types are defined in rfc2459 + final Integer dnsNameType = 2; + final Integer uriNameType = 6; + if (subjAltNames != null) + { + if (logger.isDebugEnabled()) + logger.debug("found subjAltNames: " + subjAltNames); + + // First look for a URI in the subjectAltName field + for (List altName : subjAltNames) + { + // 0th position is the alt name type + // 1st position is the alt name data + if (altName.get(0).equals(uriNameType)) + { + SipURI altNameUri; + try + { + altNameUri = + provider.getAddressFactory().createSipURI( + (String) altName.get(1)); + // only sip URIs are allowed + if (!"sip".equals(altNameUri.getScheme())) + continue; + // user certificates are not allowed + if (altNameUri.getUser() != null) + continue; + String altHostName = altNameUri.getHost(); + if (logger.isDebugEnabled()) + { + logger.debug("found uri " + altName.get(1) + + ", hostName " + altHostName); + } + certIdentities.add(altHostName); + } + catch (ParseException e) + { + logger.error("certificate contains invalid uri: " + + altName.get(1)); + } + } + + } + // DNS An implementation MUST accept a domain name system + // identifier as a SIP domain identity if and only if no other + // identity is found that matches the "sip" URI type described + // above. + if (certIdentities.isEmpty()) + { + for (List altName : subjAltNames) + { + if (altName.get(0).equals(dnsNameType)) + { + if (logger.isDebugEnabled()) + logger.debug("found dns " + altName.get(1)); + certIdentities.add(altName.get(1).toString()); + } + } + } + } + else + { + // If and only if the subjectAltName does not appear in the + // certificate, the implementation MAY examine the CN field of the + // certificate. If a valid DNS name is found there, the + // implementation MAY accept this value as a SIP domain identity. + String dname = cert.getSubjectDN().getName(); + String cname = ""; + try + { + Pattern EXTRACT_CN = + Pattern.compile(".*CN\\s*=\\s*([\\w*\\.]+).*"); + Matcher matcher = EXTRACT_CN.matcher(dname); + if (matcher.matches()) + { + cname = matcher.group(1); + if (logger.isDebugEnabled()) + { + logger.debug("found CN: " + cname + " from DN: " + + dname); + } + certIdentities.add(cname); + } + } + catch (Exception ex) + { + logger.error("exception while extracting CN", ex); + } + } + return certIdentities; + } +} diff --git a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java index f4f6ede..6d9edda 100644 --- a/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java +++ b/src/net/java/sip/communicator/impl/sysactivity/SystemActivityNotifications.java @@ -1,251 +1,251 @@ -/* - * 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.impl.sysactivity; - -import net.java.sip.communicator.util.Logger; -import org.jitsi.util.*; - -/** - * @author Damian Minkov - */ -public class SystemActivityNotifications -{ - /** - * The Logger used by the SystemActivityNotifications - * class to log debugging information. - */ - private static final Logger logger - = Logger.getLogger(SystemActivityNotifications.class); - - /** - * Computer display has stand by. - */ - public static final int NOTIFY_DISPLAY_SLEEP = 2; - - /** - * Computer display wakes up after stand by. - */ - public static final int NOTIFY_DISPLAY_WAKE = 3; - - /** - * A change in dns configuration has occurred. - */ - public static final int NOTIFY_DNS_CHANGE = 10; - - /** - * All processes have been informed about ending session, now notify for - * the actual end session. - */ - public static final int NOTIFY_ENDSESSION = 12; - - /** - * A change in network configuration has occurred. - */ - public static final int NOTIFY_NETWORK_CHANGE = 9; - - /** - * Notifies for start of process of ending desktop session, - * logoff or shutdown. - */ - public static final int NOTIFY_QUERY_ENDSESSION = 11; - - /** - * Screen has been locked. - */ - public static final int NOTIFY_SCREEN_LOCKED = 7; - - /** - * Screen has been unlocked. - */ - public static final int NOTIFY_SCREEN_UNLOCKED = 8; - - /** - * Screensaver has been started. - */ - public static final int NOTIFY_SCREENSAVER_START = 4; - - /** - * Screensaver has been stopped. - */ - public static final int NOTIFY_SCREENSAVER_STOP = 6; - - /** - * Screensaver will stop. - */ - public static final int NOTIFY_SCREENSAVER_WILL_STOP = 5; - - /** - * Notify that computers is going to sleep. - */ - public static final int NOTIFY_SLEEP = 0; - - /** - * Notify that computer is wakeing up after stand by. - */ - public static final int NOTIFY_WAKE = 1; - - /** - * The native instance. - */ - private static long ptr; - - /** - * Init native library. - */ - static - { - try - { - // Don't load native library on Android to prevent the exception - if(!org.jitsi.util.OSUtils.IS_ANDROID) - { - JNIUtils.loadLibrary("sysactivitynotifications", - SystemActivityNotifications.class.getClassLoader()); - - ptr = allocAndInit(); - if (ptr == -1) - ptr = 0; - } - } - catch (Throwable t) - { - if (t instanceof ThreadDeath) - throw (ThreadDeath) t; - else - logger.warn("Failed to initialize native counterpart", t); - } - } - - /** - * Allocate native resources and gets a pointer. - * - * @return - */ - private static native long allocAndInit(); - - /** - * Returns the when was last input in milliseconds. The time when there was - * any activity on the computer. - * - * @return the last input in milliseconds - */ - public static native long getLastInput(); - - /** - * Whether native library is loaded. - * - * @return whether native library is loaded. - */ - public static boolean isLoaded() - { - return (ptr != 0); - } - - /** - * Release native resources. - * - * @param ptr - */ - private static native void release(long ptr); - - /** - * Sets notifier delegate. - * - * @param ptr - * @param delegate - */ - public static native void setDelegate( - long ptr, - NotificationsDelegate delegate); - - /** - * Sets delegate. - * - * @param delegate - */ - public static void setDelegate(NotificationsDelegate delegate) - { - if (ptr != 0) - setDelegate(ptr, delegate); - } - - /** - * Start. - */ - public static void start() - { - if (ptr != 0) - start(ptr); - } - - /** - * Start processing. - * - * @param ptr - */ - private static native void start(long ptr); - - /** - * Stop. - */ - public static void stop() - { - if (ptr != 0) - { - stop(ptr); - release(ptr); - ptr = 0; - } - } - - /** - * Stop processing. - * - * @param ptr - */ - private static native void stop(long ptr); - - /** - * Delegate class to be notified about changes. - */ - public interface NotificationsDelegate - { - /** - * Callback method when receiving notifications. - * - * @param type - */ - public void notify(int type); - - /** - * Callback method when receiving special network notifications. - * - * @param family family of network change (ipv6, ipv4) - * @param luidIndex unique index of interface - * @param name name of the interface - * @param type of the interface - * @param connected whether interface is connected or not. - */ - public void notifyNetworkChange( - int family, - long luidIndex, - String name, - long type, - boolean connected); - } -} +/* + * 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.impl.sysactivity; + +import net.java.sip.communicator.util.Logger; +import org.jitsi.util.*; + +/** + * @author Damian Minkov + */ +public class SystemActivityNotifications +{ + /** + * The Logger used by the SystemActivityNotifications + * class to log debugging information. + */ + private static final Logger logger + = Logger.getLogger(SystemActivityNotifications.class); + + /** + * Computer display has stand by. + */ + public static final int NOTIFY_DISPLAY_SLEEP = 2; + + /** + * Computer display wakes up after stand by. + */ + public static final int NOTIFY_DISPLAY_WAKE = 3; + + /** + * A change in dns configuration has occurred. + */ + public static final int NOTIFY_DNS_CHANGE = 10; + + /** + * All processes have been informed about ending session, now notify for + * the actual end session. + */ + public static final int NOTIFY_ENDSESSION = 12; + + /** + * A change in network configuration has occurred. + */ + public static final int NOTIFY_NETWORK_CHANGE = 9; + + /** + * Notifies for start of process of ending desktop session, + * logoff or shutdown. + */ + public static final int NOTIFY_QUERY_ENDSESSION = 11; + + /** + * Screen has been locked. + */ + public static final int NOTIFY_SCREEN_LOCKED = 7; + + /** + * Screen has been unlocked. + */ + public static final int NOTIFY_SCREEN_UNLOCKED = 8; + + /** + * Screensaver has been started. + */ + public static final int NOTIFY_SCREENSAVER_START = 4; + + /** + * Screensaver has been stopped. + */ + public static final int NOTIFY_SCREENSAVER_STOP = 6; + + /** + * Screensaver will stop. + */ + public static final int NOTIFY_SCREENSAVER_WILL_STOP = 5; + + /** + * Notify that computers is going to sleep. + */ + public static final int NOTIFY_SLEEP = 0; + + /** + * Notify that computer is wakeing up after stand by. + */ + public static final int NOTIFY_WAKE = 1; + + /** + * The native instance. + */ + private static long ptr; + + /** + * Init native library. + */ + static + { + try + { + // Don't load native library on Android to prevent the exception + if(!org.jitsi.util.OSUtils.IS_ANDROID) + { + JNIUtils.loadLibrary("sysactivitynotifications", + SystemActivityNotifications.class.getClassLoader()); + + ptr = allocAndInit(); + if (ptr == -1) + ptr = 0; + } + } + catch (Throwable t) + { + if (t instanceof ThreadDeath) + throw (ThreadDeath) t; + else + logger.warn("Failed to initialize native counterpart", t); + } + } + + /** + * Allocate native resources and gets a pointer. + * + * @return + */ + private static native long allocAndInit(); + + /** + * Returns the when was last input in milliseconds. The time when there was + * any activity on the computer. + * + * @return the last input in milliseconds + */ + public static native long getLastInput(); + + /** + * Whether native library is loaded. + * + * @return whether native library is loaded. + */ + public static boolean isLoaded() + { + return (ptr != 0); + } + + /** + * Release native resources. + * + * @param ptr + */ + private static native void release(long ptr); + + /** + * Sets notifier delegate. + * + * @param ptr + * @param delegate + */ + public static native void setDelegate( + long ptr, + NotificationsDelegate delegate); + + /** + * Sets delegate. + * + * @param delegate + */ + public static void setDelegate(NotificationsDelegate delegate) + { + if (ptr != 0) + setDelegate(ptr, delegate); + } + + /** + * Start. + */ + public static void start() + { + if (ptr != 0) + start(ptr); + } + + /** + * Start processing. + * + * @param ptr + */ + private static native void start(long ptr); + + /** + * Stop. + */ + public static void stop() + { + if (ptr != 0) + { + stop(ptr); + release(ptr); + ptr = 0; + } + } + + /** + * Stop processing. + * + * @param ptr + */ + private static native void stop(long ptr); + + /** + * Delegate class to be notified about changes. + */ + public interface NotificationsDelegate + { + /** + * Callback method when receiving notifications. + * + * @param type + */ + public void notify(int type); + + /** + * Callback method when receiving special network notifications. + * + * @param family family of network change (ipv6, ipv4) + * @param luidIndex unique index of interface + * @param name name of the interface + * @param type of the interface + * @param connected whether interface is connected or not. + */ + public void notifyNetworkChange( + int family, + long luidIndex, + String name, + long type, + boolean connected); + } +} diff --git a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java index ce1fc18..7783de8 100644 --- a/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java +++ b/src/net/java/sip/communicator/plugin/contactinfo/ContactInfoActivator.java @@ -1,208 +1,208 @@ -/* - * 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.plugin.contactinfo; - -import java.util.*; - -import net.java.sip.communicator.service.browserlauncher.*; -import net.java.sip.communicator.service.contactlist.*; -import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.util.*; - -import org.jitsi.service.configuration.*; -import org.osgi.framework.*; - -/** - * The Activator of the Contact Info bundle. - * - * @author Adam Goldstein - * @author Yana Stamcheva - */ -public class ContactInfoActivator implements BundleActivator -{ - private Logger logger = Logger.getLogger(ContactInfoActivator.class); - - /** - * Indicates if the contact info button is enabled in the chat window. - */ - private static final String ENABLED_IN_CHAT_WINDOW_PROP - = "net.java.sip.communicator.plugin.contactinfo." + - "ENABLED_IN_CHAT_WINDOW_PROP"; - - /** - * Indicates if the contact info button is enabled in the call window. - */ - private static final String ENABLED_IN_CALL_WINDOW_PROP - = "net.java.sip.communicator.plugin.contactinfo." + - "ENABLED_IN_CALL_WINDOW_PROP"; - - private static BrowserLauncherService browserLauncherService; - - /** - * The image loader service implementation. - */ - private static ImageLoaderService imageLoaderService = null; - - /** - * The contact list service implementation. - */ - private static MetaContactListService metaCListService; - - static BundleContext bundleContext; - - /** - * Starts this bundle. - */ - public void start(BundleContext bc) throws Exception - { - bundleContext = bc; - - Hashtable containerFilter - = new Hashtable(); - containerFilter.put( - Container.CONTAINER_ID, - Container.CONTAINER_CONTACT_RIGHT_BUTTON_MENU.getID()); - - bundleContext.registerService( - PluginComponentFactory.class.getName(), - new ContactInfoPluginComponentFactory( - Container.CONTAINER_CONTACT_RIGHT_BUTTON_MENU), - containerFilter); - - if(getConfigService().getBoolean(ENABLED_IN_CHAT_WINDOW_PROP, false)) - { - containerFilter = new Hashtable(); - containerFilter.put( - Container.CONTAINER_ID, - Container.CONTAINER_CHAT_TOOL_BAR.getID()); - - bundleContext.registerService( - PluginComponentFactory.class.getName(), - new ContactInfoPluginComponentFactory( - Container.CONTAINER_CHAT_TOOL_BAR), - containerFilter); - } - - if(getConfigService().getBoolean(ENABLED_IN_CALL_WINDOW_PROP, false)) - { - containerFilter = new Hashtable(); - containerFilter.put( - Container.CONTAINER_ID, - Container.CONTAINER_CALL_DIALOG.getID()); - - bundleContext.registerService( - PluginComponentFactory.class.getName(), - new ContactInfoPluginComponentFactory( - Container.CONTAINER_CALL_DIALOG), - containerFilter); - } - - if (logger.isInfoEnabled()) - logger.info("CONTACT INFO... [REGISTERED]"); - } - - public void stop(BundleContext bc) throws Exception - { - } - - /** - * Returns the BrowserLauncherService obtained from the bundle - * context. - * @return the BrowserLauncherService obtained from the bundle - * context - */ - public static BrowserLauncherService getBrowserLauncher() - { - if (browserLauncherService == null) - { - ServiceReference serviceReference = bundleContext - .getServiceReference(BrowserLauncherService.class.getName()); - - browserLauncherService = (BrowserLauncherService) bundleContext - .getService(serviceReference); - } - - return browserLauncherService; - } - - /** - * Returns the imageLoaderService instance, if missing query osgi for it. - * @return the imageLoaderService. - */ - public static ImageLoaderService getImageLoaderService() - { - if(imageLoaderService == null) - { - imageLoaderService - = ServiceUtils.getService( - bundleContext, - ImageLoaderService.class); - } - - return imageLoaderService; - } - - /** - * Returns the MetaContactListService obtained from the bundle - * context. - * @return the MetaContactListService obtained from the bundle - * context - */ - public static MetaContactListService getContactListService() - { - if (metaCListService == null) - { - metaCListService - = ServiceUtils.getService( - bundleContext, - MetaContactListService.class); - } - return metaCListService; - } - - /** - * Returns a reference to a ConfigurationService implementation currently - * registered in the bundle context or null if no such implementation was - * found. - * - * @return a currently valid implementation of the ConfigurationService. - */ - public static ConfigurationService getConfigService() - { - return ServiceUtils.getService(bundleContext, - ConfigurationService.class); - } - - /** - * Contact info create factory. - */ - private class ContactInfoPluginComponentFactory - extends PluginComponentFactory - { - ContactInfoPluginComponentFactory(Container c) - { - super(c); - } - - @Override - protected PluginComponent getPluginInstance() - { - return new ContactInfoMenuItem(getContainer(), this); - } - } -} +/* + * 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.plugin.contactinfo; + +import java.util.*; + +import net.java.sip.communicator.service.browserlauncher.*; +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.util.*; + +import org.jitsi.service.configuration.*; +import org.osgi.framework.*; + +/** + * The Activator of the Contact Info bundle. + * + * @author Adam Goldstein + * @author Yana Stamcheva + */ +public class ContactInfoActivator implements BundleActivator +{ + private Logger logger = Logger.getLogger(ContactInfoActivator.class); + + /** + * Indicates if the contact info button is enabled in the chat window. + */ + private static final String ENABLED_IN_CHAT_WINDOW_PROP + = "net.java.sip.communicator.plugin.contactinfo." + + "ENABLED_IN_CHAT_WINDOW_PROP"; + + /** + * Indicates if the contact info button is enabled in the call window. + */ + private static final String ENABLED_IN_CALL_WINDOW_PROP + = "net.java.sip.communicator.plugin.contactinfo." + + "ENABLED_IN_CALL_WINDOW_PROP"; + + private static BrowserLauncherService browserLauncherService; + + /** + * The image loader service implementation. + */ + private static ImageLoaderService imageLoaderService = null; + + /** + * The contact list service implementation. + */ + private static MetaContactListService metaCListService; + + static BundleContext bundleContext; + + /** + * Starts this bundle. + */ + public void start(BundleContext bc) throws Exception + { + bundleContext = bc; + + Hashtable containerFilter + = new Hashtable(); + containerFilter.put( + Container.CONTAINER_ID, + Container.CONTAINER_CONTACT_RIGHT_BUTTON_MENU.getID()); + + bundleContext.registerService( + PluginComponentFactory.class.getName(), + new ContactInfoPluginComponentFactory( + Container.CONTAINER_CONTACT_RIGHT_BUTTON_MENU), + containerFilter); + + if(getConfigService().getBoolean(ENABLED_IN_CHAT_WINDOW_PROP, false)) + { + containerFilter = new Hashtable(); + containerFilter.put( + Container.CONTAINER_ID, + Container.CONTAINER_CHAT_TOOL_BAR.getID()); + + bundleContext.registerService( + PluginComponentFactory.class.getName(), + new ContactInfoPluginComponentFactory( + Container.CONTAINER_CHAT_TOOL_BAR), + containerFilter); + } + + if(getConfigService().getBoolean(ENABLED_IN_CALL_WINDOW_PROP, false)) + { + containerFilter = new Hashtable(); + containerFilter.put( + Container.CONTAINER_ID, + Container.CONTAINER_CALL_DIALOG.getID()); + + bundleContext.registerService( + PluginComponentFactory.class.getName(), + new ContactInfoPluginComponentFactory( + Container.CONTAINER_CALL_DIALOG), + containerFilter); + } + + if (logger.isInfoEnabled()) + logger.info("CONTACT INFO... [REGISTERED]"); + } + + public void stop(BundleContext bc) throws Exception + { + } + + /** + * Returns the BrowserLauncherService obtained from the bundle + * context. + * @return the BrowserLauncherService obtained from the bundle + * context + */ + public static BrowserLauncherService getBrowserLauncher() + { + if (browserLauncherService == null) + { + ServiceReference serviceReference = bundleContext + .getServiceReference(BrowserLauncherService.class.getName()); + + browserLauncherService = (BrowserLauncherService) bundleContext + .getService(serviceReference); + } + + return browserLauncherService; + } + + /** + * Returns the imageLoaderService instance, if missing query osgi for it. + * @return the imageLoaderService. + */ + public static ImageLoaderService getImageLoaderService() + { + if(imageLoaderService == null) + { + imageLoaderService + = ServiceUtils.getService( + bundleContext, + ImageLoaderService.class); + } + + return imageLoaderService; + } + + /** + * Returns the MetaContactListService obtained from the bundle + * context. + * @return the MetaContactListService obtained from the bundle + * context + */ + public static MetaContactListService getContactListService() + { + if (metaCListService == null) + { + metaCListService + = ServiceUtils.getService( + bundleContext, + MetaContactListService.class); + } + return metaCListService; + } + + /** + * Returns a reference to a ConfigurationService implementation currently + * registered in the bundle context or null if no such implementation was + * found. + * + * @return a currently valid implementation of the ConfigurationService. + */ + public static ConfigurationService getConfigService() + { + return ServiceUtils.getService(bundleContext, + ConfigurationService.class); + } + + /** + * Contact info create factory. + */ + private class ContactInfoPluginComponentFactory + extends PluginComponentFactory + { + ContactInfoPluginComponentFactory(Container c) + { + super(c); + } + + @Override + protected PluginComponent getPluginInstance() + { + return new ContactInfoMenuItem(getContainer(), this); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java b/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java index a1284cd..9d13fe2 100644 --- a/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java +++ b/src/net/java/sip/communicator/plugin/desktoputil/X509CertificatePanel.java @@ -1,4 +1,4 @@ -/* +/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,490 +15,490 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.plugin.desktoputil; - -import java.awt.*; -import java.security.*; -import java.security.cert.*; -import java.security.cert.Certificate; -import java.security.interfaces.*; -import java.util.*; - -import javax.naming.*; -import javax.naming.ldap.*; -import javax.security.auth.x500.*; -import javax.swing.*; -import javax.swing.border.*; -import javax.swing.event.*; -import javax.swing.text.*; -import javax.swing.tree.*; - -import org.jitsi.service.resources.*; - -/** - * Panel that shows the content of an X509Certificate. - */ -public class X509CertificatePanel - extends TransparentPanel -{ - private static final long serialVersionUID = -8368302061995971947L; - - private final JEditorPane infoTextPane = new JEditorPane(); - - private final ResourceManagementService R - = DesktopUtilActivator.getResources(); - - /** - * Constructs a X509 certificate panel from a single certificate. - * If a chain is available instead use the second constructor. - * This constructor is kept for backwards compatibility and for convenience - * when there is only one certificate of interest. - * - * @param certificate X509Certificate object - */ - public X509CertificatePanel(Certificate certificate) - { - this(new Certificate[] - { - certificate - }); - } - - /** - * Constructs a X509 certificate panel. - * - * @param certificates X509Certificate objects - */ - public X509CertificatePanel(Certificate[] certificates) - { - setLayout(new BorderLayout(5, 5)); - - // Certificate chain list - TransparentPanel topPanel = new TransparentPanel(new BorderLayout()); - topPanel.add(new JLabel("" - + R.getI18NString("service.gui.CERT_INFO_CHAIN") - + ""), BorderLayout.NORTH); - - DefaultMutableTreeNode top = new DefaultMutableTreeNode(); - DefaultMutableTreeNode previous = top; - for (int i = certificates.length - 1; i >= 0; i--) - { - Certificate cert = certificates[i]; - DefaultMutableTreeNode next = new DefaultMutableTreeNode(cert); - previous.add(next); - previous = next; - } - JTree tree = new JTree(top); - tree.setBorder(new BevelBorder(BevelBorder.LOWERED)); - tree.setRootVisible(false); - tree.setExpandsSelectedPaths(true); - tree.getSelectionModel().setSelectionMode( - TreeSelectionModel.SINGLE_TREE_SELECTION); - tree.setCellRenderer(new DefaultTreeCellRenderer() - { - - @Override - public Component getTreeCellRendererComponent(JTree tree, - Object value, boolean sel, boolean expanded, boolean leaf, - int row, boolean hasFocus) - { - JLabel component = (JLabel) super.getTreeCellRendererComponent( - tree, value, sel, expanded, leaf, row, hasFocus); - if (value instanceof DefaultMutableTreeNode) - { - Object o = ((DefaultMutableTreeNode) value).getUserObject(); - if (o instanceof X509Certificate) - { - component.setText( - getSimplifiedName((X509Certificate) o)); - } - else - { - // We don't know how to represent this certificate type, - // let's use the first 20 characters - String text = o.toString(); - if (text.length() > 20) - { - text = text.substring(0, 20); - } - component.setText(text); - } - } - return component; - } - - }); - tree.getSelectionModel().addTreeSelectionListener( - new TreeSelectionListener() - { - - @Override - public void valueChanged(TreeSelectionEvent e) - { - valueChangedPerformed(e); - } - }); - tree.setSelectionPath(new TreePath((( - (DefaultTreeModel)tree.getModel()).getPathToRoot(previous)))); - topPanel.add(tree, BorderLayout.CENTER); - - add(topPanel, BorderLayout.NORTH); - - // Certificate details pane - Caret caret = infoTextPane.getCaret(); - if (caret instanceof DefaultCaret) - { - ((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.NEVER_UPDATE); - } - - /* - * Make JEditorPane respect our default font because we will be using it - * to just display text. - */ - infoTextPane.putClientProperty( - JEditorPane.HONOR_DISPLAY_PROPERTIES, - true); - - infoTextPane.setOpaque(false); - infoTextPane.setEditable(false); - infoTextPane.setContentType("text/html"); - infoTextPane.setText(toString(certificates[0])); - - final JScrollPane certScroll = new JScrollPane(infoTextPane); - certScroll.setPreferredSize(new Dimension(300, 500)); - add(certScroll, BorderLayout.CENTER); - } - - /** - * Creates a String representation of the given object. - * @param certificate to print - * @return the String representation - */ - private String toString(Object certificate) - { - final StringBuilder sb = new StringBuilder(); - sb.append("\n"); - - if (certificate instanceof X509Certificate) - { - renderX509(sb, (X509Certificate) certificate); - } - else - { - sb.append("
\n");
-            sb.append(certificate.toString());
-            sb.append("
\n"); - } - - sb.append(""); - return sb.toString(); - } - - /** - * Appends an HTML representation of the given X509Certificate. - * @param sb StringBuilder to append to - * @param certificate to print - */ - private void renderX509(StringBuilder sb, X509Certificate certificate) - { - X500Principal issuer = certificate.getIssuerX500Principal(); - X500Principal subject = certificate.getSubjectX500Principal(); - - sb.append("\n"); - - // subject - addTitle(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_TO")); - try - { - for(Rdn name : new LdapName(subject.getName()).getRdns()) - { - String nameType = name.getType(); - String lblKey = "service.gui.CERT_INFO_" + nameType; - String lbl = R.getI18NString(lblKey); - - if ((lbl == null) || ("!" + lblKey + "!").equals(lbl)) - lbl = nameType; - - final String value; - Object nameValue = name.getValue(); - - if (nameValue instanceof byte[]) - { - byte[] nameValueAsByteArray = (byte[]) nameValue; - - value - = getHex(nameValueAsByteArray) + " (" - + new String(nameValueAsByteArray) + ")"; - } - else - value = nameValue.toString(); - - addField(sb, lbl, value); - } - } - catch (InvalidNameException ine) - { - addField(sb, R.getI18NString("service.gui.CERT_INFO_CN"), - subject.getName()); - } - - // issuer - addTitle(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_BY")); - try - { - for(Rdn name : new LdapName(issuer.getName()).getRdns()) - { - String nameType = name.getType(); - String lblKey = "service.gui.CERT_INFO_" + nameType; - String lbl = R.getI18NString(lblKey); - - if ((lbl == null) || ("!" + lblKey + "!").equals(lbl)) - lbl = nameType; - - final String value; - Object nameValue = name.getValue(); - - if (nameValue instanceof byte[]) - { - byte[] nameValueAsByteArray = (byte[]) nameValue; - - value - = getHex(nameValueAsByteArray) + " (" - + new String(nameValueAsByteArray) + ")"; - } - else - value = nameValue.toString(); - - addField(sb, lbl, value); - } - } - catch (InvalidNameException ine) - { - addField(sb, R.getI18NString("service.gui.CERT_INFO_CN"), - issuer.getName()); - } - - // validity - addTitle(sb, R.getI18NString("service.gui.CERT_INFO_VALIDITY")); - addField(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_ON"), - certificate.getNotBefore().toString()); - addField(sb, R.getI18NString("service.gui.CERT_INFO_EXPIRES_ON"), - certificate.getNotAfter().toString()); - - addTitle(sb, R.getI18NString("service.gui.CERT_INFO_FINGERPRINTS")); - try - { - String sha1String = getThumbprint(certificate, "SHA1"); - String md5String = getThumbprint(certificate, "MD5"); - - addField(sb, "SHA1:", sha1String); - addField(sb, "MD5:", md5String); - } - catch (CertificateException e) - { - // do nothing as we cannot show this value - } - - addTitle(sb, R.getI18NString("service.gui.CERT_INFO_CERT_DETAILS")); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_SER_NUM"), - certificate.getSerialNumber().toString()); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_VER"), - String.valueOf(certificate.getVersion())); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_SIGN_ALG"), - String.valueOf(certificate.getSigAlgName())); - - addTitle(sb, R.getI18NString("service.gui.CERT_INFO_PUB_KEY_INFO")); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_ALG"), - certificate.getPublicKey().getAlgorithm()); - - if(certificate.getPublicKey().getAlgorithm().equals("RSA")) - { - RSAPublicKey key = (RSAPublicKey)certificate.getPublicKey(); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_PUB_KEY"), - R.getI18NString( - "service.gui.CERT_INFO_KEY_BYTES_PRINT", - new String[]{ - String.valueOf(key.getModulus().toByteArray().length-1), - key.getModulus().toString(16) - })); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_EXP"), - key.getPublicExponent().toString()); - - addField(sb, R.getI18NString("service.gui.CERT_INFO_KEY_SIZE"), - R.getI18NString( - "service.gui.CERT_INFO_KEY_BITS_PRINT", - new String[]{ - String.valueOf(key.getModulus().bitLength())})); - } - else if(certificate.getPublicKey().getAlgorithm().equals("DSA")) - { - DSAPublicKey key = - (DSAPublicKey)certificate.getPublicKey(); - - addField(sb, "Y:", key.getY().toString(16)); - } - - addField(sb, R.getI18NString("service.gui.CERT_INFO_SIGN"), - R.getI18NString( - "service.gui.CERT_INFO_KEY_BYTES_PRINT", - new String[]{ - String.valueOf(certificate.getSignature().length), - getHex(certificate.getSignature()) - })); - - sb.append("
\n"); - } - - /** - * Add a title. - * - * @param sb StringBuilder to append to - * @param title to print - */ - private void addTitle(StringBuilder sb, String title) - { - sb.append("

") - .append(title).append("

\n"); - } - - /** - * Add a field. - * @param sb StringBuilder to append to - * @param field name of the certificate field - * @param value to print - */ - private void addField(StringBuilder sb, String field, String value) - { - sb.append("") - .append("") - .append(field).append("") - .append("").append(value).append("") - .append("\n"); - } - - /** - * Converts the byte array to hex string. - * @param raw the data. - * @return the hex string. - */ - private String getHex( byte [] raw ) - { - if (raw == null) - return null; - - StringBuilder hex = new StringBuilder(2 * raw.length); - Formatter f = new Formatter(hex); - try - { - for (byte b : raw) - f.format("%02x", b); - } - finally - { - f.close(); - } - return hex.toString(); - } - - /** - * Calculates the hash of the certificate known as the "thumbprint" - * and returns it as a string representation. - * - * @param cert The certificate to hash. - * @param algorithm The hash algorithm to use. - * @return The SHA-1 hash of the certificate. - * @throws CertificateException - */ - private static String getThumbprint(X509Certificate cert, String algorithm) - throws CertificateException - { - MessageDigest digest; - try - { - digest = MessageDigest.getInstance(algorithm); - } - catch (NoSuchAlgorithmException e) - { - throw new CertificateException(e); - } - byte[] encodedCert = cert.getEncoded(); - StringBuilder sb = new StringBuilder(encodedCert.length * 2); - Formatter f = new Formatter(sb); - try - { - for (byte b : digest.digest(encodedCert)) - f.format("%02x", b); - } - finally - { - f.close(); - } - return sb.toString(); - } - - /** - * Construct a "simplified name" based on the subject DN from the - * certificate. The purpose is to have something shorter to display in the - * list. The name used is one of the following DN parts, if - * available, otherwise the complete DN: - * 'CN', 'OU' or else 'O'. - * @param cert to read subject DN from - * @return the simplified name - */ - private static String getSimplifiedName(X509Certificate cert) - { - final HashMap parts = new HashMap(); - try - { - for (Rdn name : new LdapName( - cert.getSubjectX500Principal().getName()).getRdns()) - { - if (name.getType() != null && name.getValue() != null) - { - parts.put(name.getType(), name.getValue().toString()); - } - } - } - catch (InvalidNameException ignored) // NOPMD - { - } - - String result = parts.get("CN"); - if (result == null) - { - result = parts.get("OU"); - } - if (result == null) - { - result = parts.get("O"); - } - if (result == null) - { - result = cert.getSubjectX500Principal().getName(); - } - return result; - } - - /** - * Called when the selection changed in the tree. - * Loads the selected certificate. - * @param e the event - */ - private void valueChangedPerformed(TreeSelectionEvent e) - { - Object o = e.getNewLeadSelectionPath().getLastPathComponent(); - if (o instanceof DefaultMutableTreeNode) - { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) o; - infoTextPane.setText(toString(node.getUserObject())); - } - } -} +package net.java.sip.communicator.plugin.desktoputil; + +import java.awt.*; +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.security.interfaces.*; +import java.util.*; + +import javax.naming.*; +import javax.naming.ldap.*; +import javax.security.auth.x500.*; +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.*; +import javax.swing.text.*; +import javax.swing.tree.*; + +import org.jitsi.service.resources.*; + +/** + * Panel that shows the content of an X509Certificate. + */ +public class X509CertificatePanel + extends TransparentPanel +{ + private static final long serialVersionUID = -8368302061995971947L; + + private final JEditorPane infoTextPane = new JEditorPane(); + + private final ResourceManagementService R + = DesktopUtilActivator.getResources(); + + /** + * Constructs a X509 certificate panel from a single certificate. + * If a chain is available instead use the second constructor. + * This constructor is kept for backwards compatibility and for convenience + * when there is only one certificate of interest. + * + * @param certificate X509Certificate object + */ + public X509CertificatePanel(Certificate certificate) + { + this(new Certificate[] + { + certificate + }); + } + + /** + * Constructs a X509 certificate panel. + * + * @param certificates X509Certificate objects + */ + public X509CertificatePanel(Certificate[] certificates) + { + setLayout(new BorderLayout(5, 5)); + + // Certificate chain list + TransparentPanel topPanel = new TransparentPanel(new BorderLayout()); + topPanel.add(new JLabel("" + + R.getI18NString("service.gui.CERT_INFO_CHAIN") + + ""), BorderLayout.NORTH); + + DefaultMutableTreeNode top = new DefaultMutableTreeNode(); + DefaultMutableTreeNode previous = top; + for (int i = certificates.length - 1; i >= 0; i--) + { + Certificate cert = certificates[i]; + DefaultMutableTreeNode next = new DefaultMutableTreeNode(cert); + previous.add(next); + previous = next; + } + JTree tree = new JTree(top); + tree.setBorder(new BevelBorder(BevelBorder.LOWERED)); + tree.setRootVisible(false); + tree.setExpandsSelectedPaths(true); + tree.getSelectionModel().setSelectionMode( + TreeSelectionModel.SINGLE_TREE_SELECTION); + tree.setCellRenderer(new DefaultTreeCellRenderer() + { + + @Override + public Component getTreeCellRendererComponent(JTree tree, + Object value, boolean sel, boolean expanded, boolean leaf, + int row, boolean hasFocus) + { + JLabel component = (JLabel) super.getTreeCellRendererComponent( + tree, value, sel, expanded, leaf, row, hasFocus); + if (value instanceof DefaultMutableTreeNode) + { + Object o = ((DefaultMutableTreeNode) value).getUserObject(); + if (o instanceof X509Certificate) + { + component.setText( + getSimplifiedName((X509Certificate) o)); + } + else + { + // We don't know how to represent this certificate type, + // let's use the first 20 characters + String text = o.toString(); + if (text.length() > 20) + { + text = text.substring(0, 20); + } + component.setText(text); + } + } + return component; + } + + }); + tree.getSelectionModel().addTreeSelectionListener( + new TreeSelectionListener() + { + + @Override + public void valueChanged(TreeSelectionEvent e) + { + valueChangedPerformed(e); + } + }); + tree.setSelectionPath(new TreePath((( + (DefaultTreeModel)tree.getModel()).getPathToRoot(previous)))); + topPanel.add(tree, BorderLayout.CENTER); + + add(topPanel, BorderLayout.NORTH); + + // Certificate details pane + Caret caret = infoTextPane.getCaret(); + if (caret instanceof DefaultCaret) + { + ((DefaultCaret) caret).setUpdatePolicy(DefaultCaret.NEVER_UPDATE); + } + + /* + * Make JEditorPane respect our default font because we will be using it + * to just display text. + */ + infoTextPane.putClientProperty( + JEditorPane.HONOR_DISPLAY_PROPERTIES, + true); + + infoTextPane.setOpaque(false); + infoTextPane.setEditable(false); + infoTextPane.setContentType("text/html"); + infoTextPane.setText(toString(certificates[0])); + + final JScrollPane certScroll = new JScrollPane(infoTextPane); + certScroll.setPreferredSize(new Dimension(300, 500)); + add(certScroll, BorderLayout.CENTER); + } + + /** + * Creates a String representation of the given object. + * @param certificate to print + * @return the String representation + */ + private String toString(Object certificate) + { + final StringBuilder sb = new StringBuilder(); + sb.append("\n"); + + if (certificate instanceof X509Certificate) + { + renderX509(sb, (X509Certificate) certificate); + } + else + { + sb.append("
\n");
+            sb.append(certificate.toString());
+            sb.append("
\n"); + } + + sb.append(""); + return sb.toString(); + } + + /** + * Appends an HTML representation of the given X509Certificate. + * @param sb StringBuilder to append to + * @param certificate to print + */ + private void renderX509(StringBuilder sb, X509Certificate certificate) + { + X500Principal issuer = certificate.getIssuerX500Principal(); + X500Principal subject = certificate.getSubjectX500Principal(); + + sb.append("\n"); + + // subject + addTitle(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_TO")); + try + { + for(Rdn name : new LdapName(subject.getName()).getRdns()) + { + String nameType = name.getType(); + String lblKey = "service.gui.CERT_INFO_" + nameType; + String lbl = R.getI18NString(lblKey); + + if ((lbl == null) || ("!" + lblKey + "!").equals(lbl)) + lbl = nameType; + + final String value; + Object nameValue = name.getValue(); + + if (nameValue instanceof byte[]) + { + byte[] nameValueAsByteArray = (byte[]) nameValue; + + value + = getHex(nameValueAsByteArray) + " (" + + new String(nameValueAsByteArray) + ")"; + } + else + value = nameValue.toString(); + + addField(sb, lbl, value); + } + } + catch (InvalidNameException ine) + { + addField(sb, R.getI18NString("service.gui.CERT_INFO_CN"), + subject.getName()); + } + + // issuer + addTitle(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_BY")); + try + { + for(Rdn name : new LdapName(issuer.getName()).getRdns()) + { + String nameType = name.getType(); + String lblKey = "service.gui.CERT_INFO_" + nameType; + String lbl = R.getI18NString(lblKey); + + if ((lbl == null) || ("!" + lblKey + "!").equals(lbl)) + lbl = nameType; + + final String value; + Object nameValue = name.getValue(); + + if (nameValue instanceof byte[]) + { + byte[] nameValueAsByteArray = (byte[]) nameValue; + + value + = getHex(nameValueAsByteArray) + " (" + + new String(nameValueAsByteArray) + ")"; + } + else + value = nameValue.toString(); + + addField(sb, lbl, value); + } + } + catch (InvalidNameException ine) + { + addField(sb, R.getI18NString("service.gui.CERT_INFO_CN"), + issuer.getName()); + } + + // validity + addTitle(sb, R.getI18NString("service.gui.CERT_INFO_VALIDITY")); + addField(sb, R.getI18NString("service.gui.CERT_INFO_ISSUED_ON"), + certificate.getNotBefore().toString()); + addField(sb, R.getI18NString("service.gui.CERT_INFO_EXPIRES_ON"), + certificate.getNotAfter().toString()); + + addTitle(sb, R.getI18NString("service.gui.CERT_INFO_FINGERPRINTS")); + try + { + String sha1String = getThumbprint(certificate, "SHA1"); + String md5String = getThumbprint(certificate, "MD5"); + + addField(sb, "SHA1:", sha1String); + addField(sb, "MD5:", md5String); + } + catch (CertificateException e) + { + // do nothing as we cannot show this value + } + + addTitle(sb, R.getI18NString("service.gui.CERT_INFO_CERT_DETAILS")); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_SER_NUM"), + certificate.getSerialNumber().toString()); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_VER"), + String.valueOf(certificate.getVersion())); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_SIGN_ALG"), + String.valueOf(certificate.getSigAlgName())); + + addTitle(sb, R.getI18NString("service.gui.CERT_INFO_PUB_KEY_INFO")); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_ALG"), + certificate.getPublicKey().getAlgorithm()); + + if(certificate.getPublicKey().getAlgorithm().equals("RSA")) + { + RSAPublicKey key = (RSAPublicKey)certificate.getPublicKey(); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_PUB_KEY"), + R.getI18NString( + "service.gui.CERT_INFO_KEY_BYTES_PRINT", + new String[]{ + String.valueOf(key.getModulus().toByteArray().length-1), + key.getModulus().toString(16) + })); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_EXP"), + key.getPublicExponent().toString()); + + addField(sb, R.getI18NString("service.gui.CERT_INFO_KEY_SIZE"), + R.getI18NString( + "service.gui.CERT_INFO_KEY_BITS_PRINT", + new String[]{ + String.valueOf(key.getModulus().bitLength())})); + } + else if(certificate.getPublicKey().getAlgorithm().equals("DSA")) + { + DSAPublicKey key = + (DSAPublicKey)certificate.getPublicKey(); + + addField(sb, "Y:", key.getY().toString(16)); + } + + addField(sb, R.getI18NString("service.gui.CERT_INFO_SIGN"), + R.getI18NString( + "service.gui.CERT_INFO_KEY_BYTES_PRINT", + new String[]{ + String.valueOf(certificate.getSignature().length), + getHex(certificate.getSignature()) + })); + + sb.append("
\n"); + } + + /** + * Add a title. + * + * @param sb StringBuilder to append to + * @param title to print + */ + private void addTitle(StringBuilder sb, String title) + { + sb.append("

") + .append(title).append("

\n"); + } + + /** + * Add a field. + * @param sb StringBuilder to append to + * @param field name of the certificate field + * @param value to print + */ + private void addField(StringBuilder sb, String field, String value) + { + sb.append("") + .append("") + .append(field).append("") + .append("").append(value).append("") + .append("\n"); + } + + /** + * Converts the byte array to hex string. + * @param raw the data. + * @return the hex string. + */ + private String getHex( byte [] raw ) + { + if (raw == null) + return null; + + StringBuilder hex = new StringBuilder(2 * raw.length); + Formatter f = new Formatter(hex); + try + { + for (byte b : raw) + f.format("%02x", b); + } + finally + { + f.close(); + } + return hex.toString(); + } + + /** + * Calculates the hash of the certificate known as the "thumbprint" + * and returns it as a string representation. + * + * @param cert The certificate to hash. + * @param algorithm The hash algorithm to use. + * @return The SHA-1 hash of the certificate. + * @throws CertificateException + */ + private static String getThumbprint(X509Certificate cert, String algorithm) + throws CertificateException + { + MessageDigest digest; + try + { + digest = MessageDigest.getInstance(algorithm); + } + catch (NoSuchAlgorithmException e) + { + throw new CertificateException(e); + } + byte[] encodedCert = cert.getEncoded(); + StringBuilder sb = new StringBuilder(encodedCert.length * 2); + Formatter f = new Formatter(sb); + try + { + for (byte b : digest.digest(encodedCert)) + f.format("%02x", b); + } + finally + { + f.close(); + } + return sb.toString(); + } + + /** + * Construct a "simplified name" based on the subject DN from the + * certificate. The purpose is to have something shorter to display in the + * list. The name used is one of the following DN parts, if + * available, otherwise the complete DN: + * 'CN', 'OU' or else 'O'. + * @param cert to read subject DN from + * @return the simplified name + */ + private static String getSimplifiedName(X509Certificate cert) + { + final HashMap parts = new HashMap(); + try + { + for (Rdn name : new LdapName( + cert.getSubjectX500Principal().getName()).getRdns()) + { + if (name.getType() != null && name.getValue() != null) + { + parts.put(name.getType(), name.getValue().toString()); + } + } + } + catch (InvalidNameException ignored) // NOPMD + { + } + + String result = parts.get("CN"); + if (result == null) + { + result = parts.get("OU"); + } + if (result == null) + { + result = parts.get("O"); + } + if (result == null) + { + result = cert.getSubjectX500Principal().getName(); + } + return result; + } + + /** + * Called when the selection changed in the tree. + * Loads the selected certificate. + * @param e the event + */ + private void valueChangedPerformed(TreeSelectionEvent e) + { + Object o = e.getNewLeadSelectionPath().getLastPathComponent(); + if (o instanceof DefaultMutableTreeNode) + { + DefaultMutableTreeNode node = (DefaultMutableTreeNode) o; + infoTextPane.setText(toString(node.getUserObject())); + } + } +} diff --git a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java index bce2fe7..c330879 100644 --- a/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java +++ b/src/net/java/sip/communicator/plugin/otr/ScOtrEngineImpl.java @@ -1,4 +1,4 @@ -/* +/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd @@ -15,1330 +15,1330 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package net.java.sip.communicator.plugin.otr; - -import java.net.*; -import java.security.*; -import java.util.*; -import java.util.concurrent.*; - -import net.java.otr4j.*; -import net.java.otr4j.crypto.*; -import net.java.otr4j.session.*; -import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; -import net.java.sip.communicator.plugin.otr.authdialog.*; -import net.java.sip.communicator.service.browserlauncher.*; -import net.java.sip.communicator.service.contactlist.*; -import net.java.sip.communicator.service.gui.*; -import net.java.sip.communicator.service.protocol.*; -import net.java.sip.communicator.util.*; - -import org.osgi.framework.*; - -/** - * - * @author George Politis - * @author Lyubomir Marinov - * @author Pawel Domas - * @author Marin Dzhigarov - * @author Danny van Heumen - */ -public class ScOtrEngineImpl - implements ScOtrEngine, - ChatLinkClickedListener, - ServiceListener -{ - private class ScOtrEngineHost - implements OtrEngineHost - { - @Override - public KeyPair getLocalKeyPair(SessionID sessionID) - { - AccountID accountID = - OtrActivator.getAccountIDByUID(sessionID.getAccountID()); - KeyPair keyPair = - OtrActivator.scOtrKeyManager.loadKeyPair(accountID); - if (keyPair == null) - OtrActivator.scOtrKeyManager.generateKeyPair(accountID); - - return OtrActivator.scOtrKeyManager.loadKeyPair(accountID); - } - - @Override - public OtrPolicy getSessionPolicy(SessionID sessionID) - { - return getContactPolicy(getOtrContact(sessionID).contact); - } - - @Override - public void injectMessage(SessionID sessionID, String messageText) - { - OtrContact otrContact = getOtrContact(sessionID); - Contact contact = otrContact.contact; - ContactResource resource = null; - - if (contact.supportResources()) - { - Collection resources = contact.getResources(); - if (resources != null) - { - for (ContactResource r : resources) - { - if (r.equals(otrContact.resource)) - { - resource = r; - break; - } - } - } - } - - OperationSetBasicInstantMessaging imOpSet - = contact - .getProtocolProvider() - .getOperationSet( - OperationSetBasicInstantMessaging.class); - - // This is a dirty way of detecting whether the injected message - // contains HTML markup. If this is the case then we should create - // the message with the appropriate content type so that the remote - // party can properly display the HTML. - // When otr4j injects QueryMessages it calls - // OtrEngineHost.getFallbackMessage() which is currently the only - // host method that uses HTML so we can simply check if the injected - // message contains the string that getFallbackMessage() returns. - String otrHtmlFallbackMessage - = ""; - String contentType - = messageText.contains(otrHtmlFallbackMessage) - ? OperationSetBasicInstantMessaging.HTML_MIME_TYPE - : OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE; - Message message - = imOpSet.createMessage( - messageText, - contentType, - OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, - null); - - injectedMessageUIDs.add(message.getMessageUID()); - imOpSet.sendInstantMessage(contact, resource, message); - } - - @Override - public void showError(SessionID sessionID, String err) - { - ScOtrEngineImpl.this.showError(sessionID, err); - } - - public void showWarning(SessionID sessionID, String warn) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.SYSTEM_MESSAGE, warn, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - } - - @Override - public void unreadableMessageReceived(SessionID sessionID) - throws OtrException - { - OtrContact otrContact = getOtrContact(sessionID); - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - - Contact contact = otrContact.contact; - String error = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.unreadablemsgreceived", - new String[] - {contact.getDisplayName() + resourceName}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.ERROR_MESSAGE, error, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - } - - @Override - public void unencryptedMessageReceived(SessionID sessionID, String msg) - throws OtrException - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - String warn = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.unencryptedmsgreceived"); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.SYSTEM_MESSAGE, warn, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - } - - @Override - public void smpError(SessionID sessionID, int tlvType, boolean cheated) - throws OtrException - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - logger.debug("SMP error occurred" - + ". Contact: " + contact.getDisplayName() - + ". TLV type: " + tlvType - + ". Cheated: " + cheated); - - String error = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.smperror"); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.ERROR_MESSAGE, error, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.setProgressFail(); - progressDialog.setVisible(true); - } - - @Override - public void smpAborted(SessionID sessionID) throws OtrException - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - Session session = otrEngine.getSession(sessionID); - if (session.isSmpInProgress()) - { - String warn = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.smpaborted", - new String[] {contact.getDisplayName()}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.SYSTEM_MESSAGE, warn, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - - SmpProgressDialog progressDialog = - progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.setProgressFail(); - progressDialog.setVisible(true); - } - } - - @Override - public void finishedSessionMessage(SessionID sessionID, String msgText) - throws OtrException - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; - String error = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.sessionfinishederror", - new String[] - {msgText, contact.getDisplayName() + resourceName}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.ERROR_MESSAGE, error, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - } - - @Override - public void requireEncryptedMessage(SessionID sessionID, String msgText) - throws OtrException - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - String error = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.requireencryption", - new String[] - {msgText}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.ERROR_MESSAGE, error, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - } - - @Override - public byte[] getLocalFingerprintRaw(SessionID sessionID) - { - AccountID accountID = - OtrActivator.getAccountIDByUID(sessionID.getAccountID()); - return - OtrActivator.scOtrKeyManager.getLocalFingerprintRaw(accountID); - } - - @Override - public void askForSecret( - SessionID sessionID, InstanceTag receiverTag, String question) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - SmpAuthenticateBuddyDialog dialog = - new SmpAuthenticateBuddyDialog( - otrContact, receiverTag, question); - dialog.setVisible(true); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.init(); - progressDialog.setVisible(true); - } - - @Override - public void verify( - SessionID sessionID, String fingerprint, boolean approved) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - OtrActivator.scOtrKeyManager.verify(otrContact, fingerprint); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.setProgressSuccess(); - progressDialog.setVisible(true); - } - - @Override - public void unverify(SessionID sessionID, String fingerprint) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - OtrActivator.scOtrKeyManager.unverify(otrContact, fingerprint); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.setProgressFail(); - progressDialog.setVisible(true); - } - - @Override - public String getReplyForUnreadableMessage(SessionID sessionID) - { - AccountID accountID = - OtrActivator.getAccountIDByUID(sessionID.getAccountID()); - - return OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.unreadablemsgreply", - new String[] {accountID.getDisplayName(), - accountID.getDisplayName()}); - } - - @Override - public String getFallbackMessage(SessionID sessionID) - { - AccountID accountID = - OtrActivator.getAccountIDByUID(sessionID.getAccountID()); - - return OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.fallbackmessage", - new String[] {accountID.getDisplayName()}); - } - - @Override - public void multipleInstancesDetected(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; - String message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.multipleinstancesdetected", - new String[] - {contact.getDisplayName() + resourceName}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - message, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - } - - @Override - public void messageFromAnotherInstanceReceived(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; - String message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.msgfromanotherinstance", - new String[] - {contact.getDisplayName() + resourceName}); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - message, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - } - - /** - * Provide fragmenter instructions according to the Instant Messaging - * transport channel of the contact's protocol. - */ - @Override - public FragmenterInstructions getFragmenterInstructions( - final SessionID sessionID) - { - final OtrContact otrContact = getOtrContact(sessionID); - final OperationSetBasicInstantMessagingTransport transport = - otrContact.contact.getProtocolProvider().getOperationSet( - OperationSetBasicInstantMessagingTransport.class); - if (transport == null) - { - // There is no operation set for querying transport parameters. - // Assuming transport capabilities are unlimited. - if (logger.isDebugEnabled()) - { - logger.debug("No implementation of " - + "BasicInstantMessagingTransport available. Assuming " - + "OTR defaults for OTR fragmentation instructions."); - } - return null; - } - int messageSize = transport.getMaxMessageSize(otrContact.contact); - if (messageSize - == OperationSetBasicInstantMessagingTransport.UNLIMITED) - { - messageSize = FragmenterInstructions.UNLIMITED; - } - int numberOfMessages = - transport.getMaxNumberOfMessages(otrContact.contact); - if (numberOfMessages - == OperationSetBasicInstantMessagingTransport.UNLIMITED) - { - numberOfMessages = FragmenterInstructions.UNLIMITED; - } - if (logger.isDebugEnabled()) - { - logger.debug("OTR fragmentation instructions for sending a " - + "message to " + otrContact.contact.getDisplayName() - + " (" + otrContact.contact.getAddress() - + "). Maximum number of " + "messages: " + numberOfMessages - + ", maximum message size: " + messageSize); - } - return new FragmenterInstructions(numberOfMessages, messageSize); - } - } - - /** - * The max timeout period elapsed prior to establishing a TIMED_OUT session. - */ - private static final int SESSION_TIMEOUT = - OtrActivator.configService.getInt( - "net.java.sip.communicator.plugin.otr.SESSION_STATUS_TIMEOUT", - 30000); - - /** - * Manages the scheduling of TimerTasks that are used to set Contact's - * ScSessionStatus (to TIMED_OUT) after a period of time. - */ - private ScSessionStatusScheduler scheduler = new ScSessionStatusScheduler(); - - /** - * This mapping is used for taking care of keeping SessionStatus and - * ScSessionStatus in sync for every Session object. - */ - private Map scSessionStatusMap = - new ConcurrentHashMap(); - - private static final Map contactsMap = - new Hashtable(); - - private static final Map progressDialogMap = - new ConcurrentHashMap(); - - public static OtrContact getOtrContact(SessionID sessionID) - { - return contactsMap.get(new ScSessionID(sessionID)); - } - - /** - * Returns the ScSessionID for given UUID. - * @param guid the UUID identifying ScSessionID. - * @return the ScSessionID for given UUID or null - * if no matching session found. - */ - public static ScSessionID getScSessionForGuid(UUID guid) - { - for(ScSessionID scSessionID : contactsMap.keySet()) - { - if(scSessionID.getGUID().equals(guid)) - { - return scSessionID; - } - } - return null; - } - - public static SessionID getSessionID(OtrContact otrContact) - { - ProtocolProviderService pps = otrContact.contact.getProtocolProvider(); - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - SessionID sessionID - = new SessionID( - pps.getAccountID().getAccountUniqueID(), - otrContact.contact.getAddress() + resourceName, - pps.getProtocolName()); - - synchronized (contactsMap) - { - if(contactsMap.containsKey(new ScSessionID(sessionID))) - return sessionID; - - ScSessionID scSessionID = new ScSessionID(sessionID); - - contactsMap.put(scSessionID, otrContact); - } - - return sessionID; - } - - private final OtrConfigurator configurator = new OtrConfigurator(); - - private final List injectedMessageUIDs = new Vector(); - - private final List listeners = - new Vector(); - - /** - * The logger - */ - private final Logger logger = Logger.getLogger(ScOtrEngineImpl.class); - - private final OtrEngineHost otrEngineHost = new ScOtrEngineHost(); - - private final OtrSessionManager otrEngine; - - public ScOtrEngineImpl() - { - otrEngine = new OtrSessionManagerImpl(otrEngineHost); - - // Clears the map after previous instance - // This is required because of OSGi restarts in the same VM on Android - contactsMap.clear(); - scSessionStatusMap.clear(); - - this.otrEngine.addOtrEngineListener(new OtrEngineListener() - { - @Override - public void sessionStatusChanged(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - String resourceName = otrContact.resource != null ? - "/" + otrContact.resource.getResourceName() : ""; - Contact contact = otrContact.contact; - // Cancels any scheduled tasks that will change the - // ScSessionStatus for this Contact - scheduler.cancel(otrContact); - - ScSessionStatus scSessionStatus = getSessionStatus(otrContact); - String message = ""; - final Session session = otrEngine.getSession(sessionID); - switch (session.getSessionStatus()) - { - case ENCRYPTED: - scSessionStatus = ScSessionStatus.ENCRYPTED; - scSessionStatusMap.put(sessionID, scSessionStatus); - PublicKey remotePubKey = session.getRemotePublicKey(); - - String remoteFingerprint = null; - try - { - remoteFingerprint = - new OtrCryptoEngineImpl(). - getFingerprint(remotePubKey); - } - catch (OtrCryptoException e) - { - logger.debug( - "Could not get the fingerprint from the " - + "public key of contact: " + contact); - } - - List allFingerprintsOfContact = - OtrActivator.scOtrKeyManager. - getAllRemoteFingerprints(contact); - if (allFingerprintsOfContact != null) - { - if (!allFingerprintsOfContact.contains( - remoteFingerprint)) - { - OtrActivator.scOtrKeyManager.saveFingerprint( - contact, remoteFingerprint); - } - } - - if (!OtrActivator.scOtrKeyManager.isVerified( - contact, remoteFingerprint)) - { - OtrActivator.scOtrKeyManager.unverify( - otrContact, remoteFingerprint); - UUID sessionGuid = null; - for(ScSessionID scSessionID : contactsMap.keySet()) - { - if(scSessionID.getSessionID().equals(sessionID)) - { - sessionGuid = scSessionID.getGUID(); - break; - } - } - - OtrActivator.uiService.getChat(contact) - .addChatLinkClickedListener(ScOtrEngineImpl.this); - - String unverifiedSessionWarning - = OtrActivator.resourceService.getI18NString( - "plugin.otr.activator" - + ".unverifiedsessionwarning", - new String[] - { - contact.getDisplayName() + resourceName, - this.getClass().getName(), - "AUTHENTIFICATION", - sessionGuid.toString() - }); - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - unverifiedSessionWarning, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - - } - - // show info whether history is on or off - String otrAndHistoryMessage; - if(!OtrActivator.getMessageHistoryService() - .isHistoryLoggingEnabled() || - !isHistoryLoggingEnabled(contact)) - { - otrAndHistoryMessage = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.historyoff", - new String[]{ - OtrActivator.resourceService - .getSettingsString( - "service.gui.APPLICATION_NAME"), - this.getClass().getName(), - "showHistoryPopupMenu" - }); - } - else - { - otrAndHistoryMessage = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.historyon", - new String[]{ - OtrActivator.resourceService - .getSettingsString( - "service.gui.APPLICATION_NAME"), - this.getClass().getName(), - "showHistoryPopupMenu" - }); - } - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - otrAndHistoryMessage, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - - message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.multipleinstancesdetected", - new String[] - {contact.getDisplayName()}); - - if (contact.supportResources() - && contact.getResources() != null - && contact.getResources().size() > 1) - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), - new Date(), Chat.SYSTEM_MESSAGE, - message, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - - message - = OtrActivator.resourceService.getI18NString( - OtrActivator.scOtrKeyManager.isVerified( - contact, remoteFingerprint) - ? "plugin.otr.activator.sessionstared" - : "plugin.otr.activator" - + ".unverifiedsessionstared", - new String[] - {contact.getDisplayName() + resourceName}); - - break; - case FINISHED: - scSessionStatus = ScSessionStatus.FINISHED; - scSessionStatusMap.put(sessionID, scSessionStatus); - message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.sessionfinished", - new String[] - {contact.getDisplayName() + resourceName}); - break; - case PLAINTEXT: - scSessionStatus = ScSessionStatus.PLAINTEXT; - scSessionStatusMap.put(sessionID, scSessionStatus); - message = - OtrActivator.resourceService.getI18NString( - "plugin.otr.activator.sessionlost", new String[] - {contact.getDisplayName() + resourceName}); - break; - } - - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.SYSTEM_MESSAGE, message, - OperationSetBasicInstantMessaging.HTML_MIME_TYPE); - - for (ScOtrEngineListener l : getListeners()) - l.sessionStatusChanged(otrContact); - } - - @Override - public void multipleInstancesDetected(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - for (ScOtrEngineListener l : getListeners()) - l.multipleInstancesDetected(otrContact); - } - - @Override - public void outgoingSessionChanged(SessionID sessionID) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - for (ScOtrEngineListener l : getListeners()) - l.outgoingSessionChanged(otrContact); - } - }); - } - - /** - * Checks whether history is enabled for the metacontact containing - * the contact. - * @param contact the contact to check. - * @return whether chat logging is enabled while chatting - * with contact. - */ - private boolean isHistoryLoggingEnabled(Contact contact) - { - MetaContact metaContact = OtrActivator - .getContactListService().findMetaContactByContact(contact); - if(metaContact != null) - return OtrActivator.getMessageHistoryService() - .isHistoryLoggingEnabled(metaContact.getMetaUID()); - else - return true; - } - - @Override - public void addListener(ScOtrEngineListener l) - { - synchronized (listeners) - { - if (!listeners.contains(l)) - listeners.add(l); - } - } - - @Override - public void chatLinkClicked(URI url) - { - String action = url.getPath(); - if(action.equals("/AUTHENTIFICATION")) - { - UUID guid = UUID.fromString(url.getQuery()); - - if(guid == null) - throw new RuntimeException( - "No UUID found in OTR authenticate URL"); - - // Looks for registered action handler - OtrActionHandler actionHandler - = ServiceUtils.getService( - OtrActivator.bundleContext, - OtrActionHandler.class); - - if(actionHandler != null) - { - actionHandler.onAuthenticateLinkClicked(guid); - } - else - { - logger.error("No OtrActionHandler registered"); - } - } - } - - @Override - public void endSession(OtrContact otrContact) - { - SessionID sessionID = getSessionID(otrContact); - try - { - setSessionStatus(otrContact, ScSessionStatus.PLAINTEXT); - - otrEngine.getSession(sessionID).endSession(); - } - catch (OtrException e) - { - showError(sessionID, e.getMessage()); - } - } - - @Override - public OtrPolicy getContactPolicy(Contact contact) - { - ProtocolProviderService pps = contact.getProtocolProvider(); - SessionID sessionID - = new SessionID( - pps.getAccountID().getAccountUniqueID(), - contact.getAddress(), - pps.getProtocolName()); - int policy = - this.configurator.getPropertyInt(sessionID + "contact_policy", - -1); - if (policy < 0) - return getGlobalPolicy(); - else - return new OtrPolicyImpl(policy); - } - - @Override - public OtrPolicy getGlobalPolicy() - { - /* - * SEND_WHITESPACE_TAG bit will be lowered until we stabilize the OTR. - */ - int defaultScOtrPolicy = - OtrPolicy.OTRL_POLICY_DEFAULT & ~OtrPolicy.SEND_WHITESPACE_TAG; - return new OtrPolicyImpl(this.configurator.getPropertyInt( - "GLOBAL_POLICY", defaultScOtrPolicy)); - } - - /** - * Gets a copy of the list of ScOtrEngineListeners registered with - * this instance which may safely be iterated without the risk of a - * ConcurrentModificationException. - * - * @return a copy of the list of ScOtrEngineListeners registered - * with this instance which may safely be iterated without the risk of a - * ConcurrentModificationException - */ - private ScOtrEngineListener[] getListeners() - { - synchronized (listeners) - { - return listeners.toArray(new ScOtrEngineListener[listeners.size()]); - } - } - - /** - * Manages the scheduling of TimerTasks that are used to set Contact's - * ScSessionStatus after a period of time. - * - * @author Marin Dzhigarov - */ - private class ScSessionStatusScheduler - { - private final Timer timer = new Timer(); - - private final Map tasks = - new ConcurrentHashMap(); - - public void scheduleScSessionStatusChange( - final OtrContact otrContact, final ScSessionStatus status) - { - cancel(otrContact); - - TimerTask task - = new TimerTask() - { - @Override - public void run() - { - setSessionStatus(otrContact, status); - } - }; - timer.schedule(task, SESSION_TIMEOUT); - tasks.put(otrContact, task); - } - - public void cancel(final OtrContact otrContact) - { - TimerTask task = tasks.get(otrContact); - if (task != null) - task.cancel(); - tasks.remove(otrContact); - } - - public void serviceChanged(ServiceEvent ev) - { - Object service - = OtrActivator.bundleContext.getService( - ev.getServiceReference()); - - if (!(service instanceof ProtocolProviderService)) - return; - - if (ev.getType() == ServiceEvent.UNREGISTERING) - { - ProtocolProviderService provider - = (ProtocolProviderService) service; - - Iterator i = tasks.keySet().iterator(); - - while (i.hasNext()) - { - OtrContact otrContact = i.next(); - if (provider.equals( - otrContact.contact.getProtocolProvider())) - { - cancel(otrContact); - i.remove(); - } - } - } - } - } - - private void setSessionStatus(OtrContact contact, ScSessionStatus status) - { - scSessionStatusMap.put(getSessionID(contact), status); - scheduler.cancel(contact); - for (ScOtrEngineListener l : getListeners()) - l.sessionStatusChanged(contact); - } - - @Override - public ScSessionStatus getSessionStatus(OtrContact contact) - { - SessionID sessionID = getSessionID(contact); - SessionStatus sessionStatus = otrEngine.getSession(sessionID).getSessionStatus(); - ScSessionStatus scSessionStatus = null; - if (!scSessionStatusMap.containsKey(sessionID)) - { - switch (sessionStatus) - { - case PLAINTEXT: - scSessionStatus = ScSessionStatus.PLAINTEXT; - break; - case ENCRYPTED: - scSessionStatus = ScSessionStatus.ENCRYPTED; - break; - case FINISHED: - scSessionStatus = ScSessionStatus.FINISHED; - break; - } - scSessionStatusMap.put(sessionID, scSessionStatus); - } - return scSessionStatusMap.get(sessionID); - } - - @Override - public boolean isMessageUIDInjected(String mUID) - { - return injectedMessageUIDs.contains(mUID); - } - - @Override - public void launchHelp() - { - ServiceReference ref = - OtrActivator.bundleContext - .getServiceReference(BrowserLauncherService.class.getName()); - - if (ref == null) - return; - - BrowserLauncherService service = - (BrowserLauncherService) OtrActivator.bundleContext.getService(ref); - - service.openURL(OtrActivator.resourceService - .getI18NString("plugin.otr.authbuddydialog.HELP_URI")); - } - - @Override - public void refreshSession(OtrContact otrContact) - { - SessionID sessionID = getSessionID(otrContact); - try - { - otrEngine.getSession(sessionID).refreshSession(); - } - catch (OtrException e) - { - logger.error("Error refreshing session", e); - showError(sessionID, e.getMessage()); - } - } - - @Override - public void removeListener(ScOtrEngineListener l) - { - synchronized (listeners) - { - listeners.remove(l); - } - } - - /** - * Cleans the contactsMap when ProtocolProviderService - * gets unregistered. - */ - @Override - public void serviceChanged(ServiceEvent ev) - { - Object service - = OtrActivator.bundleContext.getService(ev.getServiceReference()); - - if (!(service instanceof ProtocolProviderService)) - return; - - if (ev.getType() == ServiceEvent.UNREGISTERING) - { - if (logger.isDebugEnabled()) - { - logger.debug( - "Unregistering a ProtocolProviderService, cleaning" - + " OTR's ScSessionID to Contact map."); - logger.debug( - "Unregistering a ProtocolProviderService, cleaning" - + " OTR's Contact to SpmProgressDialog map."); - } - - ProtocolProviderService provider - = (ProtocolProviderService) service; - - synchronized(contactsMap) - { - Iterator i = contactsMap.values().iterator(); - - while (i.hasNext()) +package net.java.sip.communicator.plugin.otr; + +import java.net.*; +import java.security.*; +import java.util.*; +import java.util.concurrent.*; + +import net.java.otr4j.*; +import net.java.otr4j.crypto.*; +import net.java.otr4j.session.*; +import net.java.sip.communicator.plugin.otr.OtrContactManager.OtrContact; +import net.java.sip.communicator.plugin.otr.authdialog.*; +import net.java.sip.communicator.service.browserlauncher.*; +import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.gui.*; +import net.java.sip.communicator.service.protocol.*; +import net.java.sip.communicator.util.*; + +import org.osgi.framework.*; + +/** + * + * @author George Politis + * @author Lyubomir Marinov + * @author Pawel Domas + * @author Marin Dzhigarov + * @author Danny van Heumen + */ +public class ScOtrEngineImpl + implements ScOtrEngine, + ChatLinkClickedListener, + ServiceListener +{ + private class ScOtrEngineHost + implements OtrEngineHost + { + @Override + public KeyPair getLocalKeyPair(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + KeyPair keyPair = + OtrActivator.scOtrKeyManager.loadKeyPair(accountID); + if (keyPair == null) + OtrActivator.scOtrKeyManager.generateKeyPair(accountID); + + return OtrActivator.scOtrKeyManager.loadKeyPair(accountID); + } + + @Override + public OtrPolicy getSessionPolicy(SessionID sessionID) + { + return getContactPolicy(getOtrContact(sessionID).contact); + } + + @Override + public void injectMessage(SessionID sessionID, String messageText) + { + OtrContact otrContact = getOtrContact(sessionID); + Contact contact = otrContact.contact; + ContactResource resource = null; + + if (contact.supportResources()) + { + Collection resources = contact.getResources(); + if (resources != null) + { + for (ContactResource r : resources) + { + if (r.equals(otrContact.resource)) + { + resource = r; + break; + } + } + } + } + + OperationSetBasicInstantMessaging imOpSet + = contact + .getProtocolProvider() + .getOperationSet( + OperationSetBasicInstantMessaging.class); + + // This is a dirty way of detecting whether the injected message + // contains HTML markup. If this is the case then we should create + // the message with the appropriate content type so that the remote + // party can properly display the HTML. + // When otr4j injects QueryMessages it calls + // OtrEngineHost.getFallbackMessage() which is currently the only + // host method that uses HTML so we can simply check if the injected + // message contains the string that getFallbackMessage() returns. + String otrHtmlFallbackMessage + = ""; + String contentType + = messageText.contains(otrHtmlFallbackMessage) + ? OperationSetBasicInstantMessaging.HTML_MIME_TYPE + : OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE; + Message message + = imOpSet.createMessage( + messageText, + contentType, + OperationSetBasicInstantMessaging.DEFAULT_MIME_ENCODING, + null); + + injectedMessageUIDs.add(message.getMessageUID()); + imOpSet.sendInstantMessage(contact, resource, message); + } + + @Override + public void showError(SessionID sessionID, String err) + { + ScOtrEngineImpl.this.showError(sessionID, err); + } + + public void showWarning(SessionID sessionID, String warn) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.SYSTEM_MESSAGE, warn, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void unreadableMessageReceived(SessionID sessionID) + throws OtrException + { + OtrContact otrContact = getOtrContact(sessionID); + String resourceName = otrContact.resource != null ? + "/" + otrContact.resource.getResourceName() : ""; + + Contact contact = otrContact.contact; + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.unreadablemsgreceived", + new String[] + {contact.getDisplayName() + resourceName}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void unencryptedMessageReceived(SessionID sessionID, String msg) + throws OtrException + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + String warn = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.unencryptedmsgreceived"); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.SYSTEM_MESSAGE, warn, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void smpError(SessionID sessionID, int tlvType, boolean cheated) + throws OtrException + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + logger.debug("SMP error occurred" + + ". Contact: " + contact.getDisplayName() + + ". TLV type: " + tlvType + + ". Cheated: " + cheated); + + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.smperror"); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.setProgressFail(); + progressDialog.setVisible(true); + } + + @Override + public void smpAborted(SessionID sessionID) throws OtrException + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + Session session = otrEngine.getSession(sessionID); + if (session.isSmpInProgress()) + { + String warn = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.smpaborted", + new String[] {contact.getDisplayName()}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.SYSTEM_MESSAGE, warn, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + + SmpProgressDialog progressDialog = + progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.setProgressFail(); + progressDialog.setVisible(true); + } + } + + @Override + public void finishedSessionMessage(SessionID sessionID, String msgText) + throws OtrException + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + String resourceName = otrContact.resource != null ? + "/" + otrContact.resource.getResourceName() : ""; + Contact contact = otrContact.contact; + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.sessionfinishederror", + new String[] + {msgText, contact.getDisplayName() + resourceName}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void requireEncryptedMessage(SessionID sessionID, String msgText) + throws OtrException + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + String error = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.requireencryption", + new String[] + {msgText}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, error, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public byte[] getLocalFingerprintRaw(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + return + OtrActivator.scOtrKeyManager.getLocalFingerprintRaw(accountID); + } + + @Override + public void askForSecret( + SessionID sessionID, InstanceTag receiverTag, String question) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + SmpAuthenticateBuddyDialog dialog = + new SmpAuthenticateBuddyDialog( + otrContact, receiverTag, question); + dialog.setVisible(true); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.init(); + progressDialog.setVisible(true); + } + + @Override + public void verify( + SessionID sessionID, String fingerprint, boolean approved) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + OtrActivator.scOtrKeyManager.verify(otrContact, fingerprint); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.setProgressSuccess(); + progressDialog.setVisible(true); + } + + @Override + public void unverify(SessionID sessionID, String fingerprint) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + OtrActivator.scOtrKeyManager.unverify(otrContact, fingerprint); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.setProgressFail(); + progressDialog.setVisible(true); + } + + @Override + public String getReplyForUnreadableMessage(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + + return OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.unreadablemsgreply", + new String[] {accountID.getDisplayName(), + accountID.getDisplayName()}); + } + + @Override + public String getFallbackMessage(SessionID sessionID) + { + AccountID accountID = + OtrActivator.getAccountIDByUID(sessionID.getAccountID()); + + return OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.fallbackmessage", + new String[] {accountID.getDisplayName()}); + } + + @Override + public void multipleInstancesDetected(SessionID sessionID) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + String resourceName = otrContact.resource != null ? + "/" + otrContact.resource.getResourceName() : ""; + Contact contact = otrContact.contact; + String message = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.multipleinstancesdetected", + new String[] + {contact.getDisplayName() + resourceName}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), + new Date(), Chat.SYSTEM_MESSAGE, + message, + OperationSetBasicInstantMessaging.HTML_MIME_TYPE); + } + + @Override + public void messageFromAnotherInstanceReceived(SessionID sessionID) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + String resourceName = otrContact.resource != null ? + "/" + otrContact.resource.getResourceName() : ""; + Contact contact = otrContact.contact; + String message = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.msgfromanotherinstance", + new String[] + {contact.getDisplayName() + resourceName}); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), + new Date(), Chat.SYSTEM_MESSAGE, + message, + OperationSetBasicInstantMessaging.HTML_MIME_TYPE); + } + + /** + * Provide fragmenter instructions according to the Instant Messaging + * transport channel of the contact's protocol. + */ + @Override + public FragmenterInstructions getFragmenterInstructions( + final SessionID sessionID) + { + final OtrContact otrContact = getOtrContact(sessionID); + final OperationSetBasicInstantMessagingTransport transport = + otrContact.contact.getProtocolProvider().getOperationSet( + OperationSetBasicInstantMessagingTransport.class); + if (transport == null) + { + // There is no operation set for querying transport parameters. + // Assuming transport capabilities are unlimited. + if (logger.isDebugEnabled()) + { + logger.debug("No implementation of " + + "BasicInstantMessagingTransport available. Assuming " + + "OTR defaults for OTR fragmentation instructions."); + } + return null; + } + int messageSize = transport.getMaxMessageSize(otrContact.contact); + if (messageSize + == OperationSetBasicInstantMessagingTransport.UNLIMITED) + { + messageSize = FragmenterInstructions.UNLIMITED; + } + int numberOfMessages = + transport.getMaxNumberOfMessages(otrContact.contact); + if (numberOfMessages + == OperationSetBasicInstantMessagingTransport.UNLIMITED) + { + numberOfMessages = FragmenterInstructions.UNLIMITED; + } + if (logger.isDebugEnabled()) + { + logger.debug("OTR fragmentation instructions for sending a " + + "message to " + otrContact.contact.getDisplayName() + + " (" + otrContact.contact.getAddress() + + "). Maximum number of " + "messages: " + numberOfMessages + + ", maximum message size: " + messageSize); + } + return new FragmenterInstructions(numberOfMessages, messageSize); + } + } + + /** + * The max timeout period elapsed prior to establishing a TIMED_OUT session. + */ + private static final int SESSION_TIMEOUT = + OtrActivator.configService.getInt( + "net.java.sip.communicator.plugin.otr.SESSION_STATUS_TIMEOUT", + 30000); + + /** + * Manages the scheduling of TimerTasks that are used to set Contact's + * ScSessionStatus (to TIMED_OUT) after a period of time. + */ + private ScSessionStatusScheduler scheduler = new ScSessionStatusScheduler(); + + /** + * This mapping is used for taking care of keeping SessionStatus and + * ScSessionStatus in sync for every Session object. + */ + private Map scSessionStatusMap = + new ConcurrentHashMap(); + + private static final Map contactsMap = + new Hashtable(); + + private static final Map progressDialogMap = + new ConcurrentHashMap(); + + public static OtrContact getOtrContact(SessionID sessionID) + { + return contactsMap.get(new ScSessionID(sessionID)); + } + + /** + * Returns the ScSessionID for given UUID. + * @param guid the UUID identifying ScSessionID. + * @return the ScSessionID for given UUID or null + * if no matching session found. + */ + public static ScSessionID getScSessionForGuid(UUID guid) + { + for(ScSessionID scSessionID : contactsMap.keySet()) + { + if(scSessionID.getGUID().equals(guid)) + { + return scSessionID; + } + } + return null; + } + + public static SessionID getSessionID(OtrContact otrContact) + { + ProtocolProviderService pps = otrContact.contact.getProtocolProvider(); + String resourceName = otrContact.resource != null ? + "/" + otrContact.resource.getResourceName() : ""; + SessionID sessionID + = new SessionID( + pps.getAccountID().getAccountUniqueID(), + otrContact.contact.getAddress() + resourceName, + pps.getProtocolName()); + + synchronized (contactsMap) + { + if(contactsMap.containsKey(new ScSessionID(sessionID))) + return sessionID; + + ScSessionID scSessionID = new ScSessionID(sessionID); + + contactsMap.put(scSessionID, otrContact); + } + + return sessionID; + } + + private final OtrConfigurator configurator = new OtrConfigurator(); + + private final List injectedMessageUIDs = new Vector(); + + private final List listeners = + new Vector(); + + /** + * The logger + */ + private final Logger logger = Logger.getLogger(ScOtrEngineImpl.class); + + private final OtrEngineHost otrEngineHost = new ScOtrEngineHost(); + + private final OtrSessionManager otrEngine; + + public ScOtrEngineImpl() + { + otrEngine = new OtrSessionManagerImpl(otrEngineHost); + + // Clears the map after previous instance + // This is required because of OSGi restarts in the same VM on Android + contactsMap.clear(); + scSessionStatusMap.clear(); + + this.otrEngine.addOtrEngineListener(new OtrEngineListener() + { + @Override + public void sessionStatusChanged(SessionID sessionID) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + String resourceName = otrContact.resource != null ? + "/" + otrContact.resource.getResourceName() : ""; + Contact contact = otrContact.contact; + // Cancels any scheduled tasks that will change the + // ScSessionStatus for this Contact + scheduler.cancel(otrContact); + + ScSessionStatus scSessionStatus = getSessionStatus(otrContact); + String message = ""; + final Session session = otrEngine.getSession(sessionID); + switch (session.getSessionStatus()) + { + case ENCRYPTED: + scSessionStatus = ScSessionStatus.ENCRYPTED; + scSessionStatusMap.put(sessionID, scSessionStatus); + PublicKey remotePubKey = session.getRemotePublicKey(); + + String remoteFingerprint = null; + try + { + remoteFingerprint = + new OtrCryptoEngineImpl(). + getFingerprint(remotePubKey); + } + catch (OtrCryptoException e) + { + logger.debug( + "Could not get the fingerprint from the " + + "public key of contact: " + contact); + } + + List allFingerprintsOfContact = + OtrActivator.scOtrKeyManager. + getAllRemoteFingerprints(contact); + if (allFingerprintsOfContact != null) + { + if (!allFingerprintsOfContact.contains( + remoteFingerprint)) + { + OtrActivator.scOtrKeyManager.saveFingerprint( + contact, remoteFingerprint); + } + } + + if (!OtrActivator.scOtrKeyManager.isVerified( + contact, remoteFingerprint)) + { + OtrActivator.scOtrKeyManager.unverify( + otrContact, remoteFingerprint); + UUID sessionGuid = null; + for(ScSessionID scSessionID : contactsMap.keySet()) + { + if(scSessionID.getSessionID().equals(sessionID)) + { + sessionGuid = scSessionID.getGUID(); + break; + } + } + + OtrActivator.uiService.getChat(contact) + .addChatLinkClickedListener(ScOtrEngineImpl.this); + + String unverifiedSessionWarning + = OtrActivator.resourceService.getI18NString( + "plugin.otr.activator" + + ".unverifiedsessionwarning", + new String[] + { + contact.getDisplayName() + resourceName, + this.getClass().getName(), + "AUTHENTIFICATION", + sessionGuid.toString() + }); + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), + new Date(), Chat.SYSTEM_MESSAGE, + unverifiedSessionWarning, + OperationSetBasicInstantMessaging.HTML_MIME_TYPE); + + } + + // show info whether history is on or off + String otrAndHistoryMessage; + if(!OtrActivator.getMessageHistoryService() + .isHistoryLoggingEnabled() || + !isHistoryLoggingEnabled(contact)) + { + otrAndHistoryMessage = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.historyoff", + new String[]{ + OtrActivator.resourceService + .getSettingsString( + "service.gui.APPLICATION_NAME"), + this.getClass().getName(), + "showHistoryPopupMenu" + }); + } + else + { + otrAndHistoryMessage = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.historyon", + new String[]{ + OtrActivator.resourceService + .getSettingsString( + "service.gui.APPLICATION_NAME"), + this.getClass().getName(), + "showHistoryPopupMenu" + }); + } + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), + new Date(), Chat.SYSTEM_MESSAGE, + otrAndHistoryMessage, + OperationSetBasicInstantMessaging.HTML_MIME_TYPE); + + message = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.multipleinstancesdetected", + new String[] + {contact.getDisplayName()}); + + if (contact.supportResources() + && contact.getResources() != null + && contact.getResources().size() > 1) + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), + new Date(), Chat.SYSTEM_MESSAGE, + message, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + + message + = OtrActivator.resourceService.getI18NString( + OtrActivator.scOtrKeyManager.isVerified( + contact, remoteFingerprint) + ? "plugin.otr.activator.sessionstared" + : "plugin.otr.activator" + + ".unverifiedsessionstared", + new String[] + {contact.getDisplayName() + resourceName}); + + break; + case FINISHED: + scSessionStatus = ScSessionStatus.FINISHED; + scSessionStatusMap.put(sessionID, scSessionStatus); + message = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.sessionfinished", + new String[] + {contact.getDisplayName() + resourceName}); + break; + case PLAINTEXT: + scSessionStatus = ScSessionStatus.PLAINTEXT; + scSessionStatusMap.put(sessionID, scSessionStatus); + message = + OtrActivator.resourceService.getI18NString( + "plugin.otr.activator.sessionlost", new String[] + {contact.getDisplayName() + resourceName}); + break; + } + + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.SYSTEM_MESSAGE, message, + OperationSetBasicInstantMessaging.HTML_MIME_TYPE); + + for (ScOtrEngineListener l : getListeners()) + l.sessionStatusChanged(otrContact); + } + + @Override + public void multipleInstancesDetected(SessionID sessionID) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + for (ScOtrEngineListener l : getListeners()) + l.multipleInstancesDetected(otrContact); + } + + @Override + public void outgoingSessionChanged(SessionID sessionID) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + for (ScOtrEngineListener l : getListeners()) + l.outgoingSessionChanged(otrContact); + } + }); + } + + /** + * Checks whether history is enabled for the metacontact containing + * the contact. + * @param contact the contact to check. + * @return whether chat logging is enabled while chatting + * with contact. + */ + private boolean isHistoryLoggingEnabled(Contact contact) + { + MetaContact metaContact = OtrActivator + .getContactListService().findMetaContactByContact(contact); + if(metaContact != null) + return OtrActivator.getMessageHistoryService() + .isHistoryLoggingEnabled(metaContact.getMetaUID()); + else + return true; + } + + @Override + public void addListener(ScOtrEngineListener l) + { + synchronized (listeners) + { + if (!listeners.contains(l)) + listeners.add(l); + } + } + + @Override + public void chatLinkClicked(URI url) + { + String action = url.getPath(); + if(action.equals("/AUTHENTIFICATION")) + { + UUID guid = UUID.fromString(url.getQuery()); + + if(guid == null) + throw new RuntimeException( + "No UUID found in OTR authenticate URL"); + + // Looks for registered action handler + OtrActionHandler actionHandler + = ServiceUtils.getService( + OtrActivator.bundleContext, + OtrActionHandler.class); + + if(actionHandler != null) + { + actionHandler.onAuthenticateLinkClicked(guid); + } + else + { + logger.error("No OtrActionHandler registered"); + } + } + } + + @Override + public void endSession(OtrContact otrContact) + { + SessionID sessionID = getSessionID(otrContact); + try + { + setSessionStatus(otrContact, ScSessionStatus.PLAINTEXT); + + otrEngine.getSession(sessionID).endSession(); + } + catch (OtrException e) + { + showError(sessionID, e.getMessage()); + } + } + + @Override + public OtrPolicy getContactPolicy(Contact contact) + { + ProtocolProviderService pps = contact.getProtocolProvider(); + SessionID sessionID + = new SessionID( + pps.getAccountID().getAccountUniqueID(), + contact.getAddress(), + pps.getProtocolName()); + int policy = + this.configurator.getPropertyInt(sessionID + "contact_policy", + -1); + if (policy < 0) + return getGlobalPolicy(); + else + return new OtrPolicyImpl(policy); + } + + @Override + public OtrPolicy getGlobalPolicy() + { + /* + * SEND_WHITESPACE_TAG bit will be lowered until we stabilize the OTR. + */ + int defaultScOtrPolicy = + OtrPolicy.OTRL_POLICY_DEFAULT & ~OtrPolicy.SEND_WHITESPACE_TAG; + return new OtrPolicyImpl(this.configurator.getPropertyInt( + "GLOBAL_POLICY", defaultScOtrPolicy)); + } + + /** + * Gets a copy of the list of ScOtrEngineListeners registered with + * this instance which may safely be iterated without the risk of a + * ConcurrentModificationException. + * + * @return a copy of the list of ScOtrEngineListeners registered + * with this instance which may safely be iterated without the risk of a + * ConcurrentModificationException + */ + private ScOtrEngineListener[] getListeners() + { + synchronized (listeners) + { + return listeners.toArray(new ScOtrEngineListener[listeners.size()]); + } + } + + /** + * Manages the scheduling of TimerTasks that are used to set Contact's + * ScSessionStatus after a period of time. + * + * @author Marin Dzhigarov + */ + private class ScSessionStatusScheduler + { + private final Timer timer = new Timer(); + + private final Map tasks = + new ConcurrentHashMap(); + + public void scheduleScSessionStatusChange( + final OtrContact otrContact, final ScSessionStatus status) + { + cancel(otrContact); + + TimerTask task + = new TimerTask() + { + @Override + public void run() + { + setSessionStatus(otrContact, status); + } + }; + timer.schedule(task, SESSION_TIMEOUT); + tasks.put(otrContact, task); + } + + public void cancel(final OtrContact otrContact) + { + TimerTask task = tasks.get(otrContact); + if (task != null) + task.cancel(); + tasks.remove(otrContact); + } + + public void serviceChanged(ServiceEvent ev) + { + Object service + = OtrActivator.bundleContext.getService( + ev.getServiceReference()); + + if (!(service instanceof ProtocolProviderService)) + return; + + if (ev.getType() == ServiceEvent.UNREGISTERING) + { + ProtocolProviderService provider + = (ProtocolProviderService) service; + + Iterator i = tasks.keySet().iterator(); + + while (i.hasNext()) + { + OtrContact otrContact = i.next(); + if (provider.equals( + otrContact.contact.getProtocolProvider())) + { + cancel(otrContact); + i.remove(); + } + } + } + } + } + + private void setSessionStatus(OtrContact contact, ScSessionStatus status) + { + scSessionStatusMap.put(getSessionID(contact), status); + scheduler.cancel(contact); + for (ScOtrEngineListener l : getListeners()) + l.sessionStatusChanged(contact); + } + + @Override + public ScSessionStatus getSessionStatus(OtrContact contact) + { + SessionID sessionID = getSessionID(contact); + SessionStatus sessionStatus = otrEngine.getSession(sessionID).getSessionStatus(); + ScSessionStatus scSessionStatus = null; + if (!scSessionStatusMap.containsKey(sessionID)) + { + switch (sessionStatus) + { + case PLAINTEXT: + scSessionStatus = ScSessionStatus.PLAINTEXT; + break; + case ENCRYPTED: + scSessionStatus = ScSessionStatus.ENCRYPTED; + break; + case FINISHED: + scSessionStatus = ScSessionStatus.FINISHED; + break; + } + scSessionStatusMap.put(sessionID, scSessionStatus); + } + return scSessionStatusMap.get(sessionID); + } + + @Override + public boolean isMessageUIDInjected(String mUID) + { + return injectedMessageUIDs.contains(mUID); + } + + @Override + public void launchHelp() + { + ServiceReference ref = + OtrActivator.bundleContext + .getServiceReference(BrowserLauncherService.class.getName()); + + if (ref == null) + return; + + BrowserLauncherService service = + (BrowserLauncherService) OtrActivator.bundleContext.getService(ref); + + service.openURL(OtrActivator.resourceService + .getI18NString("plugin.otr.authbuddydialog.HELP_URI")); + } + + @Override + public void refreshSession(OtrContact otrContact) + { + SessionID sessionID = getSessionID(otrContact); + try + { + otrEngine.getSession(sessionID).refreshSession(); + } + catch (OtrException e) + { + logger.error("Error refreshing session", e); + showError(sessionID, e.getMessage()); + } + } + + @Override + public void removeListener(ScOtrEngineListener l) + { + synchronized (listeners) + { + listeners.remove(l); + } + } + + /** + * Cleans the contactsMap when ProtocolProviderService + * gets unregistered. + */ + @Override + public void serviceChanged(ServiceEvent ev) + { + Object service + = OtrActivator.bundleContext.getService(ev.getServiceReference()); + + if (!(service instanceof ProtocolProviderService)) + return; + + if (ev.getType() == ServiceEvent.UNREGISTERING) + { + if (logger.isDebugEnabled()) + { + logger.debug( + "Unregistering a ProtocolProviderService, cleaning" + + " OTR's ScSessionID to Contact map."); + logger.debug( + "Unregistering a ProtocolProviderService, cleaning" + + " OTR's Contact to SpmProgressDialog map."); + } + + ProtocolProviderService provider + = (ProtocolProviderService) service; + + synchronized(contactsMap) + { + Iterator i = contactsMap.values().iterator(); + + while (i.hasNext()) { - OtrContact otrContact = i.next(); - if (provider.equals( - otrContact.contact.getProtocolProvider())) - { - scSessionStatusMap.remove(getSessionID(otrContact)); - i.remove(); - } - } - } - - Iterator i = progressDialogMap.keySet().iterator(); - - while (i.hasNext()) - { - if (provider.equals(i.next().contact.getProtocolProvider())) - i.remove(); - } - scheduler.serviceChanged(ev); - } - } - - @Override - public void setContactPolicy(Contact contact, OtrPolicy policy) - { - ProtocolProviderService pps = contact.getProtocolProvider(); - SessionID sessionID - = new SessionID( - pps.getAccountID().getAccountUniqueID(), - contact.getAddress(), - pps.getProtocolName()); - - String propertyID = sessionID + "contact_policy"; - if (policy == null) - this.configurator.removeProperty(propertyID); - else - this.configurator.setProperty(propertyID, policy.getPolicy()); - - for (ScOtrEngineListener l : getListeners()) - l.contactPolicyChanged(contact); - } - - @Override - public void setGlobalPolicy(OtrPolicy policy) - { - if (policy == null) - this.configurator.removeProperty("GLOBAL_POLICY"); - else - this.configurator.setProperty("GLOBAL_POLICY", policy.getPolicy()); - - for (ScOtrEngineListener l : getListeners()) - l.globalPolicyChanged(); - } - - public void showError(SessionID sessionID, String err) - { - OtrContact otrContact = getOtrContact(sessionID); - if (otrContact == null) - return; - - Contact contact = otrContact.contact; - OtrActivator.uiService.getChat(contact).addMessage( - contact.getDisplayName(), new Date(), - Chat.ERROR_MESSAGE, err, - OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); - } - - @Override - public void startSession(OtrContact otrContact) - { - SessionID sessionID = getSessionID(otrContact); - - ScSessionStatus scSessionStatus = getSessionStatus(otrContact); - scSessionStatus = ScSessionStatus.LOADING; - scSessionStatusMap.put(sessionID, scSessionStatus); - for (ScOtrEngineListener l : getListeners()) - { - l.sessionStatusChanged(otrContact); - } - - scheduler.scheduleScSessionStatusChange( - otrContact, ScSessionStatus.TIMED_OUT); - - try - { - otrEngine.getSession(sessionID).startSession(); - } - catch (OtrException e) - { - logger.error("Error starting session", e); - showError(sessionID, e.getMessage()); - } - } - - @Override - public String transformReceiving(OtrContact otrContact, String msgText) - { - SessionID sessionID = getSessionID(otrContact); - try - { - return otrEngine.getSession(sessionID).transformReceiving(msgText); - } - catch (OtrException e) - { - logger.error("Error receiving the message", e); - showError(sessionID, e.getMessage()); - return null; - } - } - - @Override - public String[] transformSending(OtrContact otrContact, String msgText) - { - SessionID sessionID = getSessionID(otrContact); - try - { - return otrEngine.getSession(sessionID).transformSending(msgText); - } - catch (OtrException e) - { - logger.error("Error transforming the message", e); - showError(sessionID, e.getMessage()); - return null; - } - } - - private Session getSession(OtrContact contact) - { - SessionID sessionID = getSessionID(contact); - return otrEngine.getSession(sessionID); - } - - @Override - public void initSmp(OtrContact otrContact, String question, String secret) - { - Session session = getSession(otrContact); - try - { - session.initSmp(question, secret); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(otrContact.contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.init(); - progressDialog.setVisible(true); - } - catch (OtrException e) - { - logger.error("Error initializing SMP session with contact " - + otrContact.contact.getDisplayName(), e); - showError(session.getSessionID(), e.getMessage()); - } - } - - @Override - public void respondSmp( OtrContact otrContact, - InstanceTag receiverTag, - String question, - String secret) - { - Session session = getSession(otrContact); - try - { - session.respondSmp(receiverTag, question, secret); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(otrContact.contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.incrementProgress(); - progressDialog.setVisible(true); - } - catch (OtrException e) - { - logger.error( - "Error occured when sending SMP response to contact " - + otrContact.contact.getDisplayName(), e); - showError(session.getSessionID(), e.getMessage()); - } - } - - @Override - public void abortSmp(OtrContact otrContact) - { - Session session = getSession(otrContact); - try - { - session.abortSmp(); - - SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); - if (progressDialog == null) - { - progressDialog = new SmpProgressDialog(otrContact.contact); - progressDialogMap.put(otrContact, progressDialog); - } - - progressDialog.dispose(); - } - catch (OtrException e) - { - logger.error("Error aborting SMP session with contact " - + otrContact.contact.getDisplayName(), e); - showError(session.getSessionID(), e.getMessage()); - } - } - - @Override - public PublicKey getRemotePublicKey(OtrContact otrContact) - { - if (otrContact == null) - return null; - - Session session = getSession(otrContact); - - return session.getRemotePublicKey(); - } - - @Override - public List getSessionInstances(OtrContact otrContact) - { - if (otrContact == null) - return Collections.emptyList(); - return getSession(otrContact).getInstances(); - } - - @Override - public boolean setOutgoingSession(OtrContact contact, InstanceTag tag) - { - if (contact == null) - return false; - - Session session = getSession(contact); - - scSessionStatusMap.remove(session.getSessionID()); - return session.setOutgoingInstance(tag); - } - - @Override - public Session getOutgoingSession(OtrContact contact) - { - if (contact == null) - return null; - - SessionID sessionID = getSessionID(contact); - - return otrEngine.getSession(sessionID).getOutgoingInstance(); - } -} + OtrContact otrContact = i.next(); + if (provider.equals( + otrContact.contact.getProtocolProvider())) + { + scSessionStatusMap.remove(getSessionID(otrContact)); + i.remove(); + } + } + } + + Iterator i = progressDialogMap.keySet().iterator(); + + while (i.hasNext()) + { + if (provider.equals(i.next().contact.getProtocolProvider())) + i.remove(); + } + scheduler.serviceChanged(ev); + } + } + + @Override + public void setContactPolicy(Contact contact, OtrPolicy policy) + { + ProtocolProviderService pps = contact.getProtocolProvider(); + SessionID sessionID + = new SessionID( + pps.getAccountID().getAccountUniqueID(), + contact.getAddress(), + pps.getProtocolName()); + + String propertyID = sessionID + "contact_policy"; + if (policy == null) + this.configurator.removeProperty(propertyID); + else + this.configurator.setProperty(propertyID, policy.getPolicy()); + + for (ScOtrEngineListener l : getListeners()) + l.contactPolicyChanged(contact); + } + + @Override + public void setGlobalPolicy(OtrPolicy policy) + { + if (policy == null) + this.configurator.removeProperty("GLOBAL_POLICY"); + else + this.configurator.setProperty("GLOBAL_POLICY", policy.getPolicy()); + + for (ScOtrEngineListener l : getListeners()) + l.globalPolicyChanged(); + } + + public void showError(SessionID sessionID, String err) + { + OtrContact otrContact = getOtrContact(sessionID); + if (otrContact == null) + return; + + Contact contact = otrContact.contact; + OtrActivator.uiService.getChat(contact).addMessage( + contact.getDisplayName(), new Date(), + Chat.ERROR_MESSAGE, err, + OperationSetBasicInstantMessaging.DEFAULT_MIME_TYPE); + } + + @Override + public void startSession(OtrContact otrContact) + { + SessionID sessionID = getSessionID(otrContact); + + ScSessionStatus scSessionStatus = getSessionStatus(otrContact); + scSessionStatus = ScSessionStatus.LOADING; + scSessionStatusMap.put(sessionID, scSessionStatus); + for (ScOtrEngineListener l : getListeners()) + { + l.sessionStatusChanged(otrContact); + } + + scheduler.scheduleScSessionStatusChange( + otrContact, ScSessionStatus.TIMED_OUT); + + try + { + otrEngine.getSession(sessionID).startSession(); + } + catch (OtrException e) + { + logger.error("Error starting session", e); + showError(sessionID, e.getMessage()); + } + } + + @Override + public String transformReceiving(OtrContact otrContact, String msgText) + { + SessionID sessionID = getSessionID(otrContact); + try + { + return otrEngine.getSession(sessionID).transformReceiving(msgText); + } + catch (OtrException e) + { + logger.error("Error receiving the message", e); + showError(sessionID, e.getMessage()); + return null; + } + } + + @Override + public String[] transformSending(OtrContact otrContact, String msgText) + { + SessionID sessionID = getSessionID(otrContact); + try + { + return otrEngine.getSession(sessionID).transformSending(msgText); + } + catch (OtrException e) + { + logger.error("Error transforming the message", e); + showError(sessionID, e.getMessage()); + return null; + } + } + + private Session getSession(OtrContact contact) + { + SessionID sessionID = getSessionID(contact); + return otrEngine.getSession(sessionID); + } + + @Override + public void initSmp(OtrContact otrContact, String question, String secret) + { + Session session = getSession(otrContact); + try + { + session.initSmp(question, secret); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(otrContact.contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.init(); + progressDialog.setVisible(true); + } + catch (OtrException e) + { + logger.error("Error initializing SMP session with contact " + + otrContact.contact.getDisplayName(), e); + showError(session.getSessionID(), e.getMessage()); + } + } + + @Override + public void respondSmp( OtrContact otrContact, + InstanceTag receiverTag, + String question, + String secret) + { + Session session = getSession(otrContact); + try + { + session.respondSmp(receiverTag, question, secret); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(otrContact.contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.incrementProgress(); + progressDialog.setVisible(true); + } + catch (OtrException e) + { + logger.error( + "Error occured when sending SMP response to contact " + + otrContact.contact.getDisplayName(), e); + showError(session.getSessionID(), e.getMessage()); + } + } + + @Override + public void abortSmp(OtrContact otrContact) + { + Session session = getSession(otrContact); + try + { + session.abortSmp(); + + SmpProgressDialog progressDialog = progressDialogMap.get(otrContact); + if (progressDialog == null) + { + progressDialog = new SmpProgressDialog(otrContact.contact); + progressDialogMap.put(otrContact, progressDialog); + } + + progressDialog.dispose(); + } + catch (OtrException e) + { + logger.error("Error aborting SMP session with contact " + + otrContact.contact.getDisplayName(), e); + showError(session.getSessionID(), e.getMessage()); + } + } + + @Override + public PublicKey getRemotePublicKey(OtrContact otrContact) + { + if (otrContact == null) + return null; + + Session session = getSession(otrContact); + + return session.getRemotePublicKey(); + } + + @Override + public List getSessionInstances(OtrContact otrContact) + { + if (otrContact == null) + return Collections.emptyList(); + return getSession(otrContact).getInstances(); + } + + @Override + public boolean setOutgoingSession(OtrContact contact, InstanceTag tag) + { + if (contact == null) + return false; + + Session session = getSession(contact); + + scSessionStatusMap.remove(session.getSessionID()); + return session.setOutgoingInstance(tag); + } + + @Override + public Session getOutgoingSession(OtrContact contact) + { + if (contact == null) + return null; + + SessionID sessionID = getSessionID(contact); + + return otrEngine.getSession(sessionID).getOutgoingInstance(); + } +} diff --git a/src/net/java/sip/communicator/service/muc/ChatRoomProviderWrapperListener.java b/src/net/java/sip/communicator/service/muc/ChatRoomProviderWrapperListener.java index 261f2d7..dab910b 100644 --- a/src/net/java/sip/communicator/service/muc/ChatRoomProviderWrapperListener.java +++ b/src/net/java/sip/communicator/service/muc/ChatRoomProviderWrapperListener.java @@ -1,40 +1,40 @@ -/* - * 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.muc; - -/** - * Listener which registers for provider add/remove changes. - */ -public interface ChatRoomProviderWrapperListener -{ - /** - * When a provider wrapper is added this method is called to inform - * listeners. - * @param provider which was added. - */ - public void chatRoomProviderWrapperAdded( - ChatRoomProviderWrapper provider); - - /** - * When a provider wrapper is removed this method is called to inform - * listeners. - * @param provider which was removed. - */ - public void chatRoomProviderWrapperRemoved( - ChatRoomProviderWrapper provider); -} +/* + * 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.muc; + +/** + * Listener which registers for provider add/remove changes. + */ +public interface ChatRoomProviderWrapperListener +{ + /** + * When a provider wrapper is added this method is called to inform + * listeners. + * @param provider which was added. + */ + public void chatRoomProviderWrapperAdded( + ChatRoomProviderWrapper provider); + + /** + * When a provider wrapper is removed this method is called to inform + * listeners. + * @param provider which was removed. + */ + public void chatRoomProviderWrapperRemoved( + ChatRoomProviderWrapper provider); +} diff --git a/src/net/java/sip/communicator/service/protocol/AccountManager.java b/src/net/java/sip/communicator/service/protocol/AccountManager.java index c608092..3ce85dc 100644 --- a/src/net/java/sip/communicator/service/protocol/AccountManager.java +++ b/src/net/java/sip/communicator/service/protocol/AccountManager.java @@ -1,1083 +1,1083 @@ -/* - * 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.service.credentialsstorage.*; -import net.java.sip.communicator.service.protocol.event.*; -import net.java.sip.communicator.util.*; -import net.java.sip.communicator.util.Base64; //disambiguate from java.util.Base64 - -import org.jitsi.service.configuration.*; -import org.osgi.framework.*; - -/** - * Represents an implementation of AccountManager which loads the - * accounts in a separate thread. - * - * @author Lyubomir Marinov - * @author Yana Stamcheva - */ -public class AccountManager -{ - /** - * The delay in milliseconds the background Thread loading the - * stored accounts should wait before dying so that it doesn't get recreated - * for each ProtocolProviderFactory registration. - */ - private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000; - - /** - * The BundleContext this service is registered in. - */ - private final BundleContext bundleContext; - - /** - * The AccountManagerListeners currently interested in the - * events fired by this manager. - */ - private final List listeners = - new LinkedList(); - - /** - * The queue of ProtocolProviderFactory services awaiting their - * stored accounts to be loaded. - */ - private final Queue loadStoredAccountsQueue = - new LinkedList(); - - /** - * The Thread loading the stored accounts of the - * ProtocolProviderFactory services waiting in - * {@link #loadStoredAccountsQueue}. - */ - private Thread loadStoredAccountsThread; - - /** - * The Logger used by this AccountManagerImpl instance for - * logging output. - */ - private final Logger logger = Logger.getLogger(AccountManager.class); - - /** - * The list of AccountIDs, corresponding to all stored accounts. - */ - private final Vector storedAccounts = new Vector(); - - /** - * The prefix of the account unique identifier. - */ - private static final String ACCOUNT_UID_PREFIX = "acc"; - - /** - * Initializes a new AccountManagerImpl instance loaded in a - * specific BundleContext (in which the caller will usually - * later register it). - * - * @param bundleContext the BundleContext 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 AccountManagerListener to add - */ - public void addListener(AccountManagerListener listener) - { - synchronized (listeners) - { - if (!listeners.contains(listener)) - listeners.add(listener); - } - } - - /** - * Loads the accounts stored for a specific - * ProtocolProviderFactory. - * - * @param factory the ProtocolProviderFactory to load the - * stored accounts of - */ - private void doLoadStoredAccounts(ProtocolProviderFactory factory) - { - ConfigurationService configService - = ProtocolProviderActivator.getConfigurationService(); - String factoryPackage = getFactoryImplPackageName(factory); - List accounts - = configService.getPropertyNamesByPrefix(factoryPackage, true); - - if (logger.isDebugEnabled()) - logger.debug("Discovered " + accounts.size() + " stored " - + factoryPackage + " accounts"); - - for (Iterator 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 storedAccountProperties = - configService.getPropertyNamesByPrefix(storedAccount, false); - Map accountProperties = - new Hashtable(); - boolean disabled = false; - CredentialsStorageService credentialsStorage - = ServiceUtils.getService( - bundleContext, - CredentialsStorageService.class); - - int prefLen = storedAccount.length() + 1; - for (Iterator storedAccountPropertyIter - = storedAccountProperties.iterator(); - storedAccountPropertyIter.hasNext();) - { - String property = storedAccountPropertyIter.next(); - String value = configService.getString(property); - - //strip the package prefix - if(prefLen > property.length()) - continue; - - property = property.substring(prefLen); - - 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); - } - catch (ExceptionInInitializerError ex) - { - // In case we fail to instantiate the ProtocolProviderService. - logger.error( - "Failed to create account service instance for account " - + accountProperties, ex); - } - } - } - - /** - * Notifies the registered {@link #listeners} that the stored accounts of a - * specific ProtocolProviderFactory have just been loaded. - * - * @param factory the ProtocolProviderFactory 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 factory. - * @param factory the factory which package will be returned. - * @return the package name of the factory. - */ - public String getFactoryImplPackageName(ProtocolProviderFactory factory) - { - String className = factory.getClass().getName(); - - return className.substring(0, className.lastIndexOf('.')); - } - - /** - * Check for stored accounts for the supplied protocolName. - * @param protocolName the protocol name to check for - * @param includeHidden whether to include hidden providers - * @return true if there is any account stored in configuration - * service with protocolName, false otherwise. - */ - public boolean hasStoredAccounts(String protocolName, boolean includeHidden) - { - return hasStoredAccount(protocolName, includeHidden, null); - } - - /** - * Checks whether a stored account with userID is stored - * in configuration. - * - * @param protocolName the protocol name - * @param includeHidden whether to check hidden providers - * @param userID the user id to check. - * @return true if there is any account stored in configuration - * service with protocolName and userID, - * false otherwise. - */ - public boolean hasStoredAccount(String protocolName, - boolean includeHidden, - String userID) - { - Collection> factoryRefs - = ServiceUtils.getServiceReferences( - bundleContext, - ProtocolProviderFactory.class); - boolean hasStoredAccounts = false; - - if (!factoryRefs.isEmpty()) - { - ConfigurationService configService - = ProtocolProviderActivator.getConfigurationService(); - - for (ServiceReference factoryRef - : factoryRefs) - { - ProtocolProviderFactory factory - = bundleContext.getService(factoryRef); - - if ((protocolName != null) - && !protocolName.equals(factory.getProtocolName())) - { - continue; - } - - String factoryPackage = getFactoryImplPackageName(factory); - List storedAccounts - = configService - .getPropertyNamesByPrefix(factoryPackage + ".acc", - false); - - /* Ignore the hidden accounts. */ - for (Iterator storedAccountIter = - storedAccounts.iterator(); storedAccountIter.hasNext();) - { - String storedAccount = storedAccountIter.next(); - List storedAccountProperties = - configService.getPropertyNamesByPrefix(storedAccount, - true); - boolean hidden = false; - String accountUserID = null; - - if (!includeHidden || userID != null) - { - for (Iterator 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 uid in stored - * configuration. The uid is the one generated when creating - * accounts with prefix ACCOUNT_UID_PREFIX. - * - * @return AccountID if there is any account stored in configuration - * service with uid, - * null otherwise. - */ - public AccountID findAccountID(String uid) - { - Collection> factoryRefs - = ServiceUtils.getServiceReferences( - bundleContext, - ProtocolProviderFactory.class); - - if (!factoryRefs.isEmpty()) - { - ConfigurationService configService - = ProtocolProviderActivator.getConfigurationService(); - - for (ServiceReference factoryRef - : factoryRefs) - { - ProtocolProviderFactory factory - = bundleContext.getService(factoryRef); - - String factoryPackage = getFactoryImplPackageName(factory); - List storedAccountsProps - = configService - .getPropertyNamesByPrefix(factoryPackage, true); - - for (Iterator 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 - * ProtocolProviderFactory and notifies the registered - * {@link #listeners} that the stored accounts of the specified - * factory have just been loaded - * - * @param factory the ProtocolProviderFactory to load the - * stored accounts of - */ - private void loadStoredAccounts(ProtocolProviderFactory factory) - { - doLoadStoredAccounts(factory); - - fireStoredAccountsLoaded(factory); - } - - /** - * Notifies this manager that a specific - * ProtocolProviderFactory has been registered as a service. - * The current implementation queues the specified factory to - * have its stored accounts as soon as possible. - * - * @param factory the ProtocolProviderFactory which has been - * registered as a service. - */ - private void protocolProviderFactoryRegistered( - ProtocolProviderFactory factory) - { - queueLoadStoredAccounts(factory); - } - - /** - * Queues a specific ProtocolProviderFactory to have its stored - * accounts loaded as soon as possible. - * - * @param factory the ProtocolProviderFactory 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 AccountManagerListener to remove - */ - public void removeListener(AccountManagerListener listener) - { - synchronized (listeners) - { - listeners.remove(listener); - } - } - - /** - * Running in {@link #loadStoredAccountsThread}, loads the stored accounts - * of the ProtocolProviderFactory 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 - * ProtocolProviderFactory services in order to queue them for - * loading their stored accounts. - * - * @param serviceEvent the ServiceEvent 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 AccountID - * created by a specific ProtocolProviderFactory. - * - * @param factory the ProtocolProviderFactory which created the - * account to be stored - * @param accountID the account in the form of AccountID 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.getAccountUniqueID() ); - - Map configurationProperties - = new HashMap(); - - // 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 accountProperties = accountID.getAccountProperties(); - - for (Map.Entry 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) - && // And only if it's not stored already in encrypted form. - // Account registration object clears also this property - // in order to forget the password - !configurationProperties.containsKey( - factoryPackage+"."+accountNodeName+".ENCRYPTED_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 accountUID account for which the prefix will be returned - * @return configuration prefix for given accountID if exists or - * null otherwise - */ - public String getAccountNodeName( ProtocolProviderFactory factory, - String accountUID ) - { - ConfigurationService configurationService - = ProtocolProviderActivator.getConfigurationService(); - String factoryPackage = getFactoryImplPackageName(factory); - - // First check if such accountID already exists in the configuration. - List storedAccounts = - configurationService.getPropertyNamesByPrefix(factoryPackage, true); - String accountNodeName = null; - - for (Iterator 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 == null) - continue; - - if (storedAccountUID.equals(accountUID)) - accountNodeName = configurationService.getString(storedAccount); - } - return accountNodeName; - } - - /** - * Removes the account with accountID from the set of accounts - * that are persistently stored inside the configuration service. - * - * @param factory the ProtocolProviderFactory 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 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 (accountID.getAccountUniqueID().equals(accountUID)) - { - //retrieve the names of all properties registered for the - //current account. - List 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 Iterator over a list of all stored - * AccountIDs. 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. - *

- * 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 Iterator over a list of all stored - * AccountIDs - */ - public Collection getStoredAccounts() - { - synchronized (storedAccounts) - { - return new Vector(storedAccounts); - } - } - - /** - * Loads the account corresponding to the given AccountID. An - * account is loaded when its ProtocolProviderService is registered - * in the bundle context. This method is meant to load the account through - * the corresponding ProtocolProviderFactory. - * - * @param accountID the identifier of the account to load - * @throws OperationFailedException if anything goes wrong while loading the - * account corresponding to the specified accountID - */ - 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 AccountID. An - * account is unloaded when its ProtocolProviderService is - * unregistered in the bundle context. This method is meant to unload the - * account through the corresponding ProtocolProviderFactory. - * - * @param accountID the identifier of the account to load - * @throws OperationFailedException if anything goes wrong while unloading - * the account corresponding to the specified accountID - */ - 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 - = 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 accountID is - * loaded. An account is loaded if its ProtocolProviderService 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 true to indicate that the account with the given - * accountID is loaded, false - 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; - } -} +/* + * 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.service.credentialsstorage.*; +import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.util.*; +import net.java.sip.communicator.util.Base64; //disambiguate from java.util.Base64 + +import org.jitsi.service.configuration.*; +import org.osgi.framework.*; + +/** + * Represents an implementation of AccountManager which loads the + * accounts in a separate thread. + * + * @author Lyubomir Marinov + * @author Yana Stamcheva + */ +public class AccountManager +{ + /** + * The delay in milliseconds the background Thread loading the + * stored accounts should wait before dying so that it doesn't get recreated + * for each ProtocolProviderFactory registration. + */ + private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000; + + /** + * The BundleContext this service is registered in. + */ + private final BundleContext bundleContext; + + /** + * The AccountManagerListeners currently interested in the + * events fired by this manager. + */ + private final List listeners = + new LinkedList(); + + /** + * The queue of ProtocolProviderFactory services awaiting their + * stored accounts to be loaded. + */ + private final Queue loadStoredAccountsQueue = + new LinkedList(); + + /** + * The Thread loading the stored accounts of the + * ProtocolProviderFactory services waiting in + * {@link #loadStoredAccountsQueue}. + */ + private Thread loadStoredAccountsThread; + + /** + * The Logger used by this AccountManagerImpl instance for + * logging output. + */ + private final Logger logger = Logger.getLogger(AccountManager.class); + + /** + * The list of AccountIDs, corresponding to all stored accounts. + */ + private final Vector storedAccounts = new Vector(); + + /** + * The prefix of the account unique identifier. + */ + private static final String ACCOUNT_UID_PREFIX = "acc"; + + /** + * Initializes a new AccountManagerImpl instance loaded in a + * specific BundleContext (in which the caller will usually + * later register it). + * + * @param bundleContext the BundleContext 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 AccountManagerListener to add + */ + public void addListener(AccountManagerListener listener) + { + synchronized (listeners) + { + if (!listeners.contains(listener)) + listeners.add(listener); + } + } + + /** + * Loads the accounts stored for a specific + * ProtocolProviderFactory. + * + * @param factory the ProtocolProviderFactory to load the + * stored accounts of + */ + private void doLoadStoredAccounts(ProtocolProviderFactory factory) + { + ConfigurationService configService + = ProtocolProviderActivator.getConfigurationService(); + String factoryPackage = getFactoryImplPackageName(factory); + List accounts + = configService.getPropertyNamesByPrefix(factoryPackage, true); + + if (logger.isDebugEnabled()) + logger.debug("Discovered " + accounts.size() + " stored " + + factoryPackage + " accounts"); + + for (Iterator 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 storedAccountProperties = + configService.getPropertyNamesByPrefix(storedAccount, false); + Map accountProperties = + new Hashtable(); + boolean disabled = false; + CredentialsStorageService credentialsStorage + = ServiceUtils.getService( + bundleContext, + CredentialsStorageService.class); + + int prefLen = storedAccount.length() + 1; + for (Iterator storedAccountPropertyIter + = storedAccountProperties.iterator(); + storedAccountPropertyIter.hasNext();) + { + String property = storedAccountPropertyIter.next(); + String value = configService.getString(property); + + //strip the package prefix + if(prefLen > property.length()) + continue; + + property = property.substring(prefLen); + + 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); + } + catch (ExceptionInInitializerError ex) + { + // In case we fail to instantiate the ProtocolProviderService. + logger.error( + "Failed to create account service instance for account " + + accountProperties, ex); + } + } + } + + /** + * Notifies the registered {@link #listeners} that the stored accounts of a + * specific ProtocolProviderFactory have just been loaded. + * + * @param factory the ProtocolProviderFactory 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 factory. + * @param factory the factory which package will be returned. + * @return the package name of the factory. + */ + public String getFactoryImplPackageName(ProtocolProviderFactory factory) + { + String className = factory.getClass().getName(); + + return className.substring(0, className.lastIndexOf('.')); + } + + /** + * Check for stored accounts for the supplied protocolName. + * @param protocolName the protocol name to check for + * @param includeHidden whether to include hidden providers + * @return true if there is any account stored in configuration + * service with protocolName, false otherwise. + */ + public boolean hasStoredAccounts(String protocolName, boolean includeHidden) + { + return hasStoredAccount(protocolName, includeHidden, null); + } + + /** + * Checks whether a stored account with userID is stored + * in configuration. + * + * @param protocolName the protocol name + * @param includeHidden whether to check hidden providers + * @param userID the user id to check. + * @return true if there is any account stored in configuration + * service with protocolName and userID, + * false otherwise. + */ + public boolean hasStoredAccount(String protocolName, + boolean includeHidden, + String userID) + { + Collection> factoryRefs + = ServiceUtils.getServiceReferences( + bundleContext, + ProtocolProviderFactory.class); + boolean hasStoredAccounts = false; + + if (!factoryRefs.isEmpty()) + { + ConfigurationService configService + = ProtocolProviderActivator.getConfigurationService(); + + for (ServiceReference factoryRef + : factoryRefs) + { + ProtocolProviderFactory factory + = bundleContext.getService(factoryRef); + + if ((protocolName != null) + && !protocolName.equals(factory.getProtocolName())) + { + continue; + } + + String factoryPackage = getFactoryImplPackageName(factory); + List storedAccounts + = configService + .getPropertyNamesByPrefix(factoryPackage + ".acc", + false); + + /* Ignore the hidden accounts. */ + for (Iterator storedAccountIter = + storedAccounts.iterator(); storedAccountIter.hasNext();) + { + String storedAccount = storedAccountIter.next(); + List storedAccountProperties = + configService.getPropertyNamesByPrefix(storedAccount, + true); + boolean hidden = false; + String accountUserID = null; + + if (!includeHidden || userID != null) + { + for (Iterator 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 uid in stored + * configuration. The uid is the one generated when creating + * accounts with prefix ACCOUNT_UID_PREFIX. + * + * @return AccountID if there is any account stored in configuration + * service with uid, + * null otherwise. + */ + public AccountID findAccountID(String uid) + { + Collection> factoryRefs + = ServiceUtils.getServiceReferences( + bundleContext, + ProtocolProviderFactory.class); + + if (!factoryRefs.isEmpty()) + { + ConfigurationService configService + = ProtocolProviderActivator.getConfigurationService(); + + for (ServiceReference factoryRef + : factoryRefs) + { + ProtocolProviderFactory factory + = bundleContext.getService(factoryRef); + + String factoryPackage = getFactoryImplPackageName(factory); + List storedAccountsProps + = configService + .getPropertyNamesByPrefix(factoryPackage, true); + + for (Iterator 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 + * ProtocolProviderFactory and notifies the registered + * {@link #listeners} that the stored accounts of the specified + * factory have just been loaded + * + * @param factory the ProtocolProviderFactory to load the + * stored accounts of + */ + private void loadStoredAccounts(ProtocolProviderFactory factory) + { + doLoadStoredAccounts(factory); + + fireStoredAccountsLoaded(factory); + } + + /** + * Notifies this manager that a specific + * ProtocolProviderFactory has been registered as a service. + * The current implementation queues the specified factory to + * have its stored accounts as soon as possible. + * + * @param factory the ProtocolProviderFactory which has been + * registered as a service. + */ + private void protocolProviderFactoryRegistered( + ProtocolProviderFactory factory) + { + queueLoadStoredAccounts(factory); + } + + /** + * Queues a specific ProtocolProviderFactory to have its stored + * accounts loaded as soon as possible. + * + * @param factory the ProtocolProviderFactory 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 AccountManagerListener to remove + */ + public void removeListener(AccountManagerListener listener) + { + synchronized (listeners) + { + listeners.remove(listener); + } + } + + /** + * Running in {@link #loadStoredAccountsThread}, loads the stored accounts + * of the ProtocolProviderFactory 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 + * ProtocolProviderFactory services in order to queue them for + * loading their stored accounts. + * + * @param serviceEvent the ServiceEvent 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 AccountID + * created by a specific ProtocolProviderFactory. + * + * @param factory the ProtocolProviderFactory which created the + * account to be stored + * @param accountID the account in the form of AccountID 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.getAccountUniqueID() ); + + Map configurationProperties + = new HashMap(); + + // 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 accountProperties = accountID.getAccountProperties(); + + for (Map.Entry 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) + && // And only if it's not stored already in encrypted form. + // Account registration object clears also this property + // in order to forget the password + !configurationProperties.containsKey( + factoryPackage+"."+accountNodeName+".ENCRYPTED_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 accountUID account for which the prefix will be returned + * @return configuration prefix for given accountID if exists or + * null otherwise + */ + public String getAccountNodeName( ProtocolProviderFactory factory, + String accountUID ) + { + ConfigurationService configurationService + = ProtocolProviderActivator.getConfigurationService(); + String factoryPackage = getFactoryImplPackageName(factory); + + // First check if such accountID already exists in the configuration. + List storedAccounts = + configurationService.getPropertyNamesByPrefix(factoryPackage, true); + String accountNodeName = null; + + for (Iterator 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 == null) + continue; + + if (storedAccountUID.equals(accountUID)) + accountNodeName = configurationService.getString(storedAccount); + } + return accountNodeName; + } + + /** + * Removes the account with accountID from the set of accounts + * that are persistently stored inside the configuration service. + * + * @param factory the ProtocolProviderFactory 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 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 (accountID.getAccountUniqueID().equals(accountUID)) + { + //retrieve the names of all properties registered for the + //current account. + List 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 Iterator over a list of all stored + * AccountIDs. 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. + *

+ * 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 Iterator over a list of all stored + * AccountIDs + */ + public Collection getStoredAccounts() + { + synchronized (storedAccounts) + { + return new Vector(storedAccounts); + } + } + + /** + * Loads the account corresponding to the given AccountID. An + * account is loaded when its ProtocolProviderService is registered + * in the bundle context. This method is meant to load the account through + * the corresponding ProtocolProviderFactory. + * + * @param accountID the identifier of the account to load + * @throws OperationFailedException if anything goes wrong while loading the + * account corresponding to the specified accountID + */ + 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 AccountID. An + * account is unloaded when its ProtocolProviderService is + * unregistered in the bundle context. This method is meant to unload the + * account through the corresponding ProtocolProviderFactory. + * + * @param accountID the identifier of the account to load + * @throws OperationFailedException if anything goes wrong while unloading + * the account corresponding to the specified accountID + */ + 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 + = 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 accountID is + * loaded. An account is loaded if its ProtocolProviderService 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 true to indicate that the account with the given + * accountID is loaded, false - 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; + } +} -- cgit v1.1