/* * 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, 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<\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; isize; 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->expiresnext) { 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 && puid->lenhash_linker.puid, puid->s); linker->hash_linker.puid_len = puid->len; } else { 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; } 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 ; isize ; 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) return -1; 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) return -1; 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; } /****************************** 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; }