Browse Source

Merge branch 'outbound'

* outbound: (46 commits)
  modules/registrar: fixed lock usage in unregister()
  modules/pv: can only retrieve $ruid for a request
  modules/pv: added new $ruid and $branch(ruid) PVs
  modules/xprint: updated use of get_branch() and next_branch() to cope with additional argument
  modules/tm: updated use of get_branch() and next_branch() to cope with additional argument
  modules/pv: updated use of get_branch() and next_branch() to cope with additional argument
  modules/permissions: updated use of get_branch() and next_branch() to cope with additional argument
  modules/domain: updated use of get_branch() and next_branch() to cope with additional argument
  core: updated use of get_branch() and next_branch() to cope with additional argument
  core: updated get_branch() and next_branch() to return ruid (if set)
  modules/tm: updated t_serial to store/retrieve ruid
  modules/registrar: tidied up unregister() function
  modules/ims_isc: updated to include new argument to append_branch()
  modules/ims_icscf: updated to include new argument to append_branch()
  modules/exec: updated to include new argument to append_branch()
  modules/enum: updated to include new argument to append_branch()
  modules/dialplan: updated to include new argument to append_branch()
  modules/cpl-c: updated to include new argument to append_branch()
  modules/corex: updated to include new argument to append_branch()
  modules/avpops: updated to include new argument to append_branch()
  ...
Peter Dunkley 12 năm trước cách đây
mục cha
commit
3445718155
45 tập tin đã thay đổi với 982 bổ sung481 xóa
  1. 1 1
      action.c
  2. 2 0
      config.h
  3. 33 6
      dset.c
  4. 12 5
      dset.h
  5. 0 190
      lib/kcore/parse_supported.c
  6. 0 94
      lib/kcore/parse_supported.h
  7. 1 1
      modules/alias_db/alookup.c
  8. 2 2
      modules/avpops/avpops_impl.c
  9. 1 1
      modules/corex/corex_lib.c
  10. 1 1
      modules/cpl-c/cpl_sig.c
  11. 1 1
      modules/dialplan/dialplan.c
  12. 1 1
      modules/domain/domain.c
  13. 2 2
      modules/enum/enum.c
  14. 1 1
      modules/exec/exec.c
  15. 1 1
      modules/ims_icscf/scscf_list.c
  16. 1 1
      modules/ims_isc/isc.c
  17. 5 5
      modules/ims_registrar_scscf/reply.c
  18. 1 1
      modules/permissions/permissions.c
  19. 3 0
      modules/pv/pv.c
  20. 11 1
      modules/pv/pv_branch.c
  21. 21 3
      modules/pv/pv_core.c
  22. 3 0
      modules/pv/pv_core.h
  23. 73 65
      modules/registrar/README
  24. 57 34
      modules/registrar/doc/registrar_admin.xml
  25. 13 1
      modules/registrar/lookup.c
  26. 26 2
      modules/registrar/reg_mod.c
  27. 36 10
      modules/registrar/reply.c
  28. 3 1
      modules/registrar/rerrno.h
  29. 109 26
      modules/registrar/save.c
  30. 1 2
      modules/registrar/save.h
  31. 2 2
      modules/rls/subscribe.c
  32. 3 3
      modules/sst/sst_handlers.c
  33. 1 1
      modules/tm/t_fwd.c
  34. 46 10
      modules/tm/t_serial.c
  35. 3 3
      modules/xprint/xp_lib.c
  36. 2 0
      parser/keys.h
  37. 39 0
      parser/msg_parser.c
  38. 5 0
      parser/msg_parser.h
  39. 40 0
      parser/parse_option_tags.c
  40. 166 0
      parser/parse_option_tags.h
  41. 73 0
      parser/parse_require.c
  42. 50 0
      parser/parse_require.h
  43. 73 0
      parser/parse_supported.c
  44. 54 0
      parser/parse_supported.h
  45. 3 3
      select_core.c

+ 1 - 1
action.c

@@ -509,7 +509,7 @@ int do_action(struct run_act_ctx* h, struct action* a, struct sip_msg* msg)
 			ret=append_branch(msg, &a->val[0].u.str, &msg->dst_uri,
 					  &msg->path_vec, a->val[1].u.number,
 					  (flag_t)flags, msg->force_send_socket,
-					  0, 0);
+					  0, 0, 0);
 			/* if the uri is the ruri and q was also not changed, mark
 			   ruri as consumed, to avoid having an identical branch */
 			if ((a->val[0].u.str.s == 0 || a->val[0].u.str.len == 0) &&

+ 2 - 0
config.h

@@ -75,6 +75,8 @@
 
 #define MAX_INSTANCE_SIZE 256 		/*!< Maximum length of +sip.instance contact header param value buffer */
 
+#define MAX_RUID_SIZE 65		/*!< Maximum length of ruid for location records */
+
 #define MY_VIA "Via: SIP/2.0/UDP "
 #define MY_VIA_LEN (sizeof(MY_VIA) - 1)
 

+ 33 - 6
dset.c

@@ -215,7 +215,8 @@ void set_branch_iterator(int n)
  */
 char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 		 str* path, unsigned int *flags,
-		 struct socket_info** force_socket)
+		 struct socket_info** force_socket,
+		 str *ruid)
 {
 	if (i < nr_branches) {
 		*len = branches[i].len;
@@ -232,6 +233,10 @@ char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 			*force_socket = branches[i].force_send_socket;
 		if (flags)
 			*flags = branches[i].flags;
+		if (ruid) {
+			ruid->len = branches[i].ruid_len;
+			ruid->s = (ruid->len)?branches[i].ruid:0;
+		}
 		return branches[i].uri;
 	} else {
 		*len = 0;
@@ -248,6 +253,10 @@ char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 			*force_socket = 0;
 		if (flags)
 			*flags = 0;
+		if (ruid) {
+			ruid->s = 0;
+			ruid->len = 0;
+		}
 		return 0;
 	}
 }
@@ -258,12 +267,13 @@ char* get_branch(unsigned int i, int* len, qvalue_t* q, str* dst_uri,
  * 0 is returned if there are no more branches
  */
 char* next_branch(int* len, qvalue_t* q, str* dst_uri, str* path,
-		  unsigned int* flags, struct socket_info** force_socket)
+		  unsigned int* flags, struct socket_info** force_socket,
+		  str* ruid)
 {
 	char* ret;
 	
 	ret=get_branch(branch_iterator, len, q, dst_uri, path, flags,
-		       force_socket);
+		       force_socket, ruid);
 	if (likely(ret))
 		branch_iterator++;
 	return ret;
@@ -297,7 +307,8 @@ void clear_branches(void)
 int append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path,
 		  qvalue_t q, unsigned int flags,
 		  struct socket_info* force_socket,
-		  str* instance, unsigned int reg_id)
+		  str* instance, unsigned int reg_id,
+		  str* ruid)
 {
 	str luri;
 
@@ -381,6 +392,22 @@ int append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path,
 	/* copy reg_id */
 	branches[nr_branches].reg_id = reg_id;
 
+	/* copy ruid string */
+	if (unlikely(ruid && ruid->len && ruid->s)) {
+		if (unlikely(ruid->len > MAX_RUID_SIZE - 1)) {
+			LOG(L_ERR, "too long ruid: %.*s\n",
+			    ruid->len, ruid->s);
+			return -1;
+		}
+		memcpy(branches[nr_branches].ruid, ruid->s,
+		       ruid->len);
+		branches[nr_branches].ruid[ruid->len] = 0;
+		branches[nr_branches].ruid_len = ruid->len;
+	} else {
+		branches[nr_branches].ruid[0] = '\0';
+		branches[nr_branches].ruid_len = 0;
+	}
+
 	nr_branches++;
 	return 1;
 }
@@ -415,7 +442,7 @@ char* print_dset(struct sip_msg* msg, int* len)
 	crt_branch = get_branch_iterator();
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0))) {
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0))) {
 		cnt++;
 		*len += uri.len;
 		if (q != Q_UNSPECIFIED) {
@@ -456,7 +483,7 @@ char* print_dset(struct sip_msg* msg, int* len)
 	}
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0))) {
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0))) {
 		if (i) {
 			memcpy(p, CONTACT_DELIM, CONTACT_DELIM_LEN);
 			p += CONTACT_DELIM_LEN;

+ 12 - 5
dset.h

@@ -66,6 +66,10 @@ struct branch
     /* reg-id contact header param value */
     unsigned int reg_id;
 
+    /* ruid value from usrloc */
+    char ruid[MAX_RUID_SIZE];
+    unsigned int ruid_len;
+
     /* Branch flags */
     flag_t flags;
 };
@@ -88,11 +92,12 @@ int drop_sip_branch(int idx);
 int append_branch(struct sip_msg* msg, str* uri, str* dst_uri, str* path,
 		  qvalue_t q, unsigned int flags,
 		  struct socket_info* force_socket,
-		  str* instance, unsigned int reg_id);
+		  str* instance, unsigned int reg_id,
+		  str* ruid);
 
 /*! \brief kamailio compatible version */
 #define km_append_branch(msg, uri, dst_uri, path, q, flags, force_socket) \
-    append_branch(msg, uri, dst_uri, path, q, flags, force_socket, 0, 0)
+    append_branch(msg, uri, dst_uri, path, q, flags, force_socket, 0, 0, 0)
 
 /*! \brief ser compatible append_branch version.
  *  append_branch version compatible with ser: no path or branch flags support
@@ -109,7 +114,7 @@ static inline int ser_append_branch(struct sip_msg* msg,
     s_uri.len=uri_len;
     s_dst_uri.s=dst_uri;
     s_dst_uri.len=dst_uri_len;
-    return append_branch(msg, &s_uri, &s_dst_uri, 0, q, 0, force_socket, 0, 0);
+    return append_branch(msg, &s_uri, &s_dst_uri, 0, q, 0, force_socket, 0, 0, 0);
 }
 
 
@@ -134,11 +139,13 @@ void set_branch_iterator(int n);
  *  *len) or 0 if there are no more branches.
  */
 char* next_branch(int* len, qvalue_t* q, str* dst_uri, str* path,
-		  unsigned int* flags, struct socket_info** force_socket);
+		  unsigned int* flags, struct socket_info** force_socket,
+		  str *ruid);
 
 char* get_branch( unsigned int i, int* len, qvalue_t* q, str* dst_uri,
 		  str* path, unsigned int *flags,
-		  struct socket_info** force_socket);
+		  struct socket_info** force_socket,
+		  str* ruid);
 
 /*! \brief
  * Empty the array of branches

+ 0 - 190
lib/kcore/parse_supported.c

@@ -1,190 +0,0 @@
-/*
- * $Id$
- *
- * Copyright (C) 2006 Andreas Granig <[email protected]>
- *
- * This file is part of Kamailio, a free SIP server.
- *
- * Kamailio is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * Kamailio 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*!
- * \file
- * \brief Supported parser
- * \ingroup parser
- */
-
-#include "../../mem/mem.h"
-#include "../../parser/keys.h"
-#include "parse_supported.h"
-
-#define _100r_ 0x72303031   /* "100r" for "100rel" */
-#define _time_ 0x656d6974   /*!< "time" */
-
-#define IS_DELIM(c) (*(c) == ' ' || *(c) == '\t' || *(c) == '\r' || *(c) == '\n' || *(c) == ',')
-
-/* from parser/parse_hname2.c: */
-#define LOWER_BYTE(b) ((b) | 0x20)
-#define LOWER_DWORD(d) ((d) | 0x20202020)
-#define READ(val) \
-	(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
-
-
-/*!
- * Parse Supported HF body.
- */
-static inline int parse_supported_body(str *body, unsigned int *sup)
-{
-	register char* p;
-	register unsigned int val;
-	int len, pos = 0;
-
-	*sup = 0;
-
-	p = body->s;
-	len = body->len;
-
-	while (pos < len) {
-		/* skip spaces and commas */
-		for (; pos < len && IS_DELIM(p); ++pos, ++p);
-
-		val = LOWER_DWORD(READ(p));
-		switch (val) {
-
-			/* "path" */
-			case _path_:
-				if(pos + 4 <= len && IS_DELIM(p+4)) {
-					*sup |= F_SUPPORTED_PATH;
-					pos += 5; p += 5;
-				}
-				break;
-
-			/* "100rel" */
-			case _100r_:
-				if ( pos+6 <= len
-					 && LOWER_BYTE(*(p+4))=='e' && LOWER_BYTE(*(p+5))=='l'
-					 && IS_DELIM(p+6)) {
-					*sup |= F_SUPPORTED_100REL;
-					pos += SUPPORTED_100REL_LEN + 1;
-					p   += SUPPORTED_100REL_LEN + 1;
-				}
-				break;
-
-			/* "timer" */
-			case _time_:
-				if ( pos+5 <= len && LOWER_BYTE(*(p+4))=='r'
-					 && IS_DELIM(p+5) ) {
-					*sup |= F_SUPPORTED_TIMER;
-					pos += SUPPORTED_TIMER_LEN + 1;
-					p   += SUPPORTED_TIMER_LEN + 1;
-				}
-				break;
-
-			/* extra supported or unknown */
-			default:
-				if(pos+SUPPORTED_EVENTLIST_LEN<=len
-						&& strncasecmp(p, SUPPORTED_EVENTLIST_STR,
-							SUPPORTED_EVENTLIST_LEN)==0
-						&& IS_DELIM(p+SUPPORTED_EVENTLIST_LEN) ) {
-					*sup |= F_SUPPORTED_EVENTLIST;
-					pos += SUPPORTED_EVENTLIST_LEN + 1;
-					p   += SUPPORTED_EVENTLIST_LEN + 1;
-				} else if(pos+SUPPORTED_GRUU_LEN<=len
-						&& strncasecmp(p, SUPPORTED_GRUU_STR,
-							SUPPORTED_GRUU_LEN)==0
-						&& IS_DELIM(p+SUPPORTED_GRUU_LEN)) {
-					*sup |= F_SUPPORTED_GRUU;
-					pos += SUPPORTED_GRUU_LEN + 1;
-					p   += SUPPORTED_GRUU_LEN + 1;
-				} else if(pos+SUPPORTED_OUTBOUND_LEN<=len
-						&& strncasecmp(p, SUPPORTED_OUTBOUND_STR,
-							SUPPORTED_OUTBOUND_LEN)==0
-						&& IS_DELIM(p+SUPPORTED_OUTBOUND_LEN)) {
-					*sup |= F_SUPPORTED_OUTBOUND;
-					pos += SUPPORTED_OUTBOUND_LEN + 1;
-					p   += SUPPORTED_OUTBOUND_LEN + 1;
-				} else {
-					/* skip element */
-					for (; pos < len && !IS_DELIM(p); ++pos, ++p);
-				}
-				break;
-		}
-	}
-	
-	return 0;
-}
-
-
-/**
- * wrapper to free the content of parsed supported header
- */
-void hf_free_supported(void *parsed)
-{
-	struct supported_body *sb;
-	sb = (struct supported_body*)parsed;
-	free_supported(&sb);
-}
-
-/*!
- * Parse all Supported headers
- */
-int parse_supported( struct sip_msg *msg)
-{
-	unsigned int supported;
-	struct hdr_field  *hdr;
-	struct supported_body *sb;
-
-	/* maybe the header is already parsed! */
-	if (msg->supported && msg->supported->parsed)
-		return 0;
-
-	/* parse to the end in order to get all SUPPORTED headers */
-	if (parse_headers(msg,HDR_EOH_F,0)==-1 || !msg->supported)
-		return -1;
-
-	/* bad luck! :-( - we have to parse them */
-	supported = 0;
-	for( hdr=msg->supported ; hdr ; hdr=next_sibling_hdr(hdr)) {
-		if (hdr->parsed) {
-			supported |= ((struct supported_body*)hdr->parsed)->supported;
-			continue;
-		}
-
-		sb = (struct supported_body*)pkg_malloc(sizeof(struct supported_body));
-		if (sb == 0) {
-			LM_ERR("out of pkg_memory\n");
-			return -1;
-		}
-
-		parse_supported_body(&(hdr->body), &(sb->supported));
-		sb->hfree = hf_free_supported;
-		sb->supported_all = 0;
-		hdr->parsed = (void*)sb;
-		supported |= sb->supported;
-	}
-
-	((struct supported_body*)msg->supported->parsed)->supported_all = 
-		supported;
-	return 0;
-}
-
-/* free supported header structure */
-void free_supported(struct supported_body **sb)
-{
-	if (sb && *sb) {
-		pkg_free(*sb);
-		*sb = 0;
-	}
-}

+ 0 - 94
lib/kcore/parse_supported.h

@@ -1,94 +0,0 @@
-/*
- * $Id$
- *
- * Copyright (C) 2006 Andreas Granig <[email protected]>
- *
- * This file is part of Kamailio, a free SIP server.
- *
- * Kamailio is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version
- *
- * Kamailio 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- *
- * History:
- * -------
- * 2006-03-02  parse_supported() parses and cumulates all SUPPORTED 
- *             headers (bogdan)
- */
-
-/*!
- * \file
- * \brief Supported parser
- * \ingroup parser
- */
-
-#ifndef PARSE_SUPPORTED_H
-#define PARSE_SUPPORTED_H
-
-#include "../../parser/msg_parser.h"
-#include "../../parser/hf.h"
-#include "../../mem/mem.h"
-
-
-#define F_SUPPORTED_PATH		(1 << 0)
-#define F_SUPPORTED_100REL		(1 << 1)
-#define F_SUPPORTED_TIMER		(1 << 2)
-#define F_SUPPORTED_EVENTLIST   (1 << 3)
-#define F_SUPPORTED_GRUU        (1 << 4)
-#define F_SUPPORTED_OUTBOUND    (1 << 5)
-
-#define SUPPORTED_PATH_STR		"path"
-#define SUPPORTED_PATH_LEN		(sizeof(SUPPORTED_PATH_STR)-1)
-
-/* RFC 3262 (PRACK) */
-#define SUPPORTED_100REL_STR	"100rel"
-#define SUPPORTED_100REL_LEN	(sizeof(SUPPORTED_100REL_STR)-1)
-
-/* RFC 4028 */
-#define SUPPORTED_TIMER_STR		"timer"
-#define SUPPORTED_TIMER_LEN		(sizeof(SUPPORTED_TIMER_STR)-1)
-
-/* RFC 4662 (RLS) */
-#define SUPPORTED_EVENTLIST_STR  "eventlist"
-#define SUPPORTED_EVENTLIST_LEN  (sizeof(SUPPORTED_EVENTLIST_STR)-1)
-
-/* RFC 5627 */
-#define SUPPORTED_GRUU_STR		"gruu"
-#define SUPPORTED_GRUU_LEN		(sizeof(SUPPORTED_GRUU_STR)-1)
-
-/* RFC 5626 */
-#define SUPPORTED_OUTBOUND_STR	"outbound"
-#define SUPPORTED_OUTBOUND_LEN	(sizeof(SUPPORTED_OUTBOUND_STR)-1)
-
-#define get_supported(p_msg) \
-	((p_msg)->supported ? ((struct supported_body*)(p_msg)->supported->parsed)->supported_all : 0)
-
-
-struct supported_body {
-	hf_parsed_free_f hfree;        /* function to free the content */
-	unsigned int supported;        /* supported mask for the current hdr */
-	unsigned int supported_all;    /* suppoted mask for the all "supported" hdr
-	                                *  - it's set only for the first hdr in 
-	                                *  sibling list*/
-};
-
-
-/*!
- * Parse all Supported headers.
- */
-int parse_supported( struct sip_msg *msg);
-
-
-void free_supported(struct supported_body **sb);
-
-#endif /* PARSE_SUPPORTED_H */

+ 1 - 1
modules/alias_db/alookup.c

@@ -202,7 +202,7 @@ int alias_db_lookup(struct sip_msg* _msg, str table_s)
 		} else {
 			user_s.s = useruri_buf;
 			if (append_branch(_msg, &user_s, 0, 0, MIN_Q, 0, 0,
-					  0, 0) == -1)
+					  0, 0, 0) == -1)
 			{
 				LM_ERR("error while appending branches\n");
 				goto err_server;

+ 2 - 2
modules/avpops/avpops_impl.c

@@ -999,7 +999,7 @@ int ops_pushto_avp (struct sip_msg* msg, struct fis_param* dst,
 				/* if is not the first modification, push the current uri as
 				 * branch */
 			    if (append_branch( msg, 0, 0, 0, Q_UNSPECIFIED,
-					       0, 0, 0, 0) != 1)
+					       0, 0, 0, 0, 0) != 1)
 				{
 					LM_ERR("append_branch action failed\n");
 					goto error;
@@ -1026,7 +1026,7 @@ int ops_pushto_avp (struct sip_msg* msg, struct fis_param* dst,
 			ruri_mark_new(); /* re-use uri for serial forking */
 		} else if (dst->opd&AVPOPS_USE_BRANCH) {
 			if (append_branch( msg, &val, 0, 0, Q_UNSPECIFIED, 0,
-					   msg->force_send_socket, 0, 0) != 1)
+					   msg->force_send_socket, 0, 0, 0) != 1)
 			{
 				LM_ERR("append_branch action failed\n");
 				goto error;

+ 1 - 1
modules/corex/corex_lib.c

@@ -71,7 +71,7 @@ int corex_append_branch(sip_msg_t *msg, gparam_t *pu, gparam_t *pq)
 	getbflagsval(0, &branch_flags);
 	ret = append_branch(msg, (uri.len>0)?&uri:0, &msg->dst_uri,
 			    &msg->path_vec, q, branch_flags,
-			    msg->force_send_socket, 0, 0);
+			    msg->force_send_socket, 0, 0, 0);
 
 
 	if(uri.len<=0)

+ 1 - 1
modules/cpl-c/cpl_sig.c

@@ -92,7 +92,7 @@ int cpl_proxy_to_loc_set( struct sip_msg *msg, struct location **locs,
 			(*locs)->addr.uri.len, (*locs)->addr.uri.s, bflags);
 		if(append_branch(msg, &(*locs)->addr.uri,
 				 &(*locs)->addr.received, 0,
-				 Q_UNSPECIFIED, bflags, 0, 0, 0)==-1){
+				 Q_UNSPECIFIED, bflags, 0, 0, 0, 0)==-1){
 			LM_ERR("failed when appending branch <%s>\n",
 			       (*locs)->addr.uri.s);
 			goto error;

+ 1 - 1
modules/dialplan/dialplan.c

@@ -295,7 +295,7 @@ static int dp_update(struct sip_msg * msg, pv_spec_t * src, pv_spec_t * dest,
 
 	if(is_route_type(FAILURE_ROUTE)
 			&& (dest->type==PVT_RURI || dest->type==PVT_RURI_USERNAME)) {
-	    if (append_branch(msg, 0, 0, 0, Q_UNSPECIFIED, 0, 0, 0, 0) != 1) {
+	    if (append_branch(msg, 0, 0, 0, Q_UNSPECIFIED, 0, 0, 0, 0, 0) != 1) {
 			LM_ERR("append_branch action failed\n");
 			return -1;
 		}

+ 1 - 1
modules/domain/domain.c

@@ -142,7 +142,7 @@ int is_uri_host_local(struct sip_msg* _msg, char* _s1, char* _s2)
 	}
 	return hash_table_lookup(&(_msg->parsed_uri.host), &did, &attrs);
     } else if (is_route_type(FAILURE_ROUTE)) {
-	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0);
+	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0, 0);
 	if (branch.s) {
 	    if (parse_uri(branch.s, branch.len, &puri) < 0) {
 		LM_ERR("error while parsing branch URI\n");

+ 2 - 2
modules/enum/enum.c

@@ -687,7 +687,7 @@ int do_query(struct sip_msg* _msg, char *user, char *name, str *service) {
 		q = q - 10;
 		curr_prio = priority;
 	    }
-	    if (append_branch(_msg, &result, 0, 0, q, 0, 0, 0, 0) == -1) {
+	    if (append_branch(_msg, &result, 0, 0, q, 0, 0, 0, 0, 0) == -1) {
 		goto done;
 	    }
 	}
@@ -1137,7 +1137,7 @@ int enum_pv_query_3(struct sip_msg* _msg, char* _sp, char* _suffix,
 				q = q - 10;
 				curr_prio = priority;
 			}
-			if (append_branch(_msg, &result, 0, 0, q, 0, 0, 0, 0)
+			if (append_branch(_msg, &result, 0, 0, q, 0, 0, 0, 0, 0)
 			    == -1) {
 				goto done;
 			}

+ 1 - 1
modules/exec/exec.c

@@ -168,7 +168,7 @@ int exec_str(struct sip_msg *msg, char *cmd, char *param, int param_len) {
 			}
 		} else {
 		    if (append_branch(msg, &uri, 0, 0, Q_UNSPECIFIED, 0, 0,
-				      0, 0) == -1) {
+				      0, 0, 0) == -1) {
 				LM_ERR("append_branch failed; too many or too long URIs?\n");
 				goto error02;
 			}

+ 1 - 1
modules/ims_icscf/scscf_list.c

@@ -384,7 +384,7 @@ int I_scscf_select(struct sip_msg* msg, char* str1, char* str2) {
         } else {
             /* subsequent */
             req = msg;
-            append_branch(req, &scscf_name, 0, 0, Q_UNSPECIFIED, 0, 0, 0, 0);
+            append_branch(req, &scscf_name, 0, 0, Q_UNSPECIFIED, 0, 0, 0, 0, 0);
             result = CSCF_RETURN_TRUE;
         }
     } else {

+ 1 - 1
modules/ims_isc/isc.c

@@ -76,7 +76,7 @@ int isc_forward(struct sip_msg *msg, isc_match *m, isc_mark *mark) {
 
 	/* append branch if last trigger failed */
 	if (is_route_type(FAILURE_ROUTE))
-		append_branch(msg, &(msg->first_line.u.request.uri), &(msg->dst_uri), 0, Q_UNSPECIFIED, 0, 0, 0, 0);
+		append_branch(msg, &(msg->first_line.u.request.uri), &(msg->dst_uri), 0, Q_UNSPECIFIED, 0, 0, 0, 0, 0);
 
 	// Determines the tm transaction identifiers.
 	// If no transaction, then creates one

+ 5 - 5
modules/ims_registrar_scscf/reply.c

@@ -38,7 +38,7 @@
 #include "../../ut.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/contact/contact.h"
-#include "../../lib/kcore/parse_supported.h"
+#include "../../parser/parse_supported.h"
 #include "../../data_lump_rpl.h"
 #include "../ims_usrloc_scscf/usrloc.h"
 #include "rerrno.h"
@@ -563,7 +563,7 @@ int build_p_associated_uri(ims_subscription* s) {
  * Send a reply
  */
 int reg_send_reply_transactional(struct sip_msg* _m, contact_for_header_t* contact_header, struct cell* t_cell) {
-    str unsup = str_init(SUPPORTED_PATH_STR);
+    str unsup = str_init(OPTION_TAG_PATH_STR);
     long code;
     str msg = str_init(MSG_200); /* makes gcc shut up */
     char* buf;
@@ -582,7 +582,7 @@ int reg_send_reply_transactional(struct sip_msg* _m, contact_for_header_t* conta
                     return -1;
                 if (add_path(_m, &_m->path_vec) < 0)
                     return -1;
-            } else if (get_supported(_m) & F_SUPPORTED_PATH) {
+            } else if (get_supported(_m) & F_OPTION_TAG_PATH) {
                 if (add_path(_m, &_m->path_vec) < 0)
                     return -1;
             } else if (path_mode == PATH_MODE_STRICT) {
@@ -656,7 +656,7 @@ int reg_send_reply_transactional(struct sip_msg* _m, contact_for_header_t* conta
  * Send a reply
  */
 int reg_send_reply(struct sip_msg* _m, contact_for_header_t* contact_header) {
-    str unsup = str_init(SUPPORTED_PATH_STR);
+    str unsup = str_init(OPTION_TAG_PATH_STR);
     long code;
     str msg = str_init(MSG_200); /* makes gcc shut up */
     char* buf;
@@ -675,7 +675,7 @@ int reg_send_reply(struct sip_msg* _m, contact_for_header_t* contact_header) {
                     return -1;
                 if (add_path(_m, &_m->path_vec) < 0)
                     return -1;
-            } else if (get_supported(_m) & F_SUPPORTED_PATH) {
+            } else if (get_supported(_m) & F_OPTION_TAG_PATH) {
                 if (add_path(_m, &_m->path_vec) < 0)
                     return -1;
             } else if (path_mode == PATH_MODE_STRICT) {

+ 1 - 1
modules/permissions/permissions.c

@@ -406,7 +406,7 @@ static int check_routing(struct sip_msg* msg, int idx)
 	}
 
 check_branches:
-	for( br_idx=0 ; (branch.s=get_branch(br_idx,&branch.len,&q,0,0,0,0))!=0 ;
+	for( br_idx=0 ; (branch.s=get_branch(br_idx,&branch.len,&q,0,0,0,0,0))!=0 ;
 			br_idx++ ) {
 		uri_str = get_plain_uri(&branch);
 		if (!uri_str) {

+ 3 - 0
modules/pv/pv.c

@@ -410,6 +410,9 @@ static pv_export_t mod_pvs[] = {
 	{{"ua", (sizeof("ua")-1)}, /* */
 		PVT_OTHER, pv_get_useragent, 0,
 		0, 0, 0, 0},
+	{{"ruid", (sizeof("ruid")-1)}, /* */
+		PVT_OTHER, pv_get_ruid, 0,
+		0, 0, 0, 0},
 
 	{ {"shv", (sizeof("shv")-1)}, PVT_OTHER, pv_get_shvar,
 		pv_set_shvar, pv_parse_shvar_name, 0, 0, 0},

+ 11 - 1
modules/pv/pv_branch.c

@@ -40,6 +40,7 @@ int pv_get_branchx(struct sip_msg *msg, pv_param_t *param,
 	str path;
 	unsigned int fl = 0;
 	struct socket_info* fsocket = NULL;
+	str ruid;
 
 	/* get the index */
 	if(pv_get_spec_index(msg, param, &idx, &idxf)!=0)
@@ -48,7 +49,7 @@ int pv_get_branchx(struct sip_msg *msg, pv_param_t *param,
 		return pv_get_null(msg, param, res);
 	}
 
-	uri.s = get_branch(idx, &uri.len, &lq, &duri, &path, &fl, &fsocket);
+	uri.s = get_branch(idx, &uri.len, &lq, &duri, &path, &fl, &fsocket, &ruid);
 
 	/* branch(count) doesn't need a valid branch, everything else does */
 	if(uri.s == 0 && ( param->pvn.u.isname.name.n != 5/* count*/ ))
@@ -79,6 +80,10 @@ int pv_get_branchx(struct sip_msg *msg, pv_param_t *param,
 			return pv_get_uintval(msg, param, res, nr_branches);
 		case 6: /* flags */
 			return pv_get_uintval(msg, param, res, fl);
+		case 7: /* ruid */
+			if(ruid.len==0)
+				return pv_get_null(msg, param, res);
+			return pv_get_strval(msg, param, res, &ruid);
 		default:
 			/* 0 - uri */
 			return pv_get_strval(msg, param, res, &uri);
@@ -245,6 +250,9 @@ int pv_set_branchx(struct sip_msg* msg, pv_param_t *param,
 			}
 			br->flags = val->ri;
 		break;
+		case 7: /* ruid */
+			/* do nothing - cannot set the ruid */
+		break;
 		default:
 			/* 0 - uri */
 			if(val==NULL || (val->flags&PV_VAL_NULL))
@@ -296,6 +304,8 @@ int pv_parse_branchx_name(pv_spec_p sp, str *in)
 		case 4: 
 			if(strncmp(in->s, "path", 4)==0)
 				sp->pvp.pvn.u.isname.name.n = 2;
+			else if (strncmp(in->s, "ruid", 4)==0)
+				sp->pvp.pvn.u.isname.name.n = 7;
 			else goto error;
 		break;
 		case 1: 

+ 21 - 3
modules/pv/pv_core.c

@@ -1358,7 +1358,7 @@ int pv_get_branch(struct sip_msg *msg, pv_param_t *param,
 		return pv_get_null(msg, param, res);
 
 
-	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0);
+	branch.s = get_branch(0, &branch.len, &q, 0, 0, 0, 0, 0);
 	if (!branch.s) {
 		return pv_get_null(msg, param, res);
 	}
@@ -1387,7 +1387,7 @@ int pv_get_branches(struct sip_msg *msg, pv_param_t *param,
   
 	cnt = s.len = 0;
 
-	while ((uri.s = get_branch(cnt, &uri.len, &q, 0, 0, 0, 0)))
+	while ((uri.s = get_branch(cnt, &uri.len, &q, 0, 0, 0, 0, 0)))
 	{
 		cnt++;
 		s.len += uri.len;
@@ -1411,7 +1411,7 @@ int pv_get_branches(struct sip_msg *msg, pv_param_t *param,
 	i = 0;
 	p = pv_local_buf;
 
-	while ((uri.s = get_branch(i, &uri.len, &q, 0, 0, 0, 0)))
+	while ((uri.s = get_branch(i, &uri.len, &q, 0, 0, 0, 0, 0)))
 	{
 		if (i)
 		{
@@ -1804,6 +1804,24 @@ int pv_get_cnt(struct sip_msg *msg, pv_param_t *param,
 	return pv_get_uintval(msg, param, res, n);
 }
 
+int pv_get_ruid(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res)
+{
+	if(msg==NULL)
+		return -1;
+
+	if(msg->first_line.type == SIP_REPLY)
+		return pv_get_null(msg, param, res);
+
+	if(msg->ruid.len==0) 
+	{
+		LM_DBG("no ruid\n");
+		return pv_get_null(msg, param, res);
+	}
+	
+	return pv_get_strval(msg, param, res, &msg->ruid);
+}
+
 
 /********* end PV get functions *********/
 

+ 3 - 0
modules/pv/pv_core.h

@@ -214,6 +214,9 @@ int pv_get_server_id(struct sip_msg *msg, pv_param_t *param,
 int pv_get_cnt(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res);
 
+int pv_get_ruid(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+
 /********* end PV get functions *********/
 
 /********* start PV set functions *********/

+ 73 - 65
modules/registrar/README

@@ -16,7 +16,7 @@ Edited by
 
 Bogdan-Andre Iancu
 
-   Copyright © 2003 FhG FOKUS
+   Copyright © 2003 FhG FOKUS
      __________________________________________________________________
 
    Table of Contents
@@ -68,7 +68,7 @@ Bogdan-Andre Iancu
               4.3. lookup_branches(domain)
               4.4. registered(domain [, uri])
               4.5. add_sock_hdr(hdr_name)
-              4.6. unregister(domain, uri)
+              4.6. unregister(domain, uri[, ruid])
               4.7. reg_fetch_contacts(domain, uri, profile)
               4.8. reg_free_contacts(profile)
 
@@ -176,7 +176,7 @@ Chapter 1. Admin Guide
         4.3. lookup_branches(domain)
         4.4. registered(domain [, uri])
         4.5. add_sock_hdr(hdr_name)
-        4.6. unregister(domain, uri)
+        4.6. unregister(domain, uri[, ruid])
         4.7. reg_fetch_contacts(domain, uri, profile)
         4.8. reg_free_contacts(profile)
 
@@ -216,8 +216,8 @@ Chapter 1. Admin Guide
      * off - stores the value of the Path headers into usrloc without
        passing it back to the UAC in the reply.
      * lazy - stores the Path header and passes it back to the UAC if
-       Path-support is indicated by the "path" param in the Supported HF.
-     * strict - rejects the registration with "420 Bad Extension" if
+       Path-support is indicated by the “path� param in the Supported HF.
+     * strict - rejects the registration with “420 Bad Extension� if
        there's a Path header but no support for it is indicated by the
        UAC. Otherwise it's stored and passed back to the UAC.
 
@@ -229,8 +229,8 @@ Chapter 1. Admin Guide
    client's NAT).
 
    The whole process is transparent to the user, so no config changes are
-   required beside setting the registrar-parameters "use_path" and
-   "path_mode".
+   required beside setting the registrar-parameters “use_path� and
+   “path_mode�.
 
 1.2. GRUU Support
 
@@ -294,7 +294,7 @@ Chapter 1. Admin Guide
    contact parameters, this value will be used for newly created usrloc
    records. The parameter contains number of second to expire (for example
    use 3600 for one hour). If it is set to a lower value than the
-   "min_expires" parameter then it will be ignored. This parameter can be
+   “min_expires� parameter then it will be ignored. This parameter can be
    modified via ser config framework. A random value in a specific
    interval can be selected by using the default_expires_range parameter
 
@@ -308,9 +308,9 @@ modparam("registrar", "default_expires", 1800)
 3.2. default_expires_range (integer)
 
    This parameter specifies that the expiry used for newly created usrloc
-   records are not fixed(when "default_expires" applies), but a random
-   value in the interval "[default_expires-default_expires_range%,
-   default_expires+default_expires_range%]". The value is between 0 and
+   records are not fixed(when “default_expires� applies), but a random
+   value in the interval “[default_expires-default_expires_range%,
+   default_expires+default_expires_range%]�. The value is between 0 and
    100 and represent the maximim percentage from default_expires that will
    be substracted or added when computing the value. Default in 0, meaning
    default_expires is left unmodified. This parameter can be modified via
@@ -529,7 +529,7 @@ modparam("registrar", "method_filtering", 1)
 3.17. use_path (integer)
 
    If set to 1, the Path header is handled according to the parameter This
-   parameter can be modified via ser config framework. "path_mode".
+   parameter can be modified via ser config framework. “path_mode�.
 
    Default value is 0 (disabled).
 
@@ -546,12 +546,12 @@ modparam("registrar", "use_path", 1)
        the reply.
      * 1 - The Path header is saved into usrloc, but is only included in
        the reply if path support is indicated in the registration request
-       by the "path" option of the "Supported" header.
+       by the “path� option of the “Supported� header.
      * 2 - The path header is only saved into usrloc, if path support is
-       indicated in the registration request by the "path" option of the
-       "Supported" header. If no path support is indicated, the request is
-       rejected with "420 - Bad Extension" and the header "Unsupported:
-       path" is included in the reply along with the received "Path"
+       indicated in the registration request by the “path� option of the
+       “Supported� header. If no path support is indicated, the request is
+       rejected with “420 - Bad Extension� and the header “Unsupported:
+       path� is included in the reply along with the received “Path�
        header. This mode is the one recommended by RFC-3327.
 
    Default value is 2.
@@ -563,10 +563,10 @@ modparam("registrar", "path_mode", 0)
 
 3.19. path_use_received (integer)
 
-   If set to 1, the "received" parameter of the first Path URI of a
+   If set to 1, the “received� parameter of the first Path URI of a
    registration is set as received-uri and the NAT branch flag is set for
    this contact. This is useful if the registrar is placed behind a SIP
-   loadbalancer, which passes the nat'ed UAC address as "received"
+   loadbalancer, which passes the nat'ed UAC address as “received�
    parameter in it's Path uri.
 
    Default value is 0 (disabled).
@@ -627,7 +627,7 @@ modparam("registrar", "xavp_cfg", "ulrcd")
 
 3.23. gruu_enabled (integer)
 
-   If set to 1 and GRUU "+sip.instance" parameter to Contact header of
+   If set to 1 and GRUU “+sip.instance� parameter to Contact header of
    REGISTER is present, then the value of the parameter is saved to
    location and pub-gruu and temp-gruu addresses are generated.
 
@@ -642,24 +642,23 @@ modparam("registrar", "gruu_enabled", 0)
 
 3.24. outbound_mode (integer)
 
-   If set to 0 then this module will accept REGISTER requests that do not
+   If set to 0 this module will accept REGISTER requests that do not
    contain a Supported: header with the outbound options-tag. The 200 OK
    response to REGISTER requests that this module generates will not
-   contain Require: or Supported: headers with the outbound options tag.
+   contain Require: or Supported: headers with the outbound options-tag.
+   If the client has a Require: header with the outbound options tag the
+   REGISTER will be rejected with a 420 Bad Extension response.
 
-   If set to 1 then this module will accept REGISTER requests that do not
-   contain a Supported: header with the outbound options-tag. The 200 OK
-   response to REGISTER requests that this module generates will contain a
-   Supported: header with the outbound options tag.
-
-   If set to 2 then this module will reject REGISTER requests that do not
-   contain a Supported: header with the outbound options-tag. The 200 OK
-   response to REGISTER requests that this module generates will contain
-   Require: and Supported: headers with the outbound options tag.
+   If set to 1 this module will accept REGISTER requests that do not
+   contain a Supported: header with the outbound options-tag and REGISTER
+   requests that do contain a Supported: or Requires: header with the
+   outbound options-tag. When the client supports outbound the appropriate
+   RFC5626 procedures will be followed.
 
-   Set this parameter to 2 if you are using SIP Outbound (RFC 5626) and
-   want your Edge Proxy to insert a Flow-Timer: header into the 200 OK
-   response to REGISTERs (as per RFC 5626 section 5.4).
+   If set to 2 this module will reject REGISTER requests that do not
+   contain a Supported: header with the outbound options-tag. When the
+   client supports outbound the appropriate RFC5626 procedures will be
+   followed.
 
    Default value is 0.
 
@@ -675,17 +674,17 @@ modparam("registrar", "outbound_mode", 2)
 
    If set to > 0 then this module will add a Flow-Timer: header containing
    this value to 200 OK responses to REGISTER requests. This parameter may
-   only be set to a value > 0 when outbound_mode is set to 2.
+   only be set to a value > 0 when outbound_mode is set to 1 or 2.
 
    When set to a value > 0 this parameter should be set to slightly less
    than the connection timeout value between the UAC and the network (this
    corresponds to the core tcp_connection_lifetime option and websocket
    keepalive_timeout modparam). This parameter is most useful when you
-   have a single edge proxy/registrar. If you are using a separate SIP
-   Outbound Edge Proxy you should consider leaving this parameter set to 0
-   and adding the Flow-Timer: header on the Edge Proxy (as this allows you
-   to keep all of the timer values for a specific flow in one
-   configuration - that of the Edge Proxy).
+   have a single edge proxy/registrar or if you have an edge proxy that
+   cannot modify responses. If you are using a separate edge proxy you
+   should consider leaving this parameter set to 0 and adding the
+   Flow-Timer: header on the edge proxy as this allows you to keep all of
+   the timer values for a specific flow in one configuration.
 
    Default value is 0.
 
@@ -701,11 +700,11 @@ modparam("registrar", "flow_timer", 25)
    4.3. lookup_branches(domain)
    4.4. registered(domain [, uri])
    4.5. add_sock_hdr(hdr_name)
-   4.6. unregister(domain, uri)
+   4.6. unregister(domain, uri[, ruid])
    4.7. reg_fetch_contacts(domain, uri, profile)
    4.8. reg_free_contacts(profile)
 
-4.1. save(domain, [, flags [, uri]])
+4.1.  save(domain, [, flags [, uri]])
 
    The function processes a REGISTER message. It can add, remove or modify
    usrloc records depending on Contact and Expires HFs in the REGISTER
@@ -749,7 +748,7 @@ save("location", "0x01");
 save("location", "0x00", "sip:[email protected]");
 ...
 
-4.2. lookup(domain [, uri])
+4.2.  lookup(domain [, uri])
 
    The function extracts username from Request-URI and tries to find all
    contacts for the username in usrloc. If there are no such contacts, -1
@@ -789,7 +788,7 @@ switch ($retcode) {
 };
 ...
 
-4.3. lookup_branches(domain)
+4.3.  lookup_branches(domain)
 
    The function performs lookup(domain) on r-uri and additional branches
    (only branches that have no other attributes set than uri).
@@ -806,7 +805,7 @@ switch ($retcode) {
 lookup_branches("location");
 ...
 
-4.4. registered(domain [, uri])
+4.4.  registered(domain [, uri])
 
    The function returns true if the AOR in the Request-URI is registered,
    false otherwise. The function does not modify the message being
@@ -828,9 +827,9 @@ if (registered("location")) {
 };
 ...
 
-4.5. add_sock_hdr(hdr_name)
+4.5.  add_sock_hdr(hdr_name)
 
-   Adds to the current REGISTER request a new header with "hdr_name" which
+   Adds to the current REGISTER request a new header with “hdr_name� which
    contains the description of the received socket (proto:ip:port)
 
    This make sens only in multiple replicated servers scenarios.
@@ -845,9 +844,11 @@ if (registered("location")) {
 add_sock_hdr("Sock-Info");
 ...
 
-4.6. unregister(domain, uri)
+4.6.  unregister(domain, uri[, ruid])
 
-   The function remove all the contact associated to 'uri'.
+   The function removes contacts associated with 'uri'. If 'ruid' is
+   provided a specific contact is removed, if 'ruid' is not provided all
+   contacts are removed.
 
    Meaning of the parameters is as follows:
      * domain - Name of table that should be used for the lookup or
@@ -855,6 +856,8 @@ add_sock_hdr("Sock-Info");
      * uri - The SIP URI address of the user which to remove the contact
        addresses for. It can contain pseudo-variables that are evaluated
        at runtime.
+     * ruid - The record unique ID for a a specific contact to be removed.
+       It can contain pseudo-variables that are evaluated at runtime.
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
@@ -862,9 +865,10 @@ add_sock_hdr("Sock-Info");
 ...
 unregister("location", "$ru");
 unregister("location", "sip:[email protected]");
+unregister("location", "$ru", "$ulc(caller=>ruid)");
 ...
 
-4.7. reg_fetch_contacts(domain, uri, profile)
+4.7.  reg_fetch_contacts(domain, uri, profile)
 
    The function fetches the contacts for 'uri' from table 'domain' to
    pseudo-variable $ulc(profile).
@@ -886,7 +890,7 @@ reg_fetch_contacts("location", "$ru", "callee");
 reg_fetch_contacts("location", "sip:[email protected]", "caller");
 ...
 
-4.8. reg_free_contacts(profile)
+4.8.  reg_free_contacts(profile)
 
    The function frees the contacts from pseudo-variable $ulc(profile).
    Should be called to release the content of a profile. Anyhow, fetching
@@ -955,28 +959,32 @@ event_route[usrloc:contact-expired] {
 7.1. $ulc(profile=>attr)
 
    Access the attributes of contact addresses stored in 'profile'. It must
-   be used after a call of "reg_fetch_contacts()".
+   be used after a call of “reg_fetch_contacts()�.
 
-   The "profile" has to be one of the values used with
-   "reg_fetch_contacts()".
+   The “profile� has to be one of the values used with
+   “reg_fetch_contacts()�.
 
-   The "attr" can be:
+   The “attr� can be:
      * aor - address of record
      * domain - use location domain name
      * aorhash - hash id for the record
-     * count - number of contacts
      * addr - contact address
      * path - path vector
      * received - received address
      * expires - expires value
      * callid - call-id header value
      * q - the q value
+     * cseq - the cseq value
      * flags - flags value
      * cflags - cflags value
      * user_agent - user agent
      * socket - local socket
      * modified - last modified time
      * methods - methods value
+     * count - number of contacts
+     * ruid - record unique ID
+     * reg-id - reg-id value
+     * instance - instance value
 
    The pseudo-variable accepts positive index value to access a specific
    contact record.
@@ -1013,9 +1021,9 @@ if(reg_fetch_contacts("location", "$fu", "caller"))
 
 Chapter 2. Frequently Asked Questions
 
-   2.1. What happend with the old "nat_flag" module parameter?
-   2.2. What happend with the old "use_domain" module parameter?
-   2.3. What happend with the old "save_noreply" and "save_memory"
+   2.1. What happend with the old “nat_flag� module parameter?
+   2.2. What happend with the old “use_domain� module parameter?
+   2.3. What happend with the old “save_noreply� and “save_memory�
           functions?
 
    2.4. Where can I find more about Kamailio?
@@ -1025,23 +1033,23 @@ Chapter 2. Frequently Asked Questions
 
    2.1.
 
-   What happend with the old "nat_flag" module parameter?
+   What happend with the old “nat_flag� module parameter?
 
    In was removed, as the module internally loads this value from the
-   "USRLOC" module (see the "nat_bflag" USRLOC parameter).
+   “USRLOC� module (see the “nat_bflag� USRLOC parameter).
 
    2.2.
 
-   What happend with the old "use_domain" module parameter?
+   What happend with the old “use_domain� module parameter?
 
    In was removed, as the module internally loads this option from the
-   "USRLOC" module. This was done in order to simplify the configuration.
+   “USRLOC� module. This was done in order to simplify the configuration.
 
    2.3.
 
-   What happend with the old "save_noreply" and "save_memory" functions?
+   What happend with the old “save_noreply� and “save_memory� functions?
 
-   There functions were merged into the new "save(domain,flags)"
+   There functions were merged into the new “save(domain,flags)�
    functions. If a reply should be sent or if the DB should be updated
    also is controlled via the flags.
 

+ 57 - 34
modules/registrar/doc/registrar_admin.xml

@@ -724,31 +724,27 @@ modparam("registrar", "gruu_enabled", 0)
 	<section>
 		<title><varname>outbound_mode</varname> (integer)</title>
 		<para>
-		If set to 0 then this module will accept REGISTER requests
-		that do not contain a Supported: header with the outbound
-		options-tag. The 200 OK response to REGISTER requests that this
-		module generates will not contain Require: or Supported: headers
-		with the outbound options tag.
+		If set to 0 this module will accept REGISTER requests that do
+		not contain a Supported: header with the outbound options-tag.
+		The 200 OK response to REGISTER requests that this module
+		generates will not contain Require: or Supported: headers with
+		the outbound options-tag. If the client has a Require: header
+		with the outbound options tag the REGISTER will be rejected
+		with a 420 Bad Extension response.
 		</para>
 		<para>
-		If set to 1 then this module will accept REGISTER requests
-		that do not contain a Supported: header with the outbound
-		options-tag. The 200 OK response to REGISTER requests that this
-		module generates will contain a Supported: header with the
-		outbound options tag.
+		If set to 1 this module will accept REGISTER requests that
+		do not contain a Supported: header with the outbound
+		options-tag and REGISTER requests that do contain a Supported:
+		or Requires: header with the outbound options-tag. When the
+		client supports outbound the appropriate RFC5626 procedures
+		will be followed.
 		</para>
 		<para>
-		If set to 2 then this module will reject REGISTER requests
-		that do not contain a Supported: header with the outbound
-		options-tag. The 200 OK response to REGISTER requests that this
-		module generates will contain Require: and Supported: headers
-		with the outbound options tag.
-		</para>
-		<para>
-		Set this parameter to 2 if you are using SIP Outbound (RFC
-		5626) and want your Edge Proxy to insert a Flow-Timer: header
-		into the 200 OK response to REGISTERs (as per RFC 5626 section
-		5.4).
+		If set to 2 this module will reject REGISTER requests that
+		do not contain a Supported: header with the outbound
+		options-tag. When the client supports outbound the appropriate
+		RFC5626 procedures will be followed.
 		</para>
 		<para>
 		<emphasis>
@@ -775,7 +771,7 @@ modparam("registrar", "outbound_mode", 2)
 		If set to > 0 then this module will add a Flow-Timer: header
 		containing this value to 200 OK responses to REGISTER requests.
 		This parameter may only be set to a value > 0 when
-		<emphasis>outbound_mode</emphasis> is set to 2.
+		<emphasis>outbound_mode</emphasis> is set to 1 or 2.
 		</para>
 		<para>
 		When set to a value > 0 this parameter should be set to slightly
@@ -783,13 +779,14 @@ modparam("registrar", "outbound_mode", 2)
 		network (this corresponds to the core
 		<emphasis>tcp_connection_lifetime</emphasis> option and
 		<emphasis>websocket</emphasis>
-		<emphasis>keepalive_timeout</emphasis> modparam). This parameter
-		is most useful when you have a single edge proxy/registrar. If
-		you are using a separate SIP Outbound Edge Proxy you should
+		<emphasis>keepalive_timeout</emphasis> modparam). This
+		parameter is most useful when you have a single edge
+		proxy/registrar or if you have an edge proxy that cannot modify
+		responses. If you are using a separate edge proxy you should
 		consider leaving this parameter set to 0 and adding the
-		Flow-Timer: header on the Edge Proxy (as this allows you to
+		Flow-Timer: header on the edge proxy as this allows you to
 		keep all of the timer values for a specific flow in one
-		configuration - that of the Edge Proxy).
+		configuration.
 		</para>
 		<para>
 		<emphasis>
@@ -1080,10 +1077,12 @@ add_sock_hdr("Sock-Info");
 	</section>
 	<section>
 		<title>
-		<function moreinfo="none">unregister(domain, uri)</function>
+		<function moreinfo="none">unregister(domain, uri[, ruid])</function>
 		</title>
 		<para>
-		The function remove all the contact associated to 'uri'.
+		The function removes contacts associated with 'uri'. If 'ruid' is
+		provided a specific contact is removed, if 'ruid' is not provided
+		all contacts are removed.
 		</para>
 		<para>Meaning of the parameters is as follows:</para>
 		<itemizedlist>
@@ -1100,6 +1099,13 @@ add_sock_hdr("Sock-Info");
 			pseudo-variables that are evaluated at runtime.
 			</para>
 		</listitem>
+		<listitem>
+			<para>
+			<emphasis>ruid</emphasis> - The record unique ID for a
+			a specific contact to be removed. It can contain
+			pseudo-variables that are evaluated at runtime.
+			</para>
+		</listitem>
 		</itemizedlist>
 		<para>
 		This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
@@ -1110,6 +1116,7 @@ add_sock_hdr("Sock-Info");
 ...
 unregister("location", "$ru");
 unregister("location", "sip:[email protected]");
+unregister("location", "$ru", "$ulc(caller=>ruid)");
 ...
 </programlisting>
 		</example>
@@ -1277,11 +1284,7 @@ event_route[usrloc:contact-expired] {
 				<listitem>
 				<para><emphasis>aorhash</emphasis> - hash id for the record
 				</para>
-				</listitem>	  
-				<listitem>
-				<para><emphasis>count</emphasis> - number of contacts
-				</para>
-				</listitem>	  
+				</listitem>
 				<listitem>
 				<para><emphasis>addr</emphasis> - contact address
 				</para>
@@ -1307,6 +1310,10 @@ event_route[usrloc:contact-expired] {
 				</para>
 				</listitem>	  
 				<listitem>
+				<para><emphasis>cseq</emphasis> - the cseq value
+				</para>
+				</listitem>	  
+				<listitem>
 				<para><emphasis>flags</emphasis> - flags value
 				</para>
 				</listitem>	  
@@ -1330,6 +1337,22 @@ event_route[usrloc:contact-expired] {
 				<para><emphasis>methods</emphasis> - methods value
 				</para>
 				</listitem>	  
+				<listitem>
+				<para><emphasis>count</emphasis> - number of contacts
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>ruid</emphasis> - record unique ID
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>reg-id</emphasis> - reg-id value
+				</para>
+				</listitem>	  
+				<listitem>
+				<para><emphasis>instance</emphasis> - instance value
+				</para>
+				</listitem>	  
 			</itemizedlist>
 			<para>
 				The pseudo-variable accepts positive index value to access

+ 13 - 1
modules/registrar/lookup.c

@@ -273,6 +273,13 @@ int lookup(struct sip_msg* _m, udomain_t* _d, str* _uri)
 		
 		_m->reg_id = ptr->reg_id;
 
+		if (ptr->ruid.len) {
+		    if (set_ruid(_m, &(ptr->ruid)) < 0) {
+				ret = -3;
+				goto done;
+		    }
+		}
+
 		set_ruri_q(ptr->q);
 
 		old_bflags = 0;
@@ -316,7 +323,8 @@ int lookup(struct sip_msg* _m, udomain_t* _d, str* _uri)
 					  &ptr->path, ptr->q, ptr->cflags,
 					  ptr->sock,
 					  ptr->instance.len?&(ptr->instance):0,
-				          ptr->instance.len?ptr->reg_id:0)
+				          ptr->instance.len?ptr->reg_id:0,
+					  &ptr->ruid)
 			    == -1) {
 				LM_ERR("failed to append a branch\n");
 				/* Also give a chance to the next branches*/
@@ -351,6 +359,7 @@ int reset_ruri_branch(sip_msg_t *msg)
 	setbflagsval(0, 0);
 	reset_instance(msg);
 	msg->reg_id = 0;
+	reset_ruid(msg);
 	return 0;
 }
 
@@ -375,6 +384,7 @@ int lookup_branches(sip_msg_t *msg, udomain_t *d)
 	flag_t ruri_b_flags = 0;
 	str ruri_b_instance = {0};
 	unsigned int ruri_b_reg_id = 0;
+	str ruri_b_ruid = {0};
 	branch_t *crt = NULL;
 
 	ret = 1;
@@ -399,6 +409,7 @@ int lookup_branches(sip_msg_t *msg, udomain_t *d)
 	getbflagsval(0, &ruri_b_flags);
 	ruri_b_instance = msg->instance;
 	ruri_b_reg_id = msg->reg_id;
+	ruri_b_ruid = msg->ruid;
 	reset_ruri_branch(msg);
 
 	for(i=0; i<nr_branches_start; i++) {
@@ -478,6 +489,7 @@ done:
 	setbflagsval(0, ruri_b_flags);
 	msg->instance = ruri_b_instance;
 	msg->reg_id = ruri_b_reg_id;
+	msg->ruid = ruri_b_ruid;
 
 	return (found)?1:ret;
 }

+ 26 - 2
modules/registrar/reg_mod.c

@@ -94,6 +94,7 @@ static int w_lookup(struct sip_msg* _m, char* _d, char* _p2);
 static int w_lookup_branches(struct sip_msg* _m, char* _d, char* _p2);
 static int w_registered(struct sip_msg* _m, char* _d, char* _uri);
 static int w_unregister(struct sip_msg* _m, char* _d, char* _uri);
+static int w_unregister2(struct sip_msg* _m, char* _d, char* _uri, char *_ruid);
 
 /*! \brief Fixup functions */
 static int domain_fixup(void** param, int param_no);
@@ -186,6 +187,8 @@ static cmd_export_t cmds[] = {
 			REQUEST_ROUTE },
 	{"unregister",   (cmd_function)w_unregister,  2,  unreg_fixup, 0,
 			REQUEST_ROUTE| FAILURE_ROUTE },
+	{"unregister",   (cmd_function)w_unregister2, 3, unreg_fixup, 0,
+			REQUEST_ROUTE| FAILURE_ROUTE },
 	{"reg_fetch_contacts", (cmd_function)pv_fetch_contacts, 3, 
 			fetchc_fixup, 0,
 			REQUEST_ROUTE| FAILURE_ROUTE },
@@ -400,7 +403,7 @@ static int mod_init(void)
 	}
 
 	if (reg_flow_timer < 0 || reg_flow_timer > REG_FLOW_TIMER_MAX
-			|| (reg_flow_timer > 0 && reg_outbound_mode != REG_OUTBOUND_REQUIRE)) {
+			|| (reg_flow_timer > 0 && reg_outbound_mode == REG_OUTBOUND_NONE)) {
 		LM_ERR("bad value for flow_timer\n");
 		return -1;
 	}
@@ -501,7 +504,26 @@ static int w_unregister(struct sip_msg* _m, char* _d, char* _uri)
 		return -1;
 	}
 
-	return unregister(_m, (udomain_t*)_d, &uri);
+	return unregister(_m, (udomain_t*)_d, &uri, NULL);
+}
+
+static int w_unregister2(struct sip_msg* _m, char* _d, char* _uri, char *_ruid)
+{
+	str uri = {0};
+	str ruid = {0};
+	if(fixup_get_svalue(_m, (gparam_p)_uri, &uri)!=0 || uri.len<=0)
+	{
+		LM_ERR("invalid uri parameter\n");
+		return -1;
+	}
+	if(fixup_get_svalue(_m, (gparam_p)_ruid, &ruid)!=0 || ruid.len<=0)
+	{
+		LM_ERR("invalid ruid parameter\n");
+		return -1;
+	}
+
+
+	return unregister(_m, (udomain_t*)_d, &uri, &ruid);
 }
 
 /*! \brief
@@ -546,6 +568,8 @@ static int unreg_fixup(void** param, int param_no)
 		return domain_fixup(param, 1);
 	} else if (param_no == 2) {
 		return fixup_spve_null(param, 1);
+	} else if (param_no == 3) {
+		return fixup_spve_null(param, 1);
 	}
 	return 0;
 }

+ 36 - 10
modules/registrar/reply.c

@@ -39,7 +39,8 @@
 #include "../../ut.h"
 #include "../../xavp.h"
 #include "../../parser/msg_parser.h"
-#include "../../lib/kcore/parse_supported.h"
+#include "../../parser/parse_require.h"
+#include "../../parser/parse_supported.h"
 #include "../../data_lump_rpl.h"
 #include "../usrloc/usrloc.h"
 #include "rerrno.h"
@@ -188,7 +189,7 @@ int build_contact(sip_msg_t *msg, ucontact_t* c, str *host)
 
 
 	if(msg!=NULL && parse_supported(msg)==0
-			&& (get_supported(msg) & F_SUPPORTED_GRUU))
+			&& (get_supported(msg) & F_OPTION_TAG_GRUU))
 		mode = 1;
 	else
 		mode = 0;
@@ -374,6 +375,7 @@ int build_contact(sip_msg_t *msg, ucontact_t* c, str *host)
 #define MSG_400 "Bad Request"
 #define MSG_420 "Bad Extension"
 #define MSG_421 "Extension Required"
+#define MSG_439 "First Hop Lacks Outbound Support"
 #define MSG_500 "Server Internal Error"
 #define MSG_503 "Service Unavailable"
 
@@ -408,6 +410,9 @@ int build_contact(sip_msg_t *msg, ucontact_t* c, str *host)
 #define EI_R_PARSE_PATH  "Path parse error"                         /* R_PARSE_PATH */
 #define EI_R_PATH_UNSUP  "No support for found Path indicated"      /* R_PATH_UNSUP */
 #define EI_R_OB_UNSUP    "No support for Outbound indicated"        /* R_OB_UNSUP */
+#define EI_R_OB_REQD     "No support for Outbound on server"        /* R_OB_REQD */
+#define EI_R_OB_UNSUP_EDGE "No support for Outbound on edge proxy"  /* R_OB_UNSUP_EDGE */
+
 
 str error_info[] = {
 	{EI_R_FINE,       sizeof(EI_R_FINE) - 1},
@@ -441,7 +446,8 @@ str error_info[] = {
 	{EI_R_PARSE_PATH, sizeof(EI_R_PARSE_PATH) - 1},
 	{EI_R_PATH_UNSUP, sizeof(EI_R_PATH_UNSUP) - 1},
 	{EI_R_OB_UNSUP,   sizeof(EI_R_OB_UNSUP) - 1},
-
+	{EI_R_OB_REQD,    sizeof(EI_R_OB_REQD) - 1},
+	{EI_R_OB_UNSUP_EDGE, sizeof(EI_R_OB_UNSUP_EDGE) - 1},
 };
 
 int codes[] = {
@@ -475,8 +481,9 @@ int codes[] = {
 	400, /* R_CALLID_LEN */
 	400, /* R_PARSE_PATH */
 	420, /* R_PATH_UNSUP */
-	421  /* R_OB_UNSUP */
-
+	421, /* R_OB_UNSUP */
+	420, /* R_OB_REQD */
+	439, /* R_OB_UNSUP_EDGE */
 };
 
 
@@ -610,8 +617,8 @@ static int add_flow_timer(struct sip_msg* _m)
  */
 int reg_send_reply(struct sip_msg* _m)
 {
-	str unsup = str_init(SUPPORTED_PATH_STR);
-	str outbound_str = str_init(SUPPORTED_OUTBOUND_STR);
+	str unsup = str_init(OPTION_TAG_PATH_STR);
+	str outbound_str = str_init(OPTION_TAG_OUTBOUND_STR);
 	long code;
 	str msg = str_init(MSG_200); /* makes gcc shut up */
 	char* buf;
@@ -632,7 +639,7 @@ int reg_send_reply(struct sip_msg* _m)
 					if (add_path(_m, &_m->path_vec) < 0)
 						return -1;
 				}
-				else if (get_supported(_m) & F_SUPPORTED_PATH) {
+				else if (get_supported(_m) & F_OPTION_TAG_PATH) {
 					if (add_path(_m, &_m->path_vec) < 0)
 						return -1;
 				} else if (path_mode == PATH_MODE_STRICT) {
@@ -654,14 +661,28 @@ int reg_send_reply(struct sip_msg* _m)
 			if (add_require(_m, &outbound_str) < 0)
 				return -1;
 
+			if (add_supported(_m, &outbound_str) < 0)
+				return -1;
+
 			if (reg_flow_timer > 0) {
 				if (add_flow_timer(_m) < 0)
 					return -1;
 			}
-			/* Fall-thru */
+			break;
 		case REG_OUTBOUND_SUPPORTED:
 			if (add_supported(_m, &outbound_str) < 0)
 				return -1;
+
+			if ((get_require(_m) & F_OPTION_TAG_OUTBOUND)
+			    || (get_supported(_m) & F_OPTION_TAG_OUTBOUND)) {
+				if (add_require(_m, &outbound_str) < 0)
+					return -1;
+
+				if (reg_flow_timer > 0) {
+					if (add_flow_timer(_m) < 0)
+						return -1;
+				}
+			}
 			break;
 		}
 		break;
@@ -671,6 +692,10 @@ int reg_send_reply(struct sip_msg* _m)
 		if (add_supported(_m, &outbound_str) < 0)
 			return -1;
 		break;
+	case R_OB_REQD:
+		if (add_unsupported(_m, &outbound_str) < 0)
+			return -1;
+		break;
 	default:
 		break;
 	}
@@ -680,7 +705,8 @@ int reg_send_reply(struct sip_msg* _m)
 	case 200: msg.s = MSG_200; msg.len = sizeof(MSG_200)-1;break;
 	case 400: msg.s = MSG_400; msg.len = sizeof(MSG_400)-1;break;
 	case 420: msg.s = MSG_420; msg.len = sizeof(MSG_420)-1;break;
-	case 421: msg.s = MSG_420; msg.len = sizeof(MSG_421)-1;break;
+	case 421: msg.s = MSG_421; msg.len = sizeof(MSG_421)-1;break;
+	case 439: msg.s = MSG_439; msg.len = sizeof(MSG_439)-1;break;
 	case 500: msg.s = MSG_500; msg.len = sizeof(MSG_500)-1;break;
 	case 503: msg.s = MSG_503; msg.len = sizeof(MSG_503)-1;break;
 	}

+ 3 - 1
modules/registrar/rerrno.h

@@ -64,7 +64,9 @@ typedef enum rerr {
 	R_CALLID_LEN, /*!< Callid too long */
 	R_PARSE_PATH, /*!< Error while parsing Path */
 	R_PATH_UNSUP, /*!< Path not supported by UAC */
-	R_OB_UNSUP    /*!< Outbound not supported by UAC */
+	R_OB_UNSUP,   /*!< Outbound not supported by UAC */
+	R_OB_REQD,    /*!< Outbound required by UAC but not supported on server */
+	R_OB_UNSUP_EDGE, /*!< Outbound needed for this registration but not supported on edge proxy */
 
 } rerr_t;
 

+ 109 - 26
modules/registrar/save.c

@@ -50,6 +50,7 @@
 #include "../../parser/parse_allow.h"
 #include "../../parser/parse_methods.h"
 #include "../../parser/msg_parser.h"
+#include "../../parser/parse_rr.h"
 #include "../../parser/parse_to.h"
 #include "../../parser/parse_uri.h"
 #include "../../dprint.h"
@@ -61,7 +62,8 @@
 #include "../../mod_fix.h"
 #include "../../lib/srutils/sruid.h"
 #include "../../lib/kcore/cmpapi.h"
-#include "../../lib/kcore/parse_supported.h"
+#include "../../parser/parse_require.h"
+#include "../../parser/parse_supported.h"
 #include "../../lib/kcore/statistics.h"
 #ifdef USE_TCP
 #include "../../tcp_server.h"
@@ -219,8 +221,7 @@ static inline int no_contacts(sip_msg_t *_m, udomain_t* _d, str* _a, str* _h)
 /*! \brief
  * Fills the common part (for all contacts) of the info structure
  */
-static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
-											unsigned int _e, unsigned int _f)
+static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c, unsigned int _e, unsigned int _f, int _use_regid)
 {
 	static ucontact_info_t ci;
 	static str no_ua = str_init("n/a");
@@ -371,7 +372,7 @@ static inline ucontact_info_t* pack_ci( struct sip_msg* _m, contact_t* _c,
 		}
 		if(_c->instance!=NULL && _c->instance->body.len>0)
 			ci.instance = _c->instance->body;
-		if(_c->instance!=NULL && _c->reg_id!=NULL && _c->reg_id->body.len>0) {
+		if(_use_regid && _c->instance!=NULL && _c->reg_id!=NULL && _c->reg_id->body.len>0) {
 			if(str2int(&_c->reg_id->body, &ci.reg_id)<0 || ci.reg_id==0)
 			{
 				LM_ERR("invalid reg-id value\n");
@@ -430,7 +431,7 @@ int reg_get_crt_max_contacts(void)
  * and insert all contacts from the message that have expires
  * > 0
  */
-static inline int insert_contacts(struct sip_msg* _m, udomain_t* _d, str* _a)
+static inline int insert_contacts(struct sip_msg* _m, udomain_t* _d, str* _a, int _use_regid)
 {
 	ucontact_info_t* ci;
 	urecord_t* r = NULL;
@@ -486,7 +487,7 @@ static inline int insert_contacts(struct sip_msg* _m, udomain_t* _d, str* _a)
 		}
 
 		/* pack the contact_info */
-		if ( (ci=pack_ci( (ci==0)?_m:0, _c, expires, flags))==0 ) {
+		if ( (ci=pack_ci( (ci==0)?_m:0, _c, expires, flags, _use_regid))==0 ) {
 			LM_ERR("failed to extract contact info\n");
 			goto error;
 		}
@@ -613,8 +614,7 @@ static int test_max_contacts(struct sip_msg* _m, urecord_t* _r, contact_t* _c,
  * 3) If contact in usrloc exists and expires
  *    == 0, delete contact
  */
-static inline int update_contacts(struct sip_msg* _m, urecord_t* _r,
-										int _mode)
+static inline int update_contacts(struct sip_msg* _m, urecord_t* _r, int _mode, int _use_regid)
 {
 	ucontact_info_t *ci;
 	ucontact_t *c, *ptr, *ptr0;
@@ -633,7 +633,7 @@ static inline int update_contacts(struct sip_msg* _m, urecord_t* _r,
 
 	rc = 0;
 	/* pack the contact_info */
-	if ( (ci=pack_ci( _m, 0, 0, flags))==0 ) {
+	if ( (ci=pack_ci( _m, 0, 0, flags, _use_regid))==0 ) {
 		LM_ERR("failed to initial pack contact info\n");
 		goto error;
 	}
@@ -664,7 +664,7 @@ static inline int update_contacts(struct sip_msg* _m, urecord_t* _r,
 		calc_contact_expires(_m, _c->expires, &expires);
 
 		/* pack the contact info */
-		if ( (ci=pack_ci( 0, _c, expires, 0))==0 ) {
+		if ( (ci=pack_ci( 0, _c, expires, 0, _use_regid))==0 ) {
 			LM_ERR("failed to pack contact specific info\n");
 			goto error;
 		}
@@ -780,7 +780,7 @@ error:
  * contained some contact header fields
  */
 static inline int add_contacts(struct sip_msg* _m, udomain_t* _d,
-		str* _a, int _mode)
+		str* _a, int _mode, int _use_regid)
 {
 	int res;
 	int ret;
@@ -802,7 +802,7 @@ static inline int add_contacts(struct sip_msg* _m, udomain_t* _d,
 	}
 
 	if (res == 0) { /* Contacts found */
-		if ((ret=update_contacts(_m, r, _mode)) < 0) {
+		if ((ret=update_contacts(_m, r, _mode, _use_regid)) < 0) {
 			build_contact(_m, r->contacts, &u->host);
 			ul.release_urecord(r);
 			ul.unlock_udomain(_d, _a);
@@ -811,7 +811,7 @@ static inline int add_contacts(struct sip_msg* _m, udomain_t* _d,
 		build_contact(_m, r->contacts, &u->host);
 		ul.release_urecord(r);
 	} else {
-		if (insert_contacts(_m, _d, _a) < 0) {
+		if (insert_contacts(_m, _d, _a, _use_regid) < 0) {
 			ul.unlock_udomain(_d, _a);
 			return -4;
 		}
@@ -833,6 +833,12 @@ int save(struct sip_msg* _m, udomain_t* _d, int _cflags, str *_uri)
 	str aor;
 	int ret;
 	sip_uri_t *u;
+	rr_t *route;
+	struct sip_uri puri;
+	param_hooks_t hooks;
+	param_t *params;
+	contact_t *contact;
+	int use_ob = 1, use_regid = 1;
 
 	u = parse_to_uri(_m);
 	if(u==NULL)
@@ -850,13 +856,70 @@ int save(struct sip_msg* _m, udomain_t* _d, int _cflags, str *_uri)
 	}
 
 	if (parse_supported(_m) == 0) {
-		if (!(((struct supported_body *)_m->supported->parsed)->supported_all
-				& F_SUPPORTED_OUTBOUND) && reg_outbound_mode == REG_OUTBOUND_REQUIRE) {
+		if (!(get_supported(_m)	& F_OPTION_TAG_OUTBOUND)
+				&& reg_outbound_mode == REG_OUTBOUND_REQUIRE) {
 			LM_WARN("Outbound required by server and not supported by UAC\n");
 			rerrno = R_OB_UNSUP;
 			goto error;
 		}
 	}
+
+	if (parse_require(_m) == 0) {
+		if ((get_require(_m) & F_OPTION_TAG_OUTBOUND)
+				&& reg_outbound_mode == REG_OUTBOUND_NONE) {
+			LM_WARN("Outbound required by UAC and not supported by server\n");
+			rerrno = R_OB_REQD;
+			goto error;
+		}
+	}
+
+	if (reg_outbound_mode != REG_OUTBOUND_NONE
+		&& !(parse_headers(_m, HDR_VIA2_F, 0) == -1 || _m->via2 == 0
+			|| _m->via2->error != PARSE_OK)) {
+		/* Outbound supported on server, and more than one Via: - not the first hop */
+
+		if (!(parse_headers(_m, HDR_PATH_F, 0) == -1 || _m->path == 0)) {
+			if (parse_rr_body(_m->path->body.s, _m->path->body.len, &route) < 0) {
+				LM_ERR("Failed to parse Path: header body\n");
+				goto error;
+			}
+			if (parse_uri(route->nameaddr.uri.s, route->nameaddr.uri.len, &puri) < 0) {
+				LM_ERR("Failed to parse Path: URI\n");
+				goto error;
+			}
+			if (parse_params(&puri.params, CLASS_URI, &hooks, &params) != 0) {
+				LM_ERR("Failed to parse Path: URI parameters\n");
+				goto error;
+			}
+			if (!hooks.uri.ob) {
+				/* No ;ob parameter to top Path: URI - no outbound */
+				use_ob = 0;
+			}
+
+		} else {
+			/* No Path: header - no outbound */
+			use_ob = 0;
+
+		}
+
+		contact = ((contact_body_t *) _m->contact->parsed)->contacts;
+		if (!contact) {
+			LM_ERR("empty Contact:\n");
+			goto error;
+		}
+
+		if (use_ob == 0) {
+			if ((get_supported(_m) & F_OPTION_TAG_OUTBOUND)
+			    && contact->reg_id) {
+				LM_WARN("Outbound used by UAC but not supported by edge proxy\n");
+				rerrno = R_OB_UNSUP_EDGE;
+				goto error;
+			} else {
+				/* ignore ;reg-id parameter */
+				use_regid = 0;
+			}
+		}
+	}
 	
 	get_act_time();
 	c = get_first_contact(_m);
@@ -878,7 +941,7 @@ int save(struct sip_msg* _m, udomain_t* _d, int _cflags, str *_uri)
 		}
 	} else {
 		mode = is_cflag_set(REG_SAVE_REPL_FL)?1:0;
-		if ((ret=add_contacts(_m, (udomain_t*)_d, &aor, mode)) < 0)
+		if ((ret=add_contacts(_m, (udomain_t*)_d, &aor, mode, use_regid)) < 0)
 			goto error;
 		ret = (ret==0)?1:ret;
 	}
@@ -898,26 +961,46 @@ error:
 	return 0;
 }
 
-int unregister(struct sip_msg* _m, udomain_t* _d, str* _uri)
+int unregister(struct sip_msg* _m, udomain_t* _d, str* _uri, str *_ruid)
 {
 	str aor = {0, 0};
 	sip_uri_t *u;
-
-	u = parse_to_uri(_m);
-	if(u==NULL)
-		return -2;
-
+	urecord_t *r;
+	ucontact_t *c;
 
 	if (extract_aor(_uri, &aor, NULL) < 0) {
 		LM_ERR("failed to extract Address Of Record\n");
 		return -1;
 	}
 
-	if (star(_m, _d, &aor, &u->host) < 0)
-	{
-		LM_ERR("error unregistering user [%.*s]\n", aor.len, aor.s);
-		return -1;
+	if (_ruid == NULL) {
+		/* No ruid provided - remove all contacts for aor */
+
+		u = parse_to_uri(_m);
+		if(u==NULL)
+			return -2;
+
+		if (star(_m, _d, &aor, &u->host) < 0)
+		{
+			LM_ERR("error unregistering user [%.*s]\n", aor.len, aor.s);
+			return -1;
+		}
+	} else {
+		/* ruid provided - remove a specific contact */
+
+		if (ul.get_urecord_by_ruid(_d, ul.get_aorhash(&aor),
+				_ruid, &r, &c) != 0) {
+			LM_WARN("AOR/Contact not found\n");
+			return -1;
+		}
+		if (ul.delete_ucontact(r, c) != 0) {
+			LM_WARN("could not delete contact\n");
+			return -1;
+		}
+		ul.release_urecord(r);
+		ul.unlock_udomain(_d, &aor);
 	}
+
 	return 1;
 }
 

+ 1 - 2
modules/registrar/save.h

@@ -47,8 +47,7 @@
  * Process REGISTER request and save it's contacts
  */
 int save(struct sip_msg* _m, udomain_t* _d, int _cflags, str* _uri);
-int unregister(struct sip_msg* _m, udomain_t* _d, str* _uri);
+int unregister(struct sip_msg* _m, udomain_t* _d, str* _uri, str *_ruid);
 int set_q_override(struct sip_msg* _m, int _q);
 
-
 #endif /* SAVE_H */

+ 2 - 2
modules/rls/subscribe.c

@@ -35,7 +35,7 @@
 #include "../../data_lump_rpl.h"
 #include "../../lib/kcore/cmpapi.h"
 #include "../../hashes.h"
-#include "../../lib/kcore/parse_supported.h"
+#include "../../parser/parse_supported.h"
 #include "../../parser/msg_parser.h"
 #include "../../parser/parse_event.h"
 #include "../../parser/parse_expires.h"
@@ -525,7 +525,7 @@ int rls_handle_subscribe(struct sip_msg* msg, str watcher_user, str watcher_doma
 		return -1;
 	}
 
-	if(!(get_supported(msg) & F_SUPPORTED_EVENTLIST))
+	if(!(get_supported(msg) & F_OPTION_TAG_EVENTLIST))
 	{
 		LM_DBG("No support for 'eventlist' - not for rls\n");
 		goto forpresence;

+ 3 - 3
modules/sst/sst_handlers.c

@@ -55,7 +55,7 @@
 
 #include "../../pvar.h"
 #include "../../lib/kcore/parse_sst.h"
-#include "../../lib/kcore/parse_supported.h"
+#include "../../parser/parse_supported.h"
 #include "../../mem/mem.h"
 #include "../../mem/shm_mem.h"
 #include "../../data_lump.h"
@@ -884,8 +884,8 @@ static int parse_msg_for_sst_info(struct sip_msg *msg, sst_msg_info_t *minfo)
 	 * it is not found if unsuccessfull.
 	 */
 	if ((rtn = parse_supported(msg)) == 0) {
-		if ((((struct supported_body*)msg->supported->parsed)->supported_all
-						& F_SUPPORTED_TIMER)) {
+		if ((((struct option_tag_body*)msg->supported->parsed)->option_tags_all
+						& F_OPTION_TAG_TIMER)) {
 			minfo->supported = 1;
 		}
 	}

+ 1 - 1
modules/tm/t_fwd.c

@@ -1493,7 +1493,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 
 	init_branch_iterator();
 	while((current_uri.s=next_branch( &current_uri.len, &q, &dst_uri, &path,
-										&bflags, &si))) {
+										&bflags, &si, 0))) {
 		try_new++;
 		setbflagsval(0, bflags);
 

+ 46 - 10
modules/tm/t_serial.c

@@ -50,6 +50,7 @@ struct contact {
     str path;
     struct socket_info* sock;
     str instance;
+    str ruid;
     unsigned int flags;
     unsigned short q_flag;
     struct contact *next;
@@ -92,9 +93,11 @@ static str sock_name = {"sock", 4};
 static str instance_name = {"instance", 8};
 static str flags_name = {"flags", 5};
 static str q_flag_name = {"q_flag", 6};
+static str ruid_name = {"ruid", 4};
 
 void add_contacts_avp(str *uri, str *dst_uri, str *path, str *sock_str,
-		      unsigned int flags, unsigned int q_flag, str *instance)
+		      unsigned int flags, unsigned int q_flag, str *instance,
+		      str *ruid)
 {
     sr_xavp_t *record;
     sr_xval_t val;
@@ -136,6 +139,12 @@ void add_contacts_avp(str *uri, str *dst_uri, str *path, str *sock_str,
 	xavp_add_value(&instance_name, &val, &record);
     }
 
+    if (ruid->len > 0) {
+	val.type = SR_XTYPE_STR;
+	val.v.s = *ruid;
+	xavp_add_value(&ruid_name, &val, &record);
+    }
+
     val.type = SR_XTYPE_XAVP;
     val.v.xavp = record;
     xavp_add_value(&contacts_avp, &val, NULL);
@@ -192,6 +201,7 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value)
 	contacts->path = msg->path_vec;
 	contacts->q = get_ruri_q();
 	contacts->instance = msg->instance;
+        contacts->ruid = msg->ruid;
 	first_idx = 0;
     } else {
 	/* Insert first branch to first contact */
@@ -207,6 +217,8 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value)
 	contacts->q = branch->q;
 	contacts->instance.s = branch->instance;
 	contacts->instance.len = branch->instance_len;
+        contacts->ruid.s = branch->ruid;
+        contacts->ruid.len = branch->ruid_len;
 	first_idx = 1;
     }
 
@@ -233,6 +245,8 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value)
 	next->q = branch->q;
 	next->instance.s = branch->instance;
 	next->instance.len = branch->instance_len;
+	next->ruid.s = branch->ruid;
+	next->ruid.len = branch->ruid_len;
 	next->next = (struct contact *)0;
 
 	prev = (struct contact *)0;
@@ -283,7 +297,7 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value)
 
 	add_contacts_avp(&(curr->uri), &(curr->dst_uri), &(curr->path),
 			 &sock_str, curr->flags, curr->q_flag,
-			 &(curr->instance));
+			 &(curr->instance), &(curr->ruid));
 
 	curr = curr->next;
     }
@@ -298,7 +312,7 @@ int t_load_contacts(struct sip_msg* msg, char* key, char* value)
 }
 
 void add_contact_flows_avp(str *uri, str *dst_uri, str *path, str *sock_str,
-			   unsigned int flags, str *instance)
+			   unsigned int flags, str *instance, str *ruid)
 {
     sr_xavp_t *record;
     sr_xval_t val;
@@ -332,6 +346,12 @@ void add_contact_flows_avp(str *uri, str *dst_uri, str *path, str *sock_str,
 	xavp_add_value(&instance_name, &val, &record);
     }
 
+    if (ruid->len > 0) {
+	val.type = SR_XTYPE_STR;
+	val.v.s = *ruid;
+	xavp_add_value(&ruid_name, &val, &record);
+    }
+
     val.type = SR_XTYPE_INT;
     val.v.i = flags;
     xavp_add_value(&flags_name, &val, &record);
@@ -354,7 +374,7 @@ void add_contact_flows_avp(str *uri, str *dst_uri, str *path, str *sock_str,
  * there was nothing to do. Returns -1 in case of an error. */
 int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 {
-    str uri, dst_uri, path, instance, host, sock_str;
+    str uri, dst_uri, path, instance, host, sock_str, ruid;
     struct socket_info *sock;
     unsigned int flags, q_flag;
     sr_xavp_t *xavp_list, *xavp, *prev_xavp, *vavp;
@@ -443,6 +463,9 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 	il->next = (struct instance_list *)0;
     }
 
+    vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
+    ruid = vavp->val.v.s;
+
     /* Rewrite Request-URI */
     rewrite_uri(msg, &uri);
 
@@ -462,6 +485,8 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 
     setbflagsval(0, flags);
 
+    set_ruid(msg, &ruid);
+
     /* Check if there was only one contact at this priority */
     if (q_flag) {
 	xavp_rm(xavp, NULL);
@@ -535,7 +560,7 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 	    }
 	    if (ilp) {
 		add_contact_flows_avp(&uri, &dst_uri, &path, &sock_str,
-				      flags, &instance);
+				      flags, &instance, &ruid);
 		goto check_q_flag;
 	    }
 	    if (!q_flag) {
@@ -560,8 +585,11 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
 	    }
 	}
 
-	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, 0, 0)
-	    != 1) {
+        vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
+        ruid = vavp->val.v.s;
+
+	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, 0, 0,
+			  &ruid) != 1) {
 	    LM_ERR("appending branch failed\n");
 	    free_instance_list(il);
 	    xavp_destroy_list(&xavp_list);
@@ -596,7 +624,7 @@ int t_next_contacts(struct sip_msg* msg, char* key, char* value)
  * there was nothing to do. Returns -1 in case of an error. */
 int t_next_contact_flows(struct sip_msg* msg, char* key, char* value)
 {
-    str uri, dst_uri, path, instance, host;
+    str uri, dst_uri, path, instance, host, ruid;
     struct socket_info *sock;
     unsigned int flags;
     sr_xavp_t *xavp_list, *xavp, *next_xavp, *vavp;
@@ -683,6 +711,9 @@ int t_next_contact_flows(struct sip_msg* msg, char* key, char* value)
 	il->next = (struct instance_list *)0;
     }
 
+    vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
+    ruid = vavp->val.v.s;
+
     /* Rewrite Request-URI */
     rewrite_uri(msg, &uri);
 
@@ -700,6 +731,8 @@ int t_next_contact_flows(struct sip_msg* msg, char* key, char* value)
 
     set_force_socket(msg, sock);
 
+    set_ruid(msg, &ruid);
+
     setbflagsval(0, flags);
 
     /* Append branches until out of branches. */
@@ -793,8 +826,11 @@ int t_next_contact_flows(struct sip_msg* msg, char* key, char* value)
 	vavp = xavp_get(&flags_name, xavp->val.v.xavp);
 	flags = vavp->val.v.i;
 
-	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, 0, 0)
-	    != 1) {
+        vavp = xavp_get(&ruid_name, xavp->val.v.xavp);
+        ruid = vavp->val.v.s;
+
+	if (append_branch(msg, &uri, &dst_uri, &path, 0, flags, sock, 0, 0,
+			  &ruid) != 1) {
 	    LM_ERR("appending branch failed\n");
 	    free_instance_list(il);
 	    xavp_destroy_list(&xavp_list);

+ 3 - 3
modules/xprint/xp_lib.c

@@ -700,7 +700,7 @@ static int xl_get_branch(struct sip_msg *msg, str *res, str *hp, int hi, int hf)
 
 
 	init_branch_iterator();
-	branch.s = next_branch(&branch.len, &q, 0, 0, 0, 0);
+	branch.s = next_branch(&branch.len, &q, 0, 0, 0, 0, 0);
 	if (!branch.s) {
 		return xl_get_null(msg, res, hp, hi, hf);
 	}
@@ -731,7 +731,7 @@ static int xl_get_branches(struct sip_msg *msg, str *res, str *hp, int hi, int h
 	cnt = len = 0;
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0)))
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0)))
 	{
 		cnt++;
 		len += uri.len;
@@ -756,7 +756,7 @@ static int xl_get_branches(struct sip_msg *msg, str *res, str *hp, int hi, int h
 	p = local_buf;
 
 	init_branch_iterator();
-	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0)))
+	while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0, 0)))
 	{
 		if (i)
 		{

+ 2 - 0
parser/keys.h

@@ -118,6 +118,8 @@
 #define _tity_ 0x79746974   /* "tity" */
 #define _info_ 0x6f666e69   /* "info" */
 #define _path_ 0x68746170   /* "path" */
+#define _100r_ 0x72303031   /* "100r" */
+#define _time_ 0x656d6974   /* "time" */
 
 #define _pt_l_ 0x6c2d7470   /* "pt-l" */
 #define _angu_ 0x75676e61   /* "angu" */

+ 39 - 0
parser/msg_parser.c

@@ -859,6 +859,45 @@ void reset_instance(struct sip_msg* const msg)
 }
 
 
+int set_ruid(struct sip_msg* msg, str* ruid)
+{
+	char* ptr;
+
+	if (unlikely(!msg || !ruid)) {
+		LM_ERR("invalid ruid parameter value\n");
+		return -1;
+	}
+
+	if (unlikely(ruid->len == 0)) {
+		reset_ruid(msg);
+	} else if (msg->ruid.s && (msg->ruid.len >= ruid->len)) {
+		memcpy(msg->ruid.s, ruid->s, ruid->len);
+		msg->ruid.len = ruid->len;
+	} else {
+		ptr = (char*)pkg_malloc(ruid->len);
+		if (!ptr) {
+			LM_ERR("not enough pkg memory for ruid\n");
+			return -1;
+		}
+		memcpy(ptr, ruid->s, ruid->len);
+		if (msg->ruid.s) pkg_free(msg->ruid.s);
+		msg->ruid.s = ptr;
+		msg->ruid.len = ruid->len;
+	}
+	return 0;
+}
+
+
+void reset_ruid(struct sip_msg* const msg)
+{
+	if(msg->ruid.s != 0) {
+		pkg_free(msg->ruid.s);
+	}
+	msg->ruid.s = 0;
+	msg->ruid.len = 0;
+}
+
+
 hdr_field_t* get_hdr(const sip_msg_t* const msg, const enum _hdr_types_t ht)
 {
 	hdr_field_t *hdr;

+ 5 - 0
parser/msg_parser.h

@@ -365,6 +365,7 @@ typedef struct sip_msg {
 	str path_vec;
         str instance;
         unsigned int reg_id;
+	str ruid;
 } sip_msg_t;
 
 /*! \brief pointer to a fakes message which was never received ;
@@ -453,6 +454,10 @@ int set_instance(struct sip_msg* msg, str* instance);
 
 void reset_instance(struct sip_msg* const msg);
 
+int set_ruid(struct sip_msg* msg, str* ruid);
+
+void reset_ruid(struct sip_msg* const msg);
+
 /** force a specific send socket for forwarding a request.
  * @param msg - sip msg.
  * @param fsocket - forced socket, pointer to struct socket_info, can be 0 (in

+ 40 - 0
parser/parse_option_tags.c

@@ -0,0 +1,40 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "../mem/mem.h"
+#include "parse_option_tags.h"
+
+static inline void free_option_tag(struct option_tag_body **otb)
+{
+	if (otb && *otb) {
+		pkg_free(*otb);
+		*otb = 0;
+	}
+}
+
+void hf_free_option_tag(void *parsed)
+{
+	struct option_tag_body *otb;
+	otb = (struct option_tag_body *) parsed;
+	free_option_tag(&otb);
+}

+ 166 - 0
parser/parse_option_tags.h

@@ -0,0 +1,166 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ * 
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef OPTION_TAGS_H
+#define OPTION_TAGS_H
+
+#include <strings.h>
+#include "hf.h"
+#include "keys.h"
+
+#define F_OPTION_TAG_PATH	(1 << 0)
+#define F_OPTION_TAG_100REL	(1 << 1)
+#define F_OPTION_TAG_TIMER	(1 << 2)
+#define F_OPTION_TAG_EVENTLIST	(1 << 3)
+#define F_OPTION_TAG_GRUU	(1 << 4)
+#define F_OPTION_TAG_OUTBOUND	(1 << 5)
+
+#define OPTION_TAG_PATH_STR		"path"
+#define OPTION_TAG_PATH_LEN		(sizeof(OPTION_TAG_PATH_STR)-1)
+
+/* RFC 3262 (PRACK) */
+#define OPTION_TAG_100REL_STR		"100rel"
+#define OPTION_TAG_100REL_LEN		(sizeof(OPTION_TAG_100REL_STR)-1)
+
+/* RFC 4028 */
+#define OPTION_TAG_TIMER_STR		"timer"
+#define OPTION_TAG_TIMER_LEN		(sizeof(OPTION_TAG_TIMER_STR)-1)
+
+/* RFC 4662 (RLS) */
+#define OPTION_TAG_EVENTLIST_STR	"eventlist"
+#define OPTION_TAG_EVENTLIST_LEN	(sizeof(OPTION_TAG_EVENTLIST_STR)-1)
+
+/* RFC 5627 */
+#define OPTION_TAG_GRUU_STR		"gruu"
+#define OPTION_TAG_GRUU_LEN		(sizeof(OPTION_TAG_GRUU_STR)-1)
+
+/* RFC 5626 */
+#define OPTION_TAG_OUTBOUND_STR		"outbound"
+#define OPTION_TAG_OUTBOUND_LEN		(sizeof(OPTION_TAG_OUTBOUND_STR)-1)
+
+
+struct option_tag_body {
+	hf_parsed_free_f hfree;        /* function to free the content */
+	unsigned int option_tags;      /* option-tag mask for the current hdr */
+	unsigned int option_tags_all;  /* option-tag mask for the all hdr
+	                                *  - it's set only for the first hdr in 
+	                                *  sibling list*/
+};
+
+
+#define IS_DELIM(c) (*(c) == ' ' || *(c) == '\t' || *(c) == '\r' || *(c) == '\n' || *(c) == ',')
+
+/* from parser/parse_hname2.c: */
+#define LOWER_BYTE(b) ((b) | 0x20)
+#define LOWER_DWORD(d) ((d) | 0x20202020)
+#define READ(val) \
+	(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
+
+/*!
+ * Parse HF body containing option-tags.
+ */
+static inline int parse_option_tag_body(str *body, unsigned int *tags)
+{
+	register char* p;
+	register unsigned int val;
+	int len, pos = 0;
+
+	*tags = 0;
+
+	p = body->s;
+	len = body->len;
+
+	while (pos < len) {
+		/* skip spaces and commas */
+		for (; pos < len && IS_DELIM(p); ++pos, ++p);
+
+		val = LOWER_DWORD(READ(p));
+		switch (val) {
+
+			/* "path" */
+			case _path_:
+				if(pos + 4 <= len && IS_DELIM(p+4)) {
+					*tags |= F_OPTION_TAG_PATH;
+					pos += 5; p += 5;
+				}
+				break;
+
+			/* "100rel" */
+			case _100r_:
+				if ( pos+6 <= len
+					 && LOWER_BYTE(*(p+4))=='e' && LOWER_BYTE(*(p+5))=='l'
+					 && IS_DELIM(p+6)) {
+					*tags |= F_OPTION_TAG_100REL;
+					pos += OPTION_TAG_100REL_LEN + 1;
+					p   += OPTION_TAG_100REL_LEN + 1;
+				}
+				break;
+
+			/* "timer" */
+			case _time_:
+				if ( pos+5 <= len && LOWER_BYTE(*(p+4))=='r'
+					 && IS_DELIM(p+5) ) {
+					*tags |= F_OPTION_TAG_TIMER;
+					pos += OPTION_TAG_TIMER_LEN + 1;
+					p   += OPTION_TAG_TIMER_LEN + 1;
+				}
+				break;
+
+			/* extra require or unknown */
+			default:
+				if(pos+OPTION_TAG_EVENTLIST_LEN<=len
+						&& strncasecmp(p, OPTION_TAG_EVENTLIST_STR,
+							OPTION_TAG_EVENTLIST_LEN)==0
+						&& IS_DELIM(p+OPTION_TAG_EVENTLIST_LEN) ) {
+					*tags |= F_OPTION_TAG_EVENTLIST;
+					pos += OPTION_TAG_EVENTLIST_LEN + 1;
+					p   += OPTION_TAG_EVENTLIST_LEN + 1;
+				} else if(pos+OPTION_TAG_GRUU_LEN<=len
+						&& strncasecmp(p, OPTION_TAG_GRUU_STR,
+							OPTION_TAG_GRUU_LEN)==0
+						&& IS_DELIM(p+OPTION_TAG_GRUU_LEN)) {
+					*tags |= F_OPTION_TAG_GRUU;
+					pos += OPTION_TAG_GRUU_LEN + 1;
+					p   += OPTION_TAG_GRUU_LEN + 1;
+				} else if(pos+OPTION_TAG_OUTBOUND_LEN<=len
+						&& strncasecmp(p, OPTION_TAG_OUTBOUND_STR,
+							OPTION_TAG_OUTBOUND_LEN)==0
+						&& IS_DELIM(p+OPTION_TAG_OUTBOUND_LEN)) {
+					*tags |= F_OPTION_TAG_OUTBOUND;
+					pos += OPTION_TAG_OUTBOUND_LEN + 1;
+					p   += OPTION_TAG_OUTBOUND_LEN + 1;
+				} else {
+					/* skip element */
+					for (; pos < len && !IS_DELIM(p); ++pos, ++p);
+				}
+				break;
+		}
+	}
+	
+	return 0;
+}
+
+
+void hf_free_option_tag(void *parsed);
+
+#endif /* OPTION_TAGS_H */

+ 73 - 0
parser/parse_require.c

@@ -0,0 +1,73 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ * 
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*!
+ * \file
+ * \brief Require parser
+ * \ingroup parser
+ */
+
+#include "../mem/mem.h"
+#include "parse_require.h"
+
+/*!
+ * Parse all Require headers
+ */
+int parse_require( struct sip_msg *msg)
+{
+	unsigned int require;
+	struct hdr_field  *hdr;
+	struct option_tag_body *rb;
+
+	/* maybe the header is already parsed! */
+	if (msg->require && msg->require->parsed)
+		return 0;
+
+	/* parse to the end in order to get all SUPPORTED headers */
+	if (parse_headers(msg,HDR_EOH_F,0)==-1 || !msg->require)
+		return -1;
+
+	/* bad luck! :-( - we have to parse them */
+	require = 0;
+	for( hdr=msg->require ; hdr ; hdr=next_sibling_hdr(hdr)) {
+		if (hdr->parsed) {
+			require |= ((struct option_tag_body*)hdr->parsed)->option_tags;
+			continue;
+		}
+
+		rb = (struct option_tag_body*)pkg_malloc(sizeof(struct option_tag_body));
+		if (rb == 0) {
+			LM_ERR("out of pkg_memory\n");
+			return -1;
+		}
+
+		parse_option_tag_body(&(hdr->body), &(rb->option_tags));
+		rb->hfree = hf_free_option_tag;
+		rb->option_tags_all = 0;
+		hdr->parsed = (void*)rb;
+		require |= rb->option_tags;
+	}
+
+	((struct option_tag_body*)msg->require->parsed)->option_tags_all = 
+		require;
+	return 0;
+}

+ 50 - 0
parser/parse_require.h

@@ -0,0 +1,50 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*!
+ * \file
+ * \brief Require parser
+ * \ingroup parser
+ */
+
+#ifndef PARSE_REQUIRE_H
+#define PARSE_REQUIRE_H
+
+#include "msg_parser.h"
+#include "hf.h"
+#include "../mem/mem.h"
+#include "parse_option_tags.h"
+
+#define get_require(p_msg) \
+	((p_msg)->require ? ((struct option_tag_body*)(p_msg)->require->parsed)->option_tags_all : 0)
+
+
+/*!
+ * Parse all Require headers.
+ */
+int parse_require( struct sip_msg *msg);
+
+
+void free_require(struct option_tag_body **rb);
+
+#endif /* PARSE_REQUIRE_H */

+ 73 - 0
parser/parse_supported.c

@@ -0,0 +1,73 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*!
+ * \file
+ * \brief Supported parser
+ * \ingroup parser
+ */
+
+#include "../mem/mem.h"
+#include "parse_supported.h"
+
+/*!
+ * Parse all Supported headers
+ */
+int parse_supported( struct sip_msg *msg)
+{
+	unsigned int supported;
+	struct hdr_field  *hdr;
+	struct option_tag_body *sb;
+
+	/* maybe the header is already parsed! */
+	if (msg->supported && msg->supported->parsed)
+		return 0;
+
+	/* parse to the end in order to get all SUPPORTED headers */
+	if (parse_headers(msg,HDR_EOH_F,0)==-1 || !msg->supported)
+		return -1;
+
+	/* bad luck! :-( - we have to parse them */
+	supported = 0;
+	for( hdr=msg->supported ; hdr ; hdr=next_sibling_hdr(hdr)) {
+		if (hdr->parsed) {
+			supported |= ((struct option_tag_body*)hdr->parsed)->option_tags;
+			continue;
+		}
+
+		sb = (struct option_tag_body*)pkg_malloc(sizeof(struct option_tag_body));
+		if (sb == 0) {
+			LM_ERR("out of pkg_memory\n");
+			return -1;
+		}
+
+		parse_option_tag_body(&(hdr->body), &(sb->option_tags));
+		sb->hfree = hf_free_option_tag;
+		sb->option_tags_all = 0;
+		hdr->parsed = (void*)sb;
+		supported |= sb->option_tags;
+	}
+
+	((struct option_tag_body*)msg->supported->parsed)->option_tags_all = 
+		supported;
+	return 0;
+}

+ 54 - 0
parser/parse_supported.h

@@ -0,0 +1,54 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2006 Andreas Granig <[email protected]>
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * History:
+ * -------
+ * 2006-03-02  parse_supported() parses and cumulates all SUPPORTED 
+ *             headers (bogdan)
+ */
+
+/*!
+ * \file
+ * \brief Supported parser
+ * \ingroup parser
+ */
+
+#ifndef PARSE_SUPPORTED_H
+#define PARSE_SUPPORTED_H
+
+#include "msg_parser.h"
+#include "../mem/mem.h"
+#include "parse_option_tags.h"
+
+#define get_supported(p_msg) \
+	((p_msg)->supported ? ((struct option_tag_body*)(p_msg)->supported->parsed)->option_tags_all : 0)
+
+
+/*!
+ * Parse all Supported headers.
+ */
+int parse_supported( struct sip_msg *msg);
+
+
+void free_supported(struct option_tag_body **sb);
+
+#endif /* PARSE_SUPPORTED_H */

+ 3 - 3
select_core.c

@@ -1621,7 +1621,7 @@ int select_branch_uri(str* res, select_t* s, struct sip_msg* msg) {
 		char *c;
 		init_branch_iterator();
 		len = 0;
-		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0))) {
+		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0))) {
 
 			if (s->params[SEL_POS].v.i & SEL_BRANCH_DST_URI) {
 				l = dst_uri.len;
@@ -1645,7 +1645,7 @@ int select_branch_uri(str* res, select_t* s, struct sip_msg* msg) {
 		init_branch_iterator();
 		res->len = 0;
 		n = 0;
-		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0))) {
+		while ((c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0))) {
 			if (s->params[SEL_POS].v.i & SEL_BRANCH_DST_URI) {
 				l = dst_uri.len;
 				c = dst_uri.s;
@@ -1687,7 +1687,7 @@ int select_branch_uri(str* res, select_t* s, struct sip_msg* msg) {
 		if (n < 0 || n >= nr_branches) 
 			return -1;
 		init_branch_iterator();
-		for (; (c = next_branch(&l, &q, &dst_uri, 0, 0, 0)) && n; n--);
+		for (; (c = next_branch(&l, &q, &dst_uri, 0, 0, 0, 0)) && n; n--);
 		if (!c) return 1;
 		
 		if (s->params[SEL_POS].v.i & SEL_BRANCH_DST_URI) {