/* * 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.provider.*; import org.zoolu.sip.address.NameAddress; import org.zoolu.sip.header.StatusLine; import org.zoolu.sip.header.RequestLine; import org.zoolu.sip.header.AuthorizationHeader; import org.zoolu.sip.header.WwwAuthenticateHeader; import org.zoolu.sip.transaction.*; import org.zoolu.sip.message.*; import org.zoolu.sip.authentication.DigestAuthentication; import org.zoolu.tools.LogLevel; import com.sesca.misc.Logger; import java.util.Hashtable; /** Class ExtendedInviteDialog can be used to manage extended invite dialogs. * <p> * An ExtendedInviteDialog allows the user: * <br>- to handle authentication * <br>- to handle refer/notify * <br>- to capture all methods within the dialog */ public class ExtendedInviteDialog extends org.zoolu.sip.dialog.InviteDialog { /** Max number of registration attempts. */ static final int MAX_ATTEMPTS=3; /** ExtendedInviteDialog listener. */ ExtendedInviteDialogListener dialog_listener; /** Acive transactions. */ Hashtable transactions; /** User name. */ String username; String authId; /** User name. */ String realm; /** User's passwd. */ String passwd; /** Nonce for the next authentication. */ String next_nonce; /** Qop for the next authentication. */ String qop; /** Number of authentication attempts. */ int attempts; /** Creates a new ExtendedInviteDialog. */ public ExtendedInviteDialog(SipProvider provider, ExtendedInviteDialogListener listener) { super(provider,listener); Logger.paranoia("ExtendedInviteDialod constructor#1"); init(listener); this.username=provider.getUserProfile().username; this.realm=provider.getUserProfile().realm; this.passwd=provider.getUserProfile().passwd; authId=provider.getUserProfile().authID; } /** Creates a new ExtendedInviteDialog. */ public ExtendedInviteDialog(SipProvider provider, String username, String realm, String passwd, ExtendedInviteDialogListener listener) { super(provider,listener); Logger.paranoia("ExtendedInviteDialod constructor#2"); init(listener); this.username=username; this.realm=realm; this.passwd=passwd; authId=provider.getUserProfile().authID; } /** Inits the ExtendedInviteDialog. */ private void init(ExtendedInviteDialogListener listener) { this.dialog_listener=listener; if (this.dialog_listener!=null)Logger.paranoia(" dialog_listener="+this.dialog_listener); this.transactions=new Hashtable(); this.username=null; this.realm=null; this.passwd=null; this.next_nonce=null; this.qop=null; this.attempts=0; } /** Sends a new request within the dialog */ public void request(Message req) { TransactionClient t=new TransactionClient(sip_provider,req,this); transactions.put(t.getTransactionId(),t); t.request(); } /** Sends a new REFER within the dialog */ public void refer(NameAddress refer_to) { refer(refer_to,null); } /** Sends a new REFER within the dialog */ public void refer(NameAddress refer_to, NameAddress referred_by) { MessageFactory msgf = new MessageFactory(); Message req=msgf.createReferRequest(this,refer_to,referred_by); request(req); } /** Sends a new NOTIFY within the dialog */ public void notify(int code, String reason) { notify((new StatusLine(code,reason)).toString()); } /** Sends a new NOTIFY within the dialog */ public void notify(String sipfragment) { MessageFactory msgf = new MessageFactory(); Message req=msgf.createNotifyRequest(this,"refer",null,sipfragment); request(req); } /** Responds with <i>resp</i> */ public void respond(Message resp) { printLog("inside respond(resp)",LogLevel.MEDIUM); String method=resp.getCSeqHeader().getMethod(); if (method.equals(SipMethods.INVITE) || method.equals(SipMethods.CANCEL) || method.equals(SipMethods.BYE)) { super.respond(resp); } else { TransactionIdentifier transaction_id=resp.getTransactionId(); printLog("transaction-id="+transaction_id,LogLevel.MEDIUM); if (transactions.containsKey(transaction_id)) { printLog("responding",LogLevel.LOW); TransactionServer t=(TransactionServer)transactions.get(transaction_id); t.respondWith(resp); } else printLog("transaction server not found; message discarded",LogLevel.MEDIUM); } } /** Accept a REFER */ public void acceptRefer(Message req) { printLog("inside acceptRefer(refer)",LogLevel.MEDIUM); Message resp=MessageFactory.createResponse(req,202,SipResponses.reasonOf(202),null); respond(resp); } /** Refuse a REFER */ public void refuseRefer(Message req) { printLog("inside refuseRefer(refer)",LogLevel.MEDIUM); Message resp=MessageFactory.createResponse(req,603,SipResponses.reasonOf(603),null); respond(resp); } /** Inherited from class SipProviderListener. */ public void onReceivedMessage(SipProvider provider, Message msg) { Logger.paranoia("ExtendedInviteDialog.onReceivedMessage"); printLog("Message received: "+msg.getFirstLine().substring(0,msg.toString().indexOf('\r')),LogLevel.LOW); if (msg.isResponse()) { super.onReceivedMessage(provider,msg); } else if (msg.isInvite() || msg.isAck() || msg.isCancel() || msg.isBye()) { super.onReceivedMessage(provider,msg); } else { TransactionServer t=new TransactionServer(sip_provider,msg,this); transactions.put(t.getTransactionId(),t); //t.listen(); if (msg.isRefer()) { //Message resp=MessageFactory.createResponse(msg,202,"Accepted",null,null); //respond(resp); NameAddress refer_to=msg.getReferToHeader().getNameAddress(); NameAddress referred_by=null; if (msg.hasReferredByHeader()) referred_by=msg.getReferredByHeader().getNameAddress(); dialog_listener.onDlgRefer(this,refer_to,referred_by,msg); } else if (msg.isNotify()) { Message resp=MessageFactory.createResponse(msg,200,SipResponses.reasonOf(200),null); respond(resp); String event=msg.getEventHeader().getValue(); String sipfragment=msg.getBody(); dialog_listener.onDlgNotify(this,event,sipfragment,msg); } else { printLog("Received alternative request "+msg.getRequestLine().getMethod(),LogLevel.MEDIUM); dialog_listener.onDlgAltRequest(this,msg.getRequestLine().getMethod(),msg.getBody(),msg); } } } /** Inherited from TransactionClientListener. * When the TransactionClientListener goes into the "Completed" state, receiving a failure response */ public void onTransFailureResponse(TransactionClient tc, Message msg) { Logger.paranoia("inside onTransFailureResponse"); String method=tc.getTransactionMethod(); StatusLine status_line=msg.getStatusLine(); int code=status_line.getCode(); String reason=status_line.getReason(); Logger.paranoia("ExtendedInvi;teDialog.onTransFailureResponse:"); Logger.paranoia("attempts (max) ="+attempts+" ("+MAX_ATTEMPTS+")"); Logger.paranoia("has authenticate header? "+msg.hasProxyAuthenticateHeader()); Logger.paranoia("realm="+realm); Logger.paranoia("msg realm="+((msg.hasProxyAuthenticateHeader()) ? msg.getProxyAuthenticateHeader().getRealmParam() : "n/a ")); // AUTHENTICATION-BEGIN if ((code==401 && attempts<MAX_ATTEMPTS && msg.hasWwwAuthenticateHeader() && msg.getWwwAuthenticateHeader().getRealmParam().equalsIgnoreCase(realm)) || (code==407 && attempts<MAX_ATTEMPTS && msg.hasProxyAuthenticateHeader() && msg.getProxyAuthenticateHeader().getRealmParam().equalsIgnoreCase(realm))) { attempts++; // req:ssa on cseq:ua kasvatettu Message req=tc.getRequestMessage(); req.setCSeqHeader(req.getCSeqHeader().incSequenceNumber()); WwwAuthenticateHeader wah; if (code==401) wah=msg.getWwwAuthenticateHeader(); else wah=msg.getProxyAuthenticateHeader(); String qop_options=wah.getQopOptionsParam(); qop=(qop_options!=null)? "auth" : null; RequestLine rl=req.getRequestLine(); // SESCA // BUGI client ei saa lähettää qop:ia //DigestAuthentication digest=new DigestAuthentication(rl.getMethod(),rl.getAddress().toString(),wah,qop,null,username,passwd); DigestAuthentication digest=new DigestAuthentication(rl.getMethod(),rl.getAddress().toString(),wah,null,null,authId,passwd); AuthorizationHeader ah; if (code==401) ah=digest.getAuthorizationHeader(); else ah=digest.getProxyAuthorizationHeader(); req.setAuthorizationHeader(ah); transactions.remove(tc.getTransactionId()); // SESCA // BUGI // Päivitetään invite_req:uun uusin invite-viesti. // Lähetämme uuden inviten, joten teemme uuden invitetransactionclientin, eikä transaction clientia if (method.equals(SipMethods.INVITE)) { invite_req = req; tc=new InviteTransactionClient(sip_provider,req,this); // tc=new TransactionClient(sip_provider,req,this); } else { tc=new TransactionClient(sip_provider,req,this); } transactions.put(tc.getTransactionId(),tc); tc.request(); } else // AUTHENTICATION-END if (method.equals(SipMethods.INVITE) || method.equals(SipMethods.CANCEL) || method.equals(SipMethods.BYE)) { super.onTransFailureResponse(tc,msg); } else if (tc.getTransactionMethod().equals(SipMethods.REFER)) { transactions.remove(tc.getTransactionId()); dialog_listener.onDlgReferResponse(this,code,reason,msg); } else { String body=msg.getBody(); transactions.remove(tc.getTransactionId()); dialog_listener.onDlgAltResponse(this,method,code,reason,body,msg); } } /** Inherited from TransactionClientListener. * When an TransactionClientListener goes into the "Terminated" state, receiving a 2xx response */ public void onTransSuccessResponse(TransactionClient t, Message msg) { printLog("inside onTransSuccessResponse("+t.getTransactionId()+",msg)",LogLevel.LOW); attempts=0; String method=t.getTransactionMethod(); StatusLine status_line=msg.getStatusLine(); int code=status_line.getCode(); String reason=status_line.getReason(); if (method.equals(SipMethods.INVITE) || method.equals(SipMethods.CANCEL) || method.equals(SipMethods.BYE)) { super.onTransSuccessResponse(t,msg); } else if (t.getTransactionMethod().equals(SipMethods.REFER)) { transactions.remove(t.getTransactionId()); dialog_listener.onDlgReferResponse(this,code,reason,msg); } else { String body=msg.getBody(); transactions.remove(t.getTransactionId()); dialog_listener.onDlgAltResponse(this,method,code,reason,body,msg); } } /** Inherited from TransactionClientListener. * When the TransactionClient goes into the "Terminated" state, caused by transaction timeout */ public void onTransTimeout(TransactionClient t) { printLog("inside onTransTimeout("+t.getTransactionId()+",msg)",LogLevel.LOW); String method=t.getTransactionMethod(); if (method.equals(SipMethods.INVITE) || method.equals(SipMethods.BYE)) { super.onTransTimeout(t); } else { // do something.. transactions.remove(t.getTransactionId()); } } //**************************** Logs ****************************/ /** Adds a new string to the default Log */ protected void printLog(String str, int level) { if (log!=null) log.println("ExtendedInviteDialog#"+dialog_sqn+": "+str,level+SipStack.LOG_LEVEL_DIALOG); } }