304 lines
11 KiB
C
304 lines
11 KiB
C
/*
|
|
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
|
|
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#ifndef __PJSIP_REPLACES_H__
|
|
#define __PJSIP_REPLACES_H__
|
|
|
|
|
|
/**
|
|
* @file sip_replaces.h
|
|
* @brief SIP Replaces support (RFC 3891 - SIP "Replaces" Header)
|
|
*/
|
|
#include <pjsip/sip_msg.h>
|
|
|
|
/**
|
|
* @defgroup PJSIP_REPLACES SIP Replaces support (RFC 3891 - "Replaces" Header)
|
|
* @ingroup PJSIP_HIGH_UA
|
|
* @brief SIP Replaces support (RFC 3891 - "Replaces" Header)
|
|
* @{
|
|
*
|
|
* This module implements support for Replaces header in PJSIP. The Replaces
|
|
* specification is written in RFC 3891 - The Session Initiation Protocol (SIP)
|
|
* "Replaces" Header, and can be used to enable a variety of features,
|
|
* for example: "Attended Transfer" and "Call Pickup".
|
|
*
|
|
*
|
|
*
|
|
* \section PJSIP_REPLACES_USING_SEC Using PJSIP Replaces Support
|
|
*
|
|
* \subsection PJSIP_REPLACES_INIT_SUBSEC Initialization
|
|
*
|
|
* Application needs to call #pjsip_replaces_init_module() during application
|
|
* initialization stage to register "replaces" support in PJSIP.
|
|
*
|
|
*
|
|
*
|
|
* \subsection PJSIP_REPLACES_UAC_SUBSEC UAC Behavior: Sending a Replaces Header
|
|
*
|
|
* A User Agent that wishes to replace a single existing early or
|
|
* confirmed dialog with a new dialog of its own, MAY send the target
|
|
* User Agent an INVITE request containing a Replaces header field. The
|
|
* User Agent Client (UAC) places the Call-ID, to-tag, and from-tag
|
|
* information for the target dialog in a single Replaces header field
|
|
* and sends the new INVITE to the target.
|
|
*
|
|
* To initiate outgoing INVITE request with Replaces header, application
|
|
* would create the INVITE request with #pjsip_inv_invite(), then adds
|
|
* #pjsip_replaces_hdr instance into the request, filling up the Call-ID,
|
|
* To-tag, and From-tag properties of the header with the identification
|
|
* of the dialog to be replaced. Application may also optionally
|
|
* set the \a early_only property of the header to indicate that it only
|
|
* wants to replace early dialog.
|
|
*
|
|
* Note that when the outgoing INVITE request (with Replaces) is initiated
|
|
* from an incoming REFER request (as in Attended Call Transfer case),
|
|
* this process should be done rather more automatically by PJSIP. Upon
|
|
* receiving incoming incoming REFER request, normally these processes
|
|
* will be performed:
|
|
* - Application finds \a Refer-To header,
|
|
* - Application creates outgoing dialog/invite session, specifying
|
|
* the URI in the \a Refer-To header as the initial remote target,
|
|
* - The URI in the \a Refer-To header may contain header parameters such
|
|
* as \a Replaces and \a Require headers.
|
|
* - The dialog keeps the header fields in the header parameters
|
|
* of the URI, and the invite session would add these headers into
|
|
* the outgoing INVITE request. Because of this, the outgoing
|
|
* INVITE request will contain the \a Replaces and \a Require headers.
|
|
*
|
|
*
|
|
* For more information, please see the implementation of
|
|
* #pjsua_call_xfer_replaces() in \ref PJSUA_LIB source code.
|
|
*
|
|
*
|
|
* \subsection PJSIP_REPLACES_UAS_SUBSEC UAS Behavior: Receiving a Replaces Header
|
|
*
|
|
* The Replaces header contains information used to match an existing
|
|
* SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE
|
|
* with a Replaces header, the User Agent (UA) attempts to match this
|
|
* information with a confirmed or early dialog.
|
|
*
|
|
* In PJSIP, if application wants to process the Replaces header in the
|
|
* incoming INVITE request, it should call #pjsip_replaces_verify_request()
|
|
* before creating the INVITE session. The #pjsip_replaces_verify_request()
|
|
* function checks and verifies the request to see if Replaces request
|
|
* can be processed. To be more specific, it performs the following
|
|
* verification:
|
|
* - checks that Replaces header is present. If not, the function will
|
|
* return PJ_SUCCESS without doing anything.
|
|
* - checks that no duplicate Replaces headers are present, or otherwise
|
|
* it will return 400 "Bad Request" response.
|
|
* - checks for matching dialog and verifies that the invite session has
|
|
* the correct state, and may return 481 "Call/Transaction Does Not Exist",
|
|
* 603 "Declined", or 486 "Busy Here" according to the processing rules
|
|
* specified in RFC 3891.
|
|
* - if matching dialog with correct state is found, it will give PJ_SUCCESS
|
|
* status and return the matching dialog back to the application.
|
|
*
|
|
* The following pseudocode illustrates how application can process the
|
|
* incoming INVITE if it wants to support Replaces extension:
|
|
*
|
|
\code
|
|
// Incoming INVITE request handler
|
|
pj_bool_t on_rx_invite(pjsip_rx_data *rdata)
|
|
{
|
|
pjsip_dialog *dlg, *replaced_dlg;
|
|
pjsip_inv_session *inv;
|
|
pjsip_tx_data *response;
|
|
pj_status_t status;
|
|
|
|
// Check whether Replaces header is present in the request and process accordingly.
|
|
//
|
|
status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response);
|
|
if (status != PJ_SUCCESS) {
|
|
// Something wrong with Replaces request.
|
|
//
|
|
pj_status_t status;
|
|
if (response) {
|
|
status = pjsip_endpt_send_response(endpt, rdata, response, NULL, NULL);
|
|
if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata);
|
|
} else {
|
|
// Respond with 500 (Internal Server Error)
|
|
status = pjsip_endpt_respond_stateless(endpt, rdata, 500, NULL, NULL, NULL);
|
|
if (status != PJ_SUCCESS) pjsip_tx_data_dec_ref(tdata);
|
|
}
|
|
}
|
|
|
|
// Create UAS Invite session as usual.
|
|
//
|
|
status = pjsip_dlg_create_uas_and_inc_lock(.., rdata, .., &dlg);
|
|
..
|
|
status = pjsip_inv_create_uas(dlg, .., &inv);
|
|
|
|
// Send initial 100 "Trying" to the INVITE request
|
|
//
|
|
status = pjsip_inv_initial_answer(inv, rdata, 100, ..., &response);
|
|
if (status == PJ_SUCCESS)
|
|
pjsip_inv_send_msg(inv, response);
|
|
|
|
|
|
// This is where processing is different between normal call
|
|
// (without Replaces) and call with Replaces.
|
|
//
|
|
if (replaced_dlg) {
|
|
pjsip_inv_session *replaced_inv;
|
|
|
|
// Always answer the new INVITE with 200, regardless whether
|
|
// the replaced call is in early or confirmed state.
|
|
//
|
|
status = pjsip_inv_answer(inv, 200, NULL, NULL, &response);
|
|
if (status == PJ_SUCCESS)
|
|
pjsip_inv_send_msg(inv, response);
|
|
|
|
|
|
// Get the INVITE session associated with the replaced dialog.
|
|
//
|
|
replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg);
|
|
|
|
|
|
// Disconnect the "replaced" INVITE session.
|
|
//
|
|
status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, &tdata);
|
|
if (status == PJ_SUCCESS && tdata)
|
|
status = pjsip_inv_send_msg(replaced_inv, tdata);
|
|
|
|
|
|
// It's up to application to associate the new INVITE session
|
|
// with the old (now terminated) session. For example, application
|
|
// may assign the same User Interface object for the new INVITE
|
|
// session.
|
|
|
|
} else {
|
|
// Process normal INVITE without Replaces.
|
|
...
|
|
}
|
|
}
|
|
|
|
\endcode
|
|
*
|
|
*
|
|
* For a complete sample implementation, please see \a pjsua_call_on_incoming()
|
|
* function of \ref PJSUA_LIB in \a pjsua_call.c file.
|
|
*
|
|
*
|
|
* \section PJSIP_REPLACES_REFERENCE References
|
|
*
|
|
* References:
|
|
* - <A HREF="http://www.ietf.org/rfc/rfc3891.txt">RFC 3891: The Session
|
|
* Initiation Protocol (SIP) "Replaces" Header</A>
|
|
* - \ref PJSUA_XFER
|
|
*/
|
|
|
|
PJ_BEGIN_DECL
|
|
|
|
|
|
/**
|
|
* Declaration of SIP Replaces header (RFC 3891).
|
|
*/
|
|
typedef struct pjsip_replaces_hdr
|
|
{
|
|
/** Standard header field. */
|
|
PJSIP_DECL_HDR_MEMBER(struct pjsip_replaces_hdr);
|
|
|
|
/** Call-Id */
|
|
pj_str_t call_id;
|
|
|
|
/** to-tag */
|
|
pj_str_t to_tag;
|
|
|
|
/** from-tag */
|
|
pj_str_t from_tag;
|
|
|
|
/** early-only? */
|
|
pj_bool_t early_only;
|
|
|
|
/** Other parameters */
|
|
pjsip_param other_param;
|
|
|
|
} pjsip_replaces_hdr;
|
|
|
|
|
|
|
|
/**
|
|
* Initialize Replaces support in PJSIP. This would, among other things,
|
|
* register the header parser for Replaces header.
|
|
*
|
|
* @param endpt The endpoint instance.
|
|
*
|
|
* @return PJ_SUCCESS on success.
|
|
*/
|
|
PJ_DECL(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt);
|
|
|
|
|
|
/**
|
|
* Create Replaces header.
|
|
*
|
|
* @param pool Pool to allocate the header instance from.
|
|
*
|
|
* @return An empty Replaces header instance.
|
|
*/
|
|
PJ_DECL(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool);
|
|
|
|
|
|
/**
|
|
* Verify that incoming request with Replaces header can be processed.
|
|
* This function will perform all necessary checks according to RFC 3891
|
|
* Section 3 "User Agent Server Behavior: Receiving a Replaces Header".
|
|
*
|
|
* @param rdata The incoming request to be verified.
|
|
* @param p_dlg On return, it will be filled with the matching
|
|
* dialog.
|
|
* @param lock_dlg Specifies whether this function should acquire lock
|
|
* to the matching dialog. If yes (and should be yes!),
|
|
* then application will need to release the dialog's
|
|
* lock with #pjsip_dlg_dec_lock() when the function
|
|
* returns PJ_SUCCESS and the \a p_dlg parameter is filled
|
|
* with the dialog instance.
|
|
* @param p_tdata Upon error, it will be filled with the final response
|
|
* to be sent to the request sender.
|
|
*
|
|
* @return The function returns the following:
|
|
* - If the request doesn't contain Replaces header, the
|
|
* function returns PJ_SUCCESS and \a p_dlg parameter
|
|
* will be set to NULL.
|
|
* - If the request contains Replaces header and a valid,
|
|
* matching dialog is found, the function returns
|
|
* PJ_SUCCESS and \a p_dlg parameter will be set to the
|
|
* matching dialog instance.
|
|
* - Upon error condition (as described by RFC 3891), the
|
|
* function returns non-PJ_SUCCESS, and \a p_tdata
|
|
* parameter SHOULD be set with a final response message
|
|
* to be sent to the sender of the request.
|
|
*/
|
|
PJ_DECL(pj_status_t) pjsip_replaces_verify_request(pjsip_rx_data *rdata,
|
|
pjsip_dialog **p_dlg,
|
|
pj_bool_t lock_dlg,
|
|
pjsip_tx_data **p_tdata);
|
|
|
|
|
|
|
|
PJ_END_DECL
|
|
|
|
|
|
/**
|
|
* @}
|
|
*/
|
|
|
|
|
|
#endif /* __PJSIP_REPLACES_H__ */
|
|
|