throw new NullPointerException("null response");
try {
sipResponse.checkHeaders();
} catch (ParseException ex) {
throw new SipException(ex.getMessage());
}
// check for meaningful response.
if (!sipResponse.getCSeq().getMethod().equals(this.getMethod())) {
throw new SipException(
"CSeq method does not match Request method of request that created the tx.");
}
/*
* 200-class responses to SUBSCRIBE requests also MUST contain an "Expires" header. The
* period of time in the response MAY be shorter but MUST NOT be longer than specified in
* the request.
*/
if (this.getMethod().equals(Request.SUBSCRIBE) && response.getStatusCode() / 100 == 2) {
if (response.getHeader(ExpiresHeader.NAME) == null) {
throw new SipException("Expires header is mandatory in 2xx response of SUBSCRIBE");
} else {
Expires requestExpires = (Expires) this.getOriginalRequest().getExpires();
Expires responseExpires = (Expires) response.getExpires();
/*
* If no "Expires" header is present in a SUBSCRIBE request, the implied default
* is defined by the event package being used.
*/
if (requestExpires != null
&& responseExpires.getExpires() > requestExpires.getExpires()) {
throw new SipException(
"Response Expires time exceeds request Expires time : See RFC 3265 3.1.1");
}
}
}
// Check for mandatory header.
if (sipResponse.getStatusCode() == 200
&& sipResponse.getCSeq().getMethod().equals(Request.INVITE)
&& sipResponse.getHeader(ContactHeader.NAME) == null)
throw new SipException("Contact Header is mandatory for the OK to the INVITE");
if (!this.isMessagePartOfTransaction((SIPMessage) response)) {
throw new SipException("Response does not belong to this transaction.");
}
// Fix up the response if the dialog has already been established.
try {
/*
* The UAS MAY send a final response to the initial request before
* having received PRACKs for all unacknowledged reliable provisional responses,
* unless the final response is 2xx and any of the unacknowledged reliable provisional
* responses contained a session description. In that case, it MUST NOT send a final
* response until those provisional responses are acknowledged.
*/
if (this.pendingReliableResponse != null
&& this.getDialog() != null
&& this.getState() != TransactionState.TERMINATED
&& ((SIPResponse)response).getContentTypeHeader() != null
&& response.getStatusCode() / 100 == 2
&& ((SIPResponse)response).getContentTypeHeader().getContentType()
.equalsIgnoreCase("application")
&& ((SIPResponse)response).getContentTypeHeader().getContentSubType()
.equalsIgnoreCase("sdp")) {
if (!interlockProvisionalResponses ) {
throw new SipException("cannot send response -- unacked povisional");
} else {
try {
boolean acquired = this.provisionalResponseSem.tryAcquire(1,TimeUnit.SECONDS);
if (!acquired ) {
throw new SipException("cannot send response -- unacked povisional");
}
} catch (InterruptedException ex) {
sipStack.getStackLogger().logError ("Interrupted acuqiring PRACK sem");
throw new SipException("Cannot aquire PRACK sem");
}
}
} else {
// Sending the final response cancels the
// pending response task.
if (this.pendingReliableResponse != null && sipResponse.isFinalResponse()) {
this.provisionalResponseTask.cancel();
this.provisionalResponseTask = null;
}
}
// Dialog checks. These make sure that the response
// being sent makes sense.
if (dialog != null) {
if (sipResponse.getStatusCode() / 100 == 2
&& sipStack.isDialogCreated(sipResponse.getCSeq().getMethod())) {
if (dialog.getLocalTag() == null && sipResponse.getTo().getTag() == null) {
// Trying to send final response and user forgot to set
// to
// tag on the response -- be nice and assign the tag for
// the user.
sipResponse.getTo().setTag(Utils.getInstance().generateTag());
} else if (dialog.getLocalTag() != null && sipResponse.getToTag() == null) {
if ( sipStack.getStackLogger().isLoggingEnabled(LogWriter.TRACE_DEBUG)) {
sipStack.getStackLogger().logDebug("assigning toTag : serverTransaction = " + this + " dialog "
+ dialog + " tag = " + dialog.getLocalTag());
}
sipResponse.setToTag(dialog.getLocalTag());
} else if (dialog.getLocalTag() != null && sipResponse.getToTag() != null
&& !dialog.getLocalTag().equals(sipResponse.getToTag())) {
throw new SipException("Tag mismatch dialogTag is "
+ dialog.getLocalTag() + " responseTag is "
+ sipResponse.getToTag());
}
}
if (!sipResponse.getCallId().getCallId().equals(dialog.getCallId().getCallId())) {
throw new SipException("Dialog mismatch!");
}
}
// Backward compatibility slippery slope....
// Only set the from tag in the response when the
// incoming request has a from tag.
String fromTag = ((SIPRequest) this.getRequest()).getFrom().getTag();
if (fromTag != null && sipResponse.getFromTag() != null
&& !sipResponse.getFromTag().equals(fromTag)) {
throw new SipException("From tag of request does not match response from tag");
} else if (fromTag != null) {
sipResponse.getFrom().setTag(fromTag);
} else {
if (sipStack.isLoggingEnabled(LogWriter.TRACE_DEBUG))
sipStack.getStackLogger().logDebug("WARNING -- Null From tag in request!!");
}
// See if the dialog needs to be inserted into the dialog table
// or if the state of the dialog needs to be changed.
if (dialog != null && response.getStatusCode() != 100) {
dialog.setResponseTags(sipResponse);
DialogState oldState = dialog.getState();
dialog.setLastResponse(this, (SIPResponse) response);
if (oldState == null && dialog.getState() == DialogState.TERMINATED) {
DialogTerminatedEvent event = new DialogTerminatedEvent(dialog
.getSipProvider(), dialog);
// Provide notification to the listener that the dialog has
// ended.
dialog.getSipProvider().handleEvent(event, this);
}
} else if (dialog == null && this.getMethod().equals(Request.INVITE)
&& this.retransmissionAlertEnabled
&& this.retransmissionAlertTimerTask == null
&& response.getStatusCode() / 100 == 2) {
String dialogId = ((SIPResponse) response).getDialogId(true);
this.retransmissionAlertTimerTask = new RetransmissionAlertTimerTask(dialogId);
sipStack.retransmissionAlertTransactions.put(dialogId, this);
sipStack.getTimer().schedule(this.retransmissionAlertTimerTask, 0,
SIPTransactionStack.BASE_TIMER_INTERVAL);
}
// Send message after possibly inserting the Dialog
// into the dialog table to avoid a possible race condition.
this.sendMessage((SIPResponse) response);
if ( dialog != null ) {
dialog.startRetransmitTimer(this, (SIPResponse)response);
}
} catch (IOException ex) {
if (sipStack.isLoggingEnabled())
sipStack.getStackLogger().logException(ex);
this.setState(TransactionState.TERMINATED);
raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
throw new SipException(ex.getMessage());
} catch (java.text.ParseException ex1) {
if (sipStack.isLoggingEnabled())
sipStack.getStackLogger().logException(ex1);
this.setState(TransactionState.TERMINATED);
throw new SipException(ex1.getMessage());
}
}