/* * 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 com.sesca.voip.ua; import local.net.KeepAliveSip; import local.ua.RegisterAgentListener; import org.zoolu.net.SocketAddress; import org.zoolu.sip.address.*; import org.zoolu.sip.provider.SipStack; import org.zoolu.sip.provider.SipProvider; import org.zoolu.sip.header.*; import org.zoolu.sip.message.*; import org.zoolu.sip.transaction.TransactionClient; import org.zoolu.sip.transaction.TransactionClientListener; import org.zoolu.sip.authentication.DigestAuthentication; import org.zoolu.tools.Log; import org.zoolu.tools.LogLevel; import java.util.Vector; /** Register User Agent. * It registers (one time or periodically) a contact address with a registrar server. */ public class RegisterAgent implements Runnable, TransactionClientListener { /** Max number of registration attempts. */ static final int MAX_ATTEMPTS=3; /** RegisterAgent listener */ RegisterAgentListener listener; /** SipProvider */ SipProvider sip_provider; /** User's URI with the fully qualified domain name of the registrar server. */ NameAddress target; /** User name. */ String username; /** User name. */ String realm; // Authentication name String authName; /** User's passwd. */ String passwd; /** Nonce for the next authentication. */ String next_nonce; /** Qop for the next authentication. */ String qop; /** User's contact address. */ NameAddress contact; /** Expiration time. */ int expire_time; /** Renew time. */ int renew_time; /** Whether keep on registering. */ boolean loop; /** Whether the thread is running. */ boolean is_running; /** Event logger. */ Log log; /** Number of registration attempts. */ int attempts; /** KeepAliveSip daemon. */ KeepAliveSip keep_alive; /** Creates a new RegisterAgent. */ public RegisterAgent(SipProvider sip_provider, String target_url, String contact_url, RegisterAgentListener listener) { init(sip_provider,target_url,contact_url,listener); } /** Creates a new RegisterAgent with authentication credentials (i.e. username, realm, and passwd). */ public RegisterAgent(SipProvider sip_provider, String target_url, String contact_url, String username, String realm, String passwd, RegisterAgentListener listener) { init(sip_provider,target_url,contact_url,listener); // authentication this.username=username; this.realm=realm; this.passwd=passwd; } /** Inits the RegisterAgent. */ private void init(SipProvider sip_provider, String target_url, String contact_url, RegisterAgentListener listener) { this.listener=listener; this.sip_provider=sip_provider; this.log=sip_provider.getLog(); this.target=new NameAddress(target_url); this.contact=new NameAddress(contact_url); this.expire_time=SipStack.default_expires; this.renew_time=0; this.is_running=false; this.keep_alive=null; // authentication this.username=null; this.realm=null; this.passwd=null; this.next_nonce=null; this.qop=null; this.attempts=0; } /** Whether it is periodically registering. */ public boolean isRegistering() { return is_running; } /** Registers with the registrar server. */ public void register() { register(expire_time); } /** Registers with the registrar server for <i>expire_time</i> seconds. */ public void register(int expire_time) { attempts=0; MessageFactory msgf = new MessageFactory(); if (expire_time>0) this.expire_time=expire_time; Message req=msgf.createRegisterRequest(sip_provider,target,target,contact); req.setExpiresHeader(new ExpiresHeader(String.valueOf(expire_time))); if (next_nonce!=null) { AuthorizationHeader ah=new AuthorizationHeader("Digest"); SipURL target_url=target.getAddress(); ah.addUsernameParam(username); ah.addRealmParam(realm); ah.addNonceParam(next_nonce); ah.addUriParam(req.getRequestLine().getAddress().toString()); ah.addQopParam(qop); String response=(new DigestAuthentication(SipMethods.REGISTER,ah,null,passwd)).getResponse(); ah.addResponseParam(response); req.setAuthorizationHeader(ah); } if (expire_time>0) printLog("Registering contact "+contact+" (it expires in "+expire_time+" secs)",LogLevel.HIGH); else printLog("Unregistering contact "+contact,LogLevel.HIGH); TransactionClient t=new TransactionClient(sip_provider,req,this); t.request(); } /** Unregister with the registrar server */ public void unregister() { register(0); } /** Unregister all contacts with the registrar server */ public void unregisterall() { attempts=0; MessageFactory msgf = new MessageFactory(); NameAddress user=new NameAddress(target); Message req=msgf.createRegisterRequest(sip_provider,target,target,null); //ContactHeader contact_star=new ContactHeader(); // contact is * //req.setContactHeader(contact_star); req.setExpiresHeader(new ExpiresHeader(String.valueOf(0))); printLog("Unregistering all contacts",LogLevel.HIGH); TransactionClient t=new TransactionClient(sip_provider,req,this); t.request(); } /** Periodically registers with the registrar server. * @param expire_time expiration time in seconds * @param renew_time renew time in seconds */ public void loopRegister(int expire_time, int renew_time) { this.expire_time=expire_time; this.renew_time=renew_time; loop=true; if (!is_running) (new Thread(this)).start(); } /** Periodically registers with the registrar server. * @param expire_time expiration time in seconds * @param renew_time renew time in seconds * @param keepalive_time keep-alive packet rate (inter-arrival time) in milliseconds */ public void loopRegister(int expire_time, int renew_time, long keepalive_time) { loopRegister(expire_time,renew_time); // keep-alive if (keepalive_time>0) { SipURL target_url=target.getAddress(); String target_host=target_url.getHost(); int targe_port=target_url.getPort(); if (targe_port<0) targe_port=SipStack.default_port; new KeepAliveSip(sip_provider,new SocketAddress(target_host,targe_port),null,keepalive_time); } } /** Halts the periodic registration. */ public void halt() { if (is_running) loop=false; if (keep_alive!=null) keep_alive.halt(); } // ***************************** run() ***************************** /** Run method */ public void run() { is_running=true; try { while (loop) { register(); Thread.sleep(renew_time*1000); } } catch (Exception e) { printException(e,LogLevel.HIGH); } is_running=false; } // **************** Transaction callback functions ***************** /** Callback function called when client sends back a failure response. */ /** Callback function called when client sends back a provisional response. */ public void onTransProvisionalResponse(TransactionClient transaction, Message resp) { // do nothing.. } /** Callback function called when client sends back a success response. */ public void onTransSuccessResponse(TransactionClient transaction, Message resp) { if (transaction.getTransactionMethod().equals(SipMethods.REGISTER)) { if (resp.hasAuthenticationInfoHeader()) { next_nonce=resp.getAuthenticationInfoHeader().getNextnonceParam(); } StatusLine status=resp.getStatusLine(); String result=status.getCode()+" "+status.getReason(); // update the renew_time int expires=0; if (resp.hasExpiresHeader()) { expires=resp.getExpiresHeader().getDeltaSeconds(); } else if (resp.hasContactHeader()) { Vector contacts=resp.getContacts().getHeaders(); for (int i=0; i<contacts.size(); i++) { int exp_i=(new ContactHeader((Header)contacts.elementAt(i))).getExpires(); if (exp_i>0 && (expires==0 || exp_i<expires)) expires=exp_i; } } if (expires>0 && expires<renew_time) renew_time=expires; printLog("Registration success: "+result,LogLevel.HIGH); if (listener!=null) listener.onUaRegistrationSuccess(this,target,contact,result); } } /** Callback function called when client sends back a failure response. */ public void onTransFailureResponse(TransactionClient transaction, Message resp) { if (transaction.getTransactionMethod().equals(SipMethods.REGISTER)) { StatusLine status=resp.getStatusLine(); int code=status.getCode(); if (code==401 && attempts<MAX_ATTEMPTS && resp.hasWwwAuthenticateHeader() && resp.getWwwAuthenticateHeader().getRealmParam().equalsIgnoreCase(realm)) { attempts++; Message req=transaction.getRequestMessage(); req.setCSeqHeader(req.getCSeqHeader().incSequenceNumber()); WwwAuthenticateHeader wah=resp.getWwwAuthenticateHeader(); String qop_options=wah.getQopOptionsParam(); //printLog("DEBUG: qop-options: "+qop_options,LogLevel.MEDIUM); qop=(qop_options!=null)? (char)34+"auth"+(char)34 : null; // BUGI client ei saa lähettää qop:ia //AuthorizationHeader ah=(new DigestAuthentication(SipMethods.REGISTER,req.getRequestLine().getAddress().toString(),wah,qop,null,username,passwd)).getAuthorizationHeader(); AuthorizationHeader ah=(new DigestAuthentication(SipMethods.REGISTER,req.getRequestLine().getAddress().toString(),wah,null,null,username,passwd)).getAuthorizationHeader(); req.setAuthorizationHeader(ah); TransactionClient t=new TransactionClient(sip_provider,req,this); t.request(); } else { String result=code+" "+status.getReason(); printLog("Registration failure: "+result,LogLevel.HIGH); if (listener!=null) listener.onUaRegistrationFailure(this,target,contact,result); } } } /** Callback function called when client expires timeout. */ public void onTransTimeout(TransactionClient transaction) { if (transaction.getTransactionMethod().equals(SipMethods.REGISTER)) { printLog("Registration failure: No response from server.",LogLevel.HIGH); if (listener!=null) listener.onUaRegistrationFailure(this,target,contact,"Timeout"); } } // ****************************** Logs ***************************** /** Adds a new string to the default Log */ void printLog(String str, int level) { if (log!=null) log.println("RegisterAgent: "+str,level+SipStack.LOG_LEVEL_UA); } /** Adds the Exception message to the default Log */ void printException(Exception e,int level) { if (log!=null) log.printException(e,level+SipStack.LOG_LEVEL_UA); } }