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