You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kamailio/modules/dialog/dlg_profile.c

1380 lines
33 KiB

/*
* Copyright (C) 2008 Voice System SRL
*
* This file is part of Kamailio, a free SIP server.
*
* Kamailio 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
*
* Kamailio 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/*!
* \file
* \brief Profile related functions for the dialog module
* \ingroup dialog
* Module: \ref dialog
*/
#include "../../mem/shm_mem.h"
#include "../../hashes.h"
#include "../../trim.h"
#include "../../dprint.h"
#include "../../ut.h"
#include "../../route.h"
#include "../../modules/tm/tm_load.h"
#include "../../lib/srutils/sruid.h"
#include "dlg_hash.h"
#include "dlg_var.h"
#include "dlg_handlers.h"
#include "dlg_profile.h"
/*! size of dialog profile hash */
#define PROFILE_HASH_SIZE 16
/*! tm bindings */
extern struct tm_binds d_tmb;
/*! global dialog message id */
static unsigned int current_dlg_msg_id = 0 ;
static unsigned int current_dlg_msg_pid = 0 ;
/*! pending dialog links */
static dlg_profile_link_t *current_pending_linkers = NULL;
/*! global dialog profile list */
static dlg_profile_table_t *profiles = NULL;
static dlg_profile_table_t* new_dlg_profile( str *name,
unsigned int size, unsigned int has_value);
extern int update_dlg_timeout(dlg_cell_t *, int);
static sruid_t _dlg_profile_sruid;
/*!
* \brief Add profile definitions to the global list
* \see new_dlg_profile
* \param profiles profile name
* \param has_value set to 0 for a profile without value, otherwise it has a value
* \return 0 on success, -1 on failure
*/
int add_profile_definitions( char* profiles, unsigned int has_value)
{
char *p;
char *d;
str name;
unsigned int i;
if (profiles==NULL || strlen(profiles)==0 )
return 0;
p = profiles;
do {
/* locate name of profile */
name.s = p;
d = strchr( p, ';');
if (d) {
name.len = d-p;
d++;
} else {
name.len = strlen(p);
}
/* we have the name -> trim it for spaces */
trim_spaces_lr( name );
/* check len name */
if (name.len==0)
/* ignore */
continue;
/* check the name format */
for(i=0;i<name.len;i++) {
if ( !isalnum(name.s[i]) && name.s[i] != '_' ) {
LM_ERR("bad profile name <%.*s>, char %c - use only "
"alphanumerical characters or '_'\n", name.len,name.s,name.s[i]);
return -1;
}
}
/* name ok -> create the profile */
LM_DBG("creating profile <%.*s>\n",name.len,name.s);
if (new_dlg_profile( &name, PROFILE_HASH_SIZE, has_value)==NULL) {
LM_ERR("failed to create new profile <%.*s>\n",name.len,name.s);
return -1;
}
}while( (p=d)!=NULL );
return 0;
}
/*!
* \brief Search a dialog profile in the global list
* \note Linear search, this won't have the best performance for huge profile lists
* \param name searched dialog profile
* \return pointer to the profile on success, NULL otherwise
*/
struct dlg_profile_table* search_dlg_profile(str *name)
{
struct dlg_profile_table *profile;
for( profile=profiles ; profile ; profile=profile->next ) {
if (name->len==profile->name.len &&
memcmp(name->s,profile->name.s,name->len)==0 )
return profile;
}
return NULL;
}
/*!
* \brief Creates a new dialog profile
* \see add_profile_definitions
* \param name profile name
* \param size profile size
* \param has_value set to 0 for a profile without value, otherwise it has a value
* \return pointer to the created dialog on success, NULL otherwise
*/
static struct dlg_profile_table* new_dlg_profile( str *name, unsigned int size,
unsigned int has_value)
{
struct dlg_profile_table *profile;
struct dlg_profile_table *ptmp;
unsigned int len;
unsigned int i;
if ( name->s==NULL || name->len==0 || size==0 ) {
LM_ERR("invalid parameters\n");
return NULL;
}
for( len=0,i=0 ; i<8*sizeof(size) ; i++ ) {
if ( size & (1<<i) ) len++;
}
if (len!=1) {
LM_ERR(" size %u is not power of 2!\n", size);
return NULL;
}
profile = search_dlg_profile(name);
if (profile!=NULL) {
LM_ERR("duplicate dialog profile registered <%.*s>\n",
name->len, name->s);
return NULL;
}
len = sizeof(struct dlg_profile_table) +
size*sizeof(struct dlg_profile_entry) +
name->len + 1;
profile = (struct dlg_profile_table *)shm_malloc(len);
if (profile==NULL) {
LM_ERR("no more shm mem\n");
return NULL;
}
memset( profile , 0 , len);
profile->size = size;
profile->has_value = (has_value==0)?0:1;
/* init lock */
if (lock_init( &profile->lock )==NULL) {
LM_ERR("failed to init lock\n");
shm_free(profile);
return NULL;
}
/* set inner pointers */
profile->entries = (struct dlg_profile_entry*)(profile + 1);
profile->name.s = ((char*)profile->entries) +
size*sizeof(struct dlg_profile_entry);
/* copy the name of the profile */
memcpy( profile->name.s, name->s, name->len );
profile->name.len = name->len;
profile->name.s[profile->name.len] = 0;
/* link profile */
for( ptmp=profiles ; ptmp && ptmp->next; ptmp=ptmp->next );
if (ptmp==NULL) {
profiles = profile;
sruid_init(&_dlg_profile_sruid, '-', "dlgp", SRUID_INC);
} else {
ptmp->next = profile;
}
return profile;
}
/*!
* \brief Destroy a dialog profile list
* \param profile dialog profile
*/
static void destroy_dlg_profile(struct dlg_profile_table *profile)
{
if (profile==NULL)
return;
lock_destroy( &profile->lock );
shm_free( profile );
return;
}
/*!
* \brief Destroy the global dialog profile list
*/
void destroy_dlg_profiles(void)
{
struct dlg_profile_table *profile;
while(profiles) {
profile = profiles;
profiles = profiles->next;
destroy_dlg_profile( profile );
}
return;
}
/*!
* \brief Destroy dialog linkers
* \param linker dialog linker
*/
void destroy_linkers(struct dlg_profile_link *linker)
{
struct dlg_profile_entry *p_entry;
struct dlg_profile_link *l;
struct dlg_profile_hash *lh;
while(linker) {
l = linker;
linker = linker->next;
/* unlink from profile table */
if (l->hash_linker.next) {
p_entry = &l->profile->entries[l->hash_linker.hash];
lock_get( &l->profile->lock );
lh = &l->hash_linker;
/* last element on the list? */
if (lh==lh->next) {
p_entry->first = NULL;
} else {
if (p_entry->first==lh)
p_entry->first = lh->next;
lh->next->prev = lh->prev;
lh->prev->next = lh->next;
}
lh->next = lh->prev = NULL;
p_entry->content --;
lock_release( &l->profile->lock );
}
/* free memory */
shm_free(l);
}
}
/*!
* \brief Calculate the hash profile from a dialog
* \see core_hash
* \param value hash source
* \param dlg dialog cell
* \param profile dialog profile table (for hash size)
* \return value hash if the value has a value, hash over dialog otherwise
*/
inline static unsigned int calc_hash_profile(str *value1, str *value2,
dlg_profile_table_t *profile)
{
if (profile->has_value) {
/* do hash over the value1 */
return core_hash( value1, NULL, profile->size);
} else {
/* do hash over the value2 */
if(value2)
return core_hash( value2, NULL, profile->size);
return 0;
}
}
/*!
* \brief Remove remote profile items that are expired
* \param te expiration time
*/
void remove_expired_remote_profiles(time_t te)
{
struct dlg_profile_table *profile;
struct dlg_profile_entry *p_entry;
struct dlg_profile_hash *lh;
struct dlg_profile_hash *kh;
int i;
for( profile=profiles ; profile ; profile=profile->next ) {
if(profile->flags&FLAG_PROFILE_REMOTE) {
for(i=0; i<profile->size; i++) {
/* space for optimization */
lock_get(&profile->lock);
p_entry = &profile->entries[i];
lh = p_entry->first;
while(lh) {
kh = lh->next;
if(lh->dlg==NULL && lh->expires>0 && lh->expires<te) {
/* last element on the list? */
if (lh==lh->next) {
p_entry->first = NULL;
} else {
if (p_entry->first==lh)
p_entry->first = lh->next;
lh->next->prev = lh->prev;
lh->prev->next = lh->next;
}
lh->next = lh->prev = NULL;
if(lh->linker) shm_free(lh->linker);
p_entry->content--;
lock_release(&profile->lock);
return;
}
lh = kh;
}
lock_release(&profile->lock);
}
}
}
}
/*!
* \brief Remove profile
* \param profile pointer to profile
* \param value profile value
* \param puid profile unique id
*/
int remove_profile(dlg_profile_table_t *profile, str *value, str *puid)
{
unsigned int hash;
struct dlg_profile_entry *p_entry;
struct dlg_profile_hash *lh;
hash = calc_hash_profile(value, puid, profile);
lock_get(&profile->lock );
p_entry = &profile->entries[hash];
lh = p_entry->first;
if(lh) {
do {
if(lh->dlg==NULL && lh->puid_len==puid->len
&& lh->value.len==value->len
&& strncmp(lh->puid, puid->s, puid->len)==0
&& strncmp(lh->value.s, value->s, value->len)==0) {
/* last element on the list? */
if (lh==lh->next) {
p_entry->first = NULL;
} else {
if (p_entry->first==lh)
p_entry->first = lh->next;
lh->next->prev = lh->prev;
lh->prev->next = lh->next;
}
lh->next = lh->prev = NULL;
if(lh->linker) shm_free(lh->linker);
p_entry->content--;
lock_release(&profile->lock );
return 1;
}
lh = lh->next;
} while(lh != p_entry->first);
}
lock_release(&profile->lock );
return 0;
}
/*!
* \brief Callback for cleanup of profile local vars
* \param msg SIP message
* \param flags unused
* \param param unused
* \return 1
*/
int cb_profile_reset( struct sip_msg *msg, unsigned int flags, void *param )
{
current_dlg_msg_id = 0;
current_dlg_msg_pid = 0;
if (current_pending_linkers) {
destroy_linkers(current_pending_linkers);
current_pending_linkers = NULL;
}
/* need to return non-zero - 0 will break the exec of the request */
return 1;
}
/*!
* \brief Cleanup a profile
* \param msg SIP message
* \param flags unused
* \param param unused
* \return 1
*/
int profile_cleanup( struct sip_msg *msg, unsigned int flags, void *param )
{
dlg_cell_t *dlg;
if(get_route_type()==LOCAL_ROUTE) {
return 1;
}
current_dlg_msg_id = 0;
current_dlg_msg_pid = 0;
dlg = dlg_get_ctx_dialog();
if (dlg!=NULL) {
if(dlg->dflags & DLG_FLAG_TM) {
dlg_unref(dlg, 1);
} else {
/* dialog didn't make it to tm */
dlg_unref(dlg, 2);
}
}
if (current_pending_linkers) {
destroy_linkers(current_pending_linkers);
current_pending_linkers = NULL;
}
/* need to return non-zero - 0 will break the exec of the request */
return 1;
}
/*!
* \brief Link a dialog profile
* \param linker dialog linker
* \param vkey key for profile hash table
*/
static void link_profile(struct dlg_profile_link *linker, str *vkey)
{
unsigned int hash;
struct dlg_profile_entry *p_entry;
/* calculate the hash position */
hash = calc_hash_profile(&linker->hash_linker.value, vkey, linker->profile);
linker->hash_linker.hash = hash;
/* insert into profile hash table */
p_entry = &linker->profile->entries[hash];
lock_get( &linker->profile->lock );
if (p_entry->first) {
linker->hash_linker.prev = p_entry->first->prev;
linker->hash_linker.next = p_entry->first;
p_entry->first->prev->next = &linker->hash_linker;
p_entry->first->prev = &linker->hash_linker;
} else {
p_entry->first = linker->hash_linker.next
= linker->hash_linker.prev = &linker->hash_linker;
}
p_entry->content ++;
lock_release( &linker->profile->lock );
}
/*!
* \brief Link a dialog profile
* \param linker dialog linker
* \param dlg dialog cell
*/
static void link_dlg_profile(struct dlg_profile_link *linker, struct dlg_cell *dlg)
{
struct dlg_entry *d_entry;
/* add the linker to the dialog */
/* FIXME zero h_id is not 100% for testing if the dialog is inserted
* into the hash table -> we need circular lists -bogdan */
if (dlg->h_id) {
d_entry = &d_table->entries[dlg->h_entry];
dlg_lock( d_table, d_entry);
linker->next = dlg->profile_links;
dlg->profile_links =linker;
linker->hash_linker.dlg = dlg;
dlg_unlock( d_table, d_entry);
} else {
linker->next = dlg->profile_links;
dlg->profile_links =linker;
linker->hash_linker.dlg = dlg;
}
link_profile(linker, &dlg->callid);
}
/*!
* \brief Set the global variables to the current dialog
* \param msg SIP message
* \param dlg dialog cell
*/
void set_current_dialog(sip_msg_t *msg, dlg_cell_t *dlg)
{
struct dlg_profile_link *linker;
struct dlg_profile_link *tlinker;
LM_DBG("setting current dialog [%u:%u]\n", dlg->h_entry, dlg->h_id);
/* if linkers are not from current request, just discard them */
if (msg->id!=current_dlg_msg_id || msg->pid!=current_dlg_msg_pid) {
current_dlg_msg_id = msg->id;
current_dlg_msg_pid = msg->pid;
destroy_linkers(current_pending_linkers);
} else {
/* add the linker, one by one, to the dialog */
linker = current_pending_linkers;
while (linker) {
tlinker = linker;
linker = linker->next;
/* process tlinker */
tlinker->next = NULL;
link_dlg_profile( tlinker, dlg);
}
}
current_pending_linkers = NULL;
}
/*!
* \brief Set a dialog profile
* \param msg SIP message
* \param value value
* \param profile dialog profile table
* \return 0 on success, -1 on failure
*/
int set_dlg_profile(struct sip_msg *msg, str *value, struct dlg_profile_table *profile)
{
dlg_cell_t *dlg = NULL;
dlg_profile_link_t *linker;
/* get current dialog */
dlg = dlg_get_msg_dialog(msg);
if (dlg==NULL && !is_route_type(REQUEST_ROUTE)) {
LM_CRIT("BUG - dialog not found in a non REQUEST route (%d)\n",
REQUEST_ROUTE);
return -1;
}
/* build new linker */
linker = (struct dlg_profile_link*)shm_malloc(
sizeof(struct dlg_profile_link) + (profile->has_value?value->len:0) );
if (linker==NULL) {
LM_ERR("no more shm memory\n");
goto error;
}
memset(linker, 0, sizeof(struct dlg_profile_link));
/* set backpointers to profile and linker (itself) */
linker->profile = profile;
linker->hash_linker.linker = linker;
/* set the value */
if (profile->has_value) {
linker->hash_linker.value.s = (char*)(linker+1);
memcpy( linker->hash_linker.value.s, value->s, value->len);
linker->hash_linker.value.len = value->len;
}
sruid_next_safe(&_dlg_profile_sruid);
strcpy(linker->hash_linker.puid, _dlg_profile_sruid.uid.s);
linker->hash_linker.puid_len = _dlg_profile_sruid.uid.len;
if (dlg!=NULL) {
/* add linker directly to the dialog and profile */
link_dlg_profile( linker, dlg);
} else {
/* if existing linkers are not from current request, just discard them */
if (msg->id!=current_dlg_msg_id || msg->pid!=current_dlg_msg_pid) {
current_dlg_msg_id = msg->id;
current_dlg_msg_pid = msg->pid;
destroy_linkers(current_pending_linkers);
current_pending_linkers = NULL;
}
/* no dialog yet -> set linker as pending */
if (msg->id!=current_dlg_msg_id || msg->pid!=current_dlg_msg_pid) {
current_dlg_msg_id = msg->id;
current_dlg_msg_pid = msg->pid;
destroy_linkers(current_pending_linkers);
}
linker->next = current_pending_linkers;
current_pending_linkers = linker;
}
dlg_release(dlg);
return 0;
error:
dlg_release(dlg);
return -1;
}
/*!
* \brief Add dialog to a profile
* \param dlg dialog
* \param value value
* \param profile dialog profile table
* \return 0 on success, -1 on failure
*/
int dlg_add_profile(dlg_cell_t *dlg, str *value, struct dlg_profile_table *profile,
str *puid, time_t expires, int flags)
{
dlg_profile_link_t *linker;
str vkey;
/* build new linker */
linker = (struct dlg_profile_link*)shm_malloc(
sizeof(struct dlg_profile_link)+(profile->has_value?(value->len+1):0));
if (linker==NULL) {
LM_ERR("no more shm memory\n");
goto error;
}
memset(linker, 0, sizeof(struct dlg_profile_link));
/* set backpointers to profile and linker (itself) */
linker->profile = profile;
linker->hash_linker.linker = linker;
/* set the value */
if (profile->has_value) {
linker->hash_linker.value.s = (char*)(linker+1);
memcpy(linker->hash_linker.value.s, value->s, value->len);
linker->hash_linker.value.len = value->len;
linker->hash_linker.value.s[value->len] = '\0';
}
if(puid && puid->s && puid->len>0) {
if(puid->len<SRUID_SIZE) {
strcpy(linker->hash_linker.puid, puid->s);
linker->hash_linker.puid_len = puid->len;
} else {
LM_ERR("puid size is too large\n");
shm_free(linker);
goto error;
}
} else {
sruid_next_safe(&_dlg_profile_sruid);
if(_dlg_profile_sruid.uid.len<SRUID_SIZE) {
strcpy(linker->hash_linker.puid, _dlg_profile_sruid.uid.s);
linker->hash_linker.puid_len = _dlg_profile_sruid.uid.len;
} else {
LM_ERR("sruid size is too large\n");
shm_free(linker);
goto error;
}
}
linker->hash_linker.expires = expires;
linker->hash_linker.flags = flags;
/* add linker directly to the dialog and profile */
if(dlg!=NULL) {
link_dlg_profile(linker, dlg);
} else {
vkey.s = linker->hash_linker.puid;
vkey.len = linker->hash_linker.puid_len;
profile->flags |= FLAG_PROFILE_REMOTE;
link_profile(linker, &vkey);
}
return 0;
error:
return -1;
}
/*!
* \brief Unset a dialog profile
* \param msg SIP message
* \param value value
* \param profile dialog profile table
* \return 1 on success, -1 on failure
*/
int unset_dlg_profile(sip_msg_t *msg, str *value,
dlg_profile_table_t *profile)
{
dlg_cell_t *dlg;
dlg_profile_link_t *linker;
dlg_profile_link_t *linker_prev;
dlg_entry_t *d_entry;
if (is_route_type(REQUEST_ROUTE)) {
LM_ERR("dialog delete profile cannot be used in request route\n");
return -1;
}
/* get current dialog */
dlg = dlg_get_msg_dialog(msg);
if (dlg==NULL) {
LM_WARN("dialog is NULL for delete profile\n");
return -1;
}
/* check the dialog linkers */
d_entry = &d_table->entries[dlg->h_entry];
dlg_lock( d_table, d_entry);
linker = dlg->profile_links;
linker_prev = NULL;
for( ; linker ; linker_prev=linker,linker=linker->next) {
if (linker->profile==profile) {
if (profile->has_value==0) {
goto found;
} else if (value && value->len==linker->hash_linker.value.len &&
memcmp(value->s,linker->hash_linker.value.s,value->len)==0){
goto found;
}
/* allow further search - maybe the dialog is inserted twice in
* the same profile, but with different values -bogdan
*/
}
}
dlg_unlock( d_table, d_entry);
dlg_release(dlg);
return -1;
found:
/* table still locked */
/* remove the linker element from dialog */
if (linker_prev==NULL) {
dlg->profile_links = linker->next;
} else {
linker_prev->next = linker->next;
}
linker->next = NULL;
dlg_unlock( d_table, d_entry);
/* remove linker from profile table and free it */
destroy_linkers(linker);
dlg_release(dlg);
return 1;
}
/*!
* \brief Check if a dialog belongs to a profile
* \param msg SIP message
* \param profile dialog profile table
* \param value value
* \return 1 on success, -1 on failure
*/
int is_dlg_in_profile(struct sip_msg *msg, struct dlg_profile_table *profile,
str *value)
{
struct dlg_cell *dlg;
struct dlg_profile_link *linker;
struct dlg_entry *d_entry;
int ret;
/* get current dialog */
dlg = dlg_get_msg_dialog(msg);
if (dlg==NULL)
return -1;
ret = -1;
/* check the dialog linkers */
d_entry = &d_table->entries[dlg->h_entry];
dlg_lock( d_table, d_entry);
for( linker=dlg->profile_links ; linker ; linker=linker->next) {
if (linker->profile==profile) {
if (profile->has_value==0) {
dlg_unlock( d_table, d_entry);
ret = 1;
goto done;
} else if (value && value->len==linker->hash_linker.value.len &&
memcmp(value->s,linker->hash_linker.value.s,value->len)==0){
dlg_unlock( d_table, d_entry);
ret = 1;
goto done;
}
/* allow further search - maybe the dialog is inserted twice in
* the same profile, but with different values -bogdan
*/
}
}
dlg_unlock( d_table, d_entry);
done:
dlg_release(dlg);
return ret;
}
/*!
* \brief Get the size of a profile
* \param profile evaluated profile
* \param value value
* \return the profile size
*/
unsigned int get_profile_size(struct dlg_profile_table *profile, str *value)
{
unsigned int n,i;
struct dlg_profile_hash *ph;
if (profile->has_value==0 || value==NULL) {
/* iterate through the hash and count all records */
lock_get( &profile->lock );
for( i=0,n=0 ; i<profile->size ; i++ )
n += profile->entries[i].content;
lock_release( &profile->lock );
return n;
} else {
/* iterate through the hash entry and count only matching */
/* calculate the hash position */
i = calc_hash_profile( value, NULL, profile);
n = 0;
lock_get( &profile->lock );
ph = profile->entries[i].first;
if(ph) {
do {
/* compare */
if ( value->len==ph->value.len &&
memcmp(value->s,ph->value.s,value->len)==0 ) {
/* found */
n++;
}
/* next */
ph=ph->next;
}while( ph!=profile->entries[i].first );
}
lock_release( &profile->lock );
return n;
}
}
/*
* Determine if message is in a dialog currently being tracked
*/
int is_known_dlg(struct sip_msg *msg) {
dlg_cell_t *dlg;
dlg = dlg_get_msg_dialog(msg);
if(dlg == NULL)
return -1;
dlg_release(dlg);
return 1;
}
/*
* \brief Set the timeout of all the dialogs in a given profile, by value.
* \param profile The evaluated profile name.
* \param value The value constraint.
* \param timeout The dialog timeout to apply.
*/
int dlg_set_timeout_by_profile(struct dlg_profile_table *profile,
str *value, int timeout)
{
unsigned int i = 0;
dlg_cell_t *this_dlg = NULL;
struct dlg_profile_hash *ph = NULL;
/* Private structure necessary for manipulating dialog
* timeouts outside of profile locks. Admittedly, an
* ugly hack, but avoids some concurrency issues.
*/
struct dlg_map_list {
unsigned int h_id;
unsigned int h_entry;
struct dlg_map_list *next;
} *map_head, *map_scan, *map_scan_next;
map_head = NULL;
/* If the profile has no value, iterate through every
* node and set its timeout.
*/
if(profile->has_value == 0 || value == NULL) {
lock_get(&profile->lock);
for(i = 0; i < profile->size; i ++) {
ph = profile->entries[i].first;
if(!ph) continue;
do {
struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list));
if(!d)
goto error;
memset(d, 0, sizeof(struct dlg_map_list));
d->h_id = ph->dlg->h_id;
d->h_entry = ph->dlg->h_entry;
if(map_head == NULL)
map_head = d;
else {
d->next = map_head;
map_head = d;
}
ph = ph->next;
} while(ph != profile->entries[i].first);
}
lock_release(&profile->lock);
} else {
i = calc_hash_profile(value, NULL, profile);
lock_get(&profile->lock);
ph = profile->entries[i].first;
if(ph) {
do {
if(ph && value->len == ph->value.len &&
memcmp(value->s, ph->value.s, value->len) == 0) {
struct dlg_map_list *d = malloc(sizeof(struct dlg_map_list));
if(!d)
goto error;
memset(d, 0, sizeof(struct dlg_map_list));
d->h_id = ph->dlg->h_id;
d->h_entry = ph->dlg->h_entry;
if(map_head == NULL)
map_head = d;
else {
d->next = map_head;
map_head = d;
}
}
ph = ph->next;
} while(ph && ph != profile->entries[i].first);
}
lock_release(&profile->lock);
}
/* Walk the list and bulk-set the timeout */
for(map_scan = map_head; map_scan != NULL; map_scan = map_scan_next) {
map_scan_next = map_scan->next;
this_dlg = dlg_lookup(map_scan->h_entry, map_scan->h_id);
if(!this_dlg) {
LM_CRIT("Unable to find dialog %d:%d\n", map_scan->h_entry, map_scan->h_id);
} else if(this_dlg->state >= DLG_STATE_EARLY) {
if(update_dlg_timeout(this_dlg, timeout) < 0) {
LM_ERR("Unable to set timeout on %d:%d\n", map_scan->h_entry,
map_scan->h_id);
}
dlg_release(this_dlg);
}
free(map_scan);
}
return 0;
error:
for(map_scan = map_head; map_scan != NULL; map_scan = map_scan_next) {
map_scan_next = map_scan->next;
free(map_scan);
}
return -1;
}
/****************************** MI commands *********************************/
/*!
* \brief Output a profile via MI interface
* \param cmd_tree MI command tree
* \param param unused
* \return MI root output on success, NULL on failure
*/
struct mi_root * mi_get_profile(struct mi_root *cmd_tree, void *param)
{
struct mi_node* node;
struct mi_root* rpl_tree= NULL;
struct mi_node* rpl = NULL;
struct mi_attr* attr;
struct dlg_profile_table *profile;
str *value;
str *profile_name;
unsigned int size;
int len;
char *p;
node = cmd_tree->node.kids;
if (node==NULL || !node->value.s || !node->value.len)
return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
profile_name = &node->value;
if (node->next) {
node = node->next;
if (!node->value.s || !node->value.len)
return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
if (node->next)
return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
value = &node->value;
} else {
value = NULL;
}
/* search for the profile */
profile = search_dlg_profile( profile_name );
if (profile==NULL)
return init_mi_tree( 404, MI_SSTR("Profile not found"));
size = get_profile_size( profile , value );
rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
if (rpl_tree==0)
return 0;
rpl = &rpl_tree->node;
node = add_mi_node_child(rpl, MI_DUP_VALUE, "profile", 7, NULL, 0);
if (node==0) {
free_mi_tree(rpl_tree);
return NULL;
}
attr = add_mi_attr(node, MI_DUP_VALUE, "name", 4,
profile->name.s, profile->name.len);
if(attr == NULL) {
goto error;
}
if (value) {
attr = add_mi_attr(node, MI_DUP_VALUE, "value", 5, value->s, value->len);
} else {
attr = add_mi_attr(node, MI_DUP_VALUE, "value", 5, NULL, 0);
}
if(attr == NULL) {
goto error;
}
p= int2str((unsigned long)size, &len);
attr = add_mi_attr(node, MI_DUP_VALUE, "count", 5, p, len);
if(attr == NULL) {
goto error;
}
return rpl_tree;
error:
free_mi_tree(rpl_tree);
return NULL;
}
/*!
* \brief List the profiles via MI interface
* \param cmd_tree MI command tree
* \param param unused
* \return MI root output on success, NULL on failure
*/
struct mi_root * mi_profile_list(struct mi_root *cmd_tree, void *param )
{
struct mi_node* node;
struct mi_root* rpl_tree= NULL;
struct mi_node* rpl = NULL;
struct dlg_profile_table *profile;
struct dlg_profile_hash *ph;
str *profile_name;
str *value;
unsigned int i;
node = cmd_tree->node.kids;
if (node==NULL || !node->value.s || !node->value.len)
return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
profile_name = &node->value;
if (node->next) {
node = node->next;
if (!node->value.s || !node->value.len)
return init_mi_tree( 400, MI_SSTR(MI_BAD_PARM));
if (node->next)
return init_mi_tree( 400, MI_SSTR(MI_MISSING_PARM));
value = &node->value;
} else {
value = NULL;
}
/* search for the profile */
profile = search_dlg_profile( profile_name );
if (profile==NULL)
return init_mi_tree( 404, MI_SSTR("Profile not found"));
rpl_tree = init_mi_tree( 200, MI_SSTR(MI_OK));
if (rpl_tree==0)
return 0;
rpl = &rpl_tree->node;
/* go through the hash and print the dialogs */
if (profile->has_value==0 || value==NULL) {
/* no value */
lock_get( &profile->lock );
for ( i=0 ; i< profile->size ; i++ ) {
ph = profile->entries[i].first;
if(ph) {
do {
/* print dialog */
if ( mi_print_dlg( rpl, ph->dlg, 0)!=0 )
goto error;
/* next */
ph=ph->next;
}while( ph!=profile->entries[i].first );
}
}
lock_release( &profile->lock );
} else {
/* check for value also */
lock_get( &profile->lock );
for ( i=0 ; i< profile->size ; i++ ) {
ph = profile->entries[i].first;
if(ph) {
do {
if ( value->len==ph->value.len &&
memcmp(value->s,ph->value.s,value->len)==0 ) {
/* print dialog */
if ( mi_print_dlg( rpl, ph->dlg, 0)!=0 )
goto error;
}
/* next */
ph=ph->next;
}while( ph!=profile->entries[i].first );
}
}
lock_release( &profile->lock );
}
return rpl_tree;
error:
free_mi_tree(rpl_tree);
return NULL;
}
/**
* json serialization of dialog profiles
*/
int dlg_profiles_to_json(dlg_cell_t *dlg, srjson_doc_t *jdoc)
{
dlg_profile_link_t *l;
srjson_t *aj = NULL;
srjson_t *pj = NULL;
LM_DBG("serializing profiles for dlg[%u:%u]\n",
dlg->h_entry, dlg->h_id);
if(dlg==NULL || dlg->profile_links==NULL)
return -1;
LM_DBG("start of serializing profiles for dlg[%u:%u]\n",
dlg->h_entry, dlg->h_id);
for (l = dlg->profile_links ; l ; l=l->next) {
if(aj==NULL)
{
aj = srjson_CreateArray(jdoc);
if(aj==NULL)
{
LM_ERR("cannot create json profiles array object\n");
goto error;
}
}
pj = srjson_CreateObject(jdoc);
if(pj==NULL)
{
LM_ERR("cannot create json dynamic profiles obj\n");
goto error;
}
srjson_AddStrStrToObject(jdoc, pj,
"name", 4,
l->profile->name.s, l->profile->name.len);
if(l->profile->has_value)
{
srjson_AddStrStrToObject(jdoc, pj,
"value", 5,
l->hash_linker.value.s, l->hash_linker.value.len);
}
if(l->hash_linker.puid[0]!='\0')
srjson_AddStringToObject(jdoc, pj, "puid", l->hash_linker.puid);
if(l->hash_linker.expires!=0)
srjson_AddNumberToObject(jdoc, pj, "expires", l->hash_linker.expires);
if(l->hash_linker.flags!=0)
srjson_AddNumberToObject(jdoc, pj, "flags", l->hash_linker.flags);
srjson_AddItemToArray(jdoc, aj, pj);
}
if(jdoc->root==NULL)
{
jdoc->root = srjson_CreateObject(jdoc);
if(jdoc->root==NULL)
{
LM_ERR("cannot create json root\n");
goto error;
}
}
if(aj!=NULL)
srjson_AddItemToObject(jdoc, jdoc->root, "profiles", aj);
if(jdoc->buf.s != NULL)
{
jdoc->free_fn(jdoc->buf.s);
jdoc->buf.s = NULL;
jdoc->buf.len = 0;
}
jdoc->buf.s = srjson_PrintUnformatted(jdoc, jdoc->root);
if(jdoc->buf.s!=NULL)
{
jdoc->buf.len = strlen(jdoc->buf.s);
LM_DBG("serialized profiles for dlg[%u:%u] = [[%.*s]]\n",
dlg->h_entry, dlg->h_id, jdoc->buf.len, jdoc->buf.s);
return 0;
}
return -1;
error:
srjson_Delete(jdoc, aj);
return -1;
}
/**
* json de-serialization of dialog profiles
*/
int dlg_json_to_profiles(dlg_cell_t *dlg, srjson_doc_t *jdoc)
{
srjson_t *aj = NULL;
srjson_t *it = NULL;
srjson_t *jt = NULL;
dlg_profile_table_t *profile;
str name;
str val;
str puid;
time_t expires;
int flags;
if(dlg==NULL || jdoc==NULL || jdoc->buf.s==NULL)
return -1;
if(jdoc->root == NULL)
{
jdoc->root = srjson_Parse(jdoc, jdoc->buf.s);
if(jdoc->root == NULL)
{
LM_ERR("invalid json doc [[%s]]\n", jdoc->buf.s);
return -1;
}
}
aj = srjson_GetObjectItem(jdoc, jdoc->root, "profiles");
if(aj!=NULL)
{
for(it=aj->child; it; it = it->next)
{
name.s = val.s = puid.s = NULL;
expires = 0; flags = 0;
for(jt = it->child; jt; jt = jt->next) {
if(strcmp(jt->string, "name")==0) {
name.s = jt->valuestring;
name.len = strlen(name.s);
} else if(strcmp(jt->string, "value")==0) {
val.s = jt->valuestring;
val.len = strlen(val.s);
} else if(strcmp(jt->string, "puid")==0) {
puid.s = jt->valuestring;
puid.len = strlen(puid.s);
} else if(strcmp(jt->string, "expires")==0) {
expires = (time_t)SRJSON_GET_ULONG(jt);
} else if(strcmp(jt->string, "flags")==0) {
flags = SRJSON_GET_UINT(jt);
}
}
if(name.s==NULL)
continue;
profile = search_dlg_profile(&name);
if(profile==NULL)
{
LM_ERR("profile [%.*s] not found\n", name.len, name.s);
continue;
}
if(val.s!=NULL) {
if(profile->has_value)
{
if(dlg_add_profile(dlg, &val, profile, &puid, expires, flags) < 0)
LM_ERR("dynamic profile cannot be added, ignore!\n");
else
LM_DBG("dynamic profile added [%s : %s]\n", name.s, val.s);
}
} else {
if(!profile->has_value)
{
if(dlg_add_profile(dlg, NULL, profile, &puid, expires, flags) < 0)
LM_ERR("static profile cannot be added, ignore!\n");
else
LM_DBG("static profile added [%s]\n", name.s);
}
}
}
}
return 0;
}
/*!
*
*/
int dlg_cmd_remote_profile(str *cmd, str *pname, str *value, str *puid,
time_t expires, int flags)
{
dlg_profile_table_t *dprofile;
int ret;
if(cmd==NULL || cmd->s==NULL || cmd->len<=0
|| pname==NULL || pname->s==NULL || pname->len<=0
|| puid==NULL || puid->s==NULL || puid->len<=0) {
LM_ERR("invalid parameters\n");
return -1;
}
dprofile = search_dlg_profile(pname);
if(dprofile==NULL) {
LM_ERR("profile [%.*s] not found\n", pname->len, pname->s);
return -1;
}
if(dprofile->has_value) {
if(value==NULL || value->s==NULL || value->len<=0) {
LM_ERR("profile [%.*s] requires a value\n", pname->len, pname->s);
return -1;
}
}
if(cmd->len==3 && strncmp(cmd->s, "add", 3)==0) {
if(value && value->s && value->len>0) {
ret = dlg_add_profile(NULL, value, dprofile, puid, expires, flags);
} else {
ret = dlg_add_profile(NULL, NULL, dprofile, puid, expires, flags);
}
if(ret<0) {
LM_ERR("failed to add to profile [%.*s]\n", pname->len, pname->s);
return -1;
}
} else if(cmd->len==2 && strncmp(cmd->s, "rm", 2)==0) {
ret = remove_profile(dprofile, value, puid);
return ret;
} else {
LM_ERR("unknown command [%.*s]\n", cmd->len, cmd->s);
return -1;
}
return 0;
}