From 57d5c4961eb26a46e9c88a86240ad8d83605765d Mon Sep 17 00:00:00 2001 From: Emil Ivov Date: Mon, 28 Sep 2009 10:03:10 +0000 Subject: [PATCH] Centralizes use of Contact header generation. Moves parts of incoming call handling to CallSipImpl. --- .../impl/protocol/sip/CallSipImpl.java | 159 +++++++++++++----- .../OperationSetBasicTelephonySipImpl.java | 66 ++++---- .../sip/ProtocolProviderServiceSipImpl.java | 10 +- .../impl/protocol/sip/SipMessageFactory.java | 62 ++++++- 4 files changed, 208 insertions(+), 89 deletions(-) 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 268697f6c..7444519d0 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java @@ -12,7 +12,7 @@ import javax.sip.*; import javax.sip.address.*; -import javax.sip.address.URI; +import javax.sip.address.URI;//disambiguates java.net.URI import javax.sip.header.*; import javax.sip.message.*; @@ -450,6 +450,60 @@ private CallPeerSipImpl createCallPeerFor( return callPeer; } + + /** + * Processes an incoming INVITE that is meant to replace an existing + * CallPeerSipImpl that is participating in this call. Typically + * this would happen as a result of an attended transfer. + * + * @param jainSipProvider the JAIN-SIP SipProvider that received + * the request. + * @param serverTransaction the transaction containing the INVITE request. + * @param callPeerToReplace a reference to the CallPeer that this + * INVITE is trying to replace. + */ + public void processReplacingInvite(SipProvider jainSipProvider, + ServerTransaction serverTransaction, + CallPeerSipImpl callPeerToReplace) + { + Request request = serverTransaction.getRequest(); + CallPeerSipImpl newCallPeer + = createCallPeerFor(serverTransaction, jainSipProvider); + try + { + answerCallPeer(newCallPeer); + } + catch (OperationFailedException ex) + { + logger.error( + "Failed to auto-answer the referred call peer " + + newCallPeer, ex); + /* + * RFC 3891 says an appropriate error response MUST be returned + * and callPeerToReplace must be left unchanged. + */ + //TODO should we send a response here? + return; + } + + + + //we just accepted the new peer and if we got here then it went well + //now let's hangup the other call. + try + { + hangupCallPeer(callPeerToReplace); + } + catch (OperationFailedException ex) + { + logger.error("Failed to hangup the referer " + + callPeerToReplace, ex); + callPeerToReplace.setState( + CallPeerState.FAILED, "Internal Error: " + ex); + } + + } + /** * Creates a new call and sends a RINGING response. * @@ -460,62 +514,87 @@ private CallPeerSipImpl createCallPeerFor( public CallPeerSipImpl processInvite(SipProvider jainSipProvider, ServerTransaction serverTransaction) { - Request request = serverTransaction.getRequest(); + Request invite = serverTransaction.getRequest(); CallPeerSipImpl peer = createCallPeerFor(serverTransaction, jainSipProvider); - return peer; - } - - public void processReInvite(SipProvider jainSipProvider, - ServerTransaction serverTransaction) - { - Request request = serverTransaction.getRequest(); - - } - - public void processReplacingInvite(SipProvider jainSipProvider, - ServerTransaction serverTransaction) - { - Request request = serverTransaction.getRequest(); + // extract the SDP description. + // beware: SDP description may be in ACKs so it could be that there's + // nothing here - bug report Laurent Michel + ContentLengthHeader cl = invite.getContentLength(); + if (cl != null && cl.getContentLength() > 0) + { + peer.setSdpDescription(new String(invite.getRawContent())); + } - if ((statusCode == Response.OK) && (callPeerToReplace != null)) + //send a ringing reply + Response response = null; + try { - boolean sayBye = false; + response = this.messageFactory.createResponse(Response.RINGING, invite); - try - { - answerCallPeer(callPeer); - sayBye = true; - } - catch (OperationFailedException ex) - { - logger.error( - "Failed to auto-answer the referred call peer " - + callPeer, ex); - /* - * RFC 3891 says an appropriate error response MUST be returned - * and callPeerToReplace must be left unchanged. - */ - } - if (sayBye) + //set the contact header + response.setHeader(getProtocolProvider() + .getContactHeaderForResponse(invite)); + + //add the SDP + if (statusCode == Response.OK) { try { - hangupCallPeer(callPeerToReplace); + attachSDP(callPeer, response); } catch (OperationFailedException ex) { - logger.error("Failed to hangup the referer " - + callPeerToReplace, ex); - callPeerToReplace.setState( - CallPeerState.FAILED, "Internal Error: " + ex); + logger.error("Error while trying to send response " + + response, ex); + callPeer.setState(CallPeerState.FAILED, + "Internal Error: " + ex.getMessage()); + return; } } - // Even if there was a failure, we cannot just send Response.OK. + } + catch (ParseException ex) + { + logger.error("Error while trying to send a response", ex); + callPeer.setState(CallPeerState.FAILED, + "Internal Error: " + ex.getMessage()); return; } + try + { + logger.trace("will send " + statusCode + " response: "); + serverTransaction.sendResponse(response); + logger.debug("sent a " + statusCode + " response: " + + response); + } + catch (Exception ex) + { + logger.error("Error while trying to send a request", ex); + callPeer.setState(CallPeerState.FAILED, + "Internal Error: " + ex.getMessage()); + return; + } + + if (statusCode == Response.OK) + { + try + { + setMediaFlagsForPeer(callPeer, response); + } + catch (OperationFailedException ex) + { + logger.error("Error after sending response " + response, ex); + } + } + return peer; + } + + public void processReInvite(SipProvider jainSipProvider, + ServerTransaction serverTransaction) + { + Request request = serverTransaction.getRequest(); } } 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 3df47e5e9..8b7fb8d8c 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -277,11 +277,11 @@ else if (on) * specified call peer with the sent invite * @throws OperationFailedException */ - void sendInviteRequest(CallPeerSipImpl sipPeer, - String sdpOffer) throws OperationFailedException + void sendInviteRequest(CallPeerSipImpl sipPeer, String sdpOffer) + throws OperationFailedException { Dialog dialog = sipPeer.getDialog(); - Request invite = createRequest(dialog, Request.INVITE); + Request invite = messageFactory.createRequest(dialog, Request.INVITE); try { @@ -311,8 +311,10 @@ void sendInviteRequest(CallPeerSipImpl sipPeer, * request is to be sent * @throws OperationFailedException */ - private void sendRequest(SipProvider sipProvider, Request request, - Dialog dialog) throws OperationFailedException + private void sendRequest(SipProvider sipProvider, + Request request, + Dialog dialog) + throws OperationFailedException { ClientTransaction clientTransaction = null; try @@ -1223,6 +1225,7 @@ private void processInvite(SipProvider sourceProvider, } else { + //this is a reINVITE. callSipImpl.processReInvite(sourceProvider, serverTransaction); } @@ -1244,7 +1247,7 @@ private void originalProcessInvite(SipProvider sourceProvider, Dialog dialog = serverTransaction.getDialog(); CallPeerSipImpl callPeer = activeCallsRepository.findCallPeer(dialog); - int statusCode; + int statusCode = 0; CallPeerSipImpl callPeerToReplace = null; if (callPeer == null) @@ -1288,29 +1291,24 @@ private void originalProcessInvite(SipProvider sourceProvider, // Send statusCode //... Response response = null; - try { response = protocolProvider.getMessageFactory() .createResponse(statusCode, invite); - protocolProvider.attachToTag(response, dialog); // set our display name ((ToHeader) response.getHeader(ToHeader.NAME)).getAddress() .setDisplayName(protocolProvider.getOurDisplayName()); - // extract our intended destination which should be in the from - Address callerAddress = - ((FromHeader) response.getHeader(FromHeader.NAME)) - .getAddress(); response.setHeader(protocolProvider - .getContactHeader(callerAddress)); + .getContactHeaderForResponse(invite)); + //add the SDP if (statusCode == Response.OK) { try { - processInviteSendingResponse(callPeer, response); + attachSDP(callPeer, response); } catch (OperationFailedException ex) { @@ -1331,9 +1329,9 @@ private void originalProcessInvite(SipProvider sourceProvider, } try { - logger.trace("will send " + statusCodeString + " response: "); + logger.trace("will send " + statusCode + " response: "); serverTransaction.sendResponse(response); - logger.debug("sent a " + statusCodeString + " response: " + logger.debug("sent a " + statusCode + " response: " + response); } catch (Exception ex) @@ -1348,7 +1346,7 @@ private void originalProcessInvite(SipProvider sourceProvider, { try { - processInviteSentResponse(callPeer, response); + setMediaFlagsForPeer(callPeer, response); } catch (OperationFailedException ex) { @@ -1370,8 +1368,8 @@ private void originalProcessInvite(SipProvider sourceProvider, * @throws OperationFailedException * @throws ParseException */ - private void processInviteSendingResponse(CallPeer peer, - Response response) throws OperationFailedException, ParseException + private void attachSDP(CallPeer peer, Response response) + throws OperationFailedException, ParseException { /* * At the time of this writing, we're only getting called because a @@ -1416,8 +1414,8 @@ private void processInviteSendingResponse(CallPeer peer, * @throws OperationFailedException * @throws ParseException */ - private void processInviteSentResponse(CallPeer peer, - Response response) throws OperationFailedException + private void setMediaFlagsForPeer(CallPeer peer, Response response) + throws OperationFailedException { /* * At the time of this writing, we're only getting called because a @@ -1665,11 +1663,9 @@ private void processCancel(ServerTransaction serverTransaction, ServerTransaction inviteTran = (ServerTransaction) tran; Request invite = callPeer.getFirstTransaction().getRequest(); - Response requestTerminated = - protocolProvider.getMessageFactory().createResponse( - Response.REQUEST_TERMINATED, invite); - protocolProvider.attachToTag(requestTerminated, callPeer - .getDialog()); + Response requestTerminated = protocolProvider.getMessageFactory() + .createResponse(Response.REQUEST_TERMINATED, invite); + inviteTran.sendResponse(requestTerminated); if (logger.isDebugEnabled()) logger.debug("sent request terminated response:\n" @@ -1724,10 +1720,8 @@ private void processRefer(ServerTransaction serverTransaction, Response accepted = null; try { - accepted = - protocolProvider.getMessageFactory().createResponse( + accepted = protocolProvider.getMessageFactory().createResponse( Response.ACCEPTED, referRequest); - protocolProvider.attachToTag(accepted, dialog); } catch (ParseException ex) { @@ -2069,7 +2063,7 @@ private void sendReferNotifyRequest(Dialog dialog, String subscriptionState, String reasonCode, Object content, SipProvider sipProvider) throws OperationFailedException { - Request notify = createRequest(dialog, Request.NOTIFY); + Request notify = messageFactory.createRequest(dialog, Request.NOTIFY); HeaderFactory headerFactory = protocolProvider.getHeaderFactory(); // Populate the request. @@ -2088,8 +2082,8 @@ private void sendReferNotifyRequest(Dialog dialog, SubscriptionStateHeader ssHeader = null; try { - ssHeader = - headerFactory.createSubscriptionStateHeader(subscriptionState); + ssHeader = headerFactory + .createSubscriptionStateHeader(subscriptionState); if (reasonCode != null) ssHeader.setReasonCode(reasonCode); } @@ -2105,8 +2099,8 @@ private void sendReferNotifyRequest(Dialog dialog, ContentTypeHeader ctHeader = null; try { - ctHeader = - headerFactory.createContentTypeHeader("message", "sipfrag"); + ctHeader = headerFactory + .createContentTypeHeader("message", "sipfrag"); } catch (ParseException ex) { @@ -2379,8 +2373,7 @@ private void sayBusyHere(CallPeerSipImpl callPeer) try { busyHere = messageFactory.createResponse( - Response.BUSY_HERE, request); - protocolProvider.attachToTag(busyHere, callPeer.getDialog()); + Response.BUSY_HERE, request); } catch (ParseException ex) { @@ -2571,7 +2564,6 @@ private Response createOKResponse(Request request, Dialog containingDialog) throws ParseException { Response ok = messageFactory.createResponse(Response.OK, request); - protocolProvider.attachToTag(ok, containingDialog); return ok; } 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 f847d4323..bdd7dcbfe 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -1126,12 +1126,14 @@ public ContactHeader getContactHeader(SipURI intendedDestination) Address contactAddress = addressFactory.createAddress( contactURI ); + String ourDisplayName = getOurDisplayName(); if (ourDisplayName != null) { contactAddress.setDisplayName(ourDisplayName); } registrationContactHeader = headerFactory.createContactHeader( contactAddress); + logger.debug("generated contactHeader:" + registrationContactHeader); } @@ -2000,8 +2002,9 @@ public UserAgentHeader getSipCommUserAgentHeader() * Generates a ToTag and attaches it to the to header of response. * * @param response the response that is to get the ToTag. - * @param containingDialog the Dialog instance that is to extract a unique - * Tag value (containingDialog.hashCode()) + * @param containingDialog the Dialog instance that the response + * would be sent in or null if we are not aware of the + * Dialog when calling this method. */ public void attachToTag(Response response, Dialog containingDialog) { @@ -2011,7 +2014,8 @@ public void attachToTag(Response response, Dialog containingDialog) return; } - if(containingDialog.getLocalTag() != null) + if( containingDialog != null + && containingDialog.getLocalTag() != null) { logger.debug("We seem to already have a tag in this dialog. " +"Returning"); diff --git a/src/net/java/sip/communicator/impl/protocol/sip/SipMessageFactory.java b/src/net/java/sip/communicator/impl/protocol/sip/SipMessageFactory.java index 64921abc9..d08a4ef85 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/SipMessageFactory.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/SipMessageFactory.java @@ -6,6 +6,7 @@ */ package net.java.sip.communicator.impl.protocol.sip; +import gov.nist.javax.sip.header.*; import gov.nist.javax.sip.header.extensions.*; import java.text.*; @@ -253,6 +254,27 @@ private Message attachScSpecifics(Message message) SipApplicationData.setApplicationData(message, SipApplicationData.KEY_SERVICE, this.protocolProvider); + //the jain-sip semantics allow the application to "forget" attaching a + //To tag to a response so let's make sure we do this here + if(message instanceof Response) + { + FromHeader from = (FromHeader)message.getHeader(From.NAME); + String fromTag = (from == null) ? null : from.getTag(); + Response response = (Response)message; + + //if there's a from tag and this is a non-failure response, + //then we are adding a to tag. + if(fromTag != null && fromTag.trim().length() > 0 + && response.getStatusCode() > 100 + && response.getStatusCode() < 300) + { + protocolProvider.attachToTag(response, null); + } + } + + //add a contact header. + attachContactHeader(message); + // User Agent UserAgentHeader userAgentHeader = protocolProvider.getSipCommUserAgentHeader(); @@ -267,7 +289,26 @@ private Message attachScSpecifics(Message message) return message; } - //---------------- higher level methods ---------------------------------- + /** + * The method tries to determine an address that would be reachable by the + * destination of message and then creates a ContactHeader + * out of it and attaches it to the message. The method takes into + * account both Requests and Responses. The method + * is meant to be used only for messages that have been otherwise + * initialized (in particular the Request URI in requests or the Via + * headers in responses.). + * + * @param message the message that we'd like to attach a + * ContactHeader to. + * + * @return a reference to the message that was also passed as + * a parameter in order to allow for more agility when using the method. + */ + private Message attachContactHeader(Message message) + { + //creates a Contact header + } + /** * Creates a new {@link Request} of a specific method which is to be sent in * a specific Dialog and populates its generally-necessary @@ -298,8 +339,6 @@ public Request createRequest(Dialog dialog, String method) OperationFailedException.INTERNAL_ERROR, ex, logger); } - attachScSpecifics(request); - //override the via and contact headers as jain-sip is generating one //from the listening point which is 0.0.0.0 or ::0 ArrayList viaHeaders @@ -309,22 +348,28 @@ public Request createRequest(Dialog dialog, String method) request.setHeader(protocolProvider .getContactHeader(dialog.getRemoteParty())); + //now that we've set the Via headers right, we can attach our SC + //specifics + attachScSpecifics(request); + // We have a dialog here so let's try and pre-authenticate the request. preAuthenticateRequest(request); return request; } + //---------------- higher level methods ---------------------------------- /** * Creates an invite request destined for callee. * * @param toAddress the sip address of the callee that the request is meant - * for. + * for. + * + * @return a newly created sip Request destined for + * callee. * - * @return a newly created sip Request destined for callee - * . * @throws OperationFailedException with the corresponding code if creating - * the request fails. + * the request fails. * @throws IllegalArgumentException if toAddress does not appear * to be a valid destination. */ @@ -372,8 +417,7 @@ public Request createInviteRequest( Address toAddress) try { // FromHeader - fromHeader = - headerFactory.createFromHeader( + fromHeader = headerFactory.createFromHeader( protocolProvider.getOurSipAddress(toAddress), localTag); // ToHeader