/*
    Copyright (C) 2005-2009  Michel de Boer <michel@twinklephone.com>

    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
*/

/**
 * @file
 * SIP dialog established by an INVITE transaction.
 */

#ifndef _DIALOG_H
#define _DIALOG_H

#include <string>
#include <list>
#include <set>
#include <queue>
#include "abstract_dialog.h"
#include "client_request.h"
#include "phone.h"
#include "transaction_layer.h"
#include "protocol.h"
#include "redirect.h"
#include "session.h"
#include "user.h"
#include "sockets/url.h"
#include "threads/mutex.h"
#include "parser/request.h"
#include "sdp/sdp.h"
#include "stun/stun.h"

using namespace std;

// Forward declarations
class t_phone;
class t_line;
class t_session;
class t_sub_refer;

/** Dialog state */
enum t_dialog_state {
	DS_NULL,		/**< Initial state */

	// UAC states
	DS_W4INVITE_RESP,	/**< INVITE sent, waiting for response */
	DS_W4INVITE_RESP2,	/**< Provisional response received */
	DS_EARLY,		/**< Provisional response with to-tag received */
	DS_W4BYE_RESP,		/**< BYE sent, waiting for response */

	// UAS states
	DS_W4ACK,		/**< Waiting for ACK on 2XX INVITE */
	DS_W4ANSWER,		/**< INVITE received, waiting for user to answer */

	// UAS and UAC states
	DS_CONFIRMED,		/**< Success received/sent */
	DS_W4ACK_RE_INVITE,	/**< Waiting for ACK on re-INVITE */
	DS_W4RE_INVITE_RESP,	/**< re-INVITE sent, waiting for response */
	DS_W4RE_INVITE_RESP2,	/**< re-INVITE sent, provisional response recvd */
	DS_TERMINATED,		/**< Dialog terminated */

	// Subscription states
	DS_CONFIRMED_SUB,	/**< Confirmed refer-subscription dialog */
};

/** Purpose for sending a re-INVITE request */
enum t_reinvite_purpose {
	REINVITE_HOLD,		/**< Re-invite for call hold */
	REINVITE_RETRIEVE,	/**< Re-invite for call retrieve */
};

/**
 * SIP dialog established by an INVITE transaction.
 *
 * Dialog state diagrams:
 * @dot
 * digraph call {
 *   label="Call setup and tear down state transitions"
 *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];
 *   edge [fontname=Helvetica, fontsize=9];
 *
 *   null [label="DS_NULL" URL="\ref DS_NULL"];
 *   w4invite_resp [label="DS_W4INVITE_RESP" URL="\ref DS_W4INVITE_RESP"]
 *   w4invite_resp2 [label="DS_W4INVITE_RESP2" URL="\ref DS_W4INVITE_RESP2"]
 *   early [label="DS_EARLY" URL="\ref DS_EARLY"]
 *   w4bye_resp [label="DS_W4BYE_RESP" URL="\ref DS_W4BYE_RESP"]
 *   w4ack [label="DS_W4ACK" URL="\ref DS_W4ACK"]
 *   w4answer [label="DS_W4ANSWER" URL="\ref DS_W4ANSWER"]
 *   confirmed [label="DS_CONFIRMED" URL="\ref DS_CONFIRMED"]
 *   terminated [label="DS_TERMINATED" URL="\ref DS_TERMINATED"]
 *
 *   null -> w4invite_resp [label="send INVITE"]
 *   null -> w4answer [label="receive INVITE"]
 *   null -> terminated [label="receive INVITE\nSTUN media\nbind fails"]
 *   null -> terminated [label="receive INVITE\nunsupported\nmedia or body"]
 *   w4invite_resp -> w4invite_resp2 [label="receive 1XX without to-tag"]
 *   w4invite_resp -> early [label="receive 1XX with to-tag"]
 *   w4invite_resp -> confirmed [label="receive 2XX"]
 *   w4invite_resp -> terminated [label="receive failure\nresponse"]
 *   w4invite_resp2 -> confirmed [label="receive 2XX"]
 *   w4invite_resp2 -> early [label="receive 1XX with to-tag"]
 *   w4invite_resp2 -> terminated [label="receive failure\nresponse"]
 *   early -> confirmed [label="receive 2XX"]
 *   early -> terminated [label="receive failure\nresponse"]
 *   w4answer -> w4ack [label="user answered, send 2XX"]
 *   w4answer -> terminated [label="user rejected\nsend 4XX/6XX"]
 *   w4answer -> terminated [label="receive CANCEL/BYE\nsend 487"]
 *   w4ack -> confirmed [label="receive ACK"]
 *   confirmed -> w4bye_resp [label="send BYE"]
 *   confirmed -> terminated [label="receive BYE"]
 *   w4bye_resp -> terminated [label="receive BYE response"]
 * }
 * @enddot
 *
 * @dot
 * digraph reinvite {
 *   label="re-INVITE state transitions"
 *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];
 *   edge [fontname=Helvetica, fontsize=9];
 *
 *   confirmed [label="DS_CONFIRMED" URL="\ref DS_CONFIRMED"]
 *   w4ack_re_invite [label="DS_W4ACK_RE_INVITE" URL="\ref DS_W4ACK_RE_INVITE"]
 *   w4re_invite_resp [label="DS_W4RE_INVITE_RESP" URL="\ref DS_W4RE_INVITE_RESP"]
 *   w4re_invite_resp2 [label="DS_W4RE_INVITE_RESP2" URL="\ref DS_W4RE_INVITE_RESP2"]
 *   terminated [label="DS_TERMINATED" URL="\ref DS_TERMINATED"]
 *
 *   confirmed -> w4ack_re_invite [label="receive re-INVITE, send 2XX"]
 *   confirmed -> terminated [label="receive re-INVITE\nSTUN fails"]
 *   confirmed -> confirmed [label="receive re-INVITE, send failure response"]
 *   confirmed -> w4re_invite_resp [label="send re-INVITE"]
 *   w4ack_re_invite -> confirmed [label="reveive ACK"]
 *   w4re_invite_resp -> w4re_invite_resp2 [label="receive 1XX"]
 *   w4re_invite_resp -> confirmed [label="receive final response"]
 *   w4re_invite_resp2 -> confirmed [label="receive final response"]
 *   w4re_invite_resp -> terminated [label="receive BYE"]
 *   w4re_invite_resp2 -> terminated [label="receive BYE"]
 * }
 * @enddot
 *
 * @dot
 * digraph refer {
 *   label="State transitions when REFER subscription is active"
 *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];
 *   edge [fontname=Helvetica, fontsize=9];
 *
 *   confirmed [label="DS_CONFIRMED" URL="\ref DS_CONFIRMED"]
 *   w4bye_resp [label="DS_W4BYE_RESP" URL="\ref DS_W4BYE_RESP"]
 *   w4re_invite_resp [label="DS_W4RE_INVITE_RESP" URL="\ref DS_W4RE_INVITE_RESP"]
 *   w4re_invite_resp2 [label="DS_W4RE_INVITE_RESP2" URL="\ref DS_W4RE_INVITE_RESP2"]
 *   terminated [label="DS_TERMINATED" URL="\ref DS_TERMINATED"]
 *   confirmed_sub [label="DS_CONFIRMED_SUB" URL="DS_CONFIRMED_SUB"]
 *
 *   confirmed -> confirmed_sub [label="receive BYE"]
 *   w4re_invite_resp -> confirmed_sub [label="receive BYE"]
 *   w4re_invite_resp2 -> confirmed_sub [label="receive BYE"]
 *   w4bye_resp -> confirmed_sub [label="receive BYE response"]
 *   confirmed_sub -> terminated [label="terminate subscription"]
 * }
 * @enddot
 *
 * @dot
 * digraph timeout {
 *   label="State transitions due to timeouts"
 *   node [shape=ellipse, fontname=Helvetica, fontsize=10, style=filled, fillcolor=yellow];
 *   edge [fontname=Helvetica, fontsize=9];
 *
 *   w4answer [label="DS_W4ANSWER" URL="\ref DS_W4ANSWER"]
 *   w4ack [label="DS_W4ACK" URL="\ref DS_W4ACK"]
 *   confirmed [label="DS_CONFIRMED" URL="\ref DS_CONFIRMED"]
 *   terminated [label="DS_TERMINATED" URL="\ref DS_TERMINATED"]
 *   confirmed_sub [label="DS_CONFIRMED_SUB" URL="DS_CONFIRMED_SUB"]
 *
 *   w4answer -> w4answer [label="LTMR_100REL_TIMEOUT\nretransmit 1XX" URL="LTMR_100REL_TIMEOUT"]
 *   w4answer -> terminated [label="LTMR_100REL_GUARD\nsend 500" URL="LTMR_100REL_GUARD"]
 *   w4ack -> w4ack [label="LTMR_ACK_TIMEOUT\nretransmit 2XX" URL="LTMR_ACK_TIMEOUT"]
 *   w4ack -> confirmed [label="LTMR_ACK_GUARD\ntear down call" URL="LTMR_ACK_GUARD"]
 *   confirmed_sub -> terminated [label="REFER subscription\ntimeout"]
 * }
 * @enddot
 */
class t_dialog : public t_abstract_dialog {
	friend class t_phone;
	
protected:
	t_line			*line; /**< Phone line owning this dialog. */
	t_dialog_state		state; /**< Dialog state. */

	/** Session established by this dialog. */
	t_session	*session;

	/**
	 * New session being established during re-invite.
	 * When the re-INVITE transaction finishes successfully, then
	 * this session information will override the general session.
	 */
	t_session	*session_re_invite;

	/** The purpose of an outgoing re-INVITE request */
	t_reinvite_purpose	reinvite_purpose;

	/** Indicates if the last call hold action failed. */
	bool			hold_failed;

	t_client_request	*req_out;	  /**< Pending outgoing non-INVITE request */
	t_client_request	*req_out_invite;  /**< Pending outgoing INVITE */
	t_client_request	*req_in_invite;   /**< Pending incoming INVITE */
	t_client_request	*req_cancel;      /**< Pending outgoing CANCEL */
	t_client_request	*req_refer;	  /**< Pending outgoing REFER */
	t_client_request	*req_info;	  /**< Pending outgoing INFO */

	/**
	 * Last outgoing PRACK. While a PRACK is still pending a new 1xx
	 * response might come in. A PRACK will be sent for this 1xx without
	 * waiting for the response for the previous PRACK.
	 */
	t_client_request	*req_prack;
	
	/** Pending STUN request */
	t_client_request	*req_stun;
	
	/**
	 * Incoming request queue. A request may come in when it cannot be
	 * served yet. Such a request is stored in the queue to be served
	 * later.
	 */
	list<t_client_request *>	inc_req_queue;
	
	/** Indication if request must be cancelled */
	bool request_cancelled;
	
	/**
	 * Indication that the dialog must be terminated after a 2XX
	 * on an INVITE is received (e.g. when 2XX glares with CANCEL).
	 */
	bool end_after_2xx_invite;

	/** Indication that the dialog must be terminated after ACK. */
	bool end_after_ack;

	/**
	 * Indication that the user wants to answer the call.
	 * Sending the answer must be delayed as we are still waiting for
	 * a PRACK to acknowledge a 1xx containing SDP from the
	 * far end (RFC 3262 3).
	 */
	bool answer_after_prack;
	
	/** Indication if 180 ringing has already been received */
	bool ringing_received;

	/** Cached success response to INVITE needed for retransmission */
	t_response		*resp_invite;

	/**
	 * Cached provisional response to INVITE needed for retransmission
	 * when provisional responses are sent reliable (100rel)
	 */
	t_response		*resp_1xx_invite;

	/** Cached ack needed for retransmission */
	t_request		*ack;

	/** Subscription created by REFER (RFC 3515) */
	t_sub_refer		*sub_refer;
	
	/** Queue of DTMF digits to be sent via INFO requests */
	queue<char>		dtmf_queue;

	/** @name Process incoming responses */
	//@{
	/**
	 * Process an incoming response in the @ref DS_W4INVITE_RESP state.
	 * @param r The response
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4invite_resp(t_response *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming response in the @ref DS_EARLY state.
	 * @param r The response
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_early(t_response *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming response in the @ref DS_W4BYE_RESP state.
	 * @param r The response
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4bye_resp(t_response *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming response in the @ref DS_CONFIRMED state.
	 * @param r The response
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_confirmed_resp(t_response *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming response in the @ref DS_W4RE_INVITE_RESP state.
	 * @param r The response
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4re_invite_resp(t_response *r, t_tuid tuid, t_tid tid);
	//@}

	/** @name Process incoming requests */
	//@{
	/**
	 * Process an incoming request in the @ref DS_NULL state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_null(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_W4ANSWER state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4answer(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_W4ACK state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4ack(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_W4ACK_RE_INVITE state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4ack_re_invite(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_W4RE_INVITE_RESP state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4re_invite_resp(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_W4BYE_RESP state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_w4bye_resp(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_CONFIRMED state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_confirmed(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Process an incoming request in the @ref DS_CONFIRMED_SUB state.
	 * @param r The request
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void state_confirmed_sub(t_request *r, t_tuid tuid, t_tid tid);
	//@}
	
	/** @name Process requests in the confirmed state */
	//@{
	/** Proces incoming re-INVITE. */
	void process_re_invite(t_request *r, t_tuid tuid, t_tid tid);
	
	/** Proces incoming REFER. */
	void process_refer(t_request *r, t_tuid tuid, t_tid tid);
	
	/** Process incoming SUBSCRIBE (refer subscription). */
	void process_subscribe(t_request *r, t_tuid tuid, t_tid tid);
	
	/** Process incoming NOTIFY (refer subscription). */
	void process_notify(t_request *r, t_tuid tuid, t_tid tid);
	
	/** Process incoming INFO. */
	void process_info(t_request *r, t_tuid tuid, t_tid tid);
	
	/** Process incoming MESSAGE. */
	void process_message(t_request *r, t_tuid tuid, t_tid tid);
	//@}

	/** @name Process timeouts */
	//@{
	/**
	 * Process timeout in @ref DS_W4INVITE_RESP state.
	 * @param timer The expired timer.
	 */
	void state_w4invite_resp(t_line_timer timer);
	
	/**
	 * Process timeout in @ref DS_EARLY state.
	 * @param timer The expired timer.
	 */
	void state_early(t_line_timer timer);
	
	/**
	 * Process timeout in @ref DS_W4ACK state.
	 * @param timer The expired timer.
	 */
	void state_w4ack(t_line_timer timer);
	
	/**
	 * Process timeout in @ref DS_W4ACK_RE_INVITE state.
	 * @param timer The expired timer.
	 */
	void state_w4ack_re_invite(t_line_timer timer);
	
	/**
	 * Process timeout in @ref DS_W4RE_INVITE_RESP state.
	 * @param timer The expired timer.
	 */
	void state_w4re_invite_resp(t_line_timer timer);
	
	/**
	 * Process timeout in @ref DS_W4ANSWER state.
	 * @param timer The expired timer.
	 */
	void state_w4answer(t_line_timer timer);
	
	/**
	 * Process timeout in @ref DS_CONFIRMED state.
	 * @param timer The expired timer.
	 */
	void state_confirmed(t_line_timer timer);
	//@}

	/** Make the re-INVITE session the current session. */
	void activate_new_session(void);

	/**
	 * Process SDP answer in 1xx and 2xx responses if present.
	 * Apply ringing tone for a 180 response.
	 * Determine if call should be canceled due to unsupported
	 * or missing SDP.
	 * @param r The 1XX/2XX response.
	 */
	void process_1xx_2xx_invite_resp(t_response *r);

	/**
	 * Acknowledge a reveived 2xx response on an INVITE.
	 * @param r The 2XX response.
	 */
	void ack_2xx_invite(t_response *r);

	/**
	 * Send PRACK if the response requires it.
	 * @param r The response.
	 */
	void send_prack_if_required(t_response *r);

	/**
	 * Determine if a reliable provisional repsonse must be discarded.
	 * A provisional response must be discarded because it is a retransmission 
	 * or received out of order.
	 * Initializes the remote response nr if the response is the
	 * first response.
	 * @param r The provisional response.
	 * @return true, discard response.
	 * @return false, otherwise
	 */
	bool must_discard_100rel(t_response *r);

	/**
	 * Respond to an incoming PRACK.
	 * @param r The incoming PRACK.
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 * @return true, if a success response was given.
	 * @return false if an error response was given.
	 */
	bool respond_prack(t_request *r, t_tuid tuid, t_tid tid);

	virtual void send_request(t_request *r, t_tuid tuid);
public:
	/** @name Timer durations and timer id's */
	//@{
	unsigned long		dur_ack_timeout;	/**< @ref LTMR_ACK_TIMEOUT duration (ms) */
	t_object_id		id_ack_timeout;		/**< @ref LTMR_ACK_TIMEOUT timer id */
	t_object_id		id_ack_guard;		/**< @ref LTMR_ACK_GUARD timer id */
	t_object_id		id_re_invite_guard;	/**< @ref LTMR_RE_INVITE_GUARD timer id */
	t_object_id		id_glare_retry;		/**< @ref LTMR_GLARE_RETRY timer id */
	t_object_id		id_cancel_guard;	/**< @ref LTMR_CANCEL_GUARD timer id */
	//@}

	/** @name RFC 3262 100rel timers */
	//@{
	unsigned long		dur_100rel_timeout;	/**< @ref LTMR_100REL_TIMEOUT duration (ms) */
	t_object_id		id_100rel_timeout;	/**< @ref LTMR_100REL_TIMEOUT timer id */
	t_object_id		id_100rel_guard;	/**< @ref LTMR_100REL_GUARD timer id */
	//@}

	/** Indicates if last incoming REFER was accepted. */
	bool			refer_accepted;
	
	/** Indicates if the call transfer triggered by the last outgoing REFER succeeded. */
	bool			refer_succeeded;
	
	/** Indicates if the last outgoing REFER request failed. */
	bool			out_refer_req_failed;

	/**
	 * Indicates if this dialog is setup because the user told to do
	 * so by a REFER.
	 */
	bool			is_referred_call;

	/** State of an outgoing REFER. */
	t_refer_state		refer_state;

	/**
	 * Constructor.
	 * @param _line The line owning this dialog.
	 */
	t_dialog(t_line *_line);
	
	/** Destructor. */
	virtual ~t_dialog();

	virtual t_request *create_request(t_method m);

	virtual t_dialog *copy(void);

	/**
	 * Send INIVTE request.
	 * @param to_uri The URI to be used a request-URI and To header URI
	 * @param to_display Display name for To header.
	 * @param subject If not empty, this string will go into the Subject header.
	 * @param hdr_referred_by The Reffered-By header to be put in the INVITE.
	 * @param hdr_replaces The Replaces header to be put in the INVITE.
	 * @param hdr_require Required extensions to be put in the Require header.
	 * @param hdr_request_disposition Request-Disposition header to be put in the INVITE.
	 * @param anonymous Inidicates if the INVITE should be sent anonymous.
	 *
	 * @pre Dialog is in @ref DS_NULL state.
	 */
	void send_invite(const t_url &to_uri, const string &to_display,
		const string &subject, const t_hdr_referred_by &hdr_referred_by,
		const t_hdr_replaces &hdr_replaces, 
		const t_hdr_require &hdr_require, 
		const t_hdr_request_disposition &hdr_request_disposition,
		bool anonymous);

	/**
	 * Resend the INVITE with an authorization header containing credentials
	 * for the challenge in the response.
	 * @param resp, the response on the INVITE.
	 * @return true, if resending succeeded. 
	 * @return false, if credentials could not be determined.
	 *
	 * @pre The response must be a 401 or 407.
	 */
	bool resend_invite_auth(t_response *resp);

	/**
	 * Resend the INVITE because the far-end did not support all required
	 * extensions. Extensions that could not be supported will not be
	 * required this time.
	 * @param resp The response in the INVITE.
	 * @return true, if resending succeeded.
	 * @return false, if far-end did not indicate which extensions are
	 * unsupported. Or if a required extension could not be disabled.
	 *
	 * @pre The response must be a 420.
	 */
	bool resend_invite_unsupported(t_response *resp);

	/**
	 * Redirect INVITE to the next destination.
	 * @param resp The response on the INVITE.
	 * @return true, if INIVTE was redirected successfully.
	 * @return false, if there is no next destination.
	 *
	 * @pre The response must be a 3XX.
	 */
	bool redirect_invite(t_response *resp);
	
	/**
	 * Failover INVITE to the next destination from DNS lookup.
	 * @return true, if INIVTE was successfully sent.
	 * @return false, if there is no next destination.
	 */
	bool failover_invite(void);

	/** Send BYE request. */
	void send_bye(void);
	
	/** Send OPTIONS request. */
	void send_options(void);

	/**
	 * Send CANCEL request.
	 * If an early dialog exists, then the CANCEL can be sent
	 * right away as a response has been received for the INVITE.
	 * Otherwise, the CANCEL will be sent as soon as an early dialog
	 * is created.
	 * @param early_dialog_exists Indicates if an early dialog exists.
	 */
	void send_cancel(bool early_dialog_exists);
	
	/**
	 * Indicate that the dialog must be ended if a 2XX is received
	 * on an INVITE.
	 * @param on Set/clear indication.
	 */
	void set_end_after_2xx_invite(bool on);

	/**
	 * Send re-INVITE.
	 * @pre session_re_invite attribute contains the session
	 * information for the re-INVITE.
	 */
	void send_re_invite(void);

	virtual bool resend_request_auth(t_response *resp);

	virtual bool redirect_request(t_response *resp);
	
	virtual bool failover_request(t_response *resp);

	/**
	 * Hold call.
	 * If rtp_only is false, then a re-INVITE will be sent.
	 * @param rtponly Indicates if only the RTP streams should be stopped and
	 * the soundcard freed without any SIP signaling.
	 */
	void hold(bool rtponly = false);
	
	/** Retrieve call (send re-INVITE if needed). */
	void retrieve(void);
	
	/** Kill all RTP stream associated with this dialog. */
	void kill_rtp(void);

	/**
	 * Refer a call (send REFER).
	 * @param uri URI of the refer target.
	 * @param display Display name of the refer target.
	 */
	void send_refer(const t_url &uri, const string &display);

	/**
	 * Send DTMF digit.
	 * @param digit The digit.
	 * @param inband Indicates if digit must be sent inband.
	 * @param info Indicates if digit must be sent in a SIP INFO.
	 *
	 * @pre Either inband or info or none of the indicators is true.
	 * @post If none of the indicators is true, then RFC 2833 is used.
	 */
	void send_dtmf(char digit, bool inband, bool info);
	
	/**
	 * Create a binding for the media port via STUN.
	 * @return true, if binding is created.
	 * @return false, if binding cannot be created.
	 */
	bool stun_bind_media(void);

	/** @name Handle received events */
	//@{
	void recvd_response(t_response *r, t_tuid tuid, t_tid tid);
	
	void recvd_request(t_request *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Handle incoming CANCEL request.
	 * @param r The CANCEL request.
	 * @param cancel_tid Transaction id of the CANCEL transaction.
	 * @param target_tid Transaction id of the transaction to be cancellerd.
	 */
	void recvd_cancel(t_request *r, t_tid cancel_tid, t_tid target_tid);
	
	/**
	 * Handle incoming STUN response.
	 * @param r The STUN response.
	 * @param tuid Transaction user id
	 * @param tid Transaction id
	 */
	void recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid);
	
	/**
	 * Handle the response from the user on the question for refer
	 * permission. This response is received on the dialog that received
	 * the REFER before.
	 * @param permission Permission response from the user.
	 * @param r The REFER request that was received.
	 */
	void recvd_refer_permission(bool permission, t_request *r);
	//@}

	/** Answer a call (send 200 OK). */
	void answer(void);

	/**
	 * Reject a call.
	 * @param code The response code to reject the call with.
	 * @param reason A specific reason may be given to the error code. If no
	 * reason is specified the default reason is used.
	 *
	 * @pre code >= 400
	 */
	void reject(int code, string reason = "");

	/**
	 * Redirect a call.
	 * @param code The response code to redirect the call with.
	 * @param A specific reason may be give to the error code. If no
	 * reason is specified the default reason is used.
	 * @param destinations The list of redirect destinations in order of
	 * preference.
	 *
	 * @pre code is 3XX.
	 */
	void redirect(const list<t_display_url> &destinations, int code, string reason = "");

	virtual bool match_response(t_response *r, t_tuid tuid);
	
	/**
	 * Match STUN response with dialog.
	 * @param r STUN response.
	 * @param tuid Transaction user id
	 * @return true, if response matches.
	 * @return false, otherwise.
	 */
	bool match_response(StunMessage *r, t_tuid tuid);

	/**
	 * Match CANCEL request with dialog.
	 * @param r CANCEL request.
	 * @param target_tid Transaction id of transaction to be cancelled.
	 * @return true, if request matches.
	 * @return false, otherwise.
	 */
	bool match_cancel(t_request *r, t_tid target_tid);

	/**
	 * Check if an incoming INVITE is a retransmission.
	 * @param r The INVITE request.
	 * @return true, if INVITE is a retransmission.
	 * @return false, otherwise.
	 */
	bool is_invite_retrans(t_request *r);

	/** Process a retransmission of an incoming INVITE. */
	void process_invite_retrans(void);

	/**
	 * Get the state of the dialog.
	 * @return The dialog state.
	 */
	t_dialog_state get_state(void) const;

	/**
	 * Process dialog timer timeout.
	 * @param timer The timer that expired.
	 */
	void timeout(t_line_timer timer);

	/**
	 * Process subcribe timer timeout (REFER subscription).
	 * @param timer The timer that expired.
	 * @param event_type Event type of the subscription.
	 * @param event_id Event id of the subscription.
	 */
	void timeout_sub(t_subscribe_timer timer, const string &event_type,
		const string &event_id);

	/**
	 * Get the phone that belongs to this dialog.
	 * @return The phone object.
	 */
	t_phone *get_phone(void) const;

	/**
	 * Get the line that belongs to this dialog.
	 * @return The line object.
	 */
	t_line *get_line(void) const;

	/**
	 * Get the session belonging to this dialog.
	 * @return The session belonging to this dialog.
	 * @return NULL if there is no session.
	 */
	t_session * get_session(void) const;
	
	/**
	 * Get the audio session belonging to this dialog.
	 * @return The audio session belonging to this dialog.
	 * @return NULL if there is no audio session.
	 */	
	t_audio_session *get_audio_session(void) const;
	
	/**
	 * Check if the dialog has an acitve session.
	 * @return true, if the dialog has an active session.
	 * @return false, otherwise.
	 */
	bool has_active_session(void) const;

	/**
	 * Notify the dialog of the progress of a reference.
	 * @param r The response sent by the refer target.
	 */
	void notify_refer_progress(t_response *r);
	
	/**
	 * Check if a dialog will be released.
	 * @return true, if the dialog will be released.
	 * @return false, otherwise.
	 */
	bool will_release(void) const;
};

#endif
