/* Copyright (C) 2005-2009 Michel de Boer 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, see . */ #ifndef _LINE_H #define _LINE_H #include #include #include "call_history.h" #include "dialog.h" #include "id_object.h" #include "phone.h" #include "protocol.h" #include "user.h" #include "audio/audio_codecs.h" #include "sockets/url.h" #include "parser/request.h" #include "parser/response.h" #include "stun/stun.h" using namespace std; // Forward declarations class t_dialog; class t_phone; // Info about the current call. // This info can be used by the user interface to render the // call state to the user. class t_call_info { public: mutable t_mutex mutex; t_url from_uri; string from_display; // Override of display for presentation to user, e.g. name from // address book lookup. string from_display_override; string from_organization; t_url to_uri; string to_display; string to_organization; string subject; bool dtmf_supported; bool dtmf_inband; // DTMF must be sent inband bool dtmf_info; // DTMF must be sent via SIP INFO t_hdr_referred_by hdr_referred_by; // The reason phrase of the last received provisional response // on an outgoing INVITE. string last_provisional_reason; t_audio_codec send_codec; t_audio_codec recv_codec; bool refer_supported; t_call_info(); t_call_info(const t_call_info&); t_call_info& operator=(const t_call_info&); void clear(void); // Get the from display name to show to the user. string get_from_display_presentation(void) const; }; class t_line : public t_id_object { friend class t_phone; private: t_line_state state; t_line_substate substate; bool is_on_hold; bool is_muted; bool hide_user; // Anonymous call // Indicates if a call is a consultation for a transfer bool is_transfer_consult; // The line about which this consultation handles. unsigned short consult_transfer_from_line; // Indicates if this call is to be transferred after consultation. bool to_be_transferred; // After consultation this line should be transferred to the // transfer_to_line. unsigned short consult_transfer_to_line; // Indicates if media encryption should be negotiated. bool try_to_encrypt; // Indicates if call must be auto answered bool auto_answer; // Line number (starting from 0) // The number of a line may change when it moves from the user lines // to the pool of dying lines. So a line number cannot be used as // unique line identification over longer times. unsigned short line_number; // The phone that owns this line t_phone *phone; // Dialog for which no response with a to-tag has been received. // Formally this is not a dialog yet. t_dialog *open_dialog; // Dialogs for which a response (1XX/2XX) with a to-tag has // been received. list pending_dialogs; // Outgoing call: The first dialog for which a 2XX has been received. // Incoming call: Dialog created by an incoming INVITE t_dialog *active_dialog; // Currently not used. list dying_dialogs; // Timers t_object_id id_invite_comp; t_object_id id_no_answer; // Call info t_call_info call_info; /** RTP port to be used for this line. */ unsigned short rtp_port; /** * Phone user using the line. * This member is only set when the line is not idle. * An idle line is not associated with a user. * @note The line object does not own the phone user. * Therefor the line object must never delete the phone user. */ t_phone_user *phone_user; // The incoming call script can return a specific ring tone // to be played for an incoming call. This ring tone is // stored here. If there is no specific ring tone to be played // then this attribute is empty string user_defined_ringtone; // Indicates if the line must go to seized state when it // becomes idle. bool keep_seized; // Find a dialog from the list that matches the response. t_dialog *match_response(t_response *r, const list &l) const; t_dialog *match_response(StunMessage *r, t_tuid tuid, const list &l) const; t_dialog *match_call_id_tags(const string &call_id, const string &to_tag, const string &from_tag, const list &l) const; // Get the dialog with id == did. If dialog does not exist // then NULL is returned. t_dialog *get_dialog(t_object_id did) const; // Clean up terminated dialogs void cleanup(void); // Cleanup all open and pending dialogs void cleanup_open_pending(void); // Forcefully cleanup all dialogs void cleanup_forced(void); // Cleanup state for a transfer with consultation. // If the call on this line is a consult, then the consult state of // the line that is to be transferred will be cleaned too. // If the call on this line is to be transferred, then the consult // state of the consultation line will be cleared too. void cleanup_transfer_consult_state(void); public: // Call history record t_call_record call_hist_record; t_line(t_phone *_phone, unsigned short _line_number); ~t_line(); t_line_state get_state(void) const; t_line_substate get_substate(void) const; t_refer_state get_refer_state(void) const; // Timer operations void start_timer(t_line_timer timer, t_object_id did = 0); void stop_timer(t_line_timer timer, t_object_id did = 0); /** @name Actions */ //@{ /** * Send INIVTE request. * @param pu The phone user making this call. * @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 The line is idle. */ void invite(t_phone_user *pu, 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); /** * Send INIVTE request. * @param pu The phone user making this call. * @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 no_fork If true, put a no-fork request disposition in the outgoing INVITE * @param anonymous Inidicates if the INVITE should be sent anonymous. * * @pre The line is idle. */ void invite(t_phone_user *pu, const t_url &to_uri, const string &to_display, const string &subject, bool no_fork, bool anonymous); void answer(void); void reject(void); void redirect(const list &destinations, int code, string reason = ""); void end_call(void); void send_dtmf(char digit, bool inband, bool info); //@} // OPTIONS inside dialog void options(void); bool hold(bool rtponly = false); // returns false if call cannot be put on hold void retrieve(void); // Kill all RTP stream associated with this line void kill_rtp(void); void refer(const t_url &uri, const string &display); // Mute/unmute a call // - enable = true -> mute // - enable = false -> unmute void mute(bool enable); /** @name Handle incoming responses */ //@{ void recvd_provisional(t_response *r, t_tuid tuid, t_tid tid); void recvd_success(t_response *r, t_tuid tuid, t_tid tid); void recvd_redirect(t_response *r, t_tuid tuid, t_tid tid); void recvd_client_error(t_response *r, t_tuid tuid, t_tid tid); void recvd_server_error(t_response *r, t_tuid tuid, t_tid tid); void recvd_global_error(t_response *r, t_tuid tuid, t_tid tid); //@} /** @name Handle incoming requests */ //@{ void recvd_invite(t_phone_user *pu, t_request *r, t_tid tid, const string &ringtone); void recvd_ack(t_request *r, t_tid tid); void recvd_cancel(t_request *r, t_tid cancel_tid, t_tid target_tid); void recvd_bye(t_request *r, t_tid tid); void recvd_options(t_request *r, t_tid tid); void recvd_register(t_request *r, t_tid tid); void recvd_prack(t_request *r, t_tid tid); void recvd_subscribe(t_request *r, t_tid tid); void recvd_notify(t_request *r, t_tid tid); void recvd_info(t_request *r, t_tid tid); void recvd_message(t_request *r, t_tid tid); /** * Process REFER request. * @return true, if refer has been accepted sofar. The refer may still * be rejected by the user. * @return false, if the refer has been rejected. */ bool recvd_refer(t_request *r, 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. // The request (r) is the REFER request that was received. void recvd_refer_permission(bool permission, t_request *r); void recvd_stun_resp(StunMessage *r, t_tuid tuid, t_tid tid); void failure(t_failure failure, t_tid tid); void timeout(t_line_timer timer, t_object_id did); void timeout_sub(t_subscribe_timer timer, t_object_id did, const string &event_type, const string &event_id); // Return true if the response or request matches a dialog that // is owned by this line bool match(t_response *r, t_tuid tuid) const; bool match(t_request *r) const; bool match_cancel(t_request *r, t_tid target_tid) const; bool match(StunMessage *r, t_tuid tuid) const; /** * RFC 3891 Match info from Replaces header * Match call id, to-tag and from tag like an incoming request. * @param call_id [in] The Call ID of the Replaces header. * @param to_tag [in] to-tag of the Replaces header. * @param from_tag [in] from-tag of the Replaces header. * @param no_fork_req_disposition [in] Indicates if the incoming request * contains a no-fork request disposition. * @param early_matched [out] When a match is found, early_matched * indicates if the match was on an early dialog. * @return true if a match is found with an associated dialog. */ bool match_replaces(const string &call_id, const string &to_tag, const string &from_tag, bool no_fork_req_disposition, bool &early_matched) const; // Check if an incoming INVITE is a retransmission of an INVITE // that is already being processed by this line bool is_invite_retrans(t_request *r); // Process a retransmission of an incoming INVITE void process_invite_retrans(void); // Create user uri and contact uri string create_user_contact(const string &auto_ip) const; string create_user_uri(void) const; // Create a response to an OPTIONS request // Argument 'in-dialog' indicates if the OPTIONS response is // sent within a dialog. t_response *create_options_response(t_request *r, bool in_dialog = false) const; // Send a response/request void send_response(t_response *r, t_tuid tuid, t_tid tid); void send_request(t_request *r, t_tuid tuid); t_phone *get_phone(void) const; unsigned short get_line_number(void) const; bool get_is_on_hold(void) const; bool get_is_muted(void) const; bool get_hide_user(void) const; // If this is a transfer consult, then true will be returned and // lineno will be set to the line that must be transferred. bool get_is_transfer_consult(unsigned short &lineno) const; // When setting the transfer consult indication to true, the // line that must be transferred must be passed. void set_is_transfer_consult(bool enable, unsigned short lineno); // If this line is to be transferred after consultation, then // true will be returned and lineno will be set to the line // where this line should be transferred to. bool get_to_be_transferred(unsigned short &lineno) const; // When setting the to be transferred indication to true, the // line to which must be transferred must be passed. void set_to_be_transferred(bool enable, unsigned short lineno); bool get_is_encrypted(void) const; bool get_try_to_encrypt(void) const; bool get_auto_answer(void) const; void set_auto_answer(bool enable); bool is_refer_succeeded(void) const; bool has_media(void) const; /** @name Remote (target) uri/display */ //@{ /** * Get the remote target URI of the active dialog. * @return Remote target URI. If there is no active dialog, then an * empty URI is returned. */ t_url get_remote_target_uri(void) const; /** * Get the remote target URI of the first pending dialog. * @return Remote target URI. If there is no pending dialog, then an * empty URI is returned. */ t_url get_remote_target_uri_pending(void) const; /** * Get the remote target display name of the active dialog. * @return Remote target display name. If there is no active dialog, * then an empty string is returned. */ string get_remote_target_display(void) const; /** * Get the remote target display name of the first pending dialog. * @return Remote target display name. If there is no pending dialog, * then an empty string is returned. */ string get_remote_target_display_pending(void) const; /** * Get the remote URI of the active dialog. * @return Remote URI. If there is no active dialog, then an * empty URI is returned. */ t_url get_remote_uri(void) const; /** * Get the remote URI of the first pending dialog. * @return Remote URI. If there is no pending dialog, then an * empty URI is returned. */ t_url get_remote_uri_pending(void) const; /** * Get the remote display name of the active dialog. * @return Remote display name. If there is no active dialog, * then an empty string is returned. */ string get_remote_display(void) const; /** * Get the remote display name of the first pending dialog. * @return Remote display name. If there is no pending dialog, * then an empty string is returned. */ string get_remote_display_pending(void) const; //@} /** @name Call identification */ //@{ /** * Get the call-id of the active dialog * @return If there is no active dialog, then an empty string is returned. */ string get_call_id(void) const; /** * Get the call-id of the first pending dialog * @return If there is no pending dialog, then an empty string is returned. */ string get_call_id_pending(void) const; /** * Get the local tag of the active dialog * @return If there is no active dialog, then an empty string is returned. */ string get_local_tag(void) const; /** * Get the local tag of the first pending dialog * @return If there is no pending dialog, then an empty string is returned. */ string get_local_tag_pending(void) const; /** * Get the remote tag of the active dialog * @return If there is no active dialog, then an empty string is returned. */ string get_remote_tag(void) const; /** * Get the remote tag of the first pending dialog * @return If there is no pending dialog, then an empty string is returned. */ string get_remote_tag_pending(void) const; //@} // Returns true if the remote party of the active dialog supports // the extension. // If there is no active dialog, then false is returned. bool remote_extension_supported(const string &extension) const; // Seize the line. User wants to make an outgoing call, so // the line must be marked as busy, such that an incoming call // cannot take this line. // Returns false if seizure failed bool seize(void); // Unseize the line void unseize(void); // Return the (audio) session belonging to this line. // Returns NULL if there is no (audio) session t_session *get_session(void) const; t_audio_session *get_audio_session(void) const; void notify_refer_progress(t_response *r); // Called by dialog if retrieve/hold actions failed. void failed_retrieve(void); void failed_hold(void); // Called by dialog if retry of a retrieve after a glare (491 response) // succeeded. void retry_retrieve_succeeded(void); // Get the call info record t_call_info get_call_info(void) const; void ci_set_dtmf_supported(bool supported, bool inband = false, bool info = false); void ci_set_last_provisional_reason(const string &reason); void ci_set_send_codec(t_audio_codec codec); void ci_set_recv_codec(t_audio_codec codec); void ci_set_refer_supported(bool supported); // Initialize the RTP port for this line based on the settings // in the user profile. void init_rtp_port(void); /** Get the RTP port to be used for a call on this line. */ unsigned short get_rtp_port(void) const; /** * Get the user profile of the user using the phone. * @return a pointer to the user object owned by the line. * NOT a copy. */ t_user *get_user(void) const; /** * Get the phone user using the phone. * @return Pointer to the phone user. */ t_phone_user *get_phone_user(void) const; // Get the ring tone to be played for an incoming call string get_ringtone(void) const; // ZRTP actions void confirm_zrtp_sas(void); void reset_zrtp_sas_confirmation(void); void enable_zrtp(void); void zrtp_request_go_clear(void); void zrtp_go_clear_ok(void); // Force a line to the idle state (during termination of Twinkle) void force_idle(void); // Indicate if the line must be seized after releasing void set_keep_seized(bool seize); bool get_keep_seized(void) const; /** * Get a dialog that has an active session (RTP stream). * @return The dialog that has an active session. * @return NULL, if there is no dialog with an active session. * @note There can be at most 1 dialog with an active session. */ t_dialog *get_dialog_with_active_session(void) const; }; #endif