From ecf96482548bb8e23041d2d55243fdc17a4d0dfb Mon Sep 17 00:00:00 2001 From: Emil Ivov Date: Sun, 1 Feb 2009 23:54:29 +0000 Subject: [PATCH] Fixes un-graceful handling of invalid SDP offers and answers(was crashing when receiving an empty zrtp-hash attribute). This include sending an error response when the SDP offer was corrupt and a BYE request when the problem is inside the SDP answer. Fixes impossibility to close the full screen window after a call has been ended remotely. --- .../gui/main/call/CallParticipantPanel.java | 34 ++++---- .../impl/media/CallSessionImpl.java | 23 +++++- .../OperationSetBasicTelephonySipImpl.java | 78 ++++++++++++------- 3 files changed, 91 insertions(+), 44 deletions(-) diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java index dd52a0c2b..02aebc35f 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallParticipantPanel.java @@ -26,7 +26,7 @@ * The CallParticipantPanel is the panel containing data for a call * participant in a given call. It contains information like call participant * name, photo, call duration, etc. - * + * * @author Yana Stamcheva * @author Lubomir Marinov */ @@ -70,7 +70,7 @@ public class CallParticipantPanel private ZrtpPanel zrtpPanel = null; /** * Creates a CallParticipantPanel for the given call participant. - * + * * @param callManager the CallManager that manages the call * @param callParticipant a call participant */ @@ -164,7 +164,7 @@ private Component createButtonBar( boolean heavyweight, * Creates the Component hierarchy of the central area of this * CallParticipantPanel which displays the photo of the * CallParticipant or the video if any. - * + * * @return the root of the Component hierarchy of the central * area of this CallParticipantPanel which displays the * photo of the CallParticipant or the video if any @@ -215,7 +215,7 @@ public void hierarchyChanged(HierarchyEvent event) * returned Container will track the Componentss * added to and removed from it in order to make sure that * noVideoContainer is displayed as described. - * + * * @param noVideoComponent the predefined default Component to * be displayed in the returned Container when there * is no other Component in it @@ -242,7 +242,7 @@ private Component createNameBar() * Creates the Component hierarchy of the area of * status-related information such as CallParticipant display * name, call duration, security status. - * + * * @return the root of the Component hierarchy of the area of * status-related information such as CallParticipant * display name, call duration, security status @@ -287,7 +287,7 @@ private Component createStatusBar() * Creates a new Component representing a UI means to transfer * the Call of the associated callParticipant or * null if call-transfer is unsupported. - * + * * @return a new Component representing the UI means to * transfer the Call of callParticipant or * null if call-transfer is unsupported @@ -312,7 +312,7 @@ private Component createTransferCallButton() * Creates a new Component representing a UI means to secure * the Call of the associated callParticipant or * null if secure call is unsupported. - * + * * @return a new Component representing the UI means to secure * the Call of callParticipant or * null if secure call is unsupported @@ -518,7 +518,7 @@ private void removeVideoListener() /** * When a video is added or removed for the callParticipant, * makes sure to display or hide it respectively. - * + * * @param event a VideoEvent describing the added visual * Component representing video and the provider it * was added into or null if such information is not @@ -608,7 +608,7 @@ public void run() /** * Sets the state of the contained call participant by specifying the * state name and icon. - * + * * @param state the state of the contained call participant * @param icon the icon of the state */ @@ -659,7 +659,7 @@ public void actionPerformed(ActionEvent e) * example if we receive a call, the call start time is when the call is * received and the conversation start time would be when we accept the * call. - * + * * @return the start time of the contained participant call */ public Date getCallStartTime() @@ -669,7 +669,7 @@ public Date getCallStartTime() /** * Returns the duration of the contained participant call. - * + * * @return the duration of the contained participant call */ public Date getCallDuration() @@ -680,7 +680,7 @@ public Date getCallDuration() /** * Returns this call type - GuiCallParticipantRecord: INCOMING_CALL or * OUTGOING_CALL - * + * * @return Returns this call type : INCOMING_CALL or OUTGOING_CALL */ public String getCallType() @@ -695,7 +695,7 @@ public String getCallType() * Sets the type of the call. Call type could be * GuiCallParticipantRecord.INCOMING_CALL or * GuiCallParticipantRecord.INCOMING_CALL. - * + * * @param callType the type of call to set */ public void setCallType(String callType) @@ -705,7 +705,7 @@ public void setCallType(String callType) /** * Returns the name of the participant, contained in this panel. - * + * * @return the name of the participant, contained in this panel */ public String getParticipantName() @@ -852,7 +852,11 @@ public void windowStateChanged(WindowEvent event) private void exitFullScreen(Window fullScreenWindow) { - getGraphicsConfiguration().getDevice().setFullScreenWindow(null); + GraphicsConfiguration gConfig = getGraphicsConfiguration(); + + if(gConfig != null) + gConfig.getDevice().setFullScreenWindow(null); + if (fullScreenWindow != null) { if (fullScreenWindow.isVisible()) diff --git a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java index 46e557f64..235342934 100644 --- a/src/net/java/sip/communicator/impl/media/CallSessionImpl.java +++ b/src/net/java/sip/communicator/impl/media/CallSessionImpl.java @@ -500,7 +500,19 @@ private boolean stopStreaming(RTPManager rtpManager) stoppedAtLeastOneStream = true; } - Vector receiveStreams = rtpManager.getReceiveStreams(); + Vector receiveStreams; + try + { + receiveStreams = rtpManager.getReceiveStreams(); + } + //it appears that in early call states, when there are no streams + //this method could throug a null pointer exception. Make sure we handle + //it gracefully + catch (Exception e) + { + logger.trace("Failed to retrieve receive streams", e); + receiveStreams = new Vector(); + } Iterator rsIter = receiveStreams.iterator(); while(rsIter.hasNext()) { @@ -1040,6 +1052,15 @@ public String processSdpOffer(CallParticipant offerer, String sdpOfferStr) + ex.getMessage() , ex.getCharOffset()); } + //it appears that in some cases parsing could also fail with + //other exceptions such as a NullPointerException for example so make + //sure we get those too. + catch(Exception ex) + { + throw new ParseException("Failed to parse SDPOffer: " + + ex.getMessage() + , 0); + } //create an sdp answer. SessionDescription sdpAnswer = createSessionDescription(sdpOffer, null); 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 6a5aef533..f3387d7a8 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -933,6 +933,7 @@ private void processSessionProgress(ClientTransaction clientTransaction, "There was an error parsing the SDP description of " + callParticipant.getDisplayName() + "(" + callParticipant.getAddress() + ")", exc, callParticipant); + return; } catch (MediaException exc) { @@ -941,6 +942,7 @@ private void processSessionProgress(ClientTransaction clientTransaction, + callParticipant.getDisplayName() + "(" + callParticipant.getAddress() + ")" + ". Error was: " + exc.getMessage(), exc, callParticipant); + return; } // set the call url in case there was one @@ -1046,6 +1048,7 @@ private void processInviteOK(ClientTransaction clientTransaction, logErrorAndFailCallParticipant( "Failed to create a content type header for the ACK request", ex, callParticipant); + return; } catch (InvalidArgumentException ex) { @@ -1053,6 +1056,7 @@ private void processInviteOK(ClientTransaction clientTransaction, logErrorAndFailCallParticipant( "Failed ACK request, problem with the supplied cseq", ex, callParticipant); + return; } catch (SipException ex) { @@ -1132,20 +1136,27 @@ private void processInviteOK(ClientTransaction clientTransaction, */ callParticipant.setCallInfoURL(callSession.getCallInfoURL()); } - catch (ParseException exc) - { - logErrorAndFailCallParticipant( - "There was an error parsing the SDP description of " - + callParticipant.getDisplayName() + "(" - + callParticipant.getAddress() + ")", exc, callParticipant); - } - catch (MediaException exc) - { - logErrorAndFailCallParticipant( - "We failed to process the SDP description of " - + callParticipant.getDisplayName() + "(" - + callParticipant.getAddress() + ")" + ". Error was: " - + exc.getMessage(), exc, callParticipant); + //at this point we have already sent our ack so in adition to logging + //an error we also need to hangup the call participant. + catch (Exception exc)//Media or parse exception. + { + logger.error("There was an error parsing the SDP description of " + + callParticipant.getDisplayName() + "(" + + callParticipant.getAddress() + ")", exc); + try{ + //we are connected from a SIP point of view (cause we sent our + //ack) sp make sure we set the state accordingly or the hangup + //method won't know how to end the call. + callParticipant.setState(CallParticipantState.CONNECTED); + hangupCallParticipant(callParticipant); + } + catch (Exception e){ + //I don't see what more we could do. + logger.error(e); + callParticipant.setState(CallParticipantState.FAILED, + e.getMessage()); + } + return; } // change status @@ -1242,6 +1253,7 @@ private void processAuthenticationChallenge( logErrorAndFailCallParticipant( "We failed to authenticate an INVITE request.", exc, callParticipant); + return; } } @@ -1990,12 +2002,14 @@ void processCancel(ServerTransaction serverTransaction, logErrorAndFailCallParticipant( "Failed to create an OK Response to an CANCEL request.", ex, callParticipant); + return; } catch (Exception ex) { logErrorAndFailCallParticipant( "Failed to send an OK Response to an CANCEL request.", ex, callParticipant); + return; } try { @@ -2609,7 +2623,8 @@ public void sayInternalError(CallParticipantSipImpl callParticipant) * @throws OperationFailedException if we failed constructing or sending a * SIP Message. */ - public void sayError(CallParticipantSipImpl callParticipant, int errorCode) + public void sayError(CallParticipantSipImpl callParticipant, + int errorCode) throws OperationFailedException { Dialog dialog = callParticipant.getDialog(); @@ -2634,13 +2649,13 @@ public void sayError(CallParticipantSipImpl callParticipant, int errorCode) } ServerTransaction serverTransaction = (ServerTransaction) transaction; - Response internalError = null; + Response errorResponse = null; try { - internalError = + errorResponse = protocolProvider.getMessageFactory().createResponse(errorCode, callParticipant.getFirstTransaction().getRequest()); - protocolProvider.attachToTag(internalError, dialog); + protocolProvider.attachToTag(errorResponse, dialog); } catch (ParseException ex) { @@ -2651,12 +2666,12 @@ public void sayError(CallParticipantSipImpl callParticipant, int errorCode) ContactHeader contactHeader = protocolProvider .getContactHeader(dialog.getRemoteTarget()); - internalError.addHeader(contactHeader); + errorResponse.addHeader(contactHeader); try { - serverTransaction.sendResponse(internalError); + serverTransaction.sendResponse(errorResponse); if (logger.isDebugEnabled()) - logger.debug("sent response: " + internalError); + logger.debug("sent response: " + errorResponse); } catch (Exception ex) { @@ -2913,19 +2928,26 @@ public synchronized void answerCallParticipant(CallParticipant participant) } catch (MediaException ex) { + //log, the error and tell the remote party. do not throw an + //exception as it would go to the stack and there's nothing it could + //do with it. + logger.error( + "Failed to created an SDP description for an ok response " + + "to an INVITE request!", + ex); this.sayError((CallParticipantSipImpl) participant, Response.NOT_ACCEPTABLE_HERE); - throwOperationFailedException( - "Failed to created an SDP description for an ok response " - + "to an INVITE request!", - OperationFailedException.INTERNAL_ERROR, ex); } catch (ParseException ex) { - callParticipant.setState(CallParticipantState.DISCONNECTED); - throwOperationFailedException( + //log, the error and tell the remote party. do not throw an + //exception as it would go to the stack and there's nothing it could + //do with it. + logger.error( "Failed to parse sdp data while creating invite request!", - OperationFailedException.INTERNAL_ERROR, ex); + ex); + this.sayError((CallParticipantSipImpl) participant, + Response.NOT_ACCEPTABLE_HERE); } ContactHeader contactHeader = protocolProvider.getContactHeader(