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