sip_caller_prefs.c 12.3 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
 * 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_caller_prefs.c
 * @brief SIP headers related to Caller Preferences
 *
28 29
 * Implementation of header classes for Caller-Preferences-related SIP
 * headers @AcceptContact, @RejectContact, and @RequestDisposition.
Pekka Pessi's avatar
Pekka Pessi committed
30 31 32 33 34 35 36 37 38
 *
 * @author Remeres Jacobs <remeres.jacobs@nokia.com>
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Th 23.01.2003
 */

#include "config.h"

39 40 41 42 43 44
/* 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
45 46 47 48
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
49
#include <assert.h>
Pekka Pessi's avatar
Pekka Pessi committed
50 51 52

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

53
/**@SIP_HEADER sip_request_disposition Request-Disposition Header
Pekka Pessi's avatar
Pekka Pessi committed
54 55
 *
 * The Request-Disposition header syntax is defined in
56 57
 * @RFC3841 section 10 as follows:
 *
Pekka Pessi's avatar
Pekka Pessi committed
58 59 60 61 62 63 64 65 66 67 68 69 70 71
 * @code
 *      Request-Disposition  =  ( "Request-Disposition" | "d" ) HCOLON
 *                              directive *(COMMA directive)
 *      directive            =  proxy-directive / cancel-directive /
 *                              fork-directive / recurse-directive /
 *                              parallel-directive / queue-directive)
 *      proxy-directive      =  "proxy" / "redirect"
 *      cancel-directive     =  "cancel" / "no-cancel"
 *      fork-directive       =  "fork" / "no-fork"
 *      recurse-directive    =  "recurse" / "no-recurse"
 *      parallel-directive   =  "parallel" / "sequential"
 *      queue-directive      =  "queue" / "no-queue"
 * @endcode
 *
72
 *
73
 * The parsed Request-Disposition header
74
 * is stored in #sip_request_disposition_t structure.
Pekka Pessi's avatar
Pekka Pessi committed
75 76
 */

77
/**@ingroup sip_request_disposition
Pekka Pessi's avatar
Pekka Pessi committed
78 79
 * @typedef typedef struct sip_request_disposition_s sip_request_disposition_t;
 *
80
 * The structure #sip_request_disposition_t contains representation of
81 82 83
 * @RequestDisposition header.
 *
 * The #sip_request_disposition_t is defined as follows:
Pekka Pessi's avatar
Pekka Pessi committed
84 85 86 87 88
 * @code
 * typedef struct sip_request_disposition_s
 * {
 *   sip_common_t        rd_common[1];   // Common fragment info
 *   sip_unknown_t      *rd_next;	 // Link to next (dummy)
89
 *   msg_param_t        *rd_items;       // List of directives
Pekka Pessi's avatar
Pekka Pessi committed
90 91 92 93 94 95
 * } sip_request_disposition_t;
 * @endcode
 */

static msg_xtra_f sip_request_disposition_dup_xtra;
static msg_dup_f sip_request_disposition_dup_one;
96 97
#define sip_request_disposition_update NULL

98
msg_hclass_t sip_request_disposition_class[] =
99 100
SIP_HEADER_CLASS(request_disposition, "Request-Disposition", "d", rd_items,
		 list, request_disposition);
Pekka Pessi's avatar
Pekka Pessi committed
101

102
issize_t sip_request_disposition_d(su_home_t *home, sip_header_t *h,
103
				   char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
104 105
{
  sip_request_disposition_t *rd = h->sh_request_disposition;
106

Pekka Pessi's avatar
Pekka Pessi committed
107 108 109 110
  return msg_commalist_d(home, &s, &rd->rd_items, msg_token_scan);
}


111
issize_t sip_request_disposition_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
112 113 114 115 116 117
{
  char *end = b + bsiz, *b0 = b;
  sip_request_disposition_t const *o = h->sh_request_disposition;

  assert(sip_is_request_disposition(h));

118
  MSG_COMMALIST_E(b, end, o->rd_items, flags);
Pekka Pessi's avatar
Pekka Pessi committed
119 120 121 122 123

  return b - b0;
}


124
isize_t sip_request_disposition_dup_xtra(sip_header_t const *h, isize_t offset)
Pekka Pessi's avatar
Pekka Pessi committed
125 126 127
{
  sip_request_disposition_t const *o = h->sh_request_disposition;

Pekka Pessi's avatar
Pekka Pessi committed
128
  MSG_PARAMS_SIZE(offset, o->rd_items);
Pekka Pessi's avatar
Pekka Pessi committed
129 130 131 132 133

  return offset;
}


134
/** Duplicate one #sip_request_disposition_t object */
135 136
char *sip_request_disposition_dup_one(sip_header_t *dst,
				      sip_header_t const *src,
137
				      char *b, isize_t xtra)
Pekka Pessi's avatar
Pekka Pessi committed
138 139 140 141 142 143 144 145
{
  char *end = b + xtra;
  sip_request_disposition_t *o_dst = dst->sh_request_disposition;
  sip_request_disposition_t const *o_src = src->sh_request_disposition;
  msg_param_t const **dst_items = (msg_param_t const **)&o_dst->rd_items;

  b = msg_params_dup(dst_items, o_src->rd_items, b, xtra);

146
  assert(b <= end); (void)end;
Pekka Pessi's avatar
Pekka Pessi committed
147 148 149 150 151 152

  return b;
}

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

153
/**@ingroup sip_caller_prefs
Pekka Pessi's avatar
Pekka Pessi committed
154
 *
155
 * Add a parameter to a @AcceptContact or @RejectContact header object.
Pekka Pessi's avatar
Pekka Pessi committed
156 157 158 159
 *
 * @note This function does not duplicate @p param.
 *
 * @param home   memory home
160
 * @param cp     pointer to #sip_accept_contact_t or #sip_reject_contact_t
Pekka Pessi's avatar
Pekka Pessi committed
161 162
 * @param param  parameter string
 *
163 164
 * @retval 0 when successful
 * @retval -1 upon an error
165 166
 *
 * @deprecated Use msg_header_replace_param() instead.
Pekka Pessi's avatar
Pekka Pessi committed
167 168 169 170 171
 */
int sip_caller_prefs_add_param(su_home_t *home,
			       sip_caller_prefs_t *cp,
			       char const *param)
{
172
  return  msg_header_replace_param(home, cp->cp_common, param);
Pekka Pessi's avatar
Pekka Pessi committed
173 174 175 176 177 178 179 180 181 182
}

static
size_t span_attribute_value(char *s)
{
  size_t n;

  n = span_token_lws(s);
  if (n > 0 && s[n] == '=') {
    n += 1 + span_lws(s + n + 1);
183
    if (s[n] == '"')
Pekka Pessi's avatar
Pekka Pessi committed
184 185 186 187 188 189 190 191 192 193
      n += span_quoted(s + n);
    else
      n += span_token(s + n);
    n += span_lws(s + n);
  }

  return n;
}

static
194 195
issize_t sip_caller_prefs_d(su_home_t *home, sip_header_t *h,
			    char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
196
{
197
  sip_caller_prefs_t *cp = (sip_caller_prefs_t *)h;
Pekka Pessi's avatar
Pekka Pessi committed
198 199
  url_t url[1];
  char const *ignore = NULL;
200
  int kludge = 0;
Pekka Pessi's avatar
Pekka Pessi committed
201 202

  assert(h);
203

204 205
  while (*s == ',')   /* Ignore empty entries (comma-whitespace) */
    *s = '\0', s += span_lws(s + 1) + 1;
Pekka Pessi's avatar
Pekka Pessi committed
206

207
  /* Kludge: support PoC IS spec with a typo... */
208
  if (su_casenmatch(s, "*,", 2))
209 210 211 212 213
    s[1] = ';',  kludge = 0;
  else if (s[0] != '*' && s[0] != '<') {
    /* Kludge: missing URL -  */
    size_t n = span_attribute_value(s);
    kludge = n > 0 && (s[n] == '\0' || s[n] == ',' || s[n] == ';');
Pekka Pessi's avatar
Pekka Pessi committed
214 215
  }

216 217 218 219 220 221 222 223
  if (kludge) {
    if (msg_any_list_d(home, &s, (msg_param_t **)&cp->cp_params,
		       msg_attribute_value_scanner, ';') == -1)
      return -1;
  }
  /* Parse params (and ignore display name and url) */
  else if (sip_name_addr_d(home, &s, &ignore, url, &cp->cp_params, NULL)
	   == -1)
Pekka Pessi's avatar
Pekka Pessi committed
224
    return -1;
225 226 227
  /* Be liberal... */
  /* if (url->url_type != url_any)
     return -1; */
Pekka Pessi's avatar
Pekka Pessi committed
228

229
  return msg_parse_next_field(home, h, s, slen);
Pekka Pessi's avatar
Pekka Pessi committed
230 231
}

232
static
233
issize_t sip_caller_prefs_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
234
{
Pekka Pessi's avatar
Pekka Pessi committed
235 236 237 238 239 240 241 242 243 244 245
  sip_caller_prefs_t const *cp = h->sh_caller_prefs;
  char *b0 = b, *end = b + bsiz;

  MSG_CHAR_E(b, end, '*');
  MSG_PARAMS_E(b, end, cp->cp_params, flags);
  MSG_TERM_E(b, end);

  return b - b0;
}


246
static
247
isize_t sip_caller_prefs_dup_xtra(sip_header_t const *h, isize_t offset)
Pekka Pessi's avatar
Pekka Pessi committed
248 249 250
{
  sip_caller_prefs_t const *cp = h->sh_caller_prefs;

251
  MSG_PARAMS_SIZE(offset, cp->cp_params);
Pekka Pessi's avatar
Pekka Pessi committed
252

253
  return offset;
Pekka Pessi's avatar
Pekka Pessi committed
254 255
}

256 257
static
char *sip_caller_prefs_dup_one(sip_header_t *dst, sip_header_t const *src,
258
			      char *b, isize_t xtra)
Pekka Pessi's avatar
Pekka Pessi committed
259 260 261 262 263
{
  char *end = b + xtra;
  sip_caller_prefs_t *cp = dst->sh_caller_prefs;
  sip_caller_prefs_t const *o = src->sh_caller_prefs;

Pekka Pessi's avatar
Pekka Pessi committed
264
  b = msg_params_dup(&cp->cp_params, o->cp_params, b, xtra);
Pekka Pessi's avatar
Pekka Pessi committed
265

266
  assert(b <= end); (void)end;
Pekka Pessi's avatar
Pekka Pessi committed
267 268 269 270

  return b;
}

271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
/**@SIP_HEADER sip_accept_contact Accept-Contact Header
 *
 * The Accept-Contact syntax is defined in @RFC3841 section 10 as follows:
 *
 * @code
 *    Accept-Contact  =  ("Accept-Contact" / "a") HCOLON ac-value
 *                       *(COMMA ac-value)
 *    ac-value        =  "*" *(SEMI ac-params)
 *    ac-params       =  feature-param / req-param
 *                        / explicit-param / generic-param
 *                        ;;feature param from RFC 3840
 *                        ;;generic-param from RFC 3261
 *    req-param       =  "require"
 *    explicit-param  =  "explicit"
 * @endcode
 *
 * Despite the BNF, there MUST NOT be more than one req-param or
 * explicit-param in an ac-params. Furthermore, there can only be one
 * instance of any feature tag in feature-param.
 *
 * @sa @RFC3840, @RFC3841, sip_contact_accept(), sip_contact_score().
 *
 * The parsed Accept-Contact header
 * is stored in #sip_accept_contact_t structure.
 */

/**@ingroup sip_accept_contact
 * @typedef struct sip_caller_prefs_s sip_accept_contact_t;
 *
 * The structure #sip_accept_contact_t contains representation of SIP
 * @AcceptContact header.
 *
 * The #sip_accept_contact_t is defined as follows:
 * @code
 * typedef struct caller_prefs_s {
 *   sip_common_t        cp_common[1];   // Common fragment info
 *   sip_caller_prefs_t *cp_next;        // Link to next ac-value
 *   msg_param_t const  *cp_params;      // List of parameters
 *   char const         *cp_q;           // Priority
 *   unsigned            cp_require :1;  // Shortcut to "require" parameter
 *   unsigned            cp_explicit :1; // Shortcut to "explicit" parameter
 * } sip_accept_contact_t;
 * @endcode
 */

316 317 318
#define sip_accept_contact_dup_xtra sip_caller_prefs_dup_xtra
#define sip_accept_contact_dup_one  sip_caller_prefs_dup_one

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
static int sip_accept_contact_update(msg_common_t *h,
				     char const *name, isize_t namelen,
				     char const *value);

msg_hclass_t sip_accept_contact_class[] =
SIP_HEADER_CLASS(accept_contact, "Accept-Contact", "a", cp_params, append,
		 accept_contact);

issize_t sip_accept_contact_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_caller_prefs_d(home, h, s, slen);
}


issize_t sip_accept_contact_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  return sip_caller_prefs_e(b, bsiz, h, flags);
}

static int sip_accept_contact_update(msg_common_t *h,
				     char const *name, isize_t namelen,
				     char const *value)
341 342 343 344 345 346 347 348
{
  sip_caller_prefs_t *cp = (sip_caller_prefs_t *)h;

  if (name == NULL) {
    cp->cp_q = NULL;
    cp->cp_require = 0;
    cp->cp_explicit = 0;
  }
349
#define MATCH(s) (namelen == strlen(#s) && su_casenmatch(name, #s, strlen(#s)))
350

351
#if nomore
352 353 354
  else if (MATCH(q)) {
    cp->cp_q = value;
  }
355
#endif
356 357 358 359 360 361 362 363 364 365 366 367
  else if (MATCH(require)) {
    cp->cp_require = value != NULL;
  }
  else if (MATCH(explicit)) {
    cp->cp_explicit = value != NULL;
  }

#undef MATCH

  return 0;
}

Pekka Pessi's avatar
Pekka Pessi committed
368

369
/**@SIP_HEADER sip_reject_contact Reject-Contact Header
Pekka Pessi's avatar
Pekka Pessi committed
370
 *
371
 * The Reject-Contact syntax is defined in @RFC3841 section 10 as follows:
Pekka Pessi's avatar
Pekka Pessi committed
372 373
 *
 * @code
374 375 376 377 378 379
 *    Reject-Contact  =  ("Reject-Contact" / "j") HCOLON rc-value
 *                       *(COMMA rc-value)
 *    rc-value        =  "*" *(SEMI rc-params)
 *    rc-params       =  feature-param / generic-param
 *                        ;;feature param from RFC 3840
 *                        ;;generic-param from RFC 3261
Pekka Pessi's avatar
Pekka Pessi committed
380
 * @endcode
381 382 383 384 385 386 387 388 389
 *
 * Despite the BNF, there MUST NOT be more than one instance of any feature
 * tag in feature-param.
 *
 * @sa @RFC3840, @RFC3841, sip_contact_reject(), sip_contact_score().
 *
 * The parsed Reject-Contact header
 * is stored in #sip_reject_contact_t structure.
 */
Pekka Pessi's avatar
Pekka Pessi committed
390

391 392
/**@ingroup sip_reject_contact
 * @typedef struct sip_caller_prefs_s sip_reject_contact_t;
Pekka Pessi's avatar
Pekka Pessi committed
393
 *
394 395 396 397
 * The structure #sip_reject_contact_t contains representation of SIP
 * @RejectContact header.
 *
 * The #sip_reject_contact_t is defined as follows:
Pekka Pessi's avatar
Pekka Pessi committed
398
 * @code
399
 * typedef struct caller_prefs_s {
Pekka Pessi's avatar
Pekka Pessi committed
400
 *   sip_common_t        cp_common[1];   // Common fragment info
401 402
 *   sip_caller_prefs_t *cp_next;        // Link to next rc-value
 *   msg_param_t const  *cp_params;      // List of parameters
Pekka Pessi's avatar
Pekka Pessi committed
403 404
 * } sip_reject_contact_t;
 * @endcode
405 406 407
 *
 * @note Fields @c cp_q, @c cp_require and @c cp_explicit are ignored for
 * @RejectContact header.
Pekka Pessi's avatar
Pekka Pessi committed
408 409
 */

410 411 412
#define sip_reject_contact_dup_xtra sip_caller_prefs_dup_xtra
#define sip_reject_contact_dup_one  sip_caller_prefs_dup_one
#define sip_reject_contact_update   NULL
413 414 415 416

msg_hclass_t sip_reject_contact_class[] =
SIP_HEADER_CLASS(reject_contact, "Reject-Contact", "j", cp_params, append,
		 reject_contact);
Pekka Pessi's avatar
Pekka Pessi committed
417

418
issize_t sip_reject_contact_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
Pekka Pessi's avatar
Pekka Pessi committed
419 420 421 422 423
{
  return sip_caller_prefs_d(home, h, s, slen);
}


424
issize_t sip_reject_contact_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
Pekka Pessi's avatar
Pekka Pessi committed
425 426 427
{
  return sip_caller_prefs_e(b, bsiz, h, flags);
}