diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
index 029dc3262..3f917bce6 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java
@@ -91,8 +91,18 @@ public void run()
receivedCallDialog.setVisible(true);
- final String peerName
- = sourceCall.getCallPeers().next().getDisplayName();
+ Iterator extends CallPeer> peerIterator =
+ sourceCall.getCallPeers();
+
+ if(!peerIterator.hasNext())
+ {
+ if (receivedCallDialog.isVisible())
+ receivedCallDialog.setVisible(false);
+
+ return;
+ }
+
+ final String peerName = peerIterator.next().getDisplayName();
final Date callDate = new Date();
sourceCall.addCallChangeListener(new CallChangeAdapter()
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java
index 371d43cbd..6be7282b4 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java
@@ -8,8 +8,10 @@
import java.beans.*;
+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.*;
/**
* The CallPeerAdapter is an adapter that implements all common
@@ -188,6 +190,25 @@ public void securityOff(CallPeerSecurityOffEvent securityOffEvent)
renderer.securityOff(securityOffEvent);
}
+ /**
+ * The handler for the security event received. The security event
+ * represents a timeout trying to establish a secure connection.
+ * Most probably the other peer doesn't support it.
+ *
+ * @param securityTimeoutEvent
+ * the security timeout event received
+ */
+ public void securityTimeout(
+ CallPeerSecurityTimeoutEvent securityTimeoutEvent)
+ {
+ CallPeer peer = (CallPeer) securityTimeoutEvent.getSource();
+
+ if (!peer.equals(callPeer))
+ return;
+
+ renderer.securityTimeout(securityTimeoutEvent);
+ }
+
/**
* Adds a ConferenceMemberPanel to this container when a
* ConferenceMember has been added to the corresponding conference.
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java
index 365d124ba..d9155368a 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java
@@ -77,6 +77,13 @@ public interface CallPeerRenderer
*/
public void securityOff(CallPeerSecurityOffEvent evt);
+ /**
+ * Indicates that the security is timeouted, is not supported by the
+ * other end.
+ * @param evt Details about the event that caused this message.
+ */
+ public void securityTimeout(CallPeerSecurityTimeoutEvent evt);
+
/**
* Sets the call peer adapter that manages all related listeners.
*
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
index a25f7dda4..30821781c 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java
@@ -719,6 +719,16 @@ public void securityOff(CallPeerSecurityOffEvent evt)
}
}
+ /**
+ * Indicates that the security is timeouted, is not supported by the
+ * other end.
+ * @param evt Details about the event that caused this message.
+ */
+ public void securityTimeout(CallPeerSecurityTimeoutEvent evt)
+ {
+
+ }
+
/**
* Sets the call peer adapter managing all related listeners.
* @param adapter the adapter to set
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java
index fbc0dcc0d..951df44d4 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java
@@ -341,6 +341,16 @@ public void securityOff(CallPeerSecurityOffEvent evt)
}
}
+ /**
+ * Indicates that the security is timeouted, is not supported by the
+ * other end.
+ * @param evt Details about the event that caused this message.
+ */
+ public void securityTimeout(CallPeerSecurityTimeoutEvent evt)
+ {
+
+ }
+
/**
* Sets the call peer adapter that manages all related listeners.
*
diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java
index 0b032ff40..3e8adc1e9 100644
--- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java
+++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java
@@ -236,6 +236,16 @@ public void securityOff(CallPeerSecurityOffEvent evt)
}
}
+ /**
+ * Indicates that the security is timeouted, is not supported by the
+ * other end.
+ * @param evt Details about the event that caused this message.
+ */
+ public void securityTimeout(CallPeerSecurityTimeoutEvent evt)
+ {
+
+ }
+
/**
* Sets the mute status icon to the status panel.
*
diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java
index e965546e1..b54a1d31e 100644
--- a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java
+++ b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java
@@ -350,6 +350,8 @@ public void zrtpNotSuppOther()
logger.debug(sessionTypeToString(sessionType)
+ ": Other party does not support ZRTP key negotiation protocol,"
+ " no secure calls possible.");
+
+ securityListener.securityTimeout(sessionType);
}
/**
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java
index 1d9a56230..2a2bf02f3 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java
@@ -11,6 +11,7 @@
import org.jivesoftware.smack.packet.*;
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.service.protocol.*;
import net.java.sip.communicator.service.protocol.event.*;
import net.java.sip.communicator.service.protocol.media.*;
@@ -127,6 +128,28 @@ public CallPeerGTalkImpl processGTalkInitiate(SessionIQ sessionIQ)
//before notifying about this call, make sure that it looks alright
callPeer.processSessionInitiate(sessionIQ);
+ // if paranoia is set, to accept the call we need to know that
+ // the other party has support for media encryption
+ if(getProtocolProvider().getAccountID().getAccountPropertyBoolean(
+ ProtocolProviderFactory.MODE_PARANOIA, false)
+ && callPeer.getMediaHandler().getAdvertisedEncryptionMethods().length
+ == 0)
+ {
+ //send an error response;
+ String reasonText = "Encryption required!";
+ SessionIQ errResp = GTalkPacketFactory.createSessionTerminate(
+ sessionIQ.getTo(),
+ sessionIQ.getFrom(),
+ sessionIQ.getID(),
+ Reason.SECURITY_ERROR,
+ reasonText);
+
+ callPeer.setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+
+ return null;
+ }
+
if( callPeer.getState() == CallPeerState.FAILED)
return null;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
index 8aaf171dd..a08174797 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java
@@ -184,6 +184,28 @@ public CallPeerJabberImpl processSessionInitiate(JingleIQ jingleIQ)
//before notifying about this call, make sure that it looks alright
callPeer.processSessionInitiate(jingleIQ);
+ // if paranoia is set, to accept the call we need to know that
+ // the other party has support for media encryption
+ if(getProtocolProvider().getAccountID().getAccountPropertyBoolean(
+ ProtocolProviderFactory.MODE_PARANOIA, false)
+ && callPeer.getMediaHandler().getAdvertisedEncryptionMethods().length
+ == 0)
+ {
+ //send an error response;
+ String reasonText = "Encryption required!";
+ JingleIQ errResp = JinglePacketFactory.createSessionTerminate(
+ jingleIQ.getTo(),
+ jingleIQ.getFrom(),
+ jingleIQ.getSID(),
+ Reason.SECURITY_ERROR,
+ reasonText);
+
+ callPeer.setState(CallPeerState.FAILED, reasonText);
+ getProtocolProvider().getConnection().sendPacket(errResp);
+
+ return null;
+ }
+
if( callPeer.getState() == CallPeerState.FAILED)
return null;
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java
index 2e2907e7d..a3ddcf50d 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java
@@ -148,6 +148,21 @@ else if(ext.getNamespace().equals(
}
}
+ EncryptionPacketExtension encryptionPacketExtension
+ = offer.getFirstChildOfType(EncryptionPacketExtension.class);
+ if(encryptionPacketExtension != null)
+ {
+ ZrtpHashPacketExtension zrtpHashPacketExtension =
+ encryptionPacketExtension.getFirstChildOfType(
+ ZrtpHashPacketExtension.class);
+
+ if(zrtpHashPacketExtension != null
+ && zrtpHashPacketExtension.getValue() != null)
+ {
+ addAdvertisedEncryptionMethod(SrtpControlType.ZRTP);
+ }
+ }
+
for(MediaType mediaType : MediaType.values())
{
if(!(isAudio && mediaType == MediaType.AUDIO) &&
@@ -339,6 +354,21 @@ public void processAnswer(RtpDescriptionPacketExtension answer)
{
List lst = answer.getPayloadTypes();
+ EncryptionPacketExtension encryptionPacketExtension
+ = answer.getFirstChildOfType(EncryptionPacketExtension.class);
+ if(encryptionPacketExtension != null)
+ {
+ ZrtpHashPacketExtension zrtpHashPacketExtension =
+ encryptionPacketExtension.getFirstChildOfType(
+ ZrtpHashPacketExtension.class);
+
+ if(zrtpHashPacketExtension != null
+ && zrtpHashPacketExtension.getValue() != null)
+ {
+ addAdvertisedEncryptionMethod(SrtpControlType.ZRTP);
+ }
+ }
+
boolean masterStreamSet = true;
for(MediaType mediaType : MediaType.values())
{
diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
index f4ac19eb6..d510e7aa6 100644
--- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java
@@ -398,6 +398,21 @@ public void processOffer(List offer)
localContentMap.put(content.getName(), ourContent);
atLeastOneValidDescription = true;
+
+ EncryptionPacketExtension encryptionPacketExtension
+ = description.getFirstChildOfType(EncryptionPacketExtension.class);
+ if(encryptionPacketExtension != null)
+ {
+ ZrtpHashPacketExtension zrtpHashPacketExtension =
+ encryptionPacketExtension.getFirstChildOfType(
+ ZrtpHashPacketExtension.class);
+
+ if(zrtpHashPacketExtension != null
+ && zrtpHashPacketExtension.getValue() != null)
+ {
+ addAdvertisedEncryptionMethod(SrtpControlType.ZRTP);
+ }
+ }
}
if (!atLeastOneValidDescription)
@@ -1178,6 +1193,21 @@ private void processContent(ContentPacketExtension content, boolean modify,
}
}
+ EncryptionPacketExtension encryptionPacketExtension
+ = description.getFirstChildOfType(EncryptionPacketExtension.class);
+ if(encryptionPacketExtension != null)
+ {
+ ZrtpHashPacketExtension zrtpHashPacketExtension =
+ encryptionPacketExtension.getFirstChildOfType(
+ ZrtpHashPacketExtension.class);
+
+ if(zrtpHashPacketExtension != null
+ && zrtpHashPacketExtension.getValue() != null)
+ {
+ addAdvertisedEncryptionMethod(SrtpControlType.ZRTP);
+ }
+ }
+
// create the corresponding stream...
initStream(content.getName(), connector, dev,
supportedFormats.get(0), target, direction, rtpExtensions,
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java
index 8da7ef854..3fa3a370d 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java
@@ -665,7 +665,7 @@ && getPeer().getCall().isSipZrtpAttribute())
String helloHash = zcontrol.getHelloHash();
if(helloHash != null && helloHash.length() > 0)
- md.setAttribute("zrtp-hash", helloHash);
+ md.setAttribute(SdpUtils.ZRTP_HASH_ATTR, helloHash);
}
catch (SdpException ex)
@@ -870,6 +870,8 @@ private synchronized void processAnswer(SessionDescription answer)
this.setCallInfoURL(SdpUtils.getCallInfoURL(answer));
boolean masterStreamSet = false;
+ boolean hasZrtp = false;
+ boolean hasSdes = false;
List seenMediaTypes = new ArrayList();
for (MediaDescription mediaDescription : remoteDescriptions)
{
@@ -1012,6 +1014,8 @@ private synchronized void processAnswer(SessionDescription answer)
it.remove();
}
}
+
+ hasSdes = true;
}
}
@@ -1034,10 +1038,25 @@ private synchronized void processAnswer(SessionDescription answer)
}
}
+ try
+ {
+ hasZrtp = mediaDescription.getAttribute(
+ SdpUtils.ZRTP_HASH_ATTR) != null;
+ }
+ catch (SdpParseException e)
+ {
+ logger.error("received an unparsable sdp attribute", e);
+ }
+
// create the corresponding stream...
initStream(connector, dev, supportedFormats.get(0), target,
direction, rtpExtensions, masterStream);
}
+
+ if(hasSdes)
+ addAdvertisedEncryptionMethod(SrtpControlType.SDES);
+ if(hasZrtp)
+ addAdvertisedEncryptionMethod(SrtpControlType.ZRTP);
}
/**
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
index a6cb38681..29c919bbf 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java
@@ -258,6 +258,8 @@ private CallPeerSipImpl createCallPeerFor(
mediaDirections.put(MediaType.AUDIO, MediaDirection.INACTIVE);
mediaDirections.put(MediaType.VIDEO, MediaDirection.INACTIVE);
+ boolean hasZrtp = false;
+ boolean hasSdes = false;
//this check is not mandatory catch all to skip if a problem exists
try
{
@@ -279,6 +281,9 @@ private CallPeerSipImpl createCallPeerFor(
MediaType mediaType =
SdpUtils.getMediaType(mediaDescription);
+ hasZrtp = hasZrtp || mediaDescription.getAttribute(
+ SdpUtils.ZRTP_HASH_ATTR) != null;
+
if(mediaType.equals(MediaType.VIDEO))
{
MediaDirection videoDirection =
@@ -293,6 +298,25 @@ else if(mediaType.equals(MediaType.AUDIO))
mediaDirections.put(MediaType.AUDIO,
audioDirection);
}
+
+ @SuppressWarnings("unchecked")
+ Vector attrs =
+ mediaDescription.getAttributes(true);
+ for (Attribute a : attrs)
+ {
+ try
+ {
+ if (a.getName().equals("crypto"))
+ {
+ hasSdes = true;
+ }
+ }
+ catch (SdpParseException e)
+ {
+ logger.error(
+ "received an unparsable sdp attribute", e);
+ }
+ }
}
}
}
@@ -309,6 +333,18 @@ else if(mediaType.equals(MediaType.AUDIO))
this,
mediaDirections);
}
+
+ if(hasZrtp)
+ {
+ callPeer.getMediaHandler().addAdvertisedEncryptionMethod(
+ SrtpControlType.ZRTP);
+ }
+
+ if(hasSdes)
+ {
+ callPeer.getMediaHandler().addAdvertisedEncryptionMethod(
+ SrtpControlType.SDES);
+ }
}
return callPeer;
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
index 4bff06e2e..cc909dcec 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java
@@ -190,7 +190,7 @@ public Call createCall(Contact callee, CallGroup group)
* for use by other OperationSets willing to initialize
* Calls and willing to control their establishment in ways
* different than {@link #createOutgoingCall(Address,
- * javax.sip.message.Message)}.
+ * javax.sip.message.Message,CallGroup)}.
*
* @return a new outgoing Call with no peers in it
* @throws OperationFailedException if initializing the new outgoing
@@ -623,12 +623,29 @@ && isRemoteControlNotification(request))
|| (responseStatusCodeRange == 5)
|| (responseStatusCodeRange == 6))
{
- logger.error("Received error: " + response.getStatusCode()
- + " " + response.getReasonPhrase());
+ String reason = response.getReasonPhrase();
+
+ WarningHeader warningHeader
+ = (WarningHeader)response.getHeader(WarningHeader.NAME);
+ if(warningHeader != null)
+ {
+ reason = warningHeader.getText();
+
+ logger.error("Received error: " + response.getStatusCode()
+ + " " + response.getReasonPhrase()
+ + " " + warningHeader.getText()
+ + "-" + warningHeader.getAgent()
+ + "-" + warningHeader.getName());
+ }
+ else
+ {
+ logger.error("Received error: " + response.getStatusCode()
+ + " " + response.getReasonPhrase());
+ }
if (callPeer != null)
callPeer.setState(CallPeerState.FAILED,
- response.getReasonPhrase());
+ reason);
processed = true;
}
@@ -1053,7 +1070,56 @@ private void processInvite(SipProvider sourceProvider,
//this is a brand new call (not a transferred one)
CallSipImpl call = new CallSipImpl(this);
- call.processInvite(sourceProvider, serverTransaction);
+ MediaAwareCallPeer peer =
+ call.processInvite(sourceProvider, serverTransaction);
+
+ if(getProtocolProvider().getAccountID()
+ .getAccountPropertyBoolean(
+ ProtocolProviderFactory.MODE_PARANOIA, false)
+ && peer.getMediaHandler()
+ .getAdvertisedEncryptionMethods().length == 0)
+ {
+ // if in paranoia mode and we don't find any encryption
+ // fail peer/call send error with warning explaining why
+
+ peer.setState(
+ CallPeerState.FAILED,
+ "Encryption required!",
+ Response.SESSION_NOT_ACCEPTABLE);
+
+ // 606 Not acceptable
+ // warning header : encryption required
+ WarningHeader warning = null;
+ try
+ {
+ //399 Miscellaneous warning
+ warning = protocolProvider.getHeaderFactory()
+ .createWarningHeader(
+ protocolProvider.getAccountID().getService()
+ , 399, "Encryption required!");
+ }
+ catch(InvalidArgumentException e)
+ {
+ logger.error("Cannot create warning header", e);
+ }
+ catch(ParseException e)
+ {
+ logger.error("Cannot create warning header", e);
+ }
+
+ try
+ {
+ protocolProvider.sayError(serverTransaction,
+ Response.SESSION_NOT_ACCEPTABLE,
+ warning);
+ }
+ catch(OperationFailedException e)
+ {
+ logger.error("Cannot send 606 error!", e);
+ }
+
+ return;
+ }
// checks for auto answering of call, if no further processing
// is needed return
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
index a36fb852c..ad1b254cc 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java
@@ -1938,6 +1938,25 @@ public void sendAck(ClientTransaction clientTransaction)
*/
public void sayError(ServerTransaction serverTransaction, int errorCode)
throws OperationFailedException
+ {
+ sayError(serverTransaction, errorCode, null);
+ }
+
+ /**
+ * Send an error response with the errorCode code using
+ * serverTransaction.
+ *
+ * @param serverTransaction the transaction that we'd like to send an error
+ * response in.
+ * @param errorCode the code that the response should have.
+ *
+ * @throws OperationFailedException if we failed constructing or sending a
+ * SIP Message.
+ */
+ public void sayError(ServerTransaction serverTransaction,
+ int errorCode,
+ Header header)
+ throws OperationFailedException
{
Request request = serverTransaction.getRequest();
Response errorResponse = null;
@@ -1946,6 +1965,9 @@ public void sayError(ServerTransaction serverTransaction, int errorCode)
errorResponse = getMessageFactory().createResponse(
errorCode, request);
+ if(header != null)
+ errorResponse.setHeader(header);
+
//we used to be adding a To tag here and we shouldn't. 3261 says:
//"Dialogs are created through [...] non-failure responses". and
//we are using this method for failure responses only.
diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java
index 990488a1e..90267431d 100644
--- a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java
+++ b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java
@@ -50,6 +50,11 @@ public class SdpUtils
*/
private static final String EXTMAP_ATTR = "extmap";
+ /**
+ * The name of the SDP attribute that defines zrtp hello hash.
+ */
+ public static final String ZRTP_HASH_ATTR = "zrtp-hash";
+
/**
* Parses the specified sdp String into a
* SessionDescription and returns it;
diff --git a/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java b/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java
index 9dfc84b84..2f2f10075 100644
--- a/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java
+++ b/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java
@@ -670,7 +670,7 @@ public static NotificationData fireNotification(String eventType)
/**
* Stops all sounds for the given event type.
*
- * @param notificationData the event type for which we should stop sounds. One of
+ * @param data the event type for which we should stop sounds. One of
* the static event types defined in this class.
*/
public static void stopSound(NotificationData data)
@@ -1200,6 +1200,18 @@ public void securityOn(CallPeerSecurityOnEvent evt)
public void securityOff(CallPeerSecurityOffEvent securityOffEvent)
{}
+ /**
+ * The handler for the security event received. The security event
+ * represents a timeout trying to establish a secure connection.
+ * Most probably the other peer doesn't support it.
+ *
+ * @param securityTimeoutEvent
+ * the security timeout event received
+ */
+ public void securityTimeout(
+ CallPeerSecurityTimeoutEvent securityTimeoutEvent)
+ {}
+
/**
* Processes the received security message.
* @param event the event we received
diff --git a/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java b/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java
index 6671eb2f1..aa82bb388 100644
--- a/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java
+++ b/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java
@@ -50,4 +50,12 @@ public void securityTurnedOn( int sessionType,
public void securityMessageReceived(String message,
String i18nMessage,
int severity);
+
+ /**
+ * Indicates that the other party has timeouted replying to our
+ * offer to secure the connection.
+ *
+ * @param sessionType the type of the call session - audio or video.
+ */
+ public void securityTimeout(int sessionType);
}
diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
index 8f0c042e9..797dea858 100644
--- a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
+++ b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java
@@ -334,6 +334,36 @@ protected void fireCallPeerSecurityOffEvent(CallPeerSecurityOffEvent evt)
}
}
+ /**
+ * Constructs a CallPeerSecurityStatusEvent using this call peer as
+ * source, setting it to be of type eventType and the corresponding
+ * oldValue and newValue.
+ *
+ * @param evt the event object with details to pass on to the consumers
+ */
+ protected void fireCallPeerSecurityTimeoutEvent(
+ CallPeerSecurityTimeoutEvent evt)
+ {
+ lastSecurityEvent = evt;
+
+ if (logger.isDebugEnabled())
+ logger.debug("Dispatching a CallPeerSecurityStatusEvent event to "
+ + callPeerSecurityListeners.size()
+ +" listeners. event is: " + evt.toString());
+
+ List listeners = null;
+ synchronized (callPeerSecurityListeners)
+ {
+ listeners = new ArrayList(
+ callPeerSecurityListeners);
+ }
+
+ for(CallPeerSecurityListener listener : listeners)
+ {
+ listener.securityTimeout(evt);
+ }
+ }
+
/**
* Constructs a CallPeerSecurityStatusEvent using this call peer as
* source, setting it to be of type eventType and the corresponding
diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
index 111e12d32..74fd0d1f1 100644
--- a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
+++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java
@@ -403,6 +403,12 @@ public abstract class ProtocolProviderFactory
*/
public static final String KEEP_ALIVE_INTERVAL = "KEEP_ALIVE_INTERVAL";
+ /**
+ * Paranoia mode when turned on requires all calls to be secure and
+ * indicated as such.
+ */
+ public static final String MODE_PARANOIA = "MODE_PARANOIA";
+
/**
* The BundleContext containing (or to contain) the service
* registration of this factory.
diff --git a/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java
index e55289a04..f48d17d13 100755
--- a/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java
+++ b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java
@@ -43,6 +43,17 @@ public void securityOn(
public void securityOff(
CallPeerSecurityOffEvent securityEvent);
+ /**
+ * The handler for the security event received. The security event
+ * represents a timeout trying to establish a secure connection.
+ * Most probably the other peer doesn't support it.
+ *
+ * @param securityTimeoutEvent
+ * the security timeout event received
+ */
+ public void securityTimeout(
+ CallPeerSecurityTimeoutEvent securityTimeoutEvent);
+
/**
* The handler of the security message event.
*
diff --git a/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java
new file mode 100644
index 000000000..63f546e05
--- /dev/null
+++ b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.service.protocol.event;
+
+import net.java.sip.communicator.service.protocol.*;
+
+/**
+ * The CallPeerSecurityTimeoutEvent is triggered whenever a
+ * communication with a given peer cannot be established, the peer
+ * did not answer our tries to secure the connection.
+ *
+ * @author Damian Minkov
+ */
+public class CallPeerSecurityTimeoutEvent
+ extends CallPeerSecurityStatusEvent
+{
+ /**
+ * Serial version UID.
+ */
+ private static final long serialVersionUID = 0L;
+
+ /**
+ * The event constructor
+ *
+ * @param callPeer the call peer associated with this event
+ * @param sessionType the type of the session, either
+ * {@link CallPeerSecurityStatusEvent#AUDIO_SESSION} or
+ * {@link CallPeerSecurityStatusEvent#VIDEO_SESSION}
+ */
+ public CallPeerSecurityTimeoutEvent( CallPeer callPeer,
+ int sessionType)
+ {
+ super(callPeer, sessionType);
+ }
+}
diff --git a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
index 146bcc4c6..cc8b3e154 100644
--- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
+++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java
@@ -88,6 +88,13 @@ public abstract class CallPeerMediaHandler<
private MediaDirection audioDirectionUserPreference
= MediaDirection.SENDRECV;
+ /**
+ * List of advertised encryption methods. Indicated before establishing the
+ * call.
+ */
+ private List advertisedEncryptionMethods =
+ new ArrayList();
+
/**
* A reference to the CallPeer instance that this handler is managing media
* streams for.
@@ -722,6 +729,29 @@ public boolean isSecure()
return true;
}
+ /**
+ * Returns the advertised methods for securing the call,
+ * this are the methods like SDES, ZRTP that are
+ * indicated in the initial session initialization. Missing here doesn't
+ * mean the other party don't support it.
+ * @return the advertised encryption methods.
+ */
+ public SrtpControlType[] getAdvertisedEncryptionMethods()
+ {
+ return advertisedEncryptionMethods.toArray(
+ new SrtpControlType[advertisedEncryptionMethods.size()]);
+ }
+
+ /**
+ * Adds encryption method to the list of advertised secure methods.
+ * @param encryptionMethod the method to add.
+ */
+ public void addAdvertisedEncryptionMethod(SrtpControlType encryptionMethod)
+ {
+ if(!advertisedEncryptionMethods.contains(encryptionMethod))
+ advertisedEncryptionMethods.add(encryptionMethod);
+ }
+
/**
* Passes multiStreamData to the video stream that we are using
* in this media handler (if any) so that the underlying SRTP lib could
diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java
index 5305988d8..283872040 100644
--- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java
+++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java
@@ -686,6 +686,19 @@ public void securityTurnedOn(int sessionType, String cipher,
fireCallPeerSecurityOnEvent(evt);
}
+ /**
+ * Indicates that the other party has timeouted replying to our
+ * offer to secure the connection.
+ *
+ * @param sessionType the type of the call session - audio or video.
+ */
+ public void securityTimeout(int sessionType)
+ {
+ CallPeerSecurityTimeoutEvent evt =
+ new CallPeerSecurityTimeoutEvent(this, sessionType);
+ fireCallPeerSecurityTimeoutEvent(evt);
+ }
+
/**
* Sets the security status to OFF for this call peer.
*