/* * 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.message.*; import org.zoolu.sip.header.*; import org.zoolu.sip.provider.*; import org.zoolu.tools.Log; import org.zoolu.tools.LogLevel; import org.zoolu.tools.AssertException; import com.sesca.misc.Logger; import java.util.Vector; /** Class Dialog maintains a complete information status of a generic SIP dialog. * It has the following attributes: * <ul> * <li>sip-provider</li> * <li>call-id</li> * <li>local and remote URLs</li> * <li>local and remote contact URLs</li> * <li>local and remote cseqs</li> * <li>local and remote tags</li> * <li>dialog-id</li> * <li>route set</li> * </ul> */ public abstract class Dialog extends DialogInfo implements SipProviderListener { // ************************ Static attributes ************************* /** Dialogs counter */ private static int dialog_counter=0; /** Identifier for the transaction client side of a dialog (UAC). */ public final static int UAC=0; /** Identifier for the transaction server side of a dialog (UAS). */ public final static int UAS=1; // *********************** Protected attributes *********************** /** Dialog sequence number */ protected int dialog_sqn; /** Event logger. */ protected Log log; /** SipProvider */ protected SipProvider sip_provider; /** Internal dialog state. */ protected int status; /** Dialog identifier */ protected DialogIdentifier dialog_id; // ************************* Abstract methods ************************* /** Gets the dialog state */ abstract protected String getStatus(); /** Whether the dialog is in "early" state. */ abstract public boolean isEarly(); /** Whether the dialog is in "confirmed" state. */ abstract public boolean isConfirmed(); /** Whether the dialog is in "terminated" state. */ abstract public boolean isTerminated(); /** When a new Message is received by the SipProvider. */ abstract public void onReceivedMessage(SipProvider provider, Message message); // **************************** Costructors *************************** /** Creates a new empty Dialog */ protected Dialog(SipProvider provider) { super(); this.sip_provider=provider; this.log=sip_provider.getLog(); this.dialog_sqn=dialog_counter++; this.status=0; this.dialog_id=null; } // ************************* Protected methods ************************ /** Changes the internal dialog state */ protected void changeStatus(int newstatus) { status=newstatus; Logger.debug("changed dialog state: "+getStatus()); // remove the sip_provider listener when going to "terminated" state if (isTerminated()) { if (dialog_id!=null && sip_provider.getListeners().containsKey(dialog_id)) { Logger.debug("Dialog.changeStatus deleting SipProviderListener"); sip_provider.removeSipProviderListener(dialog_id); } } else // add sip_provider listener when going to "early" or "confirmed" state if (isEarly() || isConfirmed()) { if (dialog_id!=null && !sip_provider.getListeners().containsKey(dialog_id)) sip_provider.addSipProviderListener(dialog_id,this); } } /** Whether the dialog state is equal to <i>st</i> */ protected boolean statusIs(int st) { return status==st; } // ************************** Public methods ************************** /** Gets the SipProvider of this Dialog. */ public SipProvider getSipProvider() { return sip_provider; } /** Gets the inique Dialog-ID </i> */ public DialogIdentifier getDialogID() { return dialog_id; } /** Updates empty attributes (tags, route set) and mutable attributes (cseqs, contacts), based on a new message. * @param side indicates whether the Dialog is acting as transaction client or server for the current message (use constant values Dialog.UAC or Dialog.UAS) * @param msg the message that is used to update the Dialog state */ public void update(int side, Message msg) { Logger.debug("in Dialog.update"); if (isTerminated()) { printWarning("trying to update a terminated dialog: do nothing.",LogLevel.HIGH); return; } // else // update call_id if (call_id==null) call_id=msg.getCallIdHeader().getCallId(); // update names and tags if (side==UAC) { if (remote_name==null || remote_tag==null) { ToHeader to=msg.getToHeader(); if (remote_name==null) remote_name=to.getNameAddress(); if (remote_tag==null) remote_tag=to.getTag(); } if (local_name==null || local_tag==null) { FromHeader from=msg.getFromHeader(); if (local_name==null) local_name=from.getNameAddress(); if (local_tag==null) local_tag=from.getTag(); } local_cseq=msg.getCSeqHeader().getSequenceNumber(); //if (remote_cseq==-1) remote_cseq=SipProvider.pickInitialCSeq()-1; } else { if (local_name==null || local_tag==null) { ToHeader to=msg.getToHeader(); if (local_name==null) local_name=to.getNameAddress(); if (local_tag==null) local_tag=to.getTag(); } if (remote_name==null || remote_tag==null) { FromHeader from=msg.getFromHeader(); if (remote_name==null) remote_name=from.getNameAddress(); if (remote_tag==null) remote_tag=from.getTag(); } remote_cseq=msg.getCSeqHeader().getSequenceNumber(); if (local_cseq==-1) local_cseq=SipProvider.pickInitialCSeq()-1; } // update contact if (msg.hasContactHeader()) { if ((side==UAC && msg.isRequest()) || (side==UAS && msg.isResponse())) local_contact=msg.getContactHeader().getNameAddress(); else remote_contact=msg.getContactHeader().getNameAddress(); } // update route or record-route if (side==UAC) { if (msg.isRequest() && msg.hasRouteHeader() && route==null) { route=msg.getRoutes().getValues(); } if (side==UAC && msg.isResponse() && msg.hasRecordRouteHeader()) { Vector rr=msg.getRecordRoutes().getHeaders(); int size=rr.size(); route=new Vector(size); for (int i=0; i<size; i++) route.insertElementAt((new RecordRouteHeader((Header)rr.elementAt(size-1-i))).getNameAddress(),i); } } else { if (msg.isRequest() && msg.hasRouteHeader() && route==null) { Vector reverse_route=msg.getRoutes().getValues(); int size=reverse_route.size(); route=new Vector(size); for (int i=0; i<size; i++) route.insertElementAt(reverse_route.elementAt(size-1-i),i); } if (msg.isRequest() && msg.hasRecordRouteHeader()) { Vector rr=msg.getRecordRoutes().getHeaders(); int size=rr.size(); route=new Vector(size); for (int i=0; i<size; i++) route.insertElementAt((new RecordRouteHeader((Header)rr.elementAt(i))).getNameAddress(),i); } } // update dialog_id and sip_provider listener DialogIdentifier new_id=new DialogIdentifier(call_id,local_tag,remote_tag); if (dialog_id==null || !dialog_id.equals(new_id)) { if (dialog_id!=null && sip_provider!=null && sip_provider.getListeners().containsKey(dialog_id)) { Logger.debug("Dialog.uodate deleting SipProviderListener"); sip_provider.removeSipProviderListener(dialog_id); } dialog_id=new_id; printLog("new dialog id: "+dialog_id,LogLevel.HIGH); if (sip_provider!=null) sip_provider.addSipProviderListener(dialog_id,this); } } //**************************** Logs ****************************/ /** Adds a new string to the default Log */ protected void printLog(String str, int level) { if (log!=null) log.println("Dialog#"+dialog_sqn+": "+str,level+SipStack.LOG_LEVEL_DIALOG); } /** Adds a Warning message to the default Log */ protected final void printWarning(String str, int level) { printLog("WARNING: "+str,level); } /** Adds the Exception message to the default Log */ protected final void printException(Exception e, int level) { if (log!=null) log.printException(e,level+SipStack.LOG_LEVEL_DIALOG); } /** Verifies the correct status; if not logs the event. */ protected final boolean verifyStatus(boolean expression) { return verifyThat(expression,"dialog state mismatching"); } /** Verifies an event; if not logs it. */ protected final boolean verifyThat(boolean expression, String str) { if (!expression) { if (str==null || str.length()==0) printWarning("expression check failed. ",1); else printWarning(str,1); } return expression; } }