aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip
diff options
context:
space:
mode:
authorSebastien Vincent <seb@jitsi.org>2010-12-10 18:24:33 +0000
committerSebastien Vincent <seb@jitsi.org>2010-12-10 18:24:33 +0000
commitf5b574a71a9489a4df4e9ec0e040c62a8887087c (patch)
treec9c0abb3be1db7ea188014ce329f35c02750cc1f /src/net/java/sip
parentd016f4fd6dfa2c61f38203a1ef3185b3ff6eccdb (diff)
downloadjitsi-f5b574a71a9489a4df4e9ec0e040c62a8887087c.zip
jitsi-f5b574a71a9489a4df4e9ec0e040c62a8887087c.tar.gz
jitsi-f5b574a71a9489a4df4e9ec0e040c62a8887087c.tar.bz2
Initial support for Jingle Nodes. Please note that this feature is disabled by default.
Diffstat (limited to 'src/net/java/sip')
-rw-r--r--src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java7
-rw-r--r--src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf3
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java23
-rwxr-xr-xsrc/net/java/sip/communicator/impl/protocol/jabber/JabberAccountID.java63
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java117
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidateDatagramSocket.java136
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java181
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java27
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java95
-rw-r--r--src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java10
-rwxr-xr-xsrc/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf7
-rw-r--r--src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java55
-rw-r--r--src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java487
-rwxr-xr-xsrc/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java94
-rw-r--r--src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationWizard.java35
-rw-r--r--src/net/java/sip/communicator/service/protocol/JingleNodeDescriptor.java185
-rw-r--r--src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java12
-rw-r--r--src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java1
18 files changed, 1505 insertions, 33 deletions
diff --git a/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java b/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java
index a37ced0..c190c1e 100644
--- a/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java
+++ b/src/net/java/sip/communicator/impl/gui/main/chat/toolBars/MainToolBar.java
@@ -47,6 +47,9 @@ public class MainToolBar
ChatSessionChangeListener,
Skinnable
{
+ /**
+ * Serial version UID.
+ */
private static final long serialVersionUID = -5572510509556499465L;
/**
@@ -114,7 +117,7 @@ public class MainToolBar
/**
* The current <tt>ChatSession</tt> made known to this instance by the last
- * call to its {@link #chatChanged(ChatPanel)}.
+ * call to its {@link #chatChanged(ChatPanel)}.
*/
private ChatSession chatSession;
@@ -503,7 +506,7 @@ public class MainToolBar
/**
* Sets the current <tt>ChatSession</tt> made known to this instance by the
* last call to its {@link #chatChanged(ChatPanel)}.
- *
+ *
* @param chatSession the <tt>ChatSession</tt> to become current for this
* instance
*/
diff --git a/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf b/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf
index 2f0266b..6d5e1f8 100644
--- a/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf
+++ b/src/net/java/sip/communicator/impl/netaddr/netaddr.manifest.mf
@@ -8,11 +8,14 @@ Import-Package: net.java.sip.communicator.service.configuration,
net.java.sip.communicator.service.packetlogging,
net.java.sip.communicator.util,
org.osgi.framework,
+ org.ice4j.stack,
javax.crypto,
javax.crypto.spec
Export-Package: net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.service.netaddr.event,
org.ice4j,
+ org.ice4j.socket,
+ org.ice4j.stack,
org.ice4j.ice,
org.ice4j.ice.harvest,
org.ice4j.security
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
index d6e6721..b07f71c 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/IceUdpTransportManager.java
@@ -21,6 +21,7 @@ import org.ice4j.*;
import org.ice4j.ice.*;
import org.ice4j.ice.harvest.*;
import org.ice4j.security.*;
+import org.xmpp.jnodes.smack.*;
/**
* A {@link TransportManagerJabberImpl} implementation that would use ICE for
@@ -79,7 +80,6 @@ public class IceUdpTransportManager
public IceUdpTransportManager(CallPeerJabberImpl callPeer)
{
super(callPeer);
-
iceAgent = createIceAgent();
}
@@ -185,6 +185,24 @@ public class IceUdpTransportManager
}
}
+ /* Jingle nodes candidate */
+ if(accID.isJingleNodesRelayEnabled())
+ {
+ /* this method is blocking until Jingle Nodes auto-discovery (if
+ * enabled) finished
+ */
+ SmackServiceNode serviceNode =
+ peer.getProtocolProvider().getJingleNodesServiceNode();
+
+ JingleNodesHarvester harvester = new JingleNodesHarvester(
+ serviceNode);
+
+ if(harvester != null)
+ {
+ agent.addCandidateHarvester(harvester);
+ }
+ }
+
return agent;
}
@@ -232,7 +250,8 @@ public class IceUdpTransportManager
* of the <tt>MediaStream</tt> with the specified <tt>MediaType</tt>
* @throws OperationFailedException if anything goes wrong while
* initializing the requested <tt>StreamConnector</tt>
- * @see net.java.sip.communicator.service.protocol.media.TransportManager#getStreamConnector(MediaType)
+ * @see net.java.sip.communicator.service.protocol.media.TransportManager#
+ * getStreamConnector(MediaType)
*/
@Override
public StreamConnector getStreamConnector(MediaType mediaType)
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountID.java b/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountID.java
index e1719d0..788231c 100755
--- a/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountID.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JabberAccountID.java
@@ -14,6 +14,7 @@ import net.java.sip.communicator.service.protocol.*;
* The Jabber implementation of a sip-communicator AccountID
*
* @author Damian Minkov
+ * @author Sebastien Vincent
*/
public class JabberAccountID
extends AccountID
@@ -94,7 +95,7 @@ public class JabberAccountID
}
/**
- * Determines whether this account's provider use the default STUN server
+ * Determines whether this account's provider uses the default STUN server
* provided by SIP Communicator if there is no other STUN/TURN server
* discovered/configured.
*
@@ -108,4 +109,64 @@ public class JabberAccountID
ProtocolProviderFactory.USE_DEFAULT_STUN_SERVER,
true);
}
+
+ /**
+ * Returns the list of JingleNodes trackers/relays that this account is
+ * currently configured to use.
+ *
+ * @return the list of JingleNodes trackers/relays that this account is
+ * currently configured to use.
+ */
+ public List<JingleNodeDescriptor> getJingleNodes()
+ {
+ Map<String, String> accountProperties = getAccountProperties();
+ List<JingleNodeDescriptor> serList
+ = new ArrayList<JingleNodeDescriptor>();
+
+ for (int i = 0; i < JingleNodeDescriptor.MAX_JN_RELAY_COUNT; i ++)
+ {
+ JingleNodeDescriptor node
+ = JingleNodeDescriptor.loadDescriptor(
+ accountProperties,
+ JingleNodeDescriptor.JN_PREFIX + i);
+
+ // If we don't find a relay server with the given index, it means
+ // that there're no more servers left in the table so we've nothing
+ // more to do here.
+ if (node == null)
+ break;
+
+ serList.add(node);
+ }
+
+ return serList;
+ }
+
+ /**
+ * Determines whether this account's provider is supposed to auto discover
+ * JingleNodes relay.
+ *
+ * @return <tt>true</tt> if this provider would need to discover JingleNodes
+ * relay, <tt>false</tt> otherwise
+ */
+ public boolean isJingleNodesAutoDiscoveryEnabled()
+ {
+ return getAccountPropertyBoolean(
+ ProtocolProviderFactory.AUTO_DISCOVER_JINGLE_NODES,
+ false);
+ }
+
+ /**
+ * Determines whether this account's provider uses JingleNodes relay (if
+ * available).
+ *
+ * @return <tt>true</tt> if this provider would use JingleNodes relay (if
+ * available), <tt>false</tt> otherwise
+ */
+ public boolean isJingleNodesRelayEnabled()
+ {
+ return getAccountPropertyBoolean(
+ ProtocolProviderFactory.IS_USE_JINGLE_NODES,
+ false);
+ }
}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java
new file mode 100644
index 0000000..6d439e9
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidate.java
@@ -0,0 +1,117 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.lang.reflect.*;
+import java.net.*;
+
+import org.ice4j.*;
+import org.ice4j.ice.*;
+import org.ice4j.socket.*;
+
+/**
+ * Represents a <tt>Candidate</tt> obtained via Jingle Nodes.
+ *
+ * @author Sebastien Vincent
+ */
+public class JingleNodesCandidate
+ extends LocalCandidate
+{
+ /**
+ * The socket used to communicate with relay.
+ */
+ private DatagramSocket socket = null;
+
+ /**
+ * The <tt>RelayedCandidateDatagramSocket</tt> of this
+ * <tt>JingleNodesCandidate</tt>.
+ */
+ private JingleNodesCandidateDatagramSocket
+ jingleNodesCandidateDatagramSocket = null;
+
+ /**
+ * <tt>TransportAddress</tt> of the Jingle Nodes relay where we will send
+ * our packet.
+ */
+ private TransportAddress localEndPoint = null;
+
+ /**
+ * Creates a <tt>JingleNodesRelayedCandidate</tt> for the specified
+ * transport, address, and base.
+ *
+ * @param transportAddress the transport address that this candidate is
+ * encapsulating.
+ * @param parentComponent the <tt>Component</tt> that this candidate
+ * belongs to.
+ * @param localEndPoint <tt>TransportAddress</tt> of the Jingle Nodes relay
+ * where we will send our packet.
+ */
+ public JingleNodesCandidate(TransportAddress transportAddress,
+ Component parentComponent, TransportAddress localEndPoint)
+ {
+ super(transportAddress, parentComponent,
+ CandidateType.RELAYED_CANDIDATE);
+ setBase(this);
+ setRelayServerAddress(localEndPoint);
+ this.localEndPoint = localEndPoint;
+ }
+
+ /**
+ * Gets the <tt>JingleNodesCandidateDatagramSocket</tt> of this
+ * <tt>JingleNodesCandidate</tt>.
+ * <p>
+ * <b>Note</b>: The method is part of the internal API of
+ * <tt>RelayedCandidate</tt> and <tt>TurnCandidateHarvest</tt> and is not
+ * intended for public use.
+ * </p>
+ *
+ * @return the <tt>RelayedCandidateDatagramSocket</tt> of this
+ * <tt>RelayedCandidate</tt>
+ */
+ public synchronized JingleNodesCandidateDatagramSocket
+ getRelayedCandidateDatagramSocket()
+ {
+ if (jingleNodesCandidateDatagramSocket == null)
+ {
+ try
+ {
+ jingleNodesCandidateDatagramSocket
+ = new JingleNodesCandidateDatagramSocket(
+ this, localEndPoint);
+ }
+ catch (SocketException sex)
+ {
+ throw new UndeclaredThrowableException(sex);
+ }
+ }
+ return jingleNodesCandidateDatagramSocket;
+ }
+
+ /**
+ * Gets the <tt>DatagramSocket</tt> associated with this <tt>Candidate</tt>.
+ *
+ * @return the <tt>DatagramSocket</tt> associated with this
+ * <tt>Candidate</tt>
+ */
+ public DatagramSocket getSocket()
+ {
+ if (socket == null)
+ {
+ try
+ {
+ socket
+ = new MultiplexingDatagramSocket(
+ getRelayedCandidateDatagramSocket());
+ }
+ catch (SocketException sex)
+ {
+ throw new UndeclaredThrowableException(sex);
+ }
+ }
+ return socket;
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidateDatagramSocket.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidateDatagramSocket.java
new file mode 100644
index 0000000..45579b4
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesCandidateDatagramSocket.java
@@ -0,0 +1,136 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.io.IOException;
+import java.net.*;
+
+import org.ice4j.*;
+
+/**
+ * Represents an application-purposed (as opposed to an ICE-specific)
+ * <tt>DatagramSocket</tt> for a <tt>JingleNodesCandidate</tt>.
+ *
+ * @author Sebastien Vincent
+ */
+public class JingleNodesCandidateDatagramSocket extends DatagramSocket
+{
+ /**
+ * <tt>TransportAddress</tt> of the Jingle Nodes relay where we will send
+ * our packet.
+ */
+ private TransportAddress localEndPoint = null;
+
+ /**
+ * The <tt>JingleNodesCandidate</tt>.
+ */
+ private JingleNodesCandidate jingleNodesCandidate;
+
+ /**
+ * Initializes a new <tt>JingleNodesdCandidateDatagramSocket</tt> instance
+ * which is to be the <tt>socket</tt> of a specific
+ * <tt>JingleNodesCandidate</tt>.
+ *
+ * @param jingleNodesCandidate the <tt>JingleNodesCandidate</tt> which is to
+ * use the new instance as the value of its <tt>socket</tt> property
+ * @param localEndPoint <tt>TransportAddress</tt> of the Jingle Nodes relay
+ * where we will send our packet.
+ * @throws SocketException if anything goes wrong while initializing the new
+ * <tt>JingleNodesCandidateDatagramSocket</tt> instance
+ */
+ public JingleNodesCandidateDatagramSocket(
+ JingleNodesCandidate jingleNodesCandidate,
+ TransportAddress localEndPoint)
+ throws SocketException
+ {
+ super(/* bindaddr */ (SocketAddress) null);
+ this.jingleNodesCandidate = jingleNodesCandidate;
+ this.localEndPoint = localEndPoint;
+ }
+
+ /**
+ * Sends a datagram packet from this socket. The <tt>DatagramPacket</tt>
+ * includes information indicating the data to be sent, its length, the IP
+ * address of the remote host, and the port number on the remote host.
+ *
+ * @param p the <tt>DatagramPacket</tt> to be sent
+ * @throws IOException if an I/O error occurs
+ * @see DatagramSocket#send(DatagramPacket)
+ */
+ @Override
+ public void send(DatagramPacket p)
+ throws IOException
+ {
+ byte data[] = p.getData();
+ int dataLen = p.getLength();
+ int dataOffset = p.getOffset();
+
+ /* send to Jingle Nodes relay address on local port */
+ DatagramPacket packet = new DatagramPacket(data, dataOffset, dataLen,
+ new InetSocketAddress(localEndPoint.getAddress(),
+ localEndPoint.getPort()));
+
+ //XXX reuse an existing DatagramPacket ?
+ super.send(packet);
+ }
+
+ /**
+ * Gets the local address to which the socket is bound.
+ * <tt>JingleNodesCandidateDatagramSocket</tt> returns the <tt>address</tt>
+ * of its <tt>localSocketAddress</tt>.
+ * <p>
+ * If there is a security manager, its <tt>checkConnect</tt> method is first
+ * called with the host address and <tt>-1</tt> as its arguments to see if
+ * the operation is allowed.
+ * </p>
+ *
+ * @return the local address to which the socket is bound, or an
+ * <tt>InetAddress</tt> representing any local address if either the socket
+ * is not bound, or the security manager <tt>checkConnect</tt> method does
+ * not allow the operation
+ * @see #getLocalSocketAddress()
+ * @see DatagramSocket#getLocalAddress()
+ */
+ @Override
+ public InetAddress getLocalAddress()
+ {
+ return getLocalSocketAddress().getAddress();
+ }
+
+ /**
+ * Returns the port number on the local host to which this socket is bound.
+ * <tt>JingleNodesCandidateDatagramSocket</tt> returns the <tt>port</tt> of
+ * its <tt>localSocketAddress</tt>.
+ *
+ * @return the port number on the local host to which this socket is bound
+ * @see #getLocalSocketAddress()
+ * @see DatagramSocket#getLocalPort()
+ */
+ @Override
+ public int getLocalPort()
+ {
+ return getLocalSocketAddress().getPort();
+ }
+
+ /**
+ * Returns the address of the endpoint this socket is bound to, or
+ * <tt>null</tt> if it is not bound yet. Since
+ * <tt>JingleNodesCandidateDatagramSocket</tt> represents an
+ * application-purposed <tt>DatagramSocket</tt> relaying data to and from a
+ * Jingle Nodes relay, the <tt>localSocketAddress</tt> is the
+ * <tt>transportAddress</tt> of respective <tt>JingleNodesCandidate</tt>.
+ *
+ * @return a <tt>SocketAddress</tt> representing the local endpoint of this
+ * socket, or <tt>null</tt> if it is not bound yet
+ * @see DatagramSocket#getLocalSocketAddress()
+ */
+ @Override
+ public InetSocketAddress getLocalSocketAddress()
+ {
+ return jingleNodesCandidate.getTransportAddress();
+ }
+}
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java
new file mode 100644
index 0000000..e264395
--- /dev/null
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/JingleNodesHarvester.java
@@ -0,0 +1,181 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.impl.protocol.jabber;
+
+import java.net.DatagramSocket;
+import java.util.*;
+
+import org.ice4j.*;
+import org.ice4j.ice.*;
+import org.ice4j.ice.harvest.*;
+
+import org.jivesoftware.smack.*;
+import org.xmpp.jnodes.smack.*;
+
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements a <tt>CandidateHarvester</tt> which gathers <tt>Candidate</tt>s
+ * for a specified {@link Component} using Jingle Nodes as defined in
+ * XEP 278 "Jingle Relay Nodes".
+ *
+ * @author Sebastien Vincent
+ */
+public class JingleNodesHarvester
+ implements CandidateHarvester
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>JingleNodesHarvester</tt> class and
+ * its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(JingleNodesHarvester.class.getName());
+
+ /**
+ * XMPP connection.
+ */
+ private SmackServiceNode serviceNode = null;
+
+ /**
+ * JingleNodes relay allocate two address/port couple for us. Due to the
+ * architecture of Ice4j that harvest address for each component, we store
+ * the second address/port couple.
+ */
+ private TransportAddress localAddressSecond = null;
+
+ /**
+ * JingleNodes relay allocate two address/port couple for us. Due to the
+ * architecture of Ice4j that harvest address for each component, we store
+ * the second address/port couple.
+ */
+ private TransportAddress relayedAddressSecond = null;
+
+ /**
+ * Constructor.
+ *
+ * @param serviceNode the <tt>SmackServiceNode</tt>
+ */
+ public JingleNodesHarvester(SmackServiceNode serviceNode)
+ {
+ this.serviceNode = serviceNode;
+ }
+
+ /**
+ * Gathers Jingle Nodes candidates for all host <tt>Candidate</tt>s that are
+ * already present in the specified <tt>component</tt>. This method relies
+ * on the specified <tt>component</tt> to already contain all its host
+ * candidates so that it would resolve them.
+ *
+ * @param component the {@link Component} that we'd like to gather candidate
+ * Jingle Nodes <tt>Candidate</tt>s for
+ * @return the <tt>LocalCandidate</tt>s gathered by this
+ * <tt>CandidateHarvester</tt>
+ */
+ public synchronized Collection<LocalCandidate> harvest(Component component)
+ {
+ logger.info("harvest Jingle Nodes");
+
+ Collection<LocalCandidate> candidates = new HashSet<LocalCandidate>();
+ String ip = null;
+ int port = -1;
+
+ /* if we have already a candidate (RTCP) allocated, get it */
+ if(localAddressSecond != null && relayedAddressSecond != null)
+ {
+ LocalCandidate candidate = createJingleNodesCandidate(
+ relayedAddressSecond, component, localAddressSecond);
+ candidates.add(candidate);
+ component.addLocalCandidate(candidate);
+
+ localAddressSecond = null;
+ relayedAddressSecond = null;
+ return candidates;
+ }
+
+ XMPPConnection conn = serviceNode.getConnection();
+ JingleChannelIQ ciq = null;
+
+ if (serviceNode != null)
+ {
+ final TrackerEntry preferred = serviceNode.getPreferedRelay();
+
+ if (preferred != null)
+ {
+ ciq = SmackServiceNode.getChannel(conn, preferred.getJid());
+ }
+ }
+
+ if (ciq != null && ciq.getRemoteport() > 0)
+ {
+ ip = ciq.getHost();
+ port = ciq.getRemoteport();
+
+ if(logger.isInfoEnabled())
+ {
+ logger.info("JN relay: " + ip + " remote port:" + port +
+ " local port: " + ciq.getLocalport());
+ }
+
+ /* RTP */
+ TransportAddress relayedAddress = new TransportAddress(ip, port,
+ Transport.UDP);
+ TransportAddress localAddress = new TransportAddress(ip,
+ ciq.getLocalport(), Transport.UDP);
+
+ LocalCandidate local = createJingleNodesCandidate(
+ relayedAddress, component, localAddress);
+
+ /* RTCP */
+ relayedAddressSecond
+ = new TransportAddress(ip, port + 1,Transport.UDP);
+ localAddressSecond
+ = new TransportAddress(ip, ciq.getLocalport() + 1,
+ Transport.UDP);
+
+ candidates.add(local);
+ component.addLocalCandidate(local);
+ }
+ return candidates;
+ }
+
+ /**
+ * Creates a new <tt>JingleNodesRelayedCandidate</tt> instance which is to
+ * represent a specific <tt>TransportAddress</tt>.
+ *
+ * @param transportAddress the <tt>TransportAddress</tt> allocated by the
+ * relay
+ * @param component the <tt>Component</tt> for which the candidate will be
+ * added
+ * @param localEndPoint <tt>TransportAddress</tt> of the Jingle Nodes relay
+ * where we will send our packet.
+ * @return a new <tt>JingleNodesRelayedCandidate</tt> instance which
+ * represents the specified <tt>TransportAddress</tt>
+ */
+ protected JingleNodesCandidate createJingleNodesCandidate(
+ TransportAddress transportAddress, Component component,
+ TransportAddress localEndPoint)
+ {
+ JingleNodesCandidate cand = null;
+
+ try
+ {
+ cand = new JingleNodesCandidate(transportAddress,
+ component,
+ localEndPoint);
+ DatagramSocket stunSocket = cand.getStunSocket(null);
+ cand.getStunStack().addSocket(stunSocket);
+ }
+ catch(Throwable e)
+ {
+ logger.debug(
+ "Exception occurred when creating JingleNodesCandidate: " +
+ e);
+ }
+
+ return cand;
+ }
+} \ No newline at end of file
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java
index ea0e28a..c397a57 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderFactoryJabberImpl.java
@@ -20,6 +20,10 @@ import org.osgi.framework.*;
public class ProtocolProviderFactoryJabberImpl
extends ProtocolProviderFactory
{
+ /**
+ * Indicates if ICE should be used.
+ */
+ public static final String IS_USE_JINGLE_NODES = "JINGLE_NODES_ENABLED";
/**
* Creates an instance of the ProtocolProviderFactoryJabberImpl.
@@ -47,13 +51,15 @@ public class ProtocolProviderFactoryJabberImpl
BundleContext context
= JabberActivator.getBundleContext();
if (context == null)
- throw new NullPointerException("The specified BundleContext was null");
+ throw new NullPointerException(
+ "The specified BundleContext was null");
if (userIDStr == null)
throw new NullPointerException("The specified AccountID was null");
if (accountProperties == null)
- throw new NullPointerException("The specified property map was null");
+ throw new NullPointerException(
+ "The specified property map was null");
accountProperties.put(USER_ID, userIDStr);
@@ -89,7 +95,15 @@ public class ProtocolProviderFactoryJabberImpl
return accountID;
}
- protected AccountID createAccountID(String userID, Map<String, String> accountProperties)
+ /**
+ * Create an account.
+ *
+ * @param userID the user ID
+ * @param accountProperties the properties associated with the user ID
+ * @return new <tt>AccountID</tt>
+ */
+ protected AccountID createAccountID(String userID,
+ Map<String, String> accountProperties)
{
return new JabberAccountID(userID, accountProperties);
}
@@ -104,6 +118,13 @@ public class ProtocolProviderFactoryJabberImpl
return service;
}
+ /**
+ * Modify an existing account.
+ *
+ * @param protocolProvider the <tt>ProtocolProviderService</tt> responsible
+ * of the account
+ * @param accountProperties modified properties to be set
+ */
@Override
public void modifyAccount( ProtocolProviderService protocolProvider,
Map<String, String> accountProperties)
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
index a2bdae5..a41df7f 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/ProtocolProviderServiceJabberImpl.java
@@ -31,6 +31,7 @@ import org.jivesoftware.smackx.*;
import org.jivesoftware.smackx.packet.*;
import org.osgi.framework.*;
+import org.xmpp.jnodes.smack.*;
/**
* An implementation of the protocol provider service over the Jabber protocol
@@ -92,6 +93,12 @@ public class ProtocolProviderServiceJabberImpl
= IceUdpTransportPacketExtension.NAMESPACE;
/**
+ * Jingle's Discovery Info URN for Jingle Nodes support.
+ */
+ public static final String URN_XMPP_JINGLE_NODES
+ = "http://jabber.org/protocol/jinglenodes";
+
+ /**
* Jingle's Discover Info URN for "XEP-0251: Jingle Session Transfer"
* support.
*/
@@ -237,6 +244,16 @@ public class ProtocolProviderServiceJabberImpl
private SmackPacketDebugger debugger = null;
/**
+ * Jingle Nodes service.
+ */
+ private SmackServiceNode jingleNodesServiceNode = null;
+
+ /**
+ * Synchronization object to monitore jingle nodes auto discovery.
+ */
+ private final Object jingleNodesSyncRoot = new Object();
+
+ /**
* Returns the state of the registration of this protocol provider
* @return the <tt>RegistrationState</tt> that this provider is
* currently in or null in case it is in a unknown state.
@@ -823,6 +840,8 @@ public class ProtocolProviderServiceJabberImpl
logger.error("Failed to publish presence status");
}
+ startJingleNodesDiscovery();
+
return ConnectState.STOP_TRYING;
}
else
@@ -1184,6 +1203,17 @@ public class ProtocolProviderServiceJabberImpl
supportedFeatures.add(URN_XMPP_JINGLE_RTP_VIDEO);
supportedFeatures.add(URN_XMPP_JINGLE_RTP_ZRTP);
+ /*
+ * Reflect the preference of the user with respect to the use of
+ * Jingle Nodes.
+ */
+ if (accountID.getAccountPropertyBoolean(
+ ProtocolProviderFactoryJabberImpl.IS_USE_JINGLE_NODES,
+ false))
+ {
+ supportedFeatures.add(URN_XMPP_JINGLE_NODES);
+ }
+
/* add extension to support remote control */
supportedFeatures.add(InputEvtIQ.NAMESPACE);
@@ -1758,6 +1788,71 @@ public class ProtocolProviderServiceJabberImpl
}
/**
+ * Start auto-discovery of JingleNodes tracker/relays.
+ */
+ public void startJingleNodesDiscovery()
+ {
+ // Jingle Nodes Service Initialization
+ JabberAccountID accID = (JabberAccountID)getAccountID();
+ jingleNodesServiceNode = new SmackServiceNode(connection, 60000);
+
+ for(JingleNodeDescriptor desc : accID.getJingleNodes())
+ {
+ TrackerEntry entry = new TrackerEntry(
+ desc.isRelaySupported() ? TrackerEntry.Type.relay :
+ TrackerEntry.Type.tracker,
+ TrackerEntry.Policy._public,
+ desc.getJID(),
+ JingleChannelIQ.UDP);
+
+ jingleNodesServiceNode.addTrackerEntry(entry);
+ }
+
+ final SmackServiceNode service = jingleNodesServiceNode;
+ final boolean autoDiscover = accID.isJingleNodesAutoDiscoveryEnabled();
+
+ new Thread()
+ {
+ public void run()
+ {
+ synchronized(jingleNodesSyncRoot)
+ {
+ if(logger.isInfoEnabled())
+ {
+ logger.info("Start Jingle Nodes discovery!");
+ }
+
+ final SmackServiceNode.MappedNodes nodes =
+ service.searchServices(
+ connection, 6, 3, 20, JingleChannelIQ.UDP,
+ autoDiscover);
+
+ if(logger.isInfoEnabled())
+ {
+ logger.info("Jingle Nodes discovery terminated!");
+ }
+
+ service.addEntries(nodes);
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Get the Jingle Nodes service. Note that this method will block until
+ * Jingle Nodes auto discovery (if enabled) finished.
+ *
+ * @return Jingle Nodes service
+ */
+ public SmackServiceNode getJingleNodesServiceNode()
+ {
+ synchronized(jingleNodesSyncRoot)
+ {
+ return jingleNodesServiceNode;
+ }
+ }
+
+ /**
* Logs a specific message and associated <tt>Throwable</tt> cause as an
* error using the current <tt>Logger</tt> and then throws a new
* <tt>OperationFailedException</tt> with the message, a specific error code
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
index 1e8cf9a..35fc3bb 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/RawUdpTransportManager.java
@@ -194,8 +194,9 @@ public class RawUdpTransportManager
* Starts transport candidate harvest. This method should complete rapidly
* and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
* are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the {@link #wrapupHarvest()}
- * method which would be called once we absolutely need the candidates.
+ * harvest would then need to be concluded in the
+ * {@link #wrapupCandidateHarvest()} method which would be called once we
+ * absolutely need the candidates.
*
* @param ourOffer the content list that should tell us how many stream
* connectors we actually need.
@@ -232,8 +233,9 @@ public class RawUdpTransportManager
* Starts transport candidate harvest. This method should complete rapidly
* and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
* are necessary, they should be executed in a separate thread. Candidate
- * harvest would then need to be concluded in the {@link #wrapupHarvest()}
- * method which would be called once we absolutely need the candidates.
+ * harvest would then need to be concluded in the
+ * {@link #wrapupCandidateHarvest()} method which would be called once we
+ * absolutely need the candidates.
*
* @param theirOffer a media description offer that we've received from the
* remote party and that we should use in case we need to know what
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
index 20bd54b..a48b464 100755
--- a/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/jabber.provider.manifest.mf
@@ -6,6 +6,8 @@ Bundle-Version: 0.0.1
System-Bundle: yes
Import-Package: org.osgi.framework,
org.ice4j,
+ org.ice4j.socket,
+ org.ice4j.stack,
org.ice4j.ice,
org.ice4j.ice.harvest,
org.ice4j.security,
@@ -51,4 +53,7 @@ Import-Package: org.osgi.framework,
javax.xml.parsers,
javax.net.ssl,
javax.security.sasl,
- javax.security.auth.callback
+ javax.security.auth.callback,
+ org.xmpp.jnodes,
+ org.xmpp.jnodes.smack,
+ org.xmpp.jnodes.nio \ No newline at end of file
diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java
index d905923..01d6f2e 100644
--- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java
+++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/FirstWizardPage.java
@@ -27,6 +27,11 @@ public class FirstWizardPage
extends TransparentPanel
implements WizardPage
{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
public static final String FIRST_PAGE_IDENTIFIER = "FirstPageIdentifier";
private final AccountPanel accountPanel;
@@ -180,6 +185,20 @@ public class FirstWizardPage
for (StunServerDescriptor descriptor : stunServers)
registration.addStunServer(descriptor);
+ registration.setUseJingleNodes(iceConfigPanel.isUseJingleNodes());
+ registration.setAutoDiscoverJingleNodes(
+ iceConfigPanel.isAutoDiscoverJingleNodes());
+
+ //we will be reentering all Jingle nodes so let's make sure we clear
+ //the servers vector in case we already did that with a "Next".
+ registration.getAdditionalJingleNodes().clear();
+
+ List<JingleNodeDescriptor> jingleNodes
+ = iceConfigPanel.getAdditionalJingleNodes();
+
+ for (JingleNodeDescriptor descriptor : jingleNodes)
+ registration.addJingleNodes(descriptor);
+
nextPageIdentifier = SUMMARY_PAGE_IDENTIFIER;
this.isCommitted = true;
@@ -302,10 +321,8 @@ public class FirstWizardPage
= StunServerDescriptor.loadDescriptor(
accountProperties, ProtocolProviderFactory.STUN_PREFIX + i);
-
-
// If we don't find a stun server with the given index, it means
- // that there're no more servers left i nthe table so we've nothing
+ // that there're no more servers left in the table so we've nothing
// more to do here.
if (stunServer == null)
break;
@@ -313,6 +330,38 @@ public class FirstWizardPage
iceConfigPanel.addStunServer(stunServer);
}
+ String useJN =
+ accountProperties.get(ProtocolProviderFactory.IS_USE_JINGLE_NODES);
+ boolean isUseJN = Boolean.parseBoolean(
+ (useJN != null && useJN.length() != 0) ? useJN : "false");
+
+ iceConfigPanel.setUseJingleNodes(isUseJN);
+
+ String useAutoDiscoverJN
+ = accountProperties.get(
+ ProtocolProviderFactory.AUTO_DISCOVER_JINGLE_NODES);
+ boolean isUseAutoDiscoverJN = Boolean.parseBoolean(
+ (useAutoDiscoverJN != null &&
+ useAutoDiscoverJN.length() != 0) ?
+ useAutoDiscoverJN : "false");
+
+ iceConfigPanel.setAutoDiscoverJingleNodes(isUseAutoDiscoverJN);
+
+ for (int i = 0; i < JingleNodeDescriptor.MAX_JN_RELAY_COUNT ; i ++)
+ {
+ JingleNodeDescriptor jn
+ = JingleNodeDescriptor.loadDescriptor(
+ accountProperties, JingleNodeDescriptor.JN_PREFIX + i);
+
+ // If we don't find a stun server with the given index, it means
+ // that there're no more servers left in the table so we've nothing
+ // more to do here.
+ if (jn == null)
+ break;
+
+ iceConfigPanel.addJingleNodes(jn);
+ }
+
this.isServerOverridden
= accountID.getAccountPropertyBoolean(
ProtocolProviderFactory.IS_SERVER_OVERRIDDEN,
diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java
index d238766..7b8c64b 100644
--- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java
+++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/IceConfigPanel.java
@@ -54,7 +54,7 @@ public class IceConfigPanel
/**
* The table model for our additional stun servers table.
*/
- private final StunServerTableModel tableModel = new StunServerTableModel();
+ private final ServerTableModel tableModel = new ServerTableModel();
/**
* The stun server table.
@@ -62,6 +62,30 @@ public class IceConfigPanel
private final JTable table = new JTable(tableModel);
/**
+ * The check box allowing the user to choose to use JingleNodes.
+ */
+ private final JCheckBox jnBox = new SIPCommCheckBox(
+ Resources.getString("plugin.jabberaccregwizz.USE_JINGLE_NODES"));
+
+ /**
+ * The check box allowing the user to choose to automatically discover
+ * JingleNodes relays.
+ */
+ private final JCheckBox jnAutoDiscoverBox = new SIPCommCheckBox(
+ Resources.getString("plugin.jabberaccregwizz.AUTO_DISCOVER_JN"));
+
+ /**
+ * The table model for our additional stun servers table.
+ */
+ private final ServerTableModel jnTableModel =
+ new ServerTableModel();
+
+ /**
+ * The JingleNodes server table.
+ */
+ private final JTable jnTable = new JTable(jnTableModel);
+
+ /**
* Creates an instance of <tt>IceConfigPanel</tt>.
*/
public IceConfigPanel()
@@ -80,6 +104,9 @@ public class IceConfigPanel
autoDiscoverBox.setSelected(true);
defaultStunBox.setSelected(true);
+ //jnBox.setSelected(true);
+ //jnAutoDiscoverBox.setSelected(true);
+
JPanel checkBoxPanel = new TransparentPanel(new GridLayout(0, 1));
checkBoxPanel.add(iceBox);
checkBoxPanel.add(autoDiscoverBox);
@@ -88,6 +115,14 @@ public class IceConfigPanel
add(checkBoxPanel);
add(Box.createVerticalStrut(10));
add(createAdditionalServersComponent());
+
+ checkBoxPanel = new TransparentPanel(new GridLayout(0, 1));
+ checkBoxPanel.add(jnBox);
+ checkBoxPanel.add(jnAutoDiscoverBox);
+
+ add(checkBoxPanel);
+ add(Box.createVerticalStrut(10));
+ add(createAdditionalJingleNodesComponent());
}
/**
@@ -106,7 +141,7 @@ public class IceConfigPanel
Resources.getString("plugin.jabberaccregwizz.SUPPORT_TURN"));
table.setDefaultRenderer( StunServerDescriptor.class,
- new StunServerCellRenderer());
+ new ServerCellRenderer());
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
@@ -480,7 +515,7 @@ public class IceConfigPanel
* A custom cell renderer used in the cell containing the
* <tt>StunServer</tt> instance.
*/
- private static class StunServerCellRenderer
+ private static class ServerCellRenderer
extends DefaultTableCellRenderer
{
/**
@@ -564,6 +599,27 @@ public class IceConfigPanel
: table.getBackground());
}
}
+ else if(value instanceof JingleNodeDescriptor)
+ {
+ JingleNodeDescriptor jn = (JingleNodeDescriptor) value;
+
+ this.setText(jn.getJID());
+
+ if (isSelected)
+ {
+ super.setForeground(table.getSelectionForeground());
+ super.setBackground(table.getSelectionBackground());
+ }
+ else
+ {
+ super.setForeground((unselectedForeground != null)
+ ? unselectedForeground
+ : table.getForeground());
+ super.setBackground((unselectedBackground != null)
+ ? unselectedBackground
+ : table.getBackground());
+ }
+ }
else
return super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
@@ -576,7 +632,7 @@ public class IceConfigPanel
* A custom table model, with a non editable cells and a custom class column
* objects.
*/
- private class StunServerTableModel
+ private class ServerTableModel
extends DefaultTableModel
{
/**
@@ -616,7 +672,7 @@ public class IceConfigPanel
* @return <tt>true</tt> if ICE should be used for this account, otherwise
* returns <tt>false</tt>
*/
- boolean isUseIce()
+ protected boolean isUseIce()
{
return iceBox.isSelected();
}
@@ -626,7 +682,7 @@ public class IceConfigPanel
* @param isUseIce <tt>true</tt> to indicate that ICE should be used for
* this account, <tt>false</tt> - otherwise.
*/
- void setUseIce(boolean isUseIce)
+ protected void setUseIce(boolean isUseIce)
{
iceBox.setSelected(isUseIce);
}
@@ -636,7 +692,7 @@ public class IceConfigPanel
* @return <tt>true</tt> if the stun server should be automatically
* discovered, otherwise returns <tt>false</tt>.
*/
- boolean isAutoDiscoverStun()
+ protected boolean isAutoDiscoverStun()
{
return autoDiscoverBox.isSelected();
}
@@ -646,7 +702,7 @@ public class IceConfigPanel
* @param isAutoDiscover <tt>true</tt> to indicate that stun server should
* be auto-discovered, <tt>false</tt> - otherwise.
*/
- void setAutoDiscoverStun(boolean isAutoDiscover)
+ protected void setAutoDiscoverStun(boolean isAutoDiscover)
{
autoDiscoverBox.setSelected(isAutoDiscover);
}
@@ -656,7 +712,7 @@ public class IceConfigPanel
* @return <tt>true</tt> if the default stun server should be used,
* otherwise returns <tt>false</tt>.
*/
- boolean isUseDefaultStunServer()
+ protected boolean isUseDefaultStunServer()
{
return defaultStunBox.isSelected();
}
@@ -666,7 +722,7 @@ public class IceConfigPanel
* @param isDefaultStun <tt>true</tt> to indicate that the default stun
* server should be used, <tt>false</tt> otherwise.
*/
- void setUseDefaultStunServer(boolean isDefaultStun)
+ protected void setUseDefaultStunServer(boolean isDefaultStun)
{
defaultStunBox.setSelected(isDefaultStun);
}
@@ -676,8 +732,8 @@ public class IceConfigPanel
*
* @return the list of additional stun servers entered by the user
*/
- @SuppressWarnings("unchecked")//getDataVector() is simply not parametrized
- List<StunServerDescriptor> getAdditionalStunServers()
+ @SuppressWarnings("unchecked")//getDataVector() is simply not parameterized
+ protected List<StunServerDescriptor> getAdditionalStunServers()
{
LinkedList<StunServerDescriptor> serversList
= new LinkedList<StunServerDescriptor>();
@@ -696,7 +752,7 @@ public class IceConfigPanel
* servers.
* @param stunServer the stun server to add
*/
- void addStunServer(StunServerDescriptor stunServer)
+ protected void addStunServer(StunServerDescriptor stunServer)
{
tableModel.addRow(new Object[]{stunServer,
stunServer.isTurnSupported()});
@@ -707,7 +763,7 @@ public class IceConfigPanel
*
* @param stunServer the stun server to modify
*/
- void modifyStunServer(StunServerDescriptor stunServer)
+ protected void modifyStunServer(StunServerDescriptor stunServer)
{
for (int i = 0; i < tableModel.getRowCount(); i++)
{
@@ -734,7 +790,7 @@ public class IceConfigPanel
* <tt>address</tt> and <tt>port</tt> already exists in the table, otherwise
* returns <tt>null</tt>
*/
- StunServerDescriptor getStunServer(String address, int port)
+ protected StunServerDescriptor getStunServer(String address, int port)
{
for (int i = 0; i < tableModel.getRowCount(); i++)
{
@@ -791,4 +847,405 @@ public class IceConfigPanel
return NetworkUtils.isValidPortNumber(port);
}
}
+
+ /**
+ * Creates the list of additional JingleNodes that are added by the user.
+ *
+ * @return the created component
+ */
+ private Component createAdditionalJingleNodesComponent()
+ {
+ jnTable.setPreferredScrollableViewportSize(new Dimension(450, 60));
+
+ jnTableModel.addColumn(
+ Resources.getString("plugin.jabberaccregwizz.JID_ADDRESS"));
+ jnTableModel.addColumn(
+ Resources.getString("plugin.jabberaccregwizz.RELAY_SUPPORT"));
+
+ jnTable.setDefaultRenderer(JingleNodeDescriptor.class,
+ new ServerCellRenderer());
+
+ //Create the scroll pane and add the table to it.
+ JScrollPane scrollPane = new JScrollPane(jnTable);
+
+ JButton addButton
+ = new JButton(Resources.getString("service.gui.ADD"));
+ addButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ JNConfigDialog jnDialog = new JNConfigDialog(false);
+
+ jnDialog.setVisible(true);
+ }
+ });
+
+ JButton editButton
+ = new JButton(Resources.getString("service.gui.EDIT"));
+ editButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ if(jnTable.getSelectedRow() < 0)
+ return;
+
+ JingleNodeDescriptor jn
+ = (JingleNodeDescriptor) jnTableModel.getValueAt(
+ jnTable.getSelectedRow(), 0);
+
+ if (jn != null)
+ {
+ JNConfigDialog dialog = new JNConfigDialog(
+ jn.getJID(), jn.isRelaySupported());
+
+ dialog.setVisible(true);
+ }
+ }
+ });
+
+ JButton deleteButton
+ = new JButton(Resources.getString("service.gui.DELETE"));
+ deleteButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ jnTableModel.removeRow(jnTable.getSelectedRow());
+ }
+ });
+
+ TransparentPanel buttonsPanel
+ = new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+
+ buttonsPanel.add(addButton);
+ buttonsPanel.add(editButton);
+ buttonsPanel.add(deleteButton);
+
+ TransparentPanel mainPanel = new TransparentPanel(new BorderLayout());
+ mainPanel.setBorder(BorderFactory.createTitledBorder(
+ Resources.getString(
+ "plugin.jabberaccregwizz.ADDITIONAL_JINGLE_NODES")));
+ mainPanel.add(scrollPane);
+ mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
+
+ return mainPanel;
+ }
+
+ /**
+ * The JingleNodes configuration window.
+ */
+ private class JNConfigDialog extends SIPCommDialog
+ {
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The main panel
+ */
+ private final JPanel mainPanel
+ = new TransparentPanel(new BorderLayout());
+
+ /**
+ * The address of the stun server.
+ */
+ private final JTextField addressField = new JTextField();
+
+ /**
+ * The check box where user would indicate whether a STUN server is also
+ * a TURN server.
+ */
+ private final JCheckBox supportRelayCheckBox = new JCheckBox(
+ Resources.getString("plugin.jabberaccregwizz.RELAY_SUPPORT"));
+
+ /**
+ * The pane where we show errors.
+ */
+ private JEditorPane errorMessagePane;
+
+ /**
+ * If the dialog is open via "edit" button.
+ */
+ private final boolean isEditMode;
+
+ /**
+ * Creates a new JNConfigDialog with filled in values.
+ *
+ * @param address the IP or FQDN of the server
+ * @param isRelaySupport a <tt>boolean</tt> indicating whether the node
+ * supports relay
+ */
+ public JNConfigDialog(String address, boolean isRelaySupport)
+ {
+ this(true);
+
+ addressField.setText(address);
+ supportRelayCheckBox.setSelected(isRelaySupport);
+ }
+
+ /**
+ * Creates an empty dialog.
+ *
+ * @param editMode true if the dialog is in "edit" state, false means
+ * "add" state
+ */
+ public JNConfigDialog(boolean editMode)
+ {
+ super(false);
+
+ this.isEditMode = editMode;
+
+ setTitle(Resources.getString(
+ "plugin.jabberaccregwizz.ADD_JINGLE_NODE"));
+
+ JLabel addressLabel = new JLabel(
+ Resources.getString("plugin.jabberaccregwizz.JID_ADDRESS"));
+
+ TransparentPanel labelsPanel
+ = new TransparentPanel(new GridLayout(0, 1));
+
+ labelsPanel.add(addressLabel);
+ labelsPanel.add(new JLabel());
+
+ TransparentPanel valuesPanel
+ = new TransparentPanel(new GridLayout(0, 1));
+
+ valuesPanel.add(addressField);
+ valuesPanel.add(supportRelayCheckBox);
+
+ JButton addButton
+ = new JButton(Resources.getString(isEditMode ?
+ "service.gui.EDIT" : "service.gui.ADD"));
+ JButton cancelButton
+ = new JButton(Resources.getString("service.gui.CANCEL"));
+
+ addButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ String address = addressField.getText();
+ JingleNodeDescriptor jnServer = null;
+
+ String errorMessage = null;
+ if (address == null || address.length() <= 0)
+ errorMessage = Resources.getString(
+ "plugin.jabberaccregwizz.NO_STUN_ADDRESS");
+
+ jnServer = getJingleNodes(address);
+
+ if(jnServer != null && !isEditMode)
+ {
+ errorMessage = Resources.getString(
+ "plugin.jabberaccregwizz.STUN_ALREADY_EXIST");
+ }
+
+ if (errorMessage != null)
+ {
+ loadErrorMessage(errorMessage);
+ return;
+ }
+
+ if(!isEditMode)
+ {
+ jnServer = new JingleNodeDescriptor(
+ address, supportRelayCheckBox.isSelected());
+
+ addJingleNodes(jnServer);
+ }
+ else
+ {
+ /* edit an existing Jingle Node */
+ jnServer.setAddress(address);
+
+ jnServer.setRelay(supportRelayCheckBox.isSelected());
+ modifyJingleNodes(jnServer);
+ }
+ dispose();
+ }
+ });
+
+ cancelButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ dispose();
+ }
+ });
+
+ TransparentPanel buttonsPanel
+ = new TransparentPanel(new FlowLayout(FlowLayout.RIGHT));
+ buttonsPanel.add(addButton);
+ buttonsPanel.add(cancelButton);
+
+ mainPanel.add(labelsPanel, BorderLayout.WEST);
+ mainPanel.add(valuesPanel, BorderLayout.CENTER);
+ mainPanel.add(buttonsPanel, BorderLayout.SOUTH);
+
+ mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20,
+ 20));
+ getContentPane().add(mainPanel, BorderLayout.NORTH);
+ pack();
+ }
+
+ /**
+ * Loads the given error message in the current dialog, by re-validating
+ * the content.
+ *
+ * @param errorMessage The error message to load.
+ */
+ private void loadErrorMessage(String errorMessage)
+ {
+ if (errorMessagePane == null)
+ {
+ errorMessagePane = new JEditorPane();
+
+ errorMessagePane.setOpaque(false);
+ errorMessagePane.setForeground(Color.RED);
+
+ mainPanel.add(errorMessagePane, BorderLayout.NORTH);
+ }
+
+ errorMessagePane.setText(errorMessage);
+ mainPanel.revalidate();
+ mainPanel.repaint();
+
+ this.pack();
+
+ //WORKAROUND: there's something wrong happening in this pack and
+ //components get cluttered, partially hiding the password text field.
+ //I am under the impression that this has something to do with the
+ //message pane preferred size being ignored (or being 0) which is
+ //why I am adding it's height to the dialog. It's quite ugly so
+ //please fix if you have something better in mind.
+ this.setSize(getWidth(), getHeight() +
+ errorMessagePane.getHeight());
+ }
+
+ /**
+ * Dummy implementation that we are not using.
+ *
+ * @param escaped unused
+ */
+ @Override
+ protected void close(boolean escaped) {}
+ }
+
+ /**
+ * Indicates if Jingle Nodes should be used for this account.
+ *
+ * @return <tt>true</tt> if Jingle Nodes should be used for this account,
+ * otherwise returns <tt>false</tt>
+ */
+ protected boolean isUseJingleNodes()
+ {
+ return jnBox.isSelected();
+ }
+
+ /**
+ * Sets the <tt>useJingleNodes</tt> property.
+ *
+ * @param isUseJN <tt>true</tt> to indicate that Jingle Nodes should be
+ * used for this account, <tt>false</tt> - otherwise.
+ */
+ protected void setUseJingleNodes(boolean isUseJN)
+ {
+ jnBox.setSelected(isUseJN);
+ }
+
+ /**
+ * Indicates if the Jingle Nodes relays should be automatically discovered.
+ *
+ * @return <tt>true</tt> if the Jingle Nodes relays should be automatically
+ * discovered, otherwise returns <tt>false</tt>.
+ */
+ protected boolean isAutoDiscoverJingleNodes()
+ {
+ return jnAutoDiscoverBox.isSelected();
+ }
+
+ /**
+ * Sets the <tt>autoDiscoverJingleNodes</tt> property.
+ *
+ * @param isAutoDiscover <tt>true</tt> to indicate that Jingle Nodes relays
+ * should be auto-discovered, <tt>false</tt> - otherwise.
+ */
+ protected void setAutoDiscoverJingleNodes(boolean isAutoDiscover)
+ {
+ jnAutoDiscoverBox.setSelected(isAutoDiscover);
+ }
+
+ /**
+ * Returns the list of additional Jingle Nodes entered by the user.
+ *
+ * @return the list of additional Jingle Nodes entered by the user
+ */
+ @SuppressWarnings("unchecked")//getDataVector() is simply not parameterized
+ protected List<JingleNodeDescriptor> getAdditionalJingleNodes()
+ {
+ LinkedList<JingleNodeDescriptor> serversList
+ = new LinkedList<JingleNodeDescriptor>();
+
+ Vector<Vector<JingleNodeDescriptor>> serverRows
+ = jnTableModel.getDataVector();
+
+ for(Vector<JingleNodeDescriptor> row : serverRows)
+ serversList.add(row.elementAt(0));
+
+ return serversList;
+ }
+
+ /**
+ * Indicates if a JingleNodes with the given <tt>address</tt> already exists
+ * in the additional stun servers table.
+ *
+ * @param address the JingleNodes address to check
+ *
+ * @return <tt>JingleNodesDescriptor</tt> if a Jingle Node with the given
+ * <tt>address</tt> already exists in the table, otherwise returns
+ * <tt>null</tt>
+ */
+ protected JingleNodeDescriptor getJingleNodes(String address)
+ {
+ for (int i = 0; i < jnTableModel.getRowCount(); i++)
+ {
+ JingleNodeDescriptor jn
+ = (JingleNodeDescriptor) jnTableModel.getValueAt(i, 0);
+
+ if (jn.getJID().equalsIgnoreCase(address))
+ return jn;
+ }
+ return null;
+ }
+
+ /**
+ * Adds the given <tt>jingleNode</tt> to the list of additional JingleNodes
+ *
+ * @param jingleNode the Jingle Node server to add
+ */
+ protected void addJingleNodes(JingleNodeDescriptor jingleNode)
+ {
+ jnTableModel.addRow(new Object[]{jingleNode,
+ jingleNode.isRelaySupported()});
+ }
+
+ /**
+ * Modify the given <tt>jingleNode</tt> from the list of Jingle Nodes.
+ *
+ * @param jingleNode the Jingle Node to modify
+ */
+ protected void modifyJingleNodes(JingleNodeDescriptor jingleNode)
+ {
+ for (int i = 0; i < jnTableModel.getRowCount(); i++)
+ {
+ JingleNodeDescriptor node
+ = (JingleNodeDescriptor) jnTableModel.getValueAt(i, 0);
+
+ if(jingleNode == node)
+ {
+ jnTableModel.setValueAt(jingleNode, i, 0);
+ jnTableModel.setValueAt(jingleNode.isRelaySupported(), i, 1);
+ return;
+ }
+ }
+ }
}
diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java
index 33185b9..dcb37ce 100755
--- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java
+++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistration.java
@@ -105,6 +105,22 @@ public class JabberAccountRegistration
= new ArrayList<StunServerDescriptor>();
/**
+ * Indicates if JingleNodes relays should be used.
+ */
+ private boolean isUseJingleNodes = false;
+
+ /**
+ * Indicates if JingleNodes relay server should be automatically discovered.
+ */
+ private boolean isAutoDiscoverJingleNodes = false;
+
+ /**
+ * The list of additional JingleNodes (tracker or relay) entered by user.
+ */
+ private List<JingleNodeDescriptor> additionalJingleNodes
+ = new ArrayList<JingleNodeDescriptor>();
+
+ /**
* Returns the password of the jabber registration account.
* @return the password of the jabber registration account.
*/
@@ -363,4 +379,82 @@ public class JabberAccountRegistration
{
return additionalStunServers;
}
+
+ /**
+ * Sets the <tt>autoDiscoverJingleNodes</tt> property.
+ *
+ * @param isAutoDiscover <tt>true</tt> to indicate that relay server should
+ * be auto-discovered, <tt>false</tt> - otherwise.
+ */
+ public void setAutoDiscoverJingleNodes(boolean isAutoDiscover)
+ {
+ this.isAutoDiscoverJingleNodes = isAutoDiscover;
+ }
+
+ /**
+ * Indicates if the JingleNodes relay server should be automatically
+ * discovered.
+ *
+ * @return <tt>true</tt> if the relay server should be automatically
+ * discovered, otherwise returns <tt>false</tt>.
+ */
+ public boolean isAutoDiscoverJingleNodes()
+ {
+ return isAutoDiscoverJingleNodes;
+ }
+
+ /**
+ * Sets the <tt>useJingleNodes/tt> property.
+ *
+ * @param isUseJingleNodes <tt>true</tt> to indicate that Jingle Nodes
+ * should be used for this account, <tt>false</tt> - otherwise.
+ */
+ public void setUseJingleNodes(boolean isUseJingleNodes)
+ {
+ this.isUseJingleNodes = isUseJingleNodes;
+ }
+
+ /**
+ * Sets the <tt>useJingleNodes</tt> property.
+ *
+ * @param isUseJingleNodes <tt>true</tt> to indicate that JingleNodes relays
+ * should be used for this account, <tt>false</tt> - otherwise.
+ */
+ public void isUseJingleNodes(boolean isUseJingleNodes)
+ {
+ this.isUseJingleNodes = isUseJingleNodes;
+ }
+
+ /**
+ * Indicates if JingleNodes relay should be used.
+ *
+ * @return <tt>true</tt> if JingleNodes should be used, <tt>false</tt>
+ * otherwise
+ */
+ public boolean isUseJingleNodes()
+ {
+ return isUseJingleNodes;
+ }
+
+ /**
+ * Adds the given <tt>node</tt> to the list of additional JingleNodes.
+ *
+ * @param node the <tt>node</tt> to add
+ */
+ public void addJingleNodes(JingleNodeDescriptor node)
+ {
+ additionalJingleNodes.add(node);
+ }
+
+ /**
+ * Returns the <tt>List</tt> of all additional stun servers entered by the
+ * user. The list is guaranteed not to be <tt>null</tt>.
+ *
+ * @return the <tt>List</tt> of all additional stun servers entered by the
+ * user.
+ */
+ public List<JingleNodeDescriptor> getAdditionalJingleNodes()
+ {
+ return additionalJingleNodes;
+ }
}
diff --git a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationWizard.java b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationWizard.java
index 9eaf734..b8abbee 100644
--- a/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationWizard.java
+++ b/src/net/java/sip/communicator/plugin/jabberaccregwizz/JabberAccountRegistrationWizard.java
@@ -26,11 +26,20 @@ import org.osgi.framework.*;
public class JabberAccountRegistrationWizard
implements AccountRegistrationWizard
{
+ /**
+ * The logger.
+ */
private static final Logger logger =
Logger.getLogger(JabberAccountRegistrationWizard.class);
+ /**
+ * Account suffix for Google service.
+ */
private static final String GOOGLE_USER_SUFFIX = "gmail.com";
+ /**
+ * XMPP server for Google service.
+ */
private static final String GOOGLE_CONNECT_SRV = "talk.google.com";
private FirstWizardPage firstWizardPage;
@@ -40,8 +49,14 @@ public class JabberAccountRegistrationWizard
private final WizardContainer wizardContainer;
+ /**
+ * The <tt>ProtocolProviderService</tt> of this account.
+ */
private ProtocolProviderService protocolProvider;
+ /**
+ * If the account has been modified.
+ */
private boolean isModification;
/**
@@ -243,6 +258,7 @@ public class JabberAccountRegistrationWizard
{
serverName = getServerFromUserName(userName);
}
+
accountProperties.put(ProtocolProviderFactory.SERVER_ADDRESS,
serverName);
@@ -277,6 +293,25 @@ public class JabberAccountRegistrationWizard
ProtocolProviderFactory.STUN_PREFIX + serverIndex);
}
+ accountProperties.put(ProtocolProviderFactory.IS_USE_JINGLE_NODES,
+ String.valueOf(registration.isUseJingleNodes()));
+
+ accountProperties.put(
+ ProtocolProviderFactory.AUTO_DISCOVER_JINGLE_NODES,
+ String.valueOf(registration.isAutoDiscoverJingleNodes()));
+
+ serverIndex = -1;
+ List<JingleNodeDescriptor> jnRelays
+ = registration.getAdditionalJingleNodes();
+
+ for(JingleNodeDescriptor jnRelay : jnRelays)
+ {
+ serverIndex ++;
+
+ jnRelay.storeDescriptor(accountProperties,
+ JingleNodeDescriptor.JN_PREFIX + serverIndex);
+ }
+
if (isModification)
{
providerFactory.modifyAccount( protocolProvider,
diff --git a/src/net/java/sip/communicator/service/protocol/JingleNodeDescriptor.java b/src/net/java/sip/communicator/service/protocol/JingleNodeDescriptor.java
new file mode 100644
index 0000000..7e7d4c5
--- /dev/null
+++ b/src/net/java/sip/communicator/service/protocol/JingleNodeDescriptor.java
@@ -0,0 +1,185 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package net.java.sip.communicator.service.protocol;
+
+import java.util.*;
+
+/**
+ * A <tt>JingleNodesDescriptor</tt> stores information necessary to create a
+ * JingleNodes tracker or relay candidate harvester that we could use with
+ * ICE4J. Descriptors are normally initialized by protocol wizards. They are
+ * then used to convert the data into a {@link String} form suitable for storage
+ * in an accounts properties Map.
+ *
+ * @author Yana Stamcheva
+ * @author Emil Ivov
+ * @author Sebastien Vincent
+ */
+public class JingleNodeDescriptor
+{
+ /**
+ * JingleNodes prefix to store configuration.
+ */
+ public static final String JN_PREFIX = "JINGLENODES";
+
+ /**
+ * JingleNodes prefix to store server address in configuration.
+ */
+ public static final String JN_ADDRESS = "ADDRESS";
+
+ /**
+ * JingleNodes prefix to store the relay capabilities in configuration.
+ */
+ public static final String JN_IS_RELAY_SUPPORTED = "IS_RELAY_SUPPORTED";
+
+ /**
+ * The maximum number of stun servers that we would allow.
+ */
+ public static final int MAX_JN_RELAY_COUNT = 100;
+
+ /**
+ * The address of the JingleNodes (JID).
+ */
+ private String address;
+
+ /**
+ * If the relay is supported by this JingleNodes.
+ */
+ private boolean relaySupported;
+
+ /**
+ * Creates an instance of <tt>JingleNodes</tt> by specifying all
+ * parameters.
+ *
+ * @param address address of the JingleNodes
+ * @param relaySupported if the JingleNodes supports relay
+ */
+ public JingleNodeDescriptor(String address,
+ boolean relaySupported)
+ {
+ this.address = address;
+ this.relaySupported = relaySupported;
+ }
+
+ /**
+ * Returns the address of the JingleNodes
+ *
+ * @return the address of the JingleNodes
+ */
+ public String getJID()
+ {
+ return address;
+ }
+
+ /**
+ * Sets the address of the JingleNodes.
+ *
+ * @param address the JID of the JingleNodes
+ */
+ public void setAddress(String address)
+ {
+ this.address = address;
+ }
+
+ /**
+ * Returns if the JID has relay support.
+ *
+ * @return <tt>true</tt> if relay is supported, <tt>false</tt> otherwise
+ */
+ public boolean isRelaySupported()
+ {
+ return relaySupported;
+ }
+
+ /**
+ * Sets the relay support corresponding to this JID.
+ *
+ * @param relaySupported relay value to set
+ */
+ public void setRelay(boolean relaySupported)
+ {
+ this.relaySupported = relaySupported;
+ }
+
+ /**
+ * Stores this descriptor into the specified {@link Map}.The method is meant
+ * for use with account property maps. It also allows prepending an account
+ * prefix to all property names so that multiple descriptors can be stored
+ * in a single {@link Map}.
+ *
+ * @param props the account properties {@link Map} that we'd like to store
+ * this descriptor in.
+ * @param namePrefix the prefix that we should prepend to every property
+ * name.
+ */
+ public void storeDescriptor(Map<String, String> props, String namePrefix)
+ {
+ if(namePrefix == null)
+ namePrefix = "";
+
+ props.put(namePrefix + JN_ADDRESS, getJID());
+
+
+ props.put(namePrefix + JN_IS_RELAY_SUPPORTED,
+ Boolean.toString(isRelaySupported()));
+ }
+
+ /**
+ * Loads this descriptor from the specified {@link Map}.The method is meant
+ * for use with account property maps. It also allows prepending an account
+ * prefix to all property names so that multiple descriptors can be read
+ * in a single {@link Map}.
+ *
+ * @param props the account properties {@link Map} that we'd like to load
+ * this descriptor from.
+ * @param namePrefix the prefix that we should prepend to every property
+ * name.
+ *
+ * @return the newly created descriptor or null if no descriptor was found.
+ */
+ public static JingleNodeDescriptor loadDescriptor(
+ Map<String, String> props,
+ String namePrefix)
+ {
+ if(namePrefix == null)
+ namePrefix = "";
+
+ String relayAddress = props.get(namePrefix + JN_ADDRESS);
+
+ if (relayAddress == null)
+ return null;
+
+ String relayStr = props.get(namePrefix + JN_IS_RELAY_SUPPORTED);
+
+ boolean relay = false;
+
+ try
+ {
+ relay = Boolean.parseBoolean(relayStr);
+ }
+ catch(Throwable t)
+ {
+ }
+
+ JingleNodeDescriptor relayServer =
+ new JingleNodeDescriptor(relayAddress,
+ relay);
+
+ return relayServer;
+ }
+
+ /**
+ * Returns a <tt>String</tt> representation of this descriptor
+ *
+ * @return a <tt>String</tt> representation of this descriptor.
+ */
+ public String toString()
+ {
+ return "JingleNodesDesc: " + getJID() + " relay:"
+ + isRelaySupported();
+ }
+}
diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
index 33c2004..6402a3f 100644
--- a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
+++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
@@ -160,7 +160,6 @@ public abstract class ProtocolProviderFactory
*/
public static final String PROXY_TRANSPORT = "PROXY_TRANSPORT";
-
/**
* The name of the property under which we store the user preference for a
* transport protocol to use (i.e. tcp or udp).
@@ -315,6 +314,17 @@ public abstract class ProtocolProviderFactory
public static final String STUN_IS_TURN_SUPPORTED = "IS_TURN_SUPPORTED";
/**
+ * Indicates if JingleNodes should be used with ICE.
+ */
+ public static final String IS_USE_JINGLE_NODES = "JINGLE_NODES_ENABLED";
+
+ /**
+ * Indicates if JingleNodes should be used with ICE.
+ */
+ public static final String AUTO_DISCOVER_JINGLE_NODES
+ = "AUTO_DISCOVER_JINGLE_NODES";
+
+ /**
* Address used to reach voicemail box, by services able to
* subscribe for voicemail new messages notifications.
*/
diff --git a/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java b/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java
index 5e5df32..6a43bd2 100644
--- a/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java
+++ b/src/net/java/sip/communicator/service/protocol/StunServerDescriptor.java
@@ -6,7 +6,6 @@
*/
package net.java.sip.communicator.service.protocol;
-import java.io.*;
import java.util.*;
import net.java.sip.communicator.util.*;