1
/* $Id: sip_replaces.c 4268 2012-09-28 08:56:08Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
20
#include <pjsip-ua/sip_replaces.h>
21
#include <pjsip-ua/sip_inv.h>
22
#include <pjsip/print_util.h>
23
#include <pjsip/sip_endpoint.h>
24
#include <pjsip/sip_errno.h>
25
#include <pjsip/sip_parser.h>
26
#include <pjsip/sip_transport.h>
27
#include <pjsip/sip_ua_layer.h>
28
#include <pjsip/sip_util.h>
29
#include <pj/assert.h>
32
#include <pj/string.h>
34
#define THIS_FILE "sip_replaces.c"
38
* Replaces header vptr.
40
static int replaces_hdr_print( pjsip_replaces_hdr *hdr,
41
char *buf, pj_size_t size);
42
static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool,
43
const pjsip_replaces_hdr *hdr);
44
static pjsip_replaces_hdr* replaces_hdr_shallow_clone( pj_pool_t *pool,
45
const pjsip_replaces_hdr*);
47
static pjsip_hdr_vptr replaces_hdr_vptr =
49
(pjsip_hdr_clone_fptr) &replaces_hdr_clone,
50
(pjsip_hdr_clone_fptr) &replaces_hdr_shallow_clone,
51
(pjsip_hdr_print_fptr) &replaces_hdr_print,
55
static pjsip_endpoint *the_endpt;
56
static pj_bool_t is_initialized;
58
PJ_DEF(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool)
60
pjsip_replaces_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_replaces_hdr);
61
hdr->type = PJSIP_H_OTHER;
62
hdr->name.ptr = "Replaces";
64
hdr->vptr = &replaces_hdr_vptr;
66
pj_list_init(&hdr->other_param);
70
static int replaces_hdr_print( pjsip_replaces_hdr *hdr,
71
char *buf, pj_size_t size)
74
char *endbuf = buf+size;
76
const pjsip_parser_const_t *pc = pjsip_parser_const();
78
copy_advance(p, hdr->name);
82
copy_advance(p, hdr->call_id);
83
copy_advance_pair(p, ";to-tag=", 8, hdr->to_tag);
84
copy_advance_pair(p, ";from-tag=", 10, hdr->from_tag);
86
if (hdr->early_only) {
87
const pj_str_t str_early_only = { ";early-only", 11 };
88
copy_advance(p, str_early_only);
91
printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
92
&pc->pjsip_TOKEN_SPEC,
93
&pc->pjsip_TOKEN_SPEC, ';');
101
static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool,
102
const pjsip_replaces_hdr *rhs)
104
pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(pool);
105
pj_strdup(pool, &hdr->call_id, &rhs->call_id);
106
pj_strdup(pool, &hdr->to_tag, &rhs->to_tag);
107
pj_strdup(pool, &hdr->from_tag, &rhs->from_tag);
108
hdr->early_only = rhs->early_only;
109
pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
113
static pjsip_replaces_hdr*
114
replaces_hdr_shallow_clone( pj_pool_t *pool,
115
const pjsip_replaces_hdr *rhs )
117
pjsip_replaces_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_replaces_hdr);
118
pj_memcpy(hdr, rhs, sizeof(*hdr));
119
pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
125
* Parse Replaces header.
127
static pjsip_hdr *parse_hdr_replaces(pjsip_parse_ctx *ctx)
129
pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(ctx->pool);
130
const pj_str_t to_tag = { "to-tag", 6 };
131
const pj_str_t from_tag = { "from-tag", 8 };
132
const pj_str_t early_only_tag = { "early-only", 10 };
134
/*pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->call_id);*/
135
/* Get Call-ID (until ';' is found). using pjsip_TOKEN_SPEC doesn't work
136
* because it stops parsing when '@' character is found.
138
pj_scan_get_until_ch(ctx->scanner, ';', &hdr->call_id);
140
while (*ctx->scanner->curptr == ';') {
141
pj_str_t pname, pvalue;
143
pj_scan_get_char(ctx->scanner);
144
pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
146
if (pj_stricmp(&pname, &to_tag)==0) {
147
hdr->to_tag = pvalue;
148
} else if (pj_stricmp(&pname, &from_tag)==0) {
149
hdr->from_tag = pvalue;
150
} else if (pj_stricmp(&pname, &early_only_tag)==0) {
151
hdr->early_only = PJ_TRUE;
153
pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
155
param->value = pvalue;
156
pj_list_push_back(&hdr->other_param, param);
159
pjsip_parse_end_hdr_imp( ctx->scanner );
160
return (pjsip_hdr*)hdr;
164
/* Deinitialize Replaces */
165
static void pjsip_replaces_deinit_module(pjsip_endpoint *endpt)
167
PJ_TODO(provide_initialized_flag_for_each_endpoint);
168
PJ_UNUSED_ARG(endpt);
169
is_initialized = PJ_FALSE;
173
* Initialize Replaces support in PJSIP.
175
PJ_DEF(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt)
178
const pj_str_t STR_REPLACES = { "replaces", 8 };
185
/* Register Replaces header parser */
186
status = pjsip_register_hdr_parser( "Replaces", NULL,
187
&parse_hdr_replaces);
188
if (status != PJ_SUCCESS)
191
/* Register "replaces" capability */
192
status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL,
195
/* Register deinit module to be executed when PJLIB shutdown */
196
if (pjsip_endpt_atexit(endpt, &pjsip_replaces_deinit_module) != PJ_SUCCESS)
198
/* Failure to register this function may cause this module won't
199
* work properly when the stack is restarted (without quitting
202
pj_assert(!"Failed to register Replaces deinit.");
203
PJ_LOG(1, (THIS_FILE, "Failed to register Replaces deinit."));
206
is_initialized = PJ_TRUE;
212
* Verify that incoming request with Replaces header can be processed.
214
PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata,
215
pjsip_dialog **p_dlg,
217
pjsip_tx_data **p_tdata)
219
const pj_str_t STR_REPLACES = { "Replaces", 8 };
220
pjsip_replaces_hdr *rep_hdr;
222
const char *warn_text = NULL;
223
pjsip_hdr res_hdr_list;
224
pjsip_dialog *dlg = NULL;
225
pjsip_inv_session *inv;
226
pj_status_t status = PJ_SUCCESS;
228
PJ_ASSERT_RETURN(rdata && p_dlg, PJ_EINVAL);
230
/* Check that pjsip_replaces_init_module() has been called. */
231
PJ_ASSERT_RETURN(the_endpt != NULL, PJ_EINVALIDOP);
234
/* Init output arguments */
236
if (p_tdata) *p_tdata = NULL;
238
pj_list_init(&res_hdr_list);
240
/* Find Replaces header */
241
rep_hdr = (pjsip_replaces_hdr*)
242
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES,
245
/* No Replaces header. No further processing is necessary. */
250
/* Check that there's no other Replaces header and return 400 Bad Request
253
if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES,
255
code = PJSIP_SC_BAD_REQUEST;
256
warn_text = "Found multiple Replaces headers";
260
/* Find the dialog identified by Replaces header (and always lock the
261
* dialog no matter what application wants).
263
dlg = pjsip_ua_find_dialog(&rep_hdr->call_id, &rep_hdr->to_tag,
264
&rep_hdr->from_tag, PJ_TRUE);
266
/* Respond with 481 "Call/Transaction Does Not Exist" response if
267
* no dialog is found.
270
code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
271
warn_text = "No dialog found for Replaces request";
275
/* Get the invite session within the dialog */
276
inv = pjsip_dlg_get_inv_session(dlg);
278
/* Return 481 if no invite session is present. */
280
code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
281
warn_text = "No INVITE session found for Replaces request";
285
/* Return 603 Declined response if invite session has already
288
if (inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
289
code = PJSIP_SC_DECLINE;
290
warn_text = "INVITE session already terminated";
294
/* If "early-only" flag is present, check that the invite session
295
* has not been confirmed yet. If the session has been confirmed,
296
* return 486 "Busy Here" response.
298
if (rep_hdr->early_only && inv->state >= PJSIP_INV_STATE_CONNECTING) {
299
code = PJSIP_SC_BUSY_HERE;
300
warn_text = "INVITE session already established";
304
/* If the Replaces header field matches an early dialog that was not
305
* initiated by this UA, it returns a 481 (Call/Transaction Does Not
306
* Exist) response to the new INVITE.
308
if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC)
310
/* Really return 481 only if call haven't reached early state or
311
* accept-replace-in-early-state (ticket #1587) is not allowed.
313
if (inv->state != PJSIP_INV_STATE_EARLY ||
314
pjsip_cfg()->endpt.accept_replace_in_early_state == PJ_FALSE)
316
code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
317
warn_text = "Found early INVITE session but not initiated by "
325
* Looks like everything is okay!!
333
/* Create response if necessary */
335
/* If we have dialog we must unlock it */
337
pjsip_dlg_dec_lock(dlg);
339
/* Create response */
341
pjsip_tx_data *tdata;
344
status = pjsip_endpt_create_response(the_endpt, rdata, code,
347
if (status != PJ_SUCCESS)
350
/* Add response headers. */
351
h = res_hdr_list.next;
352
while (h != &res_hdr_list) {
355
cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h);
356
PJ_ASSERT_RETURN(cloned, PJ_ENOMEM);
358
pjsip_msg_add_hdr(tdata->msg, cloned);
363
/* Add warn text, if any */
365
pjsip_warning_hdr *warn_hdr;
366
pj_str_t warn_value = pj_str((char*)warn_text);
368
warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399,
369
pjsip_endpt_name(the_endpt),
371
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr);
377
/* Can not return PJ_SUCCESS when response message is produced.
378
* Ref: PROTOS test ~#2490
380
if (status == PJ_SUCCESS)
381
status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
384
/* If application doesn't want to lock the dialog, unlock it */
386
pjsip_dlg_dec_lock(dlg);