/* * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This file is part of 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 * * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ /* Modified by: * Daina Interrante (daina.interrante@studenti.unipr.it) */ package org.zoolu.sip.dialog; import org.zoolu.sip.address.*; import org.zoolu.sip.transaction.*; import org.zoolu.sip.dialog.*; import org.zoolu.sip.message.*; import org.zoolu.sip.header.*; import org.zoolu.sip.header.*; import org.zoolu.sip.provider.*; import org.zoolu.tools.LogLevel; import java.util.Date; /** NotifierDialog. */ public class NotifierDialog extends Dialog implements TransactionClientListener/*, TransactionServerListener*/ { /** String "active" */ protected static final String ACTIVE="active"; /** String "pending" */ protected static final String PENDING="pending"; /** String "terminated" */ protected static final String TERMINATED="terminated"; /** The SubscriberDialog listener */ NotifierDialogListener listener; /** The current subscribe method */ Message subscribe_req; /** The current subscribe transaction */ TransactionServer subscribe_transaction; /** The current notify transaction */ TransactionClient notify_transaction; /** The event name */ String event; /** The subscription id */ String id; /** Internal state D_INIT (the starting point) */ protected static final int D_INIT=0; /** Internal state D_WAITING (listening for the first subscription request) */ protected static final int D_WAITING=1; /** Internal state D_SUBSCRIBED (first subscription request arrived) */ protected static final int D_SUBSCRIBED=2; /** Internal state D_PENDING (first subscription request has been accepted) */ protected static final int D_PENDING=3; /** Internal state D_ACTIVE (subscription has been activated) */ protected static final int D_ACTIVE=4; /** Internal state D_TERMINATED (first subscription request has been refused or subscription has been terminated) */ protected static final int D_TERMINATED=9; // ************************* Protected methods ************************ /** Gets the dialog state */ protected String getStatus() { switch (status) { case D_INIT : return "D_INIT"; case D_WAITING : return "D_WAITING"; case D_SUBSCRIBED : return "D_SUBSCRIBED"; case D_PENDING : return "D_PENDING"; case D_ACTIVE : return "D_ACTIVE"; case D_TERMINATED : return "D_TERMINATED"; default : return null; } } // ************************** Public methods ************************** /** Whether the dialog is in "early" state. */ public boolean isEarly() { return (status<D_PENDING); } /** Whether the dialog is in "confirmed" state. */ public boolean isConfirmed() { return (status>=D_PENDING && status<D_TERMINATED); } /** Whether the dialog is in "active" state. */ public boolean isTerminated() { return (status==D_TERMINATED); } /** Whether the subscription is "pending". */ public boolean isSubscriptionPending() { return (status>=D_SUBSCRIBED && status<D_ACTIVE); } /** Whether the subscription is "active". */ public boolean isSubscriptionActive() { return (status==D_ACTIVE); } /** Whether the subscription is "terminated". */ public boolean isSubscriptionTerminated() { return (status==D_TERMINATED); } /** Gets event type. */ public String getEvent() { return event; } /** Gets the event "id" parameter. */ public String getId() { return id; } // **************************** Costructors **************************** /** Creates a new NotifierDialog. */ public NotifierDialog(SipProvider sip_provider, NotifierDialogListener listener) { super(sip_provider); init(listener); } /** Creates a new NotifierDialog for the already received SUBSCRIBE request <i>subscribe</i>. */ public NotifierDialog(SipProvider sip_provider, Message subscribe, NotifierDialogListener listener) { super(sip_provider); init(listener); changeStatus(D_SUBSCRIBED); subscribe_req=subscribe; subscribe_transaction=new TransactionServer(sip_provider,subscribe,null); update(Dialog.UAS,subscribe); EventHeader eh=subscribe.getEventHeader(); if (eh!=null) { event=eh.getEvent(); id=eh.getId(); } } /** Inits the NotifierDialog. */ private void init(NotifierDialogListener listener) { this.listener=listener; this.subscribe_transaction=null; this.notify_transaction=null; this.subscribe_req=null; this.event=null; this.id=null; changeStatus(D_INIT); } // *************************** Public methods ************************** /** Listen for the first subscription request. */ public void listen() { printLog("inside method listen()",LogLevel.MEDIUM); if (!statusIs(D_INIT)) { printLog("first subscription already received",LogLevel.MEDIUM); return; } // else changeStatus(D_WAITING); // listen for the first SUBSCRIBE request sip_provider.addSipProviderListener(new MethodIdentifier(SipMethods.SUBSCRIBE),this); } /** Accepts the subscription request (sends a "202 Accepted" response). */ public void accept(int expires, String contact) { printLog("inside accept()",LogLevel.MEDIUM); respond(202,SipResponses.reasonOf(202),expires,contact,null,null); } /** Refuses the subscription request. */ public void refuse() { printLog("inside refuse()",LogLevel.MEDIUM); respond(403,SipResponses.reasonOf(403),-1,null,null,null); } /** 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, int expires, String contact, String content_type, String body) { printLog("inside respond("+code+","+reason+")",LogLevel.MEDIUM); NameAddress contact_url=null; if (contact!=null) contact_url=new NameAddress(contact); Message resp=MessageFactory.createResponse(subscribe_req,code,SipResponses.reasonOf(code),contact_url); if (expires>=0) resp.setExpiresHeader(new ExpiresHeader(expires)); if (body!=null) resp.setBody(content_type,body); respond(resp); } /** Responds with <i>resp</i>. */ public void respond(Message resp) { printLog("inside respond(resp)",LogLevel.MEDIUM); if (resp.getStatusLine().getCode()>=200) update(UAS,resp); subscribe_transaction.respondWith(resp); } /** Activates the subscription (subscription goes into 'active' state). */ public void activate() { activate(SipStack.default_expires); } /** Activates the subscription (subscription goes into 'active' state). */ public void activate(int expires) { notify(ACTIVE,expires,null,null); } /** Makes the subscription pending (subscription goes into 'pending' state). */ public void pending() { pending(SipStack.default_expires); } /** Makes the subscription pending (subscription goes into 'pending' state). */ public void pending(int expires) { notify(PENDING,expires,null,null); } /** Terminates the subscription (subscription goes into 'terminated' state). */ public void terminate() { terminate(null); } /** Terminates the subscription (subscription goes into 'terminated' state). */ public void terminate(String reason) { Message req=MessageFactory.createNotifyRequest(this,event,id,null,null); SubscriptionStateHeader sh=new SubscriptionStateHeader(TERMINATED); if (reason!=null) sh.setReason(reason); //sh.setExpires(0); req.setSubscriptionStateHeader(sh); notify(req); } /** Sends a NOTIFY. */ public void notify(String state, int expires, String content_type, String body) { Message req=MessageFactory.createNotifyRequest(this,event,id,content_type,body); if (state!=null) { SubscriptionStateHeader sh=new SubscriptionStateHeader(state); if (expires>=0) sh.setExpires(expires); req.setSubscriptionStateHeader(sh); } notify(req); } /** Sends a NOTIFY. */ public void notify(Message req) { String subscription_state=req.getSubscriptionStateHeader().getState(); if (subscription_state.equalsIgnoreCase(ACTIVE) && (statusIs(D_SUBSCRIBED) || statusIs(D_PENDING))) changeStatus(D_ACTIVE); else if (subscription_state.equalsIgnoreCase(PENDING) && statusIs(D_SUBSCRIBED)) changeStatus(D_PENDING); else if (subscription_state.equalsIgnoreCase(TERMINATED) && !statusIs(D_TERMINATED)) changeStatus(D_TERMINATED); TransactionClient notify_transaction=new TransactionClient(sip_provider,req,this); notify_transaction.request(); } // ************** Inherited from TransactionClientListener ************** /** When the TransactionClient is (or goes) in "Proceeding" state and receives a new 1xx provisional response */ public void onTransProvisionalResponse(TransactionClient tc, Message resp) { printLog("onTransProvisionalResponse()",LogLevel.MEDIUM); // do nothing. } /** When the TransactionClient goes into the "Completed" state receiving a 2xx response */ public void onTransSuccessResponse(TransactionClient tc, Message resp) { printLog("onTransSuccessResponse()",LogLevel.MEDIUM); StatusLine status_line=resp.getStatusLine(); if (listener!=null) listener.onDlgNotificationSuccess(this,status_line.getCode(),status_line.getReason(),resp); } /** When the TransactionClient goes into the "Completed" state receiving a 300-699 response */ public void onTransFailureResponse(TransactionClient tc, Message resp) { printLog("onTransFailureResponse()",LogLevel.MEDIUM); StatusLine status_line=resp.getStatusLine(); if (listener!=null) listener.onDlgNotificationFailure(this,status_line.getCode(),status_line.getReason(),resp); } /** When the TransactionClient goes into the "Terminated" state, caused by transaction timeout */ public void onTransTimeout(TransactionClient tc) { printLog("onTransTimeout()",LogLevel.MEDIUM); if (!statusIs(D_TERMINATED)) { changeStatus(D_TERMINATED); if (listener!=null) listener.onDlgNotifyTimeout(this); } } // ************** Inherited from SipProviderListener ************** /** When a new Message is received by the SipProvider. */ public void onReceivedMessage(SipProvider provider, Message msg) { printLog("onReceivedMessage()",LogLevel.MEDIUM); if (statusIs(D_TERMINATED)) { printLog("subscription already terminated: message discarded",LogLevel.MEDIUM); return; } // else if(msg.isRequest() && msg.isSubscribe()) { if (statusIs(this.D_WAITING)) { // the first SUBSCRIBE request changeStatus(D_SUBSCRIBED); sip_provider.removeSipProviderListener(new MethodIdentifier(SipMethods.SUBSCRIBE)); } subscribe_req=msg; NameAddress target=msg.getToHeader().getNameAddress(); NameAddress subscriber=msg.getFromHeader().getNameAddress(); EventHeader eh=msg.getEventHeader(); if (eh!=null) { event=eh.getEvent(); id=eh.getId(); } update(UAS,msg); subscribe_transaction=new TransactionServer(sip_provider,msg,null); if (listener!=null) listener.onDlgSubscribe(this,target,subscriber,event,id,msg); } else { printLog("message is not a SUBSCRIBE: message discarded",LogLevel.HIGH); } } //**************************** Logs ****************************/ /** Adds a new string to the default Log */ protected void printLog(String str, int level) { if (log!=null) log.println("NotifierDialog#"+dialog_sqn+": "+str,level+SipStack.LOG_LEVEL_DIALOG); } }