sip_parser.c 16.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 29 30 31 32 33 34 35 36
 * 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
 *
 */

/**@ingroup sip_parser
 * @CFILE sip_parser.c
 *
 * SIP parser.
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
 *
 * @date Created: Thu Oct  5 14:01:24 2000 ppessi
 */

#include "config.h"

37 38 39 40 41
/* 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/su_tagarg.h>
42
#include <sofia-sip/su_string.h>
43 44 45
#include "sofia-sip/sip_parser.h"
#include <sofia-sip/msg_mclass.h>

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

53 54 55 56
#ifndef UINT32_MAX
#define UINT32_MAX (0xffffffffU)
#endif

Pekka Pessi's avatar
Pekka Pessi committed
57 58 59 60 61 62 63
/** Version of the SIP module */
char const sip_parser_version[] = VERSION;

/** SIP version 2.0. */
char const sip_version_2_0[] = "SIP/2.0";

/** Default message class */
64
extern msg_mclass_t sip_mclass[];
Pekka Pessi's avatar
Pekka Pessi committed
65

66 67 68
static msg_mclass_t const *_default = sip_mclass;

/** Return a built-in SIP parser object. */
69
msg_mclass_t const *sip_default_mclass(void)
Pekka Pessi's avatar
Pekka Pessi committed
70
{
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
  return _default;
}

/** Update the default SIP parser.
 *
 * Use the extended SIP parser as default one.
 *
 * If the applications want to use headers added after @VERSION_1_12_5,
 * they should call this function before doing any other initialization, e.g.,
 * @code
 *   su_init();
 *   if (sip_update_default_mclass(sip_extend_mclass(NULL)) < 0) {
 *     su_deinit();
 *     exit(2);
 *   }
 * @endcode
 *
 * The default parser is not extended because it may break the old
 * applications looking for extension headers from sip_unknown list.
 *
 * @retval 0 when successful
 * @retval -1 upon an error
 *
94 95
 * @sa sip_extend_mclass()
 *
Pekka Pessi's avatar
Pekka Pessi committed
96
 * @NEW_1_12_7.
97 98 99 100 101 102 103 104 105 106 107 108
 */
int sip_update_default_mclass(msg_mclass_t const *mclass)
{
  if (mclass == NULL)
    return -1;
  _default = mclass;
  return 0;
}

/**Extend SIP parser class with extension headers.
 *
 * Extend given SIP parser class with extension headers. If the given parser
109 110
 * (message class) is the default one or NULL, make a clone of default
 * parser before extending it.
111 112 113 114 115
 *
 * @param input pointer to a SIP message class (may be NULL)
 *
 * @return Pointer to extended mclass, or NULL upon an error.
 *
116 117 118 119 120 121 122 123 124
 * @sa
 * @AlertInfo,
 * @ReplyTo,
 * @RemotePartyId,
 * @PAssertedIdentity,
 * @PPreferredIdentity,
 * @SuppressBodyIfMatch,
 * @SuppressNotifyIfMatch
 *
Pekka Pessi's avatar
Pekka Pessi committed
125
 * @NEW_1_12_7.
126 127 128 129 130 131 132 133 134 135 136
 */
msg_mclass_t *sip_extend_mclass(msg_mclass_t *input)
{
  msg_mclass_t *mclass;

  if (input == NULL || input == _default)
    mclass = msg_mclass_clone(_default, 0, 0);
  else
    mclass = input;

  if (mclass) {
137
    extern msg_hclass_t * const sip_extensions[];
138 139 140
    int i;

    for (i = 0; sip_extensions[i]; i++) {
141
      msg_hclass_t *hclass = sip_extensions[i];
142 143 144 145 146 147 148 149 150 151 152 153
      if (mclass->mc_unknown != msg_find_hclass(mclass, hclass->hc_name, NULL))
	continue;

      if (msg_mclass_insert_header(mclass, hclass, 0) < 0) {
	if (input != mclass)
	  free(mclass);
	return mclass = NULL;
      }
    }
  }

  return mclass;
Pekka Pessi's avatar
Pekka Pessi committed
154 155
}

156
/** Extract the SIP message body, including separator line.
Pekka Pessi's avatar
Pekka Pessi committed
157 158 159 160 161 162 163 164 165
 *
 * @param msg  message object [IN]
 * @param sip  public SIP message structure [IN/OUT]
 * @param b    buffer containing unparsed data [IN]
 * @param bsiz buffer size [IN]
 * @param eos  true if buffer contains whole message [IN]
 *
 * @retval -1 error
 * @retval 0  cannot proceed
166
 * @retval m
Pekka Pessi's avatar
Pekka Pessi committed
167
 */
168
issize_t sip_extract_body(msg_t *msg, sip_t *sip, char b[], isize_t bsiz, int eos)
Pekka Pessi's avatar
Pekka Pessi committed
169
{
170 171
  ssize_t m = 0;
  size_t body_len;
172

Pekka Pessi's avatar
Pekka Pessi committed
173 174
  if (!(sip->sip_flags & MSG_FLG_BODY)) {
    /* We are looking at a potential empty line */
Pekka Pessi's avatar
Pekka Pessi committed
175
    m = msg_extract_separator(msg, (msg_pub_t *)sip, b, bsiz, eos);
Pekka Pessi's avatar
Pekka Pessi committed
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
    if (m <= 0)
      return m;
    sip->sip_flags |= MSG_FLG_BODY;
    b += m;
    bsiz -= m;
  }

  if (sip->sip_content_length)
    body_len = sip->sip_content_length->l_length;
  else if (MSG_IS_MAILBOX(sip->sip_flags)) /* message fragments */
    body_len = 0;
  else if (eos)
    body_len = bsiz;
  else if (bsiz == 0)
    return m;
  else
    return -1;

  if (body_len == 0) {
    sip->sip_flags |= MSG_FLG_COMPLETE;
    return m;
  }

  if (m)
    return m;

  if (eos && body_len > bsiz) {
    sip->sip_flags |= MSG_FLG_TRUNC | MSG_FLG_ERROR;
    return bsiz;
  }

207
  if ((m = msg_extract_payload(msg, (msg_pub_t *)sip,
Pekka Pessi's avatar
Pekka Pessi committed
208
			       NULL, body_len, b, bsiz, eos)) == -1)
Pekka Pessi's avatar
Pekka Pessi committed
209
    return -1;
210

Pekka Pessi's avatar
Pekka Pessi committed
211 212 213 214 215 216 217 218 219
  sip->sip_flags |= MSG_FLG_FRAGS;
  if (bsiz >= body_len)
    sip->sip_flags |= MSG_FLG_COMPLETE;

  return m;
}

/** Parse SIP version.
 *
220
 * Parse a SIP version string. Update the
Pekka Pessi's avatar
Pekka Pessi committed
221 222 223 224 225 226 227 228 229 230 231 232
 * pointer at @a ss to first non-LWS character after the version string.
 *
 * @param ss   string to be parsed [IN/OUT]
 * @param ver  value result for version [OUT]
 *
 * @retval 0 when successful,
 * @retval -1 upon an error.
 */
int sip_version_d(char **ss, char const **ver)
{
  char *s = *ss;
  char const *result;
233
  size_t const version_size = sizeof(sip_version_2_0) - 1;
Pekka Pessi's avatar
Pekka Pessi committed
234

235
  if (su_casenmatch(s, sip_version_2_0, version_size) &&
Pekka Pessi's avatar
Pekka Pessi committed
236 237 238 239 240 241
      !IS_TOKEN(s[version_size])) {
    result = sip_version_2_0;
    s += version_size;
  }
  else {
    /* Version consists of two tokens, separated by / */
242
    size_t l1 = 0, l2 = 0, n;
Pekka Pessi's avatar
Pekka Pessi committed
243 244 245 246

    result = s;

    l1 = span_token(s);
Kai Samposalo's avatar
Kai Samposalo committed
247 248
    for (n = l1; IS_LWS(s[n]); n++)
      {}
Pekka Pessi's avatar
Pekka Pessi committed
249
    if (s[n] == '/') {
Kai Samposalo's avatar
Kai Samposalo committed
250 251
      for (n++; IS_LWS(s[n]); n++)
        {}
Pekka Pessi's avatar
Pekka Pessi committed
252 253 254 255 256 257 258 259 260 261 262 263 264
      l2 = span_token(s + n);
      n += l2;
    }

    if (l1 == 0 || l2 == 0)
      return -1;

    /* If there is extra ws between tokens, compact version */
    if (n > l1 + 1 + l2) {
      s[l1] = '/';
      memmove(s + l1 + 1, s + n - l2, l2);
      s[l1 + 1 + l2] = 0;

265
      /* Compare again with compacted version */
266
      if (su_casematch(s, sip_version_2_0))
Pekka Pessi's avatar
Pekka Pessi committed
267 268 269 270 271 272 273
	result = sip_version_2_0;
    }

    s += n;
  }

  while (IS_WS(*s)) *s++ = '\0';
274

Pekka Pessi's avatar
Pekka Pessi committed
275 276
  *ss = s;

277
  if (ver)
Pekka Pessi's avatar
Pekka Pessi committed
278 279 280 281 282 283
    *ver = result;

  return 0;
}

/** Calculate extra space required by version string */
284
isize_t sip_version_xtra(char const *version)
Pekka Pessi's avatar
Pekka Pessi committed
285 286 287
{
  if (version == SIP_VERSION_CURRENT)
    return 0;
Pekka Pessi's avatar
Pekka Pessi committed
288
  return MSG_STRING_SIZE(version);
Pekka Pessi's avatar
Pekka Pessi committed
289 290 291 292 293 294 295 296
}

/** Duplicate a transport string */
void sip_version_dup(char **pp, char const **dd, char const *s)
{
  if (s == SIP_VERSION_CURRENT)
    *dd = s;
  else
Pekka Pessi's avatar
Pekka Pessi committed
297
    MSG_STRING_DUP(*pp, *dd, s);
Pekka Pessi's avatar
Pekka Pessi committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
}

char const sip_method_name_invite[] =  	 "INVITE";
char const sip_method_name_ack[] =     	 "ACK";
char const sip_method_name_cancel[] =  	 "CANCEL";
char const sip_method_name_bye[] =     	 "BYE";
char const sip_method_name_options[] = 	 "OPTIONS";
char const sip_method_name_register[] =  "REGISTER";
char const sip_method_name_info[] =      "INFO";
char const sip_method_name_prack[] =     "PRACK";
char const sip_method_name_update[] =    "UPDATE";
char const sip_method_name_message[] =   "MESSAGE";
char const sip_method_name_subscribe[] = "SUBSCRIBE";
char const sip_method_name_notify[] =    "NOTIFY";
char const sip_method_name_refer[] =     "REFER";
char const sip_method_name_publish[] =   "PUBLISH";

/** Well-known SIP method names. */
char const * const sip_method_names[] = {
  "<UNKNOWN>",
  sip_method_name_invite,
  sip_method_name_ack,
  sip_method_name_cancel,
  sip_method_name_bye,
  sip_method_name_options,
  sip_method_name_register,
  sip_method_name_info,
  sip_method_name_prack,
  sip_method_name_update,
  sip_method_name_message,
  sip_method_name_subscribe,
  sip_method_name_notify,
  sip_method_name_refer,
  sip_method_name_publish,
  /* If you add something here, add also them to sip_method_d! */
  NULL
};

/** Get canonic method name. */
char const *sip_method_name(sip_method_t method, char const *name)
{
  const size_t N = sizeof(sip_method_names)/sizeof(sip_method_names[0]);
340
  if (method > 0 && (size_t)method < N)
Pekka Pessi's avatar
Pekka Pessi committed
341 342 343 344 345 346 347 348 349
    return sip_method_names[method];
  else if (method == 0)
    return name;
  else
    return NULL;
}

/**Parse a SIP method name.
 *
350
 * Parse a SIP method name and return a code corresponding to the method.
351 352
 * The address of the first non-LWS character after method name is stored in
 * @a *ss.
Pekka Pessi's avatar
Pekka Pessi committed
353 354 355 356 357
 *
 * @param ss    pointer to pointer to string to be parsed
 * @param return_name  value-result parameter for method name
 *
 * @note
358
 * If there is no whitespace after method name, the value in @a *return_name
Pekka Pessi's avatar
Pekka Pessi committed
359 360 361
 * may not be NUL-terminated.  The calling function @b must NUL terminate
 * the value by setting the @a **ss to NUL after first examining its value.
 *
362
 * @return The method code if method
Pekka Pessi's avatar
Pekka Pessi committed
363 364 365
 * was identified, 0 (sip_method_unknown()) if method is not known, or @c -1
 * (sip_method_invalid()) if an error occurred.
 *
366 367
 * If the value-result argument @a return_name is not @c NULL,
 * a pointer to the method name is stored to it.
Pekka Pessi's avatar
Pekka Pessi committed
368 369 370 371 372 373
 */
sip_method_t sip_method_d(char **ss, char const **return_name)
{
  char *s = *ss, c = *s;
  char const *name;
  int code = sip_method_unknown;
374
  size_t n = 0;
Pekka Pessi's avatar
Pekka Pessi committed
375 376 377 378 379 380

#define MATCH(s, m) (strncmp(s, m, n = sizeof(m) - 1) == 0)

  switch (c) {
  case 'A': if (MATCH(s, "ACK")) code = sip_method_ack; break;
  case 'B': if (MATCH(s, "BYE")) code = sip_method_bye; break;
381 382 383
  case 'C':
    if (MATCH(s, "CANCEL"))
      code = sip_method_cancel;
Pekka Pessi's avatar
Pekka Pessi committed
384
    break;
385
  case 'I':
Pekka Pessi's avatar
Pekka Pessi committed
386 387 388 389 390 391 392 393
    if (MATCH(s, "INVITE"))
      code = sip_method_invite;
    else if (MATCH(s, "INFO"))
      code = sip_method_info;
    break;
  case 'M': if (MATCH(s, "MESSAGE")) code = sip_method_message; break;
  case 'N': if (MATCH(s, "NOTIFY")) code = sip_method_notify; break;
  case 'O': if (MATCH(s, "OPTIONS")) code = sip_method_options; break;
394 395 396
  case 'P':
    if (MATCH(s, "PRACK")) code = sip_method_prack;
    else if (MATCH(s, "PUBLISH")) code = sip_method_publish;
Pekka Pessi's avatar
Pekka Pessi committed
397
    break;
398 399 400
  case 'R':
    if (MATCH(s, "REGISTER"))
      code = sip_method_register;
Pekka Pessi's avatar
Pekka Pessi committed
401
    else if (MATCH(s, "REFER"))
402
      code = sip_method_refer;
Pekka Pessi's avatar
Pekka Pessi committed
403
    break;
404 405 406
  case 'S':
    if (MATCH(s, "SUBSCRIBE"))
      code = sip_method_subscribe;
Pekka Pessi's avatar
Pekka Pessi committed
407 408 409 410 411 412 413 414 415
    break;
  case 'U':
    if (MATCH(s, "UPDATE"))
      code = sip_method_update;
    break;
  }

#undef MATCH

416
  if (IS_NON_WS(s[n]))
Pekka Pessi's avatar
Pekka Pessi committed
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    /* Unknown method */
    code = sip_method_unknown;

  if (code == sip_method_unknown) {
    name = s;
    for (n = 0; IS_UNRESERVED(s[n]); n++)
      ;
    if (s[n]) {
      if (!IS_LWS(s[n]))
	return sip_method_invalid;
      if (return_name)
	s[n++] = '\0';
    }
  }
  else {
    name = sip_method_names[code];
  }

  while (IS_LWS(s[n]))
    n++;

  *ss = (s + n);
  if (return_name) *return_name = name;

Kai Samposalo's avatar
Kai Samposalo committed
441
  return (sip_method_t)code;
Pekka Pessi's avatar
Pekka Pessi committed
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
}

/** Get method enum corresponding to method name */
sip_method_t sip_method_code(char const *name)
{
  /* Note that sip_method_d() does not change string if return_name is NULL */
  return sip_method_d((char **)&name, NULL);
}

char const sip_transport_udp[] = "SIP/2.0/UDP";
char const sip_transport_tcp[] = "SIP/2.0/TCP";
char const sip_transport_sctp[] = "SIP/2.0/SCTP";
char const sip_transport_tls[] = "SIP/2.0/TLS";

/** Decode transport */
457
issize_t sip_transport_d(char **ss, char const **ttransport)
Pekka Pessi's avatar
Pekka Pessi committed
458 459 460
{
  char const *transport;
  char *pn, *pv, *pt;
461
  size_t pn_len, pv_len, pt_len;
Pekka Pessi's avatar
Pekka Pessi committed
462 463
  char *s = *ss;

464 465 466
#define TRANSPORT_MATCH(t) \
  (su_casenmatch(s + 7, t + 7, (sizeof t) - 8) && \
   (!s[sizeof(t) - 1] || IS_LWS(s[sizeof(t) - 1]))	\
467
   && (transport = t, s += sizeof(t) - 1))
Pekka Pessi's avatar
Pekka Pessi committed
468

469
  if (!su_casenmatch(s, "SIP/2.0", 7) ||
Pekka Pessi's avatar
Pekka Pessi committed
470 471 472 473
      (!TRANSPORT_MATCH(sip_transport_udp) &&
       !TRANSPORT_MATCH(sip_transport_tcp) &&
       !TRANSPORT_MATCH(sip_transport_sctp) &&
       !TRANSPORT_MATCH(sip_transport_tls))) {
474
    /* Protocol name */
Pekka Pessi's avatar
Pekka Pessi committed
475 476 477 478 479 480
    transport = pn = s;
    skip_token(&s);
    pn_len = s - pn;
    skip_lws(&s);
    if (pn_len == 0 || *s++ != '/') return -1;
    skip_lws(&s);
481

Pekka Pessi's avatar
Pekka Pessi committed
482 483 484 485 486 487 488
    /* Protocol version */
    pv = s;
    skip_token(&s);
    pv_len = s - pv;
    skip_lws(&s);
    if (pv_len == 0 || *s++ != '/') return -1;
    skip_lws(&s);
489

Pekka Pessi's avatar
Pekka Pessi committed
490 491 492 493 494
    /* Transport protocol */
    pt = s;
    skip_token(&s);
    pt_len = s - pt;
    if (pt_len == 0) return -1;
495

Pekka Pessi's avatar
Pekka Pessi committed
496 497 498 499 500 501 502 503 504 505 506
    /* Remove whitespace between protocol name and version */
    if (pn + pn_len + 1 != pv) {
      pn[pn_len] = '/';
      pv = memmove(pn + pn_len + 1, pv, pv_len);
    }

    /* Remove whitespace between protocol version and transport */
    if (pv + pv_len + 1 != pt) {
      pv[pv_len] = '/';
      pt = memmove(pv + pv_len + 1, pt, pt_len);
      pt[pt_len] = '\0';
507

Pekka Pessi's avatar
Pekka Pessi committed
508
      /* extra whitespace? */
509
      if (su_casematch(transport, sip_transport_udp))
Pekka Pessi's avatar
Pekka Pessi committed
510
	transport = sip_transport_udp;
511
      else if (su_casematch(transport, sip_transport_tcp))
Pekka Pessi's avatar
Pekka Pessi committed
512
	transport = sip_transport_tcp;
513
      else if (su_casematch(transport, sip_transport_sctp))
Pekka Pessi's avatar
Pekka Pessi committed
514
	transport = sip_transport_sctp;
515
      else if (su_casematch(transport, sip_transport_tls))
Pekka Pessi's avatar
Pekka Pessi committed
516 517 518 519 520 521 522 523 524 525 526
	transport = sip_transport_tls;
    }
  }

  if (IS_LWS(*s)) { *s++ = '\0'; skip_lws(&s); }
  *ss = s;
  *ttransport = transport;
  return 0;
}

/** Calculate extra space required by sip_transport_dup() */
527
isize_t sip_transport_xtra(char const *transport)
Pekka Pessi's avatar
Pekka Pessi committed
528 529 530 531 532
{
  if (transport == sip_transport_udp ||
      transport == sip_transport_tcp ||
      transport == sip_transport_sctp ||
      transport == sip_transport_tls ||
533 534 535 536
      su_casematch(transport, sip_transport_udp) ||
      su_casematch(transport, sip_transport_tcp) ||
      su_casematch(transport, sip_transport_sctp) ||
      su_casematch(transport, sip_transport_tls))
Pekka Pessi's avatar
Pekka Pessi committed
537 538
    return 0;

Pekka Pessi's avatar
Pekka Pessi committed
539
  return MSG_STRING_SIZE(transport);
Pekka Pessi's avatar
Pekka Pessi committed
540 541 542 543 544 545 546 547 548 549 550 551 552
}

/** Duplicate a transport string */
void sip_transport_dup(char **pp, char const **dd, char const *s)
{
  if (s == sip_transport_udp)
    *dd = s;
  else if (s == sip_transport_tcp)
    *dd = s;
  else if (s == sip_transport_sctp)
    *dd = s;
  else if (s == sip_transport_tls)
    *dd = s;
553
  else if (su_casematch(s, sip_transport_udp))
Pekka Pessi's avatar
Pekka Pessi committed
554
    *dd = sip_transport_udp;
555
  else if (su_casematch(s, sip_transport_tcp))
Pekka Pessi's avatar
Pekka Pessi committed
556
    *dd = sip_transport_tcp;
557
  else if (su_casematch(s, sip_transport_sctp))
Pekka Pessi's avatar
Pekka Pessi committed
558
    *dd = sip_transport_sctp;
559
  else if (su_casematch(s, sip_transport_tls))
Pekka Pessi's avatar
Pekka Pessi committed
560 561
    *dd = sip_transport_tls;
  else
Pekka Pessi's avatar
Pekka Pessi committed
562
    MSG_STRING_DUP(*pp, *dd, s);
Pekka Pessi's avatar
Pekka Pessi committed
563 564
}

565
/** Parse SIP <word "@" word> construct used in @CallID. */
Pekka Pessi's avatar
Pekka Pessi committed
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
char *sip_word_at_word_d(char **ss)
{
  char *rv = *ss, *s0 = *ss;

  skip_word(ss);
  if (s0 == *ss)
    return NULL;
  if (**ss == '@') {
    (*ss)++;
    s0 = *ss;
    skip_word(ss);
    if (s0 == *ss)
      return NULL;
  }
  if (IS_LWS(**ss))
    (*ss)++;
  skip_lws(ss);

  return rv;
}

587
/**Add message separator, then test if message is complete.
Pekka Pessi's avatar
Pekka Pessi committed
588
 *
589
 * Add sip_content_length and sip_separator if they are missing.
590 591
 * The test that all necessary message components ( @From, @To,
 * @CSeq, @CallID, @ContentLength and message separator are present.
Pekka Pessi's avatar
Pekka Pessi committed
592 593
 *
 * @retval 0 when successful
594
 * @retval -1 upon an error: headers are missing and they could not be added
Pekka Pessi's avatar
Pekka Pessi committed
595 596 597 598 599
 */
int sip_complete_message(msg_t *msg)
{
  sip_t *sip = sip_object(msg);
  su_home_t *home = msg_home(msg);
600 601
  size_t len = 0;
  ssize_t mplen;
Pekka Pessi's avatar
Pekka Pessi committed
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624

  if (sip == NULL)
    return -1;

  if (!sip->sip_separator)
    sip->sip_separator = sip_separator_create(msg_home(msg));

  if (sip->sip_multipart) {
    sip_content_type_t *c = sip->sip_content_type;
    msg_multipart_t *mp = sip->sip_multipart;
    sip_common_t *head;

    if (!c || msg_multipart_complete(msg_home(msg), c, mp) < 0)
      return -1;

    if (sip->sip_payload)
      head = sip->sip_payload->pl_common;
    else
      head = sip->sip_separator->sep_common;

    if (!head || !msg_multipart_serialize(&head->h_succ, mp))
      return -1;

625 626
    mplen = msg_multipart_prepare(msg, mp, sip->sip_flags);
    if (mplen == -1)
Pekka Pessi's avatar
Pekka Pessi committed
627
      return -1;
628
    len = (size_t)mplen;
629
  }
Pekka Pessi's avatar
Pekka Pessi committed
630 631 632 633

  if (sip->sip_payload)
    len += sip->sip_payload->pl_len;

634 635 636
  if (len > UINT32_MAX)
    return -1;

Pekka Pessi's avatar
Pekka Pessi committed
637
  if (!sip->sip_content_length) {
Pekka Pessi's avatar
Pekka Pessi committed
638
    msg_header_insert(msg, (msg_pub_t *)sip, (msg_header_t*)
639
		      sip_content_length_create(home, (uint32_t)len));
Pekka Pessi's avatar
Pekka Pessi committed
640 641 642
  }
  else {
    if (sip->sip_content_length->l_length != len) {
643
      sip->sip_content_length->l_length = (uint32_t)len;
644
      msg_fragment_clear(sip->sip_content_length->l_common);
Pekka Pessi's avatar
Pekka Pessi committed
645 646
    }
  }
647 648

  if (!sip->sip_cseq ||
Pekka Pessi's avatar
Pekka Pessi committed
649 650 651 652 653 654
      !sip->sip_call_id ||
      !sip->sip_to ||
      !sip->sip_from ||
      !sip->sip_separator ||
      !sip->sip_content_length)
    return -1;
655

Pekka Pessi's avatar
Pekka Pessi committed
656 657
  return 0;
}