Jelajahi Sumber

Merged the extension of the dialog- and rr-modules from my carstenbock/ims branch into master

Carsten Bock 14 tahun lalu
induk
melakukan
60d9c5574f

+ 22 - 7
modules_k/dialog/dialog.c

@@ -4,6 +4,7 @@
  * dialog module - basic support for dialog tracking
  *
  * Copyright (C) 2006 Voice Sistem SRL
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -85,12 +86,12 @@ static char* rr_param = "did";
 static int dlg_flag = -1;
 static str timeout_spec = {NULL, 0};
 static int default_timeout = 60 * 60 * 12;  /* 12 hours */
+static int seq_match_mode = SEQ_MATCH_STRICT_ID;
 static char* profiles_wv_s = NULL;
 static char* profiles_nv_s = NULL;
 str dlg_extra_hdrs = {NULL,0};
 static int db_fetch_rows = 200;
 
-int seq_match_mode = SEQ_MATCH_STRICT_ID;
 str dlg_bridge_controller = {"sip:[email protected]", 27};
 
 str ruri_pvar_param = {"$ru", 3};
@@ -214,6 +215,13 @@ static param_export_t mod_params[]={
 	{ "from_sock_column",      STR_PARAM, &from_sock_column.s       },
 	{ "sflags_column",         STR_PARAM, &sflags_column.s          },
 	{ "toroute_name_column",   STR_PARAM, &toroute_name_column.s    },
+
+	{ "vars_table_name",       STR_PARAM, &dialog_vars_table_name   },
+	{ "vars_h_id_column",      STR_PARAM, &vars_h_id_column.s       },
+	{ "vars_h_entry_column",   STR_PARAM, &vars_h_entry_column.s    },
+	{ "vars_key_column",       STR_PARAM, &vars_key_column.s        },
+	{ "vars_value_column",     STR_PARAM, &vars_value_column.s      },
+
 	{ "db_update_period",      INT_PARAM, &db_update_period         },
 	{ "db_fetch_rows",         INT_PARAM, &db_fetch_rows            },
 	{ "profiles_with_value",   STR_PARAM, &profiles_wv_s            },
@@ -259,6 +267,8 @@ static pv_export_t mod_items[] = {
 		pv_set_dlg_ctx, pv_parse_dlg_ctx_name, 0, 0, 0 },
 	{ {"dlg",  sizeof("dlg")-1}, PVT_OTHER, pv_get_dlg,
 		0, pv_parse_dlg_name, 0, 0, 0 },
+	{ {"dlg_var", sizeof("dlg_var")-1}, PVT_OTHER, pv_get_dlg_variable,
+		pv_set_dlg_variable,    pv_parse_dialog_var_name, 0, 0, 0},
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
@@ -362,7 +372,7 @@ static int pv_get_dlg_count(struct sip_msg *msg, pv_param_t *param,
 	if(msg==NULL || res==NULL)
 		return -1;
 
-	n = active_dlgs ? (int)get_stat_val(active_dlgs) : 0;
+	n = active_dlgs ? get_stat_val(active_dlgs) : 0;
 	l = 0;
 	ch = int2str( n, &l);
 
@@ -430,6 +440,12 @@ static int mod_init(void)
 	toroute_name_column.len = strlen(toroute_name_column.s);
 	dialog_table_name.len = strlen(dialog_table_name.s);
 
+	dialog_vars_table_name.len = strlen(dialog_vars_table_name.s);
+	vars_h_id_column.len = strlen(vars_h_id_column.s);
+	vars_h_entry_column.len = strlen(vars_h_entry_column.s);
+	vars_key_column.len = strlen(vars_key_column.s);
+	vars_value_column.len = strlen(vars_value_column.s);
+
 	/* param checkings */
 	if (dlg_flag==-1) {
 		LM_ERR("no dlg flag set!!\n");
@@ -551,7 +567,7 @@ static int mod_init(void)
 
 	/* init handlers */
 	init_dlg_handlers( rr_param, dlg_flag,
-		timeout_spec.s?&timeout_avp:0, default_timeout);
+		timeout_spec.s?&timeout_avp:0, default_timeout, seq_match_mode);
 
 	/* init timer */
 	if (init_dlg_timer(dlg_ontimeout)!=0) {
@@ -612,10 +628,9 @@ static int child_init(int rank)
 		if_update_stat(dlg_enable_stats, early_dlgs, early_dlgs_cnt);
 	}
 
-	if ( ((dlg_db_mode==DB_MODE_REALTIME || dlg_db_mode==DB_MODE_DELAYED)
-				&& (rank>0 || rank==PROC_TIMER || rank==PROC_MAIN))
-			|| (dlg_db_mode==DB_MODE_SHUTDOWN && (rank==PROC_MAIN)) )
-	{
+	if ( ((dlg_db_mode==DB_MODE_REALTIME || dlg_db_mode==DB_MODE_DELAYED) &&
+	(rank>0 || rank==PROC_TIMER)) ||
+	(dlg_db_mode==DB_MODE_SHUTDOWN && (rank==PROC_MAIN)) ) {
 		if ( dlg_connect_db(&db_url) ) {
 			LM_ERR("failed to connect to database (rank=%d)\n",rank);
 			return -1;

+ 243 - 128
modules_k/dialog/dlg_db_handler.c

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2007 Voice System SRL
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -37,6 +38,7 @@
 #include "../../str.h"
 #include "../../socket_info.h"
 #include "dlg_hash.h"
+#include "dlg_var.h"
 #include "dlg_db_handler.h"
 
 
@@ -64,6 +66,12 @@ str req_uri_column			=	str_init(REQ_URI_COL);
 str dialog_table_name		=	str_init(DIALOG_TABLE_NAME);
 int dlg_db_mode				=	DB_MODE_NONE;
 
+str vars_h_id_column		=	str_init(VARS_HASH_ID_COL);
+str vars_h_entry_column		=	str_init(VARS_HASH_ENTRY_COL);
+str vars_key_column		=	str_init(VARS_KEY_COL);
+str vars_value_column		=	str_init(VARS_VALUE_COL);
+str dialog_vars_table_name	=	str_init(DIALOG_VARS_TABLE_NAME);
+
 static db1_con_t* dialog_db_handle    = 0; /* database connection handle */
 static db_func_t dialog_dbf;
 
@@ -109,15 +117,16 @@ extern int early_dlgs_cnt;
 
 
 static int load_dialog_info_from_db(int dlg_hash_size, int fetch_num_rows);
-
+static int load_dialog_vars_from_db(int fetch_num_rows);
 
 int dlg_connect_db(const str *db_url)
 {
-	dialog_db_handle = dialog_dbf.init(db_url);
-	if(dialog_db_handle == 0) {
-		LM_ERR("unable to connect to database\n");
+	if (dialog_db_handle) {
+		LM_CRIT("BUG - db connection found already open\n");
 		return -1;
 	}
+	if ((dialog_db_handle = dialog_dbf.init(db_url)) == 0)
+		return -1;
 	return 0;
 }
 
@@ -136,7 +145,12 @@ int init_dlg_db(const str *db_url, int dlg_hash_size , int db_update_period, int
 	}
 
 	if(db_check_table_version(&dialog_dbf, dialog_db_handle, &dialog_table_name, DLG_TABLE_VERSION) < 0) {
-		LM_ERR("error during table version check.\n");
+		LM_ERR("error during dialog-table version check.\n");
+		return -1;
+	}
+
+	if(db_check_table_version(&dialog_dbf, dialog_db_handle, &dialog_vars_table_name, DLG_VARS_TABLE_VERSION) < 0) {
+		LM_ERR("error during dialog-vars version check.\n");
 		return -1;
 	}
 
@@ -150,6 +164,10 @@ int init_dlg_db(const str *db_url, int dlg_hash_size , int db_update_period, int
 		LM_ERR("unable to load the dialog data\n");
 		return -1;
 	}
+	if( (load_dialog_vars_from_db(fetch_num_rows) ) !=0 ){
+		LM_ERR("unable to load the dialog data\n");
+		return -1;
+	}
 
 	dialog_dbf.close(dialog_db_handle);
 	dialog_db_handle = 0;
@@ -185,6 +203,21 @@ static int use_dialog_table(void)
 	return 0;
 }
 
+static int use_dialog_vars_table(void)
+{
+	if(!dialog_db_handle){
+		LM_ERR("invalid database handle\n");
+		return -1;
+	}
+
+	if (dialog_dbf.use_table(dialog_db_handle, &dialog_vars_table_name) < 0) {
+		LM_ERR("Error in use_table\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 
 
 static int select_entire_dialog_table(db1_res_t ** res, int fetch_num_rows)
@@ -414,11 +447,120 @@ error:
 
 
 
+static int select_entire_dialog_vars_table(db1_res_t ** res, int fetch_num_rows)
+{
+	db_key_t query_cols[DIALOG_VARS_TABLE_COL_NO] = {	&vars_h_entry_column,
+			&vars_h_id_column,	&vars_key_column,	&vars_value_column };
+
+	if(use_dialog_vars_table() != 0){
+		return -1;
+	}
+
+	/* select the whole tabel and all the columns */
+	if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH) && (fetch_num_rows > 0)) {
+		if(dialog_dbf.query(dialog_db_handle,0,0,0,query_cols, 0, 
+		DIALOG_VARS_TABLE_COL_NO, 0, 0) < 0) {
+			LM_ERR("Error while querying (fetch) database\n");
+			return -1;
+		}
+		if(dialog_dbf.fetch_result(dialog_db_handle, res, fetch_num_rows) < 0) {
+			LM_ERR("fetching rows failed\n");
+			return -1;
+		}
+	} else {
+		if(dialog_dbf.query(dialog_db_handle,0,0,0,query_cols, 0,
+		DIALOG_VARS_TABLE_COL_NO, 0, res) < 0) {
+			LM_ERR("Error while querying database\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int load_dialog_vars_from_db(int fetch_num_rows)
+{
+	db1_res_t * res;
+	db_val_t * values;
+	db_row_t * rows;
+	struct dlg_cell  * dlg; 
+	int i, nr_rows;
+
+	res = 0;
+	if((nr_rows = select_entire_dialog_vars_table(&res, fetch_num_rows)) < 0)
+		goto end;
+
+	nr_rows = RES_ROW_N(res);
+
+	LM_DBG("the database has information about %i dialog variables\n", nr_rows);
+
+	rows = RES_ROWS(res);
+
+	do {
+		/* for every row---dialog */
+		for(i=0; i<nr_rows; i++){
+
+			values = ROW_VALUES(rows + i);
+
+			if (VAL_NULL(values) || VAL_NULL(values+1)) {
+				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
+					vars_h_entry_column.len, vars_h_entry_column.s,
+					vars_h_id_column.len, vars_h_id_column.s);
+				continue;
+			}
+
+			if (VAL_NULL(values+2) || VAL_NULL(values+3)) {
+				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
+					vars_key_column.len, vars_key_column.s,
+					vars_value_column.len, vars_value_column.s);
+				continue;
+			}
+			if (VAL_INT(values) < d_table->size) {
+				dlg = (d_table->entries)[VAL_INT(values)].first;
+				while (dlg) {
+					if (dlg->h_id == VAL_INT(values+1)) {
+						set_dlg_variable_unsafe(dlg, &VAL_STR(values+2), &VAL_STR(values+3), 0);
+						continue;
+					}
+					dlg = dlg->next;
+					if (!dlg) {
+						LM_WARN("insonsistent data: the dialog h_entry/h_id does not exist!\n");
+					}
+				}
+			} else {
+				LM_WARN("insonsistent data: the h_entry in the DB does not exist!\n");
+			}
+		}
+
+		/* any more data to be fetched ?*/
+		if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH) && (fetch_num_rows > 0)) {
+			if(dialog_dbf.fetch_result(dialog_db_handle, &res, fetch_num_rows) < 0) {
+				LM_ERR("re-fetching rows failed\n");
+				goto error;
+			}
+			nr_rows = RES_ROW_N(res);
+			rows = RES_ROWS(res);
+		} else {
+			nr_rows = 0;
+		}
+
+	}while (nr_rows>0);
+
+end:
+	dialog_dbf.free_result(dialog_db_handle, res);
+	return 0;
+error:
+	dialog_dbf.free_result(dialog_db_handle, res);
+	return -1;
+
+}
+
 /*this is only called from destroy_dlg, where the cell's entry lock is acquired*/
 int remove_dialog_from_db(struct dlg_cell * cell)
 {
 	db_val_t values[2];
 	db_key_t match_keys[2] = { &h_entry_column, &h_id_column};
+	db_key_t vars_match_keys[2] = { &vars_h_entry_column, &vars_h_id_column};
 
 	/*if the dialog hasn 't been yet inserted in the database*/
 	LM_DBG("trying to remove dialog [%.*s], update_flag is %i\n",
@@ -441,17 +583,83 @@ int remove_dialog_from_db(struct dlg_cell * cell)
 		return -1;
 	}
 
+	if (use_dialog_vars_table()!=0)
+		return -1;
+
+	if(dialog_dbf.delete(dialog_db_handle, vars_match_keys, 0, values, 2) < 0) {
+		LM_ERR("failed to delete database information\n");
+		return -1;
+	}
+
 	LM_DBG("callid was %.*s\n", cell->callid.len, cell->callid.s );
 
 	return 0;
 }
 
 
+int update_dialog_vars_dbinfo(struct dlg_cell * cell, struct dlg_var * var)
+{
+	db_val_t values[DIALOG_VARS_TABLE_COL_NO];
 
-int update_dialog_dbinfo(struct dlg_cell * cell)
+	db_key_t insert_keys[DIALOG_VARS_TABLE_COL_NO] = { &vars_h_entry_column,
+			&vars_h_id_column,	&vars_key_column,	&vars_value_column };
+
+	if(use_dialog_vars_table()!=0)
+		return -1;
+
+	VAL_TYPE(values) = VAL_TYPE(values+1) = DB1_INT;
+	VAL_TYPE(values+2) = VAL_TYPE(values+3) = DB1_STR;
+	VAL_NULL(values) = VAL_NULL(values+1) = VAL_NULL(values+2) = VAL_NULL(values+3) = 0;
+	SET_STR_VALUE(values+2, var->key);
+
+	VAL_INT(values)			= cell->h_entry;
+	VAL_INT(values+1)		= cell->h_id;
+	
+	if((var->vflags & DLG_FLAG_DEL) != 0) {
+		/* delete the current variable */
+		db_key_t vars_match_keys[3] = { &vars_h_entry_column, &vars_h_id_column, &vars_key_column};
+
+		if (use_dialog_vars_table()!=0)
+			return -1;
+
+		if(dialog_dbf.delete(dialog_db_handle, vars_match_keys, 0, values, 3) < 0) {
+			LM_ERR("failed to delete database information\n");
+			return -1;
+		}
+	} else if((var->vflags & DLG_FLAG_NEW) != 0) {
+		/* save all the current dialogs information*/
+		SET_STR_VALUE(values+3, var->value);
+
+		if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, 
+								DIALOG_VARS_TABLE_COL_NO)) !=0){
+			LM_ERR("could not add another dialog-var to db\n");
+			goto error;
+		}
+		var->vflags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED);
+	} else if((var->vflags & DLG_FLAG_CHANGED) != 0) {
+		/* save only dialog's state and timeout */
+		SET_STR_VALUE(values+3, var->value);
+
+		if((dialog_dbf.update(dialog_db_handle, insert_keys, 0, 
+						values, (insert_keys+3), (values+3), 3, 1)) !=0){
+			LM_ERR("could not update database info\n");
+			goto error;
+		}
+		var->vflags &= ~DLG_FLAG_CHANGED;
+	} else {
+		return 0;
+	}
+	return 0;
+error:
+	return -1;
+}
+
+
+int update_dialog_dbinfo_unsafe(struct dlg_cell * cell)
 {
 	int i;
-	struct dlg_entry entry;
+	struct dlg_var *var;
+
 	db_val_t values[DIALOG_TABLE_COL_NO];
 
 	db_key_t insert_keys[DIALOG_TABLE_COL_NO] = { &h_entry_column,
@@ -463,6 +671,17 @@ int update_dialog_dbinfo(struct dlg_cell * cell)
 			&to_route_column,    &from_contact_column,&to_contact_column,
 			&sflags_column,      &toroute_name_column,     &req_uri_column };
 
+	if( (cell->dflags & DLG_FLAG_NEW) != 0 
+	|| (cell->dflags & DLG_FLAG_CHANGED_VARS) != 0) {
+		/* iterate the list */
+		for(var=cell->vars ; var ; var=var->next) {
+			if (update_dialog_vars_dbinfo(cell, var) != 0)
+				return -1;
+		}
+		/* Remove the flag */
+		cell->dflags &= ~DLG_FLAG_CHANGED_VARS;
+	}
+
 	if(use_dialog_table()!=0)
 		return -1;
 	
@@ -481,10 +700,6 @@ int update_dialog_dbinfo(struct dlg_cell * cell)
 		VAL_TYPE(values+18) = DB1_INT;
 		VAL_TYPE(values+19) = DB1_STR;
 
-		/* lock the entry */
-		entry = (d_table->entries)[cell->h_entry];
-		dlg_lock( d_table, &entry);
-
 		VAL_INT(values)			= cell->h_entry;
 		VAL_INT(values+1)		= cell->h_id;
 		VAL_INT(values+9)		= cell->start_ts;
@@ -540,10 +755,6 @@ int update_dialog_dbinfo(struct dlg_cell * cell)
 
 		VAL_TYPE(values+12) = VAL_TYPE(values+13) =DB1_STR;
 
-		/* lock the entry */
-		entry = (d_table->entries)[cell->h_entry];
-		dlg_lock( d_table, &entry);
-
 		VAL_INT(values)			= cell->h_entry;
 		VAL_INT(values+1)		= cell->h_id;
 		VAL_INT(values+10)		= cell->state;
@@ -568,139 +779,43 @@ int update_dialog_dbinfo(struct dlg_cell * cell)
 		return 0;
 	}
 
-	dlg_unlock( d_table, &entry);
 	return 0;
-
 error:
-	dlg_unlock( d_table, &entry);
 	return -1;
 }
 
-
+int update_dialog_dbinfo(struct dlg_cell * cell)
+{
+	struct dlg_entry entry;
+	/* lock the entry */
+	entry = (d_table->entries)[cell->h_entry];
+	dlg_lock( d_table, &entry);
+	if (update_dialog_dbinfo_unsafe(cell) != 0) {
+		dlg_unlock( d_table, &entry);
+		return -1;
+	} 
+	dlg_unlock( d_table, &entry);
+	return 0;
+}
 
 void dialog_update_db(unsigned int ticks, void * param)
 {
-	int index, i;
-	db_val_t values[DIALOG_TABLE_COL_NO];
+	int index;
 	struct dlg_entry entry;
 	struct dlg_cell  * cell; 
-	
-	db_key_t insert_keys[DIALOG_TABLE_COL_NO] = {		&h_entry_column,
-			&h_id_column,		&call_id_column,		&from_uri_column,
-			&from_tag_column,	&to_uri_column,			&to_tag_column,
-			&from_sock_column,	&to_sock_column,
-			&start_time_column,	&state_column,			&timeout_column,
-			&from_cseq_column,	&to_cseq_column,		&from_route_column,
-			&to_route_column, 	&from_contact_column, 	&to_contact_column,
-			&sflags_column,     &toroute_name_column, 	&req_uri_column };
-
-	if(use_dialog_table()!=0)
-		return;
-
-	/*save the current dialogs information*/
-	VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = 
-	VAL_TYPE(values+10) = VAL_TYPE(values+11) = DB1_INT;
-
-	VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = 
-	VAL_TYPE(values+5) = VAL_TYPE(values+6) = VAL_TYPE(values+7) = 
-	VAL_TYPE(values+8) = VAL_TYPE(values+12) = VAL_TYPE(values+13) = 
-	VAL_TYPE(values+14) = VAL_TYPE(values+15) = VAL_TYPE(values+16) = 
-	VAL_TYPE(values+17) = VAL_TYPE(values+20) = DB1_STR;
-
-	SET_NULL_FLAG(values, i, DIALOG_TABLE_COL_NO-6, 0);
-
-	VAL_TYPE(values+18) = DB1_INT;
-	VAL_TYPE(values+19) = DB1_STR;
 
 	LM_DBG("saving current_info \n");
 	
 	for(index = 0; index< d_table->size; index++){
-
 		/* lock the whole entry */
 		entry = (d_table->entries)[index];
 		dlg_lock( d_table, &entry);
 
 		for(cell = entry.first; cell != NULL; cell = cell->next){
-
-			if( (cell->dflags & DLG_FLAG_NEW) != 0 ) {
-
-				VAL_INT(values)			= cell->h_entry;
-				VAL_INT(values+1)		= cell->h_id;
-
-				VAL_INT(values+9)		= cell->start_ts;
-				VAL_INT(values+10)		= cell->state;
-				VAL_INT(values+11)	= (unsigned int)( (unsigned int)time(0) +
-					 cell->tl.timeout - get_ticks() );
-
-				SET_STR_VALUE(values+2, cell->callid);
-				SET_STR_VALUE(values+3, cell->from_uri);
-				SET_STR_VALUE(values+4, cell->tag[DLG_CALLER_LEG]);
-				SET_STR_VALUE(values+5, cell->to_uri);
-				SET_STR_VALUE(values+6, cell->tag[DLG_CALLEE_LEG]);
-				SET_PROPER_NULL_FLAG(cell->tag[DLG_CALLEE_LEG], values, 6);
-
-				SET_STR_VALUE(values+7,
-					cell->bind_addr[DLG_CALLER_LEG]->sock_str);
-				SET_STR_VALUE(values+8,
-					cell->bind_addr[DLG_CALLEE_LEG]->sock_str);
-				
-				SET_STR_VALUE(values+12, cell->cseq[DLG_CALLER_LEG]);
-				SET_STR_VALUE(values+13, cell->cseq[DLG_CALLEE_LEG]);
-
-				SET_STR_VALUE(values+14, cell->route_set[DLG_CALLER_LEG]);
-				SET_STR_VALUE(values+15, cell->route_set[DLG_CALLEE_LEG]);
-				SET_STR_VALUE(values+16, cell->contact[DLG_CALLER_LEG]);
-				SET_STR_VALUE(values+17, cell->contact[DLG_CALLEE_LEG]);
-			
-				SET_PROPER_NULL_FLAG(cell->route_set[DLG_CALLER_LEG],
-					values, 14);
-				SET_PROPER_NULL_FLAG(cell->route_set[DLG_CALLEE_LEG],
-					values, 15);
-				SET_PROPER_NULL_FLAG(cell->contact[DLG_CALLER_LEG],
-					values, 16);
-				SET_PROPER_NULL_FLAG(cell->contact[DLG_CALLEE_LEG],
-					values, 17);
-				
-				VAL_INT(values+18)		= cell->sflags;
-
-				SET_STR_VALUE(values+19, cell->toroute_name);
-				SET_PROPER_NULL_FLAG(cell->toroute_name,
-					values, 19);
-
-				SET_STR_VALUE(values+20, cell->req_uri);
-				SET_PROPER_NULL_FLAG(cell->req_uri,
-					values, 20);
-
-				if((dialog_dbf.insert(dialog_db_handle, insert_keys, 
-				values, DIALOG_TABLE_COL_NO)) !=0){
-					LM_ERR("could not add another dialog to db\n");
-					goto error;
-				}
-
-				cell->dflags &= ~(DLG_FLAG_NEW |DLG_FLAG_CHANGED);
-
-			} else if( (cell->dflags & DLG_FLAG_CHANGED)!=0 ){
-
-				VAL_INT(values)			= cell->h_entry;
-				VAL_INT(values+1)		= cell->h_id;
-
-				VAL_INT(values+10)		= cell->state;
-				VAL_INT(values+11)	= (unsigned int)( (unsigned int)time(0) +
-					 cell->tl.timeout - get_ticks() );
-				SET_STR_VALUE(values+12, cell->cseq[0]);
-				SET_STR_VALUE(values+13, cell->cseq[DLG_CALLEE_LEG]);
-
-
-				if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, 
-				(values), (insert_keys+10), (values+10), 2, 4)) !=0) {
-					LM_ERR("could not update database info\n");
-					goto error;
-				}
-
-				cell->dflags &= ~DLG_FLAG_CHANGED;
-
+			if (update_dialog_dbinfo_unsafe(cell) != 0) {
+				dlg_unlock( d_table, &entry);
+				goto error;
 			}
-
 		}
 		dlg_unlock( d_table, &entry);
 

+ 22 - 3
modules_k/dialog/dlg_db_handler.h

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2007 Voice System SRL
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -53,9 +54,20 @@
 #define TOROUTE_NAME_COL		"toroute_name"
 #define REQ_URI_COL				"req_uri"
 #define DIALOG_TABLE_NAME		"dialog"
-
 #define DLG_TABLE_VERSION		5
 
+#define DIALOG_TABLE_COL_NO 		21
+
+#define VARS_HASH_ID_COL 		"hash_id"
+#define VARS_HASH_ENTRY_COL		"hash_entry"
+#define VARS_KEY_COL			"dialog_key"
+#define VARS_VALUE_COL			"dialog_value"
+#define DIALOG_VARS_TABLE_NAME		"dialog_vars"
+#define DLG_VARS_TABLE_VERSION		1
+
+#define DIALOG_VARS_TABLE_COL_NO 4
+
+
 /*every minute the dialogs' information will be refreshed*/
 #define DB_DEFAULT_UPDATE_PERIOD	60
 #define DB_MODE_NONE				0
@@ -63,9 +75,8 @@
 #define DB_MODE_DELAYED				2
 #define DB_MODE_SHUTDOWN			3
 
-#define DIALOG_TABLE_COL_NO 		21
-
 
+/* Dialog table */
 extern str call_id_column; 
 extern str from_uri_column;
 extern str from_tag_column;
@@ -89,6 +100,14 @@ extern str toroute_name_column;
 extern str dialog_table_name;
 extern int dlg_db_mode;
 
+/* Dialog-Vars Table */
+extern str vars_h_id_column;
+extern str vars_h_entry_column;
+extern str vars_key_column;
+extern str vars_value_column;
+extern str dialog_vars_table_name;
+
+
 int init_dlg_db(const str *db_url, int dlg_hash_size, int db_update_period, int fetch_num_rows);
 int dlg_connect_db(const str *db_url);
 void destroy_dlg_db(void);

+ 16 - 6
modules_k/dialog/dlg_handlers.c

@@ -82,8 +82,8 @@ static str       rr_param;		/*!< record-route parameter for matching */
 static int       dlg_flag;		/*!< flag for dialog tracking */
 static pv_spec_t *timeout_avp;		/*!< AVP for timeout setting */
 static int       default_timeout;	/*!< default dialog timeout */
+static int       seq_match_mode;	/*!< dlg_match mode */ 
 static int       shutdown_done = 0;	/*!< 1 when destroy_dlg_handlers was called */
-extern int       seq_match_mode;	/*!< dlg_match mode */ 
 extern int       detect_spirals;
 
 extern struct rr_binds d_rrb;		/*!< binding to record-routing module */
@@ -113,9 +113,11 @@ static unsigned int CURR_DLG_ID  = 0xffffffff;	/*!< current dialog id */
  * \param dlg_flag_p dialog flag
  * \param timeout_avp_p AVP for timeout setting
  * \param default_timeout_p default timeout
+ * \param seq_match_mode_p matching mode
  */
 void init_dlg_handlers(char *rr_param_p, int dlg_flag_p,
-		pv_spec_t *timeout_avp_p ,int default_timeout_p)
+		pv_spec_t *timeout_avp_p ,int default_timeout_p,
+		int seq_match_mode_p)
 {
 	rr_param.s = rr_param_p;
 	rr_param.len = strlen(rr_param.s);
@@ -124,6 +126,7 @@ void init_dlg_handlers(char *rr_param_p, int dlg_flag_p,
 
 	timeout_avp = timeout_avp_p;
 	default_timeout = default_timeout_p;
+	seq_match_mode = seq_match_mode_p;
 }
 
 
@@ -676,6 +679,8 @@ int dlg_new_dialog(struct sip_msg *msg, struct cell *t)
 		return -1;
 	}
 
+	
+
 	/* save caller's tag, cseq, contact and record route*/
 	if (populate_leg_info(dlg, msg, t, DLG_CALLER_LEG,
 			&(get_from(msg)->tag_value)) !=0)
@@ -688,6 +693,9 @@ int dlg_new_dialog(struct sip_msg *msg, struct cell *t)
 	set_current_dialog(msg, dlg);
 	_dlg_ctx.dlg = dlg;
 
+	/* Populate initial varlist: */
+	dlg->vars = get_local_varlist_pointer(msg, 1);
+
 	link_dlg(dlg, 2/* extra ref for the callback and current dlg hook */);
 
 	/* first INVITE seen (dialog created, unconfirmed) */
@@ -713,10 +721,12 @@ int dlg_new_dialog(struct sip_msg *msg, struct cell *t)
 	if (_dlg_ctx.to_bye!=0)
 		dlg->dflags |= DLG_FLAG_TOBYE;
 
-	if ( d_tmb.register_tmcb( msg, t, TMCB_MAX,
-				dlg_tmcb_dummy, (void*)dlg, 0)<0 ) {
-		LM_ERR("failed cache in T the shortcut to dlg\n");
-		goto error;
+	if (t) {
+		if ( d_tmb.register_tmcb( msg, t, TMCB_MAX,
+					dlg_tmcb_dummy, (void*)dlg, 0)<0 ) {
+			LM_ERR("failed cache in T the shortcut to dlg\n");
+			goto error;
+		}
 	}
 #if 0
 		t->dialog_ctx = (void*) dlg;

+ 2 - 1
modules_k/dialog/dlg_handlers.h

@@ -60,7 +60,8 @@
  * \param seq_match_mode_p matching mode
  */
 void init_dlg_handlers(char *rr_param, int dlg_flag,
-		pv_spec_t *timeout_avp, int default_timeout);
+		pv_spec_t *timeout_avp, int default_timeout,
+		int seq_match_mode);
 
 
 /*!

+ 23 - 10
modules_k/dialog/dlg_hash.c

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2006 Voice System SRL
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -58,6 +59,7 @@
 #include "../../lib/kcore/hash_func.h"
 #include "../../lib/kmi/mi.h"
 #include "dlg_timer.h"
+#include "dlg_var.h"
 #include "dlg_hash.h"
 #include "dlg_profile.h"
 #include "dlg_req_within.h"
@@ -132,6 +134,7 @@ error0:
 inline void destroy_dlg(struct dlg_cell *dlg)
 {
 	int ret = 0;
+	struct dlg_var *var;
 
 	LM_DBG("destroying dialog %p\n",dlg);
 
@@ -178,6 +181,16 @@ inline void destroy_dlg(struct dlg_cell *dlg)
 	if (dlg->toroute_name.s)
 		shm_free(dlg->toroute_name.s);
 
+	
+	while (dlg->vars) {
+		var = dlg->vars;
+		dlg->vars = dlg->vars->next;
+		shm_free(var->key.s);
+		shm_free(var->value.s);
+		shm_free(var);
+	}
+
+
 	shm_free(dlg);
 	dlg = 0;
 }
@@ -543,10 +556,11 @@ void link_dlg(struct dlg_cell *dlg, int n)
  */
 #define unref_dlg_unsafe(_dlg,_cnt,_d_entry)   \
 	do { \
-		LM_DBG("unref dlg %p with %d, crt ref count: %d\n",\
+		(_dlg)->ref -= (_cnt); \
+		LM_DBG("unref dlg %p with %d -> %d\n",\
 			(_dlg),(_cnt),(_dlg)->ref);\
-		if ((_dlg)->ref<=0) {\
-			LM_CRIT("bogus op: ref %d with cnt %d for dlg %p [%u:%u] "\
+		if ((_dlg)->ref<0) {\
+			LM_CRIT("bogus ref %d with cnt %d for dlg %p [%u:%u] "\
 				"with clid '%.*s' and tags '%.*s' '%.*s'\n",\
 				(_dlg)->ref, _cnt, _dlg,\
 				(_dlg)->h_entry, (_dlg)->h_id,\
@@ -555,13 +569,11 @@ void link_dlg(struct dlg_cell *dlg, int n)
 				(_dlg)->tag[DLG_CALLER_LEG].s,\
 				(_dlg)->tag[DLG_CALLEE_LEG].len,\
 				(_dlg)->tag[DLG_CALLEE_LEG].s); \
-		} else { \
-			(_dlg)->ref -= (_cnt); \
-			if ((_dlg)->ref<=0) { \
-				unlink_unsafe_dlg( _d_entry, _dlg);\
-				LM_DBG("ref <=0 for dialog %p\n",_dlg);\
-				destroy_dlg(_dlg);\
-			}\
+		}\
+		if ((_dlg)->ref<=0) { \
+			unlink_unsafe_dlg( _d_entry, _dlg);\
+			LM_DBG("ref <=0 for dialog %p\n",_dlg);\
+			destroy_dlg(_dlg);\
 		}\
 	}while(0)
 
@@ -1132,3 +1144,4 @@ error:
 	return NULL;
 }
 
+

+ 6 - 1
modules_k/dialog/dlg_hash.h

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2006 Voice System SRL
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -75,6 +76,10 @@
 #define DLG_FLAG_CALLERBYE     (1<<4) /*!< bye from caller */
 #define DLG_FLAG_CALLEEBYE     (1<<5) /*!< bye from callee */
 #define DLG_FLAG_LOCALDLG      (1<<6) /*!< local dialog, unused */
+#define DLG_FLAG_CHANGED_VARS  (1<<7) /*!< dialog-variables changed */
+
+/* dialog-variable flags (in addition to dialog-flags) */
+#define DLG_FLAG_DEL           (1<<8) /*!< delete this var */
 
 #define DLG_CALLER_LEG         0 /*!< attribute that belongs to a caller leg */
 #define DLG_CALLEE_LEG         1 /*!< attribute that belongs to a callee leg */
@@ -112,6 +117,7 @@ struct dlg_cell
 	struct socket_info * bind_addr[2];	/*! binded address of caller and callee */
 	struct dlg_head_cbl  cbs;		/*!< dialog callbacks */
 	struct dlg_profile_link *profile_links; /*!< dialog profiles */
+	struct dlg_var       *vars;		/*!< dialog variables */
 };
 
 
@@ -158,7 +164,6 @@ extern struct dlg_cell  *current_dlg_pointer;
 #define dlg_unlock(_table, _entry) \
 		lock_set_release( (_table)->locks, (_entry)->lock_idx);
 
-
 /*!
  * \brief Unlink a dialog from the list without locking
  * \see unref_dlg_unsafe

+ 274 - 1
modules_k/dialog/dlg_var.c

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of kamailio, a free SIP server.
  *
@@ -23,9 +24,17 @@
 #include "../../route.h"
 
 #include "dlg_var.h"
+#include "dlg_hash.h"
+#include "dlg_profile.h"
+#include "dlg_db_handler.h"
 
 dlg_ctx_t _dlg_ctx;
 
+/*! global variable table, in case the dialog does not exist yet */
+struct dlg_var * var_table = 0;
+/*! ID of the current message */
+int msg_id;
+
 int dlg_cfg_cb(struct sip_msg *foo, unsigned int flags, void *bar)
 {
 	memset(&_dlg_ctx, 0, sizeof(dlg_ctx_t));
@@ -33,6 +42,270 @@ int dlg_cfg_cb(struct sip_msg *foo, unsigned int flags, void *bar)
 	return 1;
 }
 
+
+static inline struct dlg_var *new_dlg_var(str *key, str *val)
+{
+	struct dlg_var *var;
+
+	var =(struct dlg_var*)shm_malloc(sizeof(struct dlg_var));
+	if (var==NULL) {
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+	var->next = NULL;
+	var->vflags = DLG_FLAG_NEW;
+	/* set key */
+	var->key.len = key->len;
+	var->key.s = (char*)shm_malloc(var->key.len);
+	if (var->key.s==NULL) {
+		shm_free(var);			
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+	memcpy(var->key.s, key->s, key->len);
+	/* set value */
+	var->value.len = val->len;
+	var->value.s = (char*)shm_malloc(var->value.len);
+	if (var->value.s==NULL) {
+		shm_free(var->key.s);			
+		shm_free(var);			
+		LM_ERR("no more shm mem\n");
+		return NULL;
+	}
+	memcpy(var->value.s, val->s, val->len);
+	return var;
+}
+
+/*! Delete the current var-list */
+void free_local_varlist() {
+	struct dlg_var *var;
+	while (var_table) {
+		var = var_table;
+		var_table = var_table->next;
+		shm_free(var->key.s);
+		shm_free(var->value.s);
+		shm_free(var);
+	}
+}
+
+/*! Retrieve the local var-list pointer */
+struct dlg_var * get_local_varlist_pointer(struct sip_msg *msg, int clear_pointer) {
+	struct dlg_var *var;
+	/* New list, delete the old one */
+	if (msg->id != msg_id) {
+		free_local_varlist();
+		msg_id = msg->id;
+	}
+	var = var_table;
+	if (clear_pointer)
+		var_table = NULL;
+	return var;
+}
+
+/* Adds, updates and deletes dialog variables */
+int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new)
+{
+	struct dlg_var * var = NULL;
+	struct dlg_var * it;
+	struct dlg_var * it_prev;
+	struct dlg_var ** var_list;
+	
+	if (dlg) 
+		var_list = &dlg->vars;
+	else 
+		var_list = &var_table;
+
+	if ( val && (var=new_dlg_var(key, val))==NULL) {
+		LM_ERR("failed to create new dialog variable\n");
+		return -1;
+	}
+
+	/* iterate the list */
+	for( it_prev=NULL, it=*var_list ; it ; it_prev=it,it=it->next) {
+		if (key->len==it->key.len && memcmp(key->s,it->key.s,key->len)==0
+			&& (it->vflags & DLG_FLAG_DEL) == 0) {
+			/* found -> replace or delete it */
+			if (val==NULL) {
+				/* delete it */
+				if (it_prev) it_prev->next = it->next;
+				else *var_list = it->next;
+				/* Set the delete-flag for the current var: */
+				it->vflags &= DLG_FLAG_DEL;
+			} else {
+				/* replace the current it with var and free the it */
+				var->next = it->next;
+				/* Take the previous vflags: */
+				var->vflags = it->vflags & DLG_FLAG_CHANGED;
+				if (it_prev) it_prev->next = var;
+				else *var_list = var;				  
+			}
+
+			/* Free this var: */
+			shm_free(it->key.s);
+			shm_free(it->value.s);
+			shm_free(it);
+			return 0;
+		}
+	}
+
+	/* not found -> simply add a new one */
+
+	/* insert at the beginning of the list */
+	var->next = *var_list;
+	*var_list = var;
+
+	return 0;
+}
+
+str * get_dlg_variable_unsafe(struct dlg_cell *dlg, str *key)
+{
+	struct dlg_var *var, *var_list;
+
+	if (dlg) 
+		var_list = dlg->vars;
+	else
+		var_list = var_table;
+
+	/* iterate the list */
+	for(var=var_list ; var ; var=var->next) {
+		if (key->len==var->key.len && memcmp(key->s,var->key.s,key->len)==0
+		&& (var->vflags & DLG_FLAG_DEL) == 0) {
+			return &var->value;
+		}
+	}
+
+	return NULL;
+}
+
+int pv_parse_dialog_var_name(pv_spec_p sp, str *in)
+{
+	if(in==NULL || in->s==NULL || sp==NULL)
+		return -1;
+
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
+	sp->pvp.pvn.u.isname.type = AVP_NAME_STR;
+	sp->pvp.pvn.u.isname.name.s = *in;
+
+	return 0;
+}
+
+/*! Internal debugging function: Prints the list of dialogs */
+void print_lists(struct dlg_cell *dlg) {
+	struct dlg_var *varlist;
+	varlist = var_table;
+	LM_DBG("Internal var-list (%p):\n", varlist);
+	while (varlist) {
+		LM_DBG("%.*s=%.*s (flags %i)\n",
+			varlist->key.len, varlist->key.s,
+			varlist->value.len, varlist->value.s,
+			varlist->vflags);
+		varlist = varlist->next;
+	}
+	if (dlg) {
+		varlist = dlg->vars;
+		LM_DBG("Dialog var-list (%p):\n", varlist);
+		while (varlist) {
+			LM_DBG("%.*s=%.*s (flags %i)\n",
+				varlist->key.len, varlist->key.s,
+				varlist->value.len, varlist->value.s,
+				varlist->vflags);
+			varlist = varlist->next;
+		}
+	}
+}
+
+int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res)
+{
+	struct dlg_cell *dlg;
+	str * value;
+
+	if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL) {
+		LM_CRIT("BUG - bad parameters\n");
+		return -1;
+	}
+
+	/* Retrieve the current dialog */
+	dlg=get_current_dlg_pointer();
+
+	if (dlg) {
+		/* Lock the dialog */
+		dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+	} else {
+		/* Verify the local list */
+		get_local_varlist_pointer(msg, 0);
+	}
+
+	value = get_dlg_variable_unsafe(dlg, &param->pvn.u.isname.name.s);
+
+	print_lists(dlg);
+
+	/* unlock dialog */
+	if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+
+	if (value)
+		return pv_get_strval(msg, param, res, value);
+
+
+	return 0;
+}
+
+int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val)
+{
+	struct dlg_cell *dlg;
+
+	/* Retrieve the current dialog */
+	dlg=get_current_dlg_pointer();
+	
+	if (dlg) {
+		/* Lock the dialog */
+		dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
+	} else {
+		/* Verify the local list */
+		get_local_varlist_pointer(msg, 0);
+	}
+
+	if (param==NULL || param->pvn.type!=PV_NAME_INTSTR || param->pvn.u.isname.type!=AVP_NAME_STR || param->pvn.u.isname.name.s.s==NULL ) {
+		LM_CRIT("BUG - bad parameters\n");
+		return -1;
+	}
+
+	if (val==NULL || val->flags&(PV_VAL_NONE|PV_VAL_NULL|PV_VAL_EMPTY)) {
+		/* if NULL, remove the value */
+		if (set_dlg_variable_unsafe(dlg, &param->pvn.u.isname.name.s, NULL, 1)!=0) {
+			LM_ERR("failed to delete dialog variable <%.*s>\n", param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s);
+			/* unlock dialog */
+			if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+			return -1;
+		}
+	} else {
+		/* if value, must be string */
+		if ( !(val->flags&PV_VAL_STR)) {
+			LM_ERR("non-string values are not supported\n");
+			/* unlock dialog */
+			if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+			return -1;
+		}
+
+		if (set_dlg_variable_unsafe(dlg, &param->pvn.u.isname.name.s, &val->rs, 1)!=0) {
+			LM_ERR("failed to store dialog values <%.*s>\n",param->pvn.u.isname.name.s.len,param->pvn.u.isname.name.s.s);
+			/* unlock dialog */
+			if (dlg) dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+			return -1;
+		}
+	}
+	/* unlock dialog */
+	if (dlg) {
+		dlg->dflags &= DLG_FLAG_CHANGED_VARS;		
+		dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
+		if ( dlg_db_mode==DB_MODE_REALTIME )
+			update_dialog_dbinfo(dlg);
+
+	}
+	print_lists(dlg);
+
+	return 0;
+}
+
 int pv_get_dlg_ctx(struct sip_msg *msg,  pv_param_t *param,
 		pv_value_t *res)
 {
@@ -92,7 +365,7 @@ int pv_set_dlg_ctx(struct sip_msg* msg, pv_param_t *param,
 			_dlg_ctx.to_bye = n;
 		break;
 		case 4:
-			if(val && val->flags&PV_VAL_STR) {
+			if(val->flags&PV_VAL_STR) {
 				if(val->rs.s[val->rs.len]=='\0'
 						&& val->rs.len<DLG_TOROUTE_SIZE) {
 					_dlg_ctx.to_route = route_lookup(&main_rt, val->rs.s);

+ 22 - 0
modules_k/dialog/dlg_var.h

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of kamailio, a free SIP server.
  *
@@ -40,6 +41,27 @@ typedef struct _dlg_ctx {
 	unsigned int dir;
 } dlg_ctx_t;
 
+/* A dialog-variable */
+struct dlg_var {
+	str key;
+	str value;
+	unsigned int vflags;		/*!< internal variable flags */
+	struct dlg_var *next;
+};
+
+
+int pv_parse_dialog_var_name(pv_spec_p sp, str *in);
+
+int pv_get_dlg_variable(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
+
+int pv_set_dlg_variable(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
+
+/*! Retrieve the current var-list */
+struct dlg_var * get_local_varlist_pointer(struct sip_msg *msg, int clear_pointer);
+
+/* Adds, updates and deletes dialog variables */
+int set_dlg_variable_unsafe(struct dlg_cell *dlg, str *key, str *val, int new);
+
 extern dlg_ctx_t _dlg_ctx;
 
 int pv_get_dlg_ctx(struct sip_msg *msg,  pv_param_t *param,

+ 19 - 0
modules_k/dialog/doc/dialog.xml

@@ -21,6 +21,14 @@
 			<email>[email protected]</email>
 		</address>
 		</author>
+		<author>
+		<firstname>Carsten</firstname>
+		<surname>Bock</surname>
+		<affiliation><orgname>ng-voice.com</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
 		<editor>
 		<firstname>Bogdan-Andrei</firstname>
 		<surname>Iancu</surname>
@@ -28,11 +36,22 @@
 			<email>[email protected]</email>
 		</address>
 		</editor>
+		<editor>
+		<firstname>Carsten</firstname>
+		<surname>Bock</surname>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</editor>
 	</authorgroup>
 	<copyright>
 		<year>2006</year>
 		<holder>&voicesystem;</holder>
 	</copyright>
+	<copyright>
+		<year>2011</year>
+		<holder>Carsten Bock, http://www.ng-voice.com</holder>
+	</copyright>
 	<revhistory>
 		<revision>
 		<revnumber>$Revision$</revnumber>

+ 121 - 3
modules_k/dialog/doc/dialog_admin.xml

@@ -341,9 +341,9 @@ modparam("dialog", "dlg_match_mode", 1)
 		<example>
 			<title>Set <varname>detect_spirals</varname> parameter</title>
 			<programlisting format="linespecific">
-...
-modparam("dialog", "detect_spirals", 1)
-...
+				...
+				modparam("dialog", "detect_spirals", 1)
+				...
 			</programlisting>
 		</example>
 	</section>
@@ -891,6 +891,109 @@ modparam("dialog", "toroute_column", "timeout_route")
 		</example>
 	</section>
 
+	<section>
+		<title><varname>vars_table_name</varname> (string)</title>
+		<para>
+		If you want to store the variables for a dialog in a
+		database a table name must be specified.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>dialog_vars</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vars_table_name</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "vars_table_name", "my_dialog_vars")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>vars_h_id_column</varname> (string)</title>
+		<para>
+			The column name in the database to store the dialogs'
+			hash id information (as a reference to the dialog table).
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>hash_id</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vars_h_id_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "vars_h_id_column", "vars_h_id_name")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>vars_h_entry_column</varname> (string)</title>
+		<para>
+			The column name in the database to store the dialogs'
+			hash entry information (as a reference to the dialog table).
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>hash_entry</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vars_h_entry_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "vars_h_entry_column", "vars_h_entry_name")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>vars_key_column</varname> (string)</title>
+		<para>
+			The column name in the database to store the keys of a variable.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>dialog_key</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vars_key_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "vars_key_column", "vars_key_name")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section>
+		<title><varname>vars_value_column</varname> (string)</title>
+		<para>
+			The column name in the database to store the keys of a variable.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>dialog_value</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>vars_value_column</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("dialog", "vars_value_column", "vars_value_name")
+...
+</programlisting>
+		</example>
+	</section>
+
 	<section>
 		<title><varname>profiles_with_value</varname> (string)</title>
 		<para>
@@ -1947,6 +2050,21 @@ if(!uri == myself) {
 			<para>Access to dialog context attributes.</para>
 		</section>
 
+		<section>
+			<title><varname>$dlg_var(key)</varname></title>
+			<para>
+			This is a read/write variable that can be used to store 
+			custom values assigned with a dialog (e.g. the URI of a
+			billing-server, an assigned emergency-server).
+			This pseudo-variable will be available only for subsequential
+			requests after doing loose_route().
+			</para>
+			<para>
+			Note: You will receive "NULL", if there is no dialog for this
+			request.
+			</para>
+		</section>
+
 	</section>
 
 

+ 12 - 0
modules_k/rr/doc/rr.xml

@@ -29,6 +29,14 @@
 			<email>[email protected]</email>
 		</address>
 		</author>
+		<author>
+		<firstname>Carsten</firstname>
+		<surname>Bock</surname>
+		<affiliation><orgname>ng-voice.com</orgname></affiliation>
+		<address>
+			<email>[email protected]</email>
+		</address>
+		</author>
 		<editor>
 		<firstname>Jan</firstname>
 		<surname>Janak</surname>
@@ -52,6 +60,10 @@
 		<year>2005</year>
 		<holder>&voicesystem;</holder>
 	</copyright>
+	<copyright>
+		<year>2011</year>
+		<holder>Carsten Bock, [email protected]</holder>
+	</copyright>
 	<revhistory>
 		<revision>
 		<revnumber>$Revision$</revnumber>

+ 17 - 2
modules_k/rr/doc/rr_admin.xml

@@ -471,9 +471,24 @@ if (is_direction("downstream")) {
 </programlisting>
 		</example>
 	</section>
+	</section>
+	<section>
+		<title>Exported Pseudo Variables</title>
+		<section>
+			<title><function moreinfo="none">$route_uri</function></title>
+			<para>
+			Returns the URI of the top route-header.
+			</para>
 
-
-
+		<example>
+		<title>$route_uri</title>
+		<programlisting format="linespecific">
+...
+    xdbg("Route-URI is: $route_uri\n");
+...
+		</programlisting>
+		</example>
+	        </section>
 
 	</section>
 </chapter>

+ 1 - 9
modules_k/rr/loose.c

@@ -928,15 +928,7 @@ int loose_route(struct sip_msg* _m)
 #else
 		if (is_myself(&_m->parsed_uri.host, _m->parsed_uri.port_no)) {
 #endif
-			/* even if RURI is myself check to see if it's really after_strict by
-			 looking over the lr param, as kamailio always adds it
-			 If lr is missing, than surely this was not after strict
-			*/
-			if(!is_strict(GET_RURI(_m))){
-				return after_strict(_m);
-			} else {
-				return after_loose(_m, 0);
-			}
+			return after_strict(_m);
 		} else {
 			return after_loose(_m, 0);
 		}

+ 59 - 2
modules_k/rr/rr_mod.c

@@ -2,6 +2,7 @@
  * $Id$
  *
  * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2011 Carsten Bock, [email protected]
  *
  * This file is part of Kamailio, a free SIP server.
  *
@@ -38,6 +39,7 @@
 #include "../../pvar.h"
 #include "../../mem/mem.h"
 #include "../../mod_fix.h"
+#include "../../parser/parse_rr.h"
 #include "loose.h"
 #include "record.h"
 #include "rr_cb.h"
@@ -71,7 +73,8 @@ static int w_record_route_preset(struct sip_msg *,char *, char *);
 static int w_add_rr_param(struct sip_msg *,char *, char *);
 static int w_check_route_param(struct sip_msg *,char *, char *);
 static int w_is_direction(struct sip_msg *,char *, char *);
-
+/* PV functions */
+static int pv_get_route_uri_f(struct sip_msg *, pv_param_t *, pv_value_t *);
 /*!
  * \brief Exported functions
  */
@@ -112,6 +115,16 @@ static param_export_t params[] ={
 	{0, 0, 0 }
 };
 
+/*!
+ * \brief Exported Pseudo variables
+ */
+static pv_export_t mod_pvs[] = {
+    {{"route_uri", (sizeof("route_uri")-1)}, /* URI of the first Route-Header */
+     PVT_OTHER, pv_get_route_uri_f, 0, 0, 0, 0, 0},
+    {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
+};
+
+
 
 struct module_exports exports = {
 	"rr",
@@ -120,7 +133,7 @@ struct module_exports exports = {
 	params,			/*!< Exported parameters */
 	0,				/*!< exported statistics */
 	0,				/*!< exported MI functions */
-	0,				/*!< exported pseudo-variables */
+	mod_pvs,			/*!< exported pseudo-variables */
 	0,				/*!< extra processes */
 	mod_init,			/*!< initialize module */
 	0,				/*!< response function*/
@@ -292,3 +305,47 @@ static int w_is_direction(struct sip_msg *msg,char *dir, char *foo)
 {
 	return ((is_direction(msg,(int)(long)dir)==0)?1:-1);
 }
+
+
+/*
+ * Return the URI of the topmost Route-Header.
+ */
+static int
+pv_get_route_uri_f(struct sip_msg *msg, pv_param_t *param,
+		  pv_value_t *res)
+{
+	struct hdr_field* hdr;
+	rr_t* rt;
+	str uri;
+
+	if (!msg) {
+		LM_ERR("No message?!?\n");
+		return -1;
+	}
+
+	/* Parse the message until the First-Route-Header: */
+	if (parse_headers(msg, HDR_ROUTE_F, 0) == -1) {
+		LM_ERR("while parsing message\n");
+		return -1;
+    	}
+	
+	if (!msg->route) {
+		LM_INFO("No route header present.\n");
+		return -1;
+	}
+	hdr = msg->route;
+
+	/* Parse the contents of the header: */
+	if (parse_rr(hdr) == -1) {
+		LM_ERR("Error while parsing Route header\n");
+                return -1;
+	}
+
+
+	/* Retrieve the Route-Header */	
+	rt = (rr_t*)hdr->parsed;
+	uri = rt->nameaddr.uri;
+
+	return pv_get_strval(msg, param, res, &uri);
+}
+