Initial support for Jingle Nodes. Please note that this feature is disabled by default.

cusax-fix
Sebastien Vincent 15 years ago
parent d016f4fd6d
commit f5b574a71a

@ -1322,6 +1322,7 @@ javax.swing.event, javax.swing.border"/>
<zipfileset src="${lib.noinst}/smackx-debug.jar" prefix=""/>
<zipfileset src="${lib.noinst}/smack.jar" prefix=""/>
<zipfileset src="${lib.noinst}/smackx.jar" prefix=""/>
<zipfileset src="${lib.noinst}/jnsapi.jar" prefix=""/>
</jar>
</target>

@ -63,5 +63,6 @@
<classpathentry kind="lib" path="lib/installer-exclude/lcrypto-jdk16-143.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/otr4j.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/jmyspell-core.jar"/>
<classpathentry kind="lib" path="lib/installer-exclude/jnsapi.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -123,7 +123,7 @@
<compilation-unit>
<package-root>src</package-root>
<classpath
mode="compile">lib/felix.jar:lib/jdic-all.jar:lib/bundle/junit.jar:lib/bundle/log4j.jar:lib/bundle/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/dict4j.jar:lib/installer-exclude/dnsjava.jar:lib/installer-exclude/jain-sip-api.jar:lib/installer-exclude/jain-sip-ri.jar:lib/installer-exclude/jain-sdp.jar:lib/installer-exclude/jcalendar-1.3.2.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/jdom.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b5.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/junit.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/rome-0.9.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/ymsg_network_v0_67.jar:lib/installer-exclude/fmj.jar:lib/installer-exclude/jna.jar:lib/installer-exclude/lti-civil-no_s_w_t.jar:lib/installer-exclude/swing-worker-1.2.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/OrangeExtensions.jar:lib/os-specific/mac/growl4j.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/mac/installer-exclude/dock.jar:lib/os-specific/windows/jdic_stub.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar:lib/installer-exclude/aclibico-2.1.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/pircbot.jar:lib/os-specific/solaris/jdic_stub.jar:lib/os-specific/solaris/installer-exclude/jmf.jar:lib/installer-exclude/jsch-0.1.36.jar:lib/installer-exclude/apache-ant-1.7.0.jar:lib/installer-exclude/izpack-shortcut-link.jar:lib/installer-exclude/jfontchooser-1.0.5.jar:lib/installer-exclude/laf-widget.jar:lib/installer-exclude/transparency.jar:lib/installer-exclude/zrtp4j-light.jar:lib/installer-exclude/lcrypto-jdk16-143.jar:lib/installer-exclude/otr4j.jar:lib/installer-exclude/profiler4j-1.0-beta3-SC.jar:lib/installer-exclude/httpcore-4.0.1.jar:lib/installer-exclude/httpclient-4.0.1.jar:lib/installer-exclude/json-20090723.jar:lib/installer-exclude/ice4j.jar:lib/installer-exclude/dhcp4java-1.00.jar:lib/installer-exclude/jmdns.jar:lib/installer-exclude/jmyspell-core.jar</classpath>
mode="compile">lib/felix.jar:lib/jdic-all.jar:lib/bundle/junit.jar:lib/bundle/log4j.jar:lib/bundle/commons-logging.jar:lib/installer-exclude/concurrent.jar:lib/installer-exclude/dict4j.jar:lib/installer-exclude/dnsjava.jar:lib/installer-exclude/jain-sip-api.jar:lib/installer-exclude/jain-sip-ri.jar:lib/installer-exclude/jain-sdp.jar:lib/installer-exclude/jcalendar-1.3.2.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/jdom.jar:lib/installer-exclude/jmf.jar:lib/installer-exclude/jml-1.0b5.jar:lib/installer-exclude/joscar-client.jar:lib/installer-exclude/joscar-common.jar:lib/installer-exclude/joscar-protocol.jar:lib/installer-exclude/jsocks-klea.jar:lib/installer-exclude/jspeex.jar:lib/installer-exclude/junit.jar:lib/installer-exclude/log4j-1.2.8.jar:lib/installer-exclude/nist-sdp-1.0.jar:lib/installer-exclude/rome-0.9.jar:lib/installer-exclude/smack.jar:lib/installer-exclude/smackx.jar:lib/installer-exclude/ymsg_network_v0_67.jar:lib/installer-exclude/fmj.jar:lib/installer-exclude/jna.jar:lib/installer-exclude/lti-civil-no_s_w_t.jar:lib/installer-exclude/swing-worker-1.2.jar:lib/os-specific/linux/installer-exclude/jmf.jar:lib/os-specific/linux/jdic_stub.jar:lib/os-specific/mac/OrangeExtensions.jar:lib/os-specific/mac/growl4j.jar:lib/os-specific/mac/jdic_stub.jar:lib/os-specific/mac/installer-exclude/jmf.jar:lib/os-specific/mac/installer-exclude/dock.jar:lib/os-specific/windows/jdic_stub.jar:lib/os-specific/windows/installer-exclude/jmf.jar:lib/os-specific/windows/installer-exclude/sound.jar:lib/installer-exclude/aclibico-2.1.jar:lib/installer-exclude/jdic_misc.jar:lib/installer-exclude/pircbot.jar:lib/os-specific/solaris/jdic_stub.jar:lib/os-specific/solaris/installer-exclude/jmf.jar:lib/installer-exclude/jsch-0.1.36.jar:lib/installer-exclude/apache-ant-1.7.0.jar:lib/installer-exclude/izpack-shortcut-link.jar:lib/installer-exclude/jfontchooser-1.0.5.jar:lib/installer-exclude/laf-widget.jar:lib/installer-exclude/transparency.jar:lib/installer-exclude/zrtp4j-light.jar:lib/installer-exclude/lcrypto-jdk16-143.jar:lib/installer-exclude/otr4j.jar:lib/installer-exclude/profiler4j-1.0-beta3-SC.jar:lib/installer-exclude/httpcore-4.0.1.jar:lib/installer-exclude/httpclient-4.0.1.jar:lib/installer-exclude/json-20090723.jar:lib/installer-exclude/ice4j.jar:lib/installer-exclude/dhcp4java-1.00.jar:lib/installer-exclude/jmdns.jar:lib/installer-exclude/jmyspell-core.jar:lib/installer-exclude/jnsapi.jar</classpath>
<built-to>classes</built-to>
<source-level>1.5</source-level>
</compilation-unit>

Binary file not shown.

Binary file not shown.

@ -39,4 +39,8 @@ Export-Package: org.jivesoftware.smack,
org.jivesoftware.smackx.provider,
org.jivesoftware.smack.sasl,
org.xmlpull.v1,
org.xmlpull.mxp1
org.xmlpull.mxp1,
org.xmpp.jnodes,
org.xmpp.jnodes.smack,
org.xmpp.jnodes.nio

@ -14,10 +14,10 @@
# actual text at runtime, place them as you wish
# - \ at the end of a line means that the translation is continued
# in the next line
# - you cannot use single quotes when a parameter is used in the sentence.
# - you cannot use single quotes when a parameter is used in the sentence.
# For example, <You can't move contact {0}>, <Contact {0} doesn't exist>,
# or <Accept '{0}'> does not display correctly. In such cases, you need
# to use double quotes (''):
# to use double quotes (''):
# <You can''t move contact {0}>, <Contact {0} doesn''t exist>, or
# <Accept ''{0}''>
#
@ -409,7 +409,7 @@ service.gui.TODAY=Today
service.gui.TOOLS=&Tools
service.gui.TRANSFER=Trans&fer
service.gui.TO=&To:
service.gui.TRANSFER_CALL_MSG=Select the name of the contact you would like to transfer to and then click Transfer.
service.gui.TRANSFER_CALL_MSG=Select the name of the contact you would like to transfer to and then click Transfer.
service.gui.TRANSFER_CALL_TITLE=Transfer Call
service.gui.TRANSFER_CALL_TO=Transfer to:
service.gui.TRANSPARENCY_NOT_ENABLED=Transparency is not supported by your current configuration.
@ -708,6 +708,12 @@ plugin.jabberaccregwizz.NO_STUN_ADDRESS=Please fill a valid STUN server address
plugin.jabberaccregwizz.NO_STUN_USERNAME=Please fill a valid STUN server username in order to continue.
plugin.jabberaccregwizz.STUN_ALREADY_EXIST=The STUN server you specified already exist.
plugin.jabberaccregwizz.USE_DEFAULT_STUN_SERVER=Use SIP Communicator's STUN server in case no other servers are available.
plugin.jabberaccregwizz.USE_JINGLE_NODES=Use Jingle Nodes
plugin.jabberaccregwizz.AUTO_DISCOVER_JN=Auto discover Jingle Nodes relays
plugin.jabberaccregwizz.RELAY_SUPPORT=Support relay
plugin.jabberaccregwizz.ADD_JINGLE_NODE=Add Jingle Node
plugin.jabberaccregwizz.JID_ADDRESS=JID Address
plugin.jabberaccregwizz.ADDITIONAL_JINGLE_NODES=Additional Jingle Nodes
# mailbox
plugin.mailbox.OUTGOING=Outgoing Message:
@ -932,7 +938,7 @@ impl.media.security.DATA_SEND_FAILED=<html>Failed to send encryption data. Inter
impl.media.security.SECURITY_OFF=Call encryption support off
impl.media.security.SECURITY_ON=Call encryption support on
# ZRTP Configuration
# ZRTP Configuration
impl.media.security.zrtp.TITLE=ZRTP
impl.media.security.zrtp.PUB_KEYS=Public keys
impl.media.security.zrtp.HASHES=Hashes
@ -981,9 +987,9 @@ plugin.securityconfig.masterpassword.CHANGE_MASTER_PASSWORD=Change Master Passwo
plugin.securityconfig.masterpassword.USE_MASTER_PASSWORD=Use a master password
plugin.securityconfig.masterpassword.SAVED_PASSWORDS=Saved Passwords...
plugin.securityconfig.masterpassword.INFO_TEXT=A Master Password is used to protect saved account passwords. Please make sure you remember it.
plugin.securityconfig.masterpassword.CURRENT_PASSWORD=Current password:
plugin.securityconfig.masterpassword.ENTER_PASSWORD=Enter new password:
plugin.securityconfig.masterpassword.REENTER_PASSWORD=Re-enter password:
plugin.securityconfig.masterpassword.CURRENT_PASSWORD=Current password:
plugin.securityconfig.masterpassword.ENTER_PASSWORD=Enter new password:
plugin.securityconfig.masterpassword.REENTER_PASSWORD=Re-enter password:
plugin.securityconfig.masterpassword.MP_TITLE=Master Password
plugin.securityconfig.masterpassword.MP_NOT_SET=(not set)
plugin.securityconfig.masterpassword.MP_CURRENT_EMPTY=You did not enter the correct current Master Password. Please try again.

@ -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 void changeHistoryButtonsState(ChatPanel chatPanel)
/**
* 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
*/

@ -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

@ -21,6 +21,7 @@
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 @@ private Agent createIceAgent()
}
}
/* 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 @@ protected StreamConnector createStreamConnector(MediaType mediaType)
* 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)

@ -14,6 +14,7 @@
* The Jabber implementation of a sip-communicator AccountID
*
* @author Damian Minkov
* @author Sebastien Vincent
*/
public class JabberAccountID
extends AccountID
@ -94,7 +95,7 @@ public boolean isStunServerDiscoveryEnabled()
}
/**
* 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 boolean isUseDefaultStunServer()
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);
}
}

@ -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;
}
}

@ -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();
}
}

@ -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;
}
}

@ -20,6 +20,10 @@
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 AccountID installAccount( String userIDStr,
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 AccountID installAccount( String userIDStr,
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 @@ protected ProtocolProviderService createService(String userID,
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)

@ -31,6 +31,7 @@
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
@ -91,6 +92,12 @@ public class ProtocolProviderServiceJabberImpl
public static final String URN_XMPP_JINGLE_ICE_UDP_1
= 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.
@ -236,6 +243,16 @@ enum ConnectState
*/
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
@ -823,6 +840,8 @@ private ConnectState connectAndLogin(
logger.error("Failed to publish presence status");
}
startJingleNodesDiscovery();
return ConnectState.STOP_TRYING;
}
else
@ -1184,6 +1203,17 @@ protected void initialize(String screenname,
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);
@ -1757,6 +1787,71 @@ public InetAddress getNextHop()
return nextHop;
}
/**
* 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

@ -194,8 +194,9 @@ private void removeRemoteContent(String name)
* 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 void startCandidateHarvest(List<ContentPacketExtension> ourOffer)
* 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

@ -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

@ -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 void commitPage()
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 void loadAccount(ProtocolProviderService protocolProvider)
= 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 void loadAccount(ProtocolProviderService protocolProvider)
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,

@ -54,13 +54,37 @@ 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.
*/
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>.
*/
@ -80,6 +104,9 @@ public 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 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 @@ private Component createAdditionalServersComponent()
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 @@ protected void close(boolean escaped) {}
* 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 Component getTableCellRendererComponent( JTable table,
: 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 Component getTableCellRendererComponent( JTable table,
* 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 boolean isCellEditable(int row, int column)
* @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 @@ boolean isUseIce()
* @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 @@ void setUseIce(boolean isUseIce)
* @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 @@ boolean isAutoDiscoverStun()
* @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 @@ void setAutoDiscoverStun(boolean isAutoDiscover)
* @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 @@ boolean isUseDefaultStunServer()
* @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 @@ void setUseDefaultStunServer(boolean isDefaultStun)
*
* @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 @@ List<StunServerDescriptor> getAdditionalStunServers()
* 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 @@ void addStunServer(StunServerDescriptor stunServer)
*
* @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 @@ void modifyStunServer(StunServerDescriptor stunServer)
* <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 boolean verify(JComponent input)
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;
}
}
}
}

@ -104,6 +104,22 @@ public class JabberAccountRegistration
private List<StunServerDescriptor> additionalStunServers
= 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 List<StunServerDescriptor> getAdditionalStunServers()
{
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;
}
}

@ -26,11 +26,20 @@
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 ProtocolProviderService installAccount(
{
serverName = getServerFromUserName(userName);
}
accountProperties.put(ProtocolProviderFactory.SERVER_ADDRESS,
serverName);
@ -277,6 +293,25 @@ public ProtocolProviderService installAccount(
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,

@ -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();
}
}

@ -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).
@ -314,6 +313,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.

@ -6,7 +6,6 @@
*/
package net.java.sip.communicator.service.protocol;
import java.io.*;
import java.util.*;
import net.java.sip.communicator.util.*;

Loading…
Cancel
Save