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 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. *