Ongoing work on Google Talk voice support.

cusax-fix
Sebastien Vincent 15 years ago
parent d43a1b4ab2
commit 46c8da86d8

@ -747,6 +747,9 @@
<sysproperty key="java.net.preferIPv6Addresses"
value="${java.net.preferIPv6Addresses}"/>
<sysproperty key="gtalktesting"
value="${gtalktesting}"/>
<!--sysproperty key="net.java.sip.communicator.SC_HOME_DIR_LOCATION"
value="${user.home}"/>
<sysproperty key="net.java.sip.communicator.SC_HOME_DIR_NAME"

Binary file not shown.

@ -827,8 +827,10 @@ private void doSetTarget(MediaStreamTarget target)
new SessionAddress(
dataAddr.getAddress(),
dataAddr.getPort(),
controlAddr.getAddress(),
controlAddr.getPort()));
controlAddr != null ? controlAddr.getAddress()
: null,
controlAddr != null ? controlAddr.getPort()
: 0));
targetIsSet = true;
}
catch (IOException ioe)

@ -86,9 +86,12 @@ public RTPConnectorInputStream(DatagramSocket socket)
{
this.socket = socket;
closed = false;
receiverThread = new Thread(this);
receiverThread.start();
if(socket != null)
{
closed = false;
receiverThread = new Thread(this);
receiverThread.start();
}
}
/**
@ -97,7 +100,10 @@ public RTPConnectorInputStream(DatagramSocket socket)
public synchronized void close()
{
closed = true;
socket.close();
if(socket != null)
{
socket.close();
}
}
/**

@ -63,7 +63,7 @@ public class RTPConnectorOutputStream
/**
* Used for debugging. As we don't log every packet
* we must count them and decide which to log.
* we must count them and decide which to log.
*/
private long numberOfPackets = 0;
@ -169,7 +169,7 @@ public void removeTargets()
* We don't log every rtp traffic.
* We log only first then 300,500 and 1000 packets and
* then every 5000 packet.
*
*
* @param numOfPacket current packet number.
* @return wether we should log the current packet.
*/
@ -196,6 +196,11 @@ static boolean logPacket(long numOfPacket)
*/
private boolean send(RawPacket packet)
{
if(socket == null)
{
return false;
}
numberOfPackets++;
for (InetSocketAddress target : targets)
{
@ -225,7 +230,7 @@ private boolean send(RawPacket packet)
packet.getBuffer(),
packet.getOffset(),
packet.getLength());
}
}
}
catch (IOException ex)
{

@ -281,6 +281,8 @@ public class DeviceConfiguration
public static final Dimension[] SUPPORTED_RESOLUTIONS =
new Dimension[]
{
// QVGA
new Dimension(160, 100),
//QCIF
new Dimension(176, 144),
// QVGA

@ -31,7 +31,7 @@ public ParameterizedVideoFormat(
String encoding,
Dimension size,
int maxDataLength,
Class dataType,
Class<?> dataType,
float frameRate,
Map<String, String> fmtps)
{
@ -181,7 +181,7 @@ public Format intersects(Format format)
((ParameterizedVideoFormat) intersection).fmtps
= fmtps.isEmpty()
? MediaFormatImpl.EMPTY_FORMAT_PARAMETERS
: getFormatParameters();
: getFormatParameters();
return intersection;
}
@ -214,7 +214,7 @@ public boolean matches(Format format)
/**
* Initializes a new <tt>Map</tt> from an array in which the key and the
* value of an association are expressed as consecutive elements.
*
*
* @param <T> the very type of the keys and the values to be associated in
* the new <tt>Map</tt>
* @param entries the associations to be created in the new <tt>Map</tt>

@ -524,7 +524,6 @@ else if (prevPeerState.equals(CallPeerState.BUSY)
public synchronized void answer()
throws OperationFailedException
{
System.out.println("answer");
RtpDescriptionPacketExtension answer = null;
try
@ -623,7 +622,6 @@ protected void sendCandidates(
{
candidatesIQ.addExtension(candidate);
}
System.out.println("IQ: " + candidatesIQ.toXML());
protocolProvider.getConnection().sendPacket(candidatesIQ);
}

@ -183,7 +183,6 @@ else if(ext.getNamespace().equals(
atLeastOneValidDescription = true;
}
if (!atLeastOneValidDescription)
{
ProtocolProviderServiceJabberImpl.throwOperationFailedException(
@ -259,8 +258,6 @@ public RtpDescriptionPacketExtension generateSessionAccept()
continue;
}
System.out.println("session format " + format);
// stream connector
StreamConnector connector
= transportManager.getStreamConnector(mediaType);
@ -337,8 +334,6 @@ public void processAnswer(RtpDescriptionPacketExtension answer)
continue;
}
System.out.println("format: " + format);
// stream connector
StreamConnector connector
= transportManager.getStreamConnector(mediaType);
@ -366,7 +361,7 @@ public void processAnswer(RtpDescriptionPacketExtension answer)
* management
* @see CallPeerMediaHandler#getTransportManager()
*/
protected TransportManagerGTalkImpl getTransportManager()
protected synchronized TransportManagerGTalkImpl getTransportManager()
{
if (transportManager == null)
{
@ -456,6 +451,9 @@ public RtpDescriptionPacketExtension createDescription()
description.setNamespace(SessionIQProvider.
GTALK_VIDEO_NAMESPACE);
ext.setAttribute("width", 640);
ext.setAttribute("height", 480);
ext.setAttribute("framerate", 15);
isVideo = true;
}
description.addChildExtension(ext);

@ -645,7 +645,7 @@ public void processPacket(Packet packet)
// for some reason &apos; is not rendered correctly
// from our ui, lets use its equivalent. Other
// similar chars(< > & ") seem ok.
// similar chars(< > & ") seem ok.
receivedMessage =
receivedMessage.replaceAll("&apos;", "&#39;");
@ -1043,6 +1043,7 @@ private String createMailboxDescription(MailboxIQ mailboxIQ)
return message.toString();
}
/**
* Receives incoming MailNotification Packets
*/

@ -9,6 +9,8 @@
import java.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
@ -28,6 +30,7 @@
* @author Emil Ivov
* @author Symphorien Wanko
* @author Lyubomir Marinov
* @author Sebastien Vincent
*/
public class OperationSetBasicTelephonyJabberImpl
extends AbstractOperationSetBasicTelephony<ProtocolProviderServiceJabberImpl>
@ -127,14 +130,22 @@ public Call createCall(String callee)
throws OperationFailedException
{
CallJabberImpl call = new CallJabberImpl(this);
CallPeer callPeer = null;
if (createOutgoingCall(call, callee) == null)
callPeer = createOutgoingCall(call, callee);
if (callPeer == null)
{
throw new OperationFailedException(
"Failed to create outgoing call"
+ " because no peer was created",
OperationFailedException.INTERNAL_ERROR);
}
if(callPeer.getCall() != call)
{
// We may have a Google Talk call here
return callPeer.getCall();
}
return call;
}
@ -172,7 +183,7 @@ public Call createCall(Contact callee)
* @throws OperationFailedException with the corresponding code if we fail
* to create the call.
*/
CallPeerJabberImpl createOutgoingCall(
AbstractCallPeer<?, ?> createOutgoingCall(
CallJabberImpl call,
String calleeAddress)
throws OperationFailedException
@ -199,7 +210,7 @@ CallPeerJabberImpl createOutgoingCall(
* @throws OperationFailedException with the corresponding code if we fail
* to create the call.
*/
CallPeerJabberImpl createOutgoingCall(
AbstractCallPeer<?, ?> createOutgoingCall(
CallJabberImpl call,
String calleeAddress,
Iterable<PacketExtension> sessionInitiateExtensions)
@ -226,6 +237,9 @@ CallPeerJabberImpl createOutgoingCall(
getProtocolProvider().getConnection().getRoster().getPresences(
calleeAddress);
String calleeURI = null;
boolean isGingle = false;
String gingleURI = null;
int bestPriorityGTalk = -1;
// choose the resource that has the highest priority AND support Jingle
while(it.hasNext())
@ -252,6 +266,27 @@ CallPeerJabberImpl createOutgoingCall(
fullCalleeURI = calleeURI;
}
}
else
{
// test GTALK property
if(!Boolean.getBoolean("gtalktesting"))
{
continue;
}
/* see if peer supports Google Talk voice */
if(getProtocolProvider().isExtFeatureListSupported(
calleeURI, ProtocolProviderServiceJabberImpl.
CAPS_GTALK_WEB_VOICE))
{
if(priority > bestPriorityGTalk)
{
bestPriorityGTalk = priority;
isGingle = true;
gingleURI = calleeURI;
}
}
}
}
catch (XMPPException ex)
{
@ -271,7 +306,15 @@ CallPeerJabberImpl createOutgoingCall(
}
*/
if(di != null)
if(isGingle)
{
if(logger.isInfoEnabled())
{
logger.info(gingleURI + ": Google Talk dialect supported");
}
fullCalleeURI = gingleURI;
}
else if(di != null)
{
if (logger.isInfoEnabled())
logger.info(fullCalleeURI + ": jingle supported ");
@ -279,10 +322,12 @@ CallPeerJabberImpl createOutgoingCall(
else
{
if (logger.isInfoEnabled())
logger.info(calleeURI + ": jingle not supported ?");
logger.info(calleeURI +
": jingle and Google Talk not supported ?");
throw new OperationFailedException(
"Failed to create OutgoingJingleSession.\n"
+ calleeURI + " does not support jingle",
+ calleeURI + " does not support jingle or Google Talk",
OperationFailedException.INTERNAL_ERROR);
}
@ -291,16 +336,30 @@ CallPeerJabberImpl createOutgoingCall(
logger.info("Choose one is: " + fullCalleeURI + " " + bestPriority);
}
CallPeerJabberImpl peer = null;
AbstractCallPeer<?, ?> peer = null;
// initiate call
try
{
peer
= call.initiateSession(
fullCalleeURI,
di,
sessionInitiateExtensions);
if(isGingle)
{
logger.info("initiate Gingle call");
CallGTalkImpl callGTalk = new CallGTalkImpl(this);
MediaUseCase useCase = call.getMediaUseCase();
boolean isVideo = call.isLocalVideoAllowed(useCase);
callGTalk.setLocalVideoAllowed(isVideo, useCase);
peer = callGTalk.initiateGTalkSession(fullCalleeURI,
sessionInitiateExtensions);
}
else if(di != null)
{
peer
= call.initiateSession(
fullCalleeURI,
di,
sessionInitiateExtensions);
}
}
catch(Throwable t)
{
@ -397,7 +456,8 @@ public synchronized void putOnHold(CallPeer peer)
private void putOnHold(CallPeer peer, boolean on)
throws OperationFailedException
{
((CallPeerJabberImpl) peer).putOnHold(on);
if(peer instanceof CallPeerJabberImpl)
((CallPeerJabberImpl) peer).putOnHold(on);
}
/**
@ -410,7 +470,7 @@ private void putOnHold(CallPeer peer, boolean on)
@Override
public void setMute(Call call, boolean mute)
{
((CallJabberImpl) call).setMute(mute);
((MediaAwareCall<?, ?, ?>) call).setMute(mute);
}
/**
@ -426,7 +486,15 @@ public synchronized void hangupCallPeer(CallPeer peer)
throws ClassCastException,
OperationFailedException
{
((CallPeerJabberImpl) peer).hangup(null, null);
// XXX maybe add answer/hangup abstract method to MediaAwareCallPeer
if(peer instanceof CallPeerJabberImpl)
{
((CallPeerJabberImpl) peer).hangup(null, null);
}
else if(peer instanceof CallPeerGTalkImpl)
{
((CallPeerGTalkImpl) peer).hangup(null, null);
}
}
/**
@ -439,7 +507,15 @@ public synchronized void hangupCallPeer(CallPeer peer)
public void answerCallPeer(CallPeer peer)
throws OperationFailedException
{
((CallPeerJabberImpl) peer).answer();
// XXX maybe add answer/hangup abstract method to MediaAwareCallPeer
if(peer instanceof CallPeerJabberImpl)
{
((CallPeerJabberImpl) peer).answer();
}
else if(peer instanceof CallPeerGTalkImpl)
{
((CallPeerGTalkImpl) peer).answer();
}
}
/**
@ -451,6 +527,8 @@ public void shutdown()
logger.trace("Ending all active calls. ");
Iterator<CallJabberImpl> activeCalls
= this.activeCallsRepository.getActiveCalls();
Iterator<CallGTalkImpl> activeGTalkCalls
= this.activeGTalkCallsRepository.getActiveCalls();
// this is fast, but events aren't triggered ...
//jingleManager.disconnectAllSessions();
@ -475,6 +553,26 @@ public void shutdown()
}
}
}
while(activeGTalkCalls.hasNext())
{
CallGTalkImpl call = activeGTalkCalls.next();
Iterator<CallPeerGTalkImpl> callPeers = call.getCallPeers();
//go through all call peers and say bye to every one.
while (callPeers.hasNext())
{
CallPeer peer = callPeers.next();
try
{
this.hangupCallPeer(peer);
}
catch (Exception ex)
{
logger.warn("Failed to properly hangup peer " + peer, ex);
}
}
}
}
/**
@ -508,13 +606,21 @@ private void unsubscribeForJinglePackets()
*/
public boolean accept(Packet packet)
{
String sid = null;
//we only handle JingleIQ-s
if( ! (packet instanceof JingleIQ) )
if( ! (packet instanceof JingleIQ) && !(packet instanceof SessionIQ))
{
CallPeerJabberImpl callPeer =
AbstractCallPeer<?, ?> callPeer =
activeCallsRepository.findCallPeerBySessInitPacketID(
packet.getPacketID());
if(callPeer == null)
{
callPeer = activeGTalkCallsRepository.
findCallPeerBySessInitPacketID(packet.getPacketID());
}
if(callPeer != null)
{
/* packet is a response to a Jingle call but is not a JingleIQ
@ -546,19 +652,40 @@ public boolean accept(Packet packet)
return false;
}
JingleIQ jingleIQ = (JingleIQ)packet;
if( jingleIQ.getAction() == JingleAction.SESSION_INITIATE)
if(packet instanceof JingleIQ)
{
//we only accept session-initiate-s dealing RTP
return
jingleIQ.containsContentChildOfType(
RtpDescriptionPacketExtension.class);
JingleIQ jingleIQ = (JingleIQ)packet;
if( jingleIQ.getAction() == JingleAction.SESSION_INITIATE)
{
//we only accept session-initiate-s dealing RTP
return
jingleIQ.containsContentChildOfType(
RtpDescriptionPacketExtension.class);
}
sid = jingleIQ.getSID();
//if this is not a session-initiate we'll only take it if we've
//already seen its session ID.
return (activeCallsRepository.findJingleSID(sid) != null);
}
else if(packet instanceof SessionIQ)
{
SessionIQ sessionIQ = (SessionIQ)packet;
if(sessionIQ.getGTalkType() == GTalkType.INITIATE)
{
return true;
}
sid = sessionIQ.getID();
//if this is not a session-initiate we'll only take it if we've
//already seen its session ID.
return (activeCallsRepository.findJingleSID(jingleIQ.getSID()) != null);
//if this is not a session's initiate we'll only take it if we've
//already seen its session ID.
return (activeGTalkCallsRepository.findSessionID(sid) != null);
}
return false;
}
/**
@ -570,38 +697,75 @@ public boolean accept(Packet packet)
public void processPacket(Packet packet)
{
//this is not supposed to happen because of the filter ... but still
if (! (packet instanceof JingleIQ) )
if (! (packet instanceof JingleIQ) && !(packet instanceof SessionIQ))
return;
JingleIQ jingleIQ = (JingleIQ)packet;
//to prevent hijacking sessions from other jingle based features
//like file transfer for example, we should only send the
//ack if this is a session-initiate with rtp content or if we are
//the owners of this packet's sid
//first ack all "set" requests.
if(jingleIQ.getType() == IQ.Type.SET)
// test GTALK property
if(!Boolean.getBoolean("gtalktesting") && (packet instanceof SessionIQ))
{
IQ ack = IQ.createResultIQ(jingleIQ);
protocolProvider.getConnection().sendPacket(ack);
return;
}
try
if(packet instanceof JingleIQ)
{
processJingleIQ(jingleIQ);
JingleIQ jingleIQ = (JingleIQ)packet;
//to prevent hijacking sessions from other jingle based features
//like file transfer for example, we should only send the
//ack if this is a session-initiate with rtp content or if we are
//the owners of this packet's sid
//first ack all "set" requests.
if(jingleIQ.getType() == IQ.Type.SET)
{
IQ ack = IQ.createResultIQ(jingleIQ);
protocolProvider.getConnection().sendPacket(ack);
}
try
{
processJingleIQ(jingleIQ);
}
catch(Throwable t)
{
logger.info("Error while handling incoming Jingle packet: ", t);
/*
* The Javadoc on ThreadDeath says: If ThreadDeath is caught by
* a method, it is important that it be rethrown so that the
* thread actually dies.
*/
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
}
}
catch(Throwable t)
else if(packet instanceof SessionIQ)
{
logger.info("Error while handling incoming Jingle packet: ", t);
SessionIQ sessionIQ = (SessionIQ)packet;
/*
* The Javadoc on ThreadDeath says: If ThreadDeath is caught by a
* method, it is important that it be rethrown so that the thread
* actually dies.
*/
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
//first ack all "set" requests.
if(sessionIQ.getType() == IQ.Type.SET)
{
IQ ack = IQ.createResultIQ(sessionIQ);
protocolProvider.getConnection().sendPacket(ack);
}
try
{
processSessionIQ(sessionIQ);
}
catch(Throwable t)
{
logger.info("Error while handling incoming GTalk packet: ", t);
/*
* The Javadoc on ThreadDeath says: If ThreadDeath is caught by
* a method, it is important that it be rethrown so that the
* thread actually dies.
*/
if (t instanceof ThreadDeath)
throw (ThreadDeath) t;
}
}
}
@ -780,6 +944,85 @@ else if (action == JingleAction.TRANSPORT_INFO)
}
}
/**
* Analyzes the <tt>sessionIQ</tt>'s action and passes it to the
* corresponding handler.
*
* @param sessionIQ the {@link SessionIQ} packet we need to be analyzing.
*/
private void processSessionIQ(SessionIQ sessionIQ)
{
//let's first see whether we have a peer that's concerned by this IQ
CallPeerGTalkImpl callPeer =
activeGTalkCallsRepository.findCallPeer(sessionIQ.getID());
IQ.Type type = sessionIQ.getType();
if(type == Type.RESULT)
{
return;
}
if (type == Type.ERROR)
{
logger.error("Received error");
XMPPError error = sessionIQ.getError();
String message = "Remote party returned an error!";
if(error != null)
{
logger.error(" code=" + error.getCode()
+ " message=" + error.getMessage());
if (error.getMessage() != null)
message = error.getMessage();
}
if (callPeer != null)
callPeer.setState(CallPeerState.FAILED, message);
return;
}
GTalkType action = sessionIQ.getGTalkType();
if(action == GTalkType.INITIATE)
{
CallGTalkImpl call = null;
if(call == null)
{
call = new CallGTalkImpl(this);
}
call.processGTalkInitiate(sessionIQ);
return;
}
else if (callPeer == null)
{
if (logger.isDebugEnabled())
logger.debug("Received a stray trying response.");
return;
}
//the rest of these cases deal with existing peers
else if(action == GTalkType.CANDIDATES)
{
callPeer.processCandidates(sessionIQ);
}
else if(action == GTalkType.REJECT)
{
callPeer.processSessionReject(sessionIQ);
}
else if(action == GTalkType.TERMINATE)
{
callPeer.processSessionTerminate(sessionIQ);
}
else if(action == GTalkType.ACCEPT)
{
callPeer.processSessionAccept(sessionIQ);
}
}
/**
* Returns a reference to the {@link ActiveCallsRepositoryJabberImpl} that
* we are currently using.
@ -824,7 +1067,8 @@ public ProtocolProviderServiceJabberImpl getProtocolProvider()
*/
public boolean isSecure(CallPeer peer)
{
return ((CallPeerJabberImpl) peer).getMediaHandler().isSecure();
return ((MediaAwareCallPeer<?, ?, ?>) peer).getMediaHandler().
isSecure();
}
/**
@ -837,7 +1081,8 @@ public boolean isSecure(CallPeer peer)
*/
public void setSasVerified(CallPeer peer, boolean verified)
{
((CallPeerJabberImpl) peer).getMediaHandler().setSasVerified(verified);
((MediaAwareCallPeer<?, ?, ?>) peer).getMediaHandler().setSasVerified(
verified);
}
/**

@ -55,6 +55,15 @@ public class OperationSetContactCapabilitiesJabberImpl
private static final Map<Class<? extends OperationSet>, String[]>
OPERATION_SETS_TO_FEATURES
= new HashMap<Class<? extends OperationSet>, String[]>();
/**
* The <tt>Map</tt> which associates specific additionnal
* <tt>OperationSet</tt> class with the capabilities to be supported by a
* <tt>Contact</tt> in order to consider the <tt>Contact</tt> to possess the
* respective <tt>OperationSet</tt> capability.
*/
private static final Map<Class<? extends OperationSet>, String[]>
CAPS_OPERATION_SETS_TO_FEATURES
= new HashMap<Class<? extends OperationSet>, String[]>();
static
{
@ -86,6 +95,24 @@ public class OperationSetContactCapabilitiesJabberImpl
ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RTP,
ProtocolProviderServiceJabberImpl.URN_XMPP_JINGLE_RTP_VIDEO
});
CAPS_OPERATION_SETS_TO_FEATURES.put(
OperationSetBasicTelephony.class,
new String[]
{
ProtocolProviderServiceJabberImpl.CAPS_GTALK_WEB_VOICE,
});
/* XXX video does not work yet so don't expose possibility to video call
* to users
CAPS_OPERATION_SETS_TO_FEATURES.put(
OperationSetVideoTelephony.class,
new String[]
{
ProtocolProviderServiceJabberImpl.CAPS_GTALK_WEB_VOICE,
ProtocolProviderServiceJabberImpl.CAPS_GTALK_WEB_VIDEO
});
*/
}
/**
@ -297,7 +324,9 @@ private <U extends OperationSet> U getOperationSet(String jid,
if (OFFLINE_OPERATION_SETS.contains(opsetClass))
return opset;
else
{
return null;
}
}
/*
@ -321,8 +350,33 @@ private <U extends OperationSet> U getOperationSet(String jid,
&& !parentProvider.isFeatureListSupported(
jid,
features)))
opset = null;
{
if(CAPS_OPERATION_SETS_TO_FEATURES.containsKey(opsetClass))
{
String[] extFeatures =
CAPS_OPERATION_SETS_TO_FEATURES.get(
opsetClass);
// test GTalk
if(!Boolean.getBoolean("gtalktesting"))
{
opset = null;
}
else
if((extFeatures == null) || ((extFeatures.length != 0) &&
!parentProvider.isExtFeatureListSupported(jid,
extFeatures)))
{
opset = null;
}
}
else
{
opset = null;
}
}
}
return opset;
}

@ -13,6 +13,7 @@
import net.java.sip.communicator.service.neomedia.device.*;
import net.java.sip.communicator.service.neomedia.format.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.media.*;
/**
* Implements all desktop streaming related functions for XMPP.
@ -217,7 +218,8 @@ public void setLocalVideoAllowed(Call call,
*/
public boolean isLocalVideoAllowed(Call call)
{
return ((CallJabberImpl)call).isLocalVideoAllowed(MediaUseCase.DESKTOP);
return ((MediaAwareCall<?, ?, ?>)call).
isLocalVideoAllowed(MediaUseCase.DESKTOP);
}
/**

@ -414,7 +414,7 @@ protected CallJabberImpl createOutgoingCall()
* @throws OperationFailedException if inviting the specified callee to the
* specified call fails
*/
protected CallPeerJabberImpl inviteCalleeToCall(
protected CallPeer inviteCalleeToCall(
String calleeAddress,
CallJabberImpl call,
boolean wasConferenceFocus)
@ -434,7 +434,8 @@ protected CallPeerJabberImpl inviteCalleeToCall(
}
CoinPacketExtension confInfo = new CoinPacketExtension(true);
return getBasicTelephony().createOutgoingCall(call, calleeAddress,
return getBasicTelephony().createOutgoingCall(
call, calleeAddress,
Arrays.asList(new PacketExtension[] { confInfo }));
}

@ -127,13 +127,16 @@ protected Call createOutgoingVideoCall(String calleeAddress)
, OperationFailedException.INTERNAL_ERROR);
}
CallJabberImpl call = new CallJabberImpl(basicTelephony);
MediaAwareCall<?, ?, ?> call = new CallJabberImpl(basicTelephony);
/* enable video */
call.setLocalVideoAllowed(true, getMediaUseCase());
CallPeer callPeer =
basicTelephony.createOutgoingCall((CallJabberImpl)call, calleeAddress);
basicTelephony.createOutgoingCall(call, calleeAddress);
return call;
// if call is a Google Talk ones, return the CallGTalkImpl
return callPeer.getCall() == call ? call : callPeer.getCall();
}
/**

@ -20,7 +20,10 @@
import net.java.sip.communicator.service.protocol.jabberconstants.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.inputevt.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.caps.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.coin.*;
import net.java.sip.communicator.impl.protocol.jabber.sasl.*;
import net.java.sip.communicator.service.certificate.*;
@ -120,6 +123,23 @@ public class ProtocolProviderServiceJabberImpl
public static final String URN_XMPP_JINGLE_RTP_HDREXT =
"urn:xmpp:jingle:apps:rtp:rtp-hdrext:0";
/**
* Capabilities name for audio call in Google Talk web version.
*/
public static final String CAPS_GTALK_WEB_VOICE = "voice-v1";
/**
* Capabilities name for video call (receive side) in Google Talk web
* version.
*/
public static final String CAPS_GTALK_WEB_VIDEO = "video-v1";
/**
* Capabilities name for video call (sender side) in Google Talk web
* version.
*/
public static final String CAPS_GTALK_WEB_CAMERA = "camera-v1";
/**
* Used to connect to a XMPP server.
*/
@ -939,8 +959,19 @@ private void registerServiceDiscoveryManager()
// Remove features supported by smack, but not supported in
// SIP Communicator.
new String[] { "http://jabber.org/protocol/commands" },
// Add features SIP Communicator supports in addition to smack.
supportedFeatures.toArray(new String[supportedFeatures.size()]));
// Add features SIP Communicator supports in addition to
// smack.
supportedFeatures.toArray(
new String[supportedFeatures.size()]));
if(Boolean.getBoolean("gtalktesting"))
{
// Add Google Talk "ext" capabilities
discoveryManager.addExtFeature(CAPS_GTALK_WEB_VOICE);
// XXX video does not work yet
//discoveryManager.addExtFeature(CAPS_GTALK_WEB_VIDEO);
//discoveryManager.addExtFeature(CAPS_GTALK_WEB_CAMERA);
}
/*
* Expose the discoveryManager as service-public through the
@ -1219,6 +1250,16 @@ protected void initialize(String screenname,
new CoinIQProvider());
supportedFeatures.add(URN_XMPP_JINGLE_COIN);
//register our GTalk dialect provider
providerManager.addIQProvider( SessionIQ.ELEMENT_NAME,
SessionIQ.NAMESPACE,
new SessionIQProvider());
// register our JingleInfo provider
providerManager.addIQProvider(JingleInfoQueryIQ.ELEMENT_NAME,
JingleInfoQueryIQ.NAMESPACE,
new JingleInfoQueryIQProvider());
//initialize the telephony operation set
OperationSetBasicTelephonyJabberImpl basicTelephony
= new OperationSetBasicTelephonyJabberImpl(this);
@ -1606,6 +1647,50 @@ JabberStatusEnum getJabberStatusEnum()
return jabberStatusEnum;
}
/**
* Determines if the given list of <tt>ext features</tt> is supported by the
* specified jabber id.
*
* @param jid the jabber id for which to check
* @param extFeatures the list of ext features to check for
*
* @return <tt>true</tt> if the list of ext features is supported;
* otherwise, <tt>false</tt>
*/
public boolean isExtFeatureListSupported(String jid, String... extFeatures)
{
EntityCapsManager capsManager = discoveryManager.getCapsManager();
EntityCapsManager.Caps caps = capsManager.getCapsByUser(jid);
if(caps != null && caps.ext != null)
{
String exts[] = caps.ext.split(" ");
boolean found = false;
for(String extFeature : extFeatures)
{
found = false;
for(String ext : exts)
{
if(ext.equals(extFeature))
{
found = true;
break;
}
}
if(!found)
{
break;
}
}
return true;
}
return false;
}
/**
* Determines if the given list of <tt>features</tt> is supported by the
* specified jabber id.
@ -1926,6 +2011,8 @@ public void run()
if(logger.isInfoEnabled())
{
logger.info("Jingle Nodes discovery terminated!");
logger.info("Found " + nodes.getRelayEntries().size() +
" Jingle Nodes relay");
}
service.addEntries(nodes);

@ -7,16 +7,27 @@
package net.java.sip.communicator.impl.protocol.jabber;
import java.beans.*;
import java.io.*;
import java.net.*;
import java.util.*;
import org.ice4j.*;
import org.ice4j.ice.*;
import org.ice4j.ice.harvest.*;
import org.ice4j.security.*;
import org.jivesoftware.smack.*;
import org.jivesoftware.smack.filter.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.packet.IQ.*;
import org.jivesoftware.smack.util.StringUtils;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingleinfo.*;
import net.java.sip.communicator.service.httputil.*;
import net.java.sip.communicator.service.httputil.HttpUtils.HTTPResponseResult;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.netaddr.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.media.*;
import net.java.sip.communicator.util.*;
@ -42,6 +53,16 @@ public class TransportManagerGTalkImpl
private static final Logger logger
= Logger.getLogger(TransportManagerGTalkImpl.class);
/**
* Default STUN server address.
*/
private static final String DEFAULT_STUN_SERVER_ADDRESS = "stun.jitsi.net";
/**
* Default STUN server port.
*/
private static final int DEFAULT_STUN_SERVER_PORT = 3478;
/**
* The generation of the candidates we are currently generating
*/
@ -68,6 +89,11 @@ public class TransportManagerGTalkImpl
*/
private final Agent iceAgent;
/**
* Synchronization object.
*/
private final Object wrapupSyncRoot = new Object();
/**
* Creates a new instance of this transport manager, binding it to the
* specified peer.
@ -78,7 +104,6 @@ public class TransportManagerGTalkImpl
public TransportManagerGTalkImpl(CallPeerGTalkImpl callPeer)
{
super(callPeer);
iceAgent = createIceAgent();
}
@ -132,6 +157,217 @@ protected InetAddress getIntendedDestination(CallPeerGTalkImpl peer)
return peer.getProtocolProvider().getNextHop();
}
/**
* Request Google's Jingle info.
*
* @return list of servers
*/
public List<StunServerDescriptor> requestJingleInfo()
{
JingleInfoQueryIQ iq = new JingleInfoQueryIQ();
ProtocolProviderServiceJabberImpl provider =
getCallPeer().getProtocolProvider();
String accountIDService = provider.getAccountID().getService();
boolean jingleInfoIsSupported
= provider.isFeatureSupported(accountIDService,
JingleInfoQueryIQ.NAMESPACE);
final List<StunServerDescriptor> servers =
new ArrayList<StunServerDescriptor>();
final Object syncRoot = new Object();
// check for google:jingleinfo support
if(!jingleInfoIsSupported)
{
return servers;
}
if(logger.isDebugEnabled())
logger.debug("google:jingleinfo supported for " +
provider.getOurJID());
iq.setFrom(provider.getOurJID());
iq.setTo(StringUtils.parseBareAddress(
provider.getOurJID()));
iq.setType(Type.GET);
XMPPConnection connection = provider.getConnection();
PacketListener pl = new PacketListener()
{
public void processPacket(Packet p)
{
JingleInfoQueryIQ iq = (JingleInfoQueryIQ)p;
Iterator<PacketExtension> it = iq.getExtensions().iterator();
while(it.hasNext())
{
AbstractPacketExtension ext =
(AbstractPacketExtension)it.next();
if(ext.getElementName().equals(
StunPacketExtension.ELEMENT_NAME))
{
for(ServerPacketExtension e :
ext.getChildExtensionsOfType(
ServerPacketExtension.class))
{
StunServerDescriptor dsc =
new StunServerDescriptor(e.getHost(),
e.getUdp(), false, null, null);
servers.add(dsc);
}
}
else if(ext.getElementName().equals(
RelayPacketExtension.ELEMENT_NAME))
{
String token = ((RelayPacketExtension)ext).getToken();
for(ServerPacketExtension e :
ext.getChildExtensionsOfType(
ServerPacketExtension.class))
{
String headerNames[] = new String[2];
String headerValues[] = new String[2];
String addr = "http://" + e.getHost() +
"/create_session";
headerNames[0] = "X-Talk-Google-Relay-Auth";
headerNames[1] = "X-Google-Relay-Auth";
headerValues[0] = token;
headerValues[1] = token;
HTTPResponseResult res =
HttpUtils.openURLConnection(addr,
headerNames, headerValues);
Hashtable<String, String> relayData = null;
try
{
relayData =
parseGoogleRelay(res.getContentString());
}
catch (IOException excpt)
{
logger.info("HTTP query to " + e.getHost() +
"failed", excpt);
break;
}
String user = relayData.get("username");
String password = relayData.get("passsword");
StunServerDescriptor dsc =
new StunServerDescriptor(
relayData.get("relay"),
Integer.parseInt(
relayData.get("udpport")),
true,
user, password);
// not the RFC5766 TURN support
dsc.setOldTurn(true);
servers.add(dsc);
/* XXX wait TCP support for Ice4j
dsc = new StunServerDescriptor(
relayData.get("relay"),
Integer.parseInt(relayData.get("tcpport")),
true,
user,
password);
dsc.setOldTurn(true);
servers.add(dsc);
dsc = new StunServerDescriptor(
relayData.get("relay"),
Integer.parseInt(relayData.get("tcpport")),
true,
user,
password);
dsc.setOldTurn(true);
servers.add(dsc);
*/
}
}
}
synchronized(syncRoot)
{
syncRoot.notify();
}
}
};
connection.addPacketListener(pl,
new PacketFilter()
{
public boolean accept(Packet p)
{
if(p instanceof JingleInfoQueryIQ)
return true;
return false;
}
});
provider.getConnection().sendPacket(iq);
synchronized(syncRoot)
{
try
{
syncRoot.wait(2000);
}
catch(InterruptedException e)
{
}
}
connection.removePacketListener(pl);
return servers;
}
/**
* Parse HTTP response from Google relay.
*
* @param res content string
* @return String
*/
public Hashtable<String, String> parseGoogleRelay(String res)
{
// Separate each line
StringTokenizer tokenizer = new StringTokenizer(res, "\n");
Hashtable<String, String> ret = new Hashtable<String, String>();
while(tokenizer.hasMoreTokens())
{
String token = tokenizer.nextToken();
if(token.startsWith("relay.ip="))
{
ret.put("relay", token.substring(token.indexOf("=") + 1));
}
else if(token.startsWith("relay.udp_port="))
{
ret.put("udpport", token.substring(token.indexOf("=") + 1));
}
else if(token.startsWith("relay.tcp_port="))
{
ret.put("tcpport", token.substring(token.indexOf("=") + 1));
}
else if(token.startsWith("relay.ssltcp_port="))
{
ret.put("ssltcpport", token.substring(token.indexOf("=") + 1));
}
else if(token.startsWith("username="))
{
ret.put("username", token.substring(token.indexOf("=") + 1));
}
else if(token.startsWith("password="))
{
ret.put("password", token.substring(token.indexOf("=") + 1));
}
}
return ret;
}
/**
* Creates the ICE agent that we would be using in this transport manager
* for all negotiation.
@ -142,44 +378,84 @@ protected InetAddress getIntendedDestination(CallPeerGTalkImpl peer)
private Agent createIceAgent()
{
CallPeerGTalkImpl peer = getCallPeer();
Agent agent = null;
Agent agent = new Agent(CompatibilityMode.GTALK);
List<StunServerDescriptor> servers = null;
boolean atLeastOneStunServer = false;
ProtocolProviderServiceJabberImpl provider = peer.getProtocolProvider();
JabberAccountID accID = (JabberAccountID)provider.getAccountID();
/* XXX wait changes from ice4j
agent = new Agent(CompatibilityMode.GTALK);
agent.setControlling(!peer.isInitiator());
*/
/* XXX no configured STUN/TURN for the moment
* it should be discovered by a Google XMPP extension
for(StunServerDescriptor desc : accID.getStunServers())
servers = requestJingleInfo();
for(StunServerDescriptor desc : servers)
{
TransportAddress addr = new TransportAddress(
desc.getAddress(), desc.getPort(), Transport.UDP);
StunCandidateHarvester harvester;
StunCandidateHarvester harvester = null;
if(desc.isTurnSupported())
{
//Yay! a TURN server
harvester
= new TurnCandidateHarvester(
addr,
new LongTermCredential(
desc.getUsername(),
desc.getPassword()));
logger.info("Google TURN descriptor");
/* Google relay server used a special way to allocate
* address (token + HTTP request, ...) and they don't support
* long-term authentication
*/
if(desc.isOldTurn())
{
logger.info("new Google TURN harvester");
harvester = new GoogleTurnCandidateHarvester(
addr, new String(desc.getUsername()));
}
else
{
harvester
= new TurnCandidateHarvester(
addr,
new LongTermCredential(
desc.getUsername(),
desc.getPassword()));
atLeastOneStunServer = true;
}
}
else
{
// take only the first STUN server for now
if(atLeastOneStunServer)
continue;
//this is a STUN only server
harvester = new StunCandidateHarvester(addr);
atLeastOneStunServer = true;
logger.info("Found Google STUN server " + harvester);
}
if (logger.isInfoEnabled())
logger.info("Adding pre-configured harvester " + harvester);
if(harvester != null)
{
agent.addCandidateHarvester(harvester);
}
}
atLeastOneStunServer = true;
agent.addCandidateHarvester(harvester);
if(!atLeastOneStunServer)
{
/* we have no configured or discovered STUN server so takes the
* default provided by us if user allows it
*/
if(accID.isUseDefaultStunServer())
{
TransportAddress addr = new TransportAddress(
DEFAULT_STUN_SERVER_ADDRESS,
DEFAULT_STUN_SERVER_PORT,
Transport.UDP);
StunCandidateHarvester harvester =
new StunCandidateHarvester(addr);
if(harvester != null)
{
agent.addCandidateHarvester(harvester);
}
}
}
if(accID.isUPNPEnabled())
@ -191,7 +467,6 @@ private Agent createIceAgent()
agent.addCandidateHarvester(harvester);
}
}
*/
return agent;
}
@ -265,9 +540,8 @@ public StreamConnector getStreamConnector(MediaType mediaType)
if ((streamConnectorSockets != null)
&& ((streamConnector.getDataSocket()
!= streamConnectorSockets[0 /* RTP */])
))
//|| (streamConnector.getControlSocket()
// != streamConnectorSockets[1 /* RTCP */])))
|| (streamConnector.getControlSocket()
!= streamConnectorSockets[1 /* RTCP */])))
{
// Recreate the StreamConnector for the specified mediaType.
closeStreamConnector(mediaType);
@ -340,18 +614,6 @@ else if(mediaType == MediaType.VIDEO)
}
if (streamConnectorSocketCount > 0)
{
// XXX GTalk audio has not RTCP channel
if(mediaName.equals("rtp") && streamConnectorSocketCount == 1)
{
try
{
streamConnectorSockets[1] = new DatagramSocket();
}
catch(Exception e)
{
}
}
return streamConnectorSockets;
}
}
@ -423,18 +685,10 @@ else if(mediaType == MediaType.VIDEO)
}
if (streamTargetAddressCount > 0)
{
int rtcpIndex = 1;
// XXX GTalk audio has not RTCP channel
if(mediaName.equals("rtp") && streamTargetAddressCount == 1)
{
rtcpIndex = 0;
}
streamTarget
= new MediaStreamTarget(
streamTargetAddresses[0 /* RTP */],
streamTargetAddresses[rtcpIndex /* RTCP */]);
streamTargetAddresses[1 /* RTCP */]);
}
}
return streamTarget;
@ -446,12 +700,14 @@ else if(mediaType == MediaType.VIDEO)
*
* @param media the name of the stream we'd like to create.
*
* @param rtcp if true allocate an RTCP port
*
* @return the newly created {@link IceMediaStream}
*
* @throws OperationFailedException if binding on the specified media stream
* fails for some reason.
*/
private IceMediaStream createIceStream(String media)
private IceMediaStream createIceStream(String media, boolean rtcp)
throws OperationFailedException
{
IceMediaStream stream;
@ -459,8 +715,16 @@ private IceMediaStream createIceStream(String media)
try
{
//the following call involves STUN processing so it may take a while
stream = getNetAddrMgr().createIceStream(
nextMediaPortToTry, media, iceAgent);
stream = iceAgent.createMediaStream(media);
int rtpPort = nextMediaPortToTry;
//rtp
iceAgent.createComponent(stream, Transport.UDP, rtpPort, rtpPort,
rtpPort + 100);
if(rtcp)
iceAgent.createComponent(stream, Transport.UDP,
rtpPort + 1, rtpPort + 1, rtpPort + 101);
}
catch (Exception ex)
{
@ -476,7 +740,8 @@ private IceMediaStream createIceStream(String media)
//would simply include one more bind retry.
try
{
nextMediaPortToTry = stream.getComponent(Component.RTCP)
nextMediaPortToTry = stream.getComponent(rtcp ? Component.RTCP :
Component.RTP)
.getLocalCandidates().get(0)
.getTransportAddress().getPort() + 1;
}
@ -490,19 +755,6 @@ private IceMediaStream createIceStream(String media)
return stream;
}
/**
* Returns a reference to the {@link NetworkAddressManagerService}. The only
* reason this method exists is that {@link JabberActivator
* #getNetworkAddressManagerService()} is too long to write and makes code
* look clumsy.
*
* @return a reference to the {@link NetworkAddressManagerService}.
*/
private static NetworkAddressManagerService getNetAddrMgr()
{
return JabberActivator.getNetworkAddressManagerService();
}
/**
* Starts transport candidate harvest. This method should complete rapidly
* and, in case of lengthy procedures like STUN/TURN/UPnP candidate harvests
@ -528,44 +780,36 @@ public void startCandidateHarvest(
List<GTalkCandidatePacketExtension> candidates
= new LinkedList<GTalkCandidatePacketExtension>();
for(PayloadTypePacketExtension ext : ourAnswer)
synchronized(wrapupSyncRoot)
{
if(ext.getNamespace().equals(
SessionIQProvider.GTALK_AUDIO_NAMESPACE))
for(PayloadTypePacketExtension ext : ourAnswer)
{
audio = true;
if(ext.getNamespace().equals(
SessionIQProvider.GTALK_AUDIO_NAMESPACE))
{
audio = true;
}
else if(ext.getNamespace().equals(
SessionIQProvider.GTALK_VIDEO_NAMESPACE))
{
video = true;
}
}
else if(ext.getNamespace().equals(
SessionIQProvider.GTALK_VIDEO_NAMESPACE))
if(audio)
{
video = true;
}
}
IceMediaStream stream = createIceStream("rtp", video);
if(audio)
{
IceMediaStream stream = createIceStream("rtp");
candidates.addAll(GTalkPacketFactory.createCandidates("rtp",
stream));
}
/* remove RTCP component for the audio as it is not used and
* remote gmail peer does not send them
*/
for(Component cmp : stream.getComponents())
if(video)
{
if(cmp.getComponentID() == 2)
{
stream.removeComponent(cmp);
}
IceMediaStream stream = createIceStream("video_rtp", true);
candidates.addAll(
GTalkPacketFactory.createCandidates("video_rtp", stream));
}
candidates.addAll(GTalkPacketFactory.createCandidates("rtp",
stream));
}
if(video)
{
IceMediaStream stream = createIceStream("video_rtp");
candidates.addAll(GTalkPacketFactory.createCandidates("video_rtp",
stream));
}
/* send candidates */
@ -595,6 +839,12 @@ public void wrapupCandidateHarvest()
public boolean startConnectivityEstablishment(
Iterable<GTalkCandidatePacketExtension> remote)
{
if (IceProcessingState.COMPLETED.equals(iceAgent.getState())/* ||
IceProcessingState.FAILED.equals(iceAgent.getState())*/)
{
return true;
}
/* If ICE is already running, we try to update the checklists with
* the candidates. Note that this is a best effort.
*/
@ -618,21 +868,21 @@ public boolean startConnectivityEstablishment(
// change name to retrieve properly the ICE media stream
if(name.equals("rtp"))
{
numComponent = 1;
numComponent = Component.RTP;
}
else if(name.equals("rtcp"))
{
name = "rtp";
numComponent = 1;
numComponent = Component.RTCP;
}
else if(name.equals("video_rtp"))
{
numComponent = 1;
numComponent = Component.RTP;
}
else if(name.equals("video_rtcp"))
{
name = "video_rtp";
numComponent = 2;
numComponent = Component.RTCP;
}
IceMediaStream stream = iceAgent.getStream(name);
@ -661,7 +911,7 @@ else if(name.equals("video_rtcp"))
Component component
= stream.getComponent(numComponent);
/* XXX wait changes from ice4j
RemoteCandidate remoteCandidate = new RemoteCandidate(
new TransportAddress(
candidate.getAddress(),
@ -675,7 +925,6 @@ else if(name.equals("video_rtcp"))
(long)(candidate.getPreference() * 1000),
ufrag);
component.addUpdateRemoteCandidate(remoteCandidate);
*/
}
/* update all components of all streams */
@ -705,24 +954,29 @@ else if(name.equals("video_rtcp"))
// change name to retrieve properly the ICE media stream
if(name.equals("rtp"))
{
numComponent = 1;
numComponent = Component.RTP;
}
else if(name.equals("rtcp"))
{
name = "rtp";
numComponent = 1;
numComponent = Component.RTCP;
}
else if(name.equals("video_rtp"))
{
numComponent = 1;
numComponent = Component.RTP;
}
else if(name.equals("video_rtcp"))
{
name = "video_rtp";
numComponent = 2;
numComponent = Component.RTCP;
}
IceMediaStream stream = iceAgent.getStream(name);
IceMediaStream stream = null;
synchronized(wrapupSyncRoot)
{
stream = iceAgent.getStream(name);
}
if(stream == null)
{
@ -747,7 +1001,6 @@ else if(name.equals("video_rtcp"))
Component component
= stream.getComponent(numComponent);
/* XXX wait changes from ice4j
RemoteCandidate remoteCandidate = new RemoteCandidate(
new TransportAddress(
candidate.getAddress(),
@ -761,7 +1014,6 @@ else if(name.equals("video_rtcp"))
(long)(candidate.getPreference() * 1000),
ufrag);
component.addRemoteCandidate(remoteCandidate);
*/
startConnectivityEstablishment = true;
}
@ -780,9 +1032,7 @@ else if(name.equals("video_rtcp"))
{
for (Component component : stream.getComponents())
{
if(component.getName().equals("RTCP"))
continue;
if (component.getRemoteCandidateCount() < 2)
if (component.getRemoteCandidateCount() < 1)
{
startConnectivityEstablishment = false;
break;
@ -888,4 +1138,4 @@ public void close()
iceAgent.free();
}
}
}
}

@ -318,8 +318,8 @@ private static GTalkCandidatePacketExtension createCandidate(
TransportAddress transportAddress = candidate.getTransportAddress();
// XXX different username/password for each candidate ?
packet.setUsername(stream.getParentAgent().getLocalUfrag());
// different username/password for each candidate ?
packet.setUsername(((LocalCandidate)candidate).getUfrag());
packet.setPassword("");
packet.setAddress(transportAddress.getHostAddress());
packet.setPort(transportAddress.getPort());
@ -340,7 +340,8 @@ else if(candType == CandidateType.host)
}
packet.setType(candType);
packet.setPreference(candidate.getPriority() / 1000);
double priority = candidate.getPriority();
packet.setPreference((priority / 1000));
return packet;
}

@ -12,7 +12,6 @@
import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.util.*;
/**
* A straightforward extension of the IQ. A <tt>SessionIQ</tt> object is created

@ -14,7 +14,8 @@
import org.xmlpull.v1.*;
/**
* An implementation of a GTalk session IQ provider that parses incoming session IQs.
* An implementation of a GTalk session IQ provider that parses incoming session
* IQs.
*
* @author Sebastien Vincent
*/
@ -97,9 +98,7 @@ public SessionIQProvider()
* instance.
*
* @param parser an XML parser.
*
* @return a new {@link SessionIQ} instance.
*
* @throws Exception if an error occurs parsing the XML.
*/
public IQ parseIQ(XmlPullParser parser) throws Exception

@ -0,0 +1,61 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import org.jivesoftware.smack.packet.*;
/**
* The <tt>JingleInfoQueryIQ</tt> is used to discover STUN and relay server via
* the Google's Jingle Server Discovery extension.
*
* @author Sebastien Vincent
*/
public class JingleInfoQueryIQ
extends IQ
{
/**
* The namespace.
*/
public static final String NAMESPACE = "google:jingleinfo";
/**
* The element name.
*/
public static final String ELEMENT_NAME = "query";
/**
* Returns the sub-element XML section of the IQ packet, or null if
* there isn't one. Packet extensions must be included, if any are defined.
*
* @return the child element section of the IQ XML.
*/
@Override
public String getChildElementXML()
{
StringBuilder bld = new StringBuilder();
bld.append("<").append(ELEMENT_NAME).append(" xmlns='").
append(NAMESPACE).append("'");
if(getExtensions().size() == 0)
{
bld.append("/>");
}
else
{
bld.append(">");
for(PacketExtension pe : getExtensions())
{
bld.append(pe.toXML());
}
bld.append("</").append(ELEMENT_NAME).append(">");
}
return bld.toString();
}
}

@ -0,0 +1,92 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
/**
* Provider for the <tt>JingleInfoQueryIQ</tt>.
*
* @author Sebastien Vincent
*/
public class JingleInfoQueryIQProvider
implements IQProvider
{
/**
* STUN packet extension provider.
*/
private final PacketExtensionProvider stunProvider =
new StunProvider();
/**
* Relay packet extension provider.
*/
private final PacketExtensionProvider relayProvider =
new RelayProvider();
/**
* Creates a new instance of the <tt>JingleInfoQueryIQProvider</tt> and
* register all related extension providers. It is the responsibility of the
* application to register the <tt>JingleInfoQueryIQProvider</tt> itself.
*/
public JingleInfoQueryIQProvider()
{
ProviderManager providerManager = ProviderManager.getInstance();
providerManager.addExtensionProvider(
ServerPacketExtension.ELEMENT_NAME,
ServerPacketExtension.NAMESPACE,
new DefaultPacketExtensionProvider
<ServerPacketExtension>(ServerPacketExtension.class));
}
/**
* Parses a JingleInfoQueryIQ</tt>.
*
* @param parser an XML parser.
* @return a new {@link JingleInfoQueryIQ} instance.
* @throws Exception if an error occurs parsing the XML.
*/
public IQ parseIQ(XmlPullParser parser)
throws Exception
{
boolean done = false;
JingleInfoQueryIQ iq = new JingleInfoQueryIQ();
// Now go on and parse the session element's content.
while (!done)
{
int eventType = parser.next();
String elementName = parser.getName();
if (eventType == XmlPullParser.START_TAG)
{
if(elementName.equals(StunPacketExtension.ELEMENT_NAME))
{
iq.addExtension(stunProvider.parseExtension(parser));
}
else if(elementName.equals(RelayPacketExtension.ELEMENT_NAME))
{
iq.addExtension(relayProvider.parseExtension(parser));
}
}
if (eventType == XmlPullParser.END_TAG)
{
if (parser.getName().equals(JingleInfoQueryIQ.ELEMENT_NAME))
{
done = true;
}
}
}
return iq;
}
}

@ -0,0 +1,90 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import org.jivesoftware.smack.packet.*;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
/**
* Relay packet extension.
*
* @author Sebastien Vincent
*/
public class RelayPacketExtension
extends AbstractPacketExtension
{
/**
* The namespace.
*/
public static final String NAMESPACE = null;
/**
* The element name.
*/
public static final String ELEMENT_NAME = "relay";
/**
* The token.
*/
private String token = null;
/**
* Constructor.
*/
public RelayPacketExtension()
{
super(NAMESPACE, ELEMENT_NAME);
}
/**
* Set the token.
*
* @param token token
*/
public void setToken(String token)
{
this.token = token;
}
/**
* Get the token.
*
* @return authentication token
*/
public String getToken()
{
return token;
}
/**
* Get an XML string representation.
*
* @return XML string representation
*/
public String toXML()
{
StringBuilder bld = new StringBuilder();
bld.append("<").append(ELEMENT_NAME).append(">");
if(token != null)
{
bld.append("<").append("token").append(">");
bld.append(token);
bld.append("</").append("token").append(">");
}
for(PacketExtension pe : getChildExtensions())
{
bld.append(pe.toXML());
}
bld.append("</").append(ELEMENT_NAME).append(">");
return bld.toString();
}
}

@ -0,0 +1,114 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
/**
* Parser for RelayPacketExtension.
*
* @author Sebastien Vincent
*/
public class RelayProvider
implements PacketExtensionProvider
{
/**
* Parses a users extension sub-packet and creates a {@link
* StunPacketExtension} instance. At the beginning of the method
* call, the xml parser will be positioned on the opening element of the
* packet extension. As required by the smack API, at the end of the method
* call, the parser will be positioned on the closing element of the packet
* extension.
*
* @param parser an XML parser positioned at the opening
* <tt>Server</tt> element.
*
* @return a new {@link RelayPacketExtension} instance.
* @throws java.lang.Exception if an error occurs parsing the XML.
*/
public PacketExtension parseExtension(XmlPullParser parser)
throws Exception
{
boolean done = false;
int eventType;
String elementName = null;
RelayPacketExtension ext
= new RelayPacketExtension();
while (!done)
{
eventType = parser.next();
elementName = parser.getName();
if (eventType == XmlPullParser.START_TAG)
{
if(elementName.equals(ServerPacketExtension.ELEMENT_NAME))
{
PacketExtensionProvider provider = (PacketExtensionProvider)
ProviderManager.getInstance().getExtensionProvider(
ServerPacketExtension.ELEMENT_NAME,
ServerPacketExtension.NAMESPACE);
PacketExtension childExtension =
provider.parseExtension(parser);
ext.addChildExtension(childExtension);
}
else if(elementName.equals("token"))
{
ext.setToken(parseText(parser));
}
}
else if (eventType == XmlPullParser.END_TAG)
{
if (parser.getName().equals(
RelayPacketExtension.ELEMENT_NAME))
{
done = true;
}
}
}
return ext;
}
/**
* Returns the content of the next {@link XmlPullParser#TEXT} element that
* we encounter in <tt>parser</tt>.
*
* @param parser the parse that we'll be probing for text.
*
* @return the content of the next {@link XmlPullParser#TEXT} element we
* come across or <tt>null</tt> if we encounter a closing tag first.
*
* @throws java.lang.Exception if an error occurs parsing the XML.
*/
public static String parseText(XmlPullParser parser)
throws Exception
{
boolean done = false;
int eventType;
String text = null;
while (!done)
{
eventType = parser.next();
if (eventType == XmlPullParser.TEXT)
{
text = parser.getText();
}
else if (eventType == XmlPullParser.END_TAG)
{
done = true;
}
}
return text;
}
}

@ -0,0 +1,96 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
/**
* Server packet extension.
*
* @author Sebastien Vincent
*/
public class ServerPacketExtension
extends AbstractPacketExtension
{
/**
* The namespace.
*/
public static final String NAMESPACE = null;
/**
* The element name.
*/
public static final String ELEMENT_NAME = "server";
/**
* Host attribute name.
*/
public static final String HOST_ATTR_NAME = "host";
/**
* TCP attribute name.
*/
public static final String TCP_ATTR_NAME = "tcp";
/**
* UDP attribute name.
*/
public static final String UDP_ATTR_NAME = "udp";
/**
* SSL attribute name.
*/
public static final String SSL_ATTR_NAME = "tcpssl";
/**
* Constructor.
*/
public ServerPacketExtension()
{
super(NAMESPACE, ELEMENT_NAME);
}
/**
* Returns the host address.
*
* @return this host address
*/
public String getHost()
{
return super.getAttributeAsString(HOST_ATTR_NAME);
}
/**
* Returns the UDP port.
*
* @return the UDP port
*/
public int getUdp()
{
return Integer.parseInt(super.getAttributeAsString(UDP_ATTR_NAME));
}
/**
* Returns the TCP port.
*
* @return the TCP port
*/
public int getTcp()
{
return Integer.parseInt(super.getAttributeAsString(TCP_ATTR_NAME));
}
/**
* Returns the SSL port.
*
* @return the SSL port
*/
public int getSsl()
{
return Integer.parseInt(super.getAttributeAsString(SSL_ATTR_NAME));
}
}

@ -0,0 +1,36 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import net.java.sip.communicator.impl.protocol.jabber.extensions.*;
/**
* Stun packet extension.
*
* @author Sebastien Vincent
*/
public class StunPacketExtension
extends AbstractPacketExtension
{
/**
* The namespace.
*/
public static final String NAMESPACE = null;
/**
* The element name.
*/
public static final String ELEMENT_NAME = "stun";
/**
* Constructor.
*/
public StunPacketExtension()
{
super(NAMESPACE, ELEMENT_NAME);
}
}

@ -0,0 +1,74 @@
/*
* Jitsi, 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.extensions.jingleinfo;
import org.jivesoftware.smack.packet.*;
import org.jivesoftware.smack.provider.*;
import org.xmlpull.v1.*;
/**
* Parser for StunPacketExtension.
*
* @author Sebastien Vincent
*/
public class StunProvider
implements PacketExtensionProvider
{
/**
* Parses a users extension sub-packet and creates a {@link
* StunPacketExtension} instance. At the beginning of the method
* call, the xml parser will be positioned on the opening element of the
* packet extension. As required by the smack API, at the end of the method
* call, the parser will be positioned on the closing element of the packet
* extension.
*
* @param parser an XML parser positioned at the opening
* <tt>Server</tt> element.
*
* @return a new {@link StunPacketExtension} instance.
* @throws java.lang.Exception if an error occurs parsing the XML.
*/
public PacketExtension parseExtension(XmlPullParser parser)
throws Exception
{
boolean done = false;
int eventType;
String elementName = null;
StunPacketExtension ext
= new StunPacketExtension();
while (!done)
{
eventType = parser.next();
elementName = parser.getName();
if (eventType == XmlPullParser.START_TAG)
{
if(elementName.equals(ServerPacketExtension.ELEMENT_NAME))
{
PacketExtensionProvider provider = (PacketExtensionProvider)
ProviderManager.getInstance().getExtensionProvider(
ServerPacketExtension.ELEMENT_NAME,
ServerPacketExtension.NAMESPACE);
PacketExtension childExtension =
provider.parseExtension(parser);
ext.addChildExtension(childExtension);
}
}
else if (eventType == XmlPullParser.END_TAG)
{
if (parser.getName().equals(
StunPacketExtension.ELEMENT_NAME))
{
done = true;
}
}
}
return ext;
}
}

@ -47,6 +47,7 @@ Import-Package: org.osgi.framework,
net.java.sip.communicator.service.netaddr,
net.java.sip.communicator.service.argdelegation,
net.java.sip.communicator.service.certificate,
net.java.sip.communicator.service.httputil,
net.java.sip.communicator.service.gui,
org.xmlpull.v1,
org.xmlpull.mxp1,

@ -16,7 +16,6 @@
import javax.sip.message.*;
import net.java.sip.communicator.impl.protocol.sip.sdp.*;
import net.java.sip.communicator.service.neomedia.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;

@ -67,7 +67,23 @@ public class HttpUtils
*/
public static HTTPResponseResult openURLConnection(String address)
{
return openURLConnection(address, null, null);
return openURLConnection(address, null, null, null, null);
}
/**
* Opens a connection to the <tt>address</tt>.
* @param address the address to contact.
* @param headerParamNames additional header name to include
* @param headerParamValues corresponding header value to include
* @return the result if any or null if connection was not possible
* or canceled by user.
*/
public static HTTPResponseResult openURLConnection(String address,
String[] headerParamNames,
String[] headerParamValues)
{
return openURLConnection(address, null, null, headerParamNames,
headerParamValues);
}
/**
@ -79,12 +95,16 @@ public static HTTPResponseResult openURLConnection(String address)
* @param passwordPropertyName the property to use to retrieve/store
* password value if protected site is hit, for password
* CredentialsStorageService service is used.
* @param headerParamNames additional header name to include
* @param headerParamValues corresponding header value to include
* @return the result if any or null if connection was not possible
* or canceled by user.
*/
public static HTTPResponseResult openURLConnection(String address,
String usernamePropertyName,
String passwordPropertyName)
String passwordPropertyName,
String[] headerParamNames,
String[] headerParamValues)
{
try
{
@ -93,6 +113,16 @@ public static HTTPResponseResult openURLConnection(String address,
usernamePropertyName, passwordPropertyName,
httpGet.getURI().getHost());
/* add additional HTTP header */
if(headerParamNames != null && headerParamValues != null)
{
for(int i = 0 ; i < headerParamNames.length ; i++)
{
httpGet.addHeader(new BasicHeader(headerParamNames[i],
headerParamValues[i]));
}
}
HttpEntity result = executeMethod(httpClient, httpGet);
if(result == null)
@ -282,6 +312,7 @@ public static HTTPResponseResult postFile(String address,
/**
* Posting form to <tt>address</tt>. For submission we use POST method
* which is "application/x-www-form-urlencoded" encoded.
* @param address HTTP address.
* @param usernamePropertyName the property to use to retrieve/store
* username value if protected site is hit, for username
* ConfigurationService service is used.

@ -44,6 +44,12 @@ public class StunServerDescriptor
*/
private boolean isTurnSupported;
/**
* If TURN version supported by this <tt>StunServerDescriptor</tt> is not
* the RFC 5766.
*/
private boolean isOldTurn = false;
/**
* The username that we need to use with the server or <tt>null</tt> if
* this server does not require a user name.
@ -274,6 +280,27 @@ public static StunServerDescriptor loadDescriptor(
return stunServer;
}
/**
* Returns true if the TURN protocol supported is not the RFC5766 ones.
*
* @return Returns true if the TURN protocol supported is not the RFC5766
* ones.
*/
public boolean isOldTurn()
{
return isOldTurn;
}
/**
* Set the old TURN support.
*
* @param val value to set
*/
public void setOldTurn(boolean val)
{
this.isOldTurn = val;
}
/**
* Returns a <tt>String</tt> representation of this descriptor
*

@ -96,7 +96,8 @@ public StreamConnector getStreamConnector(MediaType mediaType)
if ((streamConnector == null)
|| streamConnector.getDataSocket().isClosed()
|| streamConnector.getControlSocket().isClosed())
|| (streamConnector.getControlSocket() != null &&
streamConnector.getControlSocket().isClosed()))
{
streamConnectors[mediaType.ordinal()]
= streamConnector

Loading…
Cancel
Save