Index: modules/lcr/lcr_mod.c
===================================================================
RCS file: /cvsroot/ser/sip_router/modules/lcr/lcr_mod.c,v
retrieving revision 1.12
diff -u -r1.12 lcr_mod.c
--- modules/lcr/lcr_mod.c	23 Jul 2005 16:59:45 -0000	1.12
+++ modules/lcr/lcr_mod.c	17 Aug 2005 22:06:40 -0000
@@ -31,6 +31,10 @@
  *  2005-02-25: Added support for int AVP names, combined addr and port
  *              AVPs (jh)
  *  2005-07-23: Added support for gw URI scheme and transport (jh)
+ *  2005-03-08: Added gateway capabilities 
+ *              (Andreas Granig <agranig@linguin.org>)
+ *  2005-07-21: Fix AVP list walking in capabilities code
+ *              (Phil D'Amore <ptdamore@yahoo.com>)
  */
 
 #include <stdio.h>
@@ -65,6 +69,8 @@
  */
 #define GW_TABLE_VERSION 2
 #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)
@@ -108,8 +114,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_SIZE 16
 
 /* Default avp names */
 #define DEF_GW_URI_AVP "1400"
@@ -153,6 +175,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_uri_avp       = {DEF_GW_URI_AVP,	sizeof(DEF_GW_URI_AVP) - 1};
 str contact_avp      = {DEF_CONTACT_AVP, sizeof(DEF_CONTACT_AVP) - 1};
 str inv_timer_avp    = {DEF_FR_INV_TIMER_AVP, sizeof(DEF_FR_INV_TIMER_AVP)
@@ -187,6 +214,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 to_gw(struct sip_msg* _m, char* _s1, char* _s2);
@@ -199,6 +227,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},
 	{"to_gw",         to_gw,         0, 0, REQUEST_ROUTE | FAILURE_ROUTE},
@@ -224,6 +253,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_uri_avp",               STR_PARAM, &gw_uri_avp.s },
 	{"contact_avp",              STR_PARAM, &contact_avp.s  },
         {"fr_inv_timer_avp",         STR_PARAM, &inv_timer_avp.s  },
@@ -373,6 +407,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_uri_avp.len = strlen(gw_uri_avp.s);
 	contact_avp.len = strlen(contact_avp.s);
 	inv_timer_avp.len = strlen(inv_timer_avp.s);
@@ -401,7 +440,31 @@
 				" lcr table (use ser_mysql.sh reinstall)\n");
 		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();
 
@@ -637,6 +700,15 @@
  */
 int load_gws(struct sip_msg* _m, char* _s1, char* _s2)
 {
+    return load_cap_gws(_m, NULL, NULL);
+}
+
+/*
+ * Load GW info from database to lcr_gw_addr_port AVPs
+ * using given capability if _s1 != NULL
+ */
+int load_cap_gws(struct sip_msg* _m, char* _s1, char* _s2)
+{
     db_res_t* res;
     db_row_t *row, *r;
     str ruri_user, from_uri, value;
@@ -650,6 +722,22 @@
     str addr_str, port_str;
     char *at;
     int_str val;
+    unsigned char cap_enabled;
+    int pre_len;
+    char *prefix = NULL;
+    int cap_value;
+
+    if(_s1 == NULL) {
+	    cap_enabled = 0;
+    }
+    else {
+	    str s = {_s1, strlen(_s1)};
+	    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) {
@@ -680,8 +768,60 @@
 	}
 	from_uri = get_from(_m)->uri;
     }
-    
-    q_len = snprintf(query, MAX_QUERY_SIZE, "SELECT %.*s.%.*s, %.*s.%.*s, %.*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()",
+   
+    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, %.*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,
+			    gw_table.len, gw_table.s, uri_scheme_col.len, uri_scheme_col.s,
+			    gw_table.len, gw_table.s, transport_col.len, transport_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, %.*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, uri_scheme_col.len, uri_scheme_col.s,
@@ -695,6 +835,7 @@
 		     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;
@@ -730,7 +871,18 @@
 	} else {
 	    transport = (uri_transport)VAL_INT(ROW_VALUES(row) + 3);
 	}
-	if (5 + ruri_user.len + 1 + 15 + 1 + 5 + 1 + 14 > MAX_URI_SIZE) {
+	if (!cap_enabled || VAL_NULL(ROW_VALUES(row) + 4) == 1) {
+		pre_len = 0;
+		prefix = NULL;
+	} else {
+		prefix = (char*)VAL_STRING(ROW_VALUES(row) + 4);
+		pre_len = strlen(prefix);
+		if(pre_len > MAX_PREFIX_SIZE) {
+			LOG(L_ERR, "load_gws(): Too long prefix '%s', max. %d chars allowed\n", prefix, MAX_PREFIX_SIZE);
+			goto skip;
+		}
+	}
+	if (5 + pre_len + ruri_user.len + 1 + 15 + 1 + 5 + 1 + 14 > MAX_URI_SIZE) {
 	    LOG(L_ERR, "load_gws(): Request URI would be too long\n");
 	    goto skip;
 	}
@@ -743,6 +895,9 @@
 	    LOG(L_ERR, "load_gws(): Unknown or unsupported URI scheme: %u\n", (unsigned int)scheme);
 	    goto skip;
 	}
+	if(cap_enabled && pre_len > 0) {
+		memcpy(at, prefix, pre_len); at = at + pre_len;
+	}
 	memcpy(at, ruri_user.s, ruri_user.len); at = at + ruri_user.len;
 	*at = '@'; at = at + 1;
 	address.af = AF_INET;
@@ -773,12 +928,14 @@
 		goto skip;
 	    }
 	}
+
 	value.s = (char *)&(ruri[0]);
 	value.len = at - value.s;
 	val.s = &value;
 	add_avp(gw_uri_avp_name_str|AVP_VAL_STR, gw_uri_name, val);
+	
 	DBG("load_gws(): DEBUG: Added gw_uri_avp <%.*s>\n",
-	    value.len, value.s);
+           value.len, value.s);
     skip:
 	continue;
     }
@@ -808,7 +965,6 @@
     if (!avp) return -1;
 
     if (*(tmb.route_mode) == MODE_REQUEST) {
-	
 	act.type = SET_URI_T;
 	act.p1_type = STRING_ST;
 	act.p1.string = val.s->s;
