/** * Copyright 2010 Voxeo Corporation Licensed under the Apache License, Version * 2.0 (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.voxeo.moho.sip; import java.io.IOException; import java.util.Map; import javax.media.mscontrol.join.Joinable.Direction; import javax.servlet.sip.SipServletRequest; import javax.servlet.sip.SipServletResponse; import org.apache.log4j.Logger; import com.voxeo.moho.Participant.JoinType; import com.voxeo.moho.event.JoinCompleteEvent; import com.voxeo.moho.sip.SIPCall.State; import com.voxeo.moho.util.SDPUtils; public class DirectNI2NOJoinDelegate extends JoinDelegate { private static final Logger LOG = Logger.getLogger(DirectAI2NOJoinDelegate.class); protected Direction _direction; protected SipServletResponse _response; protected byte[] _latestCall2SDP; protected boolean call1Processed; protected SipServletResponse _waitingPrackResponse; protected boolean _call1No100Rel; protected boolean updatedCall1Success; protected boolean call2Processed; protected DirectNI2NOJoinDelegate(final SIPIncomingCall call1, final SIPOutgoingCall call2, final Direction direction, final SIPCallImpl peer) { _call1 = call1; _call2 = call2; _direction = direction; _peer = peer; } @Override public void doJoin() throws Exception { super.doJoin(); if(!SIPHelper.support100rel(_call1.getSipInitnalRequest())) { _call1No100Rel =true; SIPHelper.remove100relSupport(_call2.getSipInitnalRequest()); } ((SIPOutgoingCall) _call2).setContinueRouting(_call1); if (_call1.getSIPCallState() == SIPCall.State.PROGRESSED) { call1Processed = true; ((SIPOutgoingCall) _call2).call(null, _call1.getSipSession().getApplicationSession()); } else { ((SIPOutgoingCall) _call2).call(_call1.getRemoteSdp(), _call1.getSipSession().getApplicationSession(), _call1.useReplacesHeader()); } } @Override protected void doUpdate(SipServletRequest req, SIPCallImpl call, Map<String, String> headers) throws Exception { if (_call2.equals(call)) { if (SIPHelper.getRawContentWOException(req) != null) { _latestCall2SDP = req.getRawContent(); } if(_call1No100Rel) { SipServletResponse updateResp = req.createResponse(200); SIPHelper.copyContent(_call1.getSipInitnalRequest(), updateResp); updateResp.send(); } else { SIPHelper.sendSubsequentRequest(_call1.getSipSession(), req, headers); } } else { LOG.error("Can't process UPDATE request:" + req); } } @Override protected void doUpdateResponse(SipServletResponse resp, SIPCallImpl call, Map<String, String> headers) throws Exception { if (_call1.equals(call)) { if(resp.getStatus() == SipServletResponse.SC_OK) { updatedCall1Success = true; } if(_waitingPrackResponse != null) { SipServletResponse prackResponse = _waitingPrackResponse; _waitingPrackResponse = null; if(resp.getStatus() == SipServletResponse.SC_OK) { final SipServletRequest newReq = prackResponse.createPrack(); SIPHelper.addHeaders(newReq, headers); SIPHelper.copyContent(resp, newReq); newReq.send(); } else { SipServletRequest prack = prackResponse.createPrack(); prack.setContent(SDPUtils.formulateSDP(_call2, _call1.getRemoteSdp()), "application/sdp"); prack.send(); } _call1.destroyNetworkConnection(false); } else { SIPHelper.relayResponse(resp); } } else { LOG.error("Can't process UPDATE response, discarding it:" + resp); } } @Override protected void doPrack(SipServletRequest req, SIPCallImpl call, Map<String, String> headers) throws Exception { if (_call1.equals(call) && _waitingPrackResponse != null) { SipServletResponse prackResponse = _waitingPrackResponse; _waitingPrackResponse = null; SIPHelper.relayPrack(prackResponse, req, headers); } else { LOG.warn("Can't process PRACK, send back 200OK response:" + req); req.createResponse(200).send(); } } @Override protected void doPrackResponse(SipServletResponse resp, SIPCallImpl call, Map<String, String> headers) throws Exception { if (_call2.equals(call)) { SIPHelper.relayResponse(resp); } else { LOG.warn("Didn't process PRACK response, discarding it:" + resp); } } @Override protected void doInviteResponse(final SipServletResponse res, final SIPCallImpl call, final Map<String, String> headers) throws Exception { try { if (_call2.equals(call)) { if (SIPHelper.isSuccessResponse(res) || SIPHelper.isProvisionalResponse(res)) { _response = res; if (SIPHelper.getRawContentWOException(res) != null) { _latestCall2SDP = res.getRawContent(); if(SIPHelper.isProvisionalResponse(res) && SIPHelper.needPrack(res)) { call2Processed = true; } } if((call1Processed || _call1No100Rel) && SIPHelper.isProvisionalResponse(res)) { if(SIPHelper.getRawContentWOException(res) != null && SIPHelper.needPrack(res)) { if(call1Processed) { if(_waitingPrackResponse != null && _waitingPrackResponse.getHeader("RSeq").trim().equalsIgnoreCase(res.getHeader("RSeq").trim())) { return; } _waitingPrackResponse = res; SipServletRequest updateCall1Req = _call1.getSipSession().createRequest("UPDATE"); updateCall1Req.setContent(SDPUtils.formulateSDP(_call1, res.getRawContent()), "application/sdp"); updateCall1Req.send(); } else { SipServletRequest prack = res.createPrack(); prack.setContent(SDPUtils.formulateSDP(_call2, _call1.getRemoteSdp()), "application/sdp"); prack.send(); relayUnreliableProvisionalResponse(res); } } else { SIPHelper.trySendPrack(res); if(!call1Processed) { relayUnreliableProvisionalResponse(res); } } } else { final SipServletResponse newRes = _call1.getSipInitnalRequest().createResponse(res.getStatus(), res.getReasonPhrase()); // TODO should do this at container? if (res.getStatus() == SipServletResponse.SC_SESSION_PROGRESS || res.getStatus() == SipServletResponse.SC_OK) { SIPHelper.copyPandXHeaders(res, newRes); } if(SIPHelper.isProvisionalResponse(res) && SIPHelper.needPrack(res)) { try{ if(_waitingPrackResponse != null && _waitingPrackResponse.getHeader("RSeq").trim().equalsIgnoreCase(res.getHeader("RSeq").trim())) { return; } _waitingPrackResponse = res; SIPHelper.copyContent(res, newRes); newRes.sendReliably(); } catch(Exception ex) { LOG.warn("Got exception when trying send 183 reliably. trying send back PRACK.", ex); res.createPrack().send(); newRes.send(); } } else { if(res.getStatus() == SipServletResponse.SC_OK && SIPHelper.getRawContentWOException(newRes) == null && !call1Processed) { newRes.setContent(SDPUtils.formulateSDP(_call1, _latestCall2SDP), "application/sdp"); } newRes.send(); } } } else if (SIPHelper.isErrorResponse(res)) { Exception ex = getExceptionByResponse(res); done(getJoinCompleteCauseByResponse(res), ex); disconnectCall(_call2, true, getCallCompleteCauseByResponse(res), ex); } } else if (_call1.equals(call)) { if (SIPHelper.isSuccessResponse(res)) { res.createAck().send(); try { final SipServletRequest ack2 = _response.createAck(); if (call1Processed && !call2Processed) { ack2.setContent(SDPUtils.formulateSDP(_call2, _call1.getRemoteSdp()), "application/sdp"); } ack2.send(); _call2.setSIPCallState(State.ANSWERED); successJoin(); } catch (final Exception e) { done(JoinCompleteEvent.Cause.ERROR, e); failCall(_call1, e); failCall(_call2, e); throw e; } } else if (SIPHelper.isProvisionalResponse(res)) { SIPHelper.trySendPrack(res); } else { try { _response.createAck().send(); _call2.setSIPCallState(State.ANSWERED); } catch(Exception ex) { LOG.warn("Exception when sending ACK.", ex); } Exception ex = getExceptionByResponse(res); done(getJoinCompleteCauseByResponse(res), ex); disconnectCall(_call2, true, getCallCompleteCauseByResponse(res), ex); } } } catch (final Exception e) { done(JoinCompleteEvent.Cause.ERROR, e); failCall(_call1, e); failCall(_call2, e); throw e; } } @Override protected void doAck(final SipServletRequest req, final SIPCallImpl call) throws Exception { if (_call1.equals(call)) { _call1.setSIPCallState(State.ANSWERED); if (!call1Processed || (call1Processed && updatedCall1Success)) { try { final SipServletRequest ack = _response.createAck(); if ((call1Processed && !call2Processed) || (!call1Processed && !call2Processed && SIPHelper.getRawContentWOException(_response.getRequest()) == null)) { ack.setContent(SDPUtils.formulateSDP(_call2, _call1.getRemoteSdp()), "application/sdp"); } ack.send(); _call2.setSIPCallState(State.ANSWERED); successJoin(); } catch (final Exception e) { done(JoinCompleteEvent.Cause.ERROR, e); failCall(_call1, e); failCall(_call2, e); throw e; } } else { // re-INVITE call1 _call1.reInviteRemote(_latestCall2SDP, null, null); } } } protected void successJoin() throws Exception { doDisengage(_call1, JoinType.DIRECT); _call1.linkCall(_call2, JoinType.DIRECT, _direction); _response = null; done(JoinCompleteEvent.Cause.JOINED, null); } private void relayUnreliableProvisionalResponse(SipServletResponse res) throws IOException { final SipServletResponse newRes = _call1.getSipInitnalRequest().createResponse(res.getStatus(), res.getReasonPhrase()); final byte[] content = res.getRawContent(); if (content != null) { newRes.setContent(content, res.getContentType()); } newRes.send(); } }