/**
 * yateiax.h
 * Yet Another IAX2 Stack
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2004-2014 Null Team
 *
 * This software is distributed under multiple licenses;
 * see the COPYING file in the main directory for licensing
 * information for this specific distribution.
 *
 * This use of this software may be subject to additional restrictions.
 * See the LEGAL file in the main directory for details.
 *
 * This program 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.
 */

#ifndef __YATEIAX_H
#define __YATEIAX_H

#include <yateclass.h>

#ifdef _WINDOWS

#ifdef LIBYIAX_EXPORTS
#define YIAX_API __declspec(dllexport)
#else
#ifndef LIBYIAX_STATIC
#define YIAX_API __declspec(dllimport)
#endif
#endif

#endif /* _WINDOWS */

#ifndef YIAX_API
#define YIAX_API
#endif

/**
 * Holds all Telephony Engine related classes.
 */
namespace TelEngine {

class IAXInfoElement;                    // A single IAX2 Information Element
class IAXInfoElementString;              // A single IAX2 text Information Element
class IAXInfoElementNumeric;             // A single IAX2 numeric Information Element
class IAXInfoElementBinary;              // A single IAX2 numeric Information Element
class IAXIEList;                         // Information Element container
class IAXAuthMethod;                     // Wrapper class for authentication methods values
class IAXFormatDesc;                     // IAX format description
class IAXFormat;                         // Wrapper class for formats
class IAXControl;                        // Wrapper class for subclasses of frames of type IAX
class IAXFrame;                          // This class holds an IAX frame
class IAXFullFrame;                      // This class holds an IAX full frame
class IAXFrameOut;                       // This class holds an outgoing IAX full frame
class IAXTrunkInfo;                      // Trunk info
class IAXMetaTrunkFrame;                 // Meta trunk frame
class IAXMediaData;                      // IAX2 transaction media data
class IAXTransaction;                    // An IAX2 transaction
class IAXEvent;                          // Event class
class IAXEngine;                         // IAX engine

#define IAX_PROTOCOL_VERSION         0x0002           // Protocol version
#define IAX2_MAX_CALLNO              32767            // Max call number value
#define IAX2_MAX_TRANSINFRAMELIST    127              // Max transaction incoming frame list

// Trunk frame header length
#define IAX2_TRUNKFRAME_HEADERLENGTH 8
// Trunk frame length
#define IAX2_TRUNKFRAME_LEN_MIN 20                    // 16 bytes: meta header + miniframe with timestamps header
#define IAX2_TRUNKFRAME_LEN_DEF 1400
// Trunk frame send interval in milliseconds
#define IAX2_TRUNKFRAME_SEND_MIN 5
#define IAX2_TRUNKFRAME_SEND_DEF 20

// Frame retransmission
#define IAX2_RETRANS_COUNT_MIN 1
#define IAX2_RETRANS_COUNT_MAX 10
#define IAX2_RETRANS_COUNT_DEF 4
#define IAX2_RETRANS_INTERVAL_MIN 200
#define IAX2_RETRANS_INTERVAL_MAX 5000
#define IAX2_RETRANS_INTERVAL_DEF 500

// Ping
#define IAX2_PING_INTERVAL_MIN 10000
#define IAX2_PING_INTERVAL_DEF 20000

// Sent challenge timeout
#define IAX2_CHALLENGETOUT_MIN 5000
#define IAX2_CHALLENGETOUT_DEF 30000

/**
 * This class holds a single Information Element with no data
 * @short A single IAX2 Information Element
 */
class YIAX_API IAXInfoElement : public RefObject
{
public:
    /**
     * Information Element enumeration types
     */
    enum Type {
	textframe = 0x00,	 // Text	Used internally only to generate an event of type Text
        CALLED_NUMBER = 0x01,    // Text
        CALLING_NUMBER = 0x02,   // Text
        CALLING_ANI = 0x03,      // Text
        CALLING_NAME = 0x04,     // Text
        CALLED_CONTEXT = 0x05,   // Text
        USERNAME = 0x06,         // Text
        PASSWORD = 0x07,         // Text
        CAPABILITY = 0x08,       // DW
        FORMAT = 0x09,           // DW
        LANGUAGE = 0x0a,         // Text
        VERSION = 0x0b,          // W		Value: IAX_PROTOCOL_VERSION
        ADSICPE = 0x0c,          // W
        DNID = 0x0d,             // Text
        AUTHMETHODS = 0x0e,      // W
        CHALLENGE = 0x0f,        // Text
        MD5_RESULT = 0x10,       // Text
        RSA_RESULT = 0x11,       // Text
        APPARENT_ADDR = 0x12,    // BIN
        REFRESH = 0x13,          // W
        DPSTATUS = 0x14,         // W
        CALLNO = 0x15,           // W		Max value: IAX2_MAX_CALLNO
        CAUSE = 0x16,            // Text
        IAX_UNKNOWN = 0x17,      // B
        MSGCOUNT = 0x18,         // W
        AUTOANSWER = 0x19,       // Null
        MUSICONHOLD = 0x1a,      // Text
        TRANSFERID = 0x1b,       // DW
        RDNIS = 0x1c,            // Text
        PROVISIONING = 0x1d,     // BIN
        AESPROVISIONING = 0x1e,  // BIN
        DATETIME = 0x1f,         // DW
        DEVICETYPE = 0x20,       // Text
        SERVICEIDENT = 0x21,     // BIN
        FIRMWAREVER = 0x22,      // W
        FWBLOCKDESC = 0x23,      // DW
        FWBLOCKDATA = 0x24,      // BIN
        PROVVER = 0x25,          // DW
        CALLINGPRES = 0x26,      // B
        CALLINGTON = 0x27,       // B
        CALLINGTNS = 0x28,       // W
        SAMPLINGRATE = 0x29,     // DW
        CAUSECODE = 0x2a,        // B
        ENCRYPTION = 0x2b,       // B
        ENKEY = 0x2c,            // BIN
        CODEC_PREFS = 0x2d,      // Text
        RR_JITTER = 0x2e,        // DW
        RR_LOSS = 0x2f,          // DW
        RR_PKTS = 0x30,          // DW
        RR_DELAY = 0x31,         // W
        RR_DROPPED = 0x32,       // DW
        RR_OOO = 0x33,           // DW
        CALLTOKEN = 0x36,        // BIN
        CAPABILITY2 = 0x37,      // BIN		1 byte version + array
        FORMAT2 = 0x38,          // BIN		1 byte version + array
    };

    /**
     * Constructor
     * @param type Type of this IE
     */
    inline IAXInfoElement(Type type) : m_type(type) {}

    /**
     * Destructor
     */
    virtual ~IAXInfoElement() {}

    /**
     * Get the type of this IE
     * @return Type of this IE
     */
    inline Type type() const
        { return m_type; }

    /**
     * Constructs a buffer containing this Information Element
     * @param buf Destination buffer
     */
    virtual void toBuffer(DataBlock& buf);

    /**
     * Add this element to a string
     * @param buf Destination string
     */
    virtual void toString(String& buf);

    /**
     * Get the text associated with an IE type value
     * @param ieCode Numeric code of the IE
     * @return Pointer to the IE text or 0 if it doesn't exist
     */
    static inline const char* ieText(u_int8_t ieCode)
	{ return lookup(ieCode,s_ieData); }

    /**
     * Retrieve the cause name associated with a given code
     * @param code Cause code
     * @return Cause name, 0 if not found
     */
    static inline const char* causeName(int code)
	{ return lookup(code,s_causeName); }

    /**
     * Retrieve the cause code associated with a given name
     * @param name Cause name
     * @param defVal Default value to return if not found
     * @return Cause code
     */
    static inline int causeCode(const char* name, int defVal = 0)
	{ return lookup(name,s_causeName,defVal); }

    /**
     * Cause code dictionary
     */
    static const TokenDict s_causeName[];

    /**
     * Number type dictionary
     */
    static const TokenDict s_typeOfNumber[];

    /**
     * Number presentation dictionary
     */
    static const TokenDict s_presentation[];

    /**
     * Number screening dictionary
     */
    static const TokenDict s_screening[];

private:
    static const TokenDict s_ieData[];// Association between IE type and text
    Type m_type;		// Type of this IE
};

/**
 * This class holds a single Information Element with text data
 * @short A single IAX2 text Information Element
 */
class YIAX_API IAXInfoElementString : public IAXInfoElement
{
public:
    /**
     * Constructor
     * @param type Type of this IE
     * @param buf Source buffer to construct this IE
     * @param len Buffer length
     */
    inline IAXInfoElementString(Type type, const char* buf, unsigned len) : IAXInfoElement(type), m_strData(buf,(int)len)
        {}

    /**
     * Destructor
     */
    virtual ~IAXInfoElementString() {}

    /**
     * Get the data length
     * @return The data length
     */
    inline int length() const
        { return m_strData.length(); }

    /**
     * Get the data
     * @return The data
     */
    inline String& data()
        { return m_strData; }

    /**
     * Constructs a buffer containing this Information Element
     * @param buf Destination buffer
     */
    virtual void toBuffer(DataBlock& buf);

    /**
     * Add this element to a string
     * @param buf Destination string
     */
    virtual void toString(String& buf)
	{ buf << m_strData; }

private:
    String m_strData;		// IE text data
};

/**
 * This class holds a single Information Element with 1, 2 or 4 byte(s) length data
 * @short A single IAX2 numeric Information Element
 */
class YIAX_API IAXInfoElementNumeric : public IAXInfoElement
{
public:
    /**
     * Constructor
     * @param type Type of this IE
     * @param val Source value to construct this IE
     * @param len Value length
     */
    IAXInfoElementNumeric(Type type, u_int32_t val, u_int8_t len);

    /**
     * Destructor
     */
    virtual ~IAXInfoElementNumeric() {}

    /**
     * Get the data length
     * @return The data length
     */
    inline int length() const
        { return m_length; }

    /**
     * Get the data
     * @return The data
     */
    inline u_int32_t data() const
	{ return m_numericData; }

    /**
     * Constructs a buffer containing this Information Element
     * @param buf Destination buffer
     */
    virtual void toBuffer(DataBlock& buf);

    /**
     * Add this element to a string
     * @param buf Destination string
     */
    virtual void toString(String& buf);

private:
    u_int8_t m_length;		// IE data length
    u_int32_t m_numericData;	// IE numeric data
};

/**
 * This class holds a single Information Element with binary data
 * @short A single IAX2 numeric Information Element
 */
class YIAX_API IAXInfoElementBinary : public IAXInfoElement
{
public:
    /**
     * Constructor
     * @param type Type of this IE
     * @param buf Source buffer to construct this IE
     * @param len Buffer length
     */
    IAXInfoElementBinary(Type type, unsigned char* buf, unsigned len) : IAXInfoElement(type), m_data(buf,len)
        {}

    /**
     * Destructor
     */
    virtual ~IAXInfoElementBinary() {}

    /**
     * Get the data length
     * @return The data length
     */
    inline int length() const
        { return m_data.length(); }

    /**
     * Get the data
     * @return The data
     */
    inline DataBlock& data()
        { return m_data; }

    /**
     * Set the data
     * @param buf Source buffer to construct this IE
     * @param len Buffer length
     */
    inline void setData(void* buf, unsigned len)
        { m_data.assign(buf,len); }

    /**
     * Constructs a buffer containing this Information Element
     * @param buf Destination buffer
     */
    virtual void toBuffer(DataBlock& buf);

    /**
     * Constructs an APPARENT_ADDR information element from a SocketAddr object
     * @param addr Source object
     * @return A valid IAXInfoElementBinary pointer
     */
    static IAXInfoElementBinary* packIP(const SocketAddr& addr);

    /**
     * Decode an APPARENT_ADDR information element and copy it to a SocketAddr object
     * @param addr Destination object
     * @param ie Source IE
     * @return False if ie is 0
     */
    static bool unpackIP(SocketAddr& addr, IAXInfoElementBinary* ie);

    /**
     * Add this element to a string
     * @param buf Destination string
     */
    virtual void toString(String& buf);

private:
    DataBlock m_data;		// IE binary data
};

/**
 * Management class for a list of Information Elements
 * @short Information Element container
 */
class YIAX_API IAXIEList
{
public:
    /**
     * Constructor
     */
    IAXIEList();

    /**
     * Constructor. Construct the list from an IAXFullFrame object
     * @param frame Source object
     * @param incoming True if it is an incoming frame
     */
    IAXIEList(const IAXFullFrame* frame, bool incoming = true);

    /**
     * Destructor
     */
    ~IAXIEList();

    /**
     * Get the invalid IE list flag
     * @return False if the last frame parse was unsuccessful
     */
    inline bool invalidIEList() const
	{ return m_invalidIEList; }

    /**
     * Clear the list
     */
    inline void clear()
	{ m_list.clear(); }

    /**
     * Check if the list is empty
     * @return True if the list is empty
     */
    inline bool empty()
	{ return 0 == m_list.skipNull(); }

    /**
     * Insert a VERSION Information Element in the list if not already done
     */
    void insertVersion();

    /**
     * Get the validity of the VERSION Information Element of the list if any
     * @return False if version is not IAX_PROTOCOL_VERSION or the list doesn't contain a VERSION Information Element
     */
    inline bool validVersion() {
	    u_int32_t ver = 0xFFFF;
	    getNumeric(IAXInfoElement::VERSION,ver);
	    return ver == IAX_PROTOCOL_VERSION;
	}

    /**
     * Append an Information Element to the list
     * @param ie IAXInfoElement pointer to append
     */
    inline void appendIE(IAXInfoElement* ie)
	{ m_list.append(ie); }

    /**
     * Append an Information Element taken from another list
     * @param src Source IE list
     * @param type IE to move
     * @return True if found and added
     */
    inline bool appendIE(IAXIEList& src, IAXInfoElement::Type type) {
	    IAXInfoElement* ie = src.getIE(type,true);
	    if (ie)
		appendIE(ie);
	    return ie != 0;
	}

    /**
     * Append an Information Element to the list
     * @param type The type of the IAXInfoElement to append
     */
    inline void appendNull(IAXInfoElement::Type type)
	{ m_list.append(new IAXInfoElement(type)); }

    /**
     * Append a text Information Element to the list from a String
     * @param type The type of the IAXInfoElementString to append
     * @param src The source
     */
    inline void appendString(IAXInfoElement::Type type, const String& src)
	{ m_list.append(new IAXInfoElementString(type,src.c_str(),src.length())); }

    /**
     * Append a text Information Element to the list from a buffer
     * @param type The type of the IAXInfoElementString to append
     * @param src The source
     * @param len Source length
     */
    inline void appendString(IAXInfoElement::Type type, unsigned char* src, unsigned len)
	{ m_list.append(new IAXInfoElementString(type,(char*)src,len)); }

    /**
     * Append a numeric Information Element to the list
     * @param type The type of the IAXInfoElementNumeric to append
     * @param value The source
     * @param len Source length
     */
    inline void appendNumeric(IAXInfoElement::Type type, u_int32_t value, u_int8_t len)
	{ m_list.append(new IAXInfoElementNumeric(type,value,len)); }

    /**
     * Append a binary Information Element to the list
     * @param type The type of the IAXInfoElementBinary to append
     * @param data The source data to append
     * @param len Source length
     */
    inline void appendBinary(IAXInfoElement::Type type, unsigned char* data, unsigned len)
	{ m_list.append(new IAXInfoElementBinary(type,data,len)); }

    /**
     * Construct the list from an IAXFullFrame object.
     *  On exit m_invalidIEList will contain the opposite of the returned value
     * @param frame Source object
     * @param incoming True if it is an incoming frame
     * @return False if the frame contains invalid IEs
     */
    bool createFromFrame(const IAXFullFrame* frame, bool incoming = true);

    /**
     * Construct a buffer from this list
     * @param buf Destination buffer
     */
    void toBuffer(DataBlock& buf);

    /**
     * Add this list to a string
     * @param dest Destination string
     * @param indent Optional indent for each element
     */
    void toString(String& dest, const char* indent = 0);

    /**
     * Retrieve an IAXInfoElement from the list
     * @param type The desired type
     * @param remove True to remove from list. The caller will own the object
     * @return An IAXInfoElement pointer or 0 if the list doesn't contain an IE of this type
     */
    IAXInfoElement* getIE(IAXInfoElement::Type type, bool remove = false);

    /**
     * Get the data of a list item into a String. Before any operation dest is cleared
     * @param type The desired type
     * @param dest The destination String
     * @return False if the list doesn't contain an IE of this type
     */
    bool getString(IAXInfoElement::Type type, String& dest);

    /**
     * Get the data of a list item into a numeric destination
     * @param type The desired type
     * @param dest The destination
     * @return False if the list doesn't contain an IE of this type
     */
    bool getNumeric(IAXInfoElement::Type type, u_int32_t& dest);

    /**
     * Get the data of a list item into a DataBlock. Before any operation dest is cleared
     * @param type The desired type
     * @param dest The destination buffer
     * @return False if the list doesn't contain an IE of this type
     */
    bool getBinary(IAXInfoElement::Type type, DataBlock& dest);

private:
    bool m_invalidIEList;	// Invalid IE flag
    ObjList m_list;		// The IE list
};

/**
 * This class holds the enumeration values for authentication methods
 * @short Wrapper class for authentication methods values
 */
class YIAX_API IAXAuthMethod
{
public:
    /**
     * Authentication method enumeration types
     */
    enum Type {
        Text = 1,
        MD5  = 2,
        RSA  = 4,
    };

    /**
     * Create a string list from authentication methods
     * @param dest The destination
     * @param auth The authentication methods as ORed bits
     * @param sep The separator to use
    */
    static void authList(String& dest, u_int16_t auth, char sep);

    static TokenDict s_texts[];
};


/**
 * This class holds IAX format description
 * @short IAX format description
 */
class YIAX_API IAXFormatDesc
{
public:
    /**
     * Constructor
     */
    inline IAXFormatDesc()
	: m_format(0), m_multiplier(1)
	{}

    /**
     * Get the format
     * @return The format
     */
    inline u_int32_t format() const
	{ return m_format; }

    /**
     * Get the format multiplier used to translate timestamps
     * @return The format multiplier (always greater then 0)
     */
    inline unsigned int multiplier() const
	{ return m_multiplier; }

    /**
     * Set the format
     * @param fmt The format
     * @param type Format type as IAXFormat::Media enumeration
     */
    void setFormat(u_int32_t fmt, int type);

protected:
    u_int32_t m_format;                  // The format
    unsigned int m_multiplier;           // Format multiplier derived from sampling rate
};

/**
 * This class holds the enumeration values for audio and video formats
 * @short Wrapper class for audio and video formats
 */
class YIAX_API IAXFormat
{
public:
    /**
     * Format enumeration types
     */
    enum Formats {
        G723_1 = (1 <<  0),
        GSM    = (1 <<  1),
        ULAW   = (1 <<  2),
        ALAW   = (1 <<  3),
        G726   = (1 <<  4),
        ADPCM  = (1 <<  5),
        SLIN   = (1 <<  6),
        LPC10  = (1 <<  7),
        G729   = (1 <<  8),
        SPEEX  = (1 <<  9),
        ILBC   = (1 << 10),
        G726AAL2 = (1 << 11),
        G722   = (1 << 12),
        AMR    = (1 << 13),
        // NOTE: GSM Half Rate is not defined in RFC5456
        GSM_HR    = (1 << 31),
        AudioMask = G723_1 | GSM | ULAW | ALAW | G726 | ADPCM | SLIN | LPC10 | G729 | SPEEX |
            ILBC | G726AAL2 | G722 | AMR | GSM_HR,
        JPEG   = (1 << 16),
        PNG    = (1 << 17),
        ImageMask = JPEG | PNG,
        H261   = (1 << 18),
        H263   = (1 << 19),
        H263p  = (1 << 20),
        H264   = (1 << 21),
        VideoMask = H261 | H263 | H263p | H264,
    };

    /**
     * Media type enumeration
     */
    enum Media {
        Audio = 0,
        Video,
        Image,
        TypeCount
    };

    /**
     * Constructor. Build an audio format
     * @param type Media type
    */
    inline IAXFormat(int type = Audio)
	: m_type(type)
	{}

    /**
     * Get the media type
     * @return Media type
    */
    inline int type() const
	{ return m_type; }

    /**
     * Get the format
     * @return The format
    */
    inline u_int32_t format() const
	{ return m_format.format(); }

    /**
     * Get the incoming format
     * @return The incoming format
    */
    inline u_int32_t in() const
	{ return m_formatIn.format(); }

    /**
     * Get the outgoing format
     * @return The outgoing format
    */
    inline u_int32_t out() const
	{ return m_formatOut.format(); }

    /**
     * Get the incoming or outgoing format description
     * @param in True to retrieve the incoming format, false to retrieve the outgoing one
     * @return Requested format desc
    */
    inline const IAXFormatDesc& formatDesc(bool in) const
	{ return in ? m_formatIn : m_formatOut; }

     /**
     * Get the text associated with the format
     * @return Format name
    */
    inline const char* formatName() const
	{ return formatName(format()); }

    /**
     * Get the text associated with the media type
     * @return Media name
    */
    inline const char* typeName() const
	{ return typeName(m_type); }

    /**
     * Set format
     * @param fmt Optional pointer to format to set
     * @param fmtIn Optional pointer to incoming format to set
     * @param fmtOut Optional pointer to outgoing format to set
    */
    void set(u_int32_t* fmt, u_int32_t* fmtIn, u_int32_t* fmtOut);

    /**
     * Create a string list from formats
     * @param dest The destination
     * @param formats The formats
     * @param dict Optional dictionary to use, 0 to use s_formats
     * @param sep The separator to use
    */
    static void formatList(String& dest, u_int32_t formats, const TokenDict* dict = 0,
	const char* sep = ",");

    /**
     * Pick a format from a list of capabilities
     * @param formats Capabilities list
     * @param format Optional format to pick
     * @return IAX format, 0 if not found
    */
    static u_int32_t pickFormat(u_int32_t formats, u_int32_t format = 0);

    /**
     * Encode a formats list
     * @param formats Formats list
     * @param dict Dictionary to use
     * @param sep Formats list separator
     * @return Encoded formats
    */
    static u_int32_t encode(const String& formats, const TokenDict* dict, char sep = ',');

    /**
     * Mask formats by type
     * @param value Input format(s)
     * @param type Media type to retrieve
     * @return Media format(s) from input
    */
    static inline u_int32_t mask(u_int32_t value, int type) {
	    if (type == Audio)
		return value & AudioMask;
	    if (type == Video)
		return value & VideoMask;
	    if (type == Image)
		return value & ImageMask;
	    return 0;
	}

    /**
     * Clear formats by type
     * @param value Input format(s)
     * @param type Media type to clear
     * @return Cleared format(s) from input
    */
    static inline u_int32_t clear(u_int32_t value, int type) {
	    if (type == Audio)
		return value & ~AudioMask;
	    if (type == Video)
		return value & ~VideoMask;
	    if (type == Image)
		return value & ~ImageMask;
	    return value;
	}

    /**
     * Get the text associated with a format
     * @param fmt The desired format
     * @return A pointer to the text associated with the format or 0 if the format doesn't exist
    */
    static inline const char* formatName(u_int32_t fmt)
	{ return lookup(fmt,s_formats); }

    /**
     * Get the text associated with a media type
     * @param type The media type
     * @return A pointer to the text associated with the media type
    */
    static inline const char* typeName(int type)
	{ return lookup(type,s_types); }

    /**
     * Get the text associated with a media type
     * @param type The media type
     * @return A string associated with the media type
    */
    static inline const String& typeNameStr(int type)
	{ return s_typesList[type]; }

    /**
     * Keep the texts associated with the formats
    */
    static const TokenDict s_formats[];

    /**
     * Keep the texts associated with type
     */
    static const TokenDict s_types[];

    /**
     * Keep the texts associated with a type also as String
     */
    static const String s_typesList[TypeCount];

protected:
    int m_type;
    IAXFormatDesc m_format;
    IAXFormatDesc m_formatIn;
    IAXFormatDesc m_formatOut;
};

/**
 * This class holds the enumeration values for IAX control (subclass)
 * @short Wrapper class for subclasses of frames of type IAX
 */
class YIAX_API IAXControl
{
public:
    /**
     * IAX control (subclass) enumeration types
     */
    enum Type {
        New       = 0x01,
        Ping      = 0x02,
        Pong      = 0x03,
        Ack       = 0x04,
        Hangup    = 0x05,
        Reject    = 0x06,
        Accept    = 0x07,
        AuthReq   = 0x08,
        AuthRep   = 0x09,
        Inval     = 0x0a,
        LagRq     = 0x0b,
        LagRp     = 0x0c,
        RegReq    = 0x0d,
        RegAuth   = 0x0e,
        RegAck    = 0x0f,
        RegRej    = 0x10,
        RegRel    = 0x11,
        VNAK      = 0x12,
        DpReq     = 0x13,
        DpRep     = 0x14,
        Dial      = 0x15,
        TxReq     = 0x16,
        TxCnt     = 0x17,
        TxAcc     = 0x18,
        TxReady   = 0x19,
        TxRel     = 0x1a,
        TxRej     = 0x1b,
        Quelch    = 0x1c,
        Unquelch  = 0x1d,
        Poke      = 0x1e,
	//Reserved  = 0x1f,
        MWI       = 0x20,
        Unsupport = 0x21,
        Transfer  = 0x22,
        Provision = 0x23,
        FwDownl   = 0x24,
        FwData    = 0x25,
        CallToken = 0x28,
    };

    /**
     * Get the string associated with the given IAX control type
     * @param type The requested type
     * @return The text if type is valid or 0
     */
    static inline const char* typeText(int type)
	{ return lookup(type,s_types,0); }

private:
    static TokenDict s_types[]; // Keep the association between IAX control codes and their name
};

/**
 * This class holds all data needded to manage an IAX frame
 * @short This class holds an IAX frame
 */
class YIAX_API IAXFrame : public RefObject
{
public:
    /**
     * IAX frame type enumeration
     */
    enum Type {
        DTMF    = 0x01,
        Voice   = 0x02,
        Video   = 0x03,
        Control = 0x04,
        Null    = 0x05,
        IAX     = 0x06,
        Text    = 0x07,
        Image   = 0x08,
        HTML    = 0x09,
        Noise   = 0x0a,
    };

    /**
     * Constructor. Constructs an incoming frame
     * @param type Frame type
     * @param sCallNo Source call number
     * @param tStamp Frame timestamp
     * @param retrans Retransmission flag
     * @param buf IE buffer
     * @param len IE buffer length
     * @param mark Mark flag
     */
    IAXFrame(Type type, u_int16_t sCallNo, u_int32_t tStamp, bool retrans,
	     const unsigned char* buf, unsigned int len, bool mark = false);

    /**
     * Destructor
     */
    virtual ~IAXFrame();

    /**
     * Get the type of this frame as enumeration
     * @return The type of this frame as enumeration
     */
    inline Type type() const
	{ return m_type; }

    /**
     * Get the data buffer of the frame
     * @return The data buffer of the frame
     */
    inline DataBlock& data()
        { return m_data; }

    /**
     * Get the retransmission flag of this frame
     * @return The retransmission flag of this frame
     */
    inline bool retrans() const
	{ return m_retrans; }

    /**
     * Get the source call number of this frame
     * @return The source call number of this frame
     */
    inline u_int16_t sourceCallNo() const
	{ return m_sCallNo; }

    /**
     * Get the timestamp of this frame
     * @return The timestamp of this frame
     */
    inline u_int32_t timeStamp() const
	{ return m_tStamp; }

    /**
     * Get the mark flag
     * @return The mark flag
     */
    inline bool mark() const
	{ return m_mark; }

    /**
     * Get a pointer to this frame if it is a full frame
     * @return A pointer to this frame if it is a full frame or 0
     */
    virtual IAXFullFrame* fullFrame();

    /**
     * Parse a received buffer and returns a IAXFrame pointer if valid
     * @param buf Received buffer
     * @param len Buffer length
     * @param engine The IAXEngine who requested the operation
     * @param addr The source address
     * @return A frame pointer on success or 0
     */
    static IAXFrame* parse(const unsigned char* buf, unsigned int len, IAXEngine* engine = 0, const SocketAddr* addr = 0);

    /**
     * Build a miniframe buffer
     * @param dest Destination buffer
     * @param sCallNo Source call number
     * @param ts Frame timestamp
     * @param data Data
     * @param len Data length
     */
    static inline void buildMiniFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t ts,
	void* data, unsigned int len) {
	    unsigned char header[4] = {sCallNo >> 8,sCallNo & 0xff,ts >> 8,ts & 0xff};
	    dest.assign(header,4);
	    dest.append(data,len);
	}

    /**
     * Build a video meta frame buffer
     * @param dest Destination buffer
     * @param sCallNo Source call number
     * @param tStamp Frame timestamp
     * @param mark Frame mark
     * @param data Data
     * @param len Data length
     */
    static void buildVideoMetaFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t tStamp,
	bool mark, void* data, unsigned int len);

    /**
     * Pack a subclass value according to IAX protocol
     * @param value Value to pack
     * @return The packed subclass value or 0 if invalid (>255 and not a power of 2)
     */
    static u_int8_t packSubclass(u_int32_t value);

    /**
     * Unpack a subclass value according to IAX protocol
     * @param value Value to unpack
     * @return The unpacked subclass value
     */
    static u_int32_t unpackSubclass(u_int8_t value);

    /**
     * Get the string associated with the given IAX frame type
     * @param type The requested type
     * @return The text if type is valid or 0
     */
    static inline const char* typeText(int type)
	{ return lookup(type,s_types,0); }

protected:
    /**
     * Contains the frame's IE list for an incoming frame or the whole frame for an outgoing one
     */
    DataBlock m_data;

    /**
     * Retransmission flag
     */
    bool m_retrans;

private:
    static TokenDict s_types[]; // Keep the association between IAX frame types and their names
    Type m_type;		// Frame type
    u_int16_t m_sCallNo;	// Source call number
    u_int32_t m_tStamp;		// Frame timestamp
    bool m_mark;		// Mark flag
};

/**
 * This class holds all data needded to manage an IAX full frame
 * @short This class holds an IAX full frame
 */
class YIAX_API IAXFullFrame : public IAXFrame
{
public:
    /**
     * IAX frame subclass enumeration types for frames of type Control
     */
    enum ControlType {
        Hangup = 0x01,
        //Ring = 0x02,
        Ringing = 0x03,
        Answer = 0x04,
        Busy = 0x05,
        Congestion = 0x08,
        FlashHook = 0x09,
        Option = 0x0b,
        KeyRadio = 0x0c,
        UnkeyRadio = 0x0d,
        Progressing = 0x0e,
        Proceeding = 0x0f,
        Hold = 0x10,
        Unhold = 0x11,
        VidUpdate = 0x12,
        SrcUpdate = 0x14,
        StopSounds = 0xff,
    };

    /**
     * Constructor. Constructs an incoming full frame
     * @param type Frame type
     * @param subclass Frame subclass
     * @param sCallNo Source (remote) call number
     * @param dCallNo Destination (local) call number
     * @param oSeqNo Outgoing sequence number
     * @param iSeqNo Incoming (expected) sequence number
     * @param tStamp Frame timestamp
     * @param retrans Retransmission flag
     * @param buf IE buffer
     * @param len IE buffer length
     * @param mark Mark flag
     */
    IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
		 unsigned char oSeqNo, unsigned char iSeqNo,
		 u_int32_t tStamp, bool retrans,
		 const unsigned char* buf, unsigned int len, bool mark = false);

    /**
     * Constructor. Constructs an outgoing full frame
     * @param type Frame type
     * @param subclass Frame subclass
     * @param sCallNo Source (remote) call number
     * @param dCallNo Destination (local) call number
     * @param oSeqNo Outgoing sequence number
     * @param iSeqNo Incoming (expected) sequence number
     * @param tStamp Frame timestamp
     * @param buf IE buffer
     * @param len IE buffer length
     * @param mark Mark flag
     */
    IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
		 unsigned char oSeqNo, unsigned char iSeqNo,
		 u_int32_t tStamp,
		 const unsigned char* buf = 0, unsigned int len = 0, bool mark = false);

    /**
     * Constructor. Constructs an outgoing full frame
     * @param type Frame type
     * @param subclass Frame subclass
     * @param sCallNo Source (remote) call number
     * @param dCallNo Destination (local) call number
     * @param oSeqNo Outgoing sequence number
     * @param iSeqNo Incoming (expected) sequence number
     * @param tStamp Frame timestamp
     * @param ieList List of frame IEs
     * @param maxlen Max frame data length
     * @param mark Mark flag
     */
    IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
		 unsigned char oSeqNo, unsigned char iSeqNo,
		 u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen, bool mark = false);

    /**
     * Destructor
     */
    virtual ~IAXFullFrame();

    /**
     * Get the destination call number
     * @return The destination call number
     */
    inline u_int16_t destCallNo() const
        { return m_dCallNo; }

    /**
     * Get the outgoing sequence number
     * @return The outgoing sequence number
     */
    inline unsigned char oSeqNo() const
        { return m_oSeqNo; }

    /**
     * Get the incoming sequence number
     * @return The incoming sequence number
     */
    inline unsigned char iSeqNo() const
        { return m_iSeqNo; }

    /**
     * Get the subclass of this frame
     * @return The subclass of this frame
     */
    inline u_int32_t subclass() const
	{ return m_subclass; }

    /**
     * Check if this frame is used to request authentication
     * @return True if this frame is used to request authentication (like RegReq or RegAuth)
     */
    inline bool isAuthReq() const {
	    return type() == IAXFrame::IAX &&
		(subclass() == IAXControl::AuthReq || subclass() == IAXControl::RegAuth);
	}

    /**
     * Check if this frame is an INVAL one
     * @return True if this frame is INVAL
     */
    inline bool isInval() const
	{ return type() == IAXFrame::IAX && subclass() == IAXControl::Inval; }

    /**
     * Get a pointer to this frame if it is a full frame
     * @return A pointer to this frame
     */
    virtual IAXFullFrame* fullFrame();

    /**
     * Rebuild frame buffer from the list of IEs
     * @param maxlen Max frame data length
     */
    void updateBuffer(u_int16_t maxlen);

    /**
     * Retrieve the IE list
     * @return IAXIEList pointer or NULL
     */
    inline IAXIEList* ieList()
	{ return m_ieList; }

    /**
     * Update IE list from buffer if not already done
     * @param incoming True if this is an incoming frame
     * @return True if the list is valid
     */
    bool updateIEList(bool incoming);

    /**
     * Remove the IE list
     * @param delObj True to delete it
     * @return IAXIEList pointer or NULL if requested to delete it or already NULL
     */
    IAXIEList* removeIEList(bool delObj = true);

    /**
     * Fill a string with this frame
     * @param dest The string to fill
     * @param local The local address
     * @param remote The remote address
     * @param incoming True if it is an incoming frame
     */
    void toString(String& dest, const SocketAddr& local, const SocketAddr& remote,
	bool incoming);

    /**
     * Get the string associated with the given IAX control type
     * @param type The requested control type
     * @return The text if type is valid or 0
     */
    static inline const char* controlTypeText(int type)
	{ return lookup(type,s_controlTypes,0); }

protected:
    /**
     * Destroyed notification. Clear data
     */
    virtual void destroyed();

private:
    // Build frame buffer header
    void setDataHeader();
    static TokenDict s_controlTypes[]; // Keep the association between control types and their names
    u_int16_t m_dCallNo;	// Destination call number
    unsigned char m_oSeqNo;	// Out sequence number
    unsigned char m_iSeqNo;	// In sequence number
    u_int32_t m_subclass;	// Subclass
    IAXIEList* m_ieList;        // List of IEs
};

/**
 * This class holds all data needded to manage an outgoing IAX full frame
 * @short This class holds an outgoing IAX full frame
 */
class YIAX_API IAXFrameOut : public IAXFullFrame
{
public:
    /**
     * Constructor. Constructs an outgoing full frame
     * @param type Frame type
     * @param subclass Frame subclass
     * @param sCallNo Source (remote) call number
     * @param dCallNo Destination (local) call number
     * @param oSeqNo Outgoing sequence number
     * @param iSeqNo Incoming (expected) sequence number
     * @param tStamp Frame timestamp
     * @param buf IE buffer
     * @param len IE buffer length
     * @param retransCount Retransmission counter
     * @param retransIntervalMs Time interval to the next retransmission
     * @param ackOnly Acknoledge only flag. If true, the frame only expects an ACK
     * @param mark Mark flag
     */
    inline IAXFrameOut(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
                       unsigned char oSeqNo, unsigned char iSeqNo, u_int32_t tStamp,
		       const unsigned char* buf, unsigned int len,
                       u_int16_t retransCount, u_int32_t retransIntervalMs,
		       bool ackOnly, bool mark = false)
        : IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,buf,len,mark),
          m_ack(false), m_ackOnly(ackOnly), m_retransCount(retransCount),
          m_retransTimeInterval(retransIntervalMs * 1000),
	  m_nextTransTime(Time::now() + m_retransTimeInterval)
	{}

    /**
     * Constructor. Constructs an outgoing full frame
     * @param type Frame type
     * @param subclass Frame subclass
     * @param sCallNo Source (remote) call number
     * @param dCallNo Destination (local) call number
     * @param oSeqNo Outgoing sequence number
     * @param iSeqNo Incoming (expected) sequence number
     * @param tStamp Frame timestamp
     * @param ieList List of frame IEs
     * @param maxlen Max frame data length
     * @param retransCount Retransmission counter
     * @param retransIntervalMs Time interval to the next retransmission
     * @param ackOnly Acknoledge only flag. If true, the frame only expects an ACK
     * @param mark Mark flag
     */
    inline IAXFrameOut(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
                       unsigned char oSeqNo, unsigned char iSeqNo, u_int32_t tStamp,
		       IAXIEList* ieList, u_int16_t maxlen,
                       u_int16_t retransCount, u_int32_t retransIntervalMs, bool ackOnly,
		       bool mark = false)
        : IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,ieList,maxlen,mark),
          m_ack(false), m_ackOnly(ackOnly), m_retransCount(retransCount),
          m_retransTimeInterval(retransIntervalMs * 1000),
	  m_nextTransTime(Time::now() + m_retransTimeInterval)
	{}

    /**
     * Destructor
     */
    virtual ~IAXFrameOut()
	{}

    /**
     * Get the retransmission counter of this frame
     * @return The retransmission counter is 0
     */
    inline unsigned int retransCount() const
        { return m_retransCount; }

    /**
     * Ask the frame if it's time for retransmit
     * @param time Current time
     * @return True if it's time to retransmit
     */
    inline bool timeForRetrans(u_int64_t time) const
        { return time >= m_nextTransTime; }

    /**
     * Set the retransmission flag of this frame
     */
    inline void setRetrans() {
	    if (m_retrans)
		return;
	    m_retrans = true;
	    ((unsigned char*)m_data.data())[2] |= 0x80;
	}

    /**
     * Update the retransmission counter and the time to next retransmission
     */
    inline void transmitted() {
	    if (!m_retransCount)
		return;
	    m_retransCount--;
	    m_retransTimeInterval *= 2;
	    m_nextTransTime += m_retransTimeInterval;
	}

    /**
     * Get the acknoledged flag of this frame
     * @return The acknoledged flag of this frame
     */
    inline bool ack() const
	{ return m_ack; }

    /**
     * Set the acknoledged flag of this frame
     */
    inline void setAck()
	{ m_ack = true; }

    /**
     * Get the acknoledge only flag of this frame
     * @return The acknoledge only flag of this frame
     */
    inline bool ackOnly() const
	{ return m_ackOnly; }

    /**
     * Check if absolute timeout can be set
     * @return True if absolute timeout can be set
     */
    inline bool canSetTimeout()
	{ return m_retransTimeInterval != 0; }

    /**
     * Set absolute timeout. Reset retransmission counter
     * @param tout Timeout time
     */
    inline void setTimeout(u_int64_t tout) {
	    if (!m_retransTimeInterval)
		return;
	    m_retransTimeInterval = 0;
	    m_retransCount = 0;
	    m_nextTransTime = tout;
	}

private:
    bool m_ack;				// Acknoledge flag
    bool m_ackOnly;			// Frame need only ACK as a response
    u_int16_t m_retransCount;		// Retransmission counter
    u_int32_t m_retransTimeInterval;	// Retransmission interval
    u_int64_t m_nextTransTime;		// Next transmission time
};

/**
 * This class holds trunk description
 * @short Trunk info
 */
class YIAX_API IAXTrunkInfo : public RefObject
{
public:
    /**
     * Constructor
     */
    inline IAXTrunkInfo()
	: m_timestamps(true), m_sendInterval(IAX2_TRUNKFRAME_SEND_DEF),
	m_maxLen(IAX2_TRUNKFRAME_LEN_DEF),
	m_efficientUse(false), m_trunkInSyncUsingTs(true),
	m_trunkInTsDiffRestart(5000),
	m_retransCount(IAX2_RETRANS_COUNT_DEF),
	m_retransInterval(IAX2_RETRANS_INTERVAL_DEF),
	m_pingInterval(IAX2_PING_INTERVAL_DEF)
	{}

    /**
     * Init non trunking related data
     * @param params Parameter list
     * @param prefix Parameter prefix
     * @param def Optional defaults
     */
    void init(const NamedList& params, const String& prefix = String::empty(),
	const IAXTrunkInfo* def = 0);

    /**
     * Init trunking from parameters
     * @param params Parameter list
     * @param prefix Parameter prefix
     * @param def Optional defaults
     * @param out True to init outgoing trunk data
     * @param in True to init incoming trunk data
     */
    void initTrunking(const NamedList& params, const String& prefix = String::empty(),
	const IAXTrunkInfo* def = 0, bool out = true, bool in = true);

    /**
     * Update trunking from parameters. Don't change values not present in list
     * @param params Parameter list
     * @param prefix Parameter prefix
     * @param out True to update outgoing trunk data
     * @param in True to update incoming trunk data
     */
    void updateTrunking(const NamedList& params, const String& prefix = String::empty(),
	bool out = true, bool in = true);

    /**
     * Dump info
     * @param buf Destination buffer
     * @param sep Parameters separator
     * @param out True to dump outgoing trunking info
     * @param in True to dump incoming trunking info
     * @param other True to dump non trunking info
     */
    void dump(String& buf, const char* sep = " ", bool out = true, bool in = true,
	bool other = true);

    bool m_timestamps;                   // Trunk type: with(out) timestamps
    unsigned int m_sendInterval;         // Send interval
    unsigned int m_maxLen;               // Max frame length
    bool m_efficientUse;                 // Outgoing trunking: use or not the trunk based on calls using it
    bool m_trunkInSyncUsingTs;           // Incoming trunk without timestamps: use trunk
                                         //  time or trunk timestamp to re-build frame ts
    u_int32_t m_trunkInTsDiffRestart;    // Incoming trunk without timestamp: diff between
                                         //  timestamps at which we restart
    unsigned int m_retransCount;         // Frame retransmission counter
    unsigned int m_retransInterval;      // Frame retransmission interval in milliseconds
    unsigned int m_pingInterval;         // Ping interval in milliseconds
};

/**
 * Handle meta trunk frame with timestamps
 * @short Meta trunk frame
 */
class YIAX_API IAXMetaTrunkFrame : public RefObject, public Mutex
{
public:
    /**
     * Constructor. Constructs an outgoing meta trunk frame
     * @param engine The engine that owns this frame
     * @param addr Remote peer address
     * @param timestamps True if miniframes have timestamps, false if not
     * @param maxLen Maximum frame length
     * @param sendInterval Trunk send interval in milliseconds
     */
    IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr, bool timestamps,
	unsigned int maxLen, unsigned int sendInterval);

    /**
     * Destructor
     */
    virtual ~IAXMetaTrunkFrame();

    /**
     * Get the remote peer address
     * @return The remote peer address
     */
    inline const SocketAddr& addr() const
	{ return m_addr; }

    /**
     * Retrieve the number of calls using this trunk
     * @return The number of calls using this trunk
     */
    inline unsigned int calls() const
	{ return m_calls; }

    /**
     * Change the number of calls using this trunk
     * @param add True to add a call, false to remove it
     */
    inline void changeCalls(bool add) {
	    Lock lck(this);
	    if (add)
		m_calls++;
	    else if (m_calls)
		m_calls--;
	}

    /**
     * Check if the frame is adding mini frames timestamps
     * @return True if the frame is adding mini frames timestamps
     */
    inline bool trunkTimestamps() const
	{ return m_trunkTimestamps; }

    /**
     * Retrieve the send interval
     * @return Send interval in milliseconds
     */
    inline unsigned int sendInterval() const
	{ return m_sendInterval; }

    /**
     * Retrieve the frame maximum length
     * @return Frame maximum length
     */
    inline unsigned int maxLen() const
	{ return m_maxLen; }

    /**
     * Add a mini frame. If no room, send before adding
     * @param sCallNo Sorce call number
     * @param data Mini frame data
     * @param tStamp Mini frame timestamp
     * @return The number of data bytes added to trunk, 0 on failure
     */
    unsigned int add(u_int16_t sCallNo, const DataBlock& data, u_int32_t tStamp);

    /**
     * Send this frame to remote peer if the time arrived
     * @param now Current time
     * @return The result of the write operation
     */
    inline bool timerTick(const Time& now = Time()) {
	    if (m_dataAddIdx == IAX2_TRUNKFRAME_HEADERLENGTH || !m_send)
		return false;
	    Lock lck(this);
	    return (now > m_send) && doSend(now,true);
	}

    /**
     * Send this frame to remote peer if there is any data in buffer
     * @return The result of the write operation
     */
    inline bool send() {
	    if (m_dataAddIdx == IAX2_TRUNKFRAME_HEADERLENGTH)
		return false;
	    Lock lck(this);
	    return m_dataAddIdx != IAX2_TRUNKFRAME_HEADERLENGTH && doSend();
	}

private:
    IAXMetaTrunkFrame() {}      // No default constructor
    // Send this frame to remote peer
    bool doSend(const Time& now = Time(), bool onTime = false);
    // Set timestamp and next time to send
    inline void setTimestamp(u_int64_t now) {
	    m_timeStamp = now;
	    m_send = now + (u_int64_t)m_sendInterval * 1000;
	}
    // Set next time to send
    inline void setSendTime(u_int64_t now)
	{ m_send = now + (u_int64_t)m_sendInterval * 1000; }

    // Set the timestamp of this frame
    inline void setTimestamp(u_int32_t tStamp) {
            m_data[4] = (u_int8_t)(tStamp >> 24);
	    m_data[5] = (u_int8_t)(tStamp >> 16);
	    m_data[6] = (u_int8_t)(tStamp >> 8);
	    m_data[7] = (u_int8_t)tStamp;
	}

    unsigned int m_calls;       // The number of calls using it
    u_int8_t* m_data;		// Data buffer
    u_int16_t m_dataAddIdx;	// Current add index
    u_int64_t m_timeStamp;      // First time data was added
    u_int64_t m_send;           // Time to send
    u_int32_t m_lastSentTs;     // Last sent timestamp
    unsigned int m_sendInterval;// Send interval in milliseconds
    IAXEngine* m_engine;	// The engine that owns this frame
    SocketAddr m_addr;		// Remote peer address
    bool m_trunkTimestamps;     // Trunk type: with(out) timestamps
    unsigned int m_maxLen;      // Max frame length
    unsigned int m_maxDataLen;  // Max frame data length
    unsigned char m_miniHdrLen; // Miniframe header length
};

/**
 * This class holds data used by transaction to sync media.
 * The mutexes are not reentrant
 * @short IAX2 transaction media data
 */
class YIAX_API IAXMediaData
{
    friend class IAXTransaction;
public:
    /**
     * Constructor
     */
    inline IAXMediaData()
	: m_inMutex(false,"IAXTransaction::InMedia"),
	m_outMutex(false,"IAXTransaction::OutMedia"),
	m_startedIn(false), m_startedOut(false),
	m_outStartTransTs(0), m_outFirstSrcTs(0),
	m_lastOut(0), m_lastIn(0), m_sent(0), m_sentBytes(0),
	m_recv(0), m_recvBytes(0), m_ooPackets(0), m_ooBytes(0),
	m_showInNoFmt(true), m_showOutOldTs(true),
	m_dropOut(0), m_dropOutBytes(0)
	{}

    /**
     * Increase drop out data
     * @param len The number of dropped bytes
     */
    inline void dropOut(unsigned int len) {
	    if (len) {
		m_dropOut++;
		m_dropOutBytes += len;
	    }
	}

    /**
     * Print statistics
     * @param buf Destination buffer
     */
    void print(String& buf);

protected:
    Mutex m_inMutex;
    Mutex m_outMutex;
    bool m_startedIn;                    // Incoming media started
    bool m_startedOut;                   // Outgoing media started
    int m_outStartTransTs;               // Transaction timestamp where media send started
    unsigned int m_outFirstSrcTs;        // First outgoing source packet timestamp as received from source
    u_int32_t m_lastOut;                 // Last transmitted mini timestamp
    u_int32_t m_lastIn;                  // Last received timestamp
    unsigned int m_sent;                 // Packets sent
    unsigned int m_sentBytes;            // Bytes sent
    unsigned int m_recv;                 // Packets received
    unsigned int m_recvBytes;            // Bytes received
    unsigned int m_ooPackets;            // Dropped received out of order packets
    unsigned int m_ooBytes;              // Dropped received out of order bytes
    bool m_showInNoFmt;                  // Show incoming media arrival without format debug
    bool m_showOutOldTs;                 // Show dropped media out debug message
    unsigned int m_dropOut;              // The number of dropped outgoing packets
    unsigned int m_dropOutBytes;         // The number of dropped outgoing bytes
};

/**
 * This class holds all the data needded for the management of an IAX2 transaction
 *  which might be a call leg, a register/unregister or a poke one
 * @short An IAX2 transaction
 */
class YIAX_API IAXTransaction : public RefObject, public Mutex
{
    friend class IAXEvent;
    friend class IAXEngine;
public:
    /**
     * The transaction type as enumeration
     */
    enum Type {
	Incorrect,			// Unsupported/unknown type
	New,				// Media exchange call
	RegReq,				// Registration
	RegRel,				// Registration release
	Poke,				// Ping
	//FwDownl,
    };

    /**
     * The transaction state as enumeration
     */
    enum State {
        Connected,		     	// Call leg established (Accepted) for transactions of type New
	NewLocalInvite,		     	// New outgoing transaction: Poke/New/RegReq/RegRel
	NewLocalInvite_AuthRecv,     	// Auth request received for an outgoing transaction
	NewLocalInvite_RepSent,	     	// Auth reply sent for an outgoing transaction
	NewRemoteInvite,             	// New incoming transaction: Poke/New/RegReq/RegRel
	NewRemoteInvite_AuthSent,    	// Auth sent for an incoming transaction
	NewRemoteInvite_RepRecv,     	// Auth reply received for an incoming transaction
	Unknown,                     	// Initial state
	Terminated,                  	// Terminated. No more frames accepted
        Terminating,                 	// Terminating. Wait for ACK or timeout to terminate
    };

    /**
     * Constructs an incoming transaction from a received full frame with an IAX
     *  control message that needs a new transaction
     * @param engine The engine that owns this transaction
     * @param frame A valid full frame
     * @param lcallno Local call number
     * @param addr Address from where the frame was received
     * @param data Pointer to arbitrary user data
     */
    static IAXTransaction* factoryIn(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno, const SocketAddr& addr,
		void* data = 0);

    /**
     * Constructs an outgoing transaction with an IAX control message that needs a new transaction
     * @param engine The engine that owns this transaction
     * @param type Transaction type
     * @param lcallno Local call number
     * @param addr Address to use
     * @param ieList Starting IE list
     * @param data Pointer to arbitrary user data
     */
    static IAXTransaction* factoryOut(IAXEngine* engine, Type type, u_int16_t lcallno, const SocketAddr& addr,
		IAXIEList& ieList, void* data = 0);

    /**
     * Destructor
     */
    virtual ~IAXTransaction();

    /**
     * The IAX engine this transaction belongs to
     * @return Pointer to the IAXEngine of this transaction
     */
    inline IAXEngine* getEngine() const
        { return m_engine; }

    /**
     * Get the type of this transaction
     * @return The type of the transaction as enumeration
     */
    inline Type type() const
        { return m_type; }

    /**
     * Retrieve transaction type name
     * @return Transaction type name
     */
    inline const char* typeName()
	{ return typeName(type()); }

    /**
     * Get the state of this transaction
     * @return The state of the transaction as enumeration
     */
    inline State state() const
        { return m_state; }

    /**
     * Retrieve the transaction state name
     * @return Transaction state name
     */
    inline const char* stateName()
	{ return stateName(state()); }

    /**
     * Get the timestamp of this transaction
     * @return The timestamp of this transaction
     */
    inline u_int64_t timeStamp() const
        { return Time::msecNow() - m_timeStamp; }

    /**
     * Get the direction of this transaction
     * @return True if it is an outgoing transaction
     */
    inline bool outgoing() const
        { return m_localInitTrans; }

    /**
     * Store a pointer to arbitrary user data
     * @param data User provided pointer
     */
    inline void setUserData(void* data)
        { m_userdata = data; }

    /**
     * Return the opaque user data stored in the transaction
     * @return Pointer set by user
     */
    inline void* getUserData() const
        { return m_userdata; }

    /**
     * Retrieve the local call number
     * @return 15-bit local call number
     */
    inline u_int16_t localCallNo() const
        { return m_lCallNo; }

    /**
     * Retrieve the remote call number
     * @return 15-bit remote call number
     */
    inline u_int16_t remoteCallNo() const
        { return m_rCallNo; }

    /**
     * Retrieve the remote host+port address
     * @return A reference to the remote address
     */
    inline const SocketAddr& remoteAddr() const
        { return m_addr; }

    /**
     * Retrieve the username
     * @return A reference to the username
     */
    inline const String& username()
	{ return m_username; }

    /**
     * Retrieve the calling number
     * @return A reference to the calling number
     */
    inline const String& callingNo()
	{ return m_callingNo; }

    /**
     * Retrieve the calling name
     * @return A reference to the calling name
     */
    inline const String& callingName()
	{ return m_callingName; }

    /**
     * Retrieve the called number
     * @return A reference to the called number
     */
    inline const String& calledNo()
	{ return m_calledNo; }

    /**
     * Retrieve the called context
     * @return A reference to the called context
     */
    inline const String& calledContext()
	{ return m_calledContext; }

    /**
     * Retrieve the challenge sent/received during authentication
     * @return A reference to the challenge
     */
    inline const String& challenge()
	{ return m_challenge; }

    /**
     * Retrieve the media of a given type
     * @param type Media type to retrieve
     * @return IAXFormat pointer or 0 for invalid type
     */
    inline IAXFormat* getFormat(int type) {
	    if (type == IAXFormat::Audio)
		return &m_format;
	    if (type == IAXFormat::Video)
		return &m_formatVideo;
	    return 0;
	}

    /**
     * Retrieve the media data for a given type
     * @param type Media type to retrieve
     * @return IAXMediaData pointer or 0 for invalid type
     */
    inline IAXMediaData* getData(int type) {
	    if (type == IAXFormat::Audio)
		return &m_dataAudio;
	    if (type == IAXFormat::Video)
		return &m_dataVideo;
	    return 0;
	}

    /**
     * Retrieve the media format used during initialization
     * @param type Media type to retrieve
     * @return The initial media format for the given type
     */
    inline u_int32_t format(int type) {
	    IAXFormat* fmt = getFormat(type);
	    return fmt ? fmt->format() : 0;
	}

    /**
     * Retrieve the incoming media format
     * @param type Media type to retrieve
     * @return The incoming media format for the given type
     */
    inline u_int32_t formatIn(int type) {
	    IAXFormat* fmt = getFormat(type);
	    return fmt ? fmt->in() : 0;
	}

    /**
     * Retrieve the outgoing media format
     * @param type Media type to retrieve
     * @return The outgoing media format for the given type
     */
    inline u_int32_t formatOut(int type) {
	    IAXFormat* fmt = getFormat(type);
	    return fmt ? fmt->out() : 0;
	}

    /**
     * Retrieve the media capability of this transaction
     * @return The media capability of this transaction
     */
    inline u_int32_t capability() const
	{ return m_capability; }

    /**
     * Retrieve the expiring time for a register/unregister transaction
     * @return The expiring time for a register/unregister transaction
     */
    inline u_int32_t expire() const
	{ return m_expire; }

    /**
     * Retrieve the authentication data sent/received during authentication
     * @return A reference to the authentication data
     */
    inline const String& authdata()
	{ return m_authdata; }

    /**
     * Set the destroy flag
     */
    inline void setDestroy()
	{ m_destroy = true; }

    /**
     * Start an outgoing transaction.
     * This method is thread safe
     */
    void start();

    /**
     * Process a frame from remote peer.
     * This method is thread safe
     * @param frame IAX frame belonging to this transaction to process
     * @return 'this' if successful or NULL if the frame is invalid
     */
    IAXTransaction* processFrame(IAXFrame* frame);

    /**
     * Process received media data
     * @param data Received data
     * @param tStamp Mini frame timestamp multiplied by format multiplier
     * @param type Media type
     * @param full True if received in a full frame
     * @param mark Mark flag
     * @return 0
     */
    IAXTransaction* processMedia(DataBlock& data, u_int32_t tStamp,
	int type = IAXFormat::Audio, bool full = false, bool mark = false);

    /**
     * Send media data to remote peer. Update the outgoing media format if changed
     * @param data Data to send
     * @param tStamp Data timestamp
     * @param format Data format
     * @param type Media type
     * @param mark Mark flag
     * @return The number of bytes sent
     */
    unsigned int sendMedia(const DataBlock& data, unsigned int tStamp, u_int32_t format,
	int type = IAXFormat::Audio, bool mark = false);

    /**
     * Get an IAX event from the queue
     * This method is thread safe.
     * @param now Current time
     * @return Pointer to an IAXEvent or 0 if none available
     */
    IAXEvent* getEvent(const Time& now = Time());

    /**
     * Get the maximum allowed number of full frames in the incoming frame list
     * @return The maximum allowed number of full frames in the incoming frame list
     */
    static unsigned char getMaxFrameList();

    /**
     * Set the maximum allowed number of full frames in the incoming frame list
     * @param value The new value of m_maxInFrames
     * @return False if value is greater then IAX2_MAX_TRANSINFRAMELIST
     */
    static bool setMaxFrameList(unsigned char value);

    /**
     * Send an ANSWER frame to remote peer
     * This method is thread safe
     * @return False if the current transaction state is not Connected
     */
    inline bool sendAnswer()
	{ return sendConnected(IAXFullFrame::Answer); }

    /**
     * Send a RINGING frame to remote peer
     * This method is thread safe
     * @return False if the current transaction state is not Connected
     */
    inline bool sendRinging()
	{ return sendConnected(IAXFullFrame::Ringing); }

    /**
     * Send a PROCEEDING frame to remote peer
     * This method is thread safe
     * @return False if the current transaction state is not Connected
     */
    inline bool sendProgress()
	{ return sendConnected(IAXFullFrame::Proceeding); }

    /**
     * Send an ACCEPT/REGACK frame to remote peer
     * This method is thread safe
     * @param expires Optional pointer to expiring time for register transactions
     * @return False if the transaction type is not New and state is NewRemoteInvite or NewRemoteInvite_AuthRep or
     *  if the transaction type is not RegReq and state is NewRemoteInvite or
     *  type is not RegReq/RegRel and state is NewRemoteInvite_AuthRep
     */
    bool sendAccept(unsigned int* expires = 0);

    /**
     * Send a HANGUP frame to remote peer
     * This method is thread safe
     * @param cause Optional reason for hangup
     * @param code Optional code of reason
     * @return False if the transaction type is not New or state is Terminated/Terminating
     */
    bool sendHangup(const char* cause = 0, u_int8_t code = 0);

    /**
     * Send a REJECT/REGREJ frame to remote peer
     * This method is thread safe
     * @param cause Optional reason for reject
     * @param code Optional code of reason
     * @return False if the transaction type is not New/RegReq/RegRel or state is Terminated/Terminating
     */
    bool sendReject(const char* cause = 0, u_int8_t code = 0);

    /**
     * Send an AUTHREQ/REGAUTH frame to remote peer
     * This method is thread safe
     * @return False if the current transaction state is not NewRemoteInvite
     */
    bool sendAuth();

    /**
     * Send an AUTHREP/REGREQ/REGREL frame to remote peer as a response to AUTHREQ/REGREQ/REGREL
     * This method is thread safe
     * @param response Response to send
     * @return False if the current transaction state is not NewLocalInvite_AuthRecv
     */
    bool sendAuthReply(const String& response);

    /**
     * Send a DTMF frame to remote peer
     * This method is thread safe
     * @param dtmf DTMF char to send
     * @return False if the current transaction state is not Connected or dtmf is grater then 127
     */
    inline bool sendDtmf(u_int8_t dtmf)
	{ return dtmf <= 127 ? sendConnected((IAXFullFrame::ControlType)dtmf,IAXFrame::DTMF) : false; }

    /**
     * Send a TEXT frame to remote peer
     * This method is thread safe
     * @param text Text to send
     * @return False if the current transaction state is not Connected
     */
    bool sendText(const char* text);

    /**
     * Send a NOISE frame to remote peer
     * This method is thread safe
     * @param noise Noise value to send
     * @return False if the current transaction state is not Connected or noise is grater then 127
     */
    inline bool sendNoise(u_int8_t noise)
	{ return noise <= 127 ? sendConnected((IAXFullFrame::ControlType)noise,IAXFrame::Noise) : false; }

    /**
     * Abort a registration transaction
     * This method is thread safe
     * @return False transaction is not a registration one or is already terminating
     */
    bool abortReg();

    /**
     * Enable trunking for this transaction
     * @param trunkFrame Pointer to IAXMetaTrunkFrame used to send trunked media
     * @param efficientUse Use or not the trunk based on calls using it
     * @return False trunking is already enabled for this transactio or trunkFrame is 0
     */
    bool enableTrunking(IAXMetaTrunkFrame* trunkFrame, bool efficientUse);

    /**
     * Process a received call token
     * This method is thread safe
     * @param callToken Received call token
     */
    void processCallToken(const DataBlock& callToken);

    /**
     * Process incoming audio miniframes from trunk without timestamps
     * @param ts Trunk frame timestamp
     * @param blocks Received blocks
     * @param now Current time
     */
    void processMiniNoTs(u_int32_t ts, ObjList& blocks, const Time& now = Time());

    /**
     * Print transaction data on stdin
     * @param printStats True to print media statistics
     * @param printFrames True to print in/out pending frames
     * @param location Additional location info to be shown in debug
     */
    void print(bool printStats = false, bool printFrames = false, const char* location = "status");

    /**
     * Retrieve transaction type name from transaction type
     * @param type Transaction type
     * @return Requested type name
     */
    static inline const char* typeName(int type)
	{ return lookup(type,s_typeName); }

    /**
     * Retrieve transaction state name
     * @param state Transaction state
     * @return Requested state name
     */
    static inline const char* stateName(int state)
	{ return lookup(state,s_stateName); }

    /**
     * Transaction type name
     */
    static const TokenDict s_typeName[];

    /**
     * Transaction state name
     */
    static const TokenDict s_stateName[];

    /**
     * Standard message sent if unsupported/unknown/none authentication methosd was received
     */
    static String s_iax_modNoAuthMethod;

    /**
     * Standard message sent if unsupported/unknown/none media format was received
     */
    static String s_iax_modNoMediaFormat;

    /**
     * Standard message sent if the received authentication data is incorrect
     */
    static String s_iax_modInvalidAuth;

    /**
     * Standard message sent if a received frame doesn't have an username information element
     */
    static String s_iax_modNoUsername;

protected:
    /**
     * Constructor: constructs an incoming transaction from a received full frame with an IAX
     *  control message that needs a new transaction
     * @param engine The engine that owns this transaction
     * @param frame A valid full frame
     * @param lcallno Local call number
     * @param addr Address from where the frame was received
     * @param data Pointer to arbitrary user data
     */
    IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno, const SocketAddr& addr,
	void* data = 0);

    /**
     * Constructor: constructs an outgoing transaction with an IAX control message that needs a new transaction
     * @param engine The engine that owns this transaction
     * @param type Transaction type: see Type enumeration
     * @param lcallno Local call number
     * @param addr Address to use
     * @param ieList Starting IE list
     * @param data Pointer to arbitrary user data
     */
    IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno, const SocketAddr& addr, IAXIEList& ieList,
	void* data = 0);

    /**
     * Cleanup
     */
    virtual void destroyed();

    /**
     * Init data members from an IE list
     * @param ieList IE list to init from
     */
    void init(IAXIEList& ieList);

    /**
     * Increment sequence numbers (inbound or outbound) for the frames that need it
     * @param frame Received frame if inbound is true, otherwise a transmitted one
     * @param inbound True for inbound frames
     * @return True if incremented.
     */
    bool incrementSeqNo(const IAXFullFrame* frame, bool inbound);

    /**
     * Test if frame is acceptable (not an out of order or a late one)
     * @param frame Frame to test
     * @return True if frame can be added to incoming frame list
     */
    bool isFrameAcceptable(const IAXFullFrame* frame);

    /**
     * Change the transaction state
     * @param newState the new transaction state
     * @return False if trying to change a termination state into a non termination one
     */
    bool changeState(State newState);

    /**
     * Terminate the transaction.
     * @param evType IAXEvent type to generate
     * @param local If true it is a locally generated event
     * @param frame Frame to build event from
     * @param createIEList If true create IE list in the generated event
     * @return Pointer to a valid IAXEvent
     */
    IAXEvent* terminate(u_int8_t evType, bool local, IAXFullFrame* frame = 0, bool createIEList = true);

    /**
     * Wait for ACK to terminate the transaction. No more events will be generated
     * @param evType IAXEvent type to generate
     * @param local If true it is a locally generated event
     * @param frame Frame to build event from
     * @return Pointer to a valid IAXEvent if evType if non 0, 0 otherwise
     */
    IAXEvent* waitForTerminate(u_int8_t evType = 0, bool local = true, IAXFullFrame* frame = 0);

    /**
     * Constructs an IAXFrameOut frame, send it to remote peer and put it in the transmission list
     * This method is thread safe
     * @param type Frame type
     * @param subclass Frame subclass
     * @param data Frame IE list
     * @param len Frame IE list length
     * @param tStamp Frame timestamp. If 0 the transaction timestamp will be used
     * @param ackOnly Frame's acknoledge only flag
     * @param mark Frame mark flag
     */
    void postFrame(IAXFrame::Type type, u_int32_t subclass, void* data = 0, u_int16_t len = 0, u_int32_t tStamp = 0,
	bool ackOnly = false, bool mark = false);

    /**
     * Constructs an IAXFrameOut frame, send it to remote peer and put it in the transmission list
     * This method is thread safe
     * @param type Frame type
     * @param subclass Frame subclass
     * @param ies Frame IE list
     * @param tStamp Frame timestamp. If 0 the transaction timestamp will be used
     * @param ackOnly Frame's acknoledge only flag
     */
    void postFrameIes(IAXFrame::Type type, u_int32_t subclass, IAXIEList* ies, u_int32_t tStamp = 0,
		bool ackOnly = false);

    /**
     * Send a full frame to remote peer
     * @param frame Frame to send
     * @param vnak If true the transmission is a response to a VNAK frame
     * @return True on success
     */
    bool sendFrame(IAXFrameOut* frame, bool vnak = false);

    /**
     * Create an event
     * @param evType Event type
     * @param local If true it is a locally generated event.
     * @param frame Frame to create from
     * @param newState The transaction new state
     * @return Pointer to an IAXEvent or 0 (invalid IE list)
     */
    IAXEvent* createEvent(u_int8_t evType, bool local, IAXFullFrame* frame, State newState);

    /**
     * Create an event from a received frame that is a response to a sent frame and
     *  change the transaction state to newState. Remove the response from incoming list.
     * @param frame Frame to create response for
     * @param findType Frame type to find
     * @param findSubclass Frame subclass to find
     * @param evType Event type to generate
     * @param local Local flag for the generated event.
     * @param newState New transaction state if an event was generated
     * @return Pointer to an IAXEvent or 0 (invalid IE list)
     */
    IAXEvent* createResponse(IAXFrameOut* frame, u_int8_t findType, u_int8_t findSubclass, u_int8_t evType, bool local, State newState);

    /**
     * Find a response for a previously sent frame
     * @param frame Frame to find response for
     * @param delFrame Delete frame flag. If true on exit, a response was found
     * @return Pointer to an IAXEvent or 0
     */
    IAXEvent* getEventResponse(IAXFrameOut* frame, bool& delFrame);

    /**
     * Find a response for a previously sent frame if the transaction type is New
     * @param frame Frame to find response for
     * @param delFrame Delete frame flag. If true on exit, a response was found
     * @return Pointer to an IAXEvent or 0
     */
    IAXEvent* getEventResponse_New(IAXFrameOut* frame, bool& delFrame);

    /**
     * Process an authentication request. If valid, send an authentication reply
     * @param event Already generated event
     * @return Pointer to a valid IAXEvent
     */
    IAXEvent* processAuthReq(IAXEvent* event);

    /**
     * Process an accept. If not valid (call m_engine->acceptFormatAndCapability) send a reject.
     *  Otherwise return the event
     * @param event Already generated event
     * @return Pointer to a valid IAXEvent
     */
    IAXEvent* processAccept(IAXEvent* event);

    /**
     * Process an authentication reply
     * @param event Already generated event
     * @return Pointer to a valid IAXEvent
     */
    IAXEvent* processAuthRep(IAXEvent* event);

    /**
     * Find a response for a previously sent frame if the transaction type is RegReq/RegRel
     * @param frame Frame to find response for
     * @param delFrame Delete frame flag. If true on exit, a response was found
     * @return Pointer to an IAXEvent or 0
     */
    IAXEvent* getEventResponse_Reg(IAXFrameOut* frame, bool& delFrame);

    /**
     * Update transaction data from the event
     * @param event Already generated event
     * @return The received event
     */
    IAXEvent* processRegAck(IAXEvent* event);

    /**
     * Find out if an incoming frame would start a transaction
     * @param frame Frame to process
     * @param delFrame Delete frame flag. If true on exit, frame is valid
     * @return Pointer to an IAXEvent or 0
     */
    IAXEvent* getEventStartTrans(IAXFullFrame* frame, bool& delFrame);

    /**
     * Find out if a frame is a remote request
     * @param frame Frame to process
     * @param delFrame Delete rame flag. If true on exit, a request was found
     * @return Pointer to an IAXEvent or 0
     */
    IAXEvent* getEventRequest(IAXFullFrame* frame, bool& delFrame);

    /**
     * Find out if a frame is a remote request if transaction type is New
     * @param frame Frame to process
     * @param delFrame Delete rame flag. If true on exit, a request was found
     * @return Pointer to an IAXEvent or 0
     */
    IAXEvent* getEventRequest_New(IAXFullFrame* frame, bool& delFrame);

    /**
     * Search for a frame in m_inFrames having the given type and subclass
     * @param type Frame type to find.
     * @param subclass Frame subclass to find.
     * @return Pointer to frame if found or 0.
     */
    IAXFullFrame* findInFrame(IAXFrame::Type type, u_int32_t subclass);

    /**
     * Search in m_inFrames for a frame with the same timestamp as frameOut and deletes it.
     * @param frameOut Frame to find response for
     * @param type Frame type to find
     * @param subclass Frame subclass to find
     * @return True if found.
     */
    bool findInFrameTimestamp(const IAXFullFrame* frameOut, IAXFrame::Type type, u_int32_t subclass);

    /**
     * Search in m_inFrames for an ACK frame which confirm the received frame and deletes it
     * @param frameOut Frame to find response for
     * @return True if found.
     */
    bool findInFrameAck(const IAXFullFrame* frameOut);

    /**
     * Acknoledge the last received full frame
     */
    void ackInFrames();

    /**
     * Send a frame to remote peer in state Connected
     * This method is thread safe
     * @param subclass Frame subclass to send
     * @param frametype Frame type to send
     * @return False if the current transaction state is not Connected
     */
    bool sendConnected(IAXFullFrame::ControlType subclass, IAXFrame::Type frametype = IAXFrame::Control);

    /**
     * Send an ACK frame
     * @param frame Aknoledged frame
     */
    void sendAck(const IAXFullFrame* frame);

    /**
     * Send an VNAK frame
     */
    void sendVNAK();

    /**
     * Send an Unsupport frame
     * @param subclass Unsupported frame's subclass
     */
    void sendUnsupport(u_int32_t subclass);

    /**
     * Internal protocol outgoing frames processing (PING/LAGRQ)
     * @param frame Frame to process
     * @param delFrame Delete frame flag. If true on exit, a response was found
     * @return 0.
     */
    IAXEvent* processInternalOutgoingRequest(IAXFrameOut* frame, bool& delFrame);

    /**
     * Internal protocol incoming frames processing (PING/LAGRQ)
     * @param frame Frame to process
     * @param delFrame Delete frame flag. If true on exit, a request was found
     * @return 0.
     */
    IAXEvent* processInternalIncomingRequest(const IAXFullFrame* frame, bool& delFrame);

    /**
     * Process mid call control frames
     * @param frame Frame to process
     * @param delFrame Delete frame flag. If true on exit, a request was found
     * @return A valid IAXEvent or 0
     */
    IAXEvent* processMidCallControl(IAXFullFrame* frame, bool& delFrame);

    /**
     * Process mid call IAX control frames
     * @param frame Frame to process
     * @param delFrame Delete frame flag. If true on exit, a request was found
     * @return A valid IAXEvent or 0
     */
    IAXEvent* processMidCallIAXControl(IAXFullFrame* frame, bool& delFrame);

    /**
     * Test if frame is a Reject/RegRej frame
     * @param frame Frame to process.
     * @param delFrame Delete frame flag. If true on exit, a request was found
     * @return A valid IAXEvent or 0.
     */
    IAXEvent* remoteRejectCall(IAXFullFrame* frame, bool& delFrame);

    /**
     * Process received media full frames
     * @param frame Received frame
     * @param type Media type
     * @return 0
     */
    IAXTransaction* processMediaFrame(const IAXFullFrame* frame, int type);

    /**
     * Send all frames from outgoing queue with outbound sequence number starting with seqNo.
     * @param seqNo Requested sequence number
     * @return 0
     */
    IAXTransaction* retransmitOnVNAK(u_int16_t seqNo);

    /**
     * Generate a Reject event after internally rejecting a transaction
     * @param reason The reason of rejecting
     * @param code Error code
     * @return A valid IAXEvent
     */
    IAXEvent* internalReject(const char* reason, u_int8_t code);

    /**
     * Event terminated feedback
     * This method is thread safe
     * @param event The event notifying termination
     */
    void eventTerminated(IAXEvent* event);

    /**
     * Set the current event
     * @param event The event notifying termination
     * @return event
     */
    inline IAXEvent* keepEvent(IAXEvent* event) {
	m_currentEvent = event;
	return event;
    }

private:
    void adjustTStamp(u_int32_t& tStamp);
    void postFrame(IAXFrameOut* frame);
    void receivedVoiceMiniBeforeFull();
    void resetTrunk();
    void init();
    void setPendingEvent(IAXEvent* ev = 0);
    inline void restartTrunkIn(u_int64_t now, u_int32_t ts) {
	    m_trunkInStartTime = now;
	    u_int64_t dt = (now - m_lastVoiceFrameIn) / 1000;
	    m_trunkInTsDelta = m_lastVoiceFrameInTs + (u_int32_t)dt;
	    m_trunkInFirstTs = ts;
	}
    // Process accept format and caps
    bool processAcceptFmt(IAXIEList* list);
    // Process queued ACCEPT. Reject with given reason/code if not found
    // Reject with 'nomedia' if found and format is not acceptable
    IAXEvent* checkAcceptRecv(const char* reason, u_int8_t code);

    // Params
    bool m_localInitTrans;			// True: local initiated transaction
    bool m_localReqEnd;				// Local client requested terminate
    Type m_type;				// Transaction type
    State m_state;				// Transaction state
    bool m_destroy;                             // Destroy flag
    bool m_accepted;                            // ACCEPT received and processed
    u_int64_t m_timeStamp;			// Transaction creation timestamp
    u_int64_t m_timeout;			// Transaction timeout in Terminating state
    SocketAddr m_addr;				// Socket
    u_int16_t m_lCallNo;			// Local peer call id
    u_int16_t m_rCallNo;			// Remote peer call id
    unsigned char m_oSeqNo;			// Outgoing frame sequence number
    unsigned char m_iSeqNo;			// Incoming frame sequence number
    IAXEngine* m_engine;			// Engine that owns this transaction
    void* m_userdata;				// Arbitrary user data
    u_int32_t m_lastFullFrameOut;		// Last transmitted full frame timestamp
    IAXMediaData m_dataAudio;
    IAXMediaData m_dataVideo;
    u_int16_t m_lastAck;			// Last ack'd received frame's oseqno
    IAXEvent* m_pendingEvent;			// Pointer to a pending event or 0
    IAXEvent* m_currentEvent;			// Pointer to last generated event or 0
    // Outgoing frames management
    ObjList m_outFrames;			// Transaction & protocol control outgoing frames
    unsigned int m_retransCount;		// Retransmission counter. 0 --> Timeout
    unsigned int m_retransInterval;		// Frame retransmission interval
    // Incoming frames management
    ObjList m_inFrames;				// Transaction & protocol control incoming frames
    static unsigned char m_maxInFrames;		// Max frames number allowed in m_inFrames
    // Call leg management
    u_int32_t m_pingInterval;			// Ping remote peer interval
    u_int64_t m_timeToNextPing;			// Time of the next Ping
    // Statistics
    u_int32_t m_inTotalFramesCount;		// Total received frames
    u_int32_t m_inOutOfOrderFrames;		// Total out of order frames
    u_int32_t m_inDroppedFrames;		// Total dropped frames
    // Data
    IAXAuthMethod::Type m_authmethod;		// Authentication method to use
    String m_username;				// Username
    String m_callingNo;				// Calling number
    String m_callingName;			// Calling name
    String m_calledNo;				// Called number
    String m_calledContext;			// Called context
    String m_challenge;				// Challenge
    String m_authdata;				// Auth data received with auth reply
    u_int32_t m_expire;				// Registration expiring time
    IAXFormat m_format;				// Audio format
    IAXFormat m_formatVideo;			// Video format
    u_int32_t m_capability;			// Media capability of this transaction
    bool m_callToken;                           // Call token supported/expected
    unsigned int m_adjustTsOutThreshold;        // Adjust outgoing data timestamp threshold
    unsigned int m_adjustTsOutOverrun;          // Value used to adjust outgoing data timestamp on data
                                                //  overrun (incoming data with rate greater then expected)
    unsigned int m_adjustTsOutUnderrun;         // Value used to adjust outgoing data timestamp on data
                                                //  underrun (incoming data with rate less then expected)
    u_int64_t m_lastVoiceFrameIn;               // Time we received the last voice frame
    u_int32_t m_lastVoiceFrameInTs;             // Timestamp in the last received voice frame
    int m_reqVoiceVNAK;                         // Send VNAK if not received full voice frame
    // Meta trunking
    IAXMetaTrunkFrame* m_trunkFrame;		// Reference to a trunk frame if trunking is enabled for this transaction
    bool m_trunkFrameCallsSet;                  // Trunk frame calls increased
    bool m_trunkOutEfficientUse;                // Use or not the trunk frame based on calls using it
    bool m_trunkOutSend;                        // Currently using the trunk frame
    bool m_trunkInSyncUsingTs;                  // Incoming trunk without timestamps: generate timestamp
                                                //  using time or using trunk timestamp
    u_int64_t m_trunkInStartTime;               // First time we received trunk in data
    u_int32_t m_trunkInTsDelta;                 // Value used to re-build ts: last voice timestamp
    u_int32_t m_trunkInTsDiffRestart;           // Incoming trunk without timestamp: diff between timestamps at which we restart
    u_int32_t m_trunkInFirstTs;                 // Incoming trunk without timestamp: first trunk timestamp
    // Postponed start
    IAXIEList* m_startIEs;                      // Postponed start
};

/**
 * This class holds an event generated by a transaction
 * @short Event class
 */
class YIAX_API IAXEvent
{
    friend class IAXTransaction;
    friend class IAXConnectionlessTransaction;
public:
    /**
     * Event type as enumeration
     */
    enum Type {
	DontSet = 0,            // Used internal
        Invalid,		// Invalid frame received
	Terminated,		// Transaction terminated
        Timeout,		// Transaction timeout
	NotImplemented,		// Feature not implemented
	New,			// New remote transaction
	AuthReq,		// Auth request
	AuthRep,		// Auth reply
	Accept,			// Request accepted
	Hangup,			// Remote hangup
	Reject,			// Remote reject
	Busy,			// Call busy
	Text,			// Text frame received
	Dtmf,			// DTMF frame received
	Noise,			// Noise frame received
	Answer,			// Call answered
	Quelch,			// Quelch the call
	Unquelch,		// Unquelch the call
	Progressing,		// Call progressing
	Ringing,		// Ringing
    };

    /**
     * Destructor
     * Dereferences the transaction possibly causing its destruction
     */
    ~IAXEvent();

    /**
     * Get the type of this event
     * @return The type of the event as enumeratio
     */
    inline Type type() const
	{ return m_type; }

    /**
     * Check if this is a locally generated event
     * @return True if it is a locally generated event
     */
    inline bool local() const
        { return m_local; }

    /**
     * Check if this is a transaction finalization event
     * @return True if the transaction has finalized and will be destroyed
     */
    inline bool final() const
        { return m_final; }

    /**
     * Set the final flag.
     */
    inline void setFinal()
        { m_final = true; }

    /**
     * Get the type of the frame that generated the event
     * If 0 (internal event), the event consumer must delete the event
     * @return Frame type
     */
    inline u_int8_t frameType()
	{ return m_frameType; }

    /**
     * Get the subclass of the frame that generated the event
     * @return Frame subclass
     */
    inline u_int32_t subclass()
	{ return m_subClass; }

    /**
     * Get the IAX engine this event belongs to, if any
     * @return The IAX engine this event belongs to, if any
     */
    inline IAXEngine* getEngine() const
	{ return m_transaction ? m_transaction->getEngine() : 0; }

    /**
     * Get the IAX transaction that generated the event, if any
     * @return The IAX transaction that generated the event, if any
     */
    inline IAXTransaction* getTransaction() const
	{ return m_transaction; }

    /**
     * Get the opaque user data stored in the transaction
     * @return The opaque user data stored in the transaction
     */
    inline void* getUserData() const
	{ return m_transaction ? m_transaction->getUserData() : 0; }

    /**
     * Get the IE list
     * @return IE list reference
     */
    inline IAXIEList& getList()
	{ return *m_ieList; }

protected:
    /**
     * Constructor
     * @param type Event type
     * @param local Local flag
     * @param final Final flag
     * @param transaction IAX transaction that generated the event
     * @param frameType The type of the frame that generated the event
     * @param subclass The subclass of the frame that generated the event
     */
    IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, u_int8_t frameType = 0, u_int32_t subclass = 0);

    /**
     * Constructor
     * @param type Event type
     * @param local Local flag
     * @param final Final flag
     * @param transaction IAX transaction that generated the event
     * @param frame The frame that generated the event
     */
    IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, IAXFullFrame* frame = 0);

private:
    inline IAXEvent() {}		// Default constructor

    Type m_type;			// Event type
    u_int8_t m_frameType;		// Frame type
    u_int32_t m_subClass;		// Frame subclass
    bool m_local;			// If true the event is generated locally, the receiver MUST not respond
    bool m_final;			// Final event flag
    IAXTransaction* m_transaction;	// Transaction that generated this event
    IAXIEList* m_ieList;		// IAXInfoElement list
};

/**
 * This class holds all information needded to manipulate all IAX transactions and events
 * @short IAX engine class
 */
class YIAX_API IAXEngine : public DebugEnabler, public Mutex
{
public:
    /**
     * Constructor
     * @param iface Address of the interface to use, default all (0.0.0.0)
     * @param port UDP port to run the protocol on
     * @param format Default media format
     * @param capab Media capabilities of this engine
     * @param params Optional extra parameter list
     * @param name Engine name
     */
    IAXEngine(const char* iface, int port, u_int32_t format, u_int32_t capab,
	const NamedList* params = 0, const char* name = "iaxengine");

    /**
     * Destructor
     * Closes all transactions belonging to this engine and flush all queues
     */
    virtual ~IAXEngine();

    /**
     * Retrieve the engine name
     * @return Engine name
     */
    inline const String& name() const
	{ return m_name; }

    /**
     * Retrieve the default caller number type
     * @return Default caller number type
     */
    inline u_int8_t callerNumType() const
	{ return m_callerNumType; }

    /**
     * Retrieve the default caller number presentation and screening concatenated value
     * @return Default caller number presentation and screening
     */
    inline u_int8_t callingPres() const
	{ return m_callingPres; }

    /**
     * Add a parsed frame to the transaction list
     * @param addr Address from which the frame was received
     * @param frame A parsed IAX frame
     * @return Pointer to the transaction or 0 to deref the frame
     */
    IAXTransaction* addFrame(const SocketAddr& addr, IAXFrame* frame);

    /**
     * Add a raw frame to the transaction list
     * @param addr Address from which the message was received
     * @param buf Pointer to the start of the buffer holding the IAX frame
     * @param len Length of the message buffer
     * @return Pointer to the transaction or 0
     */
    IAXTransaction* addFrame(const SocketAddr& addr, const unsigned char* buf, unsigned int len);

    /**
     * Find a complete transaction.
     * This method is thread safe
     * @param addr Remote address
     * @param rCallNo Remote transaction call number
     * @return Referrenced pointer to the transaction or 0
     */
    IAXTransaction* findTransaction(const SocketAddr& addr, u_int16_t rCallNo);

    /**
     * Process media from remote peer. Descendents must override this method
     * @param transaction IAXTransaction that owns the call leg
     * @param data Media data
     * @param tStamp Media timestamp
     * @param type Media type
     * @param mark Mark flag
     */
    virtual void processMedia(IAXTransaction* transaction, DataBlock& data, u_int32_t tStamp,
	int type, bool mark)
	{}

    /**
     * Event processor method. Keeps calling getEvent() and passing
     *  any events to processEvent() until there are no more events
     * @return True if at least one event was processed
     */
    bool process();

    /**
     * Get the timeout interval sent challenge
     * @return Sent challenge timeout interval
     */
    inline unsigned int challengeTout() const
        { return m_challengeTout; }

    /**
     * Get the maximum allowed frame length
     * @return The maximum allowed frame length
     */
    inline u_int16_t maxFullFrameDataLen() const
        { return m_maxFullFrameDataLen; }

    /**
     * Get the default media format
     * @param audio True to retrieve default audio format, false for video format
     * @return The default media format
     */
    inline u_int32_t format(bool audio = true) const
        { return audio ? m_format : m_formatVideo; }

    /**
     * Get the media capability of this engine
     * @return The media capability of this engine
     */
    inline u_int32_t capability() const
        { return m_capability; }

    /**
     * Retrieve outgoing data timestamp adjust values
     * @param thres Adjust outgoing data timestamp threshold
     * @param over Value used to adjust outgoing data timestamp on data overrun
     * @param under Value used to adjust outgoing data timestamp on data underrun
     */
    inline void getOutDataAdjust(unsigned int& thres, unsigned int& over,
	unsigned int& under) const {
	    thres = m_adjustTsOutThreshold;
	    over = m_adjustTsOutOverrun;
	    under = m_adjustTsOutUnderrun;
	}

    /**
     * Initialize outgoing data timestamp adjust values.
     * This method is thread safe
     * @param params Parameters list
     * @param tr Optional transaction to init, initialize the engine's data if 0
     */
    void initOutDataAdjust(const NamedList& params, IAXTransaction* tr = 0);

    /**
     * (Re)Initialize the engine
     * @param params Parameter list
     */
    void initialize(const NamedList& params);

    /**
     * Read data from socket
     * @param addr Socket to read from
     */
    void readSocket(SocketAddr& addr);

    /**
     * Write data to socket.
     * @param buf Data to write
     * @param len Data length
     * @param addr Socket to write to
     * @param frame Optional frame to be printed
     * @param sent Pointer to variable to be filled with the number of bytes sent
     * @return True on success
     */
    bool writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame = 0,
	unsigned int* sent = 0);

    /**
     * Write a full frame to socket
     * @param addr Socket to write to
     * @param frame Frame to write
     * @return True on success
     */
    inline bool writeSocket(const SocketAddr& addr, IAXFullFrame* frame)
	{ return !frame || writeSocket(frame->data().data(),frame->data().length(),addr,frame); }

    /**
     * Read events
     */
    void runGetEvents();

    /**
     * Removes a transaction from queue. Free the allocated local call number
     *  Does not delete it
     * @param transaction Transaction to remove
     */
    void removeTransaction(IAXTransaction* transaction);

    /**
     * Check if there are any transactions in the engine
     * This method is thread safe
     * @return True if the engine holds at least 1 transaction
     */
    bool haveTransactions();

    /**
     * Return the transactions count
     * This method is thread safe
     * @return Transactions count
     */
    u_int32_t transactionCount();

    /**
     * Send an INVAL with call numbers set to 0 to a remote peer to keep it alive
     * @param addr Address to send to
     */
    void keepAlive(const SocketAddr& addr);

    /**
     * Process a new format received with a full frame
     * @param trans Transaction that received the new format
     * @param type Media type
     * @param format The received format
     * @return True if accepted
     */
    virtual bool mediaFormatChanged(IAXTransaction* trans, int type, u_int32_t format)
	{ return false; }

    /**
     * Check call token on incoming call requests.
     * This method is called by the engine when processing an incoming call request
     * @param addr The address from where the call request was received
     * @param frame Received frame
     * @return True if accepted, false to ignore the call
     */
    virtual bool checkCallToken(const SocketAddr& addr, IAXFullFrame& frame);

    /**
     * Process the initial received format and capability.
     * If accepted on exit will set the transaction format and capability
     * @param trans Transaction that received the new format
     * @param caps Optional codecs to set in transaction before processing
     * @param type Media type
     * @return True if accepted
     */
    bool acceptFormatAndCapability(IAXTransaction* trans, unsigned int* caps = 0,
	int type = IAXFormat::Audio);

    /**
     * Default event handler. event MUST NOT be deleted
     * @param event The event to handle
     */
    virtual void defaultEventHandler(IAXEvent* event);

    /**
     * Check if the engine is exiting
     * @return True if the engine is exiting
     */
    inline bool exiting() const
	{ return m_exiting; }

    /**
     * Set the exiting flag
     */
    virtual void setExiting();

    /**
     * Enable trunking for the given transaction. Allocate a trunk meta frame if needed.
     * Trunk data is ignored if a trunk object for transaction remote address already exists
     * @param trans Transaction to enable trunking for
     * @param params Trunk parameters list, may be 0
     * @param prefix Trunk parameters name prefix
     */
    void enableTrunking(IAXTransaction* trans, const NamedList* params,
	const String& prefix = String::empty());

    /**
     * Enable trunking for the given transaction. Allocate a trunk meta frame if needed.
     * Trunk data is ignored if a trunk object for transaction remote address already exists
     * @param trans Transaction to enable trunking for
     * @param data Trunk info to use
     */
    void enableTrunking(IAXTransaction* trans, IAXTrunkInfo& data);

    /**
     * Init incoming trunking data for a given transaction
     * @param trans Transaction to init
     * @param params Trunk parameters list, may be 0
     * @param prefix Trunk parameters name prefix
     */
    void initTrunkIn(IAXTransaction* trans, const NamedList* params,
	const String& prefix = String::empty());

    /**
     * Init incoming trunking data for a given transaction
     * @param trans Transaction to init
     * @param data Trunk info to use
     */
    void initTrunkIn(IAXTransaction* trans, IAXTrunkInfo& data);

    /**
     * Retrieve the default trunk info data
     * @param info Destination to be set with trunk info pointer
     * @return True if destination pointr is valid
     */
    inline bool trunkInfo(RefPointer<IAXTrunkInfo>& info) {
	    Lock lck(m_trunkInfoMutex);
	    info = m_trunkInfoDef;
	    return info != 0;
	}

    /**
     * Send an INVAL frame
     * @param frame Frame for which to send an INVAL frame
     * @param addr The address from where the call request was received
     */
    void sendInval(IAXFullFrame* frame, const SocketAddr& addr);

    /**
     * Keep calling processTrunkFrames to send trunked media data
     */
    void runProcessTrunkFrames();

    /**
     * Get the socket used for engine operation
     * @return Reference to the UDP socket
     */
    inline Socket& socket()
	{ return m_socket; }

    /**
     * Retrieve the socket address on wgich we are bound
     * @return Local address we are bound on
     */
    inline const SocketAddr& addr() const
	{ return m_addr; }

    /**
     * Send engine formats
     * @param caps Capabilities
     * @param fmtAudio Default audio format
     * @param fmtVideo Default video format
     */
    inline void setFormats(u_int32_t caps, u_int32_t fmtAudio, u_int32_t fmtVideo) {
	    m_format = fmtAudio;
	    m_formatVideo = fmtVideo;
	    m_capability = caps;
	}

    /**
     * Retrieve a port parameter
     * @param params Parameters list
     * @param param Parameter to retrieve
     * @return The port (default, 4569, if the parameter is missing or invalid)
     */
    static inline int getPort(const NamedList& params, const String& param = "port")
	{ return params.getIntValue(param,4569); }

    /**
     * Get the MD5 data from a challenge and a password
     * @param md5data Destination String
     * @param challenge Challenge source
     * @param password Password source
     */
    static void getMD5FromChallenge(String& md5data, const String& challenge, const String& password);

    /**
     * Test if a received response to an authentication request is correct
     * @param md5data Data to compare with
     * @param challenge Received challenge
     * @param password Password source
     */
    static bool isMD5ChallengeCorrect(const String& md5data, const String& challenge, const String& password);

    /**
     * Build a time signed secret used to authenticate an IP address
     * @param buf Destination buffer
     * @param secret Extra secret to add to MD5 sum
     * @param addr Socket address
     */
    static void buildAddrSecret(String& buf, const String& secret,
	const SocketAddr& addr);

    /**
     * Decode a secret built using buildAddrSecret()
     * @param buf Input buffer
     * @param secret Extra secret to check
     * @param addr Socket address
     * @return Secret age, negative if invalid
     */
    static int addrSecretAge(const String& buf, const String& secret,
	const SocketAddr& addr);

    /**
     * Add string (keyword) if found in a dictionary or integer parameter to a named list
     * @param list Destination list
     * @param param Parameter to add to the list
     * @param tokens The dictionary used to find the given value
     * @param val The value to find/add to the list
     */
    static inline void addKeyword(NamedList& list, const char* param,
	const TokenDict* tokens, unsigned int val) {
	    const char* value = lookup(val,tokens);
	    if (value)
		list.addParam(param,value);
	    else
		list.addParam(param,String(val));
	}

    /**
     * Decode a DATETIME value
     * @param dt Value to decode
     * @param year The year component of the date
     * @param month The month component of the date
     * @param day The day component of the date
     * @param hour The hour component of the time
     * @param minute The minute component of the time
     * @param sec The seconds component of the time
     */
    static void decodeDateTime(u_int32_t dt, unsigned int& year, unsigned int& month,
	unsigned int& day, unsigned int& hour, unsigned int& minute, unsigned int& sec);

    /**
     * Calculate overall timeout from interval and retransmission counter
     * @param interval The first retransmisssion interval
     * @param nRetrans The number of retransmissions
     * @return The overall timeout
     */
    static unsigned int overallTout(unsigned int interval = IAX2_RETRANS_INTERVAL_DEF,
	unsigned int nRetrans = IAX2_RETRANS_COUNT_DEF);

protected:
    /**
     * Process all trunk meta frames in the queue
     * @param time Time of the call
     * @return True if at least one frame was sent
     */
    bool processTrunkFrames(const Time& time = Time());

    /**
     * Default event for connection transactions handler. This method may be overriden to perform custom
     *  processing
     * This method is thread safe
     * @param event Event to process
     */
    virtual void processEvent(IAXEvent* event);

    /**
     * Get an IAX event from the queue.
     * This method is thread safe.
     * @param now Current time
     * @return Pointer to an IAXEvent or 0 if none is available
     */
    IAXEvent* getEvent(const Time& now = Time());

    /**
     * Generate call number. Update used call numbers list
     * @return Call number or 0 if none available
     */
    u_int16_t generateCallNo();

    /**
     * Release a call number
     * @param lcallno Call number to release
     */
    void releaseCallNo(u_int16_t lcallno);

    /**
     * Start a transaction based on a local request
     * @param type Transaction type
     * @param addr Remote address to send the request
     * @param ieList First frame IE list
     * @param refTrans Return a refferenced transaction pointer
     * @param startTrans Start transaction
     * @return IAXTransaction pointer on success
     */
    IAXTransaction* startLocalTransaction(IAXTransaction::Type type,
	const SocketAddr& addr, IAXIEList& ieList,
	bool refTrans = false, bool startTrans = true);

    /**
     * Bind the socket. Terminate it before trying
     * @param iface Address of the interface to use, default all (0.0.0.0)
     * @param port UDP port to run the protocol on
     * @param force Force binding if failed on required port
     * @return True on success
     */
    bool bind(const char* iface, int port, bool force);

    int m_trunking;                             // Trunking capability: negative: ok, otherwise: not enabled

private:
    String m_name;                              // Engine name
    Socket m_socket;				// Socket
    SocketAddr m_addr;                          // Address we are bound on
    ObjList** m_transList;			// Full transactions
    ObjList m_incompleteTransList;		// Incomplete transactions (no remote call number)
    bool m_lUsedCallNo[IAX2_MAX_CALLNO + 1];	// Used local call numnmbers flags
    int m_lastGetEvIndex;			// getEvent: keep last array entry
    bool m_exiting;                             // Exiting flag
    // Parameters
    int m_maxFullFrameDataLen;			// Max full frame data (IE list) length
    u_int16_t m_startLocalCallNo;		// Start index of local call number allocation
    u_int16_t m_transListCount;			// m_transList count
    unsigned int m_challengeTout;		// Sent challenge timeout interval
    bool m_callToken;                           // Call token required on incoming calls
    String m_callTokenSecret;                   // Secret used to generate call tokens
    int m_callTokenAge;                         // Max allowed call token age
    bool m_showCallTokenFailures;               // Print incoming call token failures to output
    bool m_rejectMissingCallToken;              // Reject/ignore incoming calls without call token if mandatory
    bool m_printMsg;                            // Print frame to output
    u_int8_t m_callerNumType;                   // Caller number type
    u_int8_t m_callingPres;                     // Caller presentation + screening
    // Media
    u_int32_t m_format;				// The default media format
    u_int32_t m_formatVideo;                    // Default video format
    u_int32_t m_capability;			// The media capability
    unsigned int m_adjustTsOutThreshold;        // Adjust outgoing data timestamp threshold
    unsigned int m_adjustTsOutOverrun;          // Value used to adjust outgoing data timestamp on data
                                                //  overrun (incoming data with rate greater then expected)
    unsigned int m_adjustTsOutUnderrun;         // Value used to adjust outgoing data timestamp on data
                                                //  underrun (incoming data with rate less then expected)
    // Trunking
    Mutex m_mutexTrunk;				// Mutex for trunk operations
    ObjList m_trunkList;			// Trunk frames list
    Mutex m_trunkInfoMutex;                     // Trunk info mutex
    RefPointer<IAXTrunkInfo> m_trunkInfoDef;    // Defaults for trunk data
};

}

#endif /* __YATEIAX_H */

/* vi: set ts=8 sw=4 sts=4 noet: */
