/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#ifndef __PJSIP_SIP_TRANSPORT_H__
#define __PJSIP_SIP_TRANSPORT_H__

/**
 * @file sip_transport.h
 * @brief SIP Transport
 */

#include <pjsip/sip_msg.h>
#include <pjsip/sip_parser.h>
#include <pjsip/sip_resolve.h>
#include <pj/sock.h>
#include <pj/list.h>
#include <pj/ioqueue.h>
#include <pj/timer.h>

PJ_BEGIN_DECL

/**
 * @defgroup PJSIP_TRANSPORT Transport
 * @ingroup PJSIP_CORE
 * @brief This is the transport framework.
 *
 * The transport framework is fully extensible. Please see
 * <A HREF="/docs.htm">PJSIP Developer's Guide</A> PDF
 * document for more information.
 *
 * Application MUST register at least one transport to PJSIP before any
 * messages can be sent or received. Please see @ref PJSIP_TRANSPORT_UDP
 * on how to create/register UDP transport to the transport framework.
 *
 * @{
 */

/*****************************************************************************
 *
 * GENERAL TRANSPORT (NAMES, TYPES, ETC.)
 *
 *****************************************************************************/

/*
 * Forward declaration for transport factory (since it is referenced by
 * the transport factory itself).
 */
typedef struct pjsip_tpfactory pjsip_tpfactory;


/**
 * Flags for SIP transports.
 */
enum pjsip_transport_flags_e
{
    PJSIP_TRANSPORT_RELIABLE        = 1,    /**< Transport is reliable.     */
    PJSIP_TRANSPORT_SECURE          = 2,    /**< Transport is secure.       */
    PJSIP_TRANSPORT_DATAGRAM        = 4     /**< Datagram based transport.  
                                                 (it's also assumed to be 
                                                 connectionless)            */
};

/**
 * Check if transport tp is reliable.
 */
#define PJSIP_TRANSPORT_IS_RELIABLE(tp)     \
            ((tp)->flag & PJSIP_TRANSPORT_RELIABLE)

/**
 * Check if transport tp is secure.
 */
#define PJSIP_TRANSPORT_IS_SECURE(tp)       \
            ((tp)->flag & PJSIP_TRANSPORT_SECURE)

/**
 * Register new transport type to PJSIP. The PJSIP transport framework
 * contains the info for some standard transports, as declared by
 * #pjsip_transport_type_e. Application may use non-standard transport
 * with PJSIP, but before it does so, it must register the information
 * about the new transport type to PJSIP by calling this function.
 *
 * @param tp_flag   The flags describing characteristics of this
 *                  transport type.
 * @param tp_name   Transport type name.
 * @param def_port  Default port to be used for the transport.
 * @param p_tp_type On successful registration, it will be filled with
 *                  the registered type. This argument is optional.
 *
 * @return          PJ_SUCCESS if registration is successful, or
 *                  PJSIP_ETYPEEXISTS if the same transport type has
 *                  already been registered.
 */
PJ_DECL(pj_status_t) pjsip_transport_register_type(unsigned tp_flag,
                                                   const char *tp_name,
                                                   int def_port,
                                                   int *p_tp_type);


/**
 * Get the transport type from the transport name.
 *
 * @param name      Transport name, such as "TCP", or "UDP".
 *
 * @return          The transport type, or PJSIP_TRANSPORT_UNSPECIFIED if 
 *                  the name is not recognized as the name of supported 
 *                  transport.
 */
PJ_DECL(pjsip_transport_type_e) 
pjsip_transport_get_type_from_name(const pj_str_t *name);

/**
 * Get the first transport type that has the specified flags.
 *
 * @param flag      The transport flag.
 *
 * @return          Transport type.
 */
PJ_DECL(pjsip_transport_type_e) 
pjsip_transport_get_type_from_flag(unsigned flag);

/**
 * Get the socket address family of a given transport type.
 *
 * @param type      Transport type.
 *
 * @return          Transport type.
 */
PJ_DECL(int) pjsip_transport_type_get_af(pjsip_transport_type_e type);

/**
 * Get transport flag from type.
 *
 * @param type      Transport type.
 *
 * @return          Transport flags.
 */
PJ_DECL(unsigned)
pjsip_transport_get_flag_from_type( pjsip_transport_type_e type );

/**
 * Get the default SIP port number for the specified type.
 *
 * @param type      Transport type.
 *
 * @return          The port number, which is the default SIP port number for
 *                  the specified type.
 */
PJ_DECL(int) 
pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type);

/**
 * Get transport type name.
 *
 * @param t         Transport type.
 *
 * @return          Transport name.
 */
PJ_DECL(const char*) pjsip_transport_get_type_name(pjsip_transport_type_e t);

/**
 * Get longer description for the specified transport type.
 *
 * @param t         Transport type.
 *
 * @return          Transport description.
 */
PJ_DECL(const char*) pjsip_transport_get_type_desc(pjsip_transport_type_e t);



/*****************************************************************************
 *
 * TRANSPORT SELECTOR.
 *
 *****************************************************************************/

/**
 * This structure describes the type of data in pjsip_tpselector.
 */
typedef enum pjsip_tpselector_type
{
    /** Transport is not specified. */
    PJSIP_TPSELECTOR_NONE,

    /** Use the specific transport to send request. */
    PJSIP_TPSELECTOR_TRANSPORT,

    /** Use the specific listener to send request. */
    PJSIP_TPSELECTOR_LISTENER,

    /** Use the IP version criteria to send request. */
    PJSIP_TPSELECTOR_IP_VER,

} pjsip_tpselector_type;

/**
 * This enumerator describes the IP version criteria for pjsip_tpselector.
 */
typedef enum pjsip_tpselector_ip_ver
{
    /** IPv4 only. */
    PJSIP_TPSELECTOR_USE_IPV4_ONLY,

    /**
     * No preference. IP version used will depend on the order of addresses
     * returned by pjsip_resolver.
     */
    PJSIP_TPSELECTOR_NO_PREFERENCE,

    /** IPv4 is preferred. */
    PJSIP_TPSELECTOR_PREFER_IPV4,

    /** IPv6 is preferred. */
    PJSIP_TPSELECTOR_PREFER_IPV6,

    /** IPv6 only. */
    PJSIP_TPSELECTOR_USE_IPV6_ONLY

} pjsip_tpselector_ip_ver;


/**
 * This structure describes the transport/listener preference to be used
 * when sending outgoing requests.
 *
 * Normally transport will be selected automatically according to rules about
 * sending requests. But some applications (such as proxies or B2BUAs) may 
 * want to explicitly use specific transport to send requests, for example
 * when they want to make sure that outgoing request should go from a specific
 * network interface.
 *
 * The pjsip_tpselector structure is used for that purpose, i.e. to allow
 * application specificly request that a particular transport/listener
 * should be used to send request. This structure is used when calling
 * pjsip_tsx_set_transport() and pjsip_dlg_set_transport().
 *
 * If application disables connection reuse and wants to force creating
 * a new transport, it needs to consider the following couple of things:
 * - If it still wants to reuse an existing transport (if any), it
 *   needs to keep a reference to that transport and specifically set
 *   the transport to be used for sending requests.
 * - Delete those existing transports manually when no longer needed.
 */
typedef struct pjsip_tpselector
{
    /** The type of data in the union */
    pjsip_tpselector_type   type;

    /**
     * Whether to disable reuse of an existing connection.
     * This setting will be ignored if (type == PJSIP_TPSELECTOR_TRANSPORT)
     * and transport in the union below is set.
     */
    pj_bool_t disable_connection_reuse;

    /**
     * Union representing the transport/listener/IP version criteria
     * to be used.
     */
    union {
        pjsip_transport         *transport;
        pjsip_tpfactory         *listener;
        pjsip_tpselector_ip_ver  ip_ver;
        void                    *ptr;
    } u;

} pjsip_tpselector;


/**
 * Add transport/listener reference in the selector to prevent the specified
 * transport/listener from being destroyed while application still has
 * reference to it.
 *
 * @param sel   The transport selector.
 */
PJ_DECL(void) pjsip_tpselector_add_ref(pjsip_tpselector *sel);


/**
 * Decrement transport/listener reference in the selector.
 * @param sel   The transport selector
 */
PJ_DECL(void) pjsip_tpselector_dec_ref(pjsip_tpselector *sel);


/*****************************************************************************
 *
 * RECEIVE DATA BUFFER.
 *
 *****************************************************************************/

/** 
 * A customized ioqueue async operation key which is used by transport
 * to locate rdata when a pending read operation completes.
 */
typedef struct pjsip_rx_data_op_key
{
    pj_ioqueue_op_key_t         op_key; /**< ioqueue op_key.            */
    pjsip_rx_data              *rdata;  /**< rdata associated with this */
} pjsip_rx_data_op_key;


/**
 * Incoming message buffer.
 * This structure keep all the information regarding the received message. This
 * buffer lifetime is only very short, normally after the transaction has been
 * called, this buffer will be deleted/recycled. So care must be taken when
 * allocating storage from the pool of this buffer.
 */
struct pjsip_rx_data
{

    /**
     * tp_info is part of rdata that remains static for the duration of the
     * buffer. It is initialized when the buffer was created by transport.
     */
    struct 
    {
        /** Memory pool for this buffer. */
        pj_pool_t               *pool;

        /** The transport object which received this packet. */
        pjsip_transport         *transport;

        /** Other transport specific data to be attached to this buffer. */
        void                    *tp_data;

        /** Ioqueue key. */
        pjsip_rx_data_op_key     op_key;

    } tp_info;


    /**
     * pkt_info is initialized by transport when it receives an incoming
     * packet.
     */
    struct
    {
        /** Time when the message was received. */
        pj_time_val              timestamp;

        /** Pointer to the original packet. */
        char                     packet[PJSIP_MAX_PKT_LEN];

        /** Zero termination for the packet. */
        pj_uint32_t              zero;

        /** The length of the packet received. */
        pj_ssize_t               len;

        /** The source address from which the packet was received. */
        pj_sockaddr              src_addr;

        /** The length of the source address. */
        int                      src_addr_len;

        /** The IP source address string (NULL terminated). */
        char                     src_name[PJ_INET6_ADDRSTRLEN];

        /** The IP source port number. */
        int                      src_port;

    } pkt_info;


    /**
     * msg_info is initialized by transport mgr (tpmgr) before this buffer
     * is passed to endpoint.
     */
    struct
    {
        /** Start of msg buffer. */
        char                    *msg_buf;

        /** Length fo message. */
        int                      len;

        /** The parsed message, if any. */
        pjsip_msg               *msg;

        /** Short description about the message. 
         *  Application should use #pjsip_rx_data_get_info() instead.
         */
        char                    *info;

        /** The Call-ID header as found in the message. */
        pjsip_cid_hdr           *cid;

        /** The From header as found in the message. */
        pjsip_from_hdr          *from;

        /** The To header as found in the message. */
        pjsip_to_hdr            *to;

        /** The topmost Via header as found in the message. */
        pjsip_via_hdr           *via;

        /** The CSeq header as found in the message. */
        pjsip_cseq_hdr          *cseq;

        /** Max forwards header. */
        pjsip_max_fwd_hdr       *max_fwd;

        /** The first route header. */
        pjsip_route_hdr         *route;

        /** The first record-route header. */
        pjsip_rr_hdr            *record_route;

        /** Content-type header. */
        pjsip_ctype_hdr         *ctype;

        /** Content-length header. */
        pjsip_clen_hdr          *clen;

        /** "Require" header containing aggregates of all Require
         *  headers found in the message, or NULL. 
         */
        pjsip_require_hdr       *require;

        /** "Supported" header containing aggregates of all Supported
         *  headers found in the message, or NULL. 
         */
        pjsip_supported_hdr     *supported;

        /** The list of error generated by the parser when parsing 
            this message. 
         */
        pjsip_parser_err_report parse_err;

    } msg_info;


    /**
     * endpt_info is initialized by endpoint after this buffer reaches
     * endpoint.
     */
    struct
    {
        /** 
         * Data attached by modules to this message. 
         */
        void    *mod_data[PJSIP_MAX_MODULE];

    } endpt_info;

};

/**
 * Get printable information about the message in the rdata.
 *
 * @param rdata     The receive data buffer.
 *
 * @return          Printable information.
 */
PJ_DECL(char*) pjsip_rx_data_get_info(pjsip_rx_data *rdata);

/**
 * Clone pjsip_rx_data. This will duplicate the contents of
 * pjsip_rx_data and add reference count to the transport.
 * Once application has finished using the cloned pjsip_rx_data,
 * it must release it by calling  #pjsip_rx_data_free_cloned().
 *
 * By default (if flags is set to zero), this function copies the
 * transport pointer in \a tp_info, duplicates the \a pkt_info,
 * perform deep clone of the \a msg_info parts of the rdata, and
 * fills the \a endpt_info (i.e. the \a mod_data) with zeros.
 *
 * @param src       The source to be cloned.
 * @param flags     Optional flags. Must be zero for now.
 * @param p_rdata   Pointer to receive the cloned rdata.
 *
 * @return          PJ_SUCCESS on success or the appropriate error.
 */
PJ_DECL(pj_status_t) pjsip_rx_data_clone(const pjsip_rx_data *src,
                                         unsigned flags,
                                         pjsip_rx_data **p_rdata);

/**
 * Free cloned pjsip_rx_data. This function must be and must only
 * be called for a cloned pjsip_rx_data. Specifically, it must NOT
 * be called for the original pjsip_rx_data that is returned by
 * transports.
 *
 * This function will free the memory used by the pjsip_rx_data and
 * decrement the transport reference counter.
 *
 * @param rdata     The receive data buffer.
 *
 * @return          PJ_SUCCESS on success or the appropriate error.
 */
PJ_DECL(pj_status_t) pjsip_rx_data_free_cloned(pjsip_rx_data *rdata);


/*****************************************************************************
 *
 * TRANSMIT DATA BUFFER MANIPULATION.
 *
 *****************************************************************************/

/** Customized ioqueue async operation key, used by transport to keep
 *  callback parameters.
 */
typedef struct pjsip_tx_data_op_key
{
    /** ioqueue pending operation key. */
    pj_ioqueue_op_key_t     key;

    /** Transmit data associated with this key. */
    pjsip_tx_data          *tdata;

    /** Arbitrary token (attached by transport) */
    void                   *token;

    /** Callback to be called when pending transmit operation has
        completed.
     */
    void                  (*callback)(pjsip_transport*,void*,pj_ssize_t);
} pjsip_tx_data_op_key;


/**
 * Data structure for sending outgoing message. Application normally creates
 * this buffer by calling #pjsip_endpt_create_tdata.
 *
 * The lifetime of this buffer is controlled by the reference counter in this
 * structure, which is manipulated by calling #pjsip_tx_data_add_ref and
 * #pjsip_tx_data_dec_ref. When the reference counter has reached zero, then
 * this buffer will be destroyed.
 *
 * A transaction object normally will add reference counter to this buffer
 * when application calls #pjsip_tsx_send_msg, because it needs to keep the
 * message for retransmission. The transaction will release the reference
 * counter once its state has reached final state.
 */
struct pjsip_tx_data
{
    /** This is for transmission queue; it's managed by transports. */
    PJ_DECL_LIST_MEMBER(struct pjsip_tx_data);

    /** Memory pool for this buffer. */
    pj_pool_t           *pool;

    /** A name to identify this buffer. */
    char                 obj_name[PJ_MAX_OBJ_NAME];

    /** Short information describing this buffer and the message in it. 
     *  Application should use #pjsip_tx_data_get_info() instead of
     *  directly accessing this member.
     */
    char                *info;

    /** For response message, this contains the reference to timestamp when 
     *  the original request message was received. The value of this field
     *  is set when application creates response message to a request by
     *  calling #pjsip_endpt_create_response.
     */
    pj_time_val          rx_timestamp;

    /** The transport manager for this buffer. */
    pjsip_tpmgr         *mgr;

    /** Ioqueue asynchronous operation key. */
    pjsip_tx_data_op_key op_key;

    /** Lock object. */
    pj_lock_t           *lock;

    /** The message in this buffer. */
    pjsip_msg           *msg;

    /** Strict route header saved by #pjsip_process_route_set(), to be
     *  restored by #pjsip_restore_strict_route_set().
     */
    pjsip_route_hdr     *saved_strict_route;

    /** Buffer to the printed text representation of the message. When the
     *  content of this buffer is set, then the transport will send the content
     *  of this buffer instead of re-printing the message structure. If the
     *  message structure has changed, then application must invalidate this
     *  buffer by calling #pjsip_tx_data_invalidate_msg.
     */
    pjsip_buffer         buf;

    /** Reference counter. */
    pj_atomic_t         *ref_cnt;

    /** Being processed by transport? */
    int                  is_pending;

    /** Transport manager internal. */
    void                *token;

    /** Callback to be called when this tx_data has been transmitted.   */
    void               (*cb)(void*, pjsip_tx_data*, pj_ssize_t);

    /** Destination information, to be used to determine the network address
     *  of the message. For a request, this information is  initialized when
     *  the request is sent with #pjsip_endpt_send_request_stateless() and
     *  network address is resolved. For CANCEL request, this information
     *  will be copied from the original INVITE to make sure that the CANCEL
     *  request goes to the same physical network address as the INVITE
     *  request.
     */
    struct
    {
        /** Server name. 
         */
        pj_str_t                 name;

        /** Server addresses resolved. 
         */
        pjsip_server_addresses   addr;

        /** Current server address being tried. 
         */
        unsigned cur_addr;

    } dest_info;

    /** Transport information, only valid during on_tx_request() and 
     *  on_tx_response() callback.
     */
    struct
    {
        pjsip_transport     *transport;     /**< Transport being used.  */
        pj_sockaddr          dst_addr;      /**< Destination address.   */
        int                  dst_addr_len;  /**< Length of address.     */
        char                 dst_name[PJ_INET6_ADDRSTRLEN]; /**< Destination address.   */
        int                  dst_port;      /**< Destination port.      */
    } tp_info;

    /** 
     * Transport selector, to specify which transport to be used. 
     * The value here must be set with pjsip_tx_data_set_transport(),
     * to allow reference counter to be set properly.
     */
    pjsip_tpselector        tp_sel;

    /**
     * Special flag to indicate that this transmit data is a request that has
     * been updated with proper authentication response and is ready to be
     * sent for retry.
     */
    pj_bool_t               auth_retry;

    /**
     * Arbitrary data attached by PJSIP modules.
     */
    void                    *mod_data[PJSIP_MAX_MODULE];

    /**
     * If via_addr is set, it will be used as the "sent-by" field of the
     * Via header for outgoing requests as long as the request uses via_tp
     * transport. Normally application should not use or access these fields.
     */
    pjsip_host_port          via_addr;      /**< Via address.           */
    const void              *via_tp;        /**< Via transport.         */
};


/**
 * Create a new, blank transmit buffer. The reference count is initialized
 * to zero.
 *
 * @param mgr           The transport manager.
 * @param tdata         Pointer to receive transmit data.
 *
 * @return              PJ_SUCCESS, or the appropriate error code.
 *
 * @see pjsip_endpt_create_tdata
 */
PJ_DECL(pj_status_t) pjsip_tx_data_create( pjsip_tpmgr *mgr,
                                           pjsip_tx_data **tdata );

/**
 * Add reference counter to the transmit buffer. The reference counter controls
 * the life time of the buffer, ie. when the counter reaches zero, then it 
 * will be destroyed.
 *
 * @param tdata     The transmit buffer.
 */
PJ_DECL(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata );

/**
 * Decrement reference counter of the transmit buffer.
 * When the transmit buffer is no longer used, it will be destroyed and
 * caller is informed with PJSIP_EBUFDESTROYED return status.
 *
 * @param tdata     The transmit buffer data.
 * @return          This function will always succeeded eventhough the return
 *                  status is non-zero. A status PJSIP_EBUFDESTROYED will be
 *                  returned to inform that buffer is destroyed.
 */
PJ_DECL(pj_status_t) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata );

/**
 * Print the SIP message to transmit data buffer's internal buffer. This
 * may allocate memory for the buffer, if the buffer has not been allocated
 * yet, and encode the SIP message to that buffer.
 *
 * @param tdata     The transmit buffer.
 *
 * @return          PJ_SUCCESS on success of the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tx_data_encode(pjsip_tx_data *tdata);

/**
 * Check if transmit data buffer contains a valid message.
 *
 * @param tdata     The transmit buffer.
 * @return          Non-zero (PJ_TRUE) if buffer contains a valid message.
 */
PJ_DECL(pj_bool_t) pjsip_tx_data_is_valid( pjsip_tx_data *tdata );

/**
 * Invalidate the print buffer to force message to be re-printed. Call
 * when the message has changed after it has been printed to buffer. The
 * message is printed to buffer normally by transport when it is about to be 
 * sent to the wire. Subsequent sending of the message will not cause
 * the message to be re-printed, unless application invalidates the buffer
 * by calling this function.
 *
 * @param tdata     The transmit buffer.
 */
PJ_DECL(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata );

/**
 * Get short printable info about the transmit data. This will normally return
 * short information about the message.
 *
 * @param tdata     The transmit buffer.
 *
 * @return          Null terminated info string.
 */
PJ_DECL(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata );

/**
 * Set the explicit transport to be used when sending this transmit data.
 * Application should not need to call this function, but rather use
 * pjsip_tsx_set_transport() and pjsip_dlg_set_transport() instead (which
 * will call this function).
 *
 * @param tdata     The transmit buffer.
 * @param sel       Transport selector.
 *
 * @return          PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjsip_tx_data_set_transport(pjsip_tx_data *tdata,
                                                 const pjsip_tpselector *sel);

/**
 * Clone pjsip_tx_data. This will duplicate the message contents of
 * pjsip_tx_data (pjsip_tx_data.msg) and add reference count to the tdata.
 * Once application has finished using the cloned pjsip_tx_data,
 * it must release it by calling  #pjsip_tx_data_dec_ref().
 * Currently, this will only clone response message.
 *
 * @param src       The source to be cloned.
 * @param flags     Optional flags. Must be zero for now.
 * @param p_rdata   Pointer to receive the cloned tdata.
 *
 * @return          PJ_SUCCESS on success or the appropriate error.
 */
PJ_DECL(pj_status_t) pjsip_tx_data_clone(const pjsip_tx_data *src,
                                         unsigned flags,
                                         pjsip_tx_data **p_rdata);

/*****************************************************************************
 *
 * TRANSPORT
 *
 *****************************************************************************/
/**
 * Type of callback to receive transport operation status.
 */
typedef void (*pjsip_transport_callback)(pjsip_transport *tp, void *token,
                                         pj_ssize_t sent_bytes);

/**
 * This structure describes transport key to be registered to hash table.
 */
typedef struct pjsip_transport_key
{
    /**
     * Transport type.
     */
    long                    type;

    /**
     * Destination address.
     */
    pj_sockaddr             rem_addr;

} pjsip_transport_key;


/**
 * Enumeration of transport direction types.
 */
typedef enum pjsip_transport_dir
{
    PJSIP_TP_DIR_NONE,              /**< Direction not set, normally used by
                                         connectionless transports such as 
                                         UDP transport.                     */
    PJSIP_TP_DIR_OUTGOING,          /**< Outgoing connection or client mode,
                                         this is only for connection-oriented 
                                         transports.                        */
    PJSIP_TP_DIR_INCOMING,          /**< Incoming connection or server mode,
                                         this is only for connection-oriented
                                         transports.                        */
} pjsip_transport_dir;


/**
 * This structure represent the "public" interface of a SIP transport.
 * Applications normally extend this structure to include transport
 * specific members.
 */
struct pjsip_transport
{
    char                    obj_name[PJ_MAX_OBJ_NAME];  /**< Name. */

    pj_pool_t              *pool;           /**< Pool used by transport.    */
    pj_atomic_t            *ref_cnt;        /**< Reference counter.         */
    pj_lock_t              *lock;           /**< Lock object.               */
    pj_grp_lock_t          *grp_lock;       /**< Group lock for sync with
                                                 ioqueue and timer.         */
    pj_bool_t               tracing;        /**< Tracing enabled?           */
    pj_bool_t               is_shutdown;    /**< Being shutdown?            */
    pj_bool_t               is_destroying;  /**< Destroy in progress?       */

    /** Key for indexing this transport in hash table. */
    pjsip_transport_key     key;

    char                   *type_name;      /**< Type name.                 */
    unsigned                flag;           /**< #pjsip_transport_flags_e   */
    char                   *info;           /**< Transport info/description.*/

    int                     addr_len;       /**< Length of addresses.       */
    pj_sockaddr             local_addr;     /**< Bound address.             */
    pjsip_host_port         local_name;     /**< Published name (eg. STUN). */
    pjsip_host_port         remote_name;    /**< Remote address name.       */
    pjsip_transport_dir     dir;            /**< Connection direction.      */
    
    pjsip_endpoint         *endpt;          /**< Endpoint instance.         */
    pjsip_tpmgr            *tpmgr;          /**< Transport manager.         */
    pjsip_tpfactory        *factory;        /**< Factory instance. Note: it
                                                 may be invalid/shutdown.   */
    pj_timer_entry          idle_timer;     /**< Timer when ref cnt is zero.*/

    pj_timestamp            last_recv_ts;   /**< Last time receiving data.  */
    pj_size_t               last_recv_len;  /**< Last received data length. */

    void                   *data;           /**< Internal transport data.   */
    unsigned                initial_timeout;/**< Initial timeout interval
                                                 to be applied to incoming
                                                 TCP/TLS transports when no
                                                 valid data received after
                                                 a successful connection.   */

    /**
     * Function to be called by transport manager to send SIP message.
     *
     * @param transport     The transport to send the message.
     * @param packet        The buffer to send.
     * @param length        The length of the buffer to send.
     * @param op_key        Completion token, which will be supplied to
     *                      caller when pending send operation completes.
     * @param rem_addr      The remote destination address.
     * @param addr_len      Size of remote address.
     * @param callback      If supplied, the callback will be called
     *                      once a pending transmission has completed. If
     *                      the function completes immediately (i.e. return
     *                      code is not PJ_EPENDING), the callback will not
     *                      be called.
     *
     * @return              Should return PJ_SUCCESS only if data has been
     *                      succesfully queued to operating system for 
     *                      transmission. Otherwise it may return PJ_EPENDING
     *                      if the underlying transport can not send the
     *                      data immediately and will send it later, which in
     *                      this case caller doesn't have to do anything 
     *                      except wait the calback to be called, if it 
     *                      supplies one.
     *                      Other return values indicate the error code.
     */
    pj_status_t (*send_msg)(pjsip_transport *transport, 
                            pjsip_tx_data *tdata,
                            const pj_sockaddr_t *rem_addr,
                            int addr_len,
                            void *token,
                            pjsip_transport_callback callback);

    /**
     * Instruct the transport to initiate graceful shutdown procedure.
     * After all objects release their reference to this transport,
     * the transport will be deleted.
     *
     * Note that application MUST use #pjsip_transport_shutdown() instead.
     *
     * @param transport     The transport.
     *
     * @return              PJ_SUCCESS on success.
     */
    pj_status_t (*do_shutdown)(pjsip_transport *transport);

    /**
     * Forcefully destroy this transport regardless whether there are
     * objects that currently use this transport. This function should only
     * be called by transport manager or other internal objects (such as the
     * transport itself) who know what they're doing. Application should use
     * #pjsip_transport_shutdown() instead.
     *
     * @param transport     The transport.
     *
     * @return              PJ_SUCCESS on success.
     */
    pj_status_t (*destroy)(pjsip_transport *transport);

    /*
     * Application may extend this structure..
     */
};


/**
 * Register a transport instance to the transport manager. This function
 * is normally called by the transport instance when it is created
 * by application.
 *
 * @param mgr           The transport manager.
 * @param tp            The new transport to be registered.
 *
 * @return              PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr,
                                               pjsip_transport *tp );


/**
 * Start graceful shutdown procedure for this transport. After graceful
 * shutdown has been initiated, no new reference can be obtained for
 * the transport. However, existing objects that currently uses the
 * transport may still use this transport to send and receive packets.
 *
 * After all objects release their reference to this transport,
 * the transport will be destroyed immediately.
 *
 * @param tp                The transport.
 *
 * @return                  PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjsip_transport_shutdown(pjsip_transport *tp);

/**
 * Start shutdown procedure for this transport. If \a force is false,
 * the API is the same as #pjsip_transport_shutdown(), while
 * if \a force is true, existing transport users will immediately
 * receive PJSIP_TP_STATE_DISCONNECTED notification and should not
 * use the transport anymore. In either case, transport will
 * only be destroyed after all objects release their references.
 *
 * @param tp                The transport.
 * @param force             Force transport to immediately send
 *                          disconnection state notification.
 *
 * @return                  PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjsip_transport_shutdown2(pjsip_transport *tp,
                                               pj_bool_t force);

/**
 * Destroy a transport when there is no object currently uses the transport.
 * This function is normally called internally by transport manager or the
 * transport itself. Application should use #pjsip_transport_shutdown()
 * instead.
 *
 * @param tp            The transport instance.
 *
 * @return              PJ_SUCCESS on success or the appropriate error code.
 *                      Some of possible errors are PJSIP_EBUSY if the 
 *                      transport's reference counter is not zero.
 */
PJ_DECL(pj_status_t) pjsip_transport_destroy( pjsip_transport *tp);

/**
 * Add reference counter to the specified transport. Any objects that wishes
 * to keep the reference of the transport MUST increment the transport's
 * reference counter to prevent it from being destroyed.
 *
 * @param tp            The transport instance.
 *
 * @return              PJ_SUCCESS on success or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_transport_add_ref( pjsip_transport *tp );

/**
 * Decrement reference counter of the specified transport. When an object no
 * longer want to keep the reference to the transport, it must decrement the
 * reference counter. When the reference counter of the transport reaches 
 * zero, the transport manager will start the idle timer to destroy the
 * transport if no objects acquire the reference counter during the idle
 * interval.
 *
 * @param tp            The transport instance.
 *
 * @return              PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjsip_transport_dec_ref( pjsip_transport *tp );


/**
 * This function is called by transport instances to report an incoming 
 * packet to the transport manager. The transport manager then would try to
 * parse all SIP messages in the packet, and for each parsed SIP message, it
 * would report the message to the SIP endpoint (#pjsip_endpoint).
 *
 * @param mgr           The transport manager instance.
 * @param rdata         The receive data buffer containing the packet. The
 *                      transport MUST fully initialize tp_info and pkt_info
 *                      member of the rdata.
 *
 * @return              The number of bytes successfully processed from the
 *                      packet. If the transport is datagram oriented, the
 *                      value will be equal to the size of the packet. For
 *                      stream oriented transport (e.g. TCP, TLS), the value
 *                      returned may be less than the packet size, if 
 *                      partial message is received. The transport then MUST
 *                      keep the remainder part and report it again to
 *                      this function once more data/packet is received.
 */
PJ_DECL(pj_ssize_t) pjsip_tpmgr_receive_packet(pjsip_tpmgr *mgr,
                                               pjsip_rx_data *rdata);


/*****************************************************************************
 *
 * TRANSPORT FACTORY
 *
 *****************************************************************************/


/**
 * A transport factory is normally used for connection oriented transports
 * (such as TCP or TLS) to create instances of transports. It registers
 * a new transport type to the transport manager, and the transport manager
 * would ask the factory to create a transport instance when it received
 * command from application to send a SIP message using the specified
 * transport type.
 */
struct pjsip_tpfactory
{
    /** This list is managed by transport manager. */
    PJ_DECL_LIST_MEMBER(struct pjsip_tpfactory);

    char                    obj_name[PJ_MAX_OBJ_NAME];  /**< Name.      */

    pj_pool_t              *pool;           /**< Owned memory pool.     */
    pj_lock_t              *lock;           /**< Lock object.           */

    pjsip_transport_type_e  type;           /**< Transport type.        */
    char                   *type_name;      /**< Type string name.      */
    unsigned                flag;           /**< Transport flag.        */
    char                   *info;           /**< Transport info/description.*/

    pj_sockaddr             local_addr;     /**< Bound address.         */
    pjsip_host_port         addr_name;      /**< Published name.        */

    /**
     * Create new outbound connection suitable for sending SIP message
     * to specified remote address.
     * Note that the factory is responsible for both creating the
     * transport and registering it to the transport manager.
     */
    pj_status_t (*create_transport)(pjsip_tpfactory *factory,
                                    pjsip_tpmgr *mgr,
                                    pjsip_endpoint *endpt,
                                    const pj_sockaddr *rem_addr,
                                    int addr_len,
                                    pjsip_transport **transport);

    /**
     * Create new outbound connection suitable for sending SIP message
     * to specified remote address by also considering outgoing SIP 
     * message data.
     * Note that the factory is responsible for both creating the
     * transport and registering it to the transport manager.
     */
    pj_status_t (*create_transport2)(pjsip_tpfactory *factory,
                                     pjsip_tpmgr *mgr,
                                     pjsip_endpoint *endpt,
                                     const pj_sockaddr *rem_addr,
                                     int addr_len,
                                     pjsip_tx_data *tdata,
                                     pjsip_transport **transport);

    /**
     * Destroy the listener.
     */
    pj_status_t (*destroy)(pjsip_tpfactory *factory);

    /*
     * Application may extend this structure..
     */
};



/**
 * Register a transport factory.
 *
 * @param mgr           The transport manager.
 * @param tpf           Transport factory.
 *
 * @return              PJ_SUCCESS if listener was successfully created.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_register_tpfactory(pjsip_tpmgr *mgr,
                                                    pjsip_tpfactory *tpf);

/**
 * Unregister factory.
 *
 * @param mgr           The transport manager.
 * @param tpf           Transport factory.
 *
 * @return              PJ_SUCCESS is sucessfully unregistered.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_unregister_tpfactory(pjsip_tpmgr *mgr,
                                                      pjsip_tpfactory *tpf);


/*****************************************************************************
 *
 * TRANSPORT MANAGER
 *
 *****************************************************************************/

/**
 * Type of callback to be called when transport manager receives incoming
 * SIP message.
 *
 * @param ep        Endpoint.
 * @param status    Receiption status.
 * @param rd        Received packet.
 */
typedef void (*pjsip_rx_callback)(pjsip_endpoint *ep, pj_status_t status, 
                                  pjsip_rx_data *rd);

/**
 * Type of callback to be called before transport manager is about
 * to transmit SIP message.
 *
 * @param ep        Endpoint.
 * @param td        Transmit data.
 */
typedef pj_status_t (*pjsip_tx_callback)(pjsip_endpoint *ep, pjsip_tx_data*td);

/**
 * Create a transport manager. Normally application doesn't need to call
 * this function directly, since a transport manager will be created and
 * destroyed automatically by the SIP endpoint.
 *
 * @param pool      Pool.
 * @param endpt     Endpoint instance.
 * @param rx_cb     Callback to receive incoming message.
 * @param tx_cb     Callback to be called before transport manager is sending
 *                  outgoing message.
 * @param p_mgr     Pointer to receive the new transport manager.
 *
 * @return          PJ_SUCCESS or the appropriate error code on error.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_create( pj_pool_t *pool,
                                         pjsip_endpoint * endpt,
                                         pjsip_rx_callback rx_cb,
                                         pjsip_tx_callback tx_cb,
                                         pjsip_tpmgr **p_mgr);


/**
 * Find out the appropriate local address info (IP address and port) to
 * advertise in Contact header based on the remote address to be 
 * contacted. The local address info would be the address name of the
 * transport or listener which will be used to send the request.
 *
 * In this implementation, it will only select the transport based on
 * the transport type in the request.
 *
 * @see pjsip_tpmgr_find_local_addr2()
 *
 * @param tpmgr     The transport manager.
 * @param pool      Pool to allocate memory for the IP address.
 * @param type      Destination address to contact.
 * @param sel       Optional pointer to prefered transport, if any.
 * @param ip_addr   Pointer to receive the IP address.
 * @param port      Pointer to receive the port number.
 *
 * @return          PJ_SUCCESS, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr( pjsip_tpmgr *tpmgr,
                                                  pj_pool_t *pool,
                                                  pjsip_transport_type_e type,
                                                  const pjsip_tpselector *sel,
                                                  pj_str_t *ip_addr,
                                                  int *port);

/**
 * Parameter for pjsip_tpmgr_find_local_addr2() function.
 */
typedef struct pjsip_tpmgr_fla2_param
{
    /**
     * Specify transport type to use. This must be set.
     */
    pjsip_transport_type_e       tp_type;

    /**
     * Optional pointer to preferred transport, if any.
     */
    const pjsip_tpselector      *tp_sel;

    /**
     * Destination host, if known. The destination host is needed
     * if \a local_if field below is set.
     */
    pj_str_t                     dst_host;

    /**
     * Specify if the function should return which local interface
     * to use for the specified destination in \a dst_host. By definition,
     * the returned address will always be local interface address.
     */
    pj_bool_t                    local_if;

    /**
     * The returned address.
     */
    pj_str_t                     ret_addr;

    /**
     * The returned port.
     */
    pj_uint16_t                  ret_port;

    /**
     * Returned pointer to the transport. Only set if local_if is set.
     */
    const void                  *ret_tp;

} pjsip_tpmgr_fla2_param;

/**
 * Initialize with default values.
 *
 * @param prm       The parameter to be initialized.
 */
PJ_DECL(void) pjsip_tpmgr_fla2_param_default(pjsip_tpmgr_fla2_param *prm);

/**
 * Find out the appropriate local address info (IP address and port) to
 * advertise in Contact or Via header header based on the remote address
 * to be contacted. The local address info would be the address name of the
 * transport or listener which will be used to send the request.
 *
 * @see pjsip_tpmgr_find_local_addr()
 *
 * @param tpmgr     The transport manager.
 * @param pool      Pool to allocate memory for the IP address.
 * @param prm       Function input and output parameters.
 *
 * @return          PJ_SUCCESS, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_find_local_addr2(pjsip_tpmgr *tpmgr,
                                                  pj_pool_t *pool,
                                                  pjsip_tpmgr_fla2_param *prm);

/**
 * Return number of transports currently registered to the transport
 * manager.
 *
 * @param mgr       The transport manager.
 *
 * @return          Number of transports.
 */
PJ_DECL(unsigned) pjsip_tpmgr_get_transport_count(pjsip_tpmgr *mgr);


/**
 * Destroy a transport manager. Normally application doesn't need to call
 * this function directly, since a transport manager will be created and
 * destroyed automatically by the SIP endpoint.
 *
 * @param mgr       The transport manager.
 *
 * @return          PJ_SUCCESS on success.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_destroy(pjsip_tpmgr *mgr);


/**
 * Dump transport info and status to log.
 *
 * @param mgr       The transport manager.
 */
PJ_DECL(void) pjsip_tpmgr_dump_transports(pjsip_tpmgr *mgr);


/*****************************************************************************
 *
 * PUBLIC API
 *
 *****************************************************************************/


/**
 * Find transport to be used to send message to remote destination. If no
 * suitable transport is found, a new one will be created.
 *
 * This is an internal function since normally application doesn't have access
 * to transport manager. Application should use pjsip_endpt_acquire_transport()
 * instead.
 *
 * @param mgr       The transport manager instance.
 * @param type      The type of transport to be acquired.
 * @param remote    The remote address to send message to.
 * @param addr_len  Length of the remote address.
 * @param sel       Optional pointer to transport selector instance which is
 *                  used to find explicit transport, if required.
 * @param tp        Pointer to receive the transport instance, if one is found.
 *
 * @return          PJ_SUCCESS on success, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr,
                                                   pjsip_transport_type_e type,
                                                   const pj_sockaddr_t *remote,
                                                   int addr_len,
                                                   const pjsip_tpselector *sel,
                                                   pjsip_transport **tp);

/**
 * Find suitable transport for sending SIP message to specified remote 
 * destination by also considering the outgoing SIP message. If no suitable 
 * transport is found, a new one will be created.
 *
 * This is an internal function since normally application doesn't have access
 * to transport manager. Application should use pjsip_endpt_acquire_transport2()
 * instead.
 *
 * @param mgr       The transport manager instance.
 * @param type      The type of transport to be acquired.
 * @param remote    The remote address to send message to.
 * @param addr_len  Length of the remote address.
 * @param sel       Optional pointer to transport selector instance which is
 *                  used to find explicit transport, if required.
 * @param tdata     Optional pointer to data to be sent.
 * @param tp        Pointer to receive the transport instance, if one is found.
 *
 * @return          PJ_SUCCESS on success, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
                                                    pjsip_transport_type_e type,
                                                    const pj_sockaddr_t *remote,
                                                    int addr_len,
                                                    const pjsip_tpselector *sel,
                                                    pjsip_tx_data *tdata,
                                                    pjsip_transport **tp);

/**
 * Type of callback to receive notification when message or raw data
 * has been sent.
 *
 * @param token         The token that was given when calling the function
 *                      to send message or raw data.
 * @param tdata         The transmit buffer used to send the message.
 * @param bytes_sent    Number of bytes sent. On success, the value will be
 *                      positive number indicating the number of bytes sent.
 *                      On failure, the value will be a negative number of
 *                      the error code (i.e. bytes_sent = -status).
 */
typedef void (*pjsip_tp_send_callback)(void *token, pjsip_tx_data *tdata,
                                       pj_ssize_t bytes_sent);


/**
 * This is a low-level function to send a SIP message using the specified
 * transport to the specified destination.
 * 
 * @param tr        The SIP transport to be used.
 * @param tdata     Transmit data buffer containing SIP message.
 * @param addr      Destination address.
 * @param addr_len  Length of destination address.
 * @param token     Arbitrary token to be returned back to callback.
 * @param cb        Optional callback to be called to notify caller about
 *                  the completion status of the pending send operation.
 *
 * @return          If the message has been sent successfully, this function
 *                  will return PJ_SUCCESS and the callback will not be 
 *                  called. If message cannot be sent immediately, this
 *                  function will return PJ_EPENDING, and application will
 *                  be notified later about the completion via the callback.
 *                  Any statuses other than PJ_SUCCESS or PJ_EPENDING
 *                  indicates immediate failure, and in this case the 
 *                  callback will not be called.
 */
PJ_DECL(pj_status_t) pjsip_transport_send( pjsip_transport *tr, 
                                           pjsip_tx_data *tdata,
                                           const pj_sockaddr_t *addr,
                                           int addr_len,
                                           void *token,
                                           pjsip_tp_send_callback cb);


/**
 * This is a low-level function to send raw data to a destination.
 *
 * See also #pjsip_endpt_send_raw() and #pjsip_endpt_send_raw_to_uri().
 *
 * @param mgr       Transport manager.
 * @param tp_type   Transport type.
 * @param sel       Optional pointer to transport selector instance if
 *                  application wants to use a specific transport instance
 *                  rather then letting transport manager finds the suitable
 *                  transport.
 * @param tdata     Optional transmit data buffer to be used. If this value
 *                  is NULL, this function will create one internally. If
 *                  tdata is specified, this function will decrement the
 *                  reference counter upon completion.
 * @param raw_data  The data to be sent.
 * @param data_len  The length of the data.
 * @param addr      Destination address.
 * @param addr_len  Length of destination address.
 * @param token     Arbitrary token to be returned back to callback.
 * @param cb        Optional callback to be called to notify caller about
 *                  the completion status of the pending send operation.
 *
 * @return          If the message has been sent successfully, this function
 *                  will return PJ_SUCCESS and the callback will not be 
 *                  called. If message cannot be sent immediately, this
 *                  function will return PJ_EPENDING, and application will
 *                  be notified later about the completion via the callback.
 *                  Any statuses other than PJ_SUCCESS or PJ_EPENDING
 *                  indicates immediate failure, and in this case the 
 *                  callback will not be called.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_send_raw(pjsip_tpmgr *mgr,
                                          pjsip_transport_type_e tp_type,
                                          const pjsip_tpselector *sel,
                                          pjsip_tx_data *tdata,
                                          const void *raw_data,
                                          pj_size_t data_len,
                                          const pj_sockaddr_t *addr,
                                          int addr_len,
                                          void *token,
                                          pjsip_tp_send_callback cb);


/**
 * Enumeration of transport state types.
 */
typedef enum pjsip_transport_state
{
    PJSIP_TP_STATE_CONNECTED,       /**< Transport connected, applicable only
                                         to connection-oriented transports
                                         such as TCP and TLS.               */
    PJSIP_TP_STATE_DISCONNECTED,    /**< Transport disconnected, applicable
                                         only to connection-oriented 
                                         transports such as TCP and TLS.    */
    PJSIP_TP_STATE_SHUTDOWN,        /**< Transport shutdown, either
                                         due to TCP/TLS disconnect error
                                         from the network, or when shutdown
                                         is initiated by PJSIP itself.      */
    PJSIP_TP_STATE_DESTROY,         /**< Transport destroy, when transport
                                         is about to be destroyed.          */
} pjsip_transport_state;


/**
 * Definition of transport state listener key.
 */
typedef void pjsip_tp_state_listener_key;

/**
 * Structure of transport state info passed by #pjsip_tp_state_callback.
 */
typedef struct pjsip_transport_state_info {
    /**
     * The last error code related to the transport state.
     */
    pj_status_t          status;

    /**
     * Optional extended info, the content is specific for each transport type.
     */
    void                *ext_info;

    /**
     * Optional user data. In global transport state notification, this will
     * always be NULL.
     */
    void                *user_data;

} pjsip_transport_state_info;


/**
 * Type of callback to receive transport state notifications, such as
 * transport connected/disconnected. Application may shutdown the transport
 * in this callback.
 *
 * @param tp            The transport instance.
 * @param state         The transport state.
 * @param info          The transport state info.
 */
typedef void (*pjsip_tp_state_callback)(
                                    pjsip_transport *tp,
                                    pjsip_transport_state state,
                                    const pjsip_transport_state_info *info);


/**
 * Set callback of global transport state notification. The caller will be
 * notified whenever the state of any transport is changed. The type of events
 * are defined in #pjsip_transport_state.
 *
 * Note that this function will override the existing callback, if any, so
 * application is recommended to keep the old callback and manually forward
 * the notification to the old callback, otherwise other component that 
 * concerns about the transport state will no longer receive transport state 
 * events.
 * 
 * @param mgr       Transport manager.
 * @param cb        Callback to be called to notify caller about transport 
 *                  state changing.
 *
 * @return          PJ_SUCCESS on success, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_set_state_cb(pjsip_tpmgr *mgr,
                                              pjsip_tp_state_callback cb);


/**
 * Get the callback of global transport state notification.
 * 
 * @param mgr       Transport manager.
 *
 * @return          The transport state callback or NULL if it is not set.
 */
PJ_DECL(pjsip_tp_state_callback) pjsip_tpmgr_get_state_cb(
                                              const pjsip_tpmgr *mgr);


/**
 * Add a listener to the specified transport for transport state notification.
 * 
 * @param tp        The transport.
 * @param cb        Callback to be called to notify listener about transport 
 *                  state changing.
 * @param user_data The user data.
 * @param key       Output key, used to remove this listener.
 *
 * @return          PJ_SUCCESS on success, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_transport_add_state_listener (
                                            pjsip_transport *tp,
                                            pjsip_tp_state_callback cb,
                                            void *user_data,
                                            pjsip_tp_state_listener_key **key);


/**
 * Remove a listener from the specified transport for transport state 
 * notification.
 * 
 * @param tp        The transport.
 * @param key       The listener key.
 * @param user_data The user data, for validation purpose.
 *
 * @return          PJ_SUCCESS on success, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_transport_remove_state_listener (
                                    pjsip_transport *tp,
                                    pjsip_tp_state_listener_key *key,
                                    const void *user_data);


/**
 * Structure of dropped received data.
 */
typedef struct pjsip_tp_dropped_data
{
    /**
     * The transport receiving the data.
     */
    pjsip_transport *tp;

    /**
     * The data.
     */
    void            *data;

    /**
     * The data length.
     * If the status field below indicates an invalid SIP message
     * (PJSIP_EINVALIDMSG) and application detects a SIP message
     * at position p, it can pass the data back to PJSIP to be processed
     * by setting the len to p. This can be useful for apps which
     * wishes to use the same transport for SIP signalling and non-SIP
     * purposes (such as SIP outbound using STUN message).
     */
    pj_size_t        len;

    /**
     * The status or reason of drop. For example, a leading newlines (common
     * keep-alive packet) will be dropped with status PJ_EIGNORED, an invalid
     * SIP message will have status PJSIP_EINVALIDMSG, a SIP message overflow
     * will have status PJSIP_ERXOVERFLOW.
     */
    pj_status_t      status;

} pjsip_tp_dropped_data;


/**
 * Type of callback to data dropping notifications.
 *
 * @param data          The dropped data.
 */
typedef void (*pjsip_tp_on_rx_dropped_cb)(pjsip_tp_dropped_data *data);


/**
 * Set callback of data dropping. The caller will be notified whenever any
 * received data is dropped (due to leading newlines or keep-alive packet or
 * invalid SIP message). This callback can be useful for application,
 * for example, to implement custom keep-alive mechanism or connection
 * availability detection.
 *
 * @param mgr       Transport manager.
 * @param cb        The callback function, set to NULL to reset the callback.
 *
 * @return          PJ_SUCCESS on success, or the appropriate error code.
 */
PJ_DECL(pj_status_t) pjsip_tpmgr_set_drop_data_cb(pjsip_tpmgr *mgr,
                                                  pjsip_tp_on_rx_dropped_cb cb);


/**
 * @}
 */


PJ_END_DECL

#endif  /* __PJSIP_SIP_TRANSPORT_H__ */

