123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- /*
- * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
- * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <pjsip-ua/sip_replaces.h>
- #include <pjsip-ua/sip_inv.h>
- #include <pjsip/print_util.h>
- #include <pjsip/sip_endpoint.h>
- #include <pjsip/sip_errno.h>
- #include <pjsip/sip_parser.h>
- #include <pjsip/sip_transport.h>
- #include <pjsip/sip_ua_layer.h>
- #include <pjsip/sip_util.h>
- #include <pj/assert.h>
- #include <pj/log.h>
- #include <pj/pool.h>
- #include <pj/string.h>
- #define THIS_FILE "sip_replaces.c"
- /*
- * Replaces header vptr.
- */
- static int replaces_hdr_print( pjsip_replaces_hdr *hdr,
- char *buf, pj_size_t size);
- static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool,
- const pjsip_replaces_hdr *hdr);
- static pjsip_replaces_hdr* replaces_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_replaces_hdr*);
- static pjsip_hdr_vptr replaces_hdr_vptr =
- {
- (pjsip_hdr_clone_fptr) &replaces_hdr_clone,
- (pjsip_hdr_clone_fptr) &replaces_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &replaces_hdr_print,
- };
- /* Globals */
- static pjsip_endpoint *the_endpt;
- static pj_bool_t is_initialized;
- PJ_DEF(pjsip_replaces_hdr*) pjsip_replaces_hdr_create(pj_pool_t *pool)
- {
- pjsip_replaces_hdr *hdr = PJ_POOL_ZALLOC_T(pool, pjsip_replaces_hdr);
- hdr->type = PJSIP_H_OTHER;
- hdr->name.ptr = "Replaces";
- hdr->name.slen = 8;
- hdr->vptr = &replaces_hdr_vptr;
- pj_list_init(hdr);
- pj_list_init(&hdr->other_param);
- return hdr;
- }
- static int replaces_hdr_print( pjsip_replaces_hdr *hdr,
- char *buf, pj_size_t size)
- {
- char *p = buf;
- char *endbuf = buf+size;
- pj_ssize_t printed;
- const pjsip_parser_const_t *pc = pjsip_parser_const();
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
- copy_advance(p, hdr->call_id);
- copy_advance_pair(p, ";to-tag=", 8, hdr->to_tag);
- copy_advance_pair(p, ";from-tag=", 10, hdr->from_tag);
- if (hdr->early_only) {
- const pj_str_t str_early_only = { ";early-only", 11 };
- copy_advance(p, str_early_only);
- }
-
- printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
- &pc->pjsip_TOKEN_SPEC,
- &pc->pjsip_TOKEN_SPEC, ';');
- if (printed < 0)
- return (int)printed;
- p += printed;
- return (int)(p - buf);
- }
- static pjsip_replaces_hdr* replaces_hdr_clone( pj_pool_t *pool,
- const pjsip_replaces_hdr *rhs)
- {
- pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(pool);
- pj_strdup(pool, &hdr->call_id, &rhs->call_id);
- pj_strdup(pool, &hdr->to_tag, &rhs->to_tag);
- pj_strdup(pool, &hdr->from_tag, &rhs->from_tag);
- hdr->early_only = rhs->early_only;
- pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
- }
- static pjsip_replaces_hdr*
- replaces_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_replaces_hdr *rhs )
- {
- pjsip_replaces_hdr *hdr = PJ_POOL_ALLOC_T(pool, pjsip_replaces_hdr);
- pj_memcpy(hdr, rhs, sizeof(*hdr));
- pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
- return hdr;
- }
- /*
- * Parse Replaces header.
- */
- static pjsip_hdr *parse_hdr_replaces(pjsip_parse_ctx *ctx)
- {
- pjsip_replaces_hdr *hdr = pjsip_replaces_hdr_create(ctx->pool);
- const pj_str_t to_tag = { "to-tag", 6 };
- const pj_str_t from_tag = { "from-tag", 8 };
- const pj_str_t early_only_tag = { "early-only", 10 };
- /*pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->call_id);*/
- /* Get Call-ID (until ';' is found). using pjsip_TOKEN_SPEC doesn't work
- * because it stops parsing when '@' character is found.
- */
- pj_scan_get_until_ch(ctx->scanner, ';', &hdr->call_id);
- while (*ctx->scanner->curptr == ';') {
- pj_str_t pname, pvalue;
- pj_scan_get_char(ctx->scanner);
- pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
- if (pj_stricmp(&pname, &to_tag)==0) {
- hdr->to_tag = pvalue;
- } else if (pj_stricmp(&pname, &from_tag)==0) {
- hdr->from_tag = pvalue;
- } else if (pj_stricmp(&pname, &early_only_tag)==0) {
- hdr->early_only = PJ_TRUE;
- } else {
- pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
- param->name = pname;
- param->value = pvalue;
- pj_list_push_back(&hdr->other_param, param);
- }
- }
- pjsip_parse_end_hdr_imp( ctx->scanner );
- return (pjsip_hdr*)hdr;
- }
- /* Deinitialize Replaces */
- static void pjsip_replaces_deinit_module(pjsip_endpoint *endpt)
- {
- PJ_TODO(provide_initialized_flag_for_each_endpoint);
- PJ_UNUSED_ARG(endpt);
- is_initialized = PJ_FALSE;
- }
- /*
- * Initialize Replaces support in PJSIP.
- */
- PJ_DEF(pj_status_t) pjsip_replaces_init_module(pjsip_endpoint *endpt)
- {
- pj_status_t status;
- const pj_str_t STR_REPLACES = { "replaces", 8 };
- the_endpt = endpt;
- if (is_initialized)
- return PJ_SUCCESS;
- /* Register Replaces header parser */
- status = pjsip_register_hdr_parser( "Replaces", NULL,
- &parse_hdr_replaces);
- if (status != PJ_SUCCESS)
- return status;
- /* Register "replaces" capability */
- status = pjsip_endpt_add_capability(endpt, NULL, PJSIP_H_SUPPORTED, NULL,
- 1, &STR_REPLACES);
- /* Register deinit module to be executed when PJLIB shutdown */
- if (pjsip_endpt_atexit(endpt, &pjsip_replaces_deinit_module) != PJ_SUCCESS)
- {
- /* Failure to register this function may cause this module won't
- * work properly when the stack is restarted (without quitting
- * application).
- */
- pj_assert(!"Failed to register Replaces deinit.");
- PJ_LOG(1, (THIS_FILE, "Failed to register Replaces deinit."));
- }
- is_initialized = PJ_TRUE;
- return PJ_SUCCESS;
- }
- /*
- * Verify that incoming request with Replaces header can be processed.
- */
- PJ_DEF(pj_status_t) pjsip_replaces_verify_request( pjsip_rx_data *rdata,
- pjsip_dialog **p_dlg,
- pj_bool_t lock_dlg,
- pjsip_tx_data **p_tdata)
- {
- const pj_str_t STR_REPLACES = { "Replaces", 8 };
- pjsip_replaces_hdr *rep_hdr;
- int code = 200;
- const char *warn_text = NULL;
- pjsip_hdr res_hdr_list;
- pjsip_dialog *dlg = NULL;
- pjsip_inv_session *inv;
- pj_status_t status = PJ_SUCCESS;
- PJ_ASSERT_RETURN(rdata && p_dlg, PJ_EINVAL);
- /* Check that pjsip_replaces_init_module() has been called. */
- PJ_ASSERT_RETURN(the_endpt != NULL, PJ_EINVALIDOP);
- /* Init output arguments */
- *p_dlg = NULL;
- if (p_tdata) *p_tdata = NULL;
- pj_list_init(&res_hdr_list);
- /* Find Replaces header */
- rep_hdr = (pjsip_replaces_hdr*)
- pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES,
- NULL);
- if (!rep_hdr) {
- /* No Replaces header. No further processing is necessary. */
- return PJ_SUCCESS;
- }
- /* Check that there's no other Replaces header and return 400 Bad Request
- * if not.
- */
- if (pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_REPLACES,
- rep_hdr->next)) {
- code = PJSIP_SC_BAD_REQUEST;
- warn_text = "Found multiple Replaces headers";
- goto on_return;
- }
- /* Find the dialog identified by Replaces header (and always lock the
- * dialog no matter what application wants).
- */
- dlg = pjsip_ua_find_dialog(&rep_hdr->call_id, &rep_hdr->to_tag,
- &rep_hdr->from_tag, PJ_TRUE);
- /* Respond with 481 "Call/Transaction Does Not Exist" response if
- * no dialog is found.
- */
- if (dlg == NULL) {
- code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
- warn_text = "No dialog found for Replaces request";
- goto on_return;
- }
- /* Get the invite session within the dialog */
- inv = pjsip_dlg_get_inv_session(dlg);
- /* Return 481 if no invite session is present. */
- if (inv == NULL) {
- code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
- warn_text = "No INVITE session found for Replaces request";
- goto on_return;
- }
- /* Return 603 Declined response if invite session has already
- * terminated
- */
- if (inv->state >= PJSIP_INV_STATE_DISCONNECTED) {
- code = PJSIP_SC_DECLINE;
- warn_text = "INVITE session already terminated";
- goto on_return;
- }
- /* If "early-only" flag is present, check that the invite session
- * has not been confirmed yet. If the session has been confirmed,
- * return 486 "Busy Here" response.
- */
- if (rep_hdr->early_only && inv->state >= PJSIP_INV_STATE_CONNECTING) {
- code = PJSIP_SC_BUSY_HERE;
- warn_text = "INVITE session already established";
- goto on_return;
- }
- /* If the Replaces header field matches an early dialog that was not
- * initiated by this UA, it returns a 481 (Call/Transaction Does Not
- * Exist) response to the new INVITE.
- */
- if (inv->state <= PJSIP_INV_STATE_EARLY && inv->role != PJSIP_ROLE_UAC)
- {
- /* Really return 481 only if call haven't reached early state or
- * accept-replace-in-early-state (ticket #1587) is not allowed.
- */
- if (inv->state != PJSIP_INV_STATE_EARLY ||
- pjsip_cfg()->endpt.accept_replace_in_early_state == PJ_FALSE)
- {
- code = PJSIP_SC_CALL_TSX_DOES_NOT_EXIST;
- warn_text = "Found early INVITE session but not initiated by "
- "this UA";
- goto on_return;
- }
- }
- /*
- * Looks like everything is okay!!
- */
- *p_dlg = dlg;
- status = PJ_SUCCESS;
- code = 200;
- on_return:
- /* Create response if necessary */
- if (code != 200) {
- /* If we have dialog we must unlock it */
- if (dlg)
- pjsip_dlg_dec_lock(dlg);
- /* Create response */
- if (p_tdata) {
- pjsip_tx_data *tdata;
- const pjsip_hdr *h;
- status = pjsip_endpt_create_response(the_endpt, rdata, code,
- NULL, &tdata);
- if (status != PJ_SUCCESS)
- return status;
- /* Add response headers. */
- h = res_hdr_list.next;
- while (h != &res_hdr_list) {
- pjsip_hdr *cloned;
- cloned = (pjsip_hdr*) pjsip_hdr_clone(tdata->pool, h);
- PJ_ASSERT_RETURN(cloned, PJ_ENOMEM);
- pjsip_msg_add_hdr(tdata->msg, cloned);
- h = h->next;
- }
- /* Add warn text, if any */
- if (warn_text) {
- pjsip_warning_hdr *warn_hdr;
- pj_str_t warn_value = pj_str((char*)warn_text);
- warn_hdr=pjsip_warning_hdr_create(tdata->pool, 399,
- pjsip_endpt_name(the_endpt),
- &warn_value);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)warn_hdr);
- }
- *p_tdata = tdata;
- }
- /* Can not return PJ_SUCCESS when response message is produced.
- * Ref: PROTOS test ~#2490
- */
- if (status == PJ_SUCCESS)
- status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
- } else {
- /* If application doesn't want to lock the dialog, unlock it */
- if (!lock_dlg)
- pjsip_dlg_dec_lock(dlg);
- }
- return status;
- }
|