Package org.cafesip.reference.jiplet

Source Code of org.cafesip.reference.jiplet.SipPresence

/*
* Created on July 7, 2005
*
* Copyright 2005 CafeSip.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.cafesip.reference.jiplet;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;

import javax.sip.Dialog;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipProvider;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.header.AcceptHeader;
import javax.sip.header.AllowEventsHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.EventHeader;
import javax.sip.header.ExpiresHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.Header;
import javax.sip.header.SubscriptionStateHeader;
import javax.sip.header.SupportedHeader;
import javax.sip.header.ToHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.cafesip.jiplet.Jiplet;
import org.cafesip.jiplet.JipletException;
import org.cafesip.jiplet.JipletLogger;
import org.cafesip.jiplet.JipletRequest;
import org.cafesip.jiplet.JipletResponse;
import org.cafesip.jiplet.JipletTimeout;
import org.cafesip.jiplet.JipletTimer;
import org.cafesip.jiplet.JipletTransaction;
import org.cafesip.jiplet.Pair;
import org.cafesip.jiplet.TimerEvent;
import org.cafesip.jiplet.config.jip.JipletConfig;

/**
* @author Becky McElroy
*
*/
public class SipPresence extends Jiplet
{
    private static int DEFAULT_SUBSCRIBE_DURATION = 3600; // seconds

    private static long SUBS_EXPIRY_CHECK_TIMER = 10000; // msec

    private SubscriptionList subscriptionList = new SubscriptionList();

    // timer that wakes up every so often and checks for expired subscriptions
    private TimerEvent timer = null;

    public void init(JipletConfig config) throws JipletException
    {
        info(" being initialized");
    }

    public void destroy()
    {
        if (timer != null)
        {
            cancelTimer(timer);
            timer = null;
        }

        subscriptionList.dispose();
    }

    public void doExtension(JipletRequest request)
    {
        RequestEvent event = request.getRequestEvent();

        try
        {
            if (event.getRequest().getMethod().equals("PUBLISH") == true)
            {
                if (isDebugEnabled() == true)
                {
                    debug("SipPresence: Received PUBLISH message: \n"
                            + event.getRequest().toString());
                }

                // check event type, store this message in subscriptionList for
                // presentity status, ignore if cseq <= last one from this
                // subscriber
                // can use this info when sending notify (if pidf), also remove
                // from
                // subscriptionList
                // when presentity unregisters

                try
                {
                    Response response = getMessageFactory().createResponse(
                            Response.OK, event.getRequest());
                    sendResponse(event, response);
                }
                catch (Exception e)
                {
                    error("A PUBLISH message could not be processed because an exception occured.\n"
                            + e.getClass().getName()
                            + ": "
                            + e.getMessage()
                            + "\n" + JipletLogger.getStackTrace(e));
                }
            }
        }
        finally
        {
            forward(request, "ExampleSIPRecorderJiplet");
        }
    }

    public void doSubscribe(JipletRequest requestEvent)
    {
        if (timer == null)
        {
            timer = startTimer(SUBS_EXPIRY_CHECK_TIMER, true, requestEvent,
                    null);
        }

        try
        {
            RequestEvent event = requestEvent.getRequestEvent();
            Request request = event.getRequest();

            if (isDebugEnabled() == true)
            {
                debug("SipPresence: Received SUBSCRIBE: " + request);
            }

            // look at the event header first
            EventHeader ev_hdr = (EventHeader) request
                    .getHeader(EventHeader.NAME);
            if (eventHeaderValid(request, ev_hdr) == false)
            {
                // 489 Bad Event, debug msg already output
                Response response = getMessageFactory().createResponse(
                        Response.BAD_EVENT, request);
                sendResponse(event, response);
                return;
            }

            if (ev_hdr.getEventType().equals("presence.winfo") == true)
            {
                // watcher info placeholder - we don't support this package
                Response response = getMessageFactory().createResponse(
                        Response.BAD_EVENT, request);
                sendResponse(event, response);
                return;
            }

            // determine the duration
            int duration = DEFAULT_SUBSCRIBE_DURATION;
            ExpiresHeader exp = (ExpiresHeader) request
                    .getHeader(ExpiresHeader.NAME);
            if (exp != null)
            {
                duration = exp.getExpires();
            }

            // find the active subscription (if exists) for this message
            Subscription sub = subscriptionList.findSubscription(request);

            if (sub != null)
            {
                if (duration > 0)
                {
                    refresh(event, sub, duration);
                    return;
                }

                // this is an unsubscribe
                try
                {
                    unsubscribe(event, sub, duration);
                }
                catch (Exception e)
                {
                    error("A SUBSCRIBE message could not be processed because an exception occured.\n"
                            + e.getClass().getName()
                            + ": "
                            + e.getMessage()
                            + "\n" + JipletLogger.getStackTrace(e));
                }

                // drop the subscription
                subscriptionList.removeSubscription(sub);
                sub.dispose();

                return;
            }

            // new subscription

            int response_code = Response.OK;
            String reason = "OK";
            String state = SubscriptionStateHeader.ACTIVE;

            // send the response right away
            String to_tag = new Long(Calendar.getInstance().getTimeInMillis())
                    .toString();
            Response response = createResponse(response_code, reason, to_tag,
                    request, duration);
            ServerTransaction transaction = sendResponse(event, response);

            if (transaction == null)
            {
                // stack couldn't give us a dialog
                return;
            }
            Dialog dialog = transaction.getDialog();

            sub = new Subscription(this, subscriptionList, SubscriptionList
                    .getSubscriptionId(request), ev_hdr.getEventId(), ev_hdr
                    .getEventType(), ((FromHeader) request
                    .getHeader(FromHeader.NAME)).getAddress(),
                    ((ToHeader) request.getHeader(ToHeader.NAME)).getAddress(),
                    to_tag);
            sub.setDialog(dialog);
            sub.setSubscriptionState(state);
            sub.setTimeLeft(duration);

            if (duration == 0) // it's a fetch or contact was removed from the
            // phone
            {
                sub.setTerminationReason("Presence Fetch");
                sub.setSubscriptionState(SubscriptionStateHeader.TERMINATED);
            }
            else
            {
                subscriptionList.addSubscription(sub);
            }

            sub.sendNotify((SipProvider) event.getSource());
        }
        catch (Exception e)
        {
            error("A SUBSCRIBE message could not be processed because an exception occured.\n"
                    + e.getClass().getName()
                    + ": "
                    + e.getMessage()
                    + "\n"
                    + JipletLogger.getStackTrace(e));
        }
        finally
        {
            forward(requestEvent, "ExampleSIPRecorderJiplet");
        }

        // later: conversion: request URI, from, to can be: pres, sip, or
        // sips
        // later: note the Accept Header for Notification sending - The
        // presence of the "Allow-Events" header in a message is sufficient
        // to indicate support for NOTIFY. The "methods" parameter for
        // Contact may also be used to specifically announce support for
        // NOTIFY messages when registering.
    }

    private void unsubscribe(RequestEvent event, Subscription sub, int duration)
            throws Exception
    {
        int response_code = Response.OK;
        String reason = "OK";

        // set subscription state
        sub.setSubscriptionState(SubscriptionStateHeader.TERMINATED);
        sub.setTerminationReason("unsubscribed");

        // send the response
        Response response = createResponse(response_code, reason, sub
                .getToTag(), event.getRequest(), duration);
        ServerTransaction transaction = sendResponse(event, response);

        // send NOTIFY - why dialog state terminated by this time? Stack is
        // doing it?
        if (transaction != null)
        {
            // / sub.sendNotify((SipProvider) event.getSource());
        }
    }

    private void refresh(RequestEvent event, Subscription sub, int duration)
            throws Exception
    {
        int response_code = Response.ACCEPTED;
        String reason = "Accepted";

        if (sub.getSubscriptionState().equals(SubscriptionStateHeader.ACTIVE) == true)
        {
            response_code = Response.OK;
            reason = "OK";
        }
        else if (sub.getSubscriptionState().equals(
                SubscriptionStateHeader.TERMINATED) == true)
        {
            reason = sub.getTerminationReason();
            if (reason.equals("blocked"))
            {
                response_code = Response.DECLINE;
            }
            else if (reason.equals("timeout"))
            {
                response_code = Response.OK;
            }
        }

        // send the response
        Response response = createResponse(response_code, reason, sub
                .getToTag(), event.getRequest(), duration);
        ServerTransaction transaction = sendResponse(event, response);

        // update time left
        sub.setTimeLeft(duration);

        // send NOTIFY
        if (transaction != null)
        {
            sub.sendNotify((SipProvider) event.getSource());
        }
    }

    private boolean eventHeaderValid(Request request, EventHeader ev_hdr)
    {
        if (ev_hdr == null)
        {
            if (isDebugEnabled() == true)
            {
                error("Received a SUBSCRIBE message with no event header from "
                        + ((FromHeader) request.getHeader(FromHeader.NAME))
                                .getAddress().getURI().toString());
            }

            return false;
        }

        if ((ev_hdr.getEventType().equals("presence") == false)
                && (ev_hdr.getEventType().equals("presence.winfo") == false)) // watcher
        // info
        {
            if (isDebugEnabled() == true)
            {
                debug("SipPresence: Received non-presence SUBSCRIBE from "
                        + ((FromHeader) request.getHeader(FromHeader.NAME))
                                .getAddress().getURI().toString());
            }

            return false;
        }

        return true;
    }

    private Response createResponse(int statusCode, String reasonPhrase,
            String toTag, Request request, int duration) throws Exception
    {
        Response response = getMessageFactory().createResponse(statusCode,
                request);

        response.addHeader(getHeaderFactory().createExpiresHeader(duration));
        response.setReasonPhrase(reasonPhrase);
        ToHeader to = (ToHeader) response.getHeader(ToHeader.NAME);
        to.setTag(toTag);

        if (statusCode / 100 == 2) // 2xx
        {
            ContactInfo reg_info = (ContactInfo) LocationDatabase.getInstance()
                    .get(to.getAddress().getURI().toString());
            ContactHeader c = null;
            if (reg_info != null)
            {
                c = reg_info.getContact();
            }

            if (isDebugEnabled() == true)
            {
                debug("SipPresence: creating response with contact header = "
                        + c);
                debug("   got reg_info for URI "
                        + to.getAddress().getURI().toString());
                debug("   reg_info = " + reg_info);
            }

            if (c != null)
            {
                response.setHeader(c);
            }

            AcceptHeader accept = getHeaderFactory().createAcceptHeader(
                    "application", "pidf+xml");
            response.addHeader(accept);

            SupportedHeader supported = getHeaderFactory()
                    .createSupportedHeader("ms-benotify"); // for
            // messenger
            response.addHeader(supported);

            SupportedHeader shdr = getHeaderFactory().createSupportedHeader(
                    "presence");
            response.addHeader(shdr);

            Header hdr = getHeaderFactory().createHeader("ms-keep-alive",
                    "UAS; tcp=yes; hop-hop=no; end-end=yes; timeout=600");
            response.addHeader(hdr);

            AllowEventsHeader ahdr = getHeaderFactory()
                    .createAllowEventsHeader("presence");
            response.addHeader(ahdr);
        }

        return response;
    }

    private ServerTransaction sendResponse(RequestEvent event, Response response)
            throws Exception
    {
        ServerTransaction trans = event.getServerTransaction();
        if (trans == null)
        {
            try
            {
                trans = ((SipProvider) event.getSource())
                        .getNewServerTransaction(event.getRequest());
            }
            catch (TransactionAlreadyExistsException e)
            {
                if (isDebugEnabled() == true)
                {
                    debug("SipPresence: SipPresence.sendResponse() -- no existing transaction, cannot get a new one - sending response statelessly.");
                }
                ((SipProvider) event.getSource()).sendResponse(response);
                return null;
            }
        }

        if (trans == null)
        {
            if (isDebugEnabled() == true)
            {
                debug("SipPresence: SipPresence.sendResponse() -- no existing transaction, get new returned null - sending response statelessly.");
            }
            ((SipProvider) event.getSource()).sendResponse(response);
            return null;
        }

        trans.sendResponse(response);
        return trans;
    }

    /*
     * @see org.cafesip.jiplet.Jiplet#processResponse(org.cafesip.jiplet.JipletResponse)
     */
    public void processResponse(JipletResponse response)
    {
        super.processResponse(response);

        try
        {
            JipletTransaction trans = response.getTransaction();
            Subscription s = subscriptionList.findSubscription((String) trans
                    .getAttribute("subscription"));

            if (s != null)
            {
                s.processResponse(response.getResponseEvent());
            }
        }
        finally
        {
            forward(response, "ExampleSIPRecorderJiplet");
        }
    }

    /*
     * @see org.cafesip.jiplet.Jiplet#processTimeout(org.cafesip.jiplet.JipletTimeout)
     */
    public void processTimeout(JipletTimeout timeout)
    {
        try
        {
            // this method is called if there was no response to the NOTIFY
            // request
            // we sent
            super.processTimeout(timeout);

            JipletTransaction trans = timeout.getTransaction();
            Subscription s = subscriptionList.findSubscription((String) trans
                    .getAttribute("subscription"));

            if (s != null)
            {
                s.processTimeout(timeout.getTimeoutEvent());
            }
        }
        finally
        {
            forward(timeout, "ExampleSIPRecorderJiplet");
        }
    }

    public void processTimer(JipletTimer timer)
    {
        // are we here because of expired registration(s)?
        ArrayList expired = (ArrayList) timer
                .getAttribute("expiredRegistrations");

        if (expired != null)
        {
            Iterator i = expired.iterator();
            while (i.hasNext())
            {
                processRegistration((String) i.next(), null);
            }

            return;
        }

        // no, we are here to audit subscriptions - check for expired ones
        expired = subscriptionList.collectExpiredSubscriptions();

        // end the expired subscriptions
        Iterator i = expired.iterator();
        while (i.hasNext())
        {
            Subscription s = (Subscription) i.next();

            if (isDebugEnabled() == true)
            {
                debug("SipPresence: Subscription has expired. Idling Subscription with ID "
                        + s.getSubscriptionId()
                        + " from "
                        + s.getSubscribingParty().getURI().toString());
            }

            s.sendNotify();
        }
    }

    /*
     * @see org.cafesip.jiplet.Jiplet#doRegister(javax.sip.RequestEvent)
     */
    public void doRegister(JipletRequest requestEvent)
    {
        if (isDebugEnabled() == true)
        {
            debug("SipPresence: received REGISTER event");
        }

        try
        {
            Pair status = (Pair) requestEvent.getAttribute("registrationInfo");
            if (status == null)
            {
                error("SipPresence.doRegister() - SCOPE VARIABLE STATUS IS NULL - cannot notify buddies of registration/unregistration.");
                return;
            }

            processRegistration((String) status.getFirst(),
                    (ContactInfo) status.getSecond());

        }
        finally
        {
            forward(requestEvent, "ExampleSIPRecorderJiplet");
        }
        return;
    }

    private void processRegistration(String registree, ContactInfo reg_info)
    {
        if (isDebugEnabled() == true)
        {
            if (reg_info != null)
            {
                debug("SipPresence: new registree = " + registree
                        + ", contact addr = "
                        + reg_info.getContact().toString() + ", expiry = "
                        + reg_info.getExpiryTime().toString());
            }
            else
            {
                debug("SipPresence: registree logged out: " + registree);
            }
        }

        registree = getActualAddress(registree);

        // who is tracking the registree? Need to notify them.
        subscriptionList.notifyWatchers(registree, reg_info);

        // if this is an unregistration, idle any subscriptions the registree
        // has going
        if (reg_info == null)
        {
            subscriptionList.idleUserSubscriptions(registree);
        }
    }

    private String getActualAddress(String uri)
    {
        // an address can be of the form sip:name@domain:port;transport=x
        // We only want to store up to sip:name@domain
        int index = uri.indexOf(":", 4);
        if (index >= 0)
        {
            return uri.substring(0, index);
        }

        index = uri.indexOf(";");
        if (index >= 0)
        {
            return uri.substring(0, index);
        }

        return uri;
    }
}
TOP

Related Classes of org.cafesip.reference.jiplet.SipPresence

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.