sip_refer.c 11.5 KB
Newer Older
Pekka Pessi's avatar
Pekka Pessi committed
1 2 3 4 5 6 7
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
8
 * This library is free software; you can redistribute it and/or
Pekka Pessi's avatar
Pekka Pessi committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@CFILE sip_refer.c
 * @brief SIP REFER-related headers.
 *
 * The file @b sip_refer.c contains implementation of header classes for
29
 * REFER-related SIP headers @ReferTo and @ReferredBy.
Pekka Pessi's avatar
Pekka Pessi committed
30 31 32 33 34 35 36 37
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Wed Jan 23 13:23:45 EET 2002 ppessi
 */

#include "config.h"

38 39 40 41 42 43
/* Avoid casting sip_t to msg_pub_t and sip_header_t to msg_header_t */
#define MSG_PUB_T       struct sip_s
#define MSG_HDR_T       union sip_header_u

#include "sofia-sip/sip_parser.h"

Pekka Pessi's avatar
Pekka Pessi committed
44 45 46 47 48 49 50 51 52 53
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/* ====================================================================== */

/**@SIP_HEADER sip_refer_to Refer-To Header
 *
 * The Refer-To header provides a URI to reference. Its syntax is defined in
54
 * @RFC3515 section 2.1 as follows:
Pekka Pessi's avatar
Pekka Pessi committed
55 56 57 58 59 60
 *
 * @code
 *  Refer-To = ("Refer-To" / "r") HCOLON ( name-addr / addr-spec )
 *            *(SEMI generic-param)
 * @endcode
 *
61 62
 *
 * The parsed Refer-To header is stored in #sip_refer_to_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
63 64 65 66 67 68
 */

/**@ingroup sip_refer_to
 *
 * @typedef typedef struct sip_refer_to_s sip_refer_to_t;
 *
69
 * The structure #sip_refer_to_t contains representation of @ReferTo
Pekka Pessi's avatar
Pekka Pessi committed
70 71
 * header.
 *
72
 * The #sip_refer_to_t is defined as follows:
Pekka Pessi's avatar
Pekka Pessi committed
73 74 75 76 77 78 79
 * @code
 * typedef struct sip_refer_to_s
 * {
 *   sip_common_t        r_common[1];   // Common fragment info
 *   sip_error_t        *r_next;	// Link to next (dummy)
 *   char const          r_display;     // Display name
 *   url_t               r_url[1];	// URI to reference
80
 *   msg_param_t const  *r_params;      // List of generic parameters
Pekka Pessi's avatar
Pekka Pessi committed
81 82 83 84 85 86
 * } sip_refer_to_t;
 * @endcode
 */

static msg_xtra_f sip_refer_to_dup_xtra;
static msg_dup_f sip_refer_to_dup_one;
87
#define sip_refer_to_update NULL
Pekka Pessi's avatar
Pekka Pessi committed
88 89 90 91

msg_hclass_t sip_refer_to_class[] =
SIP_HEADER_CLASS(refer_to, "Refer-To", "r", r_params, single, refer_to);

92
issize_t sip_refer_to_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
93 94 95
{
  sip_refer_to_t *r = h->sh_refer_to;

Pekka Pessi's avatar
Pekka Pessi committed
96
  return sip_name_addr_d(home, &s,
Pekka Pessi's avatar
Pekka Pessi committed
97 98 99 100 101 102
			 &r->r_display,
			 r->r_url,
			 &r->r_params,
			 NULL);
}

103
issize_t sip_refer_to_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
104 105 106 107 108
{
  sip_refer_to_t const *r = h->sh_refer_to;

  assert(sip_is_refer_to(h));

Pekka Pessi's avatar
Pekka Pessi committed
109
  return sip_name_addr_e(b, bsiz, flags,
Pekka Pessi's avatar
Pekka Pessi committed
110 111 112 113 114 115
			 r->r_display, MSG_IS_CANONIC(flags),
			 r->r_url,
			 r->r_params,
			 NULL);
}

116
isize_t sip_refer_to_dup_xtra(sip_header_t const *h, isize_t offset)
Pekka Pessi's avatar
Pekka Pessi committed
117 118 119
{
  sip_refer_to_t const *r = h->sh_refer_to;

120 121 122
  MSG_PARAMS_SIZE(offset, r->r_params);
  offset += MSG_STRING_SIZE(r->r_display);
  offset += url_xtra(r->r_url);
Pekka Pessi's avatar
Pekka Pessi committed
123

124
  return offset;
Pekka Pessi's avatar
Pekka Pessi committed
125 126
}

127
/** Duplicate one #sip_refer_to_t object */
Pekka Pessi's avatar
Pekka Pessi committed
128
char *sip_refer_to_dup_one(sip_header_t *dst, sip_header_t const *src,
129
			   char *b, isize_t xtra)
Pekka Pessi's avatar
Pekka Pessi committed
130 131 132 133 134 135
{
  sip_refer_to_t *r_dst = dst->sh_refer_to;
  sip_refer_to_t const *r_src = src->sh_refer_to;

  char *end = b + xtra;

Pekka Pessi's avatar
Pekka Pessi committed
136 137
  b = msg_params_dup(&r_dst->r_params, r_src->r_params, b, xtra);
  MSG_STRING_DUP(b, r_dst->r_display, r_src->r_display);
Pekka Pessi's avatar
Pekka Pessi committed
138 139 140 141 142 143 144 145 146 147 148 149 150
  URL_DUP(b, end, r_dst->r_url, r_src->r_url);

  assert(b <= end);

  return b;
}

/* ====================================================================== */

/**@SIP_HEADER sip_referred_by Referred-By Header
 *
 * The Referred-By header conveys the identity of the original referrer to
 * the referred-to party. Its syntax is defined in
151
 * @RFC3892 section 3 as follows:
Pekka Pessi's avatar
Pekka Pessi committed
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
 *
 * @code
 *    Referred-By  =  ("Referred-By" / "b") HCOLON referrer-uri
 *                   *( SEMI (referredby-id-param / generic-param) )
 *
 *    referrer-uri = ( name-addr / addr-spec )
 *
 *    referredby-id-param = "cid" EQUAL sip-clean-msg-id
 *
 *    sip-clean-msg-id = LDQUOT dot-atom "@" (dot-atom / host) RDQUOT
 *
 *    dot-atom = atom *( "." atom )
 *
 *    atom     = 1*( alphanum / "-" / "!" / "%" / "*" /
 *                        "_" / "+" / "'" / "`" / "~"   )
 * @endcode
 *
169 170
 *
 * The parsed Referred-By header is stored in #sip_referred_by_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
171 172 173 174 175 176
 */

/**@ingroup sip_referred_by
 *
 * @typedef typedef struct sip_referred_by_s sip_referred_by_t;
 *
177
 * The structure #sip_referred_by_t contains representation of @ReferredBy
Pekka Pessi's avatar
Pekka Pessi committed
178 179
 * header.
 *
180
 * The #sip_referred_by_t is defined as follows:
Pekka Pessi's avatar
Pekka Pessi committed
181 182 183 184 185 186 187
 * @code
 * typedef struct sip_referred_by_s
 * {
 *   sip_common_t        b_common[1];   // Common fragment info
 *   sip_error_t        *b_next;	// Link to next (dummy)
 *   char const          b_display,
 *   url_t               b_url[1];	// Referrer-URI
Pekka Pessi's avatar
Pekka Pessi committed
188
 *   msg_param_t const  *b_params;      // List of parameters
189
 *   char const         *b_cid;
Pekka Pessi's avatar
Pekka Pessi committed
190 191 192 193 194 195
 * } sip_referred_by_t;
 * @endcode
 */

static msg_xtra_f sip_referred_by_dup_xtra;
static msg_dup_f sip_referred_by_dup_one;
196
static msg_update_f sip_referred_by_update;
Pekka Pessi's avatar
Pekka Pessi committed
197 198 199 200 201

msg_hclass_t sip_referred_by_class[] =
SIP_HEADER_CLASS(referred_by, "Referred-By", "b", b_params, single,
		 referred_by);

202
issize_t sip_referred_by_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
203 204 205
{
  sip_referred_by_t *b = h->sh_referred_by;

Pekka Pessi's avatar
Pekka Pessi committed
206
  if (sip_name_addr_d(home, &s,
Pekka Pessi's avatar
Pekka Pessi committed
207 208 209 210 211 212
		      &b->b_display,
		      b->b_url,
		      &b->b_params,
		      NULL) < 0)
    return -1;

213 214
  if (b->b_params)
    msg_header_update_params(b->b_common, 0);
Pekka Pessi's avatar
Pekka Pessi committed
215 216 217 218

  return 0;
}

219
issize_t sip_referred_by_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
220 221 222
{
  assert(sip_is_referred_by(h));

Pekka Pessi's avatar
Pekka Pessi committed
223
  return sip_name_addr_e(b, bsiz, flags,
Pekka Pessi's avatar
Pekka Pessi committed
224 225 226 227 228 229
			 h->sh_referred_by->b_display,
			 MSG_IS_CANONIC(flags), h->sh_referred_by->b_url,
			 h->sh_referred_by->b_params,
			 NULL);
}

230
isize_t sip_referred_by_dup_xtra(sip_header_t const *h, isize_t offset)
Pekka Pessi's avatar
Pekka Pessi committed
231 232 233
{
  sip_referred_by_t const *b = h->sh_referred_by;

234 235 236
  MSG_PARAMS_SIZE(offset, b->b_params);
  offset += MSG_STRING_SIZE(b->b_display);
  offset += url_xtra(b->b_url);
Pekka Pessi's avatar
Pekka Pessi committed
237

238
  return offset;
Pekka Pessi's avatar
Pekka Pessi committed
239 240 241 242
}

char *sip_referred_by_dup_one(sip_header_t *dst, sip_header_t const *src,
			      char *b,
243
			      isize_t xtra)
Pekka Pessi's avatar
Pekka Pessi committed
244 245 246 247 248
{
  sip_referred_by_t *nb = dst->sh_referred_by;
  sip_referred_by_t const *o = src->sh_referred_by;
  char *end = b + xtra;

Pekka Pessi's avatar
Pekka Pessi committed
249 250
  b = msg_params_dup(&nb->b_params, o->b_params, b, xtra);
  MSG_STRING_DUP(b, nb->b_display, o->b_display);
Pekka Pessi's avatar
Pekka Pessi committed
251 252 253 254 255 256 257 258 259
  URL_DUP(b, end, nb->b_url, o->b_url);

  nb->b_cid = msg_params_find(nb->b_params, "cid=");

  assert(b <= end);

  return b;
}

260
static int sip_referred_by_update(msg_common_t *h, 
261
			   char const *name, isize_t namelen,
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
			   char const *value)
{
  sip_referred_by_t *b = (sip_referred_by_t *)h;

  if (name == NULL) {
    b->b_cid = NULL;
  }
  else if (namelen == strlen("cid") && !strncasecmp(name, "cid", namelen)) {
    b->b_cid = value;
  }

  return 0;
}


Pekka Pessi's avatar
Pekka Pessi committed
277 278 279 280 281 282 283
/* ====================================================================== */

/**@SIP_HEADER sip_replaces Replaces Header
 *
 * The Replaces header indicates that a single dialog identified by the
 * header field is to be shut down and logically replaced by the incoming
 * INVITE in which it is contained. Its syntax is defined in
284
 * @RFC3891 section 6.1 as follows:
Pekka Pessi's avatar
Pekka Pessi committed
285 286 287 288 289 290 291 292 293
 *
 * @code
 *    Replaces        = "Replaces" HCOLON callid *(SEMI replaces-param)
 *    replaces-param  = to-tag / from-tag / early-flag / generic-param
 *    to-tag          = "to-tag" EQUAL token
 *    from-tag        = "from-tag" EQUAL token
 *    early-flag      = "early-only"
 * @endcode
 *
294 295
 * A Replaces header field MUST contain exactly one <to-tag> and exactly
 * one <from-tag>, as they are required for unique dialog matching.  For
296
 * compatibility with dialogs initiated by @RFC2543 compliant UAs, a
297 298 299 300 301 302
 * tag of zero ("0") matches both tags of zero and null.  A Replaces header
 * field MAY contain the <early-only> flag.
 *
 * The parsed Replaces header is stored in #sip_replaces_t structure.
 *
 * @sa @RFC3891, nta_leg_by_replaces(), nta_leg_make_replaces()
Pekka Pessi's avatar
Pekka Pessi committed
303 304 305 306 307 308
 */

/**@ingroup sip_replaces
 *
 * @typedef typedef struct sip_replaces_s sip_replaces_t;
 *
309
 * The structure #sip_replaces_t contains representation of @Replaces
Pekka Pessi's avatar
Pekka Pessi committed
310 311
 * header.
 *
312
 * The #sip_replaces_t is defined as follows:
Pekka Pessi's avatar
Pekka Pessi committed
313 314 315 316
 * @code
 * typedef struct sip_replaces_s
 * {
 *   sip_common_t        rp_common[1];   // Common fragment info
317 318 319 320 321 322
 *   sip_error_t        *rp_next;	 // Dummy link to next
 *   char const         *rp_call_id;     // @CallID of dialog to replace 
 *   msg_param_t const  *rp_params;      // List of parameters 
 *   char const         *rp_to_tag;      // Value of "to-tag" parameter 
 *   char const         *rp_from_tag;    // Value of "from-tag" parameter 
 *   unsigned            rp_early_only;  // early-only parameter
Pekka Pessi's avatar
Pekka Pessi committed
323 324 325 326 327 328
 * } sip_replaces_t;
 * @endcode
 */

static msg_xtra_f sip_replaces_dup_xtra;
static msg_dup_f sip_replaces_dup_one;
329
static msg_update_f sip_replaces_update;
Pekka Pessi's avatar
Pekka Pessi committed
330 331 332 333

msg_hclass_t sip_replaces_class[] =
SIP_HEADER_CLASS(replaces, "Replaces", "", rp_params, single, replaces);

334
/** Decode (parse) @Replaces header */
335
issize_t sip_replaces_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
336 337 338 339 340 341 342 343 344
{
  sip_replaces_t *rp = h->sh_replaces;

  rp->rp_call_id = sip_word_at_word_d(&s);
  if (!rp->rp_call_id)
    return -1;
  if (*s) {
    if (msg_params_d(home, &s, &rp->rp_params) == -1)
      return -1;
345
    msg_header_update_params(rp->rp_common, 0);
Pekka Pessi's avatar
Pekka Pessi committed
346 347 348 349 350
  }

  return s - rp->rp_call_id;
}

351
/** Encode (print) @Replaces header */
352
issize_t sip_replaces_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
353 354 355 356 357 358 359 360 361 362 363 364
{
  char *b0 = b, *end = b + bsiz;
  sip_replaces_t const *rp = h->sh_replaces;

  assert(sip_is_replaces(h));
  MSG_STRING_E(b, end, rp->rp_call_id);
  MSG_PARAMS_E(b, end, rp->rp_params, flags);
  MSG_TERM_E(b, end);

  return b - b0;
}

365
/** Calculate extra storage used by @Replaces header field */
366
isize_t sip_replaces_dup_xtra(sip_header_t const *h, isize_t offset)
Pekka Pessi's avatar
Pekka Pessi committed
367 368 369
{
  sip_replaces_t const *rp = h->sh_replaces;

370 371
  MSG_PARAMS_SIZE(offset, rp->rp_params);
  offset += MSG_STRING_SIZE(rp->rp_call_id);
Pekka Pessi's avatar
Pekka Pessi committed
372

373
  return offset;
Pekka Pessi's avatar
Pekka Pessi committed
374 375
}

376
/** Duplicate a @Replaces header field */
Pekka Pessi's avatar
Pekka Pessi committed
377
char *sip_replaces_dup_one(sip_header_t *dst, sip_header_t const *src,
378
			   char *b, isize_t xtra)
Pekka Pessi's avatar
Pekka Pessi committed
379 380 381 382 383 384
{
  sip_replaces_t *rp_dst = dst->sh_replaces;
  sip_replaces_t const *rp_src = src->sh_replaces;

  char *end = b + xtra;

Pekka Pessi's avatar
Pekka Pessi committed
385 386
  b = msg_params_dup(&rp_dst->rp_params, rp_src->rp_params, b, xtra);
  MSG_STRING_DUP(b, rp_dst->rp_call_id, rp_src->rp_call_id);
Pekka Pessi's avatar
Pekka Pessi committed
387 388 389 390 391 392

  assert(b <= end);

  return b;
}

393
/** Update parameters in @Replaces header. */
394
static int sip_replaces_update(msg_common_t *h, 
395
			       char const *name, isize_t namelen,
396
			       char const *value)
Pekka Pessi's avatar
Pekka Pessi committed
397
{
398 399 400 401 402 403
  sip_replaces_t *rp = (sip_replaces_t *)h;

  if (name == NULL) {
    rp->rp_to_tag = NULL;
    rp->rp_from_tag = NULL;
    rp->rp_early_only = 0;
Pekka Pessi's avatar
Pekka Pessi committed
404
  }
405
#define MATCH(s) (namelen == strlen(#s) && !strncasecmp(name, #s, strlen(#s)))
Pekka Pessi's avatar
Pekka Pessi committed
406

407 408 409 410 411 412 413 414 415 416 417 418 419 420
  else if (MATCH(to-tag)) {
    rp->rp_to_tag = value;
  }
  else if (MATCH(from-tag)) {
    rp->rp_from_tag = value;
  }
  else if (MATCH(early-only)) {
    rp->rp_early_only = value != NULL;
  }

#undef MATCH

  return 0;
}