/*
* Copyright (C) 2009 Risto Känsäkoski - Sesca ISW Ltd
* Copyright (C) 2005 Luca Veltri - University of Parma - Italy
*
* This file is part of SIP-Applet (www.sesca.com, www.purplescout.com)
* This file is modified from MjSip (http://www.mjsip.org)
*
* MjSip is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MjSip is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MjSip; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package org.zoolu.sip.dialog;
import org.zoolu.sip.address.*;
import org.zoolu.sip.transaction.*;
import org.zoolu.sip.message.*;
import org.zoolu.sip.header.*;
import org.zoolu.sip.provider.*;
import org.zoolu.tools.LogLevel;
import com.sesca.misc.Logger;
/** Class InviteDialog can be used to manage invite dialogs.
* An InviteDialog can be both client or server.
* (i.e. generating an INVITE request or responding to an incoming INVITE request).
* <p>
* An InviteDialog can be in state inviting/waiting/invited, accepted/refused, call,
* byed/byeing, and close.
* <p>
* InviteDialog supports the offer/answer model for the sip body, with the following rules:
* <br> - both INVITE-offer/2xx-answer and 2xx-offer/ACK-answer modes for incoming calls
* <br> - INVITE-offer/2xx-answer mode for outgoing calls.
*/
public class InviteDialog extends Dialog implements TransactionClientListener, InviteTransactionServerListener, AckTransactionServerListener, SipProviderListener
{
/** The last invite message */
Message invite_req;
/** The last ack message */
Message ack_req;
/** The InviteTransactionServer. */
InviteTransactionServer invite_ts;
/** The AckTransactionServer. */
AckTransactionServer ack_ts;
/** The BYE TransactionServer. */
TransactionServer bye_ts;
/** The InviteDialog listener */
InviteDialogListener listener;
/** Whether offer/answer are in INVITE/200_OK */
boolean invite_offer;
protected static final int D_INIT=0;
protected static final int D_WAITING=1;
protected static final int D_INVITING=2;
protected static final int D_INVITED=3;
protected static final int D_REFUSED=4;
protected static final int D_ACCEPTED=5;
protected static final int D_CALL=6;
protected static final int D_ReWAITING=11;
protected static final int D_ReINVITING=12;
protected static final int D_ReINVITED=13;
protected static final int D_ReREFUSED=14;
protected static final int D_ReACCEPTED=15;
protected static final int D_BYEING=7;
protected static final int D_BYED=8;
protected static final int D_CLOSE=9;
/** Gets the dialog state */
protected String getStatus()
{ switch (status)
{ case D_INIT : return "D_INIT";
case D_WAITING : return "D_WAITING";
case D_INVITING : return "D_INVITING";
case D_INVITED : return "D_INVITED";
case D_REFUSED : return "D_REFUSED";
case D_ACCEPTED : return "D_ACCEPTED";
case D_CALL : return "D_CALL";
case D_ReWAITING : return "D_ReWAITING";
case D_ReINVITING : return "D_ReINVITING";
case D_ReINVITED : return "D_ReINVITED";
case D_ReREFUSED : return "D_ReREFUSED";
case D_ReACCEPTED : return "D_ReACCEPTED";
case D_BYEING : return "D_BYEING";
case D_BYED : return "D_BYED";
case D_CLOSE : return "D_CLOSE";
default : return null;
}
}
// ************************** Public methods **************************
/** Whether the dialog is in "early" state. */
public boolean isEarly()
{ return status<D_ACCEPTED;
}
/** Whether the dialog is in "confirmed" state. */
public boolean isConfirmed()
{ return status>=D_ACCEPTED && status<D_CLOSE;
}
/** Whether the dialog is in "terminated" state. */
public boolean isTerminated()
{ return status==D_CLOSE;
}
/** Whether the session is "active". */
public boolean isSessionActive()
{ return (status==D_CALL);
}
/** Gets the invite message */
public Message getInviteMessage()
{ return invite_req;
}
/** Creates a new InviteDialog. */
public InviteDialog(SipProvider sip_provider, InviteDialogListener listener)
{ super(sip_provider);
init(listener);
}
/** Creates a new InviteDialog for the already received INVITE request <i>invite</i>. */
public InviteDialog(SipProvider sip_provider, Message invite, InviteDialogListener listener)
{ super(sip_provider);
init(listener);
changeStatus(D_INVITED);
invite_req=invite;
invite_ts=new InviteTransactionServer(sip_provider,invite_req,this);
update(Dialog.UAS,invite_req);
}
/** Inits the InviteDialog. */
private void init(InviteDialogListener listener)
{ log=sip_provider.getLog();
this.listener=listener;
this.invite_req=null;
this.ack_req=null;
this.invite_offer=true;
changeStatus(D_INIT);
}
/** Starts a new InviteTransactionServer. */
public void listen()
{ if (!statusIs(D_INIT)) return;
//else
changeStatus(D_WAITING);
invite_ts=new InviteTransactionServer(sip_provider,this);
invite_ts.listen();
}
/** Starts a new InviteTransactionClient
* and initializes the dialog state information.
* @param callee the callee url (and display name)
* @param caller the caller url (and display name)
* @param contact the contact url OR the contact username
* @param session_descriptor SDP body
*/
public void invite(String callee, String caller, String contact, String session_descriptor)
{ printLog("inside invite(callee,caller,contact,sdp)",LogLevel.MEDIUM);
if (!statusIs(D_INIT)) return;
// else
NameAddress to_url=new NameAddress(callee);
NameAddress from_url=new NameAddress(caller);
SipURL request_uri=to_url.getAddress();
NameAddress contact_url=null;
if (contact!=null)
{ if (contact.indexOf("sip:")>=0) contact_url=new NameAddress(contact);
else contact_url=new NameAddress(new SipURL(contact,sip_provider.getViaAddress(),sip_provider.getPortB()));
}
else contact_url=from_url;
MessageFactory msgf = new MessageFactory();
Message invite=msgf.createInviteRequest(sip_provider,request_uri,to_url,from_url,contact_url,session_descriptor);
// do invite
invite(invite);
}
/** Starts a new InviteTransactionClient
* and initializes the dialog state information
* @param invite the INVITE message
*/
public void invite(Message invite)
{ printLog("inside invite(invite)",LogLevel.MEDIUM);
if (!statusIs(D_INIT)) return;
// else
changeStatus(D_INVITING);
invite_req=invite;
update(Dialog.UAC,invite_req);
InviteTransactionClient invite_tc=new InviteTransactionClient(sip_provider,invite_req,this);
invite_tc.request();
}
/** Starts a new InviteTransactionClient with offer/answer in 2xx/ack
* and initializes the dialog state information */
public void inviteWithoutOffer(String callee, String caller, String contact)
{ invite_offer=false;
invite(callee,caller,contact,null);
}
/** Starts a new InviteTransactionClient with offer/answer in 2xx/ack
* and initializes the dialog state information */
public void inviteWithoutOffer(Message invite)
{ invite_offer=false;
invite(invite);
}
/** Re-invites the remote user.
* <p>Starts a new InviteTransactionClient and changes the dialog state information
* <p> Parameters:
* <br>- contact : the contact url OR the contact username; if null, the previous contact is used
* <br>- session_descriptor : the message body
*/
public void reInvite(String contact, String session_descriptor)
{ printLog("inside reInvite(contact,sdp)",LogLevel.MEDIUM);
if (!statusIs(D_CALL)) return;
// else
MessageFactory msgf = new MessageFactory();
Message invite=msgf.createInviteRequest(this,session_descriptor);
if (contact!=null)
{ NameAddress contact_url;
if (contact.indexOf("sip:")>=0) contact_url=new NameAddress(contact);
else contact_url=new NameAddress(new SipURL(contact,sip_provider.getViaAddress(),sip_provider.getPortB()));
invite.setContactHeader(new ContactHeader(contact_url));
}
reInvite(invite);
}
/** Re-invites the remote user.
* <p>Starts a new InviteTransactionClient and changes the dialog state information */
public void reInvite(Message invite)
{ printLog("inside reInvite(invite)",LogLevel.MEDIUM);
if (!statusIs(D_CALL)) return;
// else
changeStatus(D_ReINVITING);
invite_req=invite;
update(Dialog.UAC,invite_req);
InviteTransactionClient invite_tc=new InviteTransactionClient(sip_provider,invite_req,this);
invite_tc.request();
}
/** Re-invites the remote user with offer/answer in 2xx/ack
* <p>Starts a new InviteTransactionClient and changes the dialog state information */
public void reInviteWithoutOffer(Message invite)
{ invite_offer=false;
reInvite(invite);
}
/** Re-invites the remote user with offer/answer in 2xx/ack
* <p>Starts a new InviteTransactionClient and changes the dialog state information */
public void reInviteWithoutOffer(String contact, String session_descriptor)
{ invite_offer=false;
reInvite(contact,session_descriptor);
}
/** Sends the ack when offer/answer is in 2xx/ack */
public void ackWithAnswer(String contact, String session_descriptor)
{ if (contact!=null) setLocalContact(new NameAddress(contact));
MessageFactory msgf = new MessageFactory();
Message ack=msgf.create2xxAckRequest(this,session_descriptor);
ackWithAnswer(ack);
}
/** Sends the ack when offer/answer is in 2xx/ack */
public void ackWithAnswer(Message ack)
{ ack_req=ack;
// reset the offer/answer flag to the default value
invite_offer=true;
AckTransactionClient ack_tc=new AckTransactionClient(sip_provider,ack,null);
ack_tc.request();
}
/** Responds with <i>resp</i>.
* This method can be called when the InviteDialog is in D_INVITED or D_BYED states.
* <p>
* If the CSeq method is INVITE and the response is 2xx,
* it moves to state D_ACCEPTED, adds a new listener to the SipProviderListener,
* and creates new AckTransactionServer
* <p>
* If the CSeq method is INVITE and the response is not 2xx,
* it moves to state D_REFUSED, and sends the response. */
public void respond(Message resp)
//private void respond(Message resp)
{
Logger.paranoia("InviteDialog.respond()");
printLog("inside respond(resp)",LogLevel.MEDIUM);
String method=resp.getCSeqHeader().getMethod();
if (method.equals(SipMethods.INVITE))
{ if (!verifyStatus(statusIs(D_INVITED)||statusIs(D_ReINVITED)))
{ Logger.paranoia("respond(): InviteDialog not in (re)invited state: No response now");
return;
}
int code=resp.getStatusLine().getCode();
// 1xx provisional responses
if (code>=100 && code<200)
{ invite_ts.respondWith(resp);
return;
}
// For all final responses establish the dialog
if (code>=200)
{ //changeStatus(D_ACCEPTED);
update(Dialog.UAS,resp);
}
// 2xx success responses
if (code>=200 && code<300)
{ if(statusIs(D_INVITED)) changeStatus(D_ACCEPTED); else changeStatus(D_ReACCEPTED);
// terminates the INVITE Transaction server and activates an ACK Transaction server
invite_ts.terminate();
ConnectionIdentifier conn_id=invite_ts.getConnectionId();
ack_ts=new AckTransactionServer(sip_provider,conn_id,resp,this);
ack_ts.respond();
//if (statusIs(D_ReACCEPTED)) listener.onDlgReInviteAccepted(this);
//else listener.onDlgAccepted(this);
return;
}
else
// 300-699 failure responses
//if (code>=300)
{ if(statusIs(D_INVITED)) changeStatus(D_REFUSED); else changeStatus(D_ReREFUSED);
invite_ts.respondWith(resp);
//if (statusIs(D_ReREFUSED)) listener.onDlgReInviteRefused(this);
//else listener.onDlgRefused(this);
return;
}
}
if (method.equals(SipMethods.BYE))
{ if (!verifyStatus(statusIs(D_BYED))) return;
bye_ts.respondWith(resp);
}
}
/** Responds with <i>code</i> and <i>reason</i>.
* This method can be called when the InviteDialog is in D_INVITED, D_ReINVITED states */
public void respond(int code, String reason, String contact, String sdp)
{ printLog("inside respond("+code+","+reason+")",LogLevel.MEDIUM);
if (statusIs(D_INVITED) || statusIs(D_ReINVITED))
{ NameAddress contact_address=null;
if (contact!=null) contact_address=new NameAddress(contact);
Message resp=MessageFactory.createResponse(invite_req,code,reason,contact_address);
resp.setBody(sdp);
respond(resp);
}
//else
//printWarning("Dialog isn't in \"invited\" state: cannot respond ("+code+"/"+getStatus()+"/"+getDialogID()+")",LogLevel.MEDIUM);
}
/** Signals that the phone is ringing.
* This method should be called when the InviteDialog is in D_INVITED or D_ReINVITED state */
public void ring()
{
Logger.paranoia("InviteDialog.ring()");
printLog("inside ring()",LogLevel.MEDIUM);
respond(180,SipResponses.reasonOf(180),null,null);
}
/** Accepts the incoming call.
* This method should be called when the InviteDialog is in D_INVITED or D_ReINVITED state */
public void accept(String contact, String sdp)
{ printLog("inside accept(sdp)",LogLevel.MEDIUM);
respond(200,SipResponses.reasonOf(200),contact,sdp);
}
/** Refuses the incoming call.
* This method should be called when the InviteDialog is in D_INVITED or D_ReINVITED state */
public void refuse(int code, String reason)
{ printLog("inside refuse("+code+","+reason+")",LogLevel.MEDIUM);
respond(code,reason,null,null);
}
/** Refuses the incoming call.
* This method should be called when the InviteDialog is in D_INVITED or D_ReINVITED state */
public void refuse()
{ printLog("inside refuse()",LogLevel.MEDIUM);
//refuse(480,"Temporarily Unavailable");
//refuse(603,"Decline");
refuse(403,SipResponses.reasonOf(403));
}
/** Termiante the call.
* This method should be called when the InviteDialog is in D_CALL state
* <p>
* Increments the Cseq, moves to state D_BYEING, and creates new BYE TransactionClient */
public void bye()
{ printLog("inside bye()",LogLevel.MEDIUM);
if (statusIs(D_CALL))
{
MessageFactory msgf = new MessageFactory();
Message bye=msgf.createByeRequest(this);
bye(bye);
}
}
/** Termiante the call.
* This method should be called when the InviteDialog is in D_CALL state
* <p>
* Increments the Cseq, moves to state D_BYEING, and creates new BYE TransactionClient */
public void bye(Message bye)
{ printLog("inside bye(bye)",LogLevel.MEDIUM);
if (statusIs(D_CALL))
{ changeStatus(D_BYEING);
//dialog_state.incLocalCSeq(); // done by MessageFactory.createRequest()
TransactionClient tc=new TransactionClient(sip_provider,bye,this);
tc.request();
//listener.onDlgByeing(this);
}
}
/** Cancel the ongoing call request or a call listening.
* This method should be called when the InviteDialog is in D_INVITING or D_ReINVITING state
* or in the D_WAITING state */
public void cancel()
{ printLog("inside cancel()",LogLevel.MEDIUM);
if (statusIs(D_INVITING) || statusIs(D_ReINVITING))
{
MessageFactory msgf = new MessageFactory();
Message cancel=msgf.createCancelRequest(invite_req);
cancel(cancel);
}
else
if (statusIs(D_WAITING) || statusIs(D_ReWAITING))
{ invite_ts.terminate();
}
}
/** Cancel the ongoing call request or a call listening.
* This method should be called when the InviteDialog is in D_INVITING or D_ReINVITING state
* or in the D_WAITING state */
public void cancel(Message cancel)
{ printLog("inside cancel(cancel)",LogLevel.MEDIUM);
if (statusIs(D_INVITING) || statusIs(D_ReINVITING))
{ //changeStatus(D_CANCELING);
TransactionClient tc=new TransactionClient(sip_provider,cancel,null);
tc.request();
}
else
if (statusIs(D_WAITING) || statusIs(D_ReWAITING))
{ invite_ts.terminate();
}
}
/** Redirects the incoming call
* , specifing the <i>code</i> and <i>reason</i>.
* This method can be called when the InviteDialog is in D_INVITED or D_ReINVITED state */
public void redirect(int code, String reason, String contact)
{ printLog("inside redirect("+code+","+reason+","+contact+")",LogLevel.MEDIUM);
respond(code,reason,contact,null);
}
// ************** Inherited from SipProviderListener **************
/** Inherited from class SipProviderListener.
* Called when a new message is received (out of any ongoing transaction)
* for the current InviteDialog.
* Always checks for out-of-date methods (CSeq header sequence number).
* <p>
* If the message is ACK(2xx/INVITE) request, it moves to D_CALL state, and fires <i>onDlgAck(this,body,msg)</i>.
* <p>
* If the message is 2xx(INVITE) response, it create a new AckTransactionClient
* <p>
* If the message is BYE,
* it moves to D_BYED state, removes the listener from SipProvider, fires onDlgBye(this,msg)
* then it responds with 200 OK, moves to D_CLOSE state and fires onDlgClose(this)
*/
public void onReceivedMessage(SipProvider sip_provider, Message msg)
{ printLog("inside onReceivedMessage(sip_provider,message)",LogLevel.MEDIUM);
if (msg.isRequest() && !(msg.isAck() || msg.isCancel()) && msg.getCSeqHeader().getSequenceNumber()<=getRemoteCSeq())
{ printLog("Request message is too late (CSeq too small): Message discarded",LogLevel.HIGH);
return;
}
// invite received
if (msg.isRequest() && msg.isInvite())
{ verifyStatus(statusIs(D_INIT)||statusIs(D_CALL));
// NOTE: if the invite_ts.listen() is used, you should not arrive here with the D_INIT state..
// however state D_INIT has been included for robustness against further changes.
if (statusIs(D_INIT)) changeStatus(D_INVITED); else changeStatus(D_ReINVITED);
invite_req=msg;
invite_ts=new InviteTransactionServer(sip_provider,invite_req,this);
//((TransactionServer)transaction).listen();
update(Dialog.UAS,invite_req);
if (statusIs(D_INVITED)) listener.onDlgInvite(this,invite_req.getToHeader().getNameAddress(),invite_req.getFromHeader().getNameAddress(),invite_req.getBody(),invite_req);
else listener.onDlgReInvite(this,invite_req.getBody(),invite_req);
}
else
// ack (of 2xx of INVITE)
if (msg.isRequest() && msg.isAck())
{ if (!verifyStatus(statusIs(D_ACCEPTED)||statusIs(D_ReACCEPTED))) return;
changeStatus(D_CALL);
// terminates the AckTransactionServer
ack_ts.terminate();
listener.onDlgAck(this,msg.getBody(),msg);
listener.onDlgCall(this);
}
else
// keep sending ACK (if already sent) for any "200 OK" received
if (msg.isResponse())
{ if (!verifyStatus(statusIs(D_CALL))) return;
int code=msg.getStatusLine().getCode();
verifyThat(code>=200 && code<300,"code 2xx was expected");
if (ack_req!=null)
{ AckTransactionClient ack_tc=new AckTransactionClient(sip_provider,ack_req,null);
ack_tc.request();
}
}
else
// bye received
if (msg.isRequest() && msg.isBye())
{ if (!verifyStatus(statusIs(D_CALL)||statusIs(D_BYEING))) return;
changeStatus(D_BYED);
bye_ts=new TransactionServer(sip_provider,msg,this);
// automatically sends a 200 OK
Message resp=MessageFactory.createResponse(msg,200,SipResponses.reasonOf(200),null);
respond(resp);
listener.onDlgBye(this,msg);
changeStatus(D_CLOSE);
listener.onDlgClose(this);
}
else
// cancel received
if (msg.isRequest() && msg.isCancel())
{ if (!verifyStatus(statusIs(D_INVITED)||statusIs(D_ReINVITED))) return;
// create a CANCEL TransactionServer and send a 200 OK (CANCEL)
TransactionServer ts=new TransactionServer(sip_provider,msg,null);
//ts.listen();
ts.respondWith(MessageFactory.createResponse(msg,200,SipResponses.reasonOf(200),null));
// automatically sends a 487 Cancelled
Message resp=MessageFactory.createResponse(invite_req,487,SipResponses.reasonOf(487),null);
respond(resp);
listener.onDlgCancel(this,msg);
}
else
// any other request received
if (msg.isRequest())
{ TransactionServer ts=new TransactionServer(sip_provider,msg,null);
//ts.listen();
ts.respondWith(MessageFactory.createResponse(msg,405,SipResponses.reasonOf(405),null));
}
}
// ************** Inherited from InviteTransactionClientListener **************
/** Inherited from TransactionClientListener.
* When the TransactionClientListener is in "Proceeding" state and receives a new 1xx response
* <p>
* For INVITE transaction it fires <i>onFailureResponse(this,code,reason,body,msg)</i>. */
public void onTransProvisionalResponse(TransactionClient tc, Message msg)
{ printLog("inside onTransProvisionalResponse(tc,mdg)",LogLevel.LOW);
if (tc.getTransactionMethod().equals(SipMethods.INVITE))
{ StatusLine statusline=msg.getStatusLine();
listener.onDlgInviteProvisionalResponse(this,statusline.getCode(),statusline.getReason(),msg.getBody(),msg);
}
}
/** Inherited from TransactionClientListener.
* When the TransactionClientListener goes into the "Completed" state, receiving a failure response
* <p>
* If called for a INVITE transaction, it moves to D_CLOSE state, removes the listener from SipProvider.
* <p>
* If called for a BYE transaction, it moves to D_CLOSE state,
* removes the listener from SipProvider, and fires <i>onClose(this,msg)</i>. */
public void onTransFailureResponse(TransactionClient tc, Message msg)
{ printLog("inside onTransFailureResponse("+tc.getTransactionId()+",msg)",LogLevel.LOW);
if (tc.getTransactionMethod().equals(SipMethods.INVITE))
{ if (!verifyStatus(statusIs(D_INVITING)||statusIs(D_ReINVITING))) return;
StatusLine statusline=msg.getStatusLine();
int code=statusline.getCode();
verifyThat(code>=300 && code <700,"error code was expected");
if (statusIs(D_ReINVITING))
{ changeStatus(D_CALL);
listener.onDlgReInviteFailureResponse(this,code,statusline.getReason(),msg);
}
else
{ changeStatus(D_CLOSE);
if (code>=300 && code<400) listener.onDlgInviteRedirectResponse(this,code,statusline.getReason(),msg.getContacts(),msg);
else listener.onDlgInviteFailureResponse(this,code,statusline.getReason(),msg);
listener.onDlgClose(this);
}
}
else
if (tc.getTransactionMethod().equals(SipMethods.BYE))
{ if (!verifyStatus(statusIs(D_BYEING))) return;
StatusLine statusline=msg.getStatusLine();
int code=statusline.getCode();
verifyThat(code>=300 && code <700,"error code was expected");
changeStatus(this.D_CALL);
listener.onDlgByeFailureResponse(this,code,statusline.getReason(),msg);
}
}
/** Inherited from TransactionClientListener.
* When an TransactionClientListener goes into the "Terminated" state, receiving a 2xx response
* <p>
* If called for a INVITE transaction, it updates the dialog information, moves to D_CALL state,
* add a listener to the SipProvider, creates a new AckTransactionClient(ack,this),
* and fires <i>onSuccessResponse(this,code,body,msg)</i>.
* <p>
* If called for a BYE transaction, it moves to D_CLOSE state,
* removes the listener from SipProvider, and fires <i>onClose(this,msg)</i>. */
public void onTransSuccessResponse(TransactionClient tc, Message msg)
{ printLog("inside onTransSuccessResponse(tc,msg)",LogLevel.LOW);
if (tc.getTransactionMethod().equals(SipMethods.INVITE))
{ if (!verifyStatus(statusIs(D_INVITING)||statusIs(D_ReINVITING))) return;
MessageFactory msgf = new MessageFactory();
StatusLine statusline=msg.getStatusLine();
int code=statusline.getCode();
if (!verifyThat(code>=200 && code <300 && msg.getTransactionMethod().equals(SipMethods.INVITE),"2xx for invite was expected")) return;
boolean re_inviting=statusIs(D_ReINVITING);
changeStatus(D_CALL);
update(Dialog.UAC,msg);
if (invite_offer)
{ //invite_req=MessageFactory.createRequest(SipMethods.ACK,dialog_state,sdp.toString());
//ack=MessageFactory.createRequest(this,SipMethods.ACK,null);
ack_req=msgf.create2xxAckRequest(this,null);
AckTransactionClient ack_tc=new AckTransactionClient(sip_provider,ack_req,null);
ack_tc.request();
}
if (!re_inviting)
{ listener.onDlgInviteSuccessResponse(this,code,statusline.getReason(),msg.getBody(),msg);
listener.onDlgCall(this);
}
else
listener.onDlgReInviteSuccessResponse(this,code,statusline.getReason(),msg.getBody(),msg);
}
else
if (tc.getTransactionMethod().equals(SipMethods.BYE))
{ if (!verifyStatus(statusIs(D_BYEING))) return;
StatusLine statusline=msg.getStatusLine();
int code=statusline.getCode();
verifyThat(code>=200 && code <300,"2xx for bye was expected");
changeStatus(D_CLOSE);
listener.onDlgByeSuccessResponse(this,code,statusline.getReason(),msg);
listener.onDlgClose(this);
}
}
/** Inherited from TransactionClientListener.
* When the TransactionClient goes into the "Terminated" state, caused by transaction timeout */
public void onTransTimeout(TransactionClient tc)
{ printLog("inside onTransTimeout(tc,msg)",LogLevel.LOW);
if (tc.getTransactionMethod().equals(SipMethods.INVITE))
{ if (!verifyStatus(statusIs(D_INVITING)||statusIs(D_ReINVITING))) return;
changeStatus(D_CLOSE);
listener.onDlgTimeout(this);
listener.onDlgClose(this);
}
else
if (tc.getTransactionMethod().equals(SipMethods.BYE))
{ if (!verifyStatus(statusIs(D_BYEING))) return;
changeStatus(D_CLOSE);
listener.onDlgClose(this);
}
}
// ************** Inherited from InviteTransactionServerListener **************
/** Inherited from TransactionServerListener.
* When the TransactionServer goes into the "Trying" state receiving a request
* <p>
* If called for a INVITE transaction, it initializes the dialog information,
* <br> moves to D_INVITED state, and add a listener to the SipProvider,
* <br> and fires <i>onInvite(caller,body,msg)</i>. */
public void onTransRequest(TransactionServer ts, Message req)
{ printLog("inside onTransRequest(ts,msg)",LogLevel.LOW);
if (ts.getTransactionMethod().equals(SipMethods.INVITE))
{ if (!verifyStatus(statusIs(D_WAITING))) return;
changeStatus(D_INVITED);
invite_req=req;
update(Dialog.UAS,invite_req);
listener.onDlgInvite(this,invite_req.getToHeader().getNameAddress(),invite_req.getFromHeader().getNameAddress(),invite_req.getBody(),invite_req);
}
}
/** Inherited from InviteTransactionServerListener.
* When an InviteTransactionServer goes into the "Confirmed" state receining an ACK for NON-2xx response
* <p>
* It moves to D_CLOSE state and removes the listener from SipProvider. */
public void onTransFailureAck(InviteTransactionServer ts, Message msg)
{ printLog("inside onTransFailureAck(ts,msg)",LogLevel.LOW);
if (!verifyStatus(statusIs(D_REFUSED)||statusIs(D_ReREFUSED))) return;
if (statusIs(D_ReREFUSED))
{ changeStatus(D_CALL);
}
else
{ changeStatus(D_CLOSE);
listener.onDlgClose(this);
}
}
// ************ Inherited from AckTransactionServerListener ************
/** When the AckTransactionServer goes into the "Terminated" state, caused by transaction timeout */
public void onTransAckTimeout(AckTransactionServer ts)
{ printLog("inside onAckSrvTimeout(ts)",LogLevel.LOW);
if (!verifyStatus(statusIs(D_ACCEPTED)||statusIs(D_ReACCEPTED)||statusIs(D_REFUSED)||statusIs(D_ReREFUSED))) return;
printLog("No ACK received..",LogLevel.HIGH);
changeStatus(D_CLOSE);
listener.onDlgClose(this);
}
//**************************** Logs ****************************/
/** Adds a new string to the default Log */
protected void printLog(String str, int level)
{ if (log!=null) log.println("InviteDialog#"+dialog_sqn+": "+str,level+SipStack.LOG_LEVEL_DIALOG);
}
}