~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.2.1/pjsip/src/pjsip-ua/sip_replaces.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: sip_replaces.c 4537 2013-06-19 06:47:43Z riza $ */
 
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
#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/log.h>
 
31
#include <pj/pool.h>
 
32
#include <pj/string.h>
 
33
 
 
34
#define THIS_FILE               "sip_replaces.c"
 
35
 
 
36
 
 
37
/*
 
38
 * Replaces header vptr.
 
39
 */
 
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*);
 
46
 
 
47
static pjsip_hdr_vptr replaces_hdr_vptr = 
 
48
{
 
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,
 
52
};
 
53
 
 
54
/* Globals */
 
55
static pjsip_endpoint *the_endpt;
 
56
static pj_bool_t is_initialized;
 
57
 
 
58
PJ_DEF(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool)
 
59
{
 
60
    pjsip_replaces_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_replaces_hdr);
 
61
    hdr->type = PJSIP_H_OTHER;
 
62
    hdr->name.ptr = "Replaces";
 
63
    hdr->name.slen = 8;
 
64
    hdr->vptr = &replaces_hdr_vptr;
 
65
    pj_list_init(hdr);
 
66
    pj_list_init(&hdr->other_param);
 
67
    return hdr;
 
68
}
 
69
 
 
70
static int replaces_hdr_print( pjsip_replaces_hdr *hdr, 
 
71
                               char *buf, pj_size_t size)
 
72
{
 
73
    char *p = buf;
 
74
    char *endbuf = buf+size;
 
75
    pj_ssize_t printed;
 
76
    const pjsip_parser_const_t *pc = pjsip_parser_const();
 
77
 
 
78
    copy_advance(p, hdr->name);
 
79
    *p++ = ':';
 
80
    *p++ = ' ';
 
81
 
 
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);
 
85
 
 
86
    if (hdr->early_only) {
 
87
        const pj_str_t str_early_only = { ";early-only", 11 };
 
88
        copy_advance(p, str_early_only);
 
89
    }
 
90
    
 
91
    printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
 
92
                                   &pc->pjsip_TOKEN_SPEC, 
 
93
                                   &pc->pjsip_TOKEN_SPEC, ';');
 
94
    if (printed < 0)
 
95
        return (int)printed;
 
96
 
 
97
    p += printed;
 
98
    return (int)(p - buf);
 
99
}
 
100
 
 
101
static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool, 
 
102
                                               const pjsip_replaces_hdr *rhs)
 
103
{
 
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);
 
110
    return hdr;
 
111
}
 
112
 
 
113
static pjsip_replaces_hdr* 
 
114
replaces_hdr_shallow_clone( pj_pool_t *pool,
 
115
                            const pjsip_replaces_hdr *rhs )
 
116
{
 
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);
 
120
    return hdr;
 
121
}
 
122
 
 
123
 
 
124
/*
 
125
 * Parse Replaces header.
 
126
 */
 
127
static pjsip_hdr *parse_hdr_replaces(pjsip_parse_ctx *ctx)
 
128
{
 
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 };
 
133
 
 
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.
 
137
     */
 
138
    pj_scan_get_until_ch(ctx->scanner, ';', &hdr->call_id);
 
139
 
 
140
    while (*ctx->scanner->curptr == ';') {
 
141
        pj_str_t pname, pvalue;
 
142
 
 
143
        pj_scan_get_char(ctx->scanner);
 
144
        pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
 
145
 
 
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;
 
152
        } else {
 
153
            pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
 
154
            param->name = pname;
 
155
            param->value = pvalue;
 
156
            pj_list_push_back(&hdr->other_param, param);
 
157
        }
 
158
    }
 
159
    pjsip_parse_end_hdr_imp( ctx->scanner );
 
160
    return (pjsip_hdr*)hdr;
 
161
}
 
162
 
 
163
 
 
164
/* Deinitialize Replaces */
 
165
static void pjsip_replaces_deinit_module(pjsip_endpoint *endpt)
 
166
{
 
167
    PJ_TODO(provide_initialized_flag_for_each_endpoint);
 
168
    PJ_UNUSED_ARG(endpt);
 
169
    is_initialized = PJ_FALSE;
 
170
}
 
171
 
 
172
/*
 
173
 * Initialize Replaces support in PJSIP. 
 
174
 */
 
175
PJ_DEF(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt)
 
176
{
 
177
    pj_status_t status;
 
178
    const pj_str_t STR_REPLACES = { "replaces", 8 };
 
179
 
 
180
    the_endpt = endpt;
 
181
 
 
182
    if (is_initialized)
 
183
        return PJ_SUCCESS;
 
184
 
 
185
    /* Register Replaces header parser */
 
186
    status = pjsip_register_hdr_parser( "Replaces", NULL, 
 
187
                                        &parse_hdr_replaces);
 
188
    if (status != PJ_SUCCESS)
 
189
        return status;
 
190
 
 
191
    /* Register "replaces" capability */
 
192
    status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL,
 
193
                                        1, &STR_REPLACES);
 
194
 
 
195
    /* Register deinit module to be executed when PJLIB shutdown */
 
196
    if (pjsip_endpt_atexit(endpt, &pjsip_replaces_deinit_module) != PJ_SUCCESS)
 
197
    {
 
198
        /* Failure to register this function may cause this module won't 
 
199
         * work properly when the stack is restarted (without quitting 
 
200
         * application).
 
201
         */
 
202
        pj_assert(!"Failed to register Replaces deinit.");
 
203
        PJ_LOG(1, (THIS_FILE, "Failed to register Replaces deinit."));
 
204
    }
 
205
 
 
206
    is_initialized = PJ_TRUE;
 
207
    return PJ_SUCCESS;
 
208
}
 
209
 
 
210
 
 
211
/*
 
212
 * Verify that incoming request with Replaces header can be processed.
 
213
 */
 
214
PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata,
 
215
                                                   pjsip_dialog **p_dlg,
 
216
                                                   pj_bool_t lock_dlg,
 
217
                                                   pjsip_tx_data **p_tdata)
 
218
{
 
219
    const pj_str_t STR_REPLACES = { "Replaces", 8 };
 
220
    pjsip_replaces_hdr *rep_hdr;
 
221
    int code = 200;
 
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;
 
227
 
 
228
    PJ_ASSERT_RETURN(rdata && p_dlg, PJ_EINVAL);
 
229
 
 
230
    /* Check that pjsip_replaces_init_module() has been called. */
 
231
    PJ_ASSERT_RETURN(the_endpt != NULL, PJ_EINVALIDOP);
 
232
 
 
233
 
 
234
    /* Init output arguments */
 
235
    *p_dlg = NULL;
 
236
    if (p_tdata) *p_tdata = NULL;
 
237
 
 
238
    pj_list_init(&res_hdr_list);
 
239
 
 
240
    /* Find Replaces header */
 
241
    rep_hdr = (pjsip_replaces_hdr*) 
 
242
              pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES, 
 
243
                                         NULL);
 
244
    if (!rep_hdr) {
 
245
        /* No Replaces header. No further processing is necessary. */
 
246
        return PJ_SUCCESS;
 
247
    }
 
248
 
 
249
 
 
250
    /* Check that there's no other Replaces header and return 400 Bad Request
 
251
     * if not. 
 
252
     */
 
253
    if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES, 
 
254
                                   rep_hdr->next)) {
 
255
        code = PJSIP_SC_BAD_REQUEST;
 
256
        warn_text = "Found multiple Replaces headers";
 
257
        goto on_return;
 
258
    }
 
259
 
 
260
    /* Find the dialog identified by Replaces header (and always lock the
 
261
     * dialog no matter what application wants).
 
262
     */
 
263
    dlg = pjsip_ua_find_dialog(&rep_hdr->call_id, &rep_hdr->to_tag,
 
264
                               &rep_hdr->from_tag, PJ_TRUE);
 
265
 
 
266
    /* Respond with 481 "Call/Transaction Does Not Exist" response if
 
267
     * no dialog is found.
 
268
     */
 
269
    if (dlg == NULL) {
 
270
        code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
 
271
        warn_text = "No dialog found for Replaces request";
 
272
        goto on_return;
 
273
    }
 
274
 
 
275
    /* Get the invite session within the dialog */
 
276
    inv = pjsip_dlg_get_inv_session(dlg);
 
277
 
 
278
    /* Return 481 if no invite session is present. */
 
279
    if (inv == NULL) {
 
280
        code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
 
281
        warn_text = "No INVITE session found for Replaces request";
 
282
        goto on_return;
 
283
    }
 
284
 
 
285
    /* Return 603 Declined response if invite session has already 
 
286
     * terminated 
 
287
     */
 
288
    if (inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
 
289
        code = PJSIP_SC_DECLINE;
 
290
        warn_text = "INVITE session already terminated";
 
291
        goto on_return;
 
292
    }
 
293
 
 
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.
 
297
     */
 
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";
 
301
        goto on_return;
 
302
    }
 
303
 
 
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.
 
307
     */
 
308
    if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC)
 
309
    {
 
310
        /* Really return 481 only if call haven't reached early state or
 
311
         * accept-replace-in-early-state (ticket #1587) is not allowed.
 
312
         */
 
313
        if (inv->state != PJSIP_INV_STATE_EARLY ||
 
314
            pjsip_cfg()->endpt.accept_replace_in_early_state == PJ_FALSE)
 
315
        {
 
316
            code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
 
317
            warn_text = "Found early INVITE session but not initiated by "
 
318
                        "this UA";
 
319
            goto on_return;
 
320
        }
 
321
    }
 
322
 
 
323
 
 
324
    /*
 
325
     * Looks like everything is okay!!
 
326
     */
 
327
    *p_dlg = dlg;
 
328
    status = PJ_SUCCESS;
 
329
    code = 200;
 
330
 
 
331
on_return:
 
332
 
 
333
    /* Create response if necessary */
 
334
    if (code != 200) {
 
335
        /* If we have dialog we must unlock it */
 
336
        if (dlg)
 
337
            pjsip_dlg_dec_lock(dlg);
 
338
 
 
339
        /* Create response */
 
340
        if (p_tdata) {
 
341
            pjsip_tx_data *tdata;
 
342
            const pjsip_hdr *h;
 
343
 
 
344
            status = pjsip_endpt_create_response(the_endpt, rdata, code, 
 
345
                                                 NULL, &tdata);
 
346
 
 
347
            if (status != PJ_SUCCESS)
 
348
                return status;
 
349
 
 
350
            /* Add response headers. */
 
351
            h = res_hdr_list.next;
 
352
            while (h != &res_hdr_list) {
 
353
                pjsip_hdr *cloned;
 
354
 
 
355
                cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h);
 
356
                PJ_ASSERT_RETURN(cloned, PJ_ENOMEM);
 
357
 
 
358
                pjsip_msg_add_hdr(tdata->msg, cloned);
 
359
 
 
360
                h = h->next;
 
361
            }
 
362
 
 
363
            /* Add warn text, if any */
 
364
            if (warn_text) {
 
365
                pjsip_warning_hdr *warn_hdr;
 
366
                pj_str_t warn_value = pj_str((char*)warn_text);
 
367
 
 
368
                warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399, 
 
369
                                                  pjsip_endpt_name(the_endpt),
 
370
                                                  &warn_value);
 
371
                pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr);
 
372
            }
 
373
 
 
374
            *p_tdata = tdata;
 
375
        }
 
376
 
 
377
        /* Can not return PJ_SUCCESS when response message is produced.
 
378
         * Ref: PROTOS test ~#2490
 
379
         */
 
380
        if (status == PJ_SUCCESS)
 
381
            status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
 
382
 
 
383
    } else {
 
384
        /* If application doesn't want to lock the dialog, unlock it */
 
385
        if (!lock_dlg)
 
386
            pjsip_dlg_dec_lock(dlg);
 
387
    }
 
388
 
 
389
    return status;
 
390
}
 
391
 
 
392
 
 
393