From e6a5b770072500cf89a9cc10e4b99a972a6787b8 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sat, 11 Nov 2006 16:16:04 +0000 Subject: Attended call transfer implementation. The changes involves: - Added support for SIP Replaces extension (RFC 3891) - Added pjsua_call_xfer_replaces() to perform attended call transfer. - PJSUA checks and process Replaces header in incoming calls - Added pjsip_ua_find_dialog() API. - Added pjsip_endpt_has_capability() API. - Added pjsip_endpt_send_response2() API. - etc. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@797 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsip-ua/sip_replaces.h | 300 ++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 pjsip/include/pjsip-ua/sip_replaces.h (limited to 'pjsip/include/pjsip-ua/sip_replaces.h') diff --git a/pjsip/include/pjsip-ua/sip_replaces.h b/pjsip/include/pjsip-ua/sip_replaces.h new file mode 100644 index 00000000..38c8aa99 --- /dev/null +++ b/pjsip/include/pjsip-ua/sip_replaces.h @@ -0,0 +1,300 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 + +/** + * @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. + // + if (response) { + pjsip_endpt_send_response(endpt, rdata, response, NULL, NULL); + } else { + // Respond with 500 (Internal Server Error) + pjsip_endpt_respond_stateless(endpt, rdata, 500, NULL, NULL, NULL); + } + } + + // Create UAS Invite session as usual. + // + status = pjsip_dlg_create_uas(.., 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: + * - RFC 3891: The Session + * Initiation Protocol (SIP) "Replaces" Header + * - \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__ */ + -- cgit v1.2.3