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
header in sip_t structure, including a standard header in extra headers or
putting the extension headers into a separate library.
Probalem with extending #sip_t is that it breaks binary compatibility.
There are two recommended ways to extend the Sofia SIP parser, including a
standard header in extra headers or putting the extension headers into a
separate library.
In the text below, we use "Example" header as our example with following
ABNF:
......@@ -18,30 +16,18 @@ ABNF:
IF YOUR HEADER IS A STANDARD ONE
--------------------------------
* In <sip.h>, add:
* In <sofia-sip/sip_extra.h>, add:
- Add typedef to the header structure.
The typedefs to ordinary headers are in more or less alphabetic
order after typedef of sip_unknown_t. You should add a typedef line
like this:
You should add a typedef line like this:
typedef struct sip_example_s sip_example_t;
Note that the typedefs are documented together with the
implementation in the .c file.
- Add field to the sip_t structure (struct sip_s)
- 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:
- Add the actual header structure:
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
......@@ -61,6 +47,21 @@ IF YOUR HEADER IS A STANDARD ONE
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:
Add all relevant parsing/processing cases you can think of
......@@ -86,8 +87,10 @@ IF YOUR HEADER IS A STANDARD ONE
* 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
sofia-sip.sourceforge.net
......@@ -147,6 +150,3 @@ struct sip_example_dummy_structure {
#endif /** !defined(SIP_EXAMPLE_H) */
--->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---->8---
......@@ -69,4 +69,6 @@ ALIASES += \
"ReplyTo=@ref sip_reply_to \"Reply-To\"" \
"SuppressBodyIfMatch=@ref sip_suppress_body_if_match \"Suppress-Body-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)
*
* 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
......@@ -276,6 +278,12 @@ issize_t sip_alert_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
* @endcode
*
* 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
......@@ -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;
MSG_PARAMS_SIZE(offset, rplyto->rplyto_params);
offset += MSG_STRING_SIZE(rplyto->rplyto_display);
offset += url_xtra(rplyto->rplyto_url);
return offset;
return sip_name_addr_xtra(rplyto->rplyto_display,
rplyto->rplyto_url,
rplyto->rplyto_params,
offset);
}
/**@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,
{
sip_reply_to_t *rplyto = (sip_reply_to_t *)dst;
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 b;
return sip_name_addr_dup(&rplyto->rplyto_display, o->rplyto_display,
rplyto->rplyto_url, o->rplyto_url,
&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)
{
sip_call_info_t const *ci = h->sh_call_info;
MSG_PARAMS_SIZE(offset, ci->ci_params);
offset += url_xtra(ci->ci_url);
return offset;
return sip_name_addr_xtra(NULL,
ci->ci_url,
ci->ci_params,
offset);
}
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 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,
}
#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
#
# 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:
# C-name @SINCE sip_t-like-comment
#
......@@ -9,8 +12,19 @@
#### EXTRA HEADER LIST STARTS HERE ####
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 */
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 ####
......
......@@ -60,11 +60,84 @@ char const sip_parser_version[] = VERSION;
char const sip_version_2_0[] = "SIP/2.0";
/** 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)
{
return sip_mclass;
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
*
* @NEW_1_12_7
*/
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
* class is the default one or NULL, make a clone of it before extending it.
*
* @param input pointer to a SIP message class (may be NULL)
*
* @return Pointer to extended mclass, or NULL upon an error.
*
* @NEW_1_12_7
*/
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) {
extern msg_hclass_t const * const sip_extensions[];
int i;
for (i = 0; sip_extensions[i]; i++) {
msg_hclass_t const *hclass = sip_extensions[i];
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;