Package org.asteriskjava.iax.protocol

Source Code of org.asteriskjava.iax.protocol.ProtocolControlFrame

package org.asteriskjava.iax.protocol;


import org.asteriskjava.iax.audio.javasound.AudioInterface;
import org.asteriskjava.iax.util.ByteBuffer;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* Representation of the IAX2 protocol control frame, also known as an
* IAX Frame in the draft.
*
* @author <a href="mailto:thp@westhawk.co.uk">Tim Panton</a>
* @version $Revision: 1.31 $ $Date: 2006/11/16 14:23:31 $
*/
public class ProtocolControlFrame
        extends FullFrame {

    final static int NEW = 1; //Initiate a new call
    final static int PING = 2; //Ping request
    final static int PONG = 3; //Ping reply
    final static int ACK = 4; //Acknowledgement
    final static int HANGUP = 5; //Initiate call teardown
    final static int REJECT = 6; //Reject
    final static int ACCEPT = 7; //Accepted
    final static int AUTHREQ = 8; //Authentication request
    final static int AUTHREP = 9; // Authentication reply
    final static int INVAL = 10; //Invalid call
    final static int LAGRQ = 11; //Lag request
    final static int LAGRP = 12; //Lag reply
    final static int REGREQ = 13; //Registration request
    final static int REGAUTH = 14; //Registration authenticate
    final static int REGACK = 15; //Registration acknowledgement
    final static int REGREJ = 16; //Registration reject
    final static int REGREL = 17; //Registration release
    final static int VNAK = 18; //Video/Voice retransmit request
    final static int DPREQ = 19; //Dialplan request
    final static int DPREP = 20; //Dialplan response
    final static int DIAL = 21; //Dial
    final static int TXREQ = 22; //Transfer request
    final static int TXCNT = 23; //Transfer connect
    final static int TXACC = 24; //Transfer accept
    final static int TXREADY = 25; //Transfer ready
    final static int TXREL = 26; //Transfer release
    final static int TXREJ = 27; //Transfer reject
    final static int QUELCH = 28; //Halt audio/video transmission
    final static int UNQUELCH = 29; // Resume audio/video transmission
    final static int POKE = 30; //Poke request
    final static int PAGE = 31; //Paging call description
    final static int MWI = 32; //Message waiting indication
    final static int UNSUPPORT = 33; //Unsupported message
    final static int TRANSFER = 34; //Remote transfer request
    final static String controlText[] = {
            "ZERO",
            "Initiate a new call",
            "Ping request",
            "Ping or poke reply",
            "Acknowledgement",
            "Initiate call teardown",
            "Reject",
            "Accepted",
            "Authentication request",
            "Authentication reply",
            "Invalid call",
            "Lag request",
            "Lag reply",
            "Registration request",
            "Registration authenticate",
            "Registration acknowledgement",
            "Registration reject",
            "Registration release",
            "Video/Voice retransmit request",
            "Dialplan request",
            "Dialplan response",
            "Dial",
            "Transfer request",
            "Transfer connect",
            "Transfer accept",
            "Transfer ready",
            "Transfer release",
            "Transfer reject",
            "Halt audio/video transmission",
            "Resume audio/video transmission",
            "Poke request",
            "Paging call description",
            "Message waiting indication",
            "Unsupported message",
            "Remote transfer request"
    };
    /**
     * The information element
     */
    private InfoElement _iep;

    /**
     * The inbound constructor.
     *
     * @param p0 The Call object
     * @param p1 The incoming message bytes
     */
    public ProtocolControlFrame(Call p0, byte[] p1) {
        super(p0, p1);
        this._iamanack = (this._subclass == ACK);
        _iep = new InfoElement(this._data);
        try {
            _iep.parse(this);
        } catch (IAX2ProtocolException ex) {
            Log.warn(ex.getMessage());
        }
    }

    /**
     * The outbound constructor.
     *
     * @param p0 The Call object
     */
    public ProtocolControlFrame(Call p0) {
        super(p0);
        this._frametype = FullFrame.IAXCONTROL;
    }

    /**
     * ack is called to send any required response.
     * Switch on the type of message just received.
     */
    void ack() {
        logInbound();
        switch (this._subclass) {
            case POKE:
                Log.debug("Recieved a POKE");
                sendMyPong();
                break;
            case PING:
                sendPong();
                break;
            case PONG:
                Log.debug("Sending Ack frame");
                sendAck();
                _call.setPong(true);
                break;
            case ACK:
                break;
            case AUTHREQ:
                sendAuthRep();
                break;
            case REGAUTH:
                if (_call.isForReg()) {
                    sendRegReq();
                } else {
                    sendRegRel();
                }
                break;
            case REGACK:
                Log.debug("Sending Ack frame");
                sendAck();
                if (_call.isForReg()) {
                    _call.setRegistered(true);
                } else {
                    _call.setRegistered(false);
                }
                break;
            case REGREJ:
                Log.debug("Sending Ack frame");
                sendAck();
                _call.setRegistered(false);
                break;
            case TXREQ:
                Log.debug("Sending TXREJ frame");
                sendTxrej();
                break;
            case INVAL:
                sendAck();
                _call.removeSelf();
                break;
            case LAGRQ:
                Log.debug("Sending lag reply");
                sendAck();
                sendLagReply();
                break;
            case ACCEPT:
                Log.debug("Sending Ack frame");
                sendAck();
                _call.setAudioFormat(_iep.format);
                _call.setAccepted(true);
                _call.setRno(new Character((char) (0xffff & this._sCall)));

                //set Ringing ?
                break;
            case REJECT:
                if ((_iep != null) && (_iep.cause != null)) {
                    Log.warn("Cause " + _iep.cause);
                }
                Log.debug("Sending Ack frame");
                sendAck();
                _call.setAccepted(false);
                break;
            case HANGUP:
                Log.debug("Sending Ack frame");
                sendAck();
                int val = 0;
                if (_iep.causecode != null) {
                    val = _iep.causecode.intValue();
                }
                _call.hungup(val);
                break;
            case NEW:
                Log.debug("Got New");
                sendAcceptOrRej();
                break;
            default:
                Log.warn("Unhandled PCF");
                break;
        }
    }

    /**
     * sendTxrej
     */
    private void sendTxrej() {
        ProtocolControlFrame rej = mkAck(ProtocolControlFrame.TXREJ);
        InfoElement nip = new InfoElement();
        nip.causecode = new Integer(29);
        nip.cause = "Facility rejected";
        rej.sendMe(nip);
    }

    /**
     * Sends a hangup (HANGUP).
     */
    public void sendHangup() {
        _sCall = _call.getLno().charValue();
        _dCall = _call.getRno().charValue();
        _iseq = _call.getIseq();
        _oseq = _call.getOseq();
        _subclass = ProtocolControlFrame.HANGUP;

        InfoElement ie = new InfoElement();
        ie.causecode = new Integer(16); // Normal call clearing
        Log.warn("Sending Hangup");
        sendMe(ie);
    }

    /**
     * Sends the initial registration request for the specified
     * username with the refresh time (REGREQ).
     *
     * @param username The username
     * @param refresh  The number of seconds before the registration
     *                 expires
     */
    void sendRegReq(String username, int refresh) {
        _sCall = _call.getLno().charValue();
        _dCall = 0;
        _iseq = _call.getIseq();
        _oseq = _call.getOseqInc();
        _subclass = ProtocolControlFrame.REGREQ;

        InfoElement ie = new InfoElement();
        ie.username = username;
        ie.refresh = new Integer(refresh);
        Log.debug("Sending initial RegRequest");
        sendMe(ie);
    }

    /**
     * Sends the complete registration request with authentication
     * credentials (REGREQ).
     */
    void sendRegReq() {
        ProtocolControlFrame ack = mkAck(ProtocolControlFrame.REGREQ);
        String p = _call.getPassword();
        InfoElement nip = new InfoElement();
        nip.refresh = new Integer(60);
        buildAuthInfoElements(_iep, nip, p);
        Log.debug("Sending completed RegRequest");
        ack.sendMe(nip);
    }

    /**
     * Sends the initial registration release for the specified
     * username (REGREL).
     *
     * @param username The username
     */
    void sendRegRel(String username) {
        _sCall = _call.getLno().charValue();
        _dCall = 0;
        _iseq = _call.getIseq();
        _oseq = _call.getOseqInc();
        _subclass = ProtocolControlFrame.REGREL;

        InfoElement ie = new InfoElement();
        ie.username = username;
        Log.debug("Sending initial RegRelease");
        sendMe(ie);
    }

    /**
     * Sends the complete registration release with authentication
     * credentials (REGREL).
     */
    void sendRegRel() {
        ProtocolControlFrame ack = mkAck(ProtocolControlFrame.REGREL);
        String p = _call.getPassword();
        InfoElement nip = new InfoElement();
        buildAuthInfoElements(_iep, nip, p);
        Log.debug("Sending completed RegRelease");
        ack.sendMe(nip);
    }

    /**
     * Sends a POKE message to test connectivity of a remote IAX peer.
     * <p/>
     * <p>
     * It MUST be sent when there is no existing call to the remote
     * endpoint. It MAY also be used to "qualify" a user to a remote
     * peer, so that the remote peer can maintain awareness of the state
     * of the user. A POKE MUST have 0 as its destination call number.
     * <br/>
     * <br/>
     * Upon receiving a POKE message, the peer SHOULD respond with a PONG message.
     * </p>
     */
    void sendPoke() {
        _sCall = _call.getLno().charValue();
        _dCall = 0;
        _iseq = _call.getIseq();
        _oseq = _call.getOseqInc();
        _subclass = ProtocolControlFrame.POKE;

        Log.debug("Sending Poke");
        // POKE has no IE
        sendMe((InfoElement) null);
    }

    void sendQuelch() {
        _sCall = _call.getLno().charValue();
        _dCall = 0;
        _iseq = _call.getIseq();
        _oseq = _call.getOseqInc();
        _subclass = ProtocolControlFrame.QUELCH;

        Log.debug("Sending QUELCH");

        sendMe((InfoElement) null);
    }

    void sendUnQuelch() {
        _sCall = _call.getLno().charValue();
        _dCall = 0;
        _iseq = _call.getIseq();
        _oseq = _call.getOseqInc();
        _subclass = ProtocolControlFrame.UNQUELCH;

        Log.debug("Sending UNQUELCH");

        sendMe((InfoElement) null);
    }

    /**
     * Logs message.
     *
     * @param inout Additional text to log.
     */
    @Override
    protected void log(String inout) {
        StringBuilder bu = new StringBuilder();
        bu.append(inout).append(", PCF subClass: ");
        if (_subclass < controlText.length) {
            bu.append(controlText[_subclass]);
        }
        super.log(bu.toString());
    }

    // Birgit: Why not remove this. It does less than its parent class.

    /**
     * arrived is called when a packet arrives. This method is
     * empty.
     *
     * @throws IAX2ProtocolException
     */
    @Override
    void arrived() throws IAX2ProtocolException {
    }

    /**
     * Sends this object with the specified IE.
     *
     * @param ie The IE
     */
    void sendMe(InfoElement ie) {
        ByteBuffer buff = ByteBuffer.allocate(2048);
        buff.putChar((char) (0x8000 | _sCall));
        int rd = _dCall;
        if (_retry) {
            rd |= 0x8000;
        }
        buff.putChar((char) rd);
        long tst = this.getTimestampVal();
        tst = ((0x100000000L & tst) > 0) ? tst - 0x100000000L : tst;
        buff.putInt((int) tst);
        buff.put((byte) _oseq);
        buff.put((byte) _iseq);
        buff.put((byte) _frametype);
        int sc = _subclass;
        if (_cbit) {
            sc |= 0x80;
        }
        buff.put((byte) sc);
        if (ie != null) {
            ie.update(buff);
        }
        sendAndStore(buff);
    }

    /**
     * Commit this frame. This method is called when a packet we sent
     * has been acked.
     *
     * @param ack The ack frame sent to us
     */
    @Override
    void commit(FullFrame ack) {
        switch (this._subclass) {
            case HANGUP:
            case REJECT:
            case INVAL:

                // we want to drop the call.
                if (_call != null) {
                    _call.removeSelf();
                }
                break;
            case ACCEPT:
                _call.acceptedCall();
                break;
        }
    }

    /**
     * Sends an accept or a reject frame. This method is called by
     * ack().
     *
     * @see #ack()
     */
    private void sendAcceptOrRej() {
        int match = 0;
        Boolean will = null;
        AudioInterface a = _call.getAudioFace();
        //int can = a.supportedCodecs().intValue();
        String cause = "Congestion";
        String ourprefs = a.codecPrefString();
        byte[] theirprefs = _iep.codec_prefs;
        if (theirprefs == null) {
            // fake them using capability
            Log.debug("no codec IE");
            Integer cap = _iep.capability;
            if (cap != null) {
                Log.debug("no capability IE");
                String caps = "";
                int cbits = cap.intValue();
                for (int i = 0; i < 20; i++) {
                    if ((cbits & (1 << i)) > 0) {
                        caps += (char) (56 + i);
                    }
                }
                theirprefs = caps.getBytes();
            }
        }

        if (theirprefs != null) {
            Log.debug("Looking for a matching codec");
            Log.debug("ours = " + ourprefs);
            Log.debug("theirs = " + new String(theirprefs));
            byte[] cpr = theirprefs;
            byte[] want = ourprefs.getBytes();
            for (int j = 0; j < want.length; j++) {
                for (int i = 0; i < cpr.length; i++) {
                    if (want[j] == cpr[i]) {
                        match = 1 << (cpr[i] - 66);
                        Log.debug("found codec match " + (char) want[j]
                                + " -> " + match);
                        break;
                    } else {
                        Log.verb("codec option " + (char) want[j] + " != "
                                + (char) cpr[i]);
                    }
                }
                if (match != 0) {
                    break;
                }
            }
        }

        if (match != 0) {
            _call.setAudioFormat(new Integer(match));
            will = _call.newCallFrom(_iep.callingNo, _iep.calledNo,
                    _iep.callingName,
                    _iep.username);

        } else {
            will = Boolean.FALSE;
            cause = "No compatible codec.";
            Log.debug("codec cap was " + _iep.capability);
            Log.debug("but codec pref was " + new String(_iep.codec_prefs));
        }
        if (will != null) {
            if (will.booleanValue()) {
                ProtocolControlFrame acc = mkAck(ProtocolControlFrame.ACK);
                InfoElement ie = new InfoElement();
                ie.format = new Integer(match);
                acc.sendMe(ie);
                acc = mkAck(ProtocolControlFrame.ACCEPT);
                ie = new InfoElement();
                ie.format = new Integer(match);
                acc.sendMe(ie);
                Log.debug("we acc'd call format = " + match);
            } else {
                ProtocolControlFrame rej = mkAck(ProtocolControlFrame.REJECT);
                InfoElement ie = new InfoElement();
                ie.cause = cause;
                Log.warn("we rejected call because = " + cause);
                rej.sendMe(ie); // teardown ?
            }
        } // otherwise ignore it - we have seen it before.
    }

    /**
     * Logs the fact we received in incoming frame.
     */
    private void logInbound() {
        log("got");
    }

    /**
     * Builds authentication IE. This method is called by sendAuthRep
     * and sendRegReq.
     *
     * @param iep  The original IE
     * @param nip  The new IE
     * @param pass The password
     * @see #sendAuthRep
     * @see #sendRegReq
     */
    private void buildAuthInfoElements(InfoElement iep, InfoElement nip,
                                       String pass) {
        nip.username = iep.username;
        nip.refresh = nip.refresh;

        if (_iep.authmethods != null) {

            int model = iep.authmethods.intValue();
            if ((model & 2) > 0) {
                // do md5

                try {
                    final MessageDigest digest = MessageDigest.getInstance("MD5");
                    digest.update(_iep.challenge.getBytes());
                    digest.update(pass.getBytes());
                    nip.md5Result = Binder.enHex(digest.digest(), null);
                } catch (NoSuchAlgorithmException e) {
                    e.printStackTrace();
                }
            } else if ((model & 1) > 0) {
                // do plaintext
                nip.md5Result = pass;
            }
        }
    }

    /**
     * Sends an authentication reply with authentication credentials
     * (AUTHREP).
     *
     * @see #ack()
     */
    private void sendAuthRep() {
        if (_iep != null) {
            String p = _call.getPassword();
            InfoElement nip = new InfoElement();
            buildAuthInfoElements(_iep, nip, p);
            ProtocolControlFrame ack = mkAck(ProtocolControlFrame.AUTHREP);
            Log.debug("Sending complete AUTHREP");
            ack.sendMe(nip);
            //dump();

        }
    }

    /**
     * Sends a lag reply (LAGRP).
     *
     * @see #ack()
     */
    private void sendLagReply() {
        _sCall = _call.getLno().charValue();
        _dCall = _call.getRno().charValue();
        _iseq = _call.getIseq();
        _oseq = _call.getOseq();
        _subclass = this.LAGRP;

        sendMe((InfoElement) null);
        Log.debug("Sending LagReply");

    }

    /**
     * Sends a pong (PONG) as response to a PING.
     *
     * @see #ack()
     */
    private void sendPong() {
        ProtocolControlFrame pong = mkAck(PONG);
        Log.debug("Sending Pong");
        pong.sendMe((InfoElement) null);
    }

    private void sendMyPong() {
        _sCall = _call.getLno().charValue();
        _dCall = _call.getRno().charValue();
        _iseq = _call.getIseq();
        _oseq = _call.getOseq();
        _subclass = this.ACK;
        sendMe((InfoElement) null);
        _subclass = this.PONG;
        InfoElement ie = new InfoElement();
        // ie.rr_jitter = 0;
        // ie.rr_loss = 0;
        // ie.rr_pkts = 1;
        // ie.rr_delay = 40;
        // ie.rr_dropped = 0;
        // ie.rr_ooo = 0;
        sendMe(ie);
    }
}
TOP

Related Classes of org.asteriskjava.iax.protocol.ProtocolControlFrame

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.