/*
* $Id: avp_db.c,v 1.2.2.1 2005/04/20 19:56:26 bogdan Exp $
*
* Copyright (C) 2004 FhG Fokus
*
* This file is part of ser, a free SIP server.
*
* ser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* For a license to use the ser software under conditions
* other than those described here, or to purchase support for this
* software, please contact iptel.org by e-mail at the following addresses:
* info@iptel.org
*
* ser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include "../../sr_module.h"
#include "../../mem/mem.h"
#include "../../parser/msg_parser.h"
#include "../../parser/hf.h"
#include "../../parser/parse_from.h"
#include "../../parser/parse_to.h"
#include "../../parser/parse_uri.h"
#include "../../str.h"
#include "../../db/db.h"
#include "../../config.h"
#include "../../usr_avp.h"
MODULE_VERSION
#define CALLER_PREFIX "caller_"
#define CALLER_PREFIX_LEN (sizeof(CALLER_PREFIX) - 1)
#define CALLEE_PREFIX "callee_"
#define CALLEE_PREFIX_LEN (sizeof(CALLEE_PREFIX) - 1)
typedef enum load_avp_param {
LOAD_CALLER_UUID, /* Use the caller's UUID as the key */
LOAD_CALLEE_UUID, /* Use the callee's UUID as the key */
LOAD_CALLER, /* Use the caller's username and domain as the key */
LOAD_CALLEE /* Use the callee's username and domain as the key */
} load_avp_param_t;
static char* db_url = DEFAULT_RODB_URL; /* Database URL */
static char* db_table = "usr_preferences";
static char* uuid_column = "uuid";
static char* username_column = "username";
static char* domain_column = "domain";
static char* attr_column = "attribute";
static char* val_column = "value";
static str caller_prefix = {CALLER_PREFIX, CALLER_PREFIX_LEN};
static str callee_prefix = {CALLEE_PREFIX, CALLEE_PREFIX_LEN};
static int caller_uuid_avp = 1;
static int callee_uuid_avp = 2;
static int use_domain = 0;
static db_con_t* db_handle;
static db_func_t dbf;
static int load_avp(struct sip_msg*, char*, char*);
static int mod_init(void);
static int child_init(int);
static int load_avp_fixup(void**, int);
/*
* Exported functions
*/
static cmd_export_t cmds[] = {
{"avp_load", load_avp, 1, load_avp_fixup, REQUEST_ROUTE | FAILURE_ROUTE},
{0, 0, 0, 0, 0}
};
/*
* Exported parameters
*/
static param_export_t params[] = {
{"caller_uuid_avp", INT_PARAM, &caller_uuid_avp},
{"callee_uuid_avp", INT_PARAM, &callee_uuid_avp},
{"db_url", STR_PARAM, &db_url },
{"pref_table", STR_PARAM, &db_table },
{"uuid_column", STR_PARAM, &uuid_column },
{"username_column", STR_PARAM, &username_column},
{"domain_column", STR_PARAM, &domain_column },
{"attr_column", STR_PARAM, &attr_column },
{"val_column", INT_PARAM, &val_column },
{"use_domain", INT_PARAM, &use_domain },
{0, 0, 0}
};
struct module_exports exports = {
"avp_db",
cmds, /* Exported commands */
params, /* Exported parameters */
mod_init, /* module initialization function */
0, /* response function*/
0, /* destroy function */
0, /* oncancel function */
child_init /* per-child init function */
};
static int mod_init(void)
{
DBG("avp_db - initializing\n");
if (bind_dbmod(db_url, &dbf) < 0) {
LOG(L_ERR, "avpdb_mod_init: Unable to bind a database driver\n");
return -1;
}
if (!DB_CAPABILITY(dbf, DB_CAP_QUERY)) {
LOG(L_ERR, "avpdb_mod_init: Selected database driver does not suppor the query capability\n");
return -1;
}
caller_prefix.len = strlen(caller_prefix.s);
callee_prefix.len = strlen(callee_prefix.s);
return 0;
}
static int child_init(int rank)
{
DBG("avp_db - Initializing child %i\n", rank);
db_handle = dbf.init(db_url);
if (!db_handle) {
LOG(L_ERR, "avpdb_init_child: could not initialize connection to %s\n", db_url);
return -1;
}
return 0;
}
static int query_db(str* prefix, str* uuid, str* username, str* domain)
{
int name_len, err = -1;
db_key_t cols[2], keys[2];
db_val_t vals[2];
db_res_t* res;
db_row_t* cur_row;
int_str name, val;
str name_str, val_str;
cols[0] = attr_column;
cols[1] = val_column;
if (uuid) {
keys[0] = uuid_column;
VAL_TYPE(vals) = DB_STR;
VAL_NULL(vals) = 0;
VAL_STR(vals) = *uuid;
} else {
keys[0] = username_column;
VAL_TYPE(vals) = DB_STR;
VAL_NULL(vals) = 0;
VAL_STR(vals) = *username;
}
if (use_domain) {
keys[1] = domain_column;
VAL_TYPE(vals + 1) = DB_STR;
VAL_NULL(vals + 1) = 0;
VAL_STR(vals + 1) = *domain;
}
if (dbf.use_table(db_handle, db_table) < 0) {
LOG(L_ERR, "query_db: Unable to change the table\n");
}
err = dbf.query(db_handle, keys, 0, vals, cols, (use_domain ? 2 : 1), 2, 0, &res);
if (err) {
LOG(L_ERR,"query_db: db_query failed.");
return -1;
}
name.s = &name_str;
val.s = &val_str;
for (cur_row = res->rows; cur_row < res->rows + res->n; cur_row++) {
if (VAL_NULL(ROW_VALUES(cur_row)) || VAL_NULL(ROW_VALUES(cur_row) + 1)) {
continue;
}
name_len = strlen((char*)VAL_STRING(ROW_VALUES(cur_row)));
name_str.len = prefix->len + name_len;
name_str.s = pkg_malloc(name_str.len);
if (name_str.s == 0) {
LOG(L_ERR, "query_db: Out of memory");
dbf.free_result(db_handle, res);
return -1;
}
memcpy(name_str.s, prefix->s, prefix->len);
memcpy(name_str.s + prefix->len,
(char*)VAL_STRING(ROW_VALUES(cur_row)), name_len);
val_str.len = strlen((char*)VAL_STRING(ROW_VALUES(cur_row) + 1));
val_str.s = (char*)VAL_STRING(ROW_VALUES(cur_row) + 1);
err = add_avp(AVP_NAME_STR | AVP_VAL_STR, name, val);
if (err != 0) {
LOG(L_ERR, "query_db: add_avp failed\n");
pkg_free(name_str.s);
dbf.free_result(db_handle, res);
}
DBG("query_db: AVP '%.*s'='%.*s' has been added\n", name_str.len,
name_str.s,
val_str.len,
val_str.s);
}
dbf.free_result(db_handle, res);
return 1;
}
static int load_avp_uuid(struct sip_msg* msg, str* prefix, int avp_id)
{
struct usr_avp *uuid;
int_str attr_istr, val_istr;
attr_istr.n = avp_id;
uuid = search_first_avp(AVP_NAME_STR, attr_istr, &val_istr);
if (!uuid) {
LOG(L_ERR, "load_avp_uuid: no AVP with id %d was found\n", avp_id);
return -1;
}
if (!(uuid->flags & AVP_VAL_STR)) {
LOG(L_ERR, "load_avp_uuid: value for <%d> should "
"be of type string\n", avp_id);
return -1;
}
return query_db(prefix, val_istr.s, 0, 0);
}
static int load_avp_user(struct sip_msg* msg, str* prefix, load_avp_param_t param)
{
str* uri;
struct sip_uri puri;
if (param == LOAD_CALLER) {
if (parse_from_header(msg) < 0) {
LOG(L_ERR, "load_avp_user: Error while parsing From header field\n");
return -1;
}
uri = &get_from(msg)->uri;
if (parse_uri(uri->s, uri->len, &puri) == -1) {
LOG(L_ERR, "load_avp_user: Error while parsing From URI\n");
return -1;
}
return query_db(prefix, 0, &puri.user, &puri.host);
} else if (param == LOAD_CALLEE) {
if (parse_sip_msg_uri(msg) < 0) {
LOG(L_ERR, "load_avp_user: Request-URI parsing failed\n");
return -1;
}
if (msg->parsed_uri_ok != 1) {
LOG(L_ERR, "load_avp_user: Unable to parse Request-URI\n");
return -1;
}
return query_db(prefix, 0, &msg->parsed_uri.user, &msg->parsed_uri.host);
} else {
LOG(L_ERR, "load_avp_user: Unknown header field type\n");
return -1;
}
}
static int load_avp(struct sip_msg* msg, char* attr, char* _dummy)
{
switch((load_avp_param_t)attr) {
case LOAD_CALLER_UUID:
return load_avp_uuid(msg, &caller_prefix, caller_uuid_avp);
break;
case LOAD_CALLEE_UUID:
return load_avp_uuid(msg, &callee_prefix, callee_uuid_avp);
break;
case LOAD_CALLER:
return load_avp_user(msg, &caller_prefix, LOAD_CALLER);
break;
case LOAD_CALLEE:
return load_avp_user(msg, &callee_prefix, LOAD_CALLEE);
break;
default:
LOG(L_ERR, "load_avp: Unknown parameter value\n");
return -1;
}
}
static int load_avp_fixup(void** param, int param_no)
{
long id = 0;
if (param_no == 1) {
if (!strcasecmp(*param, "caller_uuid")) {
id = LOAD_CALLER_UUID;
} else if (!strcasecmp(*param, "callee_uuid")) {
id = LOAD_CALLEE_UUID;
} else if (!strcasecmp(*param, "caller")) {
id = LOAD_CALLER;
} else if (!strcasecmp(*param, "callee")) {
id = LOAD_CALLEE;
} else {
LOG(L_ERR, "load_avp_fixup: Unknown parameter\n");
return -1;
}
}
pkg_free(*param);
*param=(void*)id;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1