org.jitsi.jigasi.xmpp.CallControlComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.jitsi.jigasi.xmpp.CallControlComponent.java

Source

/*
 * Jigasi, the JItsi GAteway to SIP.
 *
 * Copyright @ 2015 Atlassian Pty Ltd
 *
 * 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.jitsi.jigasi.xmpp;

import net.java.sip.communicator.impl.protocol.jabber.extensions.rayo.*;
import net.java.sip.communicator.util.*;

import org.dom4j.*;
import org.jitsi.jigasi.*;
import org.jitsi.meet.*;
import org.jitsi.service.configuration.*;
import org.jitsi.xmpp.component.*;
import org.jivesoftware.smack.packet.*;
import org.osgi.framework.*;
import org.xmpp.component.*;
import org.xmpp.packet.IQ;
import org.xmpp.packet.Message;

/**
 * Experimental implementation of call control component that is capable of
 * utilizing Rayo XMPP protocol for the purpose of SIP gateway calls management.
 *
 * @author Pawel Domas
 */
public class CallControlComponent extends ComponentBase implements BundleActivator, CallsControl, ServiceListener {
    /**
     * The logger.
     */
    private final static Logger logger = Logger.getLogger(CallControlComponent.class);

    /**
     * Name of 'header' attribute that hold JVB room name.
     */
    public static final String ROOM_NAME_HEADER = "JvbRoomName";

    /**
     * Optional header for specifying password required to enter MUC room.
     */
    public static final String ROOM_PASSWORD_HEADER = "JvbRoomPassword";

    /**
     * JID allowed to make outgoing SIP calls.
     */
    public static final String ALLOWED_JID_P_NAME = "org.jitsi.jigasi.ALLOWED_JID";

    /**
     * The {@link SipGateway} service which manages gateway sessions.
     */
    private SipGateway gateway;

    /**
     * The only JID that will be allowed to create outgoing SIP calls. If not
     * set then anybody is allowed to do so.
     */
    private String allowedJid;

    /**
     * FIXME: temporary to be removed/fixed
     */
    //private Map<SipGateway, String> hangupMap
    //  = new HashMap<SipGateway, String>();

    /**
     * Creates new instance of <tt>CallControlComponent</tt>.
     * @param host the hostname or IP address to which this component will be
     *             connected.
     * @param port the port of XMPP server to which this component will connect.
     * @param domain the name of main XMPP domain on which this component will
     *               be served.
     * @param subDomain the name of subdomain on which this component will be
     *                  available.
     * @param secret the password used by the component to authenticate with
     *               XMPP server.
     */
    public CallControlComponent(String host, int port, String domain, String subDomain, String secret) {
        super(host, port, domain, subDomain, secret);
    }

    /**
     * Initializes this component.
     */
    public void init() {
        OSGi.start(this);
    }

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        this.gateway = ServiceUtils.getService(bundleContext, SipGateway.class);

        ConfigurationService config = ServiceUtils.getService(bundleContext, ConfigurationService.class);

        this.allowedJid = config.getString(ALLOWED_JID_P_NAME, null);

        if (allowedJid != null) {
            logger.info("JID allowed to make outgoing calls: " + allowedJid);
        }

        gateway.setCallsControl(this);

        gateway.setXmppServerName(getDomain());
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String[] discoInfoFeatureNamespaces() {
        return new String[] { "http://jitsi.org/protocol/jigasi", "urn:xmpp:rayo:0" };
    }

    @Override
    public String getDescription() {
        return "Call control component";
    }

    @Override
    public String getName() {
        return "Call control";
    }

    /**
     * Initializes new outgoing call.
     * @param roomName the name of the MUC room that holds JVB conference call.
     * @param roomPass optional password required to enter MUC room.
     * @param from source address(optional)
     * @param to destination call address/URI.
     * @return the call resource string that will identify newly created call.
     */
    String initNewCall(String roomName, String roomPass, String from, String to) {
        String callResource = generateNextCallResource();

        gateway.createOutgoingCall(to, roomName, roomPass, callResource);

        return callResource;
    }

    private String generateNextCallResource() {
        //FIXME: fix resource generation and check if created resource
        // is already taken
        return Long.toHexString(System.currentTimeMillis()) + "@" + getSubdomain() + "." + getDomain();
    }

    /**
     * Handles an <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>set</tt> which
     * represents a request.
     *
     * @param iq the <tt>org.xmpp.packet.IQ</tt> stanza of type <tt>set</tt>
     * which represents the request to handle
     * @return an <tt>org.xmpp.packet.IQ</tt> stanza which represents the
     * response to the specified request or <tt>null</tt> to reply with
     * <tt>feature-not-implemented</tt>
     * @throws Exception to reply with <tt>internal-server-error</tt> to the
     * specified request
     * @see AbstractComponent#handleIQSet(IQ)
     */
    @Override
    public IQ handleIQSet(IQ iq) throws Exception {
        try {
            org.jivesoftware.smack.packet.IQ smackIq = IQUtils.convert(iq);

            String fromBareJid = iq.getFrom().toBareJID();
            if (allowedJid != null && !allowedJid.equals(fromBareJid)) {
                org.jivesoftware.smack.packet.IQ error = org.jivesoftware.smack.packet.IQ
                        .createErrorResponse(smackIq, new XMPPError(XMPPError.Condition.not_allowed));

                return IQUtils.convert(error);
            } else if (allowedJid == null) {
                logger.warn("Requests are not secured by JID filter!");
            }

            if (smackIq instanceof RayoIqProvider.DialIq) {
                RayoIqProvider.DialIq dialIq = (RayoIqProvider.DialIq) smackIq;

                String from = dialIq.getSource();
                String to = dialIq.getDestination();

                String roomName = dialIq.getHeader(ROOM_NAME_HEADER);
                String roomPassword = dialIq.getHeader(ROOM_PASSWORD_HEADER);
                if (roomName == null)
                    throw new RuntimeException("No JvbRoomName header found");

                logger.info("Got dial request " + from + " -> " + to + " room: " + roomName);

                String callResource = initNewCall(roomName, roomPassword, from, to);

                callResource = "xmpp:" + callResource;

                RayoIqProvider.RefIq ref = RayoIqProvider.RefIq.createResult(smackIq, callResource);

                return IQUtils.convert(ref);
            } else if (smackIq instanceof RayoIqProvider.HangUp) {
                RayoIqProvider.HangUp hangUp = (RayoIqProvider.HangUp) smackIq;

                String callUri = hangUp.getTo();
                String callResource = callUri;

                GatewaySession session = gateway.getSession(callResource);

                if (session == null)
                    throw new RuntimeException("No gateway for call: " + callResource);

                //hangupMap.put(gateway, smackIq.getFrom());

                session.hangUp();

                org.jivesoftware.smack.packet.IQ result = org.jivesoftware.smack.packet.IQ.createResultIQ(smackIq);

                return IQUtils.convert(result);
            } else {
                return super.handleIQSet(iq);
            }
        } catch (Exception e) {
            logger.error(e, e);
            throw e;
        }
    }

    @Override
    public void callEnded(SipGateway gateway, String callResource) {
        // Send confirmation
        // FIXME: we've left the room already at this point
        /*EndExtension end = new EndExtension();
        end.setReason(
        new ReasonExtension(ReasonExtension.HANGUP));
        HeaderExtension header = new HeaderExtension();
        header.setName("uri");
        header.setValue("xmpp:" + gatewaySession.callResource);
            
        end.addChildExtension(header);
            
        gateway.sendPresenceExtension(end);*/
    }

    /**
     * Call resource currently has the form of e23gr547@callcontro.server.net.
     * This methods extract random call id part before '@' sign. In the example
     * above it is 'e23gr547'.
     * @param callResource the call resource/URI from which the call ID part
     *                     will be extracted.
     * @return extracted random call ID part from full call resource string.
     */
    @Override
    public String extractCallIdFromResource(String callResource) {
        return callResource.substring(0, callResource.indexOf("@"));
    }

    protected void sendPacketXml(String xmlToSend) {
        try {
            Document doc = DocumentHelper.parseText(xmlToSend);

            org.xmpp.packet.Message toSend = new Message(doc.getRootElement());

            send(toSend);
        } catch (DocumentException e) {
            logger.error(e, e);
        }
    }

    @Override
    public void serviceChanged(ServiceEvent serviceEvent) {
        if (serviceEvent.getType() != ServiceEvent.REGISTERED)
            return;

        ServiceReference ref = serviceEvent.getServiceReference();

        Object service = JigasiBundleActivator.osgiContext.getService(ref);

        if (!(service instanceof SipGateway))
            return;

        SipGateway gateway = (SipGateway) service;

        gateway.setCallsControl(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String allocateNewSession(SipGateway gateway) {
        return generateNextCallResource();
    }
}