--- modules/lcr/lcr_mod.c	2005-03-08 12:06:33.000000000 +0100
+++ modules/lcr/lcr_mod.c_capabilities	2005-03-08 12:07:22.000000000 +0100
@@ -30,6 +30,7 @@
  *  2005-02-20: Added sequential forking functions (jh)
  *  2005-02-25: Added support for int AVP names, combined addr and port
  *              AVPs (jh)
+ *  2005-03-08: Added gateway capabilities (agranig)
  */
 
 #include <stdio.h>
@@ -61,6 +62,8 @@
  */
 #define GW_TABLE_VERSION 1
 #define LCR_TABLE_VERSION 1
+#define CAP_TABLE_VERSION 1
+#define GRP_TABLE_VERSION 2
 
 /* usr_avp flag for sequential forking */
 #define Q_FLAG      (1<<4)
@@ -98,8 +101,24 @@
 #define PRIORITY_COL "priority"
 #define PRIORITY_COL_LEN (sizeof(PRIORITY_COL) - 1)
 
+#define CAP_TABLE "gw_cap"
+#define CAP_TABLE_LEN (sizeof(CAP_TABLE) - 1)
+
+#define CAP_ID_COL "cap_id"
+#define CAP_ID_COL_LEN (sizeof(CAP_PREFIX_COL) - 1)
+
+#define CAP_PREFIX_COL "cap_prefix"
+#define CAP_PREFIX_COL_LEN (sizeof(CAP_PREFIX_COL) - 1)
+
+#define CAP_VALUE_COL "cap_value"
+#define CAP_VALUE_COL_LEN (sizeof(CAP_VALUE_COL) - 1)
+
+#define GRP_TABLE "gw_grp"
+#define GRP_TABLE_LEN (sizeof(GRP_TABLE) - 1)
+
 #define MAX_QUERY_SIZE 512
 #define MAX_NO_OF_GWS 32
+#define MAX_PREFIX_LEN 15
 
 /* Default avp names */
 #define DEF_GW_ADDR_PORT_AVP "1400"
@@ -135,6 +154,11 @@
 str prefix_col       = {PREFIX_COL, PREFIX_COL_LEN};
 str from_uri_col     = {FROM_URI_COL, FROM_URI_COL_LEN};
 str priority_col     = {PRIORITY_COL, PRIORITY_COL_LEN};
+str cap_table        = {CAP_TABLE, CAP_TABLE_LEN};
+str cap_id_col       = {CAP_ID_COL, CAP_ID_COL_LEN};
+str cap_prefix_col   = {CAP_PREFIX_COL, CAP_PREFIX_COL_LEN};
+str cap_value_col    = {CAP_VALUE_COL, CAP_VALUE_COL_LEN};
+str grp_table        = {GRP_TABLE, GRP_TABLE_LEN};
 str gw_addr_port_avp = {DEF_GW_ADDR_PORT_AVP,
 			sizeof(DEF_GW_ADDR_PORT_AVP) - 1};
 str contact_avp      = {DEF_CONTACT_AVP, sizeof(DEF_CONTACT_AVP) - 1};
@@ -154,10 +178,11 @@
     struct contact *next;
 };
 
-union addr_port {
-    unsigned char ap[6];
+struct addr_port {
     unsigned int addr;
-    unsigned short port[3];
+    unsigned short port;
+    unsigned short strip;
+    char prefix[MAX_PREFIX_LEN + 1];
 };
 
 int_str addr_port_name, contact_name, inv_timer_name;
@@ -174,6 +199,7 @@
  * Module functions that are defined later
  */
 int load_gws(struct sip_msg* _m, char* _s1, char* _s2);
+int load_cap_gws(struct sip_msg* _m, char* _s1, char* _s2);
 int next_gw(struct sip_msg* _m, char* _s1, char* _s2);
 int from_gw(struct sip_msg* _m, char* _s1, char* _s2);
 int load_contacts (struct sip_msg*, char*, char*);
@@ -185,6 +211,7 @@
  */
 static cmd_export_t cmds[] = {
 	{"load_gws",      load_gws,      0, 0, REQUEST_ROUTE},
+	{"load_gws",      load_cap_gws,  1, 0, REQUEST_ROUTE},
 	{"next_gw",       next_gw,       0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
 	{"from_gw",       from_gw,       0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
 	{"load_contacts", load_contacts, 0, 0, REQUEST_ROUTE},
@@ -207,6 +234,11 @@
 	{"prefix_column",            STR_PARAM, &prefix_col.s   },
 	{"from_uri_column",          STR_PARAM, &from_uri_col.s },
 	{"priority_column",          STR_PARAM, &priority_col.s },
+	{"cap_table",                STR_PARAM, &cap_table.s    },
+	{"cap_id_col",               STR_PARAM, &cap_id_col.s   },
+	{"cap_value_col",            STR_PARAM, &cap_value_col.s  },
+	{"cap_prefix_col",           STR_PARAM, &cap_prefix_col.s },
+	{"grp_table",                STR_PARAM, &grp_table.s    },
 	{"gw_addr_port_avp",         STR_PARAM, &gw_addr_port_avp.s },
 	{"contact_avp",              STR_PARAM, &contact_avp.s  },
         {"fr_inv_timer_avp",         STR_PARAM, &inv_timer_avp.s  },
@@ -347,6 +379,11 @@
 	prefix_col.len = strlen(prefix_col.s);
 	from_uri_col.len = strlen(from_uri_col.s);
         priority_col.len = strlen(priority_col.s);
+	cap_table.len = strlen(cap_table.s);
+	cap_prefix_col.len = strlen(cap_prefix_col.s);
+	cap_id_col.len = strlen(cap_id_col.s);
+	cap_value_col.len = strlen(cap_value_col.s);
+	grp_table.len = strlen(grp_table.s);
 	gw_addr_port_avp.len = strlen(gw_addr_port_avp.s);
 	contact_avp.len = strlen(contact_avp.s);
 	inv_timer_avp.len = strlen(inv_timer_avp.s);
@@ -375,6 +412,30 @@
 		goto err;
 	}		
 	
+	/* Check table version */
+	ver = lcr_db_ver(db_url.s, &grp_table);
+	if (ver < 0) {
+		LOG(L_ERR, "ERROR: lcr:mod_init():"
+				" Error while querying table version\n");
+		goto err;
+	} else if (ver < GRP_TABLE_VERSION) {
+		LOG(L_ERR, "ERROR: lcr:mod_init(): Invalid table version of"
+				" gw_grp table (use ser_mysql.sh reinstall)\n");
+		goto err;
+	}		
+	
+	/* Check table version */
+	ver = lcr_db_ver(db_url.s, &cap_table);
+	if (ver < 0) {
+		LOG(L_ERR, "ERROR: lcr:mod_init():"
+				" Error while querying table version\n");
+		goto err;
+	} else if (ver < CAP_TABLE_VERSION) {
+		LOG(L_ERR, "ERROR: lcr:mod_init(): Invalid table version of"
+				" gw_cap table (use ser_mysql.sh reinstall)\n");
+		goto err;
+	}		
+	
 	/* Initialize fifo interface */
 	(void)init_lcr_fifo();
 
@@ -548,16 +609,40 @@
 /*
  * Load GW info from database to lcr_gw_addr_port AVPs
  */
-int load_gws(struct sip_msg* _m, char* _s1, char* _s2)
+int load_gws(struct sip_msg* _m, char* cap, char* _s2)
+{
+	return load_cap_gws(_m, NULL, NULL);
+}
+
+
+/*
+ * Load GW info from database to lcr_gw_addr_port AVPs
+ * taking care of gw capabilities
+ */
+int load_cap_gws(struct sip_msg* _m, char* cap, char* _s2)
 {
     db_res_t* res;
     db_row_t *row, *r;
     int_str val;	    
     str ruri_user, from_uri, value;
     char query[MAX_QUERY_SIZE];
-    int q_len, i, j;
-    union addr_port ap;
+    int q_len, i = 0, j = 0;
+    struct addr_port ap;
     unsigned int addr;
+    unsigned int cap_value = 0;
+    unsigned char cap_enabled;
+
+    if(cap == NULL) {
+	    cap_enabled = 0;
+    }
+    else {
+	    str s = {cap, strlen(cap)};
+	    if(str2int(&s, &cap_value) == -1) {
+	    	LOG(L_ERR, "load_gws(): Error converting capability value to int\n");
+		return -1;
+	    }
+	    cap_enabled = 1;
+    }
 
     /* Find Request-URI user */
     if (parse_sip_msg_uri(_m) < 0) {
@@ -580,19 +665,73 @@
 	    return -1;
     }
     from_uri = get_from(_m)->uri;
-    
-    q_len = snprintf(query, MAX_QUERY_SIZE, "SELECT %.*s.%.*s, %.*s.%.*s FROM %.*s, %.*s WHERE '%.*s' LIKE %.*s.%.*s AND '%.*s' LIKE CONCAT(%.*s.%.*s, '%%') AND %.*s.%.*s = %.*s.%.*s ORDER BY CHAR_LENGTH(%.*s.%.*s), %.*s.%.*s DESC, RAND()",
-		     gw_table.len, gw_table.s, ip_addr_col.len, ip_addr_col.s,
-		     gw_table.len, gw_table.s, port_col.len, port_col.s,
-		     gw_table.len, gw_table.s, lcr_table.len, lcr_table.s,
-		     from_uri.len, from_uri.s,
-		     lcr_table.len, lcr_table.s, from_uri_col.len, from_uri_col.s,
-		     ruri_user.len, ruri_user.s,
-		     lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s,
-		     lcr_table.len, lcr_table.s, grp_id_col.len,  grp_id_col.s,
-		     gw_table.len, gw_table.s, grp_id_col.len, grp_id_col.s,
-		     lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s,
-		     lcr_table.len, lcr_table.s, priority_col.len, priority_col.s);
+   
+    if(cap_enabled) {
+	    q_len = snprintf(query, MAX_QUERY_SIZE, 
+
+			    /* 1: SELECT gw.ip_addr, gw.port, gw_cap.prefix */ 
+			    "SELECT %.*s.%.*s, %.*s.%.*s, %.*s.%.*s " \
+			    /* 2: FROM gw, lcr, gw_cap, gw_grp */
+			    "FROM %.*s, %.*s, %.*s, %.*s " \
+			    /* 3: WHERE gw_cap.cap_value & cap_value = cap_value */
+			    "WHERE %.*s.%.*s & %d = %d "\
+			    /* 4: AND 'from_uri' LIKE lcr.from_uri */ 
+			    "AND '%.*s' LIKE %.*s.%.*s " \
+			    /* 5: AND 'ruri_user' LIKE CONCAT(lcr.prefix, '%') */
+			    "AND '%.*s' LIKE CONCAT(%.*s.%.*s, '%%') " \
+			    /* 6: AND lcr.grp_id = gw.grp_id */
+			    "AND %.*s.%.*s = %.*s.%.*s " \
+			    /* 7: AND gw_cap.cap_id = gw_grp.cap_id */
+			    "AND %.*s.%.*s = %.*s.%.*s " \
+			    /* 8: AND lcr.grp_id = gw_grp.grp_id */
+			    "AND  %.*s.%.*s = %.*s.%.*s " \
+			    /* 9: ORDER BY CHAR_LENGTH(lcr.prefix), (RAND()*10000)%((RAND()*10000)%(lcr.priority)), RAND() */
+			    "ORDER BY CHAR_LENGTH(%.*s.%.*s), (RAND()*10000)%%((RAND()*10000)%%(%.*s.%.*s)) DESC, RAND()",
+
+			     /* 1st line */
+			     gw_table.len, gw_table.s, ip_addr_col.len, ip_addr_col.s,
+			     gw_table.len, gw_table.s, port_col.len, port_col.s,
+			     cap_table.len, cap_table.s, cap_prefix_col.len, cap_prefix_col.s,
+			     /* 2nd line */
+			     gw_table.len, gw_table.s, lcr_table.len, lcr_table.s, cap_table.len, cap_table.s,
+			     grp_table.len, grp_table.s, 
+			     /* 3rd line */
+			     cap_table.len, cap_table.s, cap_value_col.len, cap_value_col.s, cap_value, cap_value,
+			     /* 4th line */
+			     from_uri.len, from_uri.s,
+			     lcr_table.len, lcr_table.s, from_uri_col.len, from_uri_col.s,
+			     /* 5th line */
+			     ruri_user.len, ruri_user.s,
+			     lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s,
+			     /* 6th line */
+			     lcr_table.len, lcr_table.s, grp_id_col.len,  grp_id_col.s,
+			     gw_table.len, gw_table.s, grp_id_col.len, grp_id_col.s,
+			     /* 7th line */
+			     cap_table.len, cap_table.s, cap_id_col.len, cap_id_col.s,
+			     grp_table.len, grp_table.s, cap_id_col.len, cap_id_col.s,
+			     /* 8th line */
+			     lcr_table.len, lcr_table.s, grp_id_col.len,  grp_id_col.s,
+			     grp_table.len, grp_table.s, grp_id_col.len,  grp_id_col.s,
+			     /* 9th line */
+			     lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s,
+			     lcr_table.len, lcr_table.s, priority_col.len, priority_col.s);
+    }
+    else {
+	    q_len = snprintf(query, MAX_QUERY_SIZE, "SELECT %.*s.%.*s, %.*s.%.*s FROM %.*s, %.*s WHERE '%.*s' LIKE %.*s.%.*s AND '%.*s' LIKE CONCAT(%.*s.%.*s, '%%') AND %.*s.%.*s = %.*s.%.*s ORDER BY CHAR_LENGTH(%.*s.%.*s), %.*s.%.*s DESC, RAND()",
+			    gw_table.len, gw_table.s, ip_addr_col.len, ip_addr_col.s,
+		            gw_table.len, gw_table.s, port_col.len, port_col.s,
+		            gw_table.len, gw_table.s, lcr_table.len, lcr_table.s,
+		            from_uri.len, from_uri.s,
+		            lcr_table.len, lcr_table.s, from_uri_col.len, from_uri_col.s,
+		            ruri_user.len, ruri_user.s,
+		            lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s,
+		            lcr_table.len, lcr_table.s, grp_id_col.len,  grp_id_col.s,
+		            gw_table.len, gw_table.s, grp_id_col.len, grp_id_col.s,
+		            lcr_table.len, lcr_table.s, prefix_col.len, prefix_col.s,
+		            lcr_table.len, lcr_table.s, priority_col.len, priority_col.s);
+    }
+
+		     
     if (q_len >= MAX_QUERY_SIZE) {
 	LOG(L_ERR, "load_gws(): Too long database query\n");
 	return -1;
@@ -615,17 +754,38 @@
 	}
 	ap.addr = addr;
 	if (VAL_NULL(ROW_VALUES(row) + 1) == 1) {
-		ap.port[2] = 0;
+		ap.port = 0;
 	} else {
-		ap.port[2] = (int)VAL_INT(ROW_VALUES(row) + 1);
+		ap.port = (int)VAL_INT(ROW_VALUES(row) + 1);
+	}
+	memset(ap.prefix, 0, sizeof(ap.prefix));
+	if (VAL_NULL(ROW_VALUES(row) + 2) != 1) {
+		char *pre = (char*)VAL_STRING(ROW_VALUES(row) + 2);
+		int pre_len = strlen(pre);
+		if(pre_len > MAX_PREFIX_LEN) {
+			LOG(L_ERR, "load_gws(): Too long prefix '%s', max. %d chars allowed\n", pre, MAX_PREFIX_LEN);
+			goto skip;
+		}
+		if(pre_len) {
+			memcpy(ap.prefix, pre, pre_len);
+		}
 	}
-	value.s = &(ap.ap[0]);
-	value.len = 6;
+	ap.strip = 0;
+	
+	value.s = (char*)&ap;
+	value.len = sizeof(struct addr_port);
 	val.s = &value;
 	add_avp(gw_ap_avp_name_str|AVP_VAL_STR, addr_port_name, val);
-	DBG("load_gws(): DEBUG: Added gw_addr_port_avp <%x, %d>\n",
-	    *((unsigned int *)(val.s->s)),
-	    *((unsigned short *)(val.s->s + 4)));
+	    
+	if(cap_enabled) {
+		DBG("load_gws(): DEBUG: Added capability-based gw_addr_port_avp <%x, %d, '%s'>\n",
+			    ap.addr, ap.port, ap.prefix);
+	} 
+	else {
+		DBG("load_gws(): DEBUG: Added gw_addr_port_avp <%x, %d>\n",
+			    ap.addr, ap.port);
+	}
+
     skip:
 	continue;
     }
@@ -650,28 +810,47 @@
     struct action act;
     int rval, port_len;
     struct ip_addr addr;
-    struct usr_avp *avp;
+    struct usr_avp *avp, *avp_next;
     str uri, uri_user, addr_str;
     char *at, *port_string;
-    union addr_port *ap;
+    struct addr_port *ap;
     unsigned int address, port;
-
+    char *prefix = NULL;
+    size_t prefix_len = 0;
+    unsigned short strip = 0;
+    
     avp = search_first_avp(gw_ap_avp_name_str, addr_port_name, &val);
     if (!avp) return -1;
+    avp_next = avp->next;
 
-    ap = (union addr_port *)(val.s->s);
+    ap = (struct addr_port *)(val.s->s);
     address = ap->addr;
-    port = (ap->port)[2];
-    destroy_avp(avp);
+    port = ap->port;
+    prefix = ap->prefix;
+    prefix_len = strlen(prefix);
+    strip = ap->strip;
 
+    destroy_avp(avp);
+    
     addr.af = AF_INET;
     addr.len = 4;
     addr.u.addr32[0] = address;
 
     if (*(tmb.route_mode) == MODE_REQUEST) {
 
+	/* if ruri-user is manipulated in MODE_REQUEST, it has to be stripped in subsequent calls */
+	int_str val_next;
+	struct addr_port *ap_next;
+	for(; avp_next; avp_next = avp_next->next) {
+		get_avp_val(avp_next, &val_next);
+		ap_next = (struct addr_port *)(val_next.s->s);
+		ap_next->strip = prefix_len;
+	}
+
 	act.p1.string = ip_addr2a(&addr);
 
+	DBG("next_gw(): DEBUG: route: next gw='%s', prefix='%s', strip previous=%d\n", act.p1.string, prefix, strip);
+
 	if (port == 0) {
 	    act.type = SET_HOSTPORT_T;
 	    act.p1_type = STRING_ST;
@@ -680,22 +859,51 @@
 	    act.type = SET_HOST_T;
 	    act.p1_type = STRING_ST;
 	    rval = do_action(&act, _m);
-	    if (rval != 1) return -1;
+
+	    if (rval != 1) {
+	    	LOG(L_ERR, "next_gw(): ERROR: do_action for SET_HOST_T failed with return value <%d>\n", rval);
+	    	return -1;
+	    }
+
 	    act.p1.string = int2str(port, &port_len);
 	    act.type = SET_PORT_T;
 	    act.p1_type = STRING_ST;
 	    rval = do_action(&act, _m);
 	}
 
+
 	if (rval != 1) {
-	    LOG(L_ERR, "next_gw(): ERROR: do_action failed with return value <%d>\n", rval);
+	    LOG(L_ERR, "next_gw(): ERROR: do_action for SET_PORT_T failed with return value <%d>\n", rval);
 	    return -1;
 	}
 
+	if(strip) {
+		act.type = STRIP_T;
+		act.p1.number = strip;
+		act.p1_type = NUMBER_ST;
+		rval = do_action(&act, _m);
+	    	if (rval != 1) {
+	    		LOG(L_ERR, "next_gw(): ERROR: do_action for STRIP_T failed with return value <%d>\n", rval);
+	    		return -1;
+	    	}
+	}
+	
+	if(prefix_len) {
+		act.type = PREFIX_T;
+		act.p1.string = prefix;
+		act.p1_type = STRING_ST;
+		rval = do_action(&act, _m);
+		if (rval != 1) {
+			LOG(L_ERR, "next_gw(): ERROR: do_action for PREFIX_T failed with return value <%d>\n", rval);
+			return -1;
+		}
+	}
+	
 	return 1;
 
     } else { /* MODE_ONFAILURE */
 
+
 	if (port != 0) {
 	    port_string = int2str(port, &port_len);
 	    port_len = port_len;
@@ -715,10 +923,11 @@
 	uri_user = _m->parsed_uri.user;
 
 	if (port != 0) {
-	    uri.len = 4 + uri_user.len + 1 + addr_str.len + 1 + port_len + 1;
+    		uri.len = 4 + prefix_len - strip + uri_user.len + 1 + addr_str.len + 1 + port_len + 1;
 	} else {
-	    uri.len = 4 + uri_user.len + 1 + addr_str.len + 1;
-	}	    
+    		uri.len = 4 + prefix_len - strip + uri_user.len + 1 + addr_str.len + 1;
+	}
+
 	if (uri.len > MAX_URI_SIZE) {
 	    LOG(L_ERR, "next_gw(): URI is too long\n");
 	    return -1;
@@ -728,12 +937,25 @@
 	    LOG(L_ERR, "next_gw(): No memory for new uri\n");
 	    return -1;
 	}
+
+	DBG("next_gw(): DEBUG: failure-route: next gw='%.*s', prefix='%s', strip previous=%d\n", 
+		addr_str.len, addr_str.s, prefix, strip);
 	
 	at = uri.s;
 	memcpy(at, "sip:", 4);
 	at = at + 4;
+	if(prefix_len > 0) {
+		memcpy(at, prefix, prefix_len);
+		at += prefix_len;
+	}
+
+	if(strip) {
+		uri_user.s += strip;
+		uri_user.len -= strip;
+	}
 	memcpy(at, uri_user.s, uri_user.len);
 	at = at + uri_user.len;
+
 	*at = '@';
 	at = at + 1;
 	memcpy(at, addr_str.s, addr_str.len);
