diff options
author | Ingo Bauersachs <ingo@jitsi.org> | 2016-02-28 21:54:06 +0100 |
---|---|---|
committer | Ingo Bauersachs <ingo@jitsi.org> | 2016-02-28 21:54:06 +0100 |
commit | be0540e2b75cc30d437daad88cb3467580eff0cc (patch) | |
tree | 47dcadc1f4cd666fce6952566a049ebb7b83c767 /src | |
parent | 577d850a0a7bb262458ba1f5d639f9ba027108ad (diff) | |
download | jitsi-be0540e2b75cc30d437daad88cb3467580eff0cc.zip jitsi-be0540e2b75cc30d437daad88cb3467580eff0cc.tar.gz jitsi-be0540e2b75cc30d437daad88cb3467580eff0cc.tar.bz2 |
Normalize line endings
See also #227
Diffstat (limited to 'src')
8 files changed, 3786 insertions, 3786 deletions
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<String> returnedAddresses = new LinkedList<String>();
-
- 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 <tt>null</tt> otherwise.
- *
- * @return the string of our outbound proxy if we are using one and
- * <tt>null</tt> 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<String> returnedAddresses = new LinkedList<String>(); + + 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 <tt>null</tt> otherwise. + * + * @return the string of our outbound proxy if we are using one and + * <tt>null</tt> 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 <a
- * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section
- * 7.1</a> 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
- * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section
- * 7.2</a>
- */
- 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<String> 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<String> 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<String> extractCertIdentities(X509Certificate cert)
- {
- List<String> certIdentities = new ArrayList<String>();
- Collection<List<?>> 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 <a + * href="http://tools.ietf.org/html/rfc5922#section-7.1">RFC5922, Section + * 7.1</a> 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 + * <a href="http://tools.ietf.org/html/rfc5922#section-7.2">RFC5922 Section + * 7.2</a> + */ + 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<String> 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<String> 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<String> extractCertIdentities(X509Certificate cert) + { + List<String> certIdentities = new ArrayList<String>(); + Collection<List<?>> 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 <tt>Logger</tt> used by the <tt>SystemActivityNotifications</tt>
- * 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 <tt>Logger</tt> used by the <tt>SystemActivityNotifications</tt> + * 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<String, String> containerFilter
- = new Hashtable<String, String>();
- 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<String, String>();
- 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<String, String>();
- 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 <tt>BrowserLauncherService</tt> obtained from the bundle
- * context.
- * @return the <tt>BrowserLauncherService</tt> 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 <tt>MetaContactListService</tt> obtained from the bundle
- * context.
- * @return the <tt>MetaContactListService</tt> 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<String, String> containerFilter + = new Hashtable<String, String>(); + 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<String, String>(); + 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<String, String>(); + 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 <tt>BrowserLauncherService</tt> obtained from the bundle + * context. + * @return the <tt>BrowserLauncherService</tt> 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 <tt>MetaContactListService</tt> obtained from the bundle + * context. + * @return the <tt>MetaContactListService</tt> 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 <tt>X509Certificate</tt> object
- */
- public X509CertificatePanel(Certificate certificate)
- {
- this(new Certificate[]
- {
- certificate
- });
- }
-
- /**
- * Constructs a X509 certificate panel.
- *
- * @param certificates <tt>X509Certificate</tt> objects
- */
- public X509CertificatePanel(Certificate[] certificates)
- {
- setLayout(new BorderLayout(5, 5));
-
- // Certificate chain list
- TransparentPanel topPanel = new TransparentPanel(new BorderLayout());
- topPanel.add(new JLabel("<html><body><b>"
- + R.getI18NString("service.gui.CERT_INFO_CHAIN")
- + "</b></body></html>"), 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("<html><body>\n");
-
- if (certificate instanceof X509Certificate)
- {
- renderX509(sb, (X509Certificate) certificate);
- }
- else
- {
- sb.append("<pre>\n");
- sb.append(certificate.toString());
- sb.append("</pre>\n");
- }
-
- sb.append("</body></html>");
- 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("<table cellspacing='1' cellpadding='1'>\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("</table>\n");
- }
-
- /**
- * Add a title.
- *
- * @param sb StringBuilder to append to
- * @param title to print
- */
- private void addTitle(StringBuilder sb, String title)
- {
- sb.append("<tr><td colspan='2'")
- .append(" style='margin-top: 5pt; white-space: nowrap'><p><b>")
- .append(title).append("</b></p></td></tr>\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("<tr>")
- .append("<td style='margin-left: 5pt; margin-right: 25pt;")
- .append(" white-space: nowrap'>")
- .append(field).append("</td>")
- .append("<td>").append(value).append("</td>")
- .append("</tr>\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<String, String> parts = new HashMap<String, String>();
- 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 <tt>X509Certificate</tt> object + */ + public X509CertificatePanel(Certificate certificate) + { + this(new Certificate[] + { + certificate + }); + } + + /** + * Constructs a X509 certificate panel. + * + * @param certificates <tt>X509Certificate</tt> objects + */ + public X509CertificatePanel(Certificate[] certificates) + { + setLayout(new BorderLayout(5, 5)); + + // Certificate chain list + TransparentPanel topPanel = new TransparentPanel(new BorderLayout()); + topPanel.add(new JLabel("<html><body><b>" + + R.getI18NString("service.gui.CERT_INFO_CHAIN") + + "</b></body></html>"), 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("<html><body>\n"); + + if (certificate instanceof X509Certificate) + { + renderX509(sb, (X509Certificate) certificate); + } + else + { + sb.append("<pre>\n"); + sb.append(certificate.toString()); + sb.append("</pre>\n"); + } + + sb.append("</body></html>"); + 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("<table cellspacing='1' cellpadding='1'>\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("</table>\n"); + } + + /** + * Add a title. + * + * @param sb StringBuilder to append to + * @param title to print + */ + private void addTitle(StringBuilder sb, String title) + { + sb.append("<tr><td colspan='2'") + .append(" style='margin-top: 5pt; white-space: nowrap'><p><b>") + .append(title).append("</b></p></td></tr>\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("<tr>") + .append("<td style='margin-left: 5pt; margin-right: 25pt;") + .append(" white-space: nowrap'>") + .append(field).append("</td>") + .append("<td>").append(value).append("</td>") + .append("</tr>\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<String, String> parts = new HashMap<String, String>(); + 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<ContactResource> 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
- = "<a href=\"http://en.wikipedia.org/wiki/Off-the-Record_Messaging\">";
- 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<SessionID, ScSessionStatus> scSessionStatusMap =
- new ConcurrentHashMap<SessionID, ScSessionStatus>();
-
- private static final Map<ScSessionID, OtrContact> contactsMap =
- new Hashtable<ScSessionID, OtrContact>();
-
- private static final Map<OtrContact, SmpProgressDialog> progressDialogMap =
- new ConcurrentHashMap<OtrContact, SmpProgressDialog>();
-
- public static OtrContact getOtrContact(SessionID sessionID)
- {
- return contactsMap.get(new ScSessionID(sessionID));
- }
-
- /**
- * Returns the <tt>ScSessionID</tt> for given <tt>UUID</tt>.
- * @param guid the <tt>UUID</tt> identifying <tt>ScSessionID</tt>.
- * @return the <tt>ScSessionID</tt> for given <tt>UUID</tt> or <tt>null</tt>
- * 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<String> injectedMessageUIDs = new Vector<String>();
-
- private final List<ScOtrEngineListener> listeners =
- new Vector<ScOtrEngineListener>();
-
- /**
- * 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<String> 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 <tt>contact</tt>.
- * @param contact the contact to check.
- * @return whether chat logging is enabled while chatting
- * with <tt>contact</tt>.
- */
- 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 <tt>ScOtrEngineListener</tt>s registered with
- * this instance which may safely be iterated without the risk of a
- * <tt>ConcurrentModificationException</tt>.
- *
- * @return a copy of the list of <tt>ScOtrEngineListener<tt>s registered
- * with this instance which may safely be iterated without the risk of a
- * <tt>ConcurrentModificationException</tt>
- */
- 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<OtrContact, TimerTask> tasks =
- new ConcurrentHashMap<OtrContact, TimerTask>();
-
- 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<OtrContact> 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 <tt>ProtocolProviderService</tt>
- * 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<OtrContact> 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<ContactResource> 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 + = "<a href=\"http://en.wikipedia.org/wiki/Off-the-Record_Messaging\">"; + 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<SessionID, ScSessionStatus> scSessionStatusMap = + new ConcurrentHashMap<SessionID, ScSessionStatus>(); + + private static final Map<ScSessionID, OtrContact> contactsMap = + new Hashtable<ScSessionID, OtrContact>(); + + private static final Map<OtrContact, SmpProgressDialog> progressDialogMap = + new ConcurrentHashMap<OtrContact, SmpProgressDialog>(); + + public static OtrContact getOtrContact(SessionID sessionID) + { + return contactsMap.get(new ScSessionID(sessionID)); + } + + /** + * Returns the <tt>ScSessionID</tt> for given <tt>UUID</tt>. + * @param guid the <tt>UUID</tt> identifying <tt>ScSessionID</tt>. + * @return the <tt>ScSessionID</tt> for given <tt>UUID</tt> or <tt>null</tt> + * 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<String> injectedMessageUIDs = new Vector<String>(); + + private final List<ScOtrEngineListener> listeners = + new Vector<ScOtrEngineListener>(); + + /** + * 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<String> 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 <tt>contact</tt>. + * @param contact the contact to check. + * @return whether chat logging is enabled while chatting + * with <tt>contact</tt>. + */ + 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 <tt>ScOtrEngineListener</tt>s registered with + * this instance which may safely be iterated without the risk of a + * <tt>ConcurrentModificationException</tt>. + * + * @return a copy of the list of <tt>ScOtrEngineListener<tt>s registered + * with this instance which may safely be iterated without the risk of a + * <tt>ConcurrentModificationException</tt> + */ + 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<OtrContact, TimerTask> tasks = + new ConcurrentHashMap<OtrContact, TimerTask>(); + + 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<OtrContact> 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 <tt>ProtocolProviderService</tt> + * 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<OtrContact> i = contactsMap.values().iterator(); + + while (i.hasNext()) { - OtrContact otrContact = i.next();
- if (provider.equals(
- otrContact.contact.getProtocolProvider()))
- {
- scSessionStatusMap.remove(getSessionID(otrContact));
- i.remove();
- }
- }
- }
-
- Iterator<OtrContact> 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<Session> 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<OtrContact> 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<Session> 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 <tt>AccountManager</tt> which loads the
- * accounts in a separate thread.
- *
- * @author Lyubomir Marinov
- * @author Yana Stamcheva
- */
-public class AccountManager
-{
- /**
- * The delay in milliseconds the background <tt>Thread</tt> loading the
- * stored accounts should wait before dying so that it doesn't get recreated
- * for each <tt>ProtocolProviderFactory</tt> registration.
- */
- private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000;
-
- /**
- * The <tt>BundleContext</tt> this service is registered in.
- */
- private final BundleContext bundleContext;
-
- /**
- * The <tt>AccountManagerListener</tt>s currently interested in the
- * events fired by this manager.
- */
- private final List<AccountManagerListener> listeners =
- new LinkedList<AccountManagerListener>();
-
- /**
- * The queue of <tt>ProtocolProviderFactory</tt> services awaiting their
- * stored accounts to be loaded.
- */
- private final Queue<ProtocolProviderFactory> loadStoredAccountsQueue =
- new LinkedList<ProtocolProviderFactory>();
-
- /**
- * The <tt>Thread</tt> loading the stored accounts of the
- * <tt>ProtocolProviderFactory</tt> services waiting in
- * {@link #loadStoredAccountsQueue}.
- */
- private Thread loadStoredAccountsThread;
-
- /**
- * The <tt>Logger</tt> used by this <tt>AccountManagerImpl</tt> instance for
- * logging output.
- */
- private final Logger logger = Logger.getLogger(AccountManager.class);
-
- /**
- * The list of <tt>AccountID</tt>s, corresponding to all stored accounts.
- */
- private final Vector<AccountID> storedAccounts = new Vector<AccountID>();
-
- /**
- * The prefix of the account unique identifier.
- */
- private static final String ACCOUNT_UID_PREFIX = "acc";
-
- /**
- * Initializes a new <tt>AccountManagerImpl</tt> instance loaded in a
- * specific <tt>BundleContext</tt> (in which the caller will usually
- * later register it).
- *
- * @param bundleContext the <tt>BundleContext</tt> in which the new
- * instance is loaded (and in which the caller will usually later
- * register it as a service)
- */
- public AccountManager(BundleContext bundleContext)
- {
- this.bundleContext = bundleContext;
-
- this.bundleContext.addServiceListener(new ServiceListener()
- {
- public void serviceChanged(ServiceEvent serviceEvent)
- {
- AccountManager.this.serviceChanged(serviceEvent);
- }
- });
- }
-
- /**
- * Implements AccountManager#addListener(AccountManagerListener).
- * @param listener the <tt>AccountManagerListener</tt> to add
- */
- public void addListener(AccountManagerListener listener)
- {
- synchronized (listeners)
- {
- if (!listeners.contains(listener))
- listeners.add(listener);
- }
- }
-
- /**
- * Loads the accounts stored for a specific
- * <tt>ProtocolProviderFactory</tt>.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> to load the
- * stored accounts of
- */
- private void doLoadStoredAccounts(ProtocolProviderFactory factory)
- {
- ConfigurationService configService
- = ProtocolProviderActivator.getConfigurationService();
- String factoryPackage = getFactoryImplPackageName(factory);
- List<String> accounts
- = configService.getPropertyNamesByPrefix(factoryPackage, true);
-
- if (logger.isDebugEnabled())
- logger.debug("Discovered " + accounts.size() + " stored "
- + factoryPackage + " accounts");
-
- for (Iterator<String> storedAccountIter = accounts.iterator();
- storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
-
- // If the property is not related to an account we skip it.
- int dotIndex = storedAccount.lastIndexOf(".");
- if (!storedAccount.substring(dotIndex + 1)
- .startsWith(ACCOUNT_UID_PREFIX))
- continue;
-
- if (logger.isDebugEnabled())
- logger.debug("Loading account " + storedAccount);
-
- List<String> storedAccountProperties =
- configService.getPropertyNamesByPrefix(storedAccount, false);
- Map<String, String> accountProperties =
- new Hashtable<String, String>();
- boolean disabled = false;
- CredentialsStorageService credentialsStorage
- = ServiceUtils.getService(
- bundleContext,
- CredentialsStorageService.class);
-
- int prefLen = storedAccount.length() + 1;
- for (Iterator<String> 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 <tt>ProtocolProviderFactory</tt> have just been loaded.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which had its
- * stored accounts just loaded
- */
- private void fireStoredAccountsLoaded(ProtocolProviderFactory factory)
- {
- AccountManagerListener[] listeners;
- synchronized (this.listeners)
- {
- listeners =
- this.listeners
- .toArray(new AccountManagerListener[this.listeners.size()]);
- }
-
- int listenerCount = listeners.length;
- if (listenerCount > 0)
- {
- AccountManagerEvent event =
- new AccountManagerEvent(this,
- AccountManagerEvent.STORED_ACCOUNTS_LOADED, factory);
-
- for (int listenerIndex = 0;
- listenerIndex < listenerCount; listenerIndex++)
- {
- listeners[listenerIndex].handleAccountManagerEvent(event);
- }
- }
- }
-
- /**
- * Returns the package name of the <tt>factory</tt>.
- * @param factory the factory which package will be returned.
- * @return the package name of the <tt>factory</tt>.
- */
- public String getFactoryImplPackageName(ProtocolProviderFactory factory)
- {
- String className = factory.getClass().getName();
-
- return className.substring(0, className.lastIndexOf('.'));
- }
-
- /**
- * Check for stored accounts for the supplied <tt>protocolName</tt>.
- * @param protocolName the protocol name to check for
- * @param includeHidden whether to include hidden providers
- * @return <tt>true</tt> if there is any account stored in configuration
- * service with <tt>protocolName</tt>, <tt>false</tt> otherwise.
- */
- public boolean hasStoredAccounts(String protocolName, boolean includeHidden)
- {
- return hasStoredAccount(protocolName, includeHidden, null);
- }
-
- /**
- * Checks whether a stored account with <tt>userID</tt> is stored
- * in configuration.
- *
- * @param protocolName the protocol name
- * @param includeHidden whether to check hidden providers
- * @param userID the user id to check.
- * @return <tt>true</tt> if there is any account stored in configuration
- * service with <tt>protocolName</tt> and <tt>userID</tt>,
- * <tt>false</tt> otherwise.
- */
- public boolean hasStoredAccount(String protocolName,
- boolean includeHidden,
- String userID)
- {
- Collection<ServiceReference<ProtocolProviderFactory>> factoryRefs
- = ServiceUtils.getServiceReferences(
- bundleContext,
- ProtocolProviderFactory.class);
- boolean hasStoredAccounts = false;
-
- if (!factoryRefs.isEmpty())
- {
- ConfigurationService configService
- = ProtocolProviderActivator.getConfigurationService();
-
- for (ServiceReference<ProtocolProviderFactory> factoryRef
- : factoryRefs)
- {
- ProtocolProviderFactory factory
- = bundleContext.getService(factoryRef);
-
- if ((protocolName != null)
- && !protocolName.equals(factory.getProtocolName()))
- {
- continue;
- }
-
- String factoryPackage = getFactoryImplPackageName(factory);
- List<String> storedAccounts
- = configService
- .getPropertyNamesByPrefix(factoryPackage + ".acc",
- false);
-
- /* Ignore the hidden accounts. */
- for (Iterator<String> storedAccountIter =
- storedAccounts.iterator(); storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
- List<String> storedAccountProperties =
- configService.getPropertyNamesByPrefix(storedAccount,
- true);
- boolean hidden = false;
- String accountUserID = null;
-
- if (!includeHidden || userID != null)
- {
- for (Iterator<String> storedAccountPropertyIter =
- storedAccountProperties.iterator();
- storedAccountPropertyIter.hasNext();)
- {
- String property = storedAccountPropertyIter.next();
- String value = configService.getString(property);
-
- property = stripPackagePrefix(property);
-
- if (ProtocolProviderFactory.IS_PROTOCOL_HIDDEN
- .equals(property))
- {
- hidden = (value != null);
- }
- else if (ProtocolProviderFactory.USER_ID
- .equals(property))
- {
- accountUserID = value;
- }
- }
- }
-
- if (includeHidden || !hidden)
- {
- if(accountUserID != null
- && userID != null
- && userID.equals(accountUserID))
- {
- hasStoredAccounts = true;
- break;
- }
- else if(userID == null)
- {
- hasStoredAccounts = true;
- break;
- }
- }
- }
-
- if (hasStoredAccounts || (protocolName != null))
- {
- break;
- }
- }
- }
- return hasStoredAccounts;
- }
-
- /**
- * Searches for stored account with <tt>uid</tt> in stored
- * configuration. The <tt>uid</tt> is the one generated when creating
- * accounts with prefix <tt>ACCOUNT_UID_PREFIX</tt>.
- *
- * @return <tt>AccountID</tt> if there is any account stored in configuration
- * service with <tt>uid</tt>,
- * <tt>null</tt> otherwise.
- */
- public AccountID findAccountID(String uid)
- {
- Collection<ServiceReference<ProtocolProviderFactory>> factoryRefs
- = ServiceUtils.getServiceReferences(
- bundleContext,
- ProtocolProviderFactory.class);
-
- if (!factoryRefs.isEmpty())
- {
- ConfigurationService configService
- = ProtocolProviderActivator.getConfigurationService();
-
- for (ServiceReference<ProtocolProviderFactory> factoryRef
- : factoryRefs)
- {
- ProtocolProviderFactory factory
- = bundleContext.getService(factoryRef);
-
- String factoryPackage = getFactoryImplPackageName(factory);
- List<String> storedAccountsProps
- = configService
- .getPropertyNamesByPrefix(factoryPackage, true);
-
- for (Iterator<String> storedAccountIter =
- storedAccountsProps.iterator();
- storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
-
- if(!storedAccount.endsWith(uid))
- continue;
-
- String accountUID = configService.getString(
- storedAccount //node id
- + "." + ProtocolProviderFactory.ACCOUNT_UID);// propname
-
- for(AccountID acc : storedAccounts)
- {
- if(acc.getAccountUniqueID().equals(accountUID))
- return acc;
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Loads the accounts stored for a specific
- * <tt>ProtocolProviderFactory</tt> and notifies the registered
- * {@link #listeners} that the stored accounts of the specified
- * <tt>factory</tt> have just been loaded
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> to load the
- * stored accounts of
- */
- private void loadStoredAccounts(ProtocolProviderFactory factory)
- {
- doLoadStoredAccounts(factory);
-
- fireStoredAccountsLoaded(factory);
- }
-
- /**
- * Notifies this manager that a specific
- * <tt>ProtocolProviderFactory</tt> has been registered as a service.
- * The current implementation queues the specified <tt>factory</tt> to
- * have its stored accounts as soon as possible.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which has been
- * registered as a service.
- */
- private void protocolProviderFactoryRegistered(
- ProtocolProviderFactory factory)
- {
- queueLoadStoredAccounts(factory);
- }
-
- /**
- * Queues a specific <tt>ProtocolProviderFactory</tt> to have its stored
- * accounts loaded as soon as possible.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> to be queued for
- * loading its stored accounts as soon as possible
- */
- private void queueLoadStoredAccounts(ProtocolProviderFactory factory)
- {
- synchronized (loadStoredAccountsQueue)
- {
- loadStoredAccountsQueue.add(factory);
- loadStoredAccountsQueue.notifyAll();
-
- if (loadStoredAccountsThread == null)
- {
- loadStoredAccountsThread = new Thread()
- {
- @Override
- public void run()
- {
- runInLoadStoredAccountsThread();
- }
- };
- loadStoredAccountsThread.setDaemon(true);
- loadStoredAccountsThread.setName(
- "AccountManager.loadStoredAccounts");
- loadStoredAccountsThread.start();
- }
- }
- }
-
- /**
- * Implements AccountManager#removeListener(AccountManagerListener).
- * @param listener the <tt>AccountManagerListener</tt> to remove
- */
- public void removeListener(AccountManagerListener listener)
- {
- synchronized (listeners)
- {
- listeners.remove(listener);
- }
- }
-
- /**
- * Running in {@link #loadStoredAccountsThread}, loads the stored accounts
- * of the <tt>ProtocolProviderFactory</tt> services waiting in
- * {@link #loadStoredAccountsQueue}
- */
- private void runInLoadStoredAccountsThread()
- {
- boolean interrupted = false;
- while (!interrupted)
- {
- try
- {
- ProtocolProviderFactory factory;
-
- synchronized (loadStoredAccountsQueue)
- {
- factory = loadStoredAccountsQueue.poll();
- if (factory == null)
- {
- /*
- * Technically, we should be handing spurious wakeups.
- * However, we cannot check the condition in a queue.
- * Anyway, we just want to keep this Thread alive long
- * enough to allow it to not be re-created multiple
- * times and not handing a spurious wakeup will just
- * cause such an inconvenience.
- */
- try
- {
- loadStoredAccountsQueue
- .wait(LOAD_STORED_ACCOUNTS_TIMEOUT);
- }
- catch (InterruptedException ex)
- {
- logger
- .warn(
- "The loading of the stored accounts has"
- + " been interrupted",
- ex);
- interrupted = true;
- break;
- }
- factory = loadStoredAccountsQueue.poll();
- }
- if (factory != null)
- loadStoredAccountsQueue.notifyAll();
- }
-
- if (factory != null)
- {
- try
- {
- loadStoredAccounts(factory);
- }
- catch (Exception ex)
- {
-
- /*
- * Swallow the exception in order to prevent a single
- * factory from halting the loading of subsequent
- * factories.
- */
- logger.error("Failed to load accounts for " + factory,
- ex);
- }
- }
- }
- finally
- {
- synchronized (loadStoredAccountsQueue)
- {
- if (!interrupted && (loadStoredAccountsQueue.size() <= 0))
- {
- if (loadStoredAccountsThread == Thread.currentThread())
- {
- loadStoredAccountsThread = null;
- loadStoredAccountsQueue.notifyAll();
- }
- break;
- }
- }
- }
- }
- }
-
- /**
- * Notifies this manager that an OSGi service has changed. The current
- * implementation tracks the registrations of
- * <tt>ProtocolProviderFactory</tt> services in order to queue them for
- * loading their stored accounts.
- *
- * @param serviceEvent the <tt>ServiceEvent</tt> containing the event
- * data
- */
- private void serviceChanged(ServiceEvent serviceEvent)
- {
- switch (serviceEvent.getType())
- {
- case ServiceEvent.REGISTERED:
- Object service
- = bundleContext.getService(serviceEvent.getServiceReference());
-
- if (service instanceof ProtocolProviderFactory)
- {
- protocolProviderFactoryRegistered(
- (ProtocolProviderFactory) service);
- }
- break;
- default:
- break;
- }
- }
-
- /**
- * Stores an account represented in the form of an <tt>AccountID</tt>
- * created by a specific <tt>ProtocolProviderFactory</tt>.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which created the
- * account to be stored
- * @param accountID the account in the form of <tt>AccountID</tt> to be
- * stored
- * @throws OperationFailedException if anything goes wrong while storing the
- * account
- */
- public void storeAccount(
- ProtocolProviderFactory factory,
- AccountID accountID)
- throws OperationFailedException
- {
- synchronized (storedAccounts)
- {
- if (!storedAccounts.contains(accountID))
- storedAccounts.add(accountID);
- }
-
- ConfigurationService configurationService
- = ProtocolProviderActivator.getConfigurationService();
- String factoryPackage = getFactoryImplPackageName(factory);
-
- String accountNodeName
- = getAccountNodeName( factory,
- accountID.getAccountUniqueID() );
-
- Map<String, Object> configurationProperties
- = new HashMap<String, Object>();
-
- // Create a unique node name of the properties node that will contain
- // this account's properties.
- if (accountNodeName == null)
- {
- accountNodeName
- = ACCOUNT_UID_PREFIX + Long.toString(System.currentTimeMillis());
-
- // set a value for the persistent node so that we could later
- // retrieve it as a property
- configurationProperties.put(
- factoryPackage /* prefix */ + "." + accountNodeName,
- accountNodeName);
-
- // register the account in the configuration service.
- // we register all the properties in the following hierarchy
- //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
- configurationProperties.put(factoryPackage// prefix
- + "." + accountNodeName // node name for the account id
- + "." + ProtocolProviderFactory.ACCOUNT_UID, // propname
- accountID.getAccountUniqueID()); // value
- }
-
- // store the rest of the properties
- Map<String, String> accountProperties = accountID.getAccountProperties();
-
- for (Map.Entry<String, String> entry : accountProperties.entrySet())
- {
- String property = entry.getKey();
- String value = entry.getValue();
- String secureStorePrefix = null;
-
- // If the property is a password, store it securely.
- if (property.equals(ProtocolProviderFactory.PASSWORD))
- {
- String accountPrefix = factoryPackage + "." + accountNodeName;
- secureStorePrefix = accountPrefix;
- }
- else if(property.endsWith("." + ProtocolProviderFactory.PASSWORD))
- {
- secureStorePrefix = factoryPackage + "." + accountNodeName +
- "." + property.substring(0, property.lastIndexOf("."));
- }
-
- if(secureStorePrefix != null)
- {
- CredentialsStorageService credentialsStorage
- = ServiceUtils.getService(
- bundleContext,
- CredentialsStorageService.class);
-
- // encrypt and store
- if ((value != null)
- && (value.length() != 0)
- && !credentialsStorage.storePassword(
- secureStorePrefix,
- value))
- {
- throw
- new OperationFailedException(
- "CredentialsStorageService failed to"
- + " storePassword",
- OperationFailedException.GENERAL_ERROR);
- }
- }
- else
- {
- configurationProperties.put(
- factoryPackage // prefix
- + "." + accountNodeName // a unique node name for the account id
- + "." + property, // propname
- value); // value
- }
- }
-
- // clear the password if missing property, modification can request
- // password delete
- if(!accountProperties.containsKey(ProtocolProviderFactory.PASSWORD)
- && // 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 <tt>accountID</tt> if exists or
- * <tt>null</tt> 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<String> storedAccounts =
- configurationService.getPropertyNamesByPrefix(factoryPackage, true);
- String accountNodeName = null;
-
- for (Iterator<String> storedAccountIter = storedAccounts.iterator();
- storedAccountIter.hasNext();)
- {
- String storedAccount = storedAccountIter.next();
-
- // If the property is not related to an account we skip it.
- int dotIndex = storedAccount.lastIndexOf(".");
- if (!storedAccount.substring(dotIndex + 1)
- .startsWith(ACCOUNT_UID_PREFIX))
- continue;
-
- String storedAccountUID
- = configurationService.getString(
- storedAccount + "." + ProtocolProviderFactory.ACCOUNT_UID);
-
- if(storedAccountUID == null)
- continue;
-
- if (storedAccountUID.equals(accountUID))
- accountNodeName = configurationService.getString(storedAccount);
- }
- return accountNodeName;
- }
-
- /**
- * Removes the account with <tt>accountID</tt> from the set of accounts
- * that are persistently stored inside the configuration service.
- *
- * @param factory the <tt>ProtocolProviderFactory</tt> which created the
- * account to be stored
- * @param accountID the AccountID of the account to remove.
- * @return true if an account has been removed and false otherwise.
- */
- public boolean removeStoredAccount(ProtocolProviderFactory factory,
- AccountID accountID)
- {
- synchronized (storedAccounts)
- {
- if (storedAccounts.contains(accountID))
- storedAccounts.remove(accountID);
- }
-
- /*
- * We're already doing it in #unloadAccount(AccountID) - we're figuring
- * out the ProtocolProviderFactory by the AccountID.
- */
- if (factory == null)
- {
- factory
- = ProtocolProviderActivator.getProtocolProviderFactory(
- accountID.getProtocolName());
- }
-
- String factoryPackage = getFactoryImplPackageName(factory);
-
- // remove the stored password explicitly using credentials service
- CredentialsStorageService credentialsStorage
- = ServiceUtils.getService(
- bundleContext,
- CredentialsStorageService.class);
- String accountPrefix =
- ProtocolProviderFactory.findAccountPrefix(bundleContext, accountID,
- factoryPackage);
-
- credentialsStorage.removePassword(accountPrefix);
-
- ConfigurationService configurationService
- = ServiceUtils.getService(
- bundleContext,
- ConfigurationService.class);
- //first retrieve all accounts that we've registered
- List<String> storedAccounts
- = configurationService.getPropertyNamesByPrefix(
- factoryPackage, true);
-
- //find an account with the corresponding id.
- for (String accountRootPropertyName : storedAccounts)
- {
- //unregister the account in the configuration service.
- //all the properties must have been registered in the following
- //hierarchy:
- //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME
- String accountUID = configurationService.getString(
- accountRootPropertyName //node id
- + "." + ProtocolProviderFactory.ACCOUNT_UID); // propname
-
- if (accountID.getAccountUniqueID().equals(accountUID))
- {
- //retrieve the names of all properties registered for the
- //current account.
- List<String> accountPropertyNames
- = configurationService.getPropertyNamesByPrefix(
- accountRootPropertyName, false);
-
- //set all account properties to null in order to remove them.
- for (String propName : accountPropertyNames)
- configurationService.setProperty(propName, null);
-
- //and now remove the parent too.
- configurationService.setProperty(accountRootPropertyName, null);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Removes all accounts which have been persistently stored.
- *
- * @see #removeStoredAccount(ProtocolProviderFactory, AccountID)
- */
- public void removeStoredAccounts()
- {
- synchronized (loadStoredAccountsQueue)
- {
- /*
- * Wait for the Thread which loads the stored account to complete so
- * that we can be sure later on that it will not load a stored
- * account while we are deleting it or another one for that matter.
- */
- boolean interrupted = false;
-
- while (loadStoredAccountsThread != null)
- try
- {
- loadStoredAccountsQueue.wait(LOAD_STORED_ACCOUNTS_TIMEOUT);
- }
- catch (InterruptedException ie)
- {
- interrupted = true;
- }
- if (interrupted)
- Thread.currentThread().interrupt();
-
- synchronized (this.storedAccounts)
- {
- AccountID[] storedAccounts
- = this.storedAccounts.toArray(
- new AccountID[this.storedAccounts.size()]);
-
- for (AccountID storedAccount : storedAccounts)
- {
- ProtocolProviderFactory ppf
- = ProtocolProviderActivator.getProtocolProviderFactory(
- storedAccount.getProtocolName());
-
- if (ppf != null)
- ppf.uninstallAccount(storedAccount);
- }
- }
- }
- }
-
- /**
- * Returns an <tt>Iterator</tt> over a list of all stored
- * <tt>AccountID</tt>s. The list of stored accounts include all registered
- * accounts and all disabled accounts. In other words in this list we could
- * find accounts that aren't loaded.
- * <p>
- * In order to check if an account is already loaded please use the
- * #isAccountLoaded(AccountID accountID) method. To load an account use the
- * #loadAccount(AccountID accountID) method.
- *
- * @return an <tt>Iterator</tt> over a list of all stored
- * <tt>AccountID</tt>s
- */
- public Collection<AccountID> getStoredAccounts()
- {
- synchronized (storedAccounts)
- {
- return new Vector<AccountID>(storedAccounts);
- }
- }
-
- /**
- * Loads the account corresponding to the given <tt>AccountID</tt>. An
- * account is loaded when its <tt>ProtocolProviderService</tt> is registered
- * in the bundle context. This method is meant to load the account through
- * the corresponding <tt>ProtocolProviderFactory</tt>.
- *
- * @param accountID the identifier of the account to load
- * @throws OperationFailedException if anything goes wrong while loading the
- * account corresponding to the specified <tt>accountID</tt>
- */
- public void loadAccount(AccountID accountID)
- throws OperationFailedException
- {
- // If the account with the given id is already loaded we have nothing
- // to do here.
- if (isAccountLoaded(accountID))
- return;
-
- ProtocolProviderFactory providerFactory
- = ProtocolProviderActivator.getProtocolProviderFactory(
- accountID.getProtocolName());
-
- if(providerFactory.loadAccount(accountID))
- {
- accountID.putAccountProperty(
- ProtocolProviderFactory.IS_ACCOUNT_DISABLED,
- String.valueOf(false));
- // Finally store the modified properties.
- storeAccount(providerFactory, accountID);
- }
- }
-
- /**
- * Unloads the account corresponding to the given <tt>AccountID</tt>. An
- * account is unloaded when its <tt>ProtocolProviderService</tt> is
- * unregistered in the bundle context. This method is meant to unload the
- * account through the corresponding <tt>ProtocolProviderFactory</tt>.
- *
- * @param accountID the identifier of the account to load
- * @throws OperationFailedException if anything goes wrong while unloading
- * the account corresponding to the specified <tt>accountID</tt>
- */
- public void unloadAccount(AccountID accountID)
- throws OperationFailedException
- {
- // If the account with the given id is already unloaded we have nothing
- // to do here.
- if (!isAccountLoaded(accountID))
- return;
-
- ProtocolProviderFactory providerFactory
- = ProtocolProviderActivator.getProtocolProviderFactory(
- accountID.getProtocolName());
-
- // Obtain the protocol provider.
- ServiceReference<ProtocolProviderService> 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 <tt>accountID</tt> is
- * loaded. An account is loaded if its <tt>ProtocolProviderService</tt> is
- * registered in the bundle context. By default all accounts are loaded.
- * However the user could manually unload an account, which would be
- * unregistered from the bundle context, but would remain in the
- * configuration file.
- *
- * @param accountID the identifier of the account to load
- * @return <tt>true</tt> to indicate that the account with the given
- * <tt>accountID</tt> is loaded, <tt>false</tt> - otherwise
- */
- public boolean isAccountLoaded(AccountID accountID)
- {
- return storedAccounts.contains(accountID) && accountID.isEnabled();
- }
-
- private String stripPackagePrefix(String property)
- {
- int packageEndIndex = property.lastIndexOf('.');
-
- if (packageEndIndex != -1)
- property = property.substring(packageEndIndex + 1);
- return property;
- }
-}
+/* + * 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 <tt>AccountManager</tt> which loads the + * accounts in a separate thread. + * + * @author Lyubomir Marinov + * @author Yana Stamcheva + */ +public class AccountManager +{ + /** + * The delay in milliseconds the background <tt>Thread</tt> loading the + * stored accounts should wait before dying so that it doesn't get recreated + * for each <tt>ProtocolProviderFactory</tt> registration. + */ + private static final long LOAD_STORED_ACCOUNTS_TIMEOUT = 30000; + + /** + * The <tt>BundleContext</tt> this service is registered in. + */ + private final BundleContext bundleContext; + + /** + * The <tt>AccountManagerListener</tt>s currently interested in the + * events fired by this manager. + */ + private final List<AccountManagerListener> listeners = + new LinkedList<AccountManagerListener>(); + + /** + * The queue of <tt>ProtocolProviderFactory</tt> services awaiting their + * stored accounts to be loaded. + */ + private final Queue<ProtocolProviderFactory> loadStoredAccountsQueue = + new LinkedList<ProtocolProviderFactory>(); + + /** + * The <tt>Thread</tt> loading the stored accounts of the + * <tt>ProtocolProviderFactory</tt> services waiting in + * {@link #loadStoredAccountsQueue}. + */ + private Thread loadStoredAccountsThread; + + /** + * The <tt>Logger</tt> used by this <tt>AccountManagerImpl</tt> instance for + * logging output. + */ + private final Logger logger = Logger.getLogger(AccountManager.class); + + /** + * The list of <tt>AccountID</tt>s, corresponding to all stored accounts. + */ + private final Vector<AccountID> storedAccounts = new Vector<AccountID>(); + + /** + * The prefix of the account unique identifier. + */ + private static final String ACCOUNT_UID_PREFIX = "acc"; + + /** + * Initializes a new <tt>AccountManagerImpl</tt> instance loaded in a + * specific <tt>BundleContext</tt> (in which the caller will usually + * later register it). + * + * @param bundleContext the <tt>BundleContext</tt> in which the new + * instance is loaded (and in which the caller will usually later + * register it as a service) + */ + public AccountManager(BundleContext bundleContext) + { + this.bundleContext = bundleContext; + + this.bundleContext.addServiceListener(new ServiceListener() + { + public void serviceChanged(ServiceEvent serviceEvent) + { + AccountManager.this.serviceChanged(serviceEvent); + } + }); + } + + /** + * Implements AccountManager#addListener(AccountManagerListener). + * @param listener the <tt>AccountManagerListener</tt> to add + */ + public void addListener(AccountManagerListener listener) + { + synchronized (listeners) + { + if (!listeners.contains(listener)) + listeners.add(listener); + } + } + + /** + * Loads the accounts stored for a specific + * <tt>ProtocolProviderFactory</tt>. + * + * @param factory the <tt>ProtocolProviderFactory</tt> to load the + * stored accounts of + */ + private void doLoadStoredAccounts(ProtocolProviderFactory factory) + { + ConfigurationService configService + = ProtocolProviderActivator.getConfigurationService(); + String factoryPackage = getFactoryImplPackageName(factory); + List<String> accounts + = configService.getPropertyNamesByPrefix(factoryPackage, true); + + if (logger.isDebugEnabled()) + logger.debug("Discovered " + accounts.size() + " stored " + + factoryPackage + " accounts"); + + for (Iterator<String> storedAccountIter = accounts.iterator(); + storedAccountIter.hasNext();) + { + String storedAccount = storedAccountIter.next(); + + // If the property is not related to an account we skip it. + int dotIndex = storedAccount.lastIndexOf("."); + if (!storedAccount.substring(dotIndex + 1) + .startsWith(ACCOUNT_UID_PREFIX)) + continue; + + if (logger.isDebugEnabled()) + logger.debug("Loading account " + storedAccount); + + List<String> storedAccountProperties = + configService.getPropertyNamesByPrefix(storedAccount, false); + Map<String, String> accountProperties = + new Hashtable<String, String>(); + boolean disabled = false; + CredentialsStorageService credentialsStorage + = ServiceUtils.getService( + bundleContext, + CredentialsStorageService.class); + + int prefLen = storedAccount.length() + 1; + for (Iterator<String> 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 <tt>ProtocolProviderFactory</tt> have just been loaded. + * + * @param factory the <tt>ProtocolProviderFactory</tt> which had its + * stored accounts just loaded + */ + private void fireStoredAccountsLoaded(ProtocolProviderFactory factory) + { + AccountManagerListener[] listeners; + synchronized (this.listeners) + { + listeners = + this.listeners + .toArray(new AccountManagerListener[this.listeners.size()]); + } + + int listenerCount = listeners.length; + if (listenerCount > 0) + { + AccountManagerEvent event = + new AccountManagerEvent(this, + AccountManagerEvent.STORED_ACCOUNTS_LOADED, factory); + + for (int listenerIndex = 0; + listenerIndex < listenerCount; listenerIndex++) + { + listeners[listenerIndex].handleAccountManagerEvent(event); + } + } + } + + /** + * Returns the package name of the <tt>factory</tt>. + * @param factory the factory which package will be returned. + * @return the package name of the <tt>factory</tt>. + */ + public String getFactoryImplPackageName(ProtocolProviderFactory factory) + { + String className = factory.getClass().getName(); + + return className.substring(0, className.lastIndexOf('.')); + } + + /** + * Check for stored accounts for the supplied <tt>protocolName</tt>. + * @param protocolName the protocol name to check for + * @param includeHidden whether to include hidden providers + * @return <tt>true</tt> if there is any account stored in configuration + * service with <tt>protocolName</tt>, <tt>false</tt> otherwise. + */ + public boolean hasStoredAccounts(String protocolName, boolean includeHidden) + { + return hasStoredAccount(protocolName, includeHidden, null); + } + + /** + * Checks whether a stored account with <tt>userID</tt> is stored + * in configuration. + * + * @param protocolName the protocol name + * @param includeHidden whether to check hidden providers + * @param userID the user id to check. + * @return <tt>true</tt> if there is any account stored in configuration + * service with <tt>protocolName</tt> and <tt>userID</tt>, + * <tt>false</tt> otherwise. + */ + public boolean hasStoredAccount(String protocolName, + boolean includeHidden, + String userID) + { + Collection<ServiceReference<ProtocolProviderFactory>> factoryRefs + = ServiceUtils.getServiceReferences( + bundleContext, + ProtocolProviderFactory.class); + boolean hasStoredAccounts = false; + + if (!factoryRefs.isEmpty()) + { + ConfigurationService configService + = ProtocolProviderActivator.getConfigurationService(); + + for (ServiceReference<ProtocolProviderFactory> factoryRef + : factoryRefs) + { + ProtocolProviderFactory factory + = bundleContext.getService(factoryRef); + + if ((protocolName != null) + && !protocolName.equals(factory.getProtocolName())) + { + continue; + } + + String factoryPackage = getFactoryImplPackageName(factory); + List<String> storedAccounts + = configService + .getPropertyNamesByPrefix(factoryPackage + ".acc", + false); + + /* Ignore the hidden accounts. */ + for (Iterator<String> storedAccountIter = + storedAccounts.iterator(); storedAccountIter.hasNext();) + { + String storedAccount = storedAccountIter.next(); + List<String> storedAccountProperties = + configService.getPropertyNamesByPrefix(storedAccount, + true); + boolean hidden = false; + String accountUserID = null; + + if (!includeHidden || userID != null) + { + for (Iterator<String> storedAccountPropertyIter = + storedAccountProperties.iterator(); + storedAccountPropertyIter.hasNext();) + { + String property = storedAccountPropertyIter.next(); + String value = configService.getString(property); + + property = stripPackagePrefix(property); + + if (ProtocolProviderFactory.IS_PROTOCOL_HIDDEN + .equals(property)) + { + hidden = (value != null); + } + else if (ProtocolProviderFactory.USER_ID + .equals(property)) + { + accountUserID = value; + } + } + } + + if (includeHidden || !hidden) + { + if(accountUserID != null + && userID != null + && userID.equals(accountUserID)) + { + hasStoredAccounts = true; + break; + } + else if(userID == null) + { + hasStoredAccounts = true; + break; + } + } + } + + if (hasStoredAccounts || (protocolName != null)) + { + break; + } + } + } + return hasStoredAccounts; + } + + /** + * Searches for stored account with <tt>uid</tt> in stored + * configuration. The <tt>uid</tt> is the one generated when creating + * accounts with prefix <tt>ACCOUNT_UID_PREFIX</tt>. + * + * @return <tt>AccountID</tt> if there is any account stored in configuration + * service with <tt>uid</tt>, + * <tt>null</tt> otherwise. + */ + public AccountID findAccountID(String uid) + { + Collection<ServiceReference<ProtocolProviderFactory>> factoryRefs + = ServiceUtils.getServiceReferences( + bundleContext, + ProtocolProviderFactory.class); + + if (!factoryRefs.isEmpty()) + { + ConfigurationService configService + = ProtocolProviderActivator.getConfigurationService(); + + for (ServiceReference<ProtocolProviderFactory> factoryRef + : factoryRefs) + { + ProtocolProviderFactory factory + = bundleContext.getService(factoryRef); + + String factoryPackage = getFactoryImplPackageName(factory); + List<String> storedAccountsProps + = configService + .getPropertyNamesByPrefix(factoryPackage, true); + + for (Iterator<String> storedAccountIter = + storedAccountsProps.iterator(); + storedAccountIter.hasNext();) + { + String storedAccount = storedAccountIter.next(); + + if(!storedAccount.endsWith(uid)) + continue; + + String accountUID = configService.getString( + storedAccount //node id + + "." + ProtocolProviderFactory.ACCOUNT_UID);// propname + + for(AccountID acc : storedAccounts) + { + if(acc.getAccountUniqueID().equals(accountUID)) + return acc; + } + } + } + } + return null; + } + + /** + * Loads the accounts stored for a specific + * <tt>ProtocolProviderFactory</tt> and notifies the registered + * {@link #listeners} that the stored accounts of the specified + * <tt>factory</tt> have just been loaded + * + * @param factory the <tt>ProtocolProviderFactory</tt> to load the + * stored accounts of + */ + private void loadStoredAccounts(ProtocolProviderFactory factory) + { + doLoadStoredAccounts(factory); + + fireStoredAccountsLoaded(factory); + } + + /** + * Notifies this manager that a specific + * <tt>ProtocolProviderFactory</tt> has been registered as a service. + * The current implementation queues the specified <tt>factory</tt> to + * have its stored accounts as soon as possible. + * + * @param factory the <tt>ProtocolProviderFactory</tt> which has been + * registered as a service. + */ + private void protocolProviderFactoryRegistered( + ProtocolProviderFactory factory) + { + queueLoadStoredAccounts(factory); + } + + /** + * Queues a specific <tt>ProtocolProviderFactory</tt> to have its stored + * accounts loaded as soon as possible. + * + * @param factory the <tt>ProtocolProviderFactory</tt> to be queued for + * loading its stored accounts as soon as possible + */ + private void queueLoadStoredAccounts(ProtocolProviderFactory factory) + { + synchronized (loadStoredAccountsQueue) + { + loadStoredAccountsQueue.add(factory); + loadStoredAccountsQueue.notifyAll(); + + if (loadStoredAccountsThread == null) + { + loadStoredAccountsThread = new Thread() + { + @Override + public void run() + { + runInLoadStoredAccountsThread(); + } + }; + loadStoredAccountsThread.setDaemon(true); + loadStoredAccountsThread.setName( + "AccountManager.loadStoredAccounts"); + loadStoredAccountsThread.start(); + } + } + } + + /** + * Implements AccountManager#removeListener(AccountManagerListener). + * @param listener the <tt>AccountManagerListener</tt> to remove + */ + public void removeListener(AccountManagerListener listener) + { + synchronized (listeners) + { + listeners.remove(listener); + } + } + + /** + * Running in {@link #loadStoredAccountsThread}, loads the stored accounts + * of the <tt>ProtocolProviderFactory</tt> services waiting in + * {@link #loadStoredAccountsQueue} + */ + private void runInLoadStoredAccountsThread() + { + boolean interrupted = false; + while (!interrupted) + { + try + { + ProtocolProviderFactory factory; + + synchronized (loadStoredAccountsQueue) + { + factory = loadStoredAccountsQueue.poll(); + if (factory == null) + { + /* + * Technically, we should be handing spurious wakeups. + * However, we cannot check the condition in a queue. + * Anyway, we just want to keep this Thread alive long + * enough to allow it to not be re-created multiple + * times and not handing a spurious wakeup will just + * cause such an inconvenience. + */ + try + { + loadStoredAccountsQueue + .wait(LOAD_STORED_ACCOUNTS_TIMEOUT); + } + catch (InterruptedException ex) + { + logger + .warn( + "The loading of the stored accounts has" + + " been interrupted", + ex); + interrupted = true; + break; + } + factory = loadStoredAccountsQueue.poll(); + } + if (factory != null) + loadStoredAccountsQueue.notifyAll(); + } + + if (factory != null) + { + try + { + loadStoredAccounts(factory); + } + catch (Exception ex) + { + + /* + * Swallow the exception in order to prevent a single + * factory from halting the loading of subsequent + * factories. + */ + logger.error("Failed to load accounts for " + factory, + ex); + } + } + } + finally + { + synchronized (loadStoredAccountsQueue) + { + if (!interrupted && (loadStoredAccountsQueue.size() <= 0)) + { + if (loadStoredAccountsThread == Thread.currentThread()) + { + loadStoredAccountsThread = null; + loadStoredAccountsQueue.notifyAll(); + } + break; + } + } + } + } + } + + /** + * Notifies this manager that an OSGi service has changed. The current + * implementation tracks the registrations of + * <tt>ProtocolProviderFactory</tt> services in order to queue them for + * loading their stored accounts. + * + * @param serviceEvent the <tt>ServiceEvent</tt> containing the event + * data + */ + private void serviceChanged(ServiceEvent serviceEvent) + { + switch (serviceEvent.getType()) + { + case ServiceEvent.REGISTERED: + Object service + = bundleContext.getService(serviceEvent.getServiceReference()); + + if (service instanceof ProtocolProviderFactory) + { + protocolProviderFactoryRegistered( + (ProtocolProviderFactory) service); + } + break; + default: + break; + } + } + + /** + * Stores an account represented in the form of an <tt>AccountID</tt> + * created by a specific <tt>ProtocolProviderFactory</tt>. + * + * @param factory the <tt>ProtocolProviderFactory</tt> which created the + * account to be stored + * @param accountID the account in the form of <tt>AccountID</tt> to be + * stored + * @throws OperationFailedException if anything goes wrong while storing the + * account + */ + public void storeAccount( + ProtocolProviderFactory factory, + AccountID accountID) + throws OperationFailedException + { + synchronized (storedAccounts) + { + if (!storedAccounts.contains(accountID)) + storedAccounts.add(accountID); + } + + ConfigurationService configurationService + = ProtocolProviderActivator.getConfigurationService(); + String factoryPackage = getFactoryImplPackageName(factory); + + String accountNodeName + = getAccountNodeName( factory, + accountID.getAccountUniqueID() ); + + Map<String, Object> configurationProperties + = new HashMap<String, Object>(); + + // Create a unique node name of the properties node that will contain + // this account's properties. + if (accountNodeName == null) + { + accountNodeName + = ACCOUNT_UID_PREFIX + Long.toString(System.currentTimeMillis()); + + // set a value for the persistent node so that we could later + // retrieve it as a property + configurationProperties.put( + factoryPackage /* prefix */ + "." + accountNodeName, + accountNodeName); + + // register the account in the configuration service. + // we register all the properties in the following hierarchy + //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME + configurationProperties.put(factoryPackage// prefix + + "." + accountNodeName // node name for the account id + + "." + ProtocolProviderFactory.ACCOUNT_UID, // propname + accountID.getAccountUniqueID()); // value + } + + // store the rest of the properties + Map<String, String> accountProperties = accountID.getAccountProperties(); + + for (Map.Entry<String, String> entry : accountProperties.entrySet()) + { + String property = entry.getKey(); + String value = entry.getValue(); + String secureStorePrefix = null; + + // If the property is a password, store it securely. + if (property.equals(ProtocolProviderFactory.PASSWORD)) + { + String accountPrefix = factoryPackage + "." + accountNodeName; + secureStorePrefix = accountPrefix; + } + else if(property.endsWith("." + ProtocolProviderFactory.PASSWORD)) + { + secureStorePrefix = factoryPackage + "." + accountNodeName + + "." + property.substring(0, property.lastIndexOf(".")); + } + + if(secureStorePrefix != null) + { + CredentialsStorageService credentialsStorage + = ServiceUtils.getService( + bundleContext, + CredentialsStorageService.class); + + // encrypt and store + if ((value != null) + && (value.length() != 0) + && !credentialsStorage.storePassword( + secureStorePrefix, + value)) + { + throw + new OperationFailedException( + "CredentialsStorageService failed to" + + " storePassword", + OperationFailedException.GENERAL_ERROR); + } + } + else + { + configurationProperties.put( + factoryPackage // prefix + + "." + accountNodeName // a unique node name for the account id + + "." + property, // propname + value); // value + } + } + + // clear the password if missing property, modification can request + // password delete + if(!accountProperties.containsKey(ProtocolProviderFactory.PASSWORD) + && // 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 <tt>accountID</tt> if exists or + * <tt>null</tt> 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<String> storedAccounts = + configurationService.getPropertyNamesByPrefix(factoryPackage, true); + String accountNodeName = null; + + for (Iterator<String> storedAccountIter = storedAccounts.iterator(); + storedAccountIter.hasNext();) + { + String storedAccount = storedAccountIter.next(); + + // If the property is not related to an account we skip it. + int dotIndex = storedAccount.lastIndexOf("."); + if (!storedAccount.substring(dotIndex + 1) + .startsWith(ACCOUNT_UID_PREFIX)) + continue; + + String storedAccountUID + = configurationService.getString( + storedAccount + "." + ProtocolProviderFactory.ACCOUNT_UID); + + if(storedAccountUID == null) + continue; + + if (storedAccountUID.equals(accountUID)) + accountNodeName = configurationService.getString(storedAccount); + } + return accountNodeName; + } + + /** + * Removes the account with <tt>accountID</tt> from the set of accounts + * that are persistently stored inside the configuration service. + * + * @param factory the <tt>ProtocolProviderFactory</tt> which created the + * account to be stored + * @param accountID the AccountID of the account to remove. + * @return true if an account has been removed and false otherwise. + */ + public boolean removeStoredAccount(ProtocolProviderFactory factory, + AccountID accountID) + { + synchronized (storedAccounts) + { + if (storedAccounts.contains(accountID)) + storedAccounts.remove(accountID); + } + + /* + * We're already doing it in #unloadAccount(AccountID) - we're figuring + * out the ProtocolProviderFactory by the AccountID. + */ + if (factory == null) + { + factory + = ProtocolProviderActivator.getProtocolProviderFactory( + accountID.getProtocolName()); + } + + String factoryPackage = getFactoryImplPackageName(factory); + + // remove the stored password explicitly using credentials service + CredentialsStorageService credentialsStorage + = ServiceUtils.getService( + bundleContext, + CredentialsStorageService.class); + String accountPrefix = + ProtocolProviderFactory.findAccountPrefix(bundleContext, accountID, + factoryPackage); + + credentialsStorage.removePassword(accountPrefix); + + ConfigurationService configurationService + = ServiceUtils.getService( + bundleContext, + ConfigurationService.class); + //first retrieve all accounts that we've registered + List<String> storedAccounts + = configurationService.getPropertyNamesByPrefix( + factoryPackage, true); + + //find an account with the corresponding id. + for (String accountRootPropertyName : storedAccounts) + { + //unregister the account in the configuration service. + //all the properties must have been registered in the following + //hierarchy: + //net.java.sip.communicator.impl.protocol.PROTO_NAME.ACC_ID.PROP_NAME + String accountUID = configurationService.getString( + accountRootPropertyName //node id + + "." + ProtocolProviderFactory.ACCOUNT_UID); // propname + + if (accountID.getAccountUniqueID().equals(accountUID)) + { + //retrieve the names of all properties registered for the + //current account. + List<String> accountPropertyNames + = configurationService.getPropertyNamesByPrefix( + accountRootPropertyName, false); + + //set all account properties to null in order to remove them. + for (String propName : accountPropertyNames) + configurationService.setProperty(propName, null); + + //and now remove the parent too. + configurationService.setProperty(accountRootPropertyName, null); + return true; + } + } + return false; + } + + /** + * Removes all accounts which have been persistently stored. + * + * @see #removeStoredAccount(ProtocolProviderFactory, AccountID) + */ + public void removeStoredAccounts() + { + synchronized (loadStoredAccountsQueue) + { + /* + * Wait for the Thread which loads the stored account to complete so + * that we can be sure later on that it will not load a stored + * account while we are deleting it or another one for that matter. + */ + boolean interrupted = false; + + while (loadStoredAccountsThread != null) + try + { + loadStoredAccountsQueue.wait(LOAD_STORED_ACCOUNTS_TIMEOUT); + } + catch (InterruptedException ie) + { + interrupted = true; + } + if (interrupted) + Thread.currentThread().interrupt(); + + synchronized (this.storedAccounts) + { + AccountID[] storedAccounts + = this.storedAccounts.toArray( + new AccountID[this.storedAccounts.size()]); + + for (AccountID storedAccount : storedAccounts) + { + ProtocolProviderFactory ppf + = ProtocolProviderActivator.getProtocolProviderFactory( + storedAccount.getProtocolName()); + + if (ppf != null) + ppf.uninstallAccount(storedAccount); + } + } + } + } + + /** + * Returns an <tt>Iterator</tt> over a list of all stored + * <tt>AccountID</tt>s. The list of stored accounts include all registered + * accounts and all disabled accounts. In other words in this list we could + * find accounts that aren't loaded. + * <p> + * In order to check if an account is already loaded please use the + * #isAccountLoaded(AccountID accountID) method. To load an account use the + * #loadAccount(AccountID accountID) method. + * + * @return an <tt>Iterator</tt> over a list of all stored + * <tt>AccountID</tt>s + */ + public Collection<AccountID> getStoredAccounts() + { + synchronized (storedAccounts) + { + return new Vector<AccountID>(storedAccounts); + } + } + + /** + * Loads the account corresponding to the given <tt>AccountID</tt>. An + * account is loaded when its <tt>ProtocolProviderService</tt> is registered + * in the bundle context. This method is meant to load the account through + * the corresponding <tt>ProtocolProviderFactory</tt>. + * + * @param accountID the identifier of the account to load + * @throws OperationFailedException if anything goes wrong while loading the + * account corresponding to the specified <tt>accountID</tt> + */ + public void loadAccount(AccountID accountID) + throws OperationFailedException + { + // If the account with the given id is already loaded we have nothing + // to do here. + if (isAccountLoaded(accountID)) + return; + + ProtocolProviderFactory providerFactory + = ProtocolProviderActivator.getProtocolProviderFactory( + accountID.getProtocolName()); + + if(providerFactory.loadAccount(accountID)) + { + accountID.putAccountProperty( + ProtocolProviderFactory.IS_ACCOUNT_DISABLED, + String.valueOf(false)); + // Finally store the modified properties. + storeAccount(providerFactory, accountID); + } + } + + /** + * Unloads the account corresponding to the given <tt>AccountID</tt>. An + * account is unloaded when its <tt>ProtocolProviderService</tt> is + * unregistered in the bundle context. This method is meant to unload the + * account through the corresponding <tt>ProtocolProviderFactory</tt>. + * + * @param accountID the identifier of the account to load + * @throws OperationFailedException if anything goes wrong while unloading + * the account corresponding to the specified <tt>accountID</tt> + */ + public void unloadAccount(AccountID accountID) + throws OperationFailedException + { + // If the account with the given id is already unloaded we have nothing + // to do here. + if (!isAccountLoaded(accountID)) + return; + + ProtocolProviderFactory providerFactory + = ProtocolProviderActivator.getProtocolProviderFactory( + accountID.getProtocolName()); + + // Obtain the protocol provider. + ServiceReference<ProtocolProviderService> 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 <tt>accountID</tt> is + * loaded. An account is loaded if its <tt>ProtocolProviderService</tt> is + * registered in the bundle context. By default all accounts are loaded. + * However the user could manually unload an account, which would be + * unregistered from the bundle context, but would remain in the + * configuration file. + * + * @param accountID the identifier of the account to load + * @return <tt>true</tt> to indicate that the account with the given + * <tt>accountID</tt> is loaded, <tt>false</tt> - otherwise + */ + public boolean isAccountLoaded(AccountID accountID) + { + return storedAccounts.contains(accountID) && accountID.isEnabled(); + } + + private String stripPackagePrefix(String property) + { + int packageEndIndex = property.lastIndexOf('.'); + + if (packageEndIndex != -1) + property = property.substring(packageEndIndex + 1); + return property; + } +} |