blob: ae60fca8ca70a48bfaa87a16a1653cdf795cf35b [file] [log] [blame]
Benny Prijono053f5222006-11-11 16:16:04 +00001/* $Id$ */
2/*
Benny Prijono844653c2008-12-23 17:27:53 +00003 * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
Benny Prijono32177c02008-06-20 22:44:47 +00004 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
Benny Prijono053f5222006-11-11 16:16:04 +00005 *
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#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>
30#include <pj/pool.h>
31#include <pj/string.h>
32
33
34/*
35 * Replaces header vptr.
36 */
37static int replaces_hdr_print( pjsip_replaces_hdr *hdr,
38 char *buf, pj_size_t size);
39static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool,
40 const pjsip_replaces_hdr *hdr);
41static pjsip_replaces_hdr* replaces_hdr_shallow_clone( pj_pool_t *pool,
42 const pjsip_replaces_hdr*);
43
44static pjsip_hdr_vptr replaces_hdr_vptr =
45{
46 (pjsip_hdr_clone_fptr) &replaces_hdr_clone,
47 (pjsip_hdr_clone_fptr) &replaces_hdr_shallow_clone,
48 (pjsip_hdr_print_fptr) &replaces_hdr_print,
49};
50
51/* Globals */
52static pjsip_endpoint *the_endpt;
53
54PJ_DEF(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool)
55{
Benny Prijonoa1e69682007-05-11 15:14:34 +000056 pjsip_replaces_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_replaces_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +000057 hdr->type = PJSIP_H_OTHER;
58 hdr->name.ptr = "Replaces";
59 hdr->name.slen = 8;
60 hdr->vptr = &replaces_hdr_vptr;
61 pj_list_init(hdr);
62 pj_list_init(&hdr->other_param);
63 return hdr;
64}
65
66static int replaces_hdr_print( pjsip_replaces_hdr *hdr,
67 char *buf, pj_size_t size)
68{
69 char *p = buf;
70 char *endbuf = buf+size;
71 int printed;
Benny Prijono1f61a8f2007-08-16 10:11:44 +000072 const pjsip_parser_const_t *pc = pjsip_parser_const();
Benny Prijono053f5222006-11-11 16:16:04 +000073
74 copy_advance(p, hdr->name);
75 *p++ = ':';
76 *p++ = ' ';
77
78 copy_advance(p, hdr->call_id);
79 copy_advance_pair(p, ";to-tag=", 8, hdr->to_tag);
80 copy_advance_pair(p, ";from-tag=", 10, hdr->from_tag);
81
82 if (hdr->early_only) {
83 const pj_str_t str_early_only = { ";early-only", 11 };
84 copy_advance(p, str_early_only);
85 }
86
87 printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
Benny Prijono1f61a8f2007-08-16 10:11:44 +000088 &pc->pjsip_TOKEN_SPEC,
89 &pc->pjsip_TOKEN_SPEC, ';');
Benny Prijono053f5222006-11-11 16:16:04 +000090 if (printed < 0)
91 return printed;
92
93 p += printed;
94 return p - buf;
95}
96
97static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool,
98 const pjsip_replaces_hdr *rhs)
99{
100 pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(pool);
101 pj_strdup(pool, &hdr->call_id, &rhs->call_id);
102 pj_strdup(pool, &hdr->to_tag, &rhs->to_tag);
103 pj_strdup(pool, &hdr->from_tag, &rhs->from_tag);
104 hdr->early_only = rhs->early_only;
105 pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
106 return hdr;
107}
108
109static pjsip_replaces_hdr*
110replaces_hdr_shallow_clone( pj_pool_t *pool,
111 const pjsip_replaces_hdr *rhs )
112{
Benny Prijonoa1e69682007-05-11 15:14:34 +0000113 pjsip_replaces_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_replaces_hdr);
Benny Prijono053f5222006-11-11 16:16:04 +0000114 pj_memcpy(hdr, rhs, sizeof(*hdr));
115 pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
116 return hdr;
117}
118
119
120/*
121 * Parse Replaces header.
122 */
123static pjsip_hdr *parse_hdr_replaces(pjsip_parse_ctx *ctx)
124{
125 pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(ctx->pool);
126 const pj_str_t to_tag = { "to-tag", 6 };
127 const pj_str_t from_tag = { "from-tag", 8 };
128 const pj_str_t early_only_tag = { "early-only", 10 };
129
Benny Prijono75862ec2006-11-27 11:10:52 +0000130 /*pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->call_id);*/
131 /* Get Call-ID (until ';' is found). using pjsip_TOKEN_SPEC doesn't work
132 * because it stops parsing when '@' character is found.
133 */
134 pj_scan_get_until_ch(ctx->scanner, ';', &hdr->call_id);
Benny Prijono053f5222006-11-11 16:16:04 +0000135
136 while (*ctx->scanner->curptr == ';') {
137 pj_str_t pname, pvalue;
138
139 pj_scan_get_char(ctx->scanner);
140 pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
141
142 if (pj_stricmp(&pname, &to_tag)==0) {
143 hdr->to_tag = pvalue;
144 } else if (pj_stricmp(&pname, &from_tag)==0) {
145 hdr->from_tag = pvalue;
146 } else if (pj_stricmp(&pname, &early_only_tag)==0) {
147 hdr->early_only = PJ_TRUE;
148 } else {
Benny Prijonoa1e69682007-05-11 15:14:34 +0000149 pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
Benny Prijono053f5222006-11-11 16:16:04 +0000150 param->name = pname;
151 param->value = pvalue;
152 pj_list_push_back(&hdr->other_param, param);
153 }
154 }
155 pjsip_parse_end_hdr_imp( ctx->scanner );
156 return (pjsip_hdr*)hdr;
157}
158
159/*
160 * Initialize Replaces support in PJSIP.
161 */
162PJ_DEF(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt)
163{
164 pj_status_t status;
165 const pj_str_t STR_REPLACES = { "replaces", 8 };
166 static pj_bool_t is_initialized;
167
168 the_endpt = endpt;
169
170 if (is_initialized)
171 return PJ_SUCCESS;
172
173 /* Register Replaces header parser */
174 status = pjsip_register_hdr_parser( "Replaces", NULL,
175 &parse_hdr_replaces);
176 if (status != PJ_SUCCESS)
177 return status;
178
179 /* Register "replaces" capability */
180 status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL,
181 1, &STR_REPLACES);
182
183 is_initialized = PJ_TRUE;
184 return PJ_SUCCESS;
185}
186
187
188/*
189 * Verify that incoming request with Replaces header can be processed.
190 */
191PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata,
192 pjsip_dialog **p_dlg,
193 pj_bool_t lock_dlg,
194 pjsip_tx_data **p_tdata)
195{
196 const pj_str_t STR_REPLACES = { "Replaces", 8 };
197 pjsip_replaces_hdr *rep_hdr;
198 int code = 200;
199 const char *warn_text = NULL;
200 pjsip_hdr res_hdr_list;
201 pjsip_dialog *dlg = NULL;
202 pjsip_inv_session *inv;
203 pj_status_t status = PJ_SUCCESS;
204
205 PJ_ASSERT_RETURN(rdata && p_dlg, PJ_EINVAL);
206
207 /* Check that pjsip_replaces_init_module() has been called. */
208 PJ_ASSERT_RETURN(the_endpt != NULL, PJ_EINVALIDOP);
209
210
211 /* Init output arguments */
212 *p_dlg = NULL;
213 if (p_tdata) *p_tdata = NULL;
214
215 pj_list_init(&res_hdr_list);
216
217 /* Find Replaces header */
218 rep_hdr = (pjsip_replaces_hdr*)
219 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES,
220 NULL);
221 if (!rep_hdr) {
222 /* No Replaces header. No further processing is necessary. */
223 return PJ_SUCCESS;
224 }
225
226
227 /* Check that there's no other Replaces header and return 400 Bad Request
228 * if not.
229 */
230 if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES,
231 rep_hdr->next)) {
232 code = PJSIP_SC_BAD_REQUEST;
233 warn_text = "Found multiple Replaces headers";
234 goto on_return;
235 }
236
237 /* Find the dialog identified by Replaces header (and always lock the
238 * dialog no matter what application wants).
239 */
240 dlg = pjsip_ua_find_dialog(&rep_hdr->call_id, &rep_hdr->to_tag,
241 &rep_hdr->from_tag, PJ_TRUE);
242
243 /* Respond with 481 "Call/Transaction Does Not Exist" response if
244 * no dialog is found.
245 */
246 if (dlg == NULL) {
247 code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
248 warn_text = "No dialog found for Replaces request";
249 goto on_return;
250 }
251
252 /* Get the invite session within the dialog */
253 inv = pjsip_dlg_get_inv_session(dlg);
254
255 /* Return 481 if no invite session is present. */
256 if (inv == NULL) {
257 code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
258 warn_text = "No INVITE session found for Replaces request";
259 goto on_return;
260 }
261
262 /* Return 603 Declined response if invite session has already
263 * terminated
264 */
265 if (inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
266 code = PJSIP_SC_DECLINE;
267 warn_text = "INVITE session already terminated";
268 goto on_return;
269 }
270
271 /* If "early-only" flag is present, check that the invite session
272 * has not been confirmed yet. If the session has been confirmed,
273 * return 486 "Busy Here" response.
274 */
275 if (rep_hdr->early_only && inv->state >= PJSIP_INV_STATE_CONNECTING) {
276 code = PJSIP_SC_BUSY_HERE;
277 warn_text = "INVITE session already established";
278 goto on_return;
279 }
280
281 /* If the Replaces header field matches an early dialog that was not
282 * initiated by this UA, it returns a 481 (Call/Transaction Does Not
283 * Exist) response to the new INVITE.
284 */
285 if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC) {
286 code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
287 warn_text = "Found early INVITE session but not initiated by this UA";
288 goto on_return;
289 }
290
291
292 /*
293 * Looks like everything is okay!!
294 */
295 *p_dlg = dlg;
296 status = PJ_SUCCESS;
297 code = 200;
298
299on_return:
300
301 /* Create response if necessary */
302 if (code != 200) {
303 /* If we have dialog we must unlock it */
304 if (dlg)
305 pjsip_dlg_dec_lock(dlg);
306
307 /* Create response */
308 if (p_tdata) {
309 pjsip_tx_data *tdata;
310 const pjsip_hdr *h;
311
312 status = pjsip_endpt_create_response(the_endpt, rdata, code,
313 NULL, &tdata);
314
315 if (status != PJ_SUCCESS)
316 return status;
317
318 /* Add response headers. */
319 h = res_hdr_list.next;
320 while (h != &res_hdr_list) {
321 pjsip_hdr *cloned;
322
Benny Prijonoa1e69682007-05-11 15:14:34 +0000323 cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h);
Benny Prijono053f5222006-11-11 16:16:04 +0000324 PJ_ASSERT_RETURN(cloned, PJ_ENOMEM);
325
326 pjsip_msg_add_hdr(tdata->msg, cloned);
327
328 h = h->next;
329 }
330
331 /* Add warn text, if any */
332 if (warn_text) {
333 pjsip_warning_hdr *warn_hdr;
334 pj_str_t warn_value = pj_str((char*)warn_text);
335
336 warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399,
337 pjsip_endpt_name(the_endpt),
338 &warn_value);
339 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr);
340 }
341
342 *p_tdata = tdata;
343 }
344
345 /* Can not return PJ_SUCCESS when response message is produced.
346 * Ref: PROTOS test ~#2490
347 */
348 if (status == PJ_SUCCESS)
349 status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
350
351 } else {
352 /* If application doesn't want to lock the dialog, unlock it */
353 if (!lock_dlg)
354 pjsip_dlg_dec_lock(dlg);
355 }
356
357 return status;
358}
359
360
361