/**
 * yatesip.h
 * Yet Another SIP 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 __YATESIP_H
#define __YATESIP_H

#include <yateclass.h>
#include <yatemime.h>

#ifdef _WINDOWS

#ifdef LIBYSIP_EXPORTS
#define YSIP_API __declspec(dllexport)
#else
#ifndef LIBYSIP_STATIC
#define YSIP_API __declspec(dllimport)
#endif
#endif

#endif /* _WINDOWS */

#ifndef YSIP_API
#define YSIP_API
#endif

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

/**
 * Token table containing default human readable responses for answer codes
 */
extern YSIP_API TokenDict* SIPResponses;

class SIPEngine;
class SIPEvent;

class YSIP_API SIPParty : public RefObject
{
public:
    SIPParty(Mutex* mutex = 0);
    SIPParty(bool reliable, Mutex* mutex = 0);
    virtual ~SIPParty();
    /**
     * Transmit an event
     * @param event Evend to send
     * @return False on fatal failure (subsequent send would fail again)
     */
    virtual bool transmit(SIPEvent* event) = 0;
    virtual const char* getProtoName() const = 0;
    virtual bool setParty(const URI& uri) = 0;
    virtual void* getTransport() = 0;
    void setAddr(const String& addr, int port, bool local);
    void getAddr(String& addr, int& port, bool local);
    inline void appendAddr(String& buf, bool local, bool unsafe = true) {
	    Lock lock(unsafe ? m_mutex : 0);
	    if (local)
		SocketAddr::appendTo(buf,m_local,m_localPort);
	    else
		SocketAddr::appendTo(buf,m_party,m_partyPort);
	}
    inline Mutex* mutex()
	{ return m_mutex; }
    inline const String& getLocalAddr() const
	{ return m_local; }
    inline const String& getPartyAddr() const
	{ return m_party; }
    inline int getLocalPort() const
	{ return m_localPort; }
    inline int getPartyPort() const
	{ return m_partyPort; }
    inline bool isReliable() const
	{ return m_reliable; }
protected:
    Mutex* m_mutex;
    bool m_reliable;
    String m_local;
    String m_party;
    int m_localPort;
    int m_partyPort;
};

/**
 * Command Sequence Number generator
 * @short SIP CSeq generator
 */
class YSIP_API SIPSequence : public RefObject, public Mutex
{
public:
    /**
     * Constructor
     * @param cseq Sequence number to start
     */
    inline SIPSequence(int32_t cseq = -1)
	: Mutex(false,"CSeq"), m_cseq(cseq)
	{ }

    /**
     * Get the last number generated by this sequence
     * @return Last generated CSeq value between 0 and 2^31-1, negative if not initialized
     */
    inline int32_t getLastCSeq() const
	{ return m_cseq; }

    /**
     * Create a CSeq value suitable for use in a new request
     * @return New CSeq value between 1 and 2^31-1
     */
    inline int32_t getNextCSeq()
	{ Lock mylock(this); return (++m_cseq > 0) ? m_cseq : (m_cseq = 1); }

    /**
     * Set the Command Sequence number
     * @param cseq New sequence number to start from
     */
    inline void setCSeq(int32_t cseq)
	{ m_cseq = cseq; }

private:
    int32_t m_cseq;
};

/**
 * An object that holds the sip message parsed into this library model.
 * This class can be used to parse a sip message from a text buffer, or it
 * can be used to create a text buffer from a sip message.
 * @short A container and parser for SIP messages
 */
class YSIP_API SIPMessage : public RefObject
{
public:
    /**
     * Various message flags
     */
    enum Flags {
	Defaults          =      0,
	NotReqRport       = 0x0001,
	NotAddAllow       = 0x0002,
	NotAddAgent       = 0x0004,
	RportAfterBranch  = 0x0008,
	NotSetRport       = 0x0010,
	NotSetReceived    = 0x0020,
	NoConnReuse       = 0x0040,      // Don't add 'alias' parameter to Via header (reliable only)
    };

    /**
     * Copy constructor
     */
    SIPMessage(const SIPMessage& original);

    /**
     * Creates a new, empty, outgoing SIPMessage.
     */
    SIPMessage(const char* _method, const char* _uri, const char* _version = "SIP/2.0");

    /**
     * Creates a new SIPMessage from parsing a text buffer.
     * @param ep Party to set in message
     * @param buf Buffer to parse
     * @param len Optional buffer length
     * @param bodyLen Pointer to body length to be set if the message was received
     *  on a stream transport. If not 0 the buffer must contain the message
     *  without its body
     */
    SIPMessage(SIPParty* ep, const char* buf, int len = -1, unsigned int* bodyLen = 0);

    /**
     * Creates a new SIPMessage as answer to another message.
     */
    SIPMessage(const SIPMessage* message, int _code, const char* _reason = 0);

    /**
     * Creates an ACK message from an original message and a response.
     */
    SIPMessage(const SIPMessage* original, const SIPMessage* answer);

    /**
     * Destroy the message and all
     */
    virtual ~SIPMessage();

    /**
     * Construct a new SIP message by parsing a text buffer
     * @param ep Party to set in message
     * @param buf Buffer to parse
     * @param len Optional buffer length
     * @param bodyLen Pointer to body length to be set if the message was received
     *  on a stream transport. If not 0 the buffer must contain the message
     *  without its body
     * @return A pointer to a valid new message or NULL
     */
    static SIPMessage* fromParsing(SIPParty* ep, const char* buf, int len = -1,
	unsigned int* bodyLen = 0);

    /**
     * Build message's body. Reset it before.
     * This method should be called after parsing a partial message (headers only)
     * @param buf Buffer to parse
     * @param len Optional buffer length
     */
    void buildBody(const char* buf, int len = -1);

    /**
     * Complete missing fields with defaults taken from a SIP engine
     * @param engine Pointer to the SIP engine to use for extra parameters
     * @param user Username to set in the From header instead of that in rURI
     * @param domain Domain to use in From instead of the local IP address
     * @param dlgTag Value of dialog tag parameter to set in To header
     * @param flags Miscellaneous completion flags, -1 to take them from engine
     */
    void complete(SIPEngine* engine, const char* user = 0, const char* domain = 0, const char* dlgTag = 0, int flags = -1);

    /**
     * Copy an entire header line (including all parameters) from another message
     * @param message Pointer to the message to copy the header from
     * @param name Name of the header to copy
     * @param newName New name to force in headers, NULL to just copy
     * @return True if the header was found and copied
     */
    bool copyHeader(const SIPMessage* message, const char* name, const char* newName = 0);

    /**
     * Copy multiple header lines (including all parameters) from another message
     * @param message Pointer to the message to copy the header from
     * @param name Name of the headers to copy
     * @param newName New name to force in headers, NULL to just copy
     * @return Number of headers found and copied
     */
    int copyAllHeaders(const SIPMessage* message, const char* name, const char* newName = 0);

    /**
     * Get the endpoint this message uses
     * @return Pointer to the endpoint of this message
     */
    inline SIPParty* getParty() const
	{ return m_ep; }

    /**
     * Set the endpoint this message uses
     * @param ep Pointer to the endpoint of this message
     */
    void setParty(SIPParty* ep = 0);

    /**
     * Check if this message is valid as result of the parsing
     */
    inline bool isValid() const
	{ return m_valid; }

    /**
     * Check if this message is an answer or a request
     */
    inline bool isAnswer() const
	{ return m_answer; }

    /**
     * Check if this message is an outgoing message
     * @return True if this message should be sent to remote
     */
    inline bool isOutgoing() const
	{ return m_outgoing; }

    /**
     * Check if this message is an ACK message
     * @return True if this message has an ACK method
     */
    inline bool isACK() const
	{ return m_ack; }

    /**
     * Check if this message is handled by a reliable protocol
     * @return True if a reliable protocol (TCP, SCTP) is used
     */
    inline bool isReliable() const
	{ return m_ep ? m_ep->isReliable() : false; }

    /**
     * Get the Command Sequence number from this message
     * @return Number part of CSEQ in this message
     */
    inline int32_t getCSeq() const
	{ return m_cseq; }

    /**
     * Set the Command Sequence number for this message
     * @param cseq Sequence number for this message
     */
    inline void setCSeq(int32_t cseq)
	{ m_cseq = cseq; }

    /**
     * Get the last flags used by this message
     * @return Flags last used, ORed together
     */
    inline int getFlags() const
	{ return m_flags; }

    /**
     * Find a header line by name
     * @param name Name of the header to locate
     * @return A pointer to the first matching header line or 0 if not found
     */
    const MimeHeaderLine* getHeader(const char* name) const;

    /**
     * Find the last header line that matches a given name name
     * @param name Name of the header to locate
     * @return A pointer to the last matching header line or 0 if not found
     */
    const MimeHeaderLine* getLastHeader(const char* name) const;

    /**
     * Count the header lines matching a specific name
     * @param name Name of the header to locate
     * @return Number of matching header lines
     */
    int countHeaders(const char* name) const;

    /**
     * Find a header parameter by name
     * @param name Name of the header to locate
     * @param param Name of the parameter to locate in the tag
     * @param last Find the last header with that name instead of first
     * @return A pointer to the first matching header line or 0 if not found
     */
    const NamedString* getParam(const char* name, const char* param, bool last = false) const;

    /**
     * Get a string value (without parameters) from a header line
     * @param name Name of the header to locate
     * @param last Find the last header with that name instead of first
     * @return The value hold in the header or an empty String
     */
    const String& getHeaderValue(const char* name, bool last = false) const;

    /**
     * Get a string value from a parameter in a header line
     * @param name Name of the header to locate
     * @param param Name of the parameter to locate in the tag
     * @param last Find the last header with that name instead of first
     * @return The value hold in the parameter or an empty String
     */
    const String& getParamValue(const char* name, const char* param, bool last = false) const;

    /**
     * Append a new header line constructed from name and content
     * @param name Name of the header to add
     * @param value Content of the new header line
     */
    inline void addHeader(const char* name, const char* value = 0)
	{ header.append(new MimeHeaderLine(name,value)); }

    /**
     * Append an already constructed header line
     * @param line Header line to add
     */
    inline void addHeader(MimeHeaderLine* line)
	{ header.append(line); }

    /**
     * Clear all header lines that match a name
     * @param name Name of the header to clear
     */
    void clearHeaders(const char* name);

    /**
     * Set a header line constructed from name and content
     */
    inline void setHeader(const char* name, const char* value = 0)
	{ clearHeaders(name); addHeader(name,value); }

    /**
     * Construct a new authorization line based on credentials and challenge
     * @param username User account name
     * @param password Clear text password for the account
     * @param meth Method to include in the authorization digest
     * @param uri URI to include in the authorization digest
     * @param proxy Set to true to authenticate to a proxy, false to a server
     * @param engine Optional engine processing this message
     * @return A new authorization line to be used in a new transaction
     */
    MimeAuthLine* buildAuth(const String& username, const String& password,
	const String& meth, const String& uri, bool proxy = false, SIPEngine* engine = 0) const;

    /**
     * Construct a new authorization line based on this answer and original message
     * @param original Origianl outgoing message
     * @param engine Optional engine processing this message
     * @return A new authorization line to be used in a new transaction
     */
    MimeAuthLine* buildAuth(const SIPMessage& original, SIPEngine* engine = 0) const;

    /**
     * Prepare the message for automatic client transaction authentication.
     * @param username Username for auto authentication
     * @param password Password for auto authentication
     */
    inline void setAutoAuth(const char* username = 0, const char* password = 0)
	{ m_authUser = username; m_authPass = password; }

    /**
     * Retrieve the username to be used for auto authentication
     * @return Username for auto authentication
     */
    inline const String& getAuthUsername() const
	{ return m_authUser; }

    /**
     * Retrieve the password to be used for auto authentication
     * @return Password for auto authentication
     */
    inline const String& getAuthPassword() const
	{ return m_authPass; }

    /**
     * Extract routes from Record-Route: headers
     * @return A list of MimeHeaderLine representing SIP routes
     */
    ObjList* getRoutes() const;

    /**
     * Add Route: headers to an outgoing message
     * @param routes List of MimeHeaderLine representing SIP routes
     */
    void addRoutes(const ObjList* routes);

    /**
     * Get the Command Sequence Number generator
     * @return Pointer to the CSeq generator of this message
     */
    inline SIPSequence* getSequence() const
	{ return m_seq; }

    /**
     * Set the Command Sequence Number generator
     * @param seq Pointer to the new CSeq generator of this message
     */
    inline void setSequence(SIPSequence* seq)
	{ m_seq = seq; }

    /**
     * Creates a binary buffer from a SIPMessage.
     */
    const DataBlock& getBuffer() const;

    /**
     * Creates a text buffer from the headers.
     */
    const String& getHeaders() const;

    /**
     * Set a new body for this message
     */
    void setBody(MimeBody* newbody = 0);

    /**
     * Sip Version
     */
    String version;

    /**
     * This holds the method name of the message.
     */
    String method;

    /**
     * URI of the request
     */
    String uri;

    /**
     * Status code
     */
    int code;

    /**
     * Reason Phrase
     */
    String reason;

    /**
     * All the headers should be in this list.
     */
    ObjList header;

    /**
     * All the body related things should be here, including the entire body and
     * the parsed body.
     */
    MimeBody* body;

protected:
    bool parse(const char* buf, int len, unsigned int* bodyLen);
    bool parseFirst(String& line);
    SIPParty* m_ep;
    RefPointer<SIPSequence> m_seq;
    bool m_valid;
    bool m_answer;
    bool m_outgoing;
    bool m_ack;
    int32_t m_cseq;
    int m_flags;
    mutable String m_string;
    mutable DataBlock m_data;
    String m_authUser;
    String m_authPass;
private:
    SIPMessage(); // no, thanks
};

/**
 * A class to store information required to identify a dialog
 * @short SIP Dialog object
 */
class YSIP_API SIPDialog : public String
{
public:
    /**
     * Default constructor, build an empty SIP dialog
     */
    inline SIPDialog()
	: remoteCSeq(-1)
	{ }

    /**
     * Copy constructor
     * @param original Original SIP dialog to copy
     */
    SIPDialog(const SIPDialog& original);

    /**
     * Constructor from a SIP message
     * @param message SIP message to copy the dialog information from
     */
    explicit SIPDialog(const SIPMessage& message);

    /**
     * Constructor from a Call ID, leaves URIs and tags empty
     * @param callid Call ID to insert in the dialog
     */
    inline explicit SIPDialog(const String& callid)
	: String(callid), remoteCSeq(-1)
	{ }

    /**
     * Assignment from another dialog
     * @param original Original SIP dialog to copy
     * @return Reference to this SIP dialog
     */
    SIPDialog& operator=(const SIPDialog& original);

    /**
     * Assignment from a SIP message
     * @param message SIP message to copy the dialog information from
     * @return Reference to this SIP dialog
     */
    SIPDialog& operator=(const SIPMessage& message);

    /**
     * Assignment from a Call ID, URIs and tags are cleared
     * @param callid Call ID to copy to the dialog
     * @return Reference to this SIP dialog
     */
    SIPDialog& operator=(const String& callid);

    /**
     * SIP dialog matching check
     * @param other Other dialog to compare to
     * @param ignoreURIs True to ignore local and remote URIs when comparing
     * @return True if the two dialogs match
     */
    bool matches(const SIPDialog& other, bool ignoreURIs) const;

    /**
     * Dialog equality comparation, suitable for RFC 2543
     * @param other Other dialog to compare to
     * @return True if the two dialogs are equal
     */
    inline bool operator==(const SIPDialog& other) const
	{ return matches(other,false); }

    /**
     * Dialog inequality comparation, suitable for RFC 2543
     * @param other Other dialog to compare to
     * @return True if the two dialogs are different
     */
    inline bool operator!=(const SIPDialog& other) const
	{ return !matches(other,false); }

    /**
     * Dialog equality comparation, suitable for RFC 3261
     * @param other Other dialog to compare to
     * @return True if the two dialogs match (ignoring local and remote URIs)
     */
    inline bool operator&=(const SIPDialog& other) const
	{ return matches(other,true); }

    /**
     * Dialog inequality comparation, suitable for RFC 3261
     * @param other Other dialog to compare to
     * @return True if the two dialogs do not match (ignoring local and remote URIs)
     */
    inline bool operator|=(const SIPDialog& other) const
	{ return !matches(other,true); }

    /**
     * Get the From URI from the dialog
     * @param outgoing True if getting the URI for an outgoing transaction
     * @return Reference to the From URI in dialog
     */
    inline const String& fromURI(bool outgoing) const
	{ return outgoing ? localURI : remoteURI; }

    /**
     * Get the From tag from the dialog
     * @param outgoing True if getting the tag for an outgoing transaction
     * @return Reference to the From URI tag in dialog
     */
    inline const String& fromTag(bool outgoing) const
	{ return outgoing ? localTag : remoteTag; }

    /**
     * Get the To URI from the dialog
     * @param outgoing True if getting the URI for an outgoing transaction
     * @return Reference to the To URI in dialog
     */
    inline const String& toURI(bool outgoing) const
	{ return outgoing ? remoteURI : localURI; }

    /**
     * Get the To tag from the dialog
     * @param outgoing True if getting the tag for an outgoing transaction
     * @return Reference to the To URI tag in dialog
     */
    inline const String& toTag(bool outgoing) const
	{ return outgoing ? remoteTag : localTag; }

    /**
     * Get the Command Sequence Number generator
     * @return Pointer to the CSeq generator of this dialog
     */
    inline SIPSequence* getSequence() const
	{ return m_seq; }

    /**
     * Set the Command Sequence Number generator
     * @param seq Pointer to the new CSeq generator of this dialog
     */
    inline void setSequence(SIPSequence* seq)
	{ m_seq = seq; }

    /**
     * Set a new Command Sequence generator
     * @param cseq New sequence number to start from
     */
    void setCSeq(int32_t cseq);

    /**
     * Get the last number generated by this dialog
     * @return Last generated CSeq value between 0 and 2^31-1, negative if no generator
     */
    inline int32_t getLastCSeq() const
	{ return m_seq ? m_seq->getLastCSeq() : -1; }

    /**
     * Create a CSeq value suitable for use in a new request
     * @return New CSeq value between 1 and 2^31-1, negative if no generator
     */
    inline int32_t getNextCSeq()
	{ return m_seq ? m_seq->getNextCSeq() : -1; }

    /**
     * Adjust the last seen remote CSeq
     * @param cseq Remote sequence number
     * @return True if remote sequence was adjusted
     */
    inline bool adjustCSeq(int cseq)
	{ return (cseq > remoteCSeq) && ((remoteCSeq = cseq) >= 0); }

    /**
     * Adjust the last seen remote CSeq from a SIP message
     * @param message Pointer to received SIP message
     * @return True if remote sequence was adjusted
     */
    inline bool adjustCSeq(const SIPMessage* message)
	{ return message && adjustCSeq(message->getCSeq()); }

    /**
     * Local URI of the dialog
     */
    String localURI;

    /**
     * Tag parameter of the local URI
     */
    String localTag;

    /**
     * Remote URI of the dialog
     */
    String remoteURI;

    /**
     * Tag parameter of the remote URI
     */
    String remoteTag;

    /**
     * Last seen Command Sequence Number
     */
    int remoteCSeq;

protected:
    RefPointer<SIPSequence> m_seq;
};

/**
 * All informaton related to a SIP transaction, starting with 1st message
 * @short A class holding one SIP transaction
 */
class YSIP_API SIPTransaction : public RefObject
{
public:
    /**
     * Current state of the transaction
     */
    enum State {
	// Invalid state - before constructor or after destructor
	Invalid,

	// Initial state - after the initial message was inserted
	Initial,

	// Trying state - got the message but no decision made yet
	Trying,

	// Process state - while locally processing the event
	Process,

	// Retrans state - retransmits latest message until getting ACK
	Retrans,

	// Finish state - transmits the last message on client retransmission
	Finish,

	// Cleared state - removed from engine, awaiting destruction
	Cleared
    };

    /**
     * Possible return values from @ref processMessage()
     */
    enum Processed {
	// Not matched at all
	NoMatch,

	// Belongs to another dialog - probably result of a fork
	NoDialog,

	// Matched to transaction/dialog and processed
	Matched
    };

    /**
     * Constructor from first message
     * @param message A pointer to the initial message, should not be used
     *  afterwards as the transaction takes ownership
     * @param engine A pointer to the SIP engine this transaction belongs
     * @param outgoing True if this transaction is for an outgoing request
     */
    SIPTransaction(SIPMessage* message, SIPEngine* engine, bool outgoing = true);

    /**
     * Copy constructor to be used with forked INVITEs
     * @param original Original transaction that is to be copied
     * @param tag Dialog tag for the new transaction
     */
    SIPTransaction(const SIPTransaction& original, const String& tag);

    /**
     * Destructor - clears all held objects
     */
    virtual ~SIPTransaction();

    /**
     * Get the name of a transaction state
     */
    static const char* stateName(int state);

    /**
     * The current state of the transaction
     * @return The current state as one of the State enums
     */
    inline int getState() const
	{ return m_state; }

    /**
     * Check if the transaction is active for the upper layer
     * @return True if the transaction is active, false if it finished
     */
    inline bool isActive() const
	{ return (Invalid < m_state) && (m_state < Finish); }

    /**
     * The first message that created this transaction
     */
    inline const SIPMessage* initialMessage() const
	{ return m_firstMessage; }

    /**
     * The last message (re)sent by this transaction
     */
    inline const SIPMessage* latestMessage() const
	{ return m_lastMessage; }

    /**
     * The most recent message handled by this transaction
     */
    inline const SIPMessage* recentMessage() const
	{ return m_lastMessage ? m_lastMessage : m_firstMessage; }

    /**
     * The SIPEngine this transaction belongs to
     */
    inline SIPEngine* getEngine() const
	{ return m_engine; }

    /**
     * Check if this transaction was initiated by the remote peer or locally
     * @return True if the transaction was created by an outgoing message
     */
    inline bool isOutgoing() const
	{ return m_outgoing; }

    /**
     * Check if this transaction was initiated locally or by the remote peer
     * @return True if the transaction was created by an incoming message
     */
    inline bool isIncoming() const
	{ return !m_outgoing; }

    /**
     * Check if this transaction is an INVITE transaction or not
     * @return True if the transaction is an INVITE
     */
    inline bool isInvite() const
	{ return m_invite; }

    /**
     * Check if this transaction is handled by a reliable protocol
     * @return True if a reliable protocol (TCP, SCTP) is used
     */
    inline bool isReliable() const
	{ return m_firstMessage ? m_firstMessage->isReliable() : false; }

    /**
     * The SIP method this transaction handles
     */
    inline const String& getMethod() const
	{ return m_firstMessage ? m_firstMessage->method : String::empty(); }

    /**
     * The SIP URI this transaction handles
     */
    inline const String& getURI() const
	{ return m_firstMessage ? m_firstMessage->uri : String::empty(); }

    /**
     * The Via branch that may uniquely identify this transaction
     * @return The branch parameter taken from the Via header
     */
    inline const String& getBranch() const
	{ return m_branch; }

    /**
     * The call ID may identify this transaction
     * @return The Call-ID parameter taken from the message
     */
    inline const String& getCallID() const
	{ return m_callid; }

    /**
     * The dialog tag that may identify this transaction
     * @return The dialog tag parameter
     */
    inline const String& getDialogTag() const
	{ return m_tag; }

    /**
     * Set a new dialog tag, optionally build a random one
     * @param tag New dialog tag, a null string will build a random tag
     */
    void setDialogTag(const char* tag = 0);

    /**
     * Set the (re)transmission flag that allows the latest outgoing message
     *  to be send over the wire
     */
    inline void setTransmit()
	{ m_transmit = true; }

    /**
     * Change transaction status to Cleared
     * This method is not thread safe
     */
    inline void setCleared()
	{ changeState(Cleared); }

    /**
     * Get the transmission attempts count for non-reliable transports
     * @return Current number of transmission attempts
     */
    inline unsigned int getTransCount() const
	{ return m_transCount; }

    /**
     * Set the number of transmission attempts for non-reliable transports
     * @param count Transmission attempts, clamped between 2 and 10, negative values ignored
     */
    void setTransCount(int count);

    /**
     * Send back an authentication required response
     * @param realm Authentication realm to send in the answer
     * @param domain Domain for which it will authenticate
     * @param stale True if the previous password is valid but nonce is too old
     * @param proxy True to authenticate as proxy, false as user agent
     */
    void requestAuth(const String& realm, const String& domain, bool stale, bool proxy = false);

    /**
     * Detect the proper credentials for any user in the engine
     * @param user String to store the authenticated user name or user to
     *  look for (if not null on entry)
     * @param proxy True to authenticate as proxy, false as user agent
     * @param userData Pointer to an optional object that is passed back to @ref SIPEngine::checkUser
     * @return Age of the nonce if user matches, negative for a failure
     */
    int authUser(String& user, bool proxy = false, GenObject* userData = 0);

    /**
     * Check if a message belongs to this transaction and process it if so
     * @param message A pointer to the message to check, should not be used
     *  afterwards if this method returned Matched
     * @param branch The branch parameter extracted from first Via header
     * @return Matched if the message was handled by this transaction, in
     *  which case it takes ownership over the message
     */
    virtual Processed processMessage(SIPMessage* message, const String& branch);

    /**
     * Process a message belonging to this transaction
     * @param message A pointer to the message to process, the caller must
     *  make sure it belongs to this transaction
     */
    virtual void processMessage(SIPMessage* message);

    /**
     * Get an event for this transaction if any is available.
     * It provides default handling for invalid states, otherwise calls
     *  the more specific protected version.
     * You may override this method if you need processing of invalid states.
     * @param pendingOnly True to only return outgoing and pending events
     * @param time Time to use in timeouts, zero to use system time
     * @return A newly allocated event or NULL if none is needed
     */
    virtual SIPEvent* getEvent(bool pendingOnly = false, u_int64_t time = 0);

    /**
     * Checks if a response message can be sent
     * @return True if the transaction can send a response message
     */
    bool setResponse() const;

    /**
     * Creates and transmits a final response message
     * @param code Response code to send
     * @param reason Human readable reason text (optional)
     * @return True if the message was queued for transmission
     */
    bool setResponse(int code, const char* reason = 0);

    /**
     * Transmits a final response message
     */
    void setResponse(SIPMessage* message);

    /**
     * Retrieve the latest response code
     * @return Code of most recent response, zero if none is known
     */
    inline int getResponseCode() const
	{ return m_response; }

    /**
     * Message send failure notification
     * @param msg Failed message
     */
    void msgTransmitFailed(SIPMessage* msg);

    /**
     * Set an arbitrary pointer as user specific data
     */
    inline void setUserData(void* data)
	{ m_private = data; }

    /**
     * Return the opaque user data
     */
    inline void* getUserData() const
	{ return m_private; }

protected:
    /**
     * Constructor from previous auto authenticated transaction. This is used only internally
     * @param original Original transaction that failed authentication
     * @param answer SIP answer that creates the new transaction
     */
    SIPTransaction(SIPTransaction& original, SIPMessage* answer);

    /**
     * Pre-destruction notification used to clean up the transaction
     */
    virtual void destroyed();

    /**
     * Attempt to perform automatic client transaction authentication
     * @param answer SIP answer that creates the new transaction
     * @return True if current client processing must be abandoned
     */
    bool tryAutoAuth(SIPMessage* answer);

    /**
     * Get an event only for client transactions
     * @param state The current state of the transaction
     * @param timeout If timeout occured, number of remaining timeouts,
     *  otherwise -1
     * @return A newly allocated event or NULL if none is needed
     */
    virtual SIPEvent* getClientEvent(int state, int timeout);

    /**
     * Get an event only for server transactions.
     * @param state The current state of the transaction
     * @param timeout If timeout occured, number of remaining timeouts,
     *  otherwise -1
     * @return A newly allocated event or NULL if none is needed
     */
    virtual SIPEvent* getServerEvent(int state, int timeout);

    /**
     * Process only the messages for client transactions
     * @param message A pointer to the message to process, should not be used
     *  afterwards if this method returned True
     * @param state The current state of the transaction
     */
    virtual void processClientMessage(SIPMessage* message, int state);

    /**
     * Process only the messages for server transactions
     * @param message A pointer to the message to process, should not be used
     *  afterwards if this method returned True
     * @param state The current state of the transaction
     */
    virtual void processServerMessage(SIPMessage* message, int state);

    /**
     * Change the transaction state
     * @param newstate The desired new state
     * @return True if state change occured
     */
    bool changeState(int newstate);

    /**
     * Set the latest message sent by this transaction
     * @param message Pointer to the latest message
     */
    void setLatestMessage(SIPMessage* message = 0);

    /**
     * Store a pending event to be picked up at the next @ref getEvent() call
     * @param event Event to store
     * @param replace True to replace any existing pending event
     */
    void setPendingEvent(SIPEvent* event = 0, bool replace = false);

    /**
     * Check if there is a pending event waiting
     * @return True is there is a pending event
     */
    inline bool isPendingEvent() const
	{ return (m_pending != 0); }

    /**
     * Set a repetitive timeout
     * @param delay How often (in microseconds) to fire the timeout
     * @param count How many times to keep firing the timeout
     */
    void setTimeout(u_int64_t delay = 0, unsigned int count = 1);

    bool m_outgoing;
    bool m_invite;
    bool m_transmit;
    int m_state;
    int m_response;
    unsigned int m_transCount;
    unsigned int m_timeouts;
    u_int64_t m_delay;
    u_int64_t m_timeout;
    SIPMessage* m_firstMessage;
    SIPMessage* m_lastMessage;
    SIPEvent* m_pending;
    SIPEngine* m_engine;
    String m_branch;
    String m_callid;
    String m_tag;
    void *m_private;
};

/**
 * This object is an event that will be taken from SIPEngine
 * @short A SIP event as retrieved from engine
 */
class YSIP_API SIPEvent
{
    friend class SIPTransaction;
public:

    SIPEvent()
	: m_message(0), m_transaction(0), m_state(SIPTransaction::Invalid)
	{ }

    SIPEvent(SIPMessage* message, SIPTransaction* transaction = 0);

    ~SIPEvent();

    /**
     * Get the SIP engine this event belongs to, if any
     * @return Pointer to owning SIP engine or NULL
     */
    inline SIPEngine* getEngine() const
	{ return m_transaction ? m_transaction->getEngine() : 0; }

    /**
     * Get the SIP message this event is supposed to handle
     * @return Pointer to SIP message causing the event
     */
    inline SIPMessage* getMessage() const
	{ return m_message; }

    /**
     * Get the SIP transaction that generated the event, if any
     * @return Pointer to owning SIP transaction or NULL
     */
    inline SIPTransaction* getTransaction() const
	{ return m_transaction; }

    /**
     * Check if the message is an outgoing message
     * @return True if the message should be sent to remote
     */
    inline bool isOutgoing() const
	{ return m_message && m_message->isOutgoing(); }

    /**
     * Check if the message is an incoming message
     * @return True if the message is coming from remote
     */
    inline bool isIncoming() const
	{ return m_message && !m_message->isOutgoing(); }

    /**
     * Get the pointer to the endpoint this event uses
     */
    inline SIPParty* getParty() const
	{ return m_message ? m_message->getParty() : 0; }

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

    /**
     * The state of the transaction when the event was generated
     */
    inline int getState() const
	{ return m_state; }

    /**
     * Check if the transaction was active when the event was generated
     * @return True if the transaction was active, false if it finished
     */
    inline bool isActive() const
	{ return (SIPTransaction::Invalid < m_state) && (m_state < SIPTransaction::Finish); }

protected:
    SIPMessage* m_message;
    SIPTransaction* m_transaction;
    int m_state;
};

/**
 * The SIP engine holds common methods and the list of current transactions
 * @short The SIP engine and transaction list
 */
class YSIP_API SIPEngine : public DebugEnabler, public Mutex
{
public:
    /**
     * Create the SIP Engine
     */
    SIPEngine(const char* userAgent = 0);

    /**
     * Destroy the SIP Engine
     */
    virtual ~SIPEngine();

    /**
     * Build a new SIPParty for a message
     * @param message Pointer to the message to build the party
     * @return True on success, false if party could not be built
     */
    virtual bool buildParty(SIPMessage* message) = 0;

    /**
     * Check user credentials for validity
     * @param username User account name
     * @param realm Authentication realm
     * @param nonce Authentication opaque nonce generated by the server
     * @param method Method of the SIP message that is being authenticated
     * @param uri URI of the SIP message that is being authenticated
     * @param response Response computed by the authenticated entity
     * @param message Message that is to be authenticated
     * @param authLine Extra credentials line to validate
     * @param userData Pointer to an optional object passed from @ref authUser
     * @return True if valid user/password, false if verification failed
     */
    virtual bool checkUser(String& username, const String& realm, const String& nonce,
	const String& method, const String& uri, const String& response,
	const SIPMessage* message, const MimeHeaderLine* authLine, GenObject* userData);

    /**
     * Authenticate a message by other means than user credentials. By default
     *  it calls @ref checkUser with empty user credential fields
     * @param noUser No plausible user credentials were detected so far
     * @param username User account name
     * @param message Message that is to be authenticated
     * @param authLine Extra credentials line to validate
     * @param userData Pointer to an optional object passed from @ref authUser
     * @return True if message is authenticated, false if verification failed
     */
    virtual bool checkAuth(bool noUser, String& username, const SIPMessage* message,
	const MimeHeaderLine* authLine, GenObject* userData);

    /**
     * Detect the proper credentials for any user in the engine
     * @param message Pointer to the message to check
     * @param user String to store the authenticated user name or user to
     *  look for (if not null on entry)
     * @param proxy True to authenticate as proxy, false as user agent
     * @param userData Pointer to an optional object that is passed back to @ref checkUser
     * @return Age of the nonce if user matches, negative for a failure
     */
    int authUser(const SIPMessage* message, String& user, bool proxy = false, GenObject* userData = 0);

    /**
     * Add a message into the transaction list
     * @param ep Party of the received message
     * @param buf A buffer containing the SIP message text
     * @param len The length of the message or -1 to interpret as C string
     * @return Pointer to the transaction or NULL if message was invalid
     */
    SIPTransaction* addMessage(SIPParty* ep, const char* buf, int len = -1);

    /**
     * Add a message into the transaction list
     * This method is thread safe
     * @param message A parsed SIP message to add to the transactions
     * @return Pointer to the transaction or NULL if message was invalid
     */
    SIPTransaction* addMessage(SIPMessage* message);

    /**
     * Get a SIPEvent from the queue.
     * This method mainly looks into the transaction list and get all kind of
     * events, like an incoming request (INVITE, REGISTRATION), a timer, an
     * outgoing message.
     * This method is thread safe
     */
    SIPEvent *getEvent();

    /**
     * This method should be called very often to get the events from the list and
     * to send them to processEvent method.
     * @return True if some events were processed this turn
     */
    bool process();

    /**
     * Default handling for events.
     * This method should be overriden for what you need and at the end you
     * should call this default one
     * This method is thread safe
     */
    virtual void processEvent(SIPEvent *event);

    /**
     * Handle answers that create new dialogs for an outgoing INVITE
     * @param answer The message that creates the INVITE fork
     * @param trans One of the transactions part of the same INVITE
     * @return Pointer to new transaction or NULL if message is ignored
     */
    virtual SIPTransaction* forkInvite(SIPMessage* answer, SIPTransaction* trans);

    /**
     * Get the timeout to be used for transactions involving human interaction.
     * The default implementation returns the proxy INVITE timeout (timer C = 3 minutes)
     *  minus the INVITE response retransmit interval (timer T2 = 4 seconds)
     * @return Duration of the timeout in microseconds
     */
    virtual u_int64_t getUserTimeout() const;

    /**
     * Get the length of a timer
     * @param which A one-character constant that selects which timer to return
     * @param reliable Whether we request the timer value for a reliable protocol
     * @return Duration of the selected timer or 0 if invalid
     */
    u_int64_t getTimer(char which, bool reliable = false) const;

    /**
     * Get the number of times to send a SIP request.
     * This value applies only when retransmission is required
     * @return The number of times to send a SIP request
     */
    inline unsigned int getReqTransCount() const
	{ return m_reqTransCount; }

    /**
     * Get the number of times to send a response to a SIP request.
     * This value applies only when retransmission is required
     * @return The number of times to send a response to a SIP request
     */
    inline unsigned int getRspTransCount() const
	{ return m_rspTransCount; }

    /**
     * Get the default value of the Max-Forwards header for this engine
     * @return The maximum number of hops the request is allowed to pass
     */
    inline unsigned int getMaxForwards() const
	{ return m_maxForwards; }

    /**
     * Get the User agent for this SIP engine
     */
    inline const String& getUserAgent() const
	{ return m_userAgent; }

    /**
     * Get the Command Sequence Number generator
     * @return Pointer to the CSeq generator of this engine
     */
    inline SIPSequence* getSequence() const
	{ return m_seq; }

    /**
     * Get a CSeq value suitable for use in a new request
     * @return New CSeq value
     */
    inline int32_t getNextCSeq()
	{ return m_seq->getNextCSeq(); }

    /**
     * Check if the engine is set up for lazy "100 Trying" messages
     * @return True if the first 100 message is to be skipped for non-INVITE
     */
    inline bool lazyTrying() const
	{ return m_lazyTrying; }

    /**
     * Set the lazy "100 Trying" messages flag
     * @param lazy100 True to not send the 1st 100 message for non-INVITE
     */
    inline void lazyTrying(bool lazy100)
	{ m_lazyTrying = lazy100; }

    /**
     * Retrieve various flags for this engine
     * @return Value of flags ORed together
     */
    inline int flags() const
	{ return m_flags; }

    /**
     * Check if message party should be changed from latest dialog party
     * @return Value of auto change party option
     */
    inline bool autoChangeParty() const
	{ return m_autoChangeParty; }

    /**
     * Get an authentication nonce
     * @param nonce String reference to fill with the current nonce
     */
    void nonceGet(String& nonce);

    /**
     * Get the age of an authentication nonce
     * @param nonce String nonce to check for validity and age
     * @return Age of the nonce in seconds, negative for invalid
     */
    long nonceAge(const String& nonce);

    /**
     * Get a nonce count
     * @param nc String reference to fill with new nonce count
     */
    void ncGet(String& nc);

    /**
     * Build an authentication response
     * @param username User account name
     * @param realm Authentication realm
     * @param passwd Account password
     * @param nonce Authentication opaque nonce generated by the server
     * @param method Method of the SIP message that is being authenticated
     * @param uri URI of the SIP message that is being authenticated
     * @param response String to store the computed response
     * @param qop Optional quality of protection type (set in list name) and parameters
     */
    static void buildAuth(const String& username, const String& realm, const String& passwd,
	const String& nonce, const String& method, const String& uri, String& response,
	const NamedList& qop = NamedList::empty());

    /**
     * Build an authentication response from already hashed components
     * @param hash_a1 MD5 digest of username:realm:password
     * @param nonce Authentication opaque nonce generated by the server
     * @param hash_a2 MD5 digest of method:uri
     * @param response String to store the computed response
     */
    static void buildAuth(const String& hash_a1, const String& nonce, const String& hash_a2,
	String& response);

    /**
     * Check if a method is in the allowed methods list
     * @param method Uppercase name of the method to check
     * @return True if the method should be allowed processing
     */
    bool isAllowed(const char* method) const;

    /**
     * Add a method to the allowed methods list
     * @param method Uppercase name of the method to add
     */
    void addAllowed(const char* method);

    /**
     * Get all the allowed methods
     * @return Comma separated list of allowed methods
     */
    inline const String& getAllowed() const
	{ return m_allowed; }

    /**
     * Remove a transaction from the list without dereferencing it
     * @param transaction Pointer to transaction to remove
     */
    inline void remove(SIPTransaction* transaction)
	{ lock(); m_transList.remove(transaction,false); unlock(); }

    /**
     * Append a transaction to the end of the list
     * @param transaction Pointer to transaction to append
     */
    inline void append(SIPTransaction* transaction)
	{ lock(); m_transList.append(transaction); unlock(); }

    /**
     * Insert a transaction at the start of the list
     * @param transaction Pointer to transaction to insert
     */
    inline void insert(SIPTransaction* transaction)
	{ lock(); m_transList.insert(transaction); unlock(); }

    /**
     * Get the number of active SIP transactions
     * @return Count of transactions in the list
     */
    inline unsigned int transactionCount()
	{ Lock mylock(this); return m_transList.count(); }

protected:
    /**
     * The list that holds all the SIP transactions.
     */
    ObjList m_transList;

    u_int64_t m_t1;
    u_int64_t m_t4;
    int m_reqTransCount;
    int m_rspTransCount;
    unsigned int m_maxForwards;
    int m_flags;
    bool m_lazyTrying;
    String m_userAgent;
    String m_allowed;
    RefPointer<SIPSequence> m_seq;
    u_int32_t m_nc;
    String m_nonce;
    String m_nonce_secret;
    u_int32_t m_nonce_time;
    Mutex m_nonce_mutex;
    bool m_autoChangeParty;
};

}

#endif /* __YATESIP_H */

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