Tristan Matthews | 0a329cc | 2013-07-17 13:20:14 -0400 | [diff] [blame] | 1 | /* $Id$ */ |
| 2 | /* |
| 3 | * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| 4 | * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; either version 2 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU General Public License |
| 17 | * along with this program; if not, write to the Free Software |
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 19 | */ |
| 20 | #ifndef __PJSIP_REPLACES_H__ |
| 21 | #define __PJSIP_REPLACES_H__ |
| 22 | |
| 23 | |
| 24 | /** |
| 25 | * @file sip_replaces.h |
| 26 | * @brief SIP Replaces support (RFC 3891 - SIP "Replaces" Header) |
| 27 | */ |
| 28 | #include <pjsip/sip_msg.h> |
| 29 | |
| 30 | /** |
| 31 | * @defgroup PJSIP_REPLACES SIP Replaces support (RFC 3891 - "Replaces" Header) |
| 32 | * @ingroup PJSIP_HIGH_UA |
| 33 | * @brief SIP Replaces support (RFC 3891 - "Replaces" Header) |
| 34 | * @{ |
| 35 | * |
| 36 | * This module implements support for Replaces header in PJSIP. The Replaces |
| 37 | * specification is written in RFC 3891 - The Session Initiation Protocol (SIP) |
| 38 | * "Replaces" Header, and can be used to enable a variety of features, |
| 39 | * for example: "Attended Transfer" and "Call Pickup". |
| 40 | * |
| 41 | * |
| 42 | * |
| 43 | * \section PJSIP_REPLACES_USING_SEC Using PJSIP Replaces Support |
| 44 | * |
| 45 | * \subsection PJSIP_REPLACES_INIT_SUBSEC Initialization |
| 46 | * |
| 47 | * Application needs to call #pjsip_replaces_init_module() during application |
| 48 | * initialization stage to register "replaces" support in PJSIP. |
| 49 | * |
| 50 | * |
| 51 | * |
| 52 | * \subsection PJSIP_REPLACES_UAC_SUBSEC UAC Behavior: Sending a Replaces Header |
| 53 | * |
| 54 | * A User Agent that wishes to replace a single existing early or |
| 55 | * confirmed dialog with a new dialog of its own, MAY send the target |
| 56 | * User Agent an INVITE request containing a Replaces header field. The |
| 57 | * User Agent Client (UAC) places the Call-ID, to-tag, and from-tag |
| 58 | * information for the target dialog in a single Replaces header field |
| 59 | * and sends the new INVITE to the target. |
| 60 | * |
| 61 | * To initiate outgoing INVITE request with Replaces header, application |
| 62 | * would create the INVITE request with #pjsip_inv_invite(), then adds |
| 63 | * #pjsip_replaces_hdr instance into the request, filling up the Call-ID, |
| 64 | * To-tag, and From-tag properties of the header with the identification |
| 65 | * of the dialog to be replaced. Application may also optionally |
| 66 | * set the \a early_only property of the header to indicate that it only |
| 67 | * wants to replace early dialog. |
| 68 | * |
| 69 | * Note that when the outgoing INVITE request (with Replaces) is initiated |
| 70 | * from an incoming REFER request (as in Attended Call Transfer case), |
| 71 | * this process should be done rather more automatically by PJSIP. Upon |
| 72 | * receiving incoming incoming REFER request, normally these processes |
| 73 | * will be performed: |
| 74 | * - Application finds \a Refer-To header, |
| 75 | * - Application creates outgoing dialog/invite session, specifying |
| 76 | * the URI in the \a Refer-To header as the initial remote target, |
| 77 | * - The URI in the \a Refer-To header may contain header parameters such |
| 78 | * as \a Replaces and \a Require headers. |
| 79 | * - The dialog keeps the header fields in the header parameters |
| 80 | * of the URI, and the invite session would add these headers into |
| 81 | * the outgoing INVITE request. Because of this, the outgoing |
| 82 | * INVITE request will contain the \a Replaces and \a Require headers. |
| 83 | * |
| 84 | * |
| 85 | * For more information, please see the implementation of |
| 86 | * #pjsua_call_xfer_replaces() in \ref PJSUA_LIB source code. |
| 87 | * |
| 88 | * |
| 89 | * \subsection PJSIP_REPLACES_UAS_SUBSEC UAS Behavior: Receiving a Replaces Header |
| 90 | * |
| 91 | * The Replaces header contains information used to match an existing |
| 92 | * SIP dialog (call-id, to-tag, and from-tag). Upon receiving an INVITE |
| 93 | * with a Replaces header, the User Agent (UA) attempts to match this |
| 94 | * information with a confirmed or early dialog. |
| 95 | * |
| 96 | * In PJSIP, if application wants to process the Replaces header in the |
| 97 | * incoming INVITE request, it should call #pjsip_replaces_verify_request() |
| 98 | * before creating the INVITE session. The #pjsip_replaces_verify_request() |
| 99 | * function checks and verifies the request to see if Replaces request |
| 100 | * can be processed. To be more specific, it performs the following |
| 101 | * verification: |
| 102 | * - checks that Replaces header is present. If not, the function will |
| 103 | * return PJ_SUCCESS without doing anything. |
| 104 | * - checks that no duplicate Replaces headers are present, or otherwise |
| 105 | * it will return 400 "Bad Request" response. |
| 106 | * - checks for matching dialog and verifies that the invite session has |
| 107 | * the correct state, and may return 481 "Call/Transaction Does Not Exist", |
| 108 | * 603 "Declined", or 486 "Busy Here" according to the processing rules |
| 109 | * specified in RFC 3891. |
| 110 | * - if matching dialog with correct state is found, it will give PJ_SUCCESS |
| 111 | * status and return the matching dialog back to the application. |
| 112 | * |
| 113 | * The following pseudocode illustrates how application can process the |
| 114 | * incoming INVITE if it wants to support Replaces extension: |
| 115 | * |
| 116 | \code |
| 117 | // Incoming INVITE request handler |
| 118 | pj_bool_t on_rx_invite(pjsip_rx_data *rdata) |
| 119 | { |
| 120 | pjsip_dialog *dlg, *replaced_dlg; |
| 121 | pjsip_inv_session *inv; |
| 122 | pjsip_tx_data *response; |
| 123 | pj_status_t status; |
| 124 | |
| 125 | // Check whether Replaces header is present in the request and process accordingly. |
| 126 | // |
| 127 | status = pjsip_replaces_verify_request(rdata, &replaced_dlg, PJ_FALSE, &response); |
| 128 | if (status != PJ_SUCCESS) { |
| 129 | // Something wrong with Replaces request. |
| 130 | // |
| 131 | if (response) { |
| 132 | pjsip_endpt_send_response(endpt, rdata, response, NULL, NULL); |
| 133 | } else { |
| 134 | // Respond with 500 (Internal Server Error) |
| 135 | pjsip_endpt_respond_stateless(endpt, rdata, 500, NULL, NULL, NULL); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | // Create UAS Invite session as usual. |
| 140 | // |
| 141 | status = pjsip_dlg_create_uas(.., rdata, .., &dlg); |
| 142 | .. |
| 143 | status = pjsip_inv_create_uas(dlg, .., &inv); |
| 144 | |
| 145 | // Send initial 100 "Trying" to the INVITE request |
| 146 | // |
| 147 | status = pjsip_inv_initial_answer(inv, rdata, 100, ..., &response); |
| 148 | if (status == PJ_SUCCESS) |
| 149 | pjsip_inv_send_msg(inv, response); |
| 150 | |
| 151 | |
| 152 | // This is where processing is different between normal call |
| 153 | // (without Replaces) and call with Replaces. |
| 154 | // |
| 155 | if (replaced_dlg) { |
| 156 | pjsip_inv_session *replaced_inv; |
| 157 | |
| 158 | // Always answer the new INVITE with 200, regardless whether |
| 159 | // the replaced call is in early or confirmed state. |
| 160 | // |
| 161 | status = pjsip_inv_answer(inv, 200, NULL, NULL, &response); |
| 162 | if (status == PJ_SUCCESS) |
| 163 | pjsip_inv_send_msg(inv, response); |
| 164 | |
| 165 | |
| 166 | // Get the INVITE session associated with the replaced dialog. |
| 167 | // |
| 168 | replaced_inv = pjsip_dlg_get_inv_session(replaced_dlg); |
| 169 | |
| 170 | |
| 171 | // Disconnect the "replaced" INVITE session. |
| 172 | // |
| 173 | status = pjsip_inv_end_session(replaced_inv, PJSIP_SC_GONE, NULL, &tdata); |
| 174 | if (status == PJ_SUCCESS && tdata) |
| 175 | status = pjsip_inv_send_msg(replaced_inv, tdata); |
| 176 | |
| 177 | |
| 178 | // It's up to application to associate the new INVITE session |
| 179 | // with the old (now terminated) session. For example, application |
| 180 | // may assign the same User Interface object for the new INVITE |
| 181 | // session. |
| 182 | |
| 183 | } else { |
| 184 | // Process normal INVITE without Replaces. |
| 185 | ... |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | \endcode |
| 190 | * |
| 191 | * |
| 192 | * For a complete sample implementation, please see \a pjsua_call_on_incoming() |
| 193 | * function of \ref PJSUA_LIB in \a pjsua_call.c file. |
| 194 | * |
| 195 | * |
| 196 | * \section PJSIP_REPLACES_REFERENCE References |
| 197 | * |
| 198 | * References: |
| 199 | * - <A HREF="http://www.ietf.org/rfc/rfc3891.txt">RFC 3891: The Session |
| 200 | * Initiation Protocol (SIP) "Replaces" Header</A> |
| 201 | * - \ref PJSUA_XFER |
| 202 | */ |
| 203 | |
| 204 | PJ_BEGIN_DECL |
| 205 | |
| 206 | |
| 207 | /** |
| 208 | * Declaration of SIP Replaces header (RFC 3891). |
| 209 | */ |
| 210 | typedef struct pjsip_replaces_hdr |
| 211 | { |
| 212 | /** Standard header field. */ |
| 213 | PJSIP_DECL_HDR_MEMBER(struct pjsip_replaces_hdr); |
| 214 | |
| 215 | /** Call-Id */ |
| 216 | pj_str_t call_id; |
| 217 | |
| 218 | /** to-tag */ |
| 219 | pj_str_t to_tag; |
| 220 | |
| 221 | /** from-tag */ |
| 222 | pj_str_t from_tag; |
| 223 | |
| 224 | /** early-only? */ |
| 225 | pj_bool_t early_only; |
| 226 | |
| 227 | /** Other parameters */ |
| 228 | pjsip_param other_param; |
| 229 | |
| 230 | } pjsip_replaces_hdr; |
| 231 | |
| 232 | |
| 233 | |
| 234 | /** |
| 235 | * Initialize Replaces support in PJSIP. This would, among other things, |
| 236 | * register the header parser for Replaces header. |
| 237 | * |
| 238 | * @param endpt The endpoint instance. |
| 239 | * |
| 240 | * @return PJ_SUCCESS on success. |
| 241 | */ |
| 242 | PJ_DECL(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt); |
| 243 | |
| 244 | |
| 245 | /** |
| 246 | * Create Replaces header. |
| 247 | * |
| 248 | * @param pool Pool to allocate the header instance from. |
| 249 | * |
| 250 | * @return An empty Replaces header instance. |
| 251 | */ |
| 252 | PJ_DECL(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool); |
| 253 | |
| 254 | |
| 255 | /** |
| 256 | * Verify that incoming request with Replaces header can be processed. |
| 257 | * This function will perform all necessary checks according to RFC 3891 |
| 258 | * Section 3 "User Agent Server Behavior: Receiving a Replaces Header". |
| 259 | * |
| 260 | * @param rdata The incoming request to be verified. |
| 261 | * @param p_dlg On return, it will be filled with the matching |
| 262 | * dialog. |
| 263 | * @param lock_dlg Specifies whether this function should acquire lock |
| 264 | * to the matching dialog. If yes (and should be yes!), |
| 265 | * then application will need to release the dialog's |
| 266 | * lock with #pjsip_dlg_dec_lock() when the function |
| 267 | * returns PJ_SUCCESS and the \a p_dlg parameter is filled |
| 268 | * with the dialog instance. |
| 269 | * @param p_tdata Upon error, it will be filled with the final response |
| 270 | * to be sent to the request sender. |
| 271 | * |
| 272 | * @return The function returns the following: |
| 273 | * - If the request doesn't contain Replaces header, the |
| 274 | * function returns PJ_SUCCESS and \a p_dlg parameter |
| 275 | * will be set to NULL. |
| 276 | * - If the request contains Replaces header and a valid, |
| 277 | * matching dialog is found, the function returns |
| 278 | * PJ_SUCCESS and \a p_dlg parameter will be set to the |
| 279 | * matching dialog instance. |
| 280 | * - Upon error condition (as described by RFC 3891), the |
| 281 | * function returns non-PJ_SUCCESS, and \a p_tdata |
| 282 | * parameter SHOULD be set with a final response message |
| 283 | * to be sent to the sender of the request. |
| 284 | */ |
| 285 | PJ_DECL(pj_status_t) pjsip_replaces_verify_request(pjsip_rx_data *rdata, |
| 286 | pjsip_dialog **p_dlg, |
| 287 | pj_bool_t lock_dlg, |
| 288 | pjsip_tx_data **p_tdata); |
| 289 | |
| 290 | |
| 291 | |
| 292 | PJ_END_DECL |
| 293 | |
| 294 | |
| 295 | /** |
| 296 | * @} |
| 297 | */ |
| 298 | |
| 299 | |
| 300 | #endif /* __PJSIP_REPLACES_H__ */ |
| 301 | |