src/modules/dialog/dlg_db_handler.c
a70300b1
 /*
  * Copyright (C) 2007 Voice System SRL
60d9c557
  * Copyright (C) 2011 Carsten Bock, carsten@ng-voice.com
a70300b1
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
a70300b1
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
a70300b1
  * 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
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
a70300b1
  * 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.
  *
0e86b73a
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
a70300b1
  *
c3291cfe
  */
 
 /*!
  * \file
  * \brief Database interface
  * \ingroup dialog
  * Module: \ref dialog
a70300b1
  */
 
 #include <stdlib.h>
 #include <string.h>
 #include <sys/time.h>
 
cf83221d
 #include "../../core/dprint.h"
 #include "../../core/ut.h"
 #include "../../core/timer.h"
afda814b
 #include "../../lib/srdb1/db.h"
cf83221d
 #include "../../core/str.h"
 #include "../../core/socket_info.h"
 #include "../../core/counters.h"
a70300b1
 #include "dlg_hash.h"
60d9c557
 #include "dlg_var.h"
10037f65
 #include "dlg_cb.h"
82fe2bff
 #include "dlg_profile.h"
a70300b1
 #include "dlg_db_handler.h"
 
 
e2cf6343
 str call_id_column			=	str_init(CALL_ID_COL);
 str from_uri_column			=	str_init(FROM_URI_COL);
 str from_tag_column			=	str_init(FROM_TAG_COL);
 str to_uri_column			=	str_init(TO_URI_COL);
 str to_tag_column			=	str_init(TO_TAG_COL);
 str h_id_column				=	str_init(HASH_ID_COL);
 str h_entry_column			=	str_init(HASH_ENTRY_COL);
 str state_column			=	str_init(STATE_COL);
 str start_time_column		=	str_init(START_TIME_COL);
 str timeout_column			=	str_init(TIMEOUT_COL);
 str to_cseq_column			=	str_init(TO_CSEQ_COL);
 str from_cseq_column		=	str_init(FROM_CSEQ_COL);
 str to_route_column			=	str_init(TO_ROUTE_COL);
 str from_route_column		=	str_init(FROM_ROUTE_COL);
 str to_contact_column		=	str_init(TO_CONTACT_COL);
 str from_contact_column		=	str_init(FROM_CONTACT_COL);
 str to_sock_column			=	str_init(TO_SOCK_COL);
 str from_sock_column		=	str_init(FROM_SOCK_COL);
9f586299
 str sflags_column			=	str_init(SFLAGS_COL);
82fe2bff
 str iflags_column			=	str_init(IFLAGS_COL);
48c3a21a
 str toroute_name_column		=	str_init(TOROUTE_NAME_COL);
8c1b49d5
 str req_uri_column			=	str_init(REQ_URI_COL);
82fe2bff
 str xdata_column			=	str_init(XDATA_COL);
e2cf6343
 str dialog_table_name		=	str_init(DIALOG_TABLE_NAME);
a70300b1
 int dlg_db_mode				=	DB_MODE_NONE;
 
60d9c557
 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);
 
afda814b
 static db1_con_t* dialog_db_handle    = 0; /* database connection handle */
a70300b1
 static db_func_t dialog_dbf;
 
138c6449
 extern int dlg_enable_stats;
8f9b6ea5
 extern int dlg_h_id_start;
 extern int dlg_h_id_step;
82fe2bff
 
a70300b1
 #define SET_STR_VALUE(_val, _str)\
 	do{\
 			VAL_STR((_val)).s 		= (_str).s;\
 			VAL_STR((_val)).len 	= (_str).len;\
 	}while(0);
 
 #define SET_NULL_FLAG(_vals, _i, _max, _flag)\
 	do{\
 		for((_i) = 0;(_i)<(_max); (_i)++)\
 			VAL_NULL((_vals)+(_i)) = (_flag);\
 	}while(0);
 
 #define SET_PROPER_NULL_FLAG(_str, _vals, _index)\
 	do{\
 		if( (_str).len == 0)\
 			VAL_NULL( (_vals)+(_index) ) = 1;\
 		else\
 			VAL_NULL( (_vals)+(_index) ) = 0;\
 	}while(0);
 
97e5849d
 #define GET_STR_VALUE(_res, _values, _index, _not_null, _unref)\
a70300b1
 	do{\
97e5849d
 		if (VAL_NULL((_values)+ (_index))) { \
 			if (_not_null) {\
5bdee1ff
 				if (_unref) dlg_unref(dlg,1);\
56c501ab
 				goto next_dialog; \
97e5849d
 			} else { \
 				(_res).s = 0; \
 				(_res).len = 0; \
 			}\
 		} else { \
 			(_res).s = VAL_STR((_values)+ (_index)).s;\
 			(_res).len = strlen(VAL_STR((_values)+ (_index)).s);\
 		} \
a70300b1
 	}while(0);
 
3d3f5ca1
 static int load_dialog_vars_from_db(int fetch_num_rows, int mode,
 		dlg_iuid_t *mval);
a70300b1
 
e2cf6343
 int dlg_connect_db(const str *db_url)
011f0642
 {
60d9c557
 	if (dialog_db_handle) {
 		LM_CRIT("BUG - db connection found already open\n");
011f0642
 		return -1;
 	}
60d9c557
 	if ((dialog_db_handle = dialog_dbf.init(db_url)) == 0)
 		return -1;
011f0642
 	return 0;
 }
 
a70300b1
 
9eb6ab94
 int init_dlg_db(const str *db_url, int dlg_hash_size , int db_update_period, int fetch_num_rows, int db_skip_load)
a70300b1
 {
 	/* Find a database module */
e2cf6343
 	if (db_bind_mod(db_url, &dialog_dbf) < 0){
b9bb99d2
 		LM_ERR("Unable to bind to a database driver\n");
a70300b1
 		return -1;
 	}
 
011f0642
 	if (dlg_connect_db(db_url)!=0){
9859cb67
 		LM_ERR("Unable to connect to the database\n");
a70300b1
 		return -1;
 	}
 
924f2089
 	if(db_check_table_version(&dialog_dbf, dialog_db_handle, &dialog_table_name, DLG_TABLE_VERSION) < 0) {
a41a0c20
 		DB_TABLE_VERSION_ERROR(dialog_table_name);
 		goto dberror;
60d9c557
 	}
 
 	if(db_check_table_version(&dialog_dbf, dialog_db_handle, &dialog_vars_table_name, DLG_VARS_TABLE_VERSION) < 0) {
a41a0c20
 		DB_TABLE_VERSION_ERROR(dialog_vars_table_name);
 		goto dberror;
a70300b1
 	}
 
9859cb67
 	if( (dlg_db_mode==DB_MODE_DELAYED) && (register_timer( dialog_update_db, 0, db_update_period)<0 )) {
 		LM_ERR("Failed to register update db timer\n");
a41a0c20
 		goto dberror;
a70300b1
 	}
 
9eb6ab94
 	if ( db_skip_load == 0 ) {
3d3f5ca1
 		if( (load_dialog_info_from_db(dlg_hash_size, fetch_num_rows, 0, NULL) ) !=0 ){
9859cb67
 			LM_ERR("Unable to load the dialog data\n");
a41a0c20
 			goto dberror;
9eb6ab94
 		}
3d3f5ca1
 		if( (load_dialog_vars_from_db(fetch_num_rows, 0, NULL) ) !=0 ){
9859cb67
 			LM_ERR("Unable to load the dialog variable data\n");
a41a0c20
 			goto dberror;
9eb6ab94
 		}
60d9c557
 	}
011f0642
 	dialog_dbf.close(dialog_db_handle);
 	dialog_db_handle = 0;
 
a70300b1
 	return 0;
a41a0c20
 
 dberror:
 	dialog_dbf.close(dialog_db_handle);
 	dialog_db_handle = 0;
 	return -1;
a70300b1
 }
 
 
 
351fc577
 void destroy_dlg_db(void)
a70300b1
 {
b9bb99d2
 	/* close the DB connection */
a70300b1
 	if (dialog_db_handle) {
 		dialog_dbf.close(dialog_db_handle);
 		dialog_db_handle = 0;
 	}
 }
 
 
 
351fc577
 static int use_dialog_table(void)
a70300b1
 {
 	if(!dialog_db_handle){
b9bb99d2
 		LM_ERR("invalid database handle\n");
a70300b1
 		return -1;
 	}
 
e2cf6343
 	if (dialog_dbf.use_table(dialog_db_handle, &dialog_table_name) < 0) {
b9bb99d2
 		LM_ERR("Error in use_table\n");
a70300b1
 		return -1;
 	}
 
 	return 0;
 }
 
60d9c557
 static int use_dialog_vars_table(void)
 {
 	if(!dialog_db_handle){
9859cb67
 		LM_ERR("invalid database handle for dialog_vars\n");
60d9c557
 		return -1;
 	}
 
 	if (dialog_dbf.use_table(dialog_db_handle, &dialog_vars_table_name) < 0) {
9859cb67
 		LM_ERR("Error in use_table for dialog_vars\n");
60d9c557
 		return -1;
 	}
 
 	return 0;
 }
 
62c98ed1
 struct socket_info * create_socket_info(db_val_t * vals, int n){
 
 	struct socket_info * sock;
fcebc1f0
 	char* p;
 	str host;
62c98ed1
 	int port, proto;
 
 	/* socket name */
fcebc1f0
 	p = (VAL_STR(vals+n)).s;
62c98ed1
 
fcebc1f0
 	if (VAL_NULL(vals+n) || p==0 || p[0]==0){
62c98ed1
 		sock = 0;
 	} else {
fcebc1f0
 		if (parse_phostport( p, &host.s, &host.len, 
62c98ed1
 		&port, &proto)!=0) {
fcebc1f0
 			LM_ERR("bad socket <%s>\n", p);
62c98ed1
 			return 0;
 		}
 		sock = grep_sock_info( &host, (unsigned short)port, proto);
 		if (sock==0) {
fcebc1f0
 			LM_WARN("non-local socket <%s>...ignoring\n", p);
62c98ed1
 			}
 	}
a70300b1
 
62c98ed1
 	return sock;
a70300b1
 }
 
 
62c98ed1
 
3d3f5ca1
 int load_dialog_info_from_db(int dlg_hash_size, int fetch_num_rows,
 		int mode, str *mval)
62c98ed1
 {
5362d0f7
 	db_key_t query_cols[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,
 			&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,
 			&from_sock_column,	&to_sock_column,    &sflags_column,
 			&toroute_name_column,	&req_uri_column, &xdata_column,
 			&iflags_column};
3d3f5ca1
 	db_key_t match_cols[1] = { &call_id_column };
 	db_val_t match_vals[1];
 	int match_cols_no = 0;
afda814b
 	db1_res_t * res;
a70300b1
 	db_val_t * values;
 	db_row_t * rows;
 	int i, nr_rows;
 	struct dlg_cell *dlg;
8c1b49d5
 	str callid, from_uri, to_uri, from_tag, to_tag, req_uri;
62c98ed1
 	str cseq1, cseq2, contact1, contact2, rroute1, rroute2;
48c3a21a
 	str toroute_name;
82fe2bff
 	str xdata;
a70300b1
 	unsigned int next_id;
82fe2bff
 	srjson_doc_t jdoc;
3d3f5ca1
 #define DLG_MAX_DB_LOAD_EXTRA 256
 	dlg_iuid_t dbuid[DLG_MAX_DB_LOAD_EXTRA];
 	int loaded_extra = 0;
 	int loaded_extra_more = 0;
8f9b6ea5
 	dlg_cell_t *dit;
5362d0f7
 
 	if(use_dialog_table() != 0){
 		return -1;
 	}
 
3d3f5ca1
 	if(mode==1 && mval!=NULL && mval->len>0) {
 		match_vals[0].type = DB1_STR;
 		match_vals[0].nul = 0;
 		match_vals[0].val.str_val = *mval;
 		match_cols_no = 1;
 	}
 
a70300b1
 	res = 0;
5362d0f7
 
 	if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH) && (fetch_num_rows > 0)) {
3d3f5ca1
 		if(dialog_dbf.query(dialog_db_handle, match_cols, 0, match_vals,
 				query_cols, match_cols_no, DIALOG_TABLE_COL_NO, 0, 0) < 0) {
5362d0f7
 			LM_ERR("Error while querying (fetch) database\n");
 			goto error;
 		}
 		if(dialog_dbf.fetch_result(dialog_db_handle, &res, fetch_num_rows) < 0) {
 			LM_ERR("fetching rows failed\n");
 			goto error;
 		}
 	} else {
3d3f5ca1
 		if(dialog_dbf.query(dialog_db_handle, match_cols, 0, match_vals,
 				query_cols, match_cols_no, DIALOG_TABLE_COL_NO, 0, &res) < 0) {
5362d0f7
 			LM_ERR("Error while querying database\n");
 			goto error;
 		}
 	}
a70300b1
 
 	nr_rows = RES_ROW_N(res);
 
b9bb99d2
 	LM_DBG("the database has information about %i dialogs\n", nr_rows);
a70300b1
 
 	rows = RES_ROWS(res);
62c98ed1
 
68c8b77d
 	do {
b921e350
 		/* for every row---dialog */
68c8b77d
 		for(i=0; i<nr_rows; i++){
b921e350
 
68c8b77d
 			values = ROW_VALUES(rows + i);
b921e350
 
68c8b77d
 			if (VAL_NULL(values) || VAL_NULL(values+1)) {
 				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
 					h_entry_column.len, h_entry_column.s,
 					h_id_column.len, h_id_column.s);
 				continue;
 			}
b921e350
 
68c8b77d
 			if (VAL_NULL(values+7) || VAL_NULL(values+8)) {
 				LM_ERR("columns %.*s or/and %.*s cannot be null -> skipping\n",
 					start_time_column.len, start_time_column.s,
 					state_column.len, state_column.s);
 				continue;
 			}
b921e350
 
68c8b77d
 			/*restore the dialog info*/
 			GET_STR_VALUE(callid, values, 2, 1, 0);
 			GET_STR_VALUE(from_uri, values, 3, 1, 0);
 			GET_STR_VALUE(from_tag, values, 4, 1, 0);
 			GET_STR_VALUE(to_uri, values, 5, 1, 0);
8c1b49d5
 			GET_STR_VALUE(req_uri, values, 20, 1, 0);
b921e350
 
8c1b49d5
 			if((dlg=build_new_dlg(&callid, &from_uri, &to_uri, &from_tag,
 							&req_uri))==0){
68c8b77d
 				LM_ERR("failed to build new dialog\n");
 				goto error;
 			}
b921e350
 
68c8b77d
 			if(dlg->h_entry != VAL_INT(values)){
 				LM_ERR("inconsistent hash data in the dialog database: "
b631edb9
 					"you may have restarted Kamailio using a different "
b921e350
 					"hash_size: please erase %.*s database and restart\n", 
68c8b77d
 					dialog_table_name.len, dialog_table_name.s);
 				shm_free(dlg);
 				goto error;
 			}
b921e350
 
3d3f5ca1
 			if(mode!=0) {
 				dlg_lock(d_table, &(d_table->entries[dlg->h_entry]));
8f9b6ea5
 				/* loading at runtime - check dialog id conflicts */
 				dit = (d_table->entries)[dlg->h_entry].first;
 				while (dit) {
 					if (dit->h_id == VAL_INT(values+1)) {
 						break;
 					}
 					dit = dit->next;
 				}
 				if(dit) {
0f2f7611
 					if(mode==1) {
 						LM_WARN("conflicting dialog id: %u/%u - skipping\n",
8f9b6ea5
 							dlg->h_entry, (unsigned int)VAL_INT(values+1));
0f2f7611
 					} else {
 						LM_DBG("conflicting dialog id: %u/%u - skipping\n",
 							dlg->h_entry, (unsigned int)VAL_INT(values+1));
 					}
8f9b6ea5
 					dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
 					shm_free(dlg);
 					continue;
 				}
3d3f5ca1
 			}
 
68c8b77d
 			/*link the dialog*/
6fb686de
 			link_dlg(dlg, 0, 0);
b921e350
 
68c8b77d
 			dlg->h_id = VAL_INT(values+1);
 			next_id = d_table->entries[dlg->h_entry].next_id;
8f9b6ea5
 			if(dlg_h_id_step==1) {
 				d_table->entries[dlg->h_entry].next_id =
 						(next_id <= dlg->h_id) ? (dlg->h_id+1) : next_id;
 			} else {
 				/* update next id only if matches this instance series */
 				if((dlg->h_id - dlg_h_id_start) % dlg_h_id_step == 0) {
 					d_table->entries[dlg->h_entry].next_id =
 						(next_id <= dlg->h_id) ? (dlg->h_id + dlg_h_id_step)
 								: next_id;
 				}
 			}
3d3f5ca1
 			if(mode!=0) {
 				dlg_unlock(d_table, &(d_table->entries[dlg->h_entry]));
 			}
 
68c8b77d
 			GET_STR_VALUE(to_tag, values, 6, 1, 1);
b921e350
 
68c8b77d
 			dlg->start_ts	= VAL_INT(values+7);
b921e350
 
68c8b77d
 			dlg->state 		= VAL_INT(values+8);
12d5cef9
 			if (dlg->state==DLG_STATE_CONFIRMED_NA ||
 			dlg->state==DLG_STATE_CONFIRMED) {
b0cd09d2
 				if_update_stat(dlg_enable_stats, active_dlgs, 1);
12d5cef9
 			} else if (dlg->state==DLG_STATE_EARLY) {
b0cd09d2
 				if_update_stat(dlg_enable_stats, early_dlgs, 1);
12d5cef9
 			}
 
82fe2bff
 			dlg->tl.timeout = (unsigned int)(VAL_INT(values+9));
 			LM_DBG("db dialog timeout is %u (%u/%u)\n", dlg->tl.timeout,
 					get_ticks(), (unsigned int)time(0));
3d5f8af6
 			if (dlg->tl.timeout<=(unsigned int)time(0)) {
68c8b77d
 				dlg->tl.timeout = 0;
3d5f8af6
 				dlg->lifetime = 0;
 			} else {
 				dlg->lifetime = dlg->tl.timeout - dlg->start_ts;
68c8b77d
 				dlg->tl.timeout -= (unsigned int)time(0);
3d5f8af6
 			}
b921e350
 
68c8b77d
 			GET_STR_VALUE(cseq1, values, 10 , 1, 1);
 			GET_STR_VALUE(cseq2, values, 11 , 1, 1);
 			GET_STR_VALUE(rroute1, values, 12, 0, 0);
 			GET_STR_VALUE(rroute2, values, 13, 0, 0);
 			GET_STR_VALUE(contact1, values, 14, 1, 1);
 			GET_STR_VALUE(contact2, values, 15, 1, 1);
b921e350
 
68c8b77d
 			if ( (dlg_set_leg_info( dlg, &from_tag, &rroute1, &contact1,
 			&cseq1, DLG_CALLER_LEG)!=0) ||
9859cb67
 			(dlg_set_leg_info( dlg, &to_tag, &rroute2, &contact2, &cseq2, DLG_CALLEE_LEG)!=0) ) {
68c8b77d
 				LM_ERR("dlg_set_leg_info failed\n");
5bdee1ff
 				dlg_unref(dlg,1);
68c8b77d
 				continue;
 			}
b921e350
 
68c8b77d
 			dlg->bind_addr[DLG_CALLER_LEG] = create_socket_info(values, 16);
 			dlg->bind_addr[DLG_CALLEE_LEG] = create_socket_info(values, 17);
82fe2bff
 
 			dlg->sflags = (unsigned int)VAL_INT(values+18);
 
48c3a21a
 			GET_STR_VALUE(toroute_name, values, 19, 0, 0);
 			dlg_set_toroute(dlg, &toroute_name);
 
82fe2bff
 			GET_STR_VALUE(xdata, values, 21, 0, 0);
d05dfc20
 			if(xdata.len > 0 && xdata.s!=NULL && dlg->state!=DLG_STATE_DELETED)
82fe2bff
 			{
 				srjson_InitDoc(&jdoc, NULL);
 				jdoc.buf = xdata;
 				dlg_json_to_profiles(dlg, &jdoc);
 				srjson_DestroyDoc(&jdoc);
 			}
 			dlg->iflags = (unsigned int)VAL_INT(values+22);
a821ff61
 			if (dlg->state==DLG_STATE_CONFIRMED)
 				dlg_ka_add(dlg);
9fe51ed4
 
 			if (!dlg->bind_addr[DLG_CALLER_LEG] || !dlg->bind_addr[DLG_CALLEE_LEG]) {
 				/* non-local socket, probably not our dialog */
 				dlg->iflags &= ~DLG_IFLAG_DMQ_SYNC;
 			}
 
23a34813
 			if(dlg->state==DLG_STATE_DELETED) {
 				/* end_ts used for force clean up not stored - set it to now */
 				dlg->end_ts = (unsigned int)time(0);
 			}
29d70f7d
 			/*restore the timer values */
 			if (0 != insert_dlg_timer( &(dlg->tl), (int)dlg->tl.timeout )) {
 				LM_CRIT("Unable to insert dlg %p [%u:%u] "
 					"with clid '%.*s' and tags '%.*s' '%.*s'\n",
 					dlg, dlg->h_entry, dlg->h_id,
 					dlg->callid.len, dlg->callid.s,
 					dlg->tag[DLG_CALLER_LEG].len, dlg->tag[DLG_CALLER_LEG].s,
 					dlg->tag[DLG_CALLEE_LEG].len, dlg->tag[DLG_CALLEE_LEG].s);
5bdee1ff
 				dlg_unref(dlg,1);
29d70f7d
 				continue;
 			}
5bdee1ff
 			dlg_ref(dlg,1);
82fe2bff
 			LM_DBG("current dialog timeout is %u (%u)\n", dlg->tl.timeout,
 					get_ticks());
b921e350
 
29d70f7d
 			dlg->dflags = 0;
10037f65
   
3d3f5ca1
 			if(mode!=0) {
 				if(loaded_extra<DLG_MAX_DB_LOAD_EXTRA) {
 					dbuid[loaded_extra].h_entry = dlg->h_entry;
 					dbuid[loaded_extra].h_id = dlg->h_id;
 					loaded_extra++;
 				} else {
 					dlg->dflags |= DLG_FLAG_DB_LOAD_EXTRA;
 					loaded_extra_more = 1;
 				}
10037f65
 			  /* if loading at runtime run the callbacks for the loaded dialog */
 			  run_dlg_load_callbacks(dlg);
3d3f5ca1
 			}
b921e350
 			next_dialog:
68c8b77d
 			;
b921e350
 		}
 
 		/* any more data to be fetched ?*/
68c8b77d
 		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) {
b921e350
 				LM_ERR("re-fetching rows failed\n");
68c8b77d
 				goto error;
 			}
b921e350
 			nr_rows = RES_ROW_N(res);
29d70f7d
 			rows = RES_ROWS(res);
68c8b77d
 		} else {
 			nr_rows = 0;
 		}
b921e350
 
5362d0f7
 	} while (nr_rows>0);
b921e350
 
3d3f5ca1
 	if(mode!=0) {
 		for(i=0; i<loaded_extra; i++) {
 			load_dialog_vars_from_db(fetch_num_rows, 1, &dbuid[i]);
 		}
0f2f7611
 		if(loaded_extra_more) {
 			/* more dialogs loaded - scan hash table */
 			for(i=0; i<d_table->size; i++) {
 				dlg_lock(d_table, &d_table->entries[i]);
 				dlg = d_table->entries[i].first;
 				while (dlg) {
 					if(dlg->dflags & DLG_FLAG_DB_LOAD_EXTRA) {
 						dbuid[0].h_entry = dlg->h_entry;
 						dbuid[0].h_id = dlg->h_id;
 						load_dialog_vars_from_db(fetch_num_rows, 1, &dbuid[0]);
 						dlg->dflags &= ~DLG_FLAG_DB_LOAD_EXTRA;
 					}
 					dlg = dlg->next;
 				}
 				dlg_unlock(d_table, &d_table->entries[i]);
 			}
 		}
3d3f5ca1
 		goto end;
 	}
 
76fc55be
 	if (dlg_db_mode==DB_MODE_SHUTDOWN) {
 		if (dialog_dbf.delete(dialog_db_handle, 0, 0, 0, 0) < 0) {
 			LM_ERR("failed to clear dialog table\n");
 			goto error;
 		}
 	}
 
f7ed7d28
 end:
a70300b1
 	dialog_dbf.free_result(dialog_db_handle, res);
 	return 0;
 error:
5362d0f7
 	if(res!=NULL) dialog_dbf.free_result(dialog_db_handle, res);
a70300b1
 	return -1;
 
 }
 
3d3f5ca1
 static int load_dialog_vars_from_db(int fetch_num_rows, int mode,
 		dlg_iuid_t *mval)
60d9c557
 {
 	db_key_t query_cols[DIALOG_VARS_TABLE_COL_NO] = {	&vars_h_entry_column,
 			&vars_h_id_column,	&vars_key_column,	&vars_value_column };
3d3f5ca1
 	db_key_t match_cols[2] = { &vars_h_entry_column, &vars_h_id_column};
 	db_val_t match_vals[2];
 	int match_cols_no = 0;
5362d0f7
 	db1_res_t * res;
 	db_val_t * values;
 	db_row_t * rows;
 	struct dlg_cell  * dlg;
 	int i, nr_rows;
 
60d9c557
 	if(use_dialog_vars_table() != 0){
 		return -1;
 	}
 
3d3f5ca1
 	if(mode==1 && mval!=NULL) {
 		VAL_TYPE(match_vals) = VAL_TYPE(match_vals+1) = DB1_INT;
 		VAL_NULL(match_vals) = VAL_NULL(match_vals+1) = 0;
 		VAL_INT(match_vals) = mval->h_entry;
 		VAL_INT(match_vals+1) = mval->h_id;
 		match_cols_no = 2;
 	}
 
5362d0f7
 	res = 0;
 	/* select the whole table and all the columns */
60d9c557
 	if (DB_CAPABILITY(dialog_dbf, DB_CAP_FETCH) && (fetch_num_rows > 0)) {
3d3f5ca1
 		if(dialog_dbf.query(dialog_db_handle, match_cols, 0, match_vals,
 				query_cols, match_cols_no, DIALOG_VARS_TABLE_COL_NO, 0, 0)
 				< 0) {
60d9c557
 			LM_ERR("Error while querying (fetch) database\n");
5362d0f7
 			goto error;
60d9c557
 		}
5362d0f7
 		if(dialog_dbf.fetch_result(dialog_db_handle, &res, fetch_num_rows) < 0) {
60d9c557
 			LM_ERR("fetching rows failed\n");
5362d0f7
 			goto error;
60d9c557
 		}
 	} else {
3d3f5ca1
 		if(dialog_dbf.query(dialog_db_handle, match_cols, 0, match_vals,
 					query_cols, match_cols_no, DIALOG_VARS_TABLE_COL_NO, 0,
 					&res) < 0) {
60d9c557
 			LM_ERR("Error while querying database\n");
5362d0f7
 			goto error;
60d9c557
 		}
 	}
 
 	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) {
3d3f5ca1
 				if(mode==1 && mval!=NULL) {
 					dlg_lock(d_table, &(d_table->entries[VAL_INT(values)]));
 				}
60d9c557
 				dlg = (d_table->entries)[VAL_INT(values)].first;
 				while (dlg) {
 					if (dlg->h_id == VAL_INT(values+1)) {
ae76e218
 						str key = { VAL_STR(values+2).s, strlen(VAL_STRING(values+2)) };
 						str value = { VAL_STR(values+3).s, strlen(VAL_STRING(values+3)) };
 						set_dlg_variable_unsafe(dlg, &key, &value);
34fc9722
 						break;
60d9c557
 					}
 					dlg = dlg->next;
 					if (!dlg) {
df6152fd
 						LM_WARN("inconsistent data: the dialog h_entry/h_id does not exist!\n");
60d9c557
 					}
 				}
3d3f5ca1
 				if(mode==1 && mval!=NULL) {
 					dlg_unlock(d_table, &(d_table->entries[VAL_INT(values)]));
 				}
60d9c557
 			} else {
df6152fd
 				LM_WARN("inconsistent data: the h_entry in the DB does not exist!\n");
60d9c557
 			}
 		}
 
 		/* 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);
 
3d3f5ca1
 	if(mode!=0) {
 		goto end;
 	}
 
76fc55be
 	if (dlg_db_mode==DB_MODE_SHUTDOWN) {
 		if (dialog_dbf.delete(dialog_db_handle, 0, 0, 0, 0) < 0) {
 			LM_ERR("failed to clear dialog variable table\n");
 			goto error;
 		}
 	}
 
f7ed7d28
 end:
60d9c557
 	dialog_dbf.free_result(dialog_db_handle, res);
 	return 0;
 error:
5362d0f7
 	if(res!=0) dialog_dbf.free_result(dialog_db_handle, res);
60d9c557
 	return -1;
 
 }
 
b9bb99d2
 /*this is only called from destroy_dlg, where the cell's entry lock is acquired*/
a70300b1
 int remove_dialog_from_db(struct dlg_cell * cell)
 {
 	db_val_t values[2];
e2cf6343
 	db_key_t match_keys[2] = { &h_entry_column, &h_id_column};
60d9c557
 	db_key_t vars_match_keys[2] = { &vars_h_entry_column, &vars_h_id_column};
a70300b1
 
 	/*if the dialog hasn 't been yet inserted in the database*/
0b8226f9
 	LM_DBG("trying to remove dialog [%.*s], update_flag is %i\n",
 			cell->callid.len, cell->callid.s,
 			cell->dflags);
29d70f7d
 	if (cell->dflags & DLG_FLAG_NEW) 
a70300b1
 		return 0;
 
 	if (use_dialog_table()!=0)
 		return -1;
 
afda814b
 	VAL_TYPE(values) = VAL_TYPE(values+1) = DB1_INT;
a70300b1
 	VAL_NULL(values) = VAL_NULL(values+1) = 0;
 
 	VAL_INT(values) 	= cell->h_entry;
 	VAL_INT(values+1) 	= cell->h_id;
 
 	if(dialog_dbf.delete(dialog_db_handle, match_keys, 0, values, 2) < 0) {
b9bb99d2
 		LM_ERR("failed to delete database information\n");
a70300b1
 		return -1;
 	}
 
60d9c557
 	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;
 	}
 
b9bb99d2
 	LM_DBG("callid was %.*s\n", cell->callid.len, cell->callid.s );
a70300b1
 
 	return 0;
 }
 
 
60d9c557
 int update_dialog_vars_dbinfo(struct dlg_cell * cell, struct dlg_var * var)
 {
 	db_val_t values[DIALOG_VARS_TABLE_COL_NO];
a70300b1
 
60d9c557
 	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)
a70300b1
 {
 	int i;
60d9c557
 	struct dlg_var *var;
82fe2bff
 	srjson_doc_t jdoc;
b6428ca8
 	str sempty = str_init("");
60d9c557
 
a70300b1
 	db_val_t values[DIALOG_TABLE_COL_NO];
 
de07fe4e
 	db_key_t insert_keys[DIALOG_TABLE_COL_NO] = { &h_entry_column, /*0*/
 			&h_id_column, /*1*/        &call_id_column,  /*2*/      &from_uri_column, /*3*/
 			&from_tag_column, /*4*/    &to_uri_column,  /*5*/       &to_tag_column, /*6*/
 			&from_sock_column, /*7*/   &to_sock_column,  /*8*/
 			&start_time_column, /*9*/  &state_column,  /*10*/       &timeout_column, /*11*/
 			&from_cseq_column, /*12*/  &to_cseq_column,  /*13*/     &from_contact_column, /*14*/
 			&to_contact_column, /*15*/ &from_route_column, /*16*/   &to_route_column, /*17*/
 			&sflags_column, /*18*/     &toroute_name_column, /*19*/ &req_uri_column, /*20*/
 			&xdata_column, /*21*/      &iflags_column  /*22*/ };
a70300b1
 
fe2dfebf
 	if(cell->state<DLG_STATE_EARLY || cell->state==DLG_STATE_DELETED) {
6620fe7b
 		LM_DBG("not storing dlg in db during initial or deleted states\n");
cecdd081
 		return 0;
 	}
 
2962852b
 	i = 0;
60d9c557
 	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;
2962852b
 			i++;
60d9c557
 		}
 		/* Remove the flag */
 		cell->dflags &= ~DLG_FLAG_CHANGED_VARS;
2962852b
 		LM_DBG("updated %d vars for dlg [%d:%d]\n", i, cell->h_entry, cell->h_id);
60d9c557
 	}
 
a70300b1
 	if(use_dialog_table()!=0)
 		return -1;
62c98ed1
 	
82fe2bff
 	srjson_InitDoc(&jdoc, NULL);
 
29d70f7d
 	if((cell->dflags & DLG_FLAG_NEW) != 0){
a70300b1
 		/* save all the current dialogs information*/
62c98ed1
 		VAL_TYPE(values) = VAL_TYPE(values+1) = VAL_TYPE(values+9) = 
afda814b
 		VAL_TYPE(values+10) = VAL_TYPE(values+11) = DB1_INT;
a70300b1
 
 		VAL_TYPE(values+2) = VAL_TYPE(values+3) = VAL_TYPE(values+4) = 
62c98ed1
 		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)=
8c1b49d5
 		VAL_TYPE(values+17) = VAL_TYPE(values+20) = DB1_STR;
a70300b1
 
023366d8
 		SET_NULL_FLAG(values, i, DIALOG_TABLE_COL_NO-6, 0);
48c3a21a
 		VAL_TYPE(values+18) = DB1_INT;
 		VAL_TYPE(values+19) = DB1_STR;
82fe2bff
 		VAL_TYPE(values+21) = DB1_STR;
 		VAL_TYPE(values+22) = DB1_INT;
a70300b1
 
 		VAL_INT(values)			= cell->h_entry;
 		VAL_INT(values+1)		= cell->h_id;
62c98ed1
 		VAL_INT(values+9)		= cell->start_ts;
 		VAL_INT(values+10)		= cell->state;
ed0b9bd5
 		VAL_INT(values+11)		= (unsigned int)( (unsigned int)time(0) +
 				 cell->tl.timeout - get_ticks() );
a70300b1
 
 		SET_STR_VALUE(values+2, cell->callid);
 		SET_STR_VALUE(values+3, cell->from_uri);
62c98ed1
 		SET_STR_VALUE(values+4, cell->tag[DLG_CALLER_LEG]);
a70300b1
 		SET_STR_VALUE(values+5, cell->to_uri);
62c98ed1
 		SET_STR_VALUE(values+6, cell->tag[DLG_CALLEE_LEG]);
 		SET_PROPER_NULL_FLAG(cell->tag[DLG_CALLEE_LEG], values, 6);
 
 
b6428ca8
 		if(cell->bind_addr[DLG_CALLER_LEG]) {
 			LM_DBG("caller sock_info is %.*s\n",
 				cell->bind_addr[DLG_CALLER_LEG]->sock_str.len,
 				cell->bind_addr[DLG_CALLER_LEG]->sock_str.s);
 			SET_STR_VALUE(values+7, cell->bind_addr[DLG_CALLER_LEG]->sock_str);
 		} else {
 			LM_DBG("no caller sock_info\n");
 			SET_STR_VALUE(values+7, sempty);
 		}
 		if(cell->bind_addr[DLG_CALLEE_LEG]) {
 			LM_DBG("callee sock_info is %.*s\n",
 				cell->bind_addr[DLG_CALLEE_LEG]->sock_str.len,
 				cell->bind_addr[DLG_CALLEE_LEG]->sock_str.s);
 			SET_STR_VALUE(values+8, cell->bind_addr[DLG_CALLEE_LEG]->sock_str);
 		} else {
 			LM_DBG("no callee sock_info\n");
 			SET_STR_VALUE(values+8, sempty);
 		}
62c98ed1
 
 		SET_STR_VALUE(values+12, cell->cseq[DLG_CALLER_LEG]);
 		SET_STR_VALUE(values+13, cell->cseq[DLG_CALLEE_LEG]);
de07fe4e
 		SET_STR_VALUE(values+14, cell->contact[DLG_CALLER_LEG]);
 		SET_STR_VALUE(values+15, cell->contact[DLG_CALLEE_LEG]);
 		SET_STR_VALUE(values+16, cell->route_set[DLG_CALLER_LEG]);
 		SET_STR_VALUE(values+17, cell->route_set[DLG_CALLEE_LEG]);
62c98ed1
 
ad72150a
 		SET_PROPER_NULL_FLAG(cell->contact[DLG_CALLER_LEG], 	values, 14);
 		SET_PROPER_NULL_FLAG(cell->contact[DLG_CALLEE_LEG], 	values, 15);
 		SET_PROPER_NULL_FLAG(cell->route_set[DLG_CALLER_LEG], 	values, 16);
 		SET_PROPER_NULL_FLAG(cell->route_set[DLG_CALLEE_LEG], 	values, 17);
62c98ed1
 
5d131fe7
 		VAL_NULL(values+18) = 0;
 		VAL_INT(values+18)  = cell->sflags;
48c3a21a
 
 		SET_STR_VALUE(values+19, cell->toroute_name);
 		SET_PROPER_NULL_FLAG(cell->toroute_name, values, 19);
8c1b49d5
 		SET_STR_VALUE(values+20, cell->req_uri);
 		SET_PROPER_NULL_FLAG(cell->req_uri, 	values, 20);
a70300b1
 
82fe2bff
 		dlg_profiles_to_json(cell, &jdoc);
 		if(jdoc.buf.s!=NULL)
 		{
 			SET_STR_VALUE(values+21, jdoc.buf);
 			SET_PROPER_NULL_FLAG(jdoc.buf, values, 21);
 		} else {
 			VAL_NULL(values+21) = 1;
 		}
 
 		VAL_NULL(values+22) = 0;
 		VAL_INT(values+22)  = cell->iflags;
 
a70300b1
 		if((dialog_dbf.insert(dialog_db_handle, insert_keys, values, 
 								DIALOG_TABLE_COL_NO)) !=0){
b9bb99d2
 			LM_ERR("could not add another dialog to db\n");
a70300b1
 			goto error;
 		}
29d70f7d
 		cell->dflags &= ~(DLG_FLAG_NEW|DLG_FLAG_CHANGED);
62c98ed1
 		
29d70f7d
 	} else if((cell->dflags & DLG_FLAG_CHANGED) != 0) {
a70300b1
 		/* save only dialog's state and timeout */
62c98ed1
 		VAL_TYPE(values) = VAL_TYPE(values+1) = 
afda814b
 		VAL_TYPE(values+10) = VAL_TYPE(values+11) = DB1_INT;
62c98ed1
 
afda814b
 		VAL_TYPE(values+12) = VAL_TYPE(values+13) =DB1_STR;
de07fe4e
 		VAL_TYPE(values+14) = VAL_TYPE(values+15) =DB1_STR;
a70300b1
 
d1828714
 		VAL_INT(values)			= cell->h_entry;
 		VAL_INT(values+1)		= cell->h_id;
62c98ed1
 		VAL_INT(values+10)		= cell->state;
ed0b9bd5
 		VAL_INT(values+11)		= (unsigned int)( (unsigned int)time(0) +
 				 cell->tl.timeout - get_ticks() );
 
62c98ed1
 		SET_STR_VALUE(values+12, cell->cseq[DLG_CALLER_LEG]);
 		SET_STR_VALUE(values+13, cell->cseq[DLG_CALLEE_LEG]);
de07fe4e
 		SET_STR_VALUE(values+14, cell->contact[DLG_CALLER_LEG]);
 		SET_STR_VALUE(values+15, cell->contact[DLG_CALLEE_LEG]);
62c98ed1
 
a70300b1
 
62c98ed1
 		VAL_NULL(values) = VAL_NULL(values+1) = 
 		VAL_NULL(values+10) = VAL_NULL(values+11) = 
de07fe4e
 		VAL_NULL(values+12) = VAL_NULL(values+13) = 
 		VAL_NULL(values+14) = VAL_NULL(values+15) = 0;
a70300b1
 
 		if((dialog_dbf.update(dialog_db_handle, (insert_keys), 0, 
de07fe4e
 						(values), (insert_keys+10), (values+10), 2, 6)) !=0){
b9bb99d2
 			LM_ERR("could not update database info\n");
a70300b1
 			goto error;
 		}
29d70f7d
 		cell->dflags &= ~(DLG_FLAG_CHANGED);
a70300b1
 	} else {
 		return 0;
 	}
 
82fe2bff
 	if(jdoc.buf.s!=NULL) {
 		jdoc.free_fn(jdoc.buf.s);
 		jdoc.buf.s = NULL;
 	}
 	srjson_DestroyDoc(&jdoc);
 
a70300b1
 	return 0;
82fe2bff
 
a70300b1
 error:
82fe2bff
 	if(jdoc.buf.s!=NULL) {
 		jdoc.free_fn(jdoc.buf.s);
 		jdoc.buf.s = NULL;
 	}
 	srjson_DestroyDoc(&jdoc);
a70300b1
 	return -1;
 }
 
60d9c557
 int update_dialog_dbinfo(struct dlg_cell * cell)
 {
 	/* lock the entry */
2d9c7f96
 	dlg_lock(d_table, &d_table->entries[cell->h_entry]);
60d9c557
 	if (update_dialog_dbinfo_unsafe(cell) != 0) {
2d9c7f96
 		dlg_unlock(d_table, &d_table->entries[cell->h_entry]);
60d9c557
 		return -1;
 	} 
2d9c7f96
 	dlg_unlock(d_table, &d_table->entries[cell->h_entry]);
60d9c557
 	return 0;
 }
a70300b1
 
b9bb99d2
 void dialog_update_db(unsigned int ticks, void * param)
a70300b1
 {
2d9c7f96
 	int i;
 	struct dlg_cell *cell;
a70300b1
 
b9bb99d2
 	LM_DBG("saving current_info \n");
a70300b1
 
2d9c7f96
 	for(i = 0; i < d_table->size; i++){
 		/* lock the slot */
 		dlg_lock(d_table, &d_table->entries[i]);
 		for(cell = d_table->entries[i].first; cell != NULL; cell = cell->next){
 			/* if update fails for one dlg, still do it for the next ones */
 			update_dialog_dbinfo_unsafe(cell);
 		}
 		dlg_unlock(d_table, &d_table->entries[i]);
a70300b1
 	}
 	return;
 }