Commit 12e094b2 authored by Pekka Pessi's avatar Pekka Pessi
Browse files

sip: added Remote-Party-ID, P-Asserted-Identity, P-Preferred-Identity

Added functions sip_update_default_mclass() and sip_extend_mclass()
for handling the extended parser. Note that Reply-To and Alert-Info are only
available with the extended parser.

darcs-hash:20070919225959-65a35-6727d2eb6dd015e46ba88a556ca32a8e5c8e9369.gz
parent e7eedf86
Description of Adding a SIP Header to Sofia SIP Adding a SIP Header to Sofia SIP
=============================================== ================================
by Pekka Pessi (2002-08-16, updated 2006-10-03) by Pekka Pessi (2002-08-16, last updated 2007-09-19)
There are three ways to extend the Sofia SIP parser, including a standard There are two recommended ways to extend the Sofia SIP parser, including a
header in sip_t structure, including a standard header in extra headers or standard header in extra headers or putting the extension headers into a
putting the extension headers into a separate library. separate library.
Probalem with extending #sip_t is that it breaks binary compatibility.
In the text below, we use "Example" header as our example with following In the text below, we use "Example" header as our example with following
ABNF: ABNF:
...@@ -18,30 +16,18 @@ ABNF: ...@@ -18,30 +16,18 @@ ABNF:
IF YOUR HEADER IS A STANDARD ONE IF YOUR HEADER IS A STANDARD ONE
-------------------------------- --------------------------------
* In <sip.h>, add: * In <sofia-sip/sip_extra.h>, add:
- Add typedef to the header structure. - Add typedef to the header structure.
The typedefs to ordinary headers are in more or less alphabetic You should add a typedef line like this:
order after typedef of sip_unknown_t. You should add a typedef line
like this:
typedef struct sip_example_s sip_example_t; typedef struct sip_example_s sip_example_t;
Note that the typedefs are documented together with the Note that the typedefs are documented together with the
implementation in the .c file. implementation in the .c file.
- Add field to the sip_t structure (struct sip_s) - Add the actual header structure:
- Remember to add a comment containing the header name
after field for benefit of the AWK script autmatically generating
boilerplate functions and macros:
sip_example_t *sip_example; /**< Example */
- The AWK script msg_parser.awk automatically creates the default
prototypes and tags for the newly created header when the entry in
sip_t structure is formatted like to the example above.
It will complain about mismatches between header name and field name.
* Add the actual header structure:
The header structure would look like below. The first field MUST be a The header structure would look like below. The first field MUST be a
sip_common_t structure, second field MUST be a pointer to next header sip_common_t structure, second field MUST be a pointer to next header
...@@ -61,6 +47,21 @@ IF YOUR HEADER IS A STANDARD ONE ...@@ -61,6 +47,21 @@ IF YOUR HEADER IS A STANDARD ONE
unsigned long ex_value; /**< Value of example. */ unsigned long ex_value; /**< Value of example. */
}; };
* Add entry to sip_extra_headers.txt:
- In this case:
example @NEW_2_0 /* Example header */
- The first is the base C name used for functions and types related to
the type. The AWK script msg_parser.awk automatically creates the
default prototypes and tags for the newly created header. It will
complain about mismatches between header name and the base name.
- If the entry is before #### DEFAULT HEADER LIST ENDS HERE ####
the new header is added to the default parser
- If after, the new header is added only to the extended parser.
- The extended parser will be used after call to
sip_update_default_mclass(NULL)
* Write parsing tests for your new headers in torture_sip.c: * Write parsing tests for your new headers in torture_sip.c:
Add all relevant parsing/processing cases you can think of Add all relevant parsing/processing cases you can think of
...@@ -86,8 +87,10 @@ IF YOUR HEADER IS A STANDARD ONE ...@@ -86,8 +87,10 @@ IF YOUR HEADER IS A STANDARD ONE
* Run "make check" after you are ready. Really. * Run "make check" after you are ready. Really.
IF YOUR HEADERS ARE NON-STANDARD IF YOUR HEADERS ARE COMPLETELY NON-STANDARD
-------------------------------- -------------------------------------------
* Add a separate library package for them
- There is an example package sofia-sip-2543.tar.gz, available from - There is an example package sofia-sip-2543.tar.gz, available from
sofia-sip.sourceforge.net sofia-sip.sourceforge.net
...@@ -147,6 +150,3 @@ struct sip_example_dummy_structure { ...@@ -147,6 +150,3 @@ struct sip_example_dummy_structure {
#endif /** !defined(SIP_EXAMPLE_H) */ #endif /** !defined(SIP_EXAMPLE_H) */
--->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---->8--- --->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---
...@@ -69,4 +69,6 @@ ALIASES += \ ...@@ -69,4 +69,6 @@ ALIASES += \
"ReplyTo=@ref sip_reply_to \"Reply-To\"" \ "ReplyTo=@ref sip_reply_to \"Reply-To\"" \
"SuppressBodyIfMatch=@ref sip_suppress_body_if_match \"Suppress-Body-If-Match\"" \ "SuppressBodyIfMatch=@ref sip_suppress_body_if_match \"Suppress-Body-If-Match\"" \
"SuppressNotifyIfMatch=@ref sip_suppress_notify_if_match \"Suppress-Notify-If-Match\"" \ "SuppressNotifyIfMatch=@ref sip_suppress_notify_if_match \"Suppress-Notify-If-Match\"" \
"RemotePartyID=@ref sip_remote_party_id \"Remote-Party-ID\"" \
"PAssertedIdentity=@ref sip_p_asserted_identity \"P-Asserted-Identity\"" \
"PPreferredIdentity=@ref sip_p_preferred_identity \"P-Preferred-Identity\"" \
...@@ -219,7 +219,9 @@ issize_t sip_error_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f) ...@@ -219,7 +219,9 @@ issize_t sip_error_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
* *
* The parsed Alert-Info header is stored in #sip_alert_info_t structure. * The parsed Alert-Info header is stored in #sip_alert_info_t structure.
* *
* @NEW_1_12_7 * @NEW_1_12_7. In order to use @b Alert-Info header,
* initialize the SIP parser with, e.g.,
* sip_update_default_mclass(sip_extend_mclass(NULL))
*/ */
/**@ingroup sip_alert_info /**@ingroup sip_alert_info
...@@ -276,6 +278,12 @@ issize_t sip_alert_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f) ...@@ -276,6 +278,12 @@ issize_t sip_alert_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
* @endcode * @endcode
* *
* The parsed Reply-To header is stored in #sip_reply_to_t structure. * The parsed Reply-To header is stored in #sip_reply_to_t structure.
*
* @sa sip_update_default_mclass()
*
* @NEW_1_12_7. In order to use @b Reply-To header,
* initialize the SIP parser with, e.g.,
* sip_update_default_mclass(sip_extend_mclass(NULL)).
*/ */
/**@ingroup sip_reply_to /**@ingroup sip_reply_to
...@@ -336,11 +344,10 @@ static isize_t sip_reply_to_dup_xtra(sip_header_t const *h, isize_t offset) ...@@ -336,11 +344,10 @@ static isize_t sip_reply_to_dup_xtra(sip_header_t const *h, isize_t offset)
{ {
sip_reply_to_t const *rplyto = (sip_reply_to_t const *)h; sip_reply_to_t const *rplyto = (sip_reply_to_t const *)h;
MSG_PARAMS_SIZE(offset, rplyto->rplyto_params); return sip_name_addr_xtra(rplyto->rplyto_display,
offset += MSG_STRING_SIZE(rplyto->rplyto_display); rplyto->rplyto_url,
offset += url_xtra(rplyto->rplyto_url); rplyto->rplyto_params,
offset);
return offset;
} }
/**@internal Duplicate one sip_reply_to_t object. */ /**@internal Duplicate one sip_reply_to_t object. */
...@@ -349,15 +356,11 @@ static char *sip_reply_to_dup_one(sip_header_t *dst, sip_header_t const *src, ...@@ -349,15 +356,11 @@ static char *sip_reply_to_dup_one(sip_header_t *dst, sip_header_t const *src,
{ {
sip_reply_to_t *rplyto = (sip_reply_to_t *)dst; sip_reply_to_t *rplyto = (sip_reply_to_t *)dst;
sip_reply_to_t const *o = (sip_reply_to_t *)src; sip_reply_to_t const *o = (sip_reply_to_t *)src;
char *end = b + xtra;
b = msg_params_dup(&rplyto->rplyto_params, o->rplyto_params, b, xtra);
MSG_STRING_DUP(b, rplyto->rplyto_display, o->rplyto_display);
URL_DUP(b, end, rplyto->rplyto_url, o->rplyto_url);
assert(b <= end); return sip_name_addr_dup(&rplyto->rplyto_display, o->rplyto_display,
rplyto->rplyto_url, o->rplyto_url,
return b; &rplyto->rplyto_params, o->rplyto_params,
b, xtra);
} }
/* ====================================================================== */ /* ====================================================================== */
...@@ -889,10 +892,10 @@ isize_t sip_info_dup_xtra(sip_header_t const *h, isize_t offset) ...@@ -889,10 +892,10 @@ isize_t sip_info_dup_xtra(sip_header_t const *h, isize_t offset)
{ {
sip_call_info_t const *ci = h->sh_call_info; sip_call_info_t const *ci = h->sh_call_info;
MSG_PARAMS_SIZE(offset, ci->ci_params); return sip_name_addr_xtra(NULL,
offset += url_xtra(ci->ci_url); ci->ci_url,
ci->ci_params,
return offset; offset);
} }
char *sip_info_dup_one(sip_header_t *dst, char *sip_info_dup_one(sip_header_t *dst,
...@@ -902,14 +905,11 @@ char *sip_info_dup_one(sip_header_t *dst, ...@@ -902,14 +905,11 @@ char *sip_info_dup_one(sip_header_t *dst,
{ {
sip_call_info_t *ci = dst->sh_call_info; sip_call_info_t *ci = dst->sh_call_info;
sip_call_info_t const *o = src->sh_call_info; sip_call_info_t const *o = src->sh_call_info;
char *end = b + xtra;
b = msg_params_dup(&ci->ci_params, o->ci_params, b, xtra);
URL_DUP(b, end, ci->ci_url, o->ci_url);
assert(b <= end);
return b; return sip_name_addr_dup(NULL, NULL,
ci->ci_url, o->ci_url,
&ci->ci_params, o->ci_params,
b, xtra);
} }
/* ====================================================================== */ /* ====================================================================== */
...@@ -1047,3 +1047,319 @@ issize_t sip_suppress_notify_if_match_e(char b[], isize_t bsiz, ...@@ -1047,3 +1047,319 @@ issize_t sip_suppress_notify_if_match_e(char b[], isize_t bsiz,
} }
#endif #endif
#if SIP_HAVE_REMOTE_PARTY_ID
/**@SIP_HEADER sip_remote_party_id Remote-Party-ID Header
*
* The syntax of the Remote-Party-ID header is described as follows:
* @code
* Remote-Party-ID = "Remote-Party-ID" HCOLON rpid *(COMMA rpid)
*
* rpid = [display-name] LAQUOT addr-spec RAQUOT
* *(SEMI rpi-token)
*
* rpi-token = rpi-screen / rpi-pty-type /
* rpi-id-type / rpi-privacy / other-rpi-token
*
* rpi-screen = "screen" EQUAL ("no" / "yes")
*
* rpi-pty-type = "party" EQUAL ("calling" / "called" / token)
*
* rpi-id-type = "id-type" EQUAL ("subscriber" / "user" /
* "term" / token)
*
* rpi-privacy = "privacy" EQUAL
* ( rpi-priv-element
* / (LDQUOT rpi-priv-element
* *(COMMA rpi-priv-element) RDQUOT) )
*
* rpi-priv-element = ("full" / "name" / "uri" / "off" / token)
* ["-" ( "network" / token )]
*
* other-rpi-token = ["-"] token [EQUAL (token / quoted-string)]
*
* @endcode
*
* @sa sip_update_default_mclass(), draft-ietf-sip-privacy-04.txt, @RFC3325
*
* @NEW_1_12_7. In order to use @b Remote-Party-ID header,
* initialize the SIP parser with, e.g.,
* sip_update_default_mclass(sip_extend_mclass(NULL)).
*/
/**@ingroup sip_remote_party_id
* @typedef typedef struct sip_remote_party_id_s sip_remote_party_id_t;
*
* The structure #sip_remote_party_id_t contains representation of SIP
* @RemotePartyID header.
*
* The #sip_remote_party_id_t is defined as follows:
* @code
* typedef struct sip_remote_party_id_s {
* sip_common_t rpid_common[1]; // Common fragment info
* sip_remote_party_id_t *rpid_next; // Link to next
* char const *rpid_display; // Display name
* url_t rpid_url[1]; // URL
* sip_param_t const *rpid_params; // Parameters
* // Shortcuts to screen, party, id-type and privacy parameters
* char const *rpid_screen, *rpid_party, *rpid_id_type, *rpid_privacy;
* } sip_remote_party_id_t;
* @endcode
*/
extern msg_xtra_f sip_remote_party_id_dup_xtra;
extern msg_dup_f sip_remote_party_id_dup_one;
static msg_update_f sip_remote_party_id_update;
msg_hclass_t sip_remote_party_id_class[] =
SIP_HEADER_CLASS(remote_party_id, "Remote-Party-ID", "",
rpid_params, append, remote_party_id);
issize_t sip_remote_party_id_d(su_home_t *home, sip_header_t *h,
char *s, isize_t slen)
{
sip_remote_party_id_t *rpid = (sip_remote_party_id_t *)h;
while (*s == ',') /* Ignore empty entries (comma-whitespace) */
*s = '\0', s += span_lws(s + 1) + 1;
if (sip_name_addr_d(home, &s,
&rpid->rpid_display,
rpid->rpid_url,
&rpid->rpid_params, NULL) == -1)
return -1;
return msg_parse_next_field(home, h, s, slen);
}
issize_t sip_remote_party_id_e(char b[], isize_t bsiz,
sip_header_t const *h, int f)
{
sip_remote_party_id_t const *rpid = (sip_remote_party_id_t *)h;
return sip_name_addr_e(b, bsiz, f,
rpid->rpid_display, 1,
rpid->rpid_url,
rpid->rpid_params,
NULL);
}
/** Calculate size of extra data required for duplicating one
* sip_remote_party_id_t header.
*/
isize_t sip_remote_party_id_dup_xtra(sip_header_t const *h, isize_t offset)
{
sip_remote_party_id_t const *rpid = (sip_remote_party_id_t *)h;
return sip_name_addr_xtra(rpid->rpid_display,
rpid->rpid_url,
rpid->rpid_params,
offset);
}
/** Duplicate one sip_remote_party_id_t object */
char *sip_remote_party_id_dup_one(sip_header_t *dst,
sip_header_t const *src,
char *b, int xtra)
{
sip_remote_party_id_t *rpid = (sip_remote_party_id_t *)dst;
sip_remote_party_id_t const *o = (sip_remote_party_id_t const *)src;
return sip_name_addr_dup(&rpid->rpid_display, o->rpid_display,
rpid->rpid_url, o->rpid_url,
&rpid->rpid_params, o->rpid_params,
b, xtra);
}
static int sip_remote_party_id_update(msg_common_t *h,
char const *name, int namelen,
char const *value)
{
sip_remote_party_id_t *rpid = (sip_remote_party_id_t *)h;
if (name == NULL) {
rpid->rpid_screen = NULL;
rpid->rpid_party = NULL;
rpid->rpid_id_type = NULL;
rpid->rpid_privacy = NULL;
}
#define MATCH(s) (namelen == strlen(#s) && !strncasecmp(name, #s, strlen(#s)))
else if (MATCH(screen))
rpid->rpid_screen = value;
else if (MATCH(party))
rpid->rpid_party = value;
else if (MATCH(id-type))
rpid->rpid_id_type = value;
else if (MATCH(privacy))
rpid->rpid_privacy = value;
#undef MATCH
return 0;
}
#endif
#if SIP_HAVE_P_ASSERTED_IDENTITY
/**@SIP_HEADER sip_p_asserted_identity P-Asserted-Identity Header
*
* The P-Asserted-Identity header is used used among trusted SIP entities
* (typically intermediaries) to carry the identity of the user sending a
* SIP message as it was verified by authentication. It is "defined" in
* @RFC3325 section 9.1 as follows:
*
* @code
* PAssertedID = "P-Asserted-Identity" HCOLON PAssertedID-value
* *(COMMA PAssertedID-value)
* PAssertedID-value = name-addr / addr-spec
* @endcode
*
* @sa @RFC3325, @PPreferredIdentity
*
* @NEW_1_12_7. In order to use @b P-Asserted-Identity header,
* initialize the SIP parser with, e.g.,
* sip_update_default_mclass(sip_extend_mclass(NULL)).
*/
/**@ingroup sip_p_asserted_identity
* @typedef typedef struct sip_p_asserted_identity_s sip_p_asserted_identity_t;
*
* The structure #sip_p_asserted_identity_t contains representation of SIP
* @PAssertedIdentity header.
*
* The #sip_p_asserted_identity_t is defined as follows:
* @code
* typedef struct sip_p_asserted_identity_s {
* sip_common_t paid_common[1]; // Common fragment info
* sip_p_asserted_identity_t *paid_next; // Link to next
* char const *paid_display; // Display name
* url_t paid_url[1]; // URL
* } sip_p_asserted_identity_t;
* @endcode
*/
static msg_xtra_f sip_p_asserted_identity_dup_xtra;
static msg_dup_f sip_p_asserted_identity_dup_one;
#define sip_p_asserted_identity_update NULL
msg_hclass_t sip_p_asserted_identity_class[] =
SIP_HEADER_CLASS(p_asserted_identity, "P-Asserted-Identity", "",
paid_common, append, p_asserted_identity);
issize_t sip_p_asserted_identity_d(su_home_t *home, sip_header_t *h,
char *s, isize_t slen)
{
sip_p_asserted_identity_t *paid = (sip_p_asserted_identity_t *)h;
while (*s == ',') /* Ignore empty entries (comma-whitespace) */
*s = '\0', s += span_lws(s + 1) + 1;
if (sip_name_addr_d(home, &s,
&paid->paid_display,
paid->paid_url,
NULL, NULL) == -1)
return -1;
return msg_parse_next_field(home, h, s, slen);
}
issize_t sip_p_asserted_identity_e(char b[], isize_t bsiz,
sip_header_t const *h, int f)
{
sip_p_asserted_identity_t const *paid = (sip_p_asserted_identity_t *)h;
return sip_name_addr_e(b, bsiz, f,
paid->paid_display, MSG_IS_CANONIC(f),
paid->paid_url,
NULL,
NULL);
}
isize_t sip_p_asserted_identity_dup_xtra(sip_header_t const *h, isize_t offset)
{
sip_p_asserted_identity_t const *paid = (sip_p_asserted_identity_t *)h;
return sip_name_addr_xtra(paid->paid_display,
paid->paid_url,
NULL,
offset);
}
/** Duplicate one sip_p_asserted_identity_t object */
char *sip_p_asserted_identity_dup_one(sip_header_t *dst,
sip_header_t const *src,
char *b, isize_t xtra)
{
sip_p_asserted_identity_t *paid = (sip_p_asserted_identity_t *)dst;
sip_p_asserted_identity_t const *o = (sip_p_asserted_identity_t *)src;
return sip_name_addr_dup(&paid->paid_display, o->paid_display,
paid->paid_url, o->paid_url,
NULL, NULL,
b, xtra);
}
#endif
#if SIP_HAVE_P_PREFERRED_IDENTITY
/**@SIP_HEADER sip_p_preferred_identity P-Preferred-Identity Header
*
* The P-Preferred-Identity header is used used among trusted SIP entities
* (typically intermediaries) to carry the identity of the user sending a
* SIP message as it was verified by authentication. It is "defined" in
* @RFC3325 section 9.1 as follows:
*
* @code
* PPreferredID = "P-Preferred-Identity" HCOLON PPreferredID-value
* *(COMMA PPreferredID-value)
* PPreferredID-value = name-addr / addr-spec
* @endcode
*
* @sa @RFC3325, @PAssertedIdentity
*
* @NEW_1_12_7. In order to use @b P-Preferred-Identity header,
* initialize the SIP parser with, e.g.,
* sip_update_default_mclass(sip_extend_mclass(NULL)).
*/
/**@ingroup sip_p_preferred_identity
* @typedef typedef struct sip_p_preferred_identity_s sip_p_preferred_identity_t;
*
* The structure #sip_p_preferred_identity_t contains representation of SIP
* @PPreferredIdentity header.
*
* The #sip_p_preferred_identity_t is defined as follows:
* @code
* typedef struct sip_p_preferred_identity_s {
* sip_common_t ppid_common[1]; // Common fragment info
* sip_p_preferred_identity_t *ppid_next; // Link to next
* char const *ppid_display; // Display name
* url_t ppid_url[1]; // URL
* } sip_p_preferred_identity_t;
* @endcode
*/
msg_hclass_t sip_p_preferred_identity_class[] =
SIP_HEADER_CLASS(p_preferred_identity, "P-Preferred-Identity", "",
ppid_common, append, p_asserted_identity);
issize_t sip_p_preferred_identity_d(su_home_t *home, sip_header_t *h,
char *s, isize_t slen)
{
return sip_p_asserted_identity_d(home, h, s, slen);
}
issize_t sip_p_preferred_identity_e(char b[], isize_t bsiz,
sip_header_t const *h, int f)
{
return sip_p_asserted_identity_e(b, bsiz, h, f);
}
#endif
# #
# This file specifies extra SIP headers not included in sip_t structure # This file specifies extra SIP headers not included in sip_t structure
# #
# The parsed headers are accessed with function (or macro) like sip_refer_sub()
# e.g., sip_refer_sub_t *rsub = sip_refer_sub(sip);
#
# The line format is: # The line format is:
# C-name @SINCE sip_t-like-comment # C-name @SINCE sip_t-like-comment
# #
...@@ -9,8 +12,19 @@ ...@@ -9,8 +12,19 @@
#### EXTRA HEADER LIST STARTS HERE #### #### EXTRA HEADER LIST STARTS HERE ####
refer_sub @NEW_1_12_5 /**< Refer-Sub header */ refer_sub @NEW_1_12_5 /**< Refer-Sub header */
#### DEFAULT HEADER LIST ENDS HERE ####
#
# These headers are added to the extended parser, installed
# as default with sip_update_default_mclass(NULL)
#
alert_info @NEW_1_12_7 /**< Alert-Info header */ alert_info @NEW_1_12_7 /**< Alert-Info header */
reply_to @NEW_1_12_7 /**< Reply-To header */ reply_to @NEW_1_12_7 /**< Reply-To header */
remote_party_id @NEW_1_12_7 /**< Remote-Party-ID header */
p_asserted_identity @NEW_1_12_7 /**<P-Asserted-Identity*/
p_preferred_identity @NEW_1_12_7 /**<P-Preferred-Identity*/
#### EXPERIMENTAL HEADER LIST STARTS HERE #### #### EXPERIMENTAL HEADER LIST STARTS HERE ####
......
...@@ -60,11 +60,84 @@ char const sip_parser_version[] = VERSION; ...@@ -60,11 +60,84 @@ char const sip_parser_version[] = VERSION;
char const sip_version_2_0[] = "SIP/2.0"; char const sip_version_2_0[] = "SIP/2.0";
/** Default message class */ /** Default message class */
extern msg_mclass_t const sip_mclass[]; extern msg_mclass_t sip_mclass[];
static msg_mclass_t const *_default = sip_mclass;
/** Return a built-in SIP parser object. */
msg_mclass_t const *sip_default_mclass(void) msg_mclass_t const *sip_default_mclass(void)