diff --git a/modules/sca/sca.c b/modules/sca/sca.c index 4c73c0f576a..89dd81634e8 100644 --- a/modules/sca/sca.c +++ b/modules/sca/sca.c @@ -34,344 +34,321 @@ #include "sca_rpc.h" #include "sca_subscribe.h" - MODULE_VERSION /* MODULE OBJECT */ -sca_mod *sca = NULL; - +sca_mod *sca = NULL; /* EXTERNAL API */ -db_func_t dbf; /* db api */ -struct tm_binds tmb; /* tm functions for sending messages */ -sl_api_t slb; /* sl callback, function for getting to-tag */ +db_func_t dbf; /* db api */ +struct tm_binds tmb; /* tm functions for sending messages */ +sl_api_t slb; /* sl callback, function for getting to-tag */ /* PROTOTYPES */ -static int sca_mod_init( void ); -static int sca_child_init( int ); -static void sca_mod_destroy( void ); -static int sca_set_config( sca_mod * ); +static int sca_mod_init(void); +static int sca_child_init(int); +static void sca_mod_destroy(void); +static int sca_set_config(sca_mod *); /* EXPORTED COMMANDS */ -static cmd_export_t cmds[] = { - { "sca_handle_subscribe", sca_handle_subscribe, 0, NULL, REQUEST_ROUTE }, - { "sca_call_info_update", sca_call_info_update, 0, NULL, - REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE }, - { "sca_call_info_update", sca_call_info_update, 1, fixup_var_int_1, - REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE }, - { NULL, NULL, -1, 0, 0 }, -}; +static cmd_export_t cmds[] = { + { "sca_handle_subscribe", sca_handle_subscribe, + 0, NULL, REQUEST_ROUTE }, + { "sca_call_info_update",sca_call_info_update, + 0, NULL, + REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE }, + {"sca_call_info_update", sca_call_info_update, + 1, fixup_var_int_1, + REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE }, + { NULL, NULL, -1, 0, 0 }, }; /* EXPORTED RPC INTERFACE */ -static rpc_export_t sca_rpc[] = { - { "sca.all_subscriptions", sca_rpc_show_all_subscriptions, - sca_rpc_show_all_subscriptions_doc, 0 }, - { "sca.subscription_count", sca_rpc_subscription_count, - sca_rpc_subscription_count_doc, 0 }, - { "sca.show_subscription", sca_rpc_show_subscription, - sca_rpc_show_subscription_doc, 0 }, - { "sca.subscribers", sca_rpc_show_subscribers, - sca_rpc_show_subscribers_doc, 0 }, - { "sca.deactivate_all_subscriptions", sca_rpc_deactivate_all_subscriptions, - sca_rpc_deactivate_all_subscriptions_doc, 0 }, - { "sca.deactivate_subscription", sca_rpc_deactivate_subscription, - sca_rpc_deactivate_subscription_doc, 0 }, - { "sca.all_appearances", sca_rpc_show_all_appearances, - sca_rpc_show_all_appearances_doc, 0 }, - { "sca.show_appearance", sca_rpc_show_appearance, - sca_rpc_show_appearance_doc, 0 }, - { "sca.seize_appearance", sca_rpc_seize_appearance, - sca_rpc_seize_appearance_doc, 0 }, - { "sca.update_appearance", sca_rpc_update_appearance, - sca_rpc_update_appearance_doc, 0 }, - { "sca.release_appearance", sca_rpc_release_appearance, - sca_rpc_release_appearance_doc, 0 }, - { NULL, NULL, NULL, 0 }, -}; +static rpc_export_t sca_rpc[] = { { "sca.all_subscriptions", + sca_rpc_show_all_subscriptions, sca_rpc_show_all_subscriptions_doc, 0 }, + { "sca.subscription_count", sca_rpc_subscription_count, + sca_rpc_subscription_count_doc, 0 }, { "sca.show_subscription", + sca_rpc_show_subscription, sca_rpc_show_subscription_doc, 0 }, { + "sca.subscribers", sca_rpc_show_subscribers, + sca_rpc_show_subscribers_doc, 0 }, { + "sca.deactivate_all_subscriptions", + sca_rpc_deactivate_all_subscriptions, + sca_rpc_deactivate_all_subscriptions_doc, 0 }, { + "sca.deactivate_subscription", sca_rpc_deactivate_subscription, + sca_rpc_deactivate_subscription_doc, 0 }, { + "sca.all_appearances", sca_rpc_show_all_appearances, + sca_rpc_show_all_appearances_doc, 0 }, { "sca.show_appearance", + sca_rpc_show_appearance, sca_rpc_show_appearance_doc, 0 }, { + "sca.seize_appearance", sca_rpc_seize_appearance, + sca_rpc_seize_appearance_doc, 0 }, { "sca.update_appearance", + sca_rpc_update_appearance, sca_rpc_update_appearance_doc, 0 }, { + "sca.release_appearance", sca_rpc_release_appearance, + sca_rpc_release_appearance_doc, 0 }, { NULL, NULL, NULL, 0 }, }; /* EXPORTED PARAMETERS */ -str outbound_proxy = STR_NULL; -str db_url = STR_STATIC_INIT( DEFAULT_DB_URL ); -str db_subs_table = STR_STATIC_INIT( "sca_subscriptions" ); -str db_state_table = STR_STATIC_INIT( "sca_state" ); -int db_update_interval = 300; -int hash_table_size = -1; -int call_info_max_expires = 3600; -int line_seize_max_expires = 15; -int purge_expired_interval = 120; - -static param_export_t params[] = { - { "outbound_proxy", PARAM_STR, &outbound_proxy }, - { "db_url", PARAM_STR, &db_url }, - { "subs_table", PARAM_STR, &db_subs_table }, - { "state_table", PARAM_STR, &db_state_table }, - { "db_update_interval", INT_PARAM, &db_update_interval }, - { "hash_table_size", INT_PARAM, &hash_table_size }, - { "call_info_max_expires", INT_PARAM, &call_info_max_expires }, - { "line_seize_max_expires", INT_PARAM, &line_seize_max_expires }, - { "purge_expired_interval", INT_PARAM, &purge_expired_interval }, - { NULL, 0, NULL }, -}; +str outbound_proxy = STR_NULL; +str db_url = STR_STATIC_INIT( DEFAULT_DB_URL ); +str db_subs_table = STR_STATIC_INIT( "sca_subscriptions" ); +str db_state_table = STR_STATIC_INIT( "sca_state" ); +int db_update_interval = 300; +int hash_table_size = -1; +int call_info_max_expires = 3600; +int line_seize_max_expires = 15; +int purge_expired_interval = 120; + +static param_export_t params[] = { + { "outbound_proxy", PARAM_STR, &outbound_proxy }, + { "db_url", PARAM_STR, &db_url }, + { "subs_table", PARAM_STR, &db_subs_table }, + { "state_table", PARAM_STR, &db_state_table }, + { "db_update_interval", INT_PARAM, &db_update_interval }, + { "hash_table_size", INT_PARAM, &hash_table_size }, + { "call_info_max_expires", INT_PARAM, &call_info_max_expires }, + { "line_seize_max_expires", INT_PARAM, &line_seize_max_expires }, + { "purge_expired_interval", INT_PARAM, &purge_expired_interval }, + { NULL, 0, NULL }, }; /* MODULE EXPORTS */ -struct module_exports exports = { - "sca", /* module name */ - cmds, /* exported functions */ - NULL, /* RPC methods */ - params, /* exported parameters */ - sca_mod_init, /* module initialization function */ - NULL, /* response handling function */ - sca_mod_destroy, /* destructor function */ - NULL, /* oncancel function */ - sca_child_init, /* per-child initialization function */ +struct module_exports exports = { "sca", /* module name */ +cmds, /* exported functions */ +NULL, /* RPC methods */ +params, /* exported parameters */ +sca_mod_init, /* module initialization function */ +NULL, /* response handling function */ +sca_mod_destroy, /* destructor function */ +NULL, /* oncancel function */ +sca_child_init, /* per-child initialization function */ }; +static int sca_bind_sl(sca_mod *scam, sl_api_t *sl_api) { + sl_cbelem_t sl_cbe; - static int -sca_bind_sl( sca_mod *scam, sl_api_t *sl_api ) -{ - sl_cbelem_t sl_cbe; - - assert( scam != NULL ); - assert( sl_api != NULL ); + assert(scam != NULL); + assert(sl_api != NULL); - if ( sl_load_api( sl_api ) != 0 ) { - LM_ERR( "Failed to initialize required sl API" ); - return( -1 ); - } - scam->sl_api = sl_api; + if (sl_load_api(sl_api) != 0) { + LM_ERR( "Failed to initialize required sl API\n" ); + return (-1); + } + scam->sl_api = sl_api; - sl_cbe.type = SLCB_REPLY_READY; - sl_cbe.cbf = (sl_cbf_f)sca_call_info_sl_reply_cb; + sl_cbe.type = SLCB_REPLY_READY; + sl_cbe.cbf = (sl_cbf_f) sca_call_info_sl_reply_cb; - if ( scam->sl_api->register_cb( &sl_cbe ) < 0 ) { - LM_ERR( "Failed to register sl reply callback" ); - return( -1 ); - } + if (scam->sl_api->register_cb(&sl_cbe) < 0) { + LM_ERR( "Failed to register sl reply callback\n" ); + return (-1); + } - return( 0 ); + return (0); } - static int -sca_bind_srdb1( sca_mod *scam, db_func_t *db_api ) -{ - db1_con_t *db_con = NULL; - int rc = -1; - - if ( db_bind_mod( scam->cfg->db_url, db_api ) != 0 ) { - LM_ERR( "Failed to initialize required DB API" ); - goto done; - } - scam->db_api = db_api; - - if ( !DB_CAPABILITY( (*db_api), DB_CAP_ALL )) { - LM_ERR( "Selected database %.*s lacks required capabilities", - STR_FMT( scam->cfg->db_url )); - goto done; - } - - /* ensure database exists and table schemas are correct */ - db_con = db_api->init( scam->cfg->db_url ); - if ( db_con == NULL ) { - LM_ERR( "sca_bind_srdb1: failed to connect to DB %.*s", - STR_FMT( scam->cfg->db_url )); - goto done; - } - - if ( db_check_table_version( db_api, db_con, - scam->cfg->subs_table, SCA_DB_SUBSCRIPTIONS_TABLE_VERSION ) < 0 ) { - LM_ERR( "Version check of %.*s table in DB %.*s failed", - STR_FMT( scam->cfg->subs_table ), STR_FMT( scam->cfg->db_url )); - LM_ERR( "%.*s table version %d required", - STR_FMT( scam->cfg->subs_table ), - SCA_DB_SUBSCRIPTIONS_TABLE_VERSION ); - goto done; - } - - /* DB and tables are OK, close DB handle. reopen in each child. */ - rc = 0; - -done: - if ( db_con != NULL ) { - db_api->close( db_con ); - db_con = NULL; - } - - return( rc ); +static int sca_bind_srdb1(sca_mod *scam, db_func_t *db_api) { + db1_con_t *db_con = NULL; + int rc = -1; + + if (db_bind_mod(scam->cfg->db_url, db_api) != 0) { + LM_ERR( "Failed to initialize required DB API\n" ); + goto done; + } + scam->db_api = db_api; + + if (!DB_CAPABILITY((*db_api), DB_CAP_ALL)) { + LM_ERR( "Selected database %.*s lacks required capabilities\n", + STR_FMT( scam->cfg->db_url )); + goto done; + } + + /* ensure database exists and table schemas are correct */ + db_con = db_api->init(scam->cfg->db_url); + if (db_con == NULL) { + LM_ERR( "sca_bind_srdb1: failed to connect to DB %.*s\n", + STR_FMT( scam->cfg->db_url )); + goto done; + } + + if (db_check_table_version(db_api, db_con, scam->cfg->subs_table, + SCA_DB_SUBSCRIPTIONS_TABLE_VERSION) < 0) { + LM_ERR( "Version check of %.*s table in DB %.*s failed\n", + STR_FMT( scam->cfg->subs_table ), STR_FMT( scam->cfg->db_url )); + LM_ERR( "%.*s table version %d required\n", + STR_FMT( scam->cfg->subs_table ), + SCA_DB_SUBSCRIPTIONS_TABLE_VERSION ); + goto done; + } + + /* DB and tables are OK, close DB handle. reopen in each child. */ + rc = 0; + + done: if (db_con != NULL) { + db_api->close(db_con); + db_con = NULL; + } + + return (rc); } - static int -sca_set_config( sca_mod *scam ) -{ - scam->cfg = (sca_config *)shm_malloc( sizeof( sca_config )); - if ( scam->cfg == NULL ) { - LM_ERR( "Failed to shm_malloc module configuration" ); - return( -1 ); - } - memset(scam->cfg, 0, sizeof( sca_config )); - - if ( outbound_proxy.s ) { - scam->cfg->outbound_proxy = &outbound_proxy; - } - - if ( !db_url.s || db_url.len<=0 ) { - LM_ERR( "sca_set_config: db_url must be set!" ); - return( -1 ); - } - scam->cfg->db_url = &db_url; - - if ( !db_subs_table.s || db_subs_table.len<=0 ) { - LM_ERR( "sca_set_config: subs_table must be set!" ); - return( -1 ); - } - scam->cfg->subs_table = &db_subs_table; - - if ( !db_state_table.s || db_state_table.len<=0 ) { - LM_ERR( "sca_set_config: state_table must be set!" ); - return( -1 ); - } - scam->cfg->state_table = &db_state_table; - - if ( hash_table_size > 0 ) { - scam->cfg->hash_table_size = 1 << hash_table_size; - } else { - scam->cfg->hash_table_size = 512; - } - - scam->cfg->db_update_interval = db_update_interval; - scam->cfg->call_info_max_expires = call_info_max_expires; - scam->cfg->line_seize_max_expires = line_seize_max_expires; - scam->cfg->purge_expired_interval = purge_expired_interval; - - return( 0 ); +static int sca_set_config(sca_mod *scam) { + scam->cfg = (sca_config *) shm_malloc(sizeof(sca_config)); + if (scam->cfg == NULL) { + LM_ERR( "Failed to shm_malloc module configuration\n" ); + return (-1); + } + memset(scam->cfg, 0, sizeof(sca_config)); + + if (outbound_proxy.s) { + scam->cfg->outbound_proxy = &outbound_proxy; + } + + if (!db_url.s || db_url.len <= 0) { + LM_ERR( "sca_set_config: db_url must be set!\n" ); + return (-1); + } + scam->cfg->db_url = &db_url; + + if (!db_subs_table.s || db_subs_table.len <= 0) { + LM_ERR( "sca_set_config: subs_table must be set!\n" ); + return (-1); + } + scam->cfg->subs_table = &db_subs_table; + + if (!db_state_table.s || db_state_table.len <= 0) { + LM_ERR( "sca_set_config: state_table must be set!\n" ); + return (-1); + } + scam->cfg->state_table = &db_state_table; + + if (hash_table_size > 0) { + scam->cfg->hash_table_size = 1 << hash_table_size; + } else { + scam->cfg->hash_table_size = 512; + } + + scam->cfg->db_update_interval = db_update_interval; + scam->cfg->call_info_max_expires = call_info_max_expires; + scam->cfg->line_seize_max_expires = line_seize_max_expires; + scam->cfg->purge_expired_interval = purge_expired_interval; + + return (0); } - static int -sca_child_init( int rank ) -{ - if ( rank == PROC_INIT || rank == PROC_TCP_MAIN ) { - return( 0 ); - } - - if ( rank == PROC_MAIN ) { - if ( fork_dummy_timer( PROC_TIMER, "SCA DB SYNC PROCESS", - 0, /* we don't need sockets, just writing to DB */ - sca_subscription_db_update_timer, /* timer cb */ - NULL, /* parameter passed to callback */ - sca->cfg->db_update_interval ) < 0 ) { - LM_ERR( "sca_child_init: failed to register subscription DB " - "sync timer process" ); - return( -1 ); +static int sca_child_init(int rank) { + if (rank == PROC_INIT || rank == PROC_TCP_MAIN) { + return (0); } - return( 0 ); - } + if (rank == PROC_MAIN) { + if ( fork_dummy_timer( PROC_TIMER, "SCA DB SYNC PROCESS", 0, /* we don't need sockets, just writing to DB */ + sca_subscription_db_update_timer, /* timer cb */ + NULL, /* parameter passed to callback */ + sca->cfg->db_update_interval) < 0) { + LM_ERR( "sca_child_init: failed to register subscription DB " + "sync timer process\n" ); + return (-1); + } + + return (0); + } - if ( sca->db_api == NULL || sca->db_api->init == NULL ) { - LM_CRIT( "sca_child_init: DB API not loaded!" ); - return( -1 ); - } + if (sca->db_api == NULL || sca->db_api->init == NULL) { + LM_CRIT( "sca_child_init: DB API not loaded!\n" ); + return (-1); + } - return( 0 ); + return (0); } - static int -sca_mod_init( void ) -{ - sca = (sca_mod *)shm_malloc( sizeof( sca_mod )); - if ( sca == NULL ) { - LM_ERR( "Failed to shm_malloc module object" ); - return( -1 ); - } - memset( sca, 0, sizeof( sca_mod )); - - if ( sca_set_config( sca ) != 0 ) { - LM_ERR( "Failed to set configuration" ); - goto error; - } - - if ( rpc_register_array( sca_rpc ) != 0 ) { - LM_ERR( "Failed to register RPC commands" ); - goto error; - } - - if ( sca_bind_srdb1( sca, &dbf ) != 0 ) { - LM_ERR( "Failed to initialize required DB API" ); - goto error; - } - - if ( load_tm_api( &tmb ) != 0 ) { - LM_ERR( "Failed to initialize required tm API" ); - goto error; - } - sca->tm_api = &tmb; - - if ( sca_bind_sl( sca, &slb ) != 0 ) { - LM_ERR( "Failed to initialize required sl API" ); - goto error; - } - - if ( sca_hash_table_create( &sca->subscriptions, - sca->cfg->hash_table_size ) != 0 ) { - LM_ERR( "Failed to create subscriptions hash table" ); - goto error; - } - if ( sca_hash_table_create( &sca->appearances, - sca->cfg->hash_table_size ) != 0 ) { - LM_ERR( "Failed to create appearances hash table" ); - goto error; - } - - sca_subscriptions_restore_from_db( sca ); - - register_timer( sca_subscription_purge_expired, sca, - sca->cfg->purge_expired_interval ); - register_timer( sca_appearance_purge_stale, sca, - sca->cfg->purge_expired_interval ); - - /* - * register separate timer process to write subscriptions to DB. - * move to 3.3+ timer API (register_basic_timer) at some point. - * - * timer process forks in sca_child_init, above. - */ - register_dummy_timers( 1 ); - - LM_INFO( "initialized" ); - - return( 0 ); - -error: - if ( sca != NULL ) { - if ( sca->cfg != NULL ) { - shm_free( sca->cfg ); +static int sca_mod_init(void) { + sca = (sca_mod *) shm_malloc(sizeof(sca_mod)); + if (sca == NULL) { + LM_ERR( "Failed to shm_malloc module object\n" ); + return (-1); } - if ( sca->subscriptions != NULL ) { - sca_hash_table_free( sca->subscriptions ); + memset(sca, 0, sizeof(sca_mod)); + + if (sca_set_config(sca) != 0) { + LM_ERR( "Failed to set configuration\n" ); + goto error; } - if ( sca->appearances != NULL ) { - sca_hash_table_free( sca->appearances ); + + if (rpc_register_array(sca_rpc) != 0) { + LM_ERR( "Failed to register RPC commands\n" ); + goto error; + } + + if (sca_bind_srdb1(sca, &dbf) != 0) { + LM_ERR( "Failed to initialize required DB API\n" ); + goto error; + } + + if (load_tm_api(&tmb) != 0) { + LM_ERR( "Failed to initialize required tm API\n" ); + goto error; + } + sca->tm_api = &tmb; + + if (sca_bind_sl(sca, &slb) != 0) { + LM_ERR( "Failed to initialize required sl API\n" ); + goto error; } - shm_free( sca ); - sca = NULL; - } - return( -1 ); + if (sca_hash_table_create(&sca->subscriptions, sca->cfg->hash_table_size) + != 0) { + LM_ERR( "Failed to create subscriptions hash table\n" ); + goto error; + } + if (sca_hash_table_create(&sca->appearances, sca->cfg->hash_table_size) + != 0) { + LM_ERR( "Failed to create appearances hash table\n" ); + goto error; + } + + sca_subscriptions_restore_from_db(sca); + + register_timer(sca_subscription_purge_expired, sca, + sca->cfg->purge_expired_interval); + register_timer(sca_appearance_purge_stale, sca, + sca->cfg->purge_expired_interval); + + /* + * register separate timer process to write subscriptions to DB. + * move to 3.3+ timer API (register_basic_timer) at some point. + * + * timer process forks in sca_child_init, above. + */ + register_dummy_timers(1); + + LM_INFO( "initialized\n" ); + + return (0); + + error: if (sca != NULL) { + if (sca->cfg != NULL) { + shm_free(sca->cfg); + } + if (sca->subscriptions != NULL) { + sca_hash_table_free(sca->subscriptions); + } + if (sca->appearances != NULL) { + sca_hash_table_free(sca->appearances); + } + shm_free(sca); + sca = NULL; + } + + return (-1); } - void -sca_mod_destroy( void ) -{ - if(sca==0) +void sca_mod_destroy(void) { + if (sca == 0) return; - /* write back to the DB to retain most current subscription info */ - if ( sca_subscription_db_update() != 0 ) { - if(sca && sca->cfg && sca->cfg->db_url) { + /* write back to the DB to retain most current subscription info */ + if (sca_subscription_db_update() != 0) { + if (sca && sca->cfg && sca->cfg->db_url) { LM_ERR( "sca_mod_destroy: failed to save current subscriptions " - "in DB %.*s", STR_FMT( sca->cfg->db_url )); + "in DB %.*s\n", STR_FMT( sca->cfg->db_url )); } - } + } - sca_db_disconnect(); + sca_db_disconnect(); } diff --git a/modules/sca/sca.h b/modules/sca/sca.h index 83a1c08811c..0dfb1c7d6fa 100644 --- a/modules/sca/sca.h +++ b/modules/sca/sca.h @@ -28,29 +28,29 @@ #define SCA_H struct _sca_config { - str *outbound_proxy; - str *db_url; - str *subs_table; - str *state_table; - int db_update_interval; - int hash_table_size; - int call_info_max_expires; - int line_seize_max_expires; - int purge_expired_interval; + str *outbound_proxy; + str *db_url; + str *subs_table; + str *state_table; + int db_update_interval; + int hash_table_size; + int call_info_max_expires; + int line_seize_max_expires; + int purge_expired_interval; }; -typedef struct _sca_config sca_config; +typedef struct _sca_config sca_config; struct _sca_mod { - sca_config *cfg; - sca_hash_table *subscriptions; - sca_hash_table *appearances; + sca_config *cfg; + sca_hash_table *subscriptions; + sca_hash_table *appearances; - db_func_t *db_api; - struct tm_binds *tm_api; - sl_api_t *sl_api; + db_func_t *db_api; + struct tm_binds *tm_api; + sl_api_t *sl_api; }; -typedef struct _sca_mod sca_mod; +typedef struct _sca_mod sca_mod; -extern sca_mod *sca; +extern sca_mod *sca; #endif /* SCA_H */ diff --git a/modules/sca/sca_appearance.c b/modules/sca/sca_appearance.c index e0ba91e61a8..1f79f9b1a11 100644 --- a/modules/sca/sca_appearance.c +++ b/modules/sca/sca_appearance.c @@ -39,132 +39,120 @@ const str SCA_APPEARANCE_STATE_STR_PROGRESSING = STR_STATIC_INIT("progressing"); const str SCA_APPEARANCE_STATE_STR_ALERTING = STR_STATIC_INIT( "alerting" ); const str SCA_APPEARANCE_STATE_STR_ACTIVE = STR_STATIC_INIT( "active" ); const str SCA_APPEARANCE_STATE_STR_HELD = STR_STATIC_INIT( "held" ); -const str SCA_APPEARANCE_STATE_STR_HELD_PRIVATE = STR_STATIC_INIT("held-private"); +const str SCA_APPEARANCE_STATE_STR_HELD_PRIVATE = +STR_STATIC_INIT("held-private"); const str SCA_APPEARANCE_STATE_STR_UNKNOWN = STR_STATIC_INIT( "unknown" ); - /* STR_ACTIVE is repeated, once for ACTIVE_PENDING, once for ACTIVE */ -const str *state_names[] = { - &SCA_APPEARANCE_STATE_STR_IDLE, - &SCA_APPEARANCE_STATE_STR_SEIZED, - &SCA_APPEARANCE_STATE_STR_PROGRESSING, - &SCA_APPEARANCE_STATE_STR_ALERTING, - &SCA_APPEARANCE_STATE_STR_ACTIVE, - &SCA_APPEARANCE_STATE_STR_ACTIVE, - &SCA_APPEARANCE_STATE_STR_HELD, - &SCA_APPEARANCE_STATE_STR_HELD_PRIVATE, - }; +const str *state_names[] = { + &SCA_APPEARANCE_STATE_STR_IDLE, + &SCA_APPEARANCE_STATE_STR_SEIZED, + &SCA_APPEARANCE_STATE_STR_PROGRESSING, + &SCA_APPEARANCE_STATE_STR_ALERTING, + &SCA_APPEARANCE_STATE_STR_ACTIVE, + &SCA_APPEARANCE_STATE_STR_ACTIVE, + &SCA_APPEARANCE_STATE_STR_HELD, + &SCA_APPEARANCE_STATE_STR_HELD_PRIVATE, +}; #define SCA_APPEARANCE_STATE_NAME_COUNT \ ( sizeof( state_names ) / sizeof( state_names[ 0 ] )) - void -sca_appearance_state_to_str( int state, str *state_str ) -{ - assert( state_str != NULL ); +void sca_appearance_state_to_str(int state, str *state_str) { + assert(state_str != NULL); - if ( state >= SCA_APPEARANCE_STATE_NAME_COUNT || state < 0 ) { - state_str->len = SCA_APPEARANCE_STATE_STR_UNKNOWN.len; - state_str->s = SCA_APPEARANCE_STATE_STR_UNKNOWN.s; + if (state >= SCA_APPEARANCE_STATE_NAME_COUNT || state < 0) { + state_str->len = SCA_APPEARANCE_STATE_STR_UNKNOWN.len; + state_str->s = SCA_APPEARANCE_STATE_STR_UNKNOWN.s; - return; - } + return; + } - state_str->len = state_names[ state ]->len; - state_str->s = state_names[ state ]->s; + state_str->len = state_names[state]->len; + state_str->s = state_names[state]->s; } - int -sca_appearance_state_from_str( str *state_str ) -{ - int state; +int sca_appearance_state_from_str(str *state_str) { + int state; - assert( state_str != NULL ); + assert(state_str != NULL); - for ( state = 0; state < SCA_APPEARANCE_STATE_NAME_COUNT; state++ ) { - if ( SCA_STR_EQ( state_str, state_names[ state ] )) { - break; + for (state = 0; state < SCA_APPEARANCE_STATE_NAME_COUNT; state++) { + if (SCA_STR_EQ(state_str, state_names[state])) { + break; + } + } + if (state >= SCA_APPEARANCE_STATE_NAME_COUNT) { + state = SCA_APPEARANCE_STATE_UNKNOWN; } - } - if ( state >= SCA_APPEARANCE_STATE_NAME_COUNT ) { - state = SCA_APPEARANCE_STATE_UNKNOWN; - } - return( state ); + return (state); } - sca_appearance * -sca_appearance_create( int appearance_index, str *owner_uri ) -{ - sca_appearance *new_appearance = NULL; - - /* - * we use multiple shm_malloc calls here because uri, owner, - * dialog and callee are mutable. could also shm_malloc a big - * block and divide it among the strs.... - */ - - new_appearance = (sca_appearance *)shm_malloc( sizeof( sca_appearance )); - if ( new_appearance == NULL ) { - LM_ERR( "Failed to shm_malloc new sca_appearance for %.*s, index %d", - STR_FMT( owner_uri ), appearance_index ); - goto error; - } - memset( new_appearance, 0, sizeof( sca_appearance )); - - new_appearance->owner.s = (char *)shm_malloc( owner_uri->len ); - if ( new_appearance->owner.s == NULL ) { - LM_ERR( "Failed to shm_malloc space for owner %.*s, index %d", - STR_FMT( owner_uri ), appearance_index ); - goto error; - } - SCA_STR_COPY( &new_appearance->owner, owner_uri ); - - new_appearance->index = appearance_index; - new_appearance->times.ctime = time( NULL ); - sca_appearance_update_state_unsafe( new_appearance, - SCA_APPEARANCE_STATE_IDLE ); - new_appearance->next = NULL; - - return( new_appearance ); - -error: - if ( new_appearance != NULL ) { - if ( !SCA_STR_EMPTY( &new_appearance->owner )) { - shm_free( new_appearance->owner.s ); - } - - shm_free( new_appearance ); - } - - return( NULL ); -} +sca_appearance *sca_appearance_create(int appearance_index, str *owner_uri) { + sca_appearance *new_appearance = NULL; + + /* + * we use multiple shm_malloc calls here because uri, owner, + * dialog and callee are mutable. could also shm_malloc a big + * block and divide it among the strs.... + */ - void -sca_appearance_free( sca_appearance *appearance ) -{ - if ( appearance != NULL ) { - if ( appearance->owner.s != NULL ) { - shm_free( appearance->owner.s ); + new_appearance = (sca_appearance *) shm_malloc(sizeof(sca_appearance)); + if (new_appearance == NULL) { + LM_ERR( "Failed to shm_malloc new sca_appearance for %.*s, index %d\n", + STR_FMT( owner_uri ), appearance_index ); + goto error; } - if ( appearance->uri.s != NULL ) { - shm_free( appearance->uri.s ); + memset(new_appearance, 0, sizeof(sca_appearance)); + + new_appearance->owner.s = (char *) shm_malloc(owner_uri->len); + if (new_appearance->owner.s == NULL) { + LM_ERR( "Failed to shm_malloc space for owner %.*s, index %d\n", + STR_FMT( owner_uri ), appearance_index ); + goto error; } - if ( appearance->dialog.id.s != NULL ) { - shm_free( appearance->dialog.id.s ); + SCA_STR_COPY(&new_appearance->owner, owner_uri); + + new_appearance->index = appearance_index; + new_appearance->times.ctime = time(NULL); + sca_appearance_update_state_unsafe(new_appearance, + SCA_APPEARANCE_STATE_IDLE); + new_appearance->next = NULL; + + return (new_appearance); + + error: if (new_appearance != NULL) { + if (!SCA_STR_EMPTY(&new_appearance->owner)) { + shm_free(new_appearance->owner.s); + } + shm_free(new_appearance); } + return (NULL); +} - if ( appearance->prev_owner.s != NULL ) { - shm_free( appearance->prev_owner.s ); - } - if ( appearance->prev_callee.s != NULL ) { - shm_free( appearance->prev_callee.s ); - } - if ( appearance->prev_dialog.id.s != NULL ) { - shm_free( appearance->prev_dialog.id.s ); - } +void sca_appearance_free(sca_appearance *appearance) { + if (appearance != NULL) { + if (appearance->owner.s != NULL) { + shm_free(appearance->owner.s); + } + if (appearance->uri.s != NULL) { + shm_free(appearance->uri.s); + } + if (appearance->dialog.id.s != NULL) { + shm_free(appearance->dialog.id.s); + } - shm_free( appearance ); - } + if (appearance->prev_owner.s != NULL) { + shm_free(appearance->prev_owner.s); + } + if (appearance->prev_callee.s != NULL) { + shm_free(appearance->prev_callee.s); + } + if (appearance->prev_dialog.id.s != NULL) { + shm_free(appearance->prev_dialog.id.s); + } + shm_free(appearance); + } } /* @@ -175,470 +163,468 @@ sca_appearance_free( sca_appearance *appearance ) * -1: error * >=1: index reserved for claimant */ - static int -sca_appearance_list_next_available_index_unsafe( sca_appearance_list *app_list ) -{ - sca_appearance *app_cur; - int idx = 1; +static int sca_appearance_list_next_available_index_unsafe( + sca_appearance_list *app_list) { + sca_appearance *app_cur; + int idx = 1; - assert( app_list != NULL ); + assert(app_list != NULL); - for ( app_cur = app_list->appearances; app_cur != NULL; - app_cur = app_cur->next, idx++ ) { - if ( idx < app_cur->index ) { - break; + for (app_cur = app_list->appearances; app_cur != NULL; + app_cur = app_cur->next, idx++) { + if (idx < app_cur->index) { + break; + } } - } - return( idx ); + return (idx); } - static sca_appearance_list * -sca_appearance_list_create( sca_mod *scam, str *aor ) -{ - sca_appearance_list *app_list; - int len; - - len = sizeof( sca_appearance_list ) + aor->len; - app_list = (sca_appearance_list *)shm_malloc( len ); - if ( app_list == NULL ) { - LM_ERR( "Failed to shm_malloc sca_appearance_list for %.*s", - STR_FMT( aor )); - return( NULL ); - } - memset( app_list, 0, sizeof( sca_appearance_list )); - - len = sizeof( sca_appearance_list ); - app_list->aor.s = (char *)app_list + len; - SCA_STR_COPY( &app_list->aor, aor ); - - return( app_list ); -} +static sca_appearance_list *sca_appearance_list_create(sca_mod *scam, str *aor) { + sca_appearance_list *app_list; + int len; - sca_appearance_list * -sca_appearance_list_for_line( sca_mod *scam, str *aor ) -{ - //sca_appearance_list + len = sizeof(sca_appearance_list) + aor->len; + app_list = (sca_appearance_list *) shm_malloc(len); + if (app_list == NULL) { + LM_ERR( "Failed to shm_malloc sca_appearance_list for %.*s\n", + STR_FMT( aor )); + return (NULL); + } + memset(app_list, 0, sizeof(sca_appearance_list)); + len = sizeof(sca_appearance_list); + app_list->aor.s = (char *) app_list + len; + SCA_STR_COPY(&app_list->aor, aor); - return( NULL ); + return (app_list); } - void -sca_appearance_list_insert_appearance( sca_appearance_list *app_list, - sca_appearance *app ) -{ - sca_appearance **cur; +sca_appearance_list *sca_appearance_list_for_line(sca_mod *scam, str *aor) { + //sca_appearance_list + return (NULL); +} - assert( app_list != NULL ); - assert( app != NULL ); +void sca_appearance_list_insert_appearance(sca_appearance_list *app_list, + sca_appearance *app) { + sca_appearance **cur; - app->appearance_list = app_list; + assert(app_list != NULL); + assert(app != NULL); - for ( cur = &app_list->appearances; *cur != NULL; cur = &(*cur)->next ) { - if ( app->index < (*cur)->index ) { - break; - } - } + app->appearance_list = app_list; - app->next = *cur; - *cur = app; + for (cur = &app_list->appearances; *cur != NULL; cur = &(*cur)->next) { + if (app->index < (*cur)->index) { + break; + } + } + app->next = *cur; + *cur = app; } - sca_appearance * -sca_appearance_list_unlink_index( sca_appearance_list *app_list, int idx ) -{ - sca_appearance *app = NULL; - sca_appearance **cur; +sca_appearance *sca_appearance_list_unlink_index(sca_appearance_list *app_list, + int idx) { + sca_appearance *app = NULL; + sca_appearance **cur; - assert( app_list != NULL ); - assert( idx > 0 ); + assert(app_list != NULL); + assert(idx > 0); - for ( cur = &app_list->appearances; *cur != NULL; cur = &(*cur)->next ) { - if ((*cur)->index == idx ) { - app = *cur; - app->appearance_list = NULL; + for (cur = &app_list->appearances; *cur != NULL; cur = &(*cur)->next) { + if ((*cur)->index == idx) { + app = *cur; + app->appearance_list = NULL; - *cur = (*cur)->next; + *cur = (*cur)->next; - break; + break; + } } - } - if ( app == NULL ) { - LM_ERR( "Tried to remove inactive %.*s appearance at index %d", - STR_FMT( &app_list->aor ), idx ); - } + if (app == NULL) { + LM_ERR( "Tried to remove inactive %.*s appearance at index %d\n", + STR_FMT( &app_list->aor ), idx ); + } - return( app ); + return (app); } - int -sca_appearance_list_unlink_appearance( sca_appearance_list *app_list, - sca_appearance **app ) -{ - sca_appearance **cur; - int rc = 0; +int sca_appearance_list_unlink_appearance(sca_appearance_list *app_list, + sca_appearance **app) { + sca_appearance **cur; + int rc = 0; - assert( app_list != NULL ); - assert( app != NULL && *app != NULL ); + assert(app_list != NULL); + assert(app != NULL && *app != NULL); - for ( cur = &app_list->appearances; *cur != NULL; cur = &(*cur)->next ) { - if ( *cur == *app ) { - *cur = (*cur)->next; + for (cur = &app_list->appearances; *cur != NULL; cur = &(*cur)->next) { + if (*cur == *app) { + *cur = (*cur)->next; - (*app)->appearance_list = NULL; - (*app)->next = NULL; + (*app)->appearance_list = NULL; + (*app)->next = NULL; - rc = 1; + rc = 1; - break; + break; + } } - } - return( rc ); + return (rc); } - int -sca_appearance_list_aor_cmp( str *aor, void *cmp_value ) -{ - sca_appearance_list *app_list = (sca_appearance_list *)cmp_value; - int cmp; +int sca_appearance_list_aor_cmp(str *aor, void *cmp_value) { + sca_appearance_list *app_list = (sca_appearance_list *) cmp_value; + int cmp; - if (( cmp = aor->len - app_list->aor.len ) != 0 ) { - return( cmp ); - } + if ((cmp = aor->len - app_list->aor.len) != 0) { + return (cmp); + } - return( memcmp( aor->s, app_list->aor.s, aor->len )); + return (memcmp(aor->s, app_list->aor.s, aor->len)); } - void -sca_appearance_list_print( void *value ) -{ - sca_appearance_list *app_list = (sca_appearance_list *)value; - sca_appearance *app; - str state_str = STR_NULL; - - LM_INFO( "Appearance state for AoR %.*s:", STR_FMT( &app_list->aor )); - - for ( app = app_list->appearances; app != NULL; app = app->next ) { - sca_appearance_state_to_str( app->state, &state_str ); - LM_INFO( "index: %d, state: %.*s, uri: %.*s, owner: %.*s, " - "callee: %.*s, dialog: %.*s;%.*s;%.*s", - app->index, STR_FMT( &state_str ), - STR_FMT( &app->uri ), STR_FMT( &app->owner ), - STR_FMT( &app->callee ), STR_FMT( &app->dialog.call_id ), - STR_FMT( &app->dialog.from_tag ), - STR_FMT( &app->dialog.to_tag )); - } +void sca_appearance_list_print(void *value) { + sca_appearance_list *app_list = (sca_appearance_list *) value; + sca_appearance *app; + str state_str = STR_NULL; + + LM_INFO( "Appearance state for AoR %.*s:\n", STR_FMT( &app_list->aor )); + + for (app = app_list->appearances; app != NULL; app = app->next) { + sca_appearance_state_to_str(app->state, &state_str); + LM_INFO( "index: %d, state: %.*s, uri: %.*s, owner: %.*s, " + "callee: %.*s, dialog: %.*s;%.*s;%.*s\n", + app->index, STR_FMT( &state_str ), + STR_FMT( &app->uri ), STR_FMT( &app->owner ), + STR_FMT( &app->callee ), STR_FMT( &app->dialog.call_id ), + STR_FMT( &app->dialog.from_tag ), + STR_FMT( &app->dialog.to_tag )); + } } - void -sca_appearance_list_free( void *value ) -{ - sca_appearance_list *app_list = (sca_appearance_list *)value; - sca_appearance *app, *app_tmp; +void sca_appearance_list_free(void *value) { + sca_appearance_list *app_list = (sca_appearance_list *) value; + sca_appearance *app, *app_tmp; - LM_DBG( "Freeing appearance list for AoR %.*s", STR_FMT( &app_list->aor )); + LM_DBG( "Freeing appearance list for AoR %.*s\n", STR_FMT( &app_list->aor )); - for ( app = app_list->appearances; app != NULL; app = app_tmp ) { - app_tmp = app->next; + for (app = app_list->appearances; app != NULL; app = app_tmp) { + app_tmp = app->next; - shm_free( app ); - } + shm_free(app); + } - shm_free( app_list ); + shm_free(app_list); } - int -sca_appearance_register( sca_mod *scam, str *aor ) -{ - sca_appearance_list *app_list; - int rc = -1; - - assert( scam != NULL ); - assert( aor != NULL ); - - if ( sca_uri_is_shared_appearance( scam, aor )) { - /* we've already registered */ - rc = 0; - goto done; - } - - app_list = sca_appearance_list_create( scam, aor ); - if ( app_list == NULL ) { - goto done; - } - - if ( sca_hash_table_kv_insert( scam->appearances, aor, app_list, - sca_appearance_list_aor_cmp, - sca_appearance_list_print, - sca_appearance_list_free ) < 0 ) { - LM_ERR( "sca_appearance_register: failed to insert appearance list " - "for %.*s", STR_FMT( aor )); - goto done; - } - - rc = 1; - -done: - return( rc ); +int sca_appearance_register(sca_mod *scam, str *aor) { + sca_appearance_list *app_list; + int rc = -1; + + assert(scam != NULL); + assert(aor != NULL); + + if (sca_uri_is_shared_appearance(scam, aor)) { + /* we've already registered */ + rc = 0; + goto done; + } + + app_list = sca_appearance_list_create(scam, aor); + if (app_list == NULL) { + goto done; + } + + if (sca_hash_table_kv_insert(scam->appearances, aor, app_list, + sca_appearance_list_aor_cmp, sca_appearance_list_print, + sca_appearance_list_free) < 0) { + LM_ERR( "sca_appearance_register: failed to insert appearance list " + "for %.*s\n", STR_FMT( aor )); + goto done; + } + + rc = 1; + + done: return (rc); } - int -sca_appearance_unregister( sca_mod *scam, str *aor ) -{ - int rc = 0; +int sca_appearance_unregister(sca_mod *scam, str *aor) { + int rc = 0; - assert( scam != NULL ); - assert( aor != NULL ); + assert(scam != NULL); + assert(aor != NULL); - if ( sca_uri_is_shared_appearance( scam, aor )) { - if (( rc = sca_hash_table_kv_delete( scam->appearances, aor )) == 0 ) { - rc = 1; - LM_INFO( "unregistered SCA AoR %.*s", STR_FMT( aor )); + if (sca_uri_is_shared_appearance(scam, aor)) { + if ((rc = sca_hash_table_kv_delete(scam->appearances, aor)) == 0) { + rc = 1; + LM_INFO( "unregistered SCA AoR %.*s\n", STR_FMT( aor )); + } } - } - return( rc ); + return (rc); } - sca_appearance * -sca_appearance_seize_index_unsafe( sca_mod *scam, str *aor, str *owner_uri, - int app_idx, int slot_idx, int *seize_error ) -{ - sca_appearance_list *app_list; - sca_appearance *app = NULL; - sca_hash_slot *slot; - int error = SCA_APPEARANCE_ERR_UNKNOWN; - - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); - if ( app_list == NULL ) { - LM_ERR( "sca_appearance_seize_index_unsafe: no appearance list for " - "%.*s", STR_FMT( aor )); - goto done; - } - - if ( app_idx <= 0 ) { - app_idx = sca_appearance_list_next_available_index_unsafe( app_list ); - } - - for ( app = app_list->appearances; app != NULL; app = app->next ) { - if ( app->index >= app_idx ) { - break; - } - } - if ( app != NULL && app->index == app_idx ) { - /* attempt to seize in-use appearance-index */ - error = SCA_APPEARANCE_ERR_INDEX_UNAVAILABLE; - app = NULL; - goto done; - } - - app = sca_appearance_create( app_idx, owner_uri ); - if ( app == NULL ) { - LM_ERR( "Failed to create new appearance for %.*s at index %d", - STR_FMT( owner_uri ), app_idx ); - error = SCA_APPEARANCE_ERR_MALLOC; - goto done; - } - sca_appearance_update_state_unsafe( app, SCA_APPEARANCE_STATE_SEIZED ); - - sca_appearance_list_insert_appearance( app_list, app ); - - error = SCA_APPEARANCE_OK; - -done: - if ( seize_error ) { - *seize_error = error; - } - - return( app ); +sca_appearance * +sca_appearance_seize_index_unsafe(sca_mod *scam, str *aor, str *owner_uri, + int app_idx, int slot_idx, int *seize_error) { + sca_appearance_list *app_list; + sca_appearance *app = NULL; + sca_hash_slot *slot; + int error = SCA_APPEARANCE_ERR_UNKNOWN; + + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); + if (app_list == NULL) { + LM_ERR( "sca_appearance_seize_index_unsafe: no appearance list for " + "%.*s\n", STR_FMT( aor )); + goto done; + } + + if (app_idx <= 0) { + app_idx = sca_appearance_list_next_available_index_unsafe(app_list); + } + + for (app = app_list->appearances; app != NULL; app = app->next) { + if (app->index >= app_idx) { + break; + } + } + if (app != NULL && app->index == app_idx) { + /* attempt to seize in-use appearance-index */ + error = SCA_APPEARANCE_ERR_INDEX_UNAVAILABLE; + app = NULL; + goto done; + } + + app = sca_appearance_create(app_idx, owner_uri); + if (app == NULL) { + LM_ERR( "Failed to create new appearance for %.*s at index %d\n", + STR_FMT( owner_uri ), app_idx ); + error = SCA_APPEARANCE_ERR_MALLOC; + goto done; + } + sca_appearance_update_state_unsafe(app, SCA_APPEARANCE_STATE_SEIZED); + + sca_appearance_list_insert_appearance(app_list, app); + + error = SCA_APPEARANCE_OK; + + done: if (seize_error) { + *seize_error = error; + } + + return (app); } - int -sca_appearance_seize_index( sca_mod *scam, str *aor, int idx, str *owner_uri ) -{ - sca_appearance *app; - int slot_idx; - int app_idx = -1; - int error = SCA_APPEARANCE_OK; +int sca_appearance_seize_index(sca_mod *scam, str *aor, int idx, str *owner_uri) { + sca_appearance *app; + int slot_idx; + int app_idx = -1; + int error = SCA_APPEARANCE_OK; - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - sca_hash_table_lock_index( scam->appearances, slot_idx ); + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + sca_hash_table_lock_index(scam->appearances, slot_idx); - app = sca_appearance_seize_index_unsafe( scam, aor, owner_uri, - idx, slot_idx, &error ); - if ( app != NULL ) { - app_idx = app->index; - } + app = sca_appearance_seize_index_unsafe(scam, aor, owner_uri, idx, slot_idx, + &error); + if (app != NULL) { + app_idx = app->index; + } - sca_hash_table_unlock_index( scam->appearances, slot_idx ); + sca_hash_table_unlock_index(scam->appearances, slot_idx); - if ( error == SCA_APPEARANCE_ERR_INDEX_UNAVAILABLE ) { - app_idx = SCA_APPEARANCE_INDEX_UNAVAILABLE; - } + if (error == SCA_APPEARANCE_ERR_INDEX_UNAVAILABLE) { + app_idx = SCA_APPEARANCE_INDEX_UNAVAILABLE; + } - return( app_idx ); + return (app_idx); } - sca_appearance * -sca_appearance_seize_next_available_unsafe( sca_mod *scam, str *aor, - str *owner_uri, int slot_idx ) -{ - sca_appearance_list *app_list; - sca_appearance *app = NULL; - sca_hash_slot *slot; - int idx = -1; - - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); - if ( app_list == NULL ) { - app_list = sca_appearance_list_create( scam, aor ); - if ( app_list == NULL ) { - goto done; - } - - if ( sca_hash_table_slot_kv_insert_unsafe( slot, app_list, - sca_appearance_list_aor_cmp, - sca_appearance_list_print, - sca_appearance_list_free ) < 0 ) { - LM_ERR( "Failed to insert appearance list for %.*s", - STR_FMT( aor )); - goto done; - } - } - - /* XXX this grows without bound. add modparam to set a hard limit */ - idx = sca_appearance_list_next_available_index_unsafe( app_list ); - /* XXX check idx > any configured max appearance index */ - - app = sca_appearance_create( idx, owner_uri ); - if ( app == NULL ) { - LM_ERR( "Failed to create new appearance for %.*s at index %d", - STR_FMT( owner_uri ), idx ); - goto done; - } - sca_appearance_update_state_unsafe( app, SCA_APPEARANCE_STATE_SEIZED ); - - sca_appearance_list_insert_appearance( app_list, app ); - -done: - return( app ); +sca_appearance * +sca_appearance_seize_next_available_unsafe(sca_mod *scam, str *aor, + str *owner_uri, int slot_idx) { + sca_appearance_list *app_list; + sca_appearance *app = NULL; + sca_hash_slot *slot; + int idx = -1; + + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); + if (app_list == NULL) { + app_list = sca_appearance_list_create(scam, aor); + if (app_list == NULL) { + goto done; + } + + if (sca_hash_table_slot_kv_insert_unsafe(slot, app_list, + sca_appearance_list_aor_cmp, sca_appearance_list_print, + sca_appearance_list_free) < 0) { + LM_ERR( "Failed to insert appearance list for %.*s\n", + STR_FMT( aor )); + goto done; + } + } + + /* XXX this grows without bound. add modparam to set a hard limit */ + idx = sca_appearance_list_next_available_index_unsafe(app_list); + /* XXX check idx > any configured max appearance index */ + + app = sca_appearance_create(idx, owner_uri); + if (app == NULL) { + LM_ERR( "Failed to create new appearance for %.*s at index %d\n", + STR_FMT( owner_uri ), idx ); + goto done; + } + sca_appearance_update_state_unsafe(app, SCA_APPEARANCE_STATE_SEIZED); + + sca_appearance_list_insert_appearance(app_list, app); + + done: return (app); } - int -sca_appearance_seize_next_available_index( sca_mod *scam, str *aor, - str *owner_uri ) -{ - sca_appearance *app; - int slot_idx; - int idx = -1; +int sca_appearance_seize_next_available_index(sca_mod *scam, str *aor, + str *owner_uri) { + sca_appearance *app; + int slot_idx; + int idx = -1; - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - sca_hash_table_lock_index( scam->appearances, slot_idx ); + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + sca_hash_table_lock_index(scam->appearances, slot_idx); - app = sca_appearance_seize_next_available_unsafe( scam, aor, - owner_uri, slot_idx ); - if ( app != NULL ) { - idx = app->index; - } + app = sca_appearance_seize_next_available_unsafe(scam, aor, owner_uri, + slot_idx); + if (app != NULL) { + idx = app->index; + } - sca_hash_table_unlock_index( scam->appearances, slot_idx ); + sca_hash_table_unlock_index(scam->appearances, slot_idx); - return( idx ); + return (idx); } - void -sca_appearance_update_state_unsafe( sca_appearance *app, int state ) -{ - assert( app != NULL ); +void sca_appearance_update_state_unsafe(sca_appearance *app, int state) { + assert(app != NULL); - app->state = state; - app->times.mtime = time( NULL ); + app->state = state; + app->times.mtime = time(NULL); } - int -sca_appearance_update_owner_unsafe( sca_appearance *app, str *owner ) -{ - assert( app != NULL ); - assert( owner != NULL ); - - if ( !SCA_STR_EMPTY( &app->owner )) { - if ( app->prev_owner.s != NULL ) { - shm_free( app->prev_owner.s ); - } - app->prev_owner.s = app->owner.s; - app->prev_owner.len = app->owner.len; - } - - app->owner.s = (char *)shm_malloc( owner->len ); - if ( app->owner.s == NULL ) { - LM_ERR( "sca_appearance_update_owner_unsafe: shm_malloc for new " - "owner %.*s failed: out of memory", STR_FMT( owner )); - goto error; - } - SCA_STR_COPY( &app->owner, owner ); - - return( 1 ); - -error: - /* restore owner */ - app->owner.s = app->prev_owner.s; - app->owner.len = app->prev_owner.len; - memset( &app->prev_owner, 0, sizeof( str )); - - return( -1 ); +int sca_appearance_update_owner_unsafe(sca_appearance *app, str *owner) { + assert(app != NULL); + assert(owner != NULL); + + if (!SCA_STR_EMPTY(&app->owner)) { + if (app->prev_owner.s != NULL) { + shm_free(app->prev_owner.s); + } + app->prev_owner.s = app->owner.s; + app->prev_owner.len = app->owner.len; + } + + app->owner.s = (char *) shm_malloc(owner->len); + if (app->owner.s == NULL) { + LM_ERR( "sca_appearance_update_owner_unsafe: shm_malloc for new " + "owner %.*s failed: out of memory\n", STR_FMT( owner )); + goto error; + } + SCA_STR_COPY(&app->owner, owner); + + return (1); + + error: + /* restore owner */ + app->owner.s = app->prev_owner.s; + app->owner.len = app->prev_owner.len; + memset(&app->prev_owner, 0, sizeof(str)); + + return (-1); } - int -sca_appearance_update_callee_unsafe( sca_appearance *app, str *callee ) -{ - assert( app != NULL ); - assert( callee != NULL ); - - if ( !SCA_STR_EMPTY( &app->callee )) { - if ( app->prev_callee.s != NULL ) { - shm_free( app->prev_callee.s ); - } - app->prev_callee.s = app->callee.s; - app->prev_callee.len = app->callee.len; - } - - app->callee.s = (char *)shm_malloc( callee->len ); - if ( app->callee.s == NULL ) { - LM_ERR( "sca_appearance_update_owner_unsafe: shm_malloc for new " - "callee %.*s failed: out of memory", STR_FMT( callee )); - goto error; - } - SCA_STR_COPY( &app->callee, callee ); - - return( 1 ); - -error: - /* restore callee */ - app->callee.s = app->prev_callee.s; - app->callee.len = app->prev_callee.len; - memset( &app->prev_callee, 0, sizeof( str )); - - return( -1 ); +int sca_appearance_update_callee_unsafe(sca_appearance *app, str *callee) { + assert(app != NULL); + assert(callee != NULL); + + if (!SCA_STR_EMPTY(&app->callee)) { + if (app->prev_callee.s != NULL) { + shm_free(app->prev_callee.s); + } + app->prev_callee.s = app->callee.s; + app->prev_callee.len = app->callee.len; + } + + app->callee.s = (char *) shm_malloc(callee->len); + if (app->callee.s == NULL) { + LM_ERR( "sca_appearance_update_owner_unsafe: shm_malloc for new " + "callee %.*s failed: out of memory\n", STR_FMT( callee )); + goto error; + } + SCA_STR_COPY(&app->callee, callee); + + return (1); + + error: + /* restore callee */ + app->callee.s = app->prev_callee.s; + app->callee.len = app->prev_callee.len; + memset(&app->prev_callee, 0, sizeof(str)); + + return (-1); } - int -sca_appearance_update_dialog_unsafe( sca_appearance *app, str *call_id, - str *from_tag, str *to_tag ) -{ - int len; +int sca_appearance_update_dialog_unsafe(sca_appearance *app, str *call_id, + str *from_tag, str *to_tag) { + int len; + + assert(app != NULL); + assert(call_id != NULL); + assert(from_tag != NULL); + + if (!SCA_STR_EMPTY(&app->dialog.id)) { + if (app->prev_dialog.id.s != NULL) { + shm_free(app->prev_dialog.id.s); + } + app->prev_dialog.id.s = app->dialog.id.s; + app->prev_dialog.id.len = app->dialog.id.len; - assert( app != NULL ); - assert( call_id != NULL ); - assert( from_tag != NULL ); + app->prev_dialog.call_id.s = app->dialog.call_id.s; + app->prev_dialog.call_id.len = app->dialog.call_id.len; - if ( !SCA_STR_EMPTY( &app->dialog.id )) { - if ( app->prev_dialog.id.s != NULL ) { - shm_free( app->prev_dialog.id.s ); + app->prev_dialog.from_tag.s = app->dialog.from_tag.s; + app->prev_dialog.from_tag.len = app->dialog.from_tag.len; + + app->prev_dialog.to_tag.s = app->dialog.to_tag.s; + app->prev_dialog.to_tag.len = app->dialog.to_tag.len; + } + + len = call_id->len + from_tag->len; + if (!SCA_STR_EMPTY(to_tag)) { + len += to_tag->len; } + + app->dialog.id.s = (char *) shm_malloc(len); + if (app->dialog.id.s == NULL) { + LM_ERR( "sca_appearance_update_dialog_unsafe: shm_malloc new dialog " + "failed: out of memory\n" ); + goto error; + } + SCA_STR_COPY(&app->dialog.id, call_id); + SCA_STR_APPEND(&app->dialog.id, from_tag); + + app->dialog.call_id.s = app->dialog.id.s; + app->dialog.call_id.len = call_id->len; + + app->dialog.from_tag.s = app->dialog.id.s + call_id->len; + app->dialog.from_tag.len = from_tag->len; + + app->dialog.to_tag.s = app->dialog.id.s + call_id->len + from_tag->len; + app->dialog.to_tag.len = to_tag->len; + + return (1); + + error: + /* restore dialog */ app->prev_dialog.id.s = app->dialog.id.s; app->prev_dialog.id.len = app->dialog.id.len; @@ -650,762 +636,692 @@ sca_appearance_update_dialog_unsafe( sca_appearance *app, str *call_id, app->prev_dialog.to_tag.s = app->dialog.to_tag.s; app->prev_dialog.to_tag.len = app->dialog.to_tag.len; - } - len = call_id->len + from_tag->len; - if ( !SCA_STR_EMPTY( to_tag )) { - len += to_tag->len; - } + memset(&app->prev_dialog, 0, sizeof(sca_dialog)); + + return (-1); +} + +int sca_appearance_update_unsafe(sca_appearance *app, int state, str *display, + str *uri, sca_dialog *dialog, str *owner, str *callee) { + int rc = SCA_APPEARANCE_OK; + int len; - app->dialog.id.s = (char *)shm_malloc( len ); - if ( app->dialog.id.s == NULL ) { - LM_ERR( "sca_appearance_update_dialog_unsafe: shm_malloc new dialog " - "failed: out of memory" ); - goto error; - } - SCA_STR_COPY( &app->dialog.id, call_id ); - SCA_STR_APPEND( &app->dialog.id, from_tag ); + if (state != SCA_APPEARANCE_STATE_UNKNOWN) { + sca_appearance_update_state_unsafe(app, state); + } - app->dialog.call_id.s = app->dialog.id.s; - app->dialog.call_id.len = call_id->len; + if (!SCA_STR_EMPTY(uri)) { + if (!SCA_STR_EMPTY(&app->uri)) { + /* the uri str's s member is shm_malloc'd separately */ + shm_free(app->uri.s); + memset(&app->uri, 0, sizeof(str)); + } - app->dialog.from_tag.s = app->dialog.id.s + call_id->len; - app->dialog.from_tag.len = from_tag->len; + /* +2 for left & right carets surrounding URI */ + len = uri->len + 2; + if (!SCA_STR_EMPTY(display)) { + /* cheaper to scan string than shm_malloc 2x display? */ + len += sca_uri_display_escapes_count(display); + /* +1 for space between display & uri */ + len += display->len + 1; + } + app->uri.s = (char *) shm_malloc(len); + if (app->uri.s == NULL) { + LM_ERR( "shm_malloc %d bytes returned NULL\n", uri->len ); + rc = SCA_APPEARANCE_ERR_MALLOC; + goto done; + } - app->dialog.to_tag.s = app->dialog.id.s + call_id->len + from_tag->len; - app->dialog.to_tag.len = to_tag->len; + if (!SCA_STR_EMPTY(display)) { + /* copy escaped display information... */ + app->uri.len = escape_common(app->uri.s, display->s, display->len); - return( 1 ); + /* ... and add a space between it and the uri */ + *(app->uri.s + app->uri.len) = ' '; + app->uri.len++; + } -error: - /* restore dialog */ - app->prev_dialog.id.s = app->dialog.id.s; - app->prev_dialog.id.len = app->dialog.id.len; + *(app->uri.s + app->uri.len) = '<'; + app->uri.len++; - app->prev_dialog.call_id.s = app->dialog.call_id.s; - app->prev_dialog.call_id.len = app->dialog.call_id.len; + SCA_STR_APPEND(&app->uri, uri); - app->prev_dialog.from_tag.s = app->dialog.from_tag.s; - app->prev_dialog.from_tag.len = app->dialog.from_tag.len; + *(app->uri.s + app->uri.len) = '>'; + app->uri.len++; + } - app->prev_dialog.to_tag.s = app->dialog.to_tag.s; - app->prev_dialog.to_tag.len = app->dialog.to_tag.len; + if (!SCA_DIALOG_EMPTY(dialog)) { + if (!SCA_STR_EQ(&dialog->id, &app->dialog.id)) { + if (app->dialog.id.s != NULL) { + shm_free(app->dialog.id.s); + } + + app->dialog.id.s = (char *) shm_malloc(dialog->id.len); + if (app->dialog.id.s == NULL) { + LM_ERR( "sca_appearance_update_unsafe: shm_malloc dialog id " + "failed: out of shared memory\n" ); + /* XXX this seems bad enough to abort... */ + return (-1); + } + SCA_STR_COPY(&app->dialog.id, &dialog->id); + + app->dialog.call_id.s = app->dialog.id.s; + app->dialog.call_id.len = dialog->call_id.len; + + app->dialog.from_tag.s = app->dialog.id.s + dialog->call_id.len; + app->dialog.from_tag.len = dialog->from_tag.len; + + if (!SCA_STR_EMPTY(&dialog->to_tag)) { + app->dialog.to_tag.s = app->dialog.id.s + dialog->call_id.len + + dialog->from_tag.len; + app->dialog.to_tag.len = dialog->to_tag.len; + } else { + app->dialog.to_tag.s = NULL; + app->dialog.to_tag.len = 0; + } + } + } - memset( &app->prev_dialog, 0, sizeof( sca_dialog )); + /* note these two blocks could be condensed and inlined */ + if (!SCA_STR_EMPTY(owner)) { + if (!SCA_STR_EQ(&app->owner, owner)) { + if (app->owner.s != NULL) { + shm_free(app->owner.s); + } + + app->owner.s = (char *) shm_malloc(owner->len); + if (app->owner.s == NULL) { + LM_ERR( "sca_appearance_update_unsafe: shm_malloc " + "appearance owner URI failed: out of shared memory\n" ); + return (-1); + } + SCA_STR_COPY(&app->owner, owner); + } + } - return( -1 ); -} + if (!SCA_STR_EMPTY(callee)) { + if (!SCA_STR_EQ(&app->callee, callee)) { + if (app->callee.s != NULL) { + shm_free(app->callee.s); + } + + app->callee.s = (char *) shm_malloc(callee->len); + if (app->callee.s == NULL) { + LM_ERR( "sca_appearance_update_unsafe: shm_malloc " + "appearance callee URI failed: out of shared memory\n" ); + return (-1); + } + SCA_STR_COPY(&app->callee, callee); + } + } - int -sca_appearance_update_unsafe( sca_appearance *app, int state, str *display, - str *uri, sca_dialog *dialog, str *owner, str *callee ) -{ - int rc = SCA_APPEARANCE_OK; - int len; - - if ( state != SCA_APPEARANCE_STATE_UNKNOWN ) { - sca_appearance_update_state_unsafe( app, state ); - } - - if ( !SCA_STR_EMPTY( uri )) { - if ( !SCA_STR_EMPTY( &app->uri )) { - /* the uri str's s member is shm_malloc'd separately */ - shm_free( app->uri.s ); - memset( &app->uri, 0, sizeof( str )); - } - - /* +2 for left & right carets surrounding URI */ - len = uri->len + 2; - if ( !SCA_STR_EMPTY( display )) { - /* cheaper to scan string than shm_malloc 2x display? */ - len += sca_uri_display_escapes_count( display ); - /* +1 for space between display & uri */ - len += display->len + 1; - } - app->uri.s = (char *)shm_malloc( len ); - if ( app->uri.s == NULL ) { - LM_ERR( "shm_malloc %d bytes returned NULL", uri->len ); - rc = SCA_APPEARANCE_ERR_MALLOC; - goto done; - } - - if ( !SCA_STR_EMPTY( display )) { - /* copy escaped display information... */ - app->uri.len = escape_common( app->uri.s, display->s, - display->len ); - - /* ... and add a space between it and the uri */ - *(app->uri.s + app->uri.len) = ' '; - app->uri.len++; - } - - *(app->uri.s + app->uri.len) = '<'; - app->uri.len++; - - SCA_STR_APPEND( &app->uri, uri ); - - *(app->uri.s + app->uri.len) = '>'; - app->uri.len++; - } - - if ( !SCA_DIALOG_EMPTY( dialog )) { - if ( !SCA_STR_EQ( &dialog->id, &app->dialog.id )) { - if ( app->dialog.id.s != NULL ) { - shm_free( app->dialog.id.s ); - } - - app->dialog.id.s = (char *)shm_malloc( dialog->id.len ); - if ( app->dialog.id.s == NULL ) { - LM_ERR( "sca_appearance_update_unsafe: shm_malloc dialog id " - "failed: out of shared memory" ); - /* XXX this seems bad enough to abort... */ - return( -1 ); - } - SCA_STR_COPY( &app->dialog.id, &dialog->id ); - - app->dialog.call_id.s = app->dialog.id.s; - app->dialog.call_id.len = dialog->call_id.len; - - app->dialog.from_tag.s = app->dialog.id.s + dialog->call_id.len; - app->dialog.from_tag.len = dialog->from_tag.len; - - if ( !SCA_STR_EMPTY( &dialog->to_tag )) { - app->dialog.to_tag.s = app->dialog.id.s + - dialog->call_id.len + - dialog->from_tag.len; - app->dialog.to_tag.len = dialog->to_tag.len; - } else { - app->dialog.to_tag.s = NULL; - app->dialog.to_tag.len = 0; - } - } - } - - /* note these two blocks could be condensed and inlined */ - if ( !SCA_STR_EMPTY( owner )) { - if ( !SCA_STR_EQ( &app->owner, owner )) { - if ( app->owner.s != NULL ) { - shm_free( app->owner.s ); - } - - app->owner.s = (char *)shm_malloc( owner->len ); - if ( app->owner.s == NULL ) { - LM_ERR( "sca_appearance_update_unsafe: shm_malloc " - "appearance owner URI failed: out of shared memory" ); - return( -1 ); - } - SCA_STR_COPY( &app->owner, owner ); - } - } - - if ( !SCA_STR_EMPTY( callee )) { - if ( !SCA_STR_EQ( &app->callee, callee )) { - if ( app->callee.s != NULL ) { - shm_free( app->callee.s ); - } - - app->callee.s = (char *)shm_malloc( callee->len ); - if ( app->callee.s == NULL ) { - LM_ERR( "sca_appearance_update_unsafe: shm_malloc " - "appearance callee URI failed: out of shared memory" ); - return( -1 ); - } - SCA_STR_COPY( &app->callee, callee ); - } - } - -done: - return( rc ); + done: return (rc); } - int -sca_uri_is_shared_appearance( sca_mod *scam, str *aor ) -{ - sca_hash_slot *slot; - sca_appearance_list *app_list; - int slot_idx; +int sca_uri_is_shared_appearance(sca_mod *scam, str *aor) { + sca_hash_slot *slot; + sca_appearance_list *app_list; + int slot_idx; - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); - sca_hash_table_lock_index( scam->appearances, slot_idx ); - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); - sca_hash_table_unlock_index( scam->appearances, slot_idx ); + sca_hash_table_lock_index(scam->appearances, slot_idx); + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); + sca_hash_table_unlock_index(scam->appearances, slot_idx); - if ( app_list == NULL ) { - return( 0 ); - } + if (app_list == NULL) { + return (0); + } - return( 1 ); + return (1); } - int -sca_uri_lock_shared_appearance( sca_mod *scam, str *aor ) -{ - sca_hash_slot *slot; - sca_appearance_list *app_list; - int slot_idx; +int sca_uri_lock_shared_appearance(sca_mod *scam, str *aor) { + sca_hash_slot *slot; + sca_appearance_list *app_list; + int slot_idx; - if ( SCA_STR_EMPTY( aor )) { - return( -1 ); - } + if (SCA_STR_EMPTY(aor)) { + return (-1); + } - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); - sca_hash_table_lock_index( scam->appearances, slot_idx ); - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); + sca_hash_table_lock_index(scam->appearances, slot_idx); + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); - if ( app_list == NULL ) { - sca_hash_table_unlock_index( scam->appearances, slot_idx ); - slot_idx = -1; - } + if (app_list == NULL) { + sca_hash_table_unlock_index(scam->appearances, slot_idx); + slot_idx = -1; + } - return( slot_idx ); + return (slot_idx); } - int -sca_uri_lock_if_shared_appearance( sca_mod *scam, str *aor, int *slot_idx ) -{ - sca_hash_slot *slot; - sca_appearance_list *app_list; +int sca_uri_lock_if_shared_appearance(sca_mod *scam, str *aor, int *slot_idx) { + sca_hash_slot *slot; + sca_appearance_list *app_list; - assert( slot_idx != NULL ); + assert(slot_idx != NULL); - if ( SCA_STR_EMPTY( aor )) { - *slot_idx = -1; - return( 0 ); - } + if (SCA_STR_EMPTY(aor)) { + *slot_idx = -1; + return (0); + } - *slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, *slot_idx ); + *slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + slot = sca_hash_table_slot_for_index(scam->appearances, *slot_idx); - sca_hash_table_lock_index( scam->appearances, *slot_idx ); - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); + sca_hash_table_lock_index(scam->appearances, *slot_idx); + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); - if ( app_list == NULL ) { - sca_hash_table_unlock_index( scam->appearances, *slot_idx ); - *slot_idx = -1; + if (app_list == NULL) { + sca_hash_table_unlock_index(scam->appearances, *slot_idx); + *slot_idx = -1; - return( 0 ); - } + return (0); + } - return( 1 ); + return (1); } - int -sca_appearance_state_for_index( sca_mod *scam, str *aor, int idx ) -{ - sca_hash_slot *slot; - sca_appearance_list *app_list; - sca_appearance *app; - int slot_idx; - int state = SCA_APPEARANCE_STATE_UNKNOWN; - - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - sca_hash_table_lock_index( scam->appearances, slot_idx ); - - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); - if ( app_list == NULL ) { - LM_DBG( "%.*s has no in-use appearances", STR_FMT( aor )); - goto done; - } - - for ( app = app_list->appearances; app != NULL; app = app->next ) { - if ( app->index == idx ) { - break; - } - } - if ( app == NULL ) { - LM_WARN( "%.*s appearance-index %d is not in use", - STR_FMT( aor ), idx ); - goto done; - } - - state = app->state; - -done: - sca_hash_table_unlock_index( scam->appearances, slot_idx ); - - return( state ); -} +int sca_appearance_state_for_index(sca_mod *scam, str *aor, int idx) { + sca_hash_slot *slot; + sca_appearance_list *app_list; + sca_appearance *app; + int slot_idx; + int state = SCA_APPEARANCE_STATE_UNKNOWN; - int -sca_appearance_update_index( sca_mod *scam, str *aor, int idx, - int state, str *display, str *uri, sca_dialog *dialog ) -{ - sca_hash_slot *slot; - sca_appearance_list *app_list; - sca_appearance *app; - str state_str = STR_NULL; - int len; - int slot_idx; - int rc = SCA_APPEARANCE_ERR_UNKNOWN; - - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - sca_hash_table_lock_index( scam->appearances, slot_idx ); - - sca_appearance_state_to_str( state, &state_str ); - - app_list = sca_hash_table_slot_kv_find_unsafe( slot, aor ); - if ( app_list == NULL ) { - LM_WARN( "Cannot update %.*s index %d to state %.*s: %.*s has no " - "in-use appearances", STR_FMT( aor ), idx, - STR_FMT( &state_str ), STR_FMT( aor )); - rc = SCA_APPEARANCE_ERR_NOT_IN_USE; - goto done; - } - - for ( app = app_list->appearances; app != NULL; app = app->next ) { - if ( app->index == idx ) { - break; - } else if ( idx == 0 ) { - if ( SCA_STR_EQ( &dialog->id, &app->dialog.id )) { - break; - } - } - } - if ( app == NULL ) { - LM_WARN( "Cannot update %.*s index %d to %.*s: index %d not in use", - STR_FMT( aor ), idx, STR_FMT( &state_str ), idx ); - rc = SCA_APPEARANCE_ERR_INDEX_INVALID; - goto done; - } - - if ( state != SCA_APPEARANCE_STATE_UNKNOWN && app->state != state ) { - sca_appearance_update_state_unsafe( app, state ); - } - - if ( !SCA_STR_EMPTY( uri )) { - if ( !SCA_STR_EMPTY( &app->uri )) { - /* the uri str's s member is shm_malloc'd separately */ - shm_free( app->uri.s ); - memset( &app->uri, 0, sizeof( str )); - } - - /* +2 for left & right carets surrounding URI */ - len = uri->len + 2; - if ( !SCA_STR_EMPTY( display )) { - /* cheaper to scan string than shm_malloc 2x display? */ - len += sca_uri_display_escapes_count( display ); - /* +1 for space between display & uri */ - len += display->len + 1; - } - app->uri.s = (char *)shm_malloc( len ); - if ( app->uri.s == NULL ) { - LM_ERR( "Failed to update %.*s index %d uri to %.*s: " - "shm_malloc %d bytes returned NULL", - STR_FMT( aor ), idx, STR_FMT( uri ), uri->len ); - rc = SCA_APPEARANCE_ERR_MALLOC; - goto done; - } - - if ( !SCA_STR_EMPTY( display )) { - /* copy escaped display information... */ - app->uri.len = escape_common( app->uri.s, display->s, - display->len ); - - /* ... and add a space between it and the uri */ - *(app->uri.s + app->uri.len) = ' '; - app->uri.len++; - } - - *(app->uri.s + app->uri.len) = '<'; - app->uri.len++; - - SCA_STR_APPEND( &app->uri, uri ); - - *(app->uri.s + app->uri.len) = '>'; - app->uri.len++; - } - - if ( !SCA_DIALOG_EMPTY( dialog )) { - if ( !SCA_STR_EQ( &dialog->id, &app->dialog.id )) { - if ( app->dialog.id.s != NULL ) { - shm_free( app->dialog.id.s ); - } - - app->dialog.id.s = (char *)shm_malloc( dialog->id.len ); - SCA_STR_COPY( &app->dialog.id, &dialog->id ); - - app->dialog.call_id.s = app->dialog.id.s; - app->dialog.call_id.len = dialog->call_id.len; - - app->dialog.from_tag.s = app->dialog.id.s + dialog->call_id.len; - app->dialog.from_tag.len = dialog->from_tag.len; - - if ( !SCA_STR_EMPTY( &dialog->to_tag )) { - app->dialog.to_tag.s = app->dialog.id.s + - dialog->call_id.len + - dialog->from_tag.len; - app->dialog.to_tag.len = dialog->to_tag.len; - } else { - app->dialog.to_tag.s = NULL; - app->dialog.to_tag.len = 0; - } - } - } - - rc = SCA_APPEARANCE_OK; - -done: - sca_hash_table_unlock_index( scam->appearances, slot_idx ); - - return( rc ); -} + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + sca_hash_table_lock_index(scam->appearances, slot_idx); + + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); + if (app_list == NULL) { + LM_DBG( "%.*s has no in-use appearances\n", STR_FMT( aor )); + goto done; + } + + for (app = app_list->appearances; app != NULL; app = app->next) { + if (app->index == idx) { + break; + } + } + if (app == NULL) { + LM_WARN( "%.*s appearance-index %d is not in use\n", + STR_FMT( aor ), idx ); + goto done; + } + + state = app->state; - int -sca_appearance_release_index( sca_mod *scam, str *aor, int idx ) -{ - sca_hash_slot *slot; - sca_hash_entry *ent; - sca_appearance_list *app_list = NULL; - sca_appearance *app; - int slot_idx; - int rc = SCA_APPEARANCE_ERR_UNKNOWN; - - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - sca_hash_table_lock_index( scam->appearances, slot_idx ); - - app_list = NULL; - for ( ent = slot->entries; ent != NULL; ent = ent->next ) { - if ( ent->compare( aor, ent->value ) == 0 ) { - app_list = (sca_appearance_list *)ent->value; - break; - } - } - if ( app_list == NULL ) { - LM_ERR( "No appearances for %.*s", STR_FMT( aor )); - rc = SCA_APPEARANCE_ERR_NOT_IN_USE; - goto done; - } - - app = sca_appearance_list_unlink_index( app_list, idx ); - if ( app == NULL ) { - LM_ERR( "Failed to unlink %.*s appearance-index %d: invalid index", - STR_FMT( aor ), idx ); - rc = SCA_APPEARANCE_ERR_INDEX_INVALID; - goto done; - } - sca_appearance_free( app ); - - rc = SCA_APPEARANCE_OK; - -done: - sca_hash_table_unlock_index( scam->appearances, slot_idx ); - - return( rc ); + done: + sca_hash_table_unlock_index(scam->appearances, slot_idx); + + return (state); } - int -sca_appearance_owner_release_all( str *aor, str *owner ) -{ - sca_appearance_list *app_list = NULL; - sca_appearance *app, **cur_app, **tmp_app; - sca_hash_slot *slot; - sca_hash_entry *ent; - int slot_idx = -1; - int released = -1; +int sca_appearance_update_index(sca_mod *scam, str *aor, int idx, int state, + str *display, str *uri, sca_dialog *dialog) { + sca_hash_slot *slot; + sca_appearance_list *app_list; + sca_appearance *app; + str state_str = STR_NULL; + int len; + int slot_idx; + int rc = SCA_APPEARANCE_ERR_UNKNOWN; + + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + sca_hash_table_lock_index(scam->appearances, slot_idx); + + sca_appearance_state_to_str(state, &state_str); + + app_list = sca_hash_table_slot_kv_find_unsafe(slot, aor); + if (app_list == NULL) { + LM_WARN( "Cannot update %.*s index %d to state %.*s: %.*s has no " + "in-use appearances\n", STR_FMT( aor ), idx, + STR_FMT( &state_str ), STR_FMT( aor )); + rc = SCA_APPEARANCE_ERR_NOT_IN_USE; + goto done; + } - slot_idx = sca_uri_lock_shared_appearance( sca, aor ); - slot = sca_hash_table_slot_for_index( sca->appearances, slot_idx ); + for (app = app_list->appearances; app != NULL; app = app->next) { + if (app->index == idx) { + break; + } else if (idx == 0) { + if (SCA_STR_EQ(&dialog->id, &app->dialog.id)) { + break; + } + } + } + if (app == NULL) { + LM_WARN( "Cannot update %.*s index %d to %.*s: index %d not in use\n", + STR_FMT( aor ), idx, STR_FMT( &state_str ), idx ); + rc = SCA_APPEARANCE_ERR_INDEX_INVALID; + goto done; + } - for ( ent = slot->entries; ent != NULL; ent = ent->next ) { - if ( ent->compare( aor, ent->value ) == 0 ) { - app_list = (sca_appearance_list *)ent->value; - break; + if (state != SCA_APPEARANCE_STATE_UNKNOWN && app->state != state) { + sca_appearance_update_state_unsafe(app, state); } - } - released = 0; + if (!SCA_STR_EMPTY(uri)) { + if (!SCA_STR_EMPTY(&app->uri)) { + /* the uri str's s member is shm_malloc'd separately */ + shm_free(app->uri.s); + memset(&app->uri, 0, sizeof(str)); + } - if ( app_list == NULL ) { - LM_DBG( "sca_appearance_owner_release_all: No appearances for %.*s", - STR_FMT( aor )); - goto done; - } + /* +2 for left & right carets surrounding URI */ + len = uri->len + 2; + if (!SCA_STR_EMPTY(display)) { + /* cheaper to scan string than shm_malloc 2x display? */ + len += sca_uri_display_escapes_count(display); + /* +1 for space between display & uri */ + len += display->len + 1; + } + app->uri.s = (char *) shm_malloc(len); + if (app->uri.s == NULL) { + LM_ERR( "Failed to update %.*s index %d uri to %.*s: " + "shm_malloc %d bytes returned NULL\n", + STR_FMT( aor ), idx, STR_FMT( uri ), uri->len ); + rc = SCA_APPEARANCE_ERR_MALLOC; + goto done; + } - for ( cur_app = &app_list->appearances; *cur_app != NULL; - cur_app = tmp_app ) { - tmp_app = &(*cur_app)->next; + if (!SCA_STR_EMPTY(display)) { + /* copy escaped display information... */ + app->uri.len = escape_common(app->uri.s, display->s, display->len); - if ( !SCA_STR_EQ( owner, &(*cur_app)->owner )) { - continue; - } + /* ... and add a space between it and the uri */ + *(app->uri.s + app->uri.len) = ' '; + app->uri.len++; + } + + *(app->uri.s + app->uri.len) = '<'; + app->uri.len++; - app = *cur_app; - *cur_app = (*cur_app)->next; - tmp_app = cur_app; + SCA_STR_APPEND(&app->uri, uri); - if ( app ) { - sca_appearance_free( app ); - released++; + *(app->uri.s + app->uri.len) = '>'; + app->uri.len++; } - } -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } + if (!SCA_DIALOG_EMPTY(dialog)) { + if (!SCA_STR_EQ(&dialog->id, &app->dialog.id)) { + if (app->dialog.id.s != NULL) { + shm_free(app->dialog.id.s); + } + + app->dialog.id.s = (char *) shm_malloc(dialog->id.len); + SCA_STR_COPY(&app->dialog.id, &dialog->id); + + app->dialog.call_id.s = app->dialog.id.s; + app->dialog.call_id.len = dialog->call_id.len; + + app->dialog.from_tag.s = app->dialog.id.s + dialog->call_id.len; + app->dialog.from_tag.len = dialog->from_tag.len; + + if (!SCA_STR_EMPTY(&dialog->to_tag)) { + app->dialog.to_tag.s = app->dialog.id.s + dialog->call_id.len + + dialog->from_tag.len; + app->dialog.to_tag.len = dialog->to_tag.len; + } else { + app->dialog.to_tag.s = NULL; + app->dialog.to_tag.len = 0; + } + } + } - return( released ); -} + rc = SCA_APPEARANCE_OK; - sca_appearance * -sca_appearance_for_index_unsafe( sca_mod *scam, str *aor, int app_idx, - int slot_idx ) -{ - sca_appearance_list *app_list; - sca_appearance *app = NULL; - sca_hash_slot *slot; - sca_hash_entry *ent; - - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - app_list = NULL; - for ( ent = slot->entries; ent != NULL; ent = ent->next ) { - if ( ent->compare( aor, ent->value ) == 0 ) { - app_list = (sca_appearance_list *)ent->value; - break; - } - } - if ( app_list == NULL ) { - LM_ERR( "No appearances for %.*s", STR_FMT( aor )); - return( NULL ); - } - - for ( app = app_list->appearances; app != NULL; app = app->next ) { - if ( app->index == app_idx ) { - break; - } - } - - return( app ); -} + done: + sca_hash_table_unlock_index(scam->appearances, slot_idx); - sca_appearance * -sca_appearance_for_dialog_unsafe( sca_mod *scam, str *aor, sca_dialog *dialog, - int slot_idx ) -{ - sca_appearance_list *app_list; - sca_appearance *app = NULL; - sca_hash_slot *slot; - sca_hash_entry *ent; - - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); - - app_list = NULL; - for ( ent = slot->entries; ent != NULL; ent = ent->next ) { - if ( ent->compare( aor, ent->value ) == 0 ) { - app_list = (sca_appearance_list *)ent->value; - break; - } - } - if ( app_list == NULL ) { - LM_ERR( "No appearances for %.*s", STR_FMT( aor )); - return( NULL ); - } - - for ( app = app_list->appearances; app != NULL; app = app->next ) { - if ( SCA_STR_EQ( &app->dialog.call_id, &dialog->call_id ) && - SCA_STR_EQ( &app->dialog.from_tag, &dialog->from_tag )) { -#ifdef notdef - if ( !SCA_STR_EMPTY( &app->dialog.to_tag ) && - !SCA_STR_EMPTY( &dialog->to_tag ) && - !SCA_STR_EQ( &app->dialog.to_tag, &dialog->to_tag )) { - continue; - } -#endif /* notdef */ - break; - } - } - - return( app ); -} - - sca_appearance * -sca_appearance_for_tags_unsafe( sca_mod *scam, str *aor, - str *call_id, str *from_tag, str *to_tag, int slot_idx ) -{ - sca_dialog dialog; - char dlg_buf[ 1024 ]; - - dialog.id.s = dlg_buf; - if ( sca_dialog_build_from_tags( &dialog, sizeof( dlg_buf ), - call_id, from_tag, to_tag ) < 0 ) { - LM_ERR( "sca_appearance_for_tags_unsafe: failed to build dialog " - "from tags" ); - return( NULL ); - } - - return( sca_appearance_for_dialog_unsafe( scam, aor, &dialog, slot_idx )); + return (rc); } - sca_appearance * -sca_appearance_unlink_by_tags( sca_mod *scam, str *aor, - str *call_id, str *from_tag, str *to_tag ) -{ - sca_appearance *app = NULL, *unl_app; - int slot_idx = -1; - - slot_idx = sca_hash_table_index_for_key( scam->appearances, aor ); - sca_hash_table_lock_index( scam->appearances, slot_idx ); - - app = sca_appearance_for_tags_unsafe( scam, aor, call_id, from_tag, - to_tag, slot_idx ); - if ( app == NULL ) { - LM_ERR( "sca_appearance_unlink_by_tags: no appearances found for %.*s " - "with dialog %.*s;%.*s;%.*s", STR_FMT( aor ), - STR_FMT( call_id ), STR_FMT( from_tag ), STR_FMT( to_tag )); - goto done; - } - - unl_app = sca_appearance_list_unlink_index( app->appearance_list, - app->index ); - if ( unl_app == NULL || unl_app != app ) { - LM_ERR( "sca_appearance_unlink_by_tags: failed to unlink %.*s " - "appearance-index %d", STR_FMT( aor ), app->index ); - app = NULL; - goto done; - } - -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( scam->appearances, slot_idx ); - } - - return( app ); -} +int sca_appearance_release_index(sca_mod *scam, str *aor, int idx) { + sca_hash_slot *slot; + sca_hash_entry *ent; + sca_appearance_list *app_list = NULL; + sca_appearance *app; + int slot_idx; + int rc = SCA_APPEARANCE_ERR_UNKNOWN; - void -sca_appearance_purge_stale( unsigned int ticks, void *param ) -{ - struct notify_list { - struct notify_list *next; - str aor; - }; - - sca_mod *scam = (sca_mod *)param; - sca_hash_table *ht; - sca_hash_entry *ent; - sca_appearance_list *app_list; - sca_appearance **cur_app, **tmp_app, *app = NULL; - struct notify_list *notify_list = NULL, *tmp_nl; - int i; - int unlinked; - time_t now, ttl; - - LM_INFO( "SCA: purging stale appearances" ); - - assert( scam != NULL ); - assert( scam->appearances != NULL ); - - now = time( NULL ); - - ht = scam->appearances; - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); - - for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent->next ) { - app_list = (sca_appearance_list *)ent->value; - if ( app_list == NULL ) { - continue; - } - - unlinked = 0; - - for ( cur_app = &app_list->appearances; *cur_app != NULL; - cur_app = tmp_app ) { - tmp_app = &(*cur_app)->next; + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); - switch ((*cur_app)->state ) { - case SCA_APPEARANCE_STATE_ACTIVE_PENDING: - ttl = SCA_APPEARANCE_STATE_PENDING_TTL; - break; + sca_hash_table_lock_index(scam->appearances, slot_idx); - case SCA_APPEARANCE_STATE_SEIZED: - ttl = SCA_APPEARANCE_STATE_SEIZED_TTL; - break; + app_list = NULL; + for (ent = slot->entries; ent != NULL; ent = ent->next) { + if (ent->compare(aor, ent->value) == 0) { + app_list = (sca_appearance_list *) ent->value; + break; + } + } + if (app_list == NULL) { + LM_ERR( "No appearances for %.*s\n", STR_FMT( aor )); + rc = SCA_APPEARANCE_ERR_NOT_IN_USE; + goto done; + } + + app = sca_appearance_list_unlink_index(app_list, idx); + if (app == NULL) { + LM_ERR( "Failed to unlink %.*s appearance-index %d: invalid index\n", + STR_FMT( aor ), idx ); + rc = SCA_APPEARANCE_ERR_INDEX_INVALID; + goto done; + } + sca_appearance_free(app); - default: - /* XXX for now just skip other appearances */ - ttl = now + 60; - break; + rc = SCA_APPEARANCE_OK; + + done: + sca_hash_table_unlock_index(scam->appearances, slot_idx); + + return (rc); +} + +int sca_appearance_owner_release_all(str *aor, str *owner) { + sca_appearance_list *app_list = NULL; + sca_appearance *app, **cur_app, **tmp_app; + sca_hash_slot *slot; + sca_hash_entry *ent; + int slot_idx = -1; + int released = -1; + + slot_idx = sca_uri_lock_shared_appearance(sca, aor); + slot = sca_hash_table_slot_for_index(sca->appearances, slot_idx); + + for (ent = slot->entries; ent != NULL; ent = ent->next) { + if (ent->compare(aor, ent->value) == 0) { + app_list = (sca_appearance_list *) ent->value; + break; } - if (( now - (*cur_app)->times.mtime ) < ttl ) { - continue; + } + + released = 0; + + if (app_list == NULL) { + LM_DBG( "sca_appearance_owner_release_all: No appearances for %.*s\n", + STR_FMT( aor )); + goto done; + } + + for (cur_app = &app_list->appearances; *cur_app != NULL; cur_app = + tmp_app) { + tmp_app = &(*cur_app)->next; + + if (!SCA_STR_EQ(owner, &(*cur_app)->owner)) { + continue; } - /* unlink stale appearance */ app = *cur_app; *cur_app = (*cur_app)->next; tmp_app = cur_app; - if ( app ) { - sca_appearance_free( app ); + if (app) { + sca_appearance_free(app); + released++; } + } + + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } + + return (released); +} - if ( unlinked ) { - /* we've already added this AoR to the NOTIFY list */ - continue; +sca_appearance * +sca_appearance_for_index_unsafe(sca_mod *scam, str *aor, int app_idx, + int slot_idx) { + sca_appearance_list *app_list; + sca_appearance *app = NULL; + sca_hash_slot *slot; + sca_hash_entry *ent; + + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + app_list = NULL; + for (ent = slot->entries; ent != NULL; ent = ent->next) { + if (ent->compare(aor, ent->value) == 0) { + app_list = (sca_appearance_list *) ent->value; + break; } - unlinked++; - - /* - * can't notify while slot is locked. make a list of AoRs to - * notify after unlocking. - */ - tmp_nl = (struct notify_list *)pkg_malloc( - sizeof( struct notify_list )); - if ( tmp_nl == NULL ) { - LM_ERR( "sca_appearance_purge_stale: failed to pkg_malloc " - "notify list entry for %.*s", - STR_FMT( &app_list->aor )); - continue; + } + if (app_list == NULL) { + LM_ERR( "No appearances for %.*s\n", STR_FMT( aor )); + return (NULL); + } + + for (app = app_list->appearances; app != NULL; app = app->next) { + if (app->index == app_idx) { + break; } + } - tmp_nl->aor.s = (char *)pkg_malloc( app_list->aor.len ); - if ( tmp_nl->aor.s == NULL ) { - LM_ERR( "sca_appearance_purge_stale: failed to pkg_malloc " - "space for copy of %.*s", - STR_FMT( &app_list->aor )); - pkg_free( tmp_nl ); - continue; + return (app); +} + +sca_appearance * +sca_appearance_for_dialog_unsafe(sca_mod *scam, str *aor, sca_dialog *dialog, + int slot_idx) { + sca_appearance_list *app_list; + sca_appearance *app = NULL; + sca_hash_slot *slot; + sca_hash_entry *ent; + + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + app_list = NULL; + for (ent = slot->entries; ent != NULL; ent = ent->next) { + if (ent->compare(aor, ent->value) == 0) { + app_list = (sca_appearance_list *) ent->value; + break; + } + } + if (app_list == NULL) { + LM_ERR( "No appearances for %.*s\n", STR_FMT( aor )); + return (NULL); + } + + for (app = app_list->appearances; app != NULL; app = app->next) { + if ( SCA_STR_EQ(&app->dialog.call_id, &dialog->call_id) && + SCA_STR_EQ( &app->dialog.from_tag, &dialog->from_tag )) { +#ifdef notdef + if ( !SCA_STR_EMPTY( &app->dialog.to_tag ) && + !SCA_STR_EMPTY( &dialog->to_tag ) && + !SCA_STR_EQ( &app->dialog.to_tag, &dialog->to_tag )) { + continue; + } +#endif /* notdef */ + break; } - SCA_STR_COPY( &tmp_nl->aor, &app_list->aor ); + } + + return (app); +} + +sca_appearance * +sca_appearance_for_tags_unsafe(sca_mod *scam, str *aor, str *call_id, + str *from_tag, str *to_tag, int slot_idx) { + sca_dialog dialog; + char dlg_buf[1024]; + + dialog.id.s = dlg_buf; + if (sca_dialog_build_from_tags(&dialog, sizeof(dlg_buf), call_id, from_tag, + to_tag) < 0) { + LM_ERR( "sca_appearance_for_tags_unsafe: failed to build dialog " + "from tags\n" ); + return (NULL); + } + + return (sca_appearance_for_dialog_unsafe(scam, aor, &dialog, slot_idx)); +} + +sca_appearance * +sca_appearance_unlink_by_tags(sca_mod *scam, str *aor, str *call_id, + str *from_tag, str *to_tag) { + sca_appearance *app = NULL, *unl_app; + int slot_idx = -1; + + slot_idx = sca_hash_table_index_for_key(scam->appearances, aor); + sca_hash_table_lock_index(scam->appearances, slot_idx); + + app = sca_appearance_for_tags_unsafe(scam, aor, call_id, from_tag, to_tag, + slot_idx); + if (app == NULL) { + LM_ERR( "sca_appearance_unlink_by_tags: no appearances found for %.*s " + "with dialog %.*s;%.*s;%.*s\n", STR_FMT( aor ), + STR_FMT( call_id ), STR_FMT( from_tag ), STR_FMT( to_tag )); + goto done; + } + + unl_app = sca_appearance_list_unlink_index(app->appearance_list, + app->index); + if (unl_app == NULL || unl_app != app) { + LM_ERR( "sca_appearance_unlink_by_tags: failed to unlink %.*s " + "appearance-index %d\n", STR_FMT( aor ), app->index ); + app = NULL; + goto done; + } - /* simple insert-at-head. order doesn't matter. */ - tmp_nl->next = notify_list; - notify_list = tmp_nl; - } + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(scam->appearances, slot_idx); } - sca_hash_table_unlock_index( ht, i ); + return (app); +} + +void sca_appearance_purge_stale(unsigned int ticks, void *param) { + struct notify_list { + struct notify_list *next; + str aor; + }; + + sca_mod *scam = (sca_mod *) param; + sca_hash_table *ht; + sca_hash_entry *ent; + sca_appearance_list *app_list; + sca_appearance **cur_app, **tmp_app, *app = NULL; + struct notify_list *notify_list = NULL, *tmp_nl; + int i; + int unlinked; + time_t now, ttl; + + LM_INFO( "SCA: purging stale appearances\n" ); + + assert(scam != NULL); + assert(scam->appearances != NULL); + + now = time(NULL); + + ht = scam->appearances; + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); + + for (ent = ht->slots[i].entries; ent != NULL; ent = ent->next) { + app_list = (sca_appearance_list *) ent->value; + if (app_list == NULL) { + continue; + } + + unlinked = 0; + + for (cur_app = &app_list->appearances; *cur_app != NULL; cur_app = + tmp_app) { + tmp_app = &(*cur_app)->next; + + switch ((*cur_app)->state) { + case SCA_APPEARANCE_STATE_ACTIVE_PENDING: + ttl = SCA_APPEARANCE_STATE_PENDING_TTL; + break; + + case SCA_APPEARANCE_STATE_SEIZED: + ttl = SCA_APPEARANCE_STATE_SEIZED_TTL; + break; + + default: + /* XXX for now just skip other appearances */ + ttl = now + 60; + break; + } + if ((now - (*cur_app)->times.mtime) < ttl) { + continue; + } + + /* unlink stale appearance */ + app = *cur_app; + *cur_app = (*cur_app)->next; + tmp_app = cur_app; + + if (app) { + sca_appearance_free(app); + } + + if (unlinked) { + /* we've already added this AoR to the NOTIFY list */ + continue; + } + unlinked++; + + /* + * can't notify while slot is locked. make a list of AoRs to + * notify after unlocking. + */ + tmp_nl = (struct notify_list *) pkg_malloc( + sizeof(struct notify_list)); + if (tmp_nl == NULL) { + LM_ERR( "sca_appearance_purge_stale: failed to pkg_malloc " + "notify list entry for %.*s\n", + STR_FMT( &app_list->aor )); + continue; + } + + tmp_nl->aor.s = (char *) pkg_malloc(app_list->aor.len); + if (tmp_nl->aor.s == NULL) { + LM_ERR( "sca_appearance_purge_stale: failed to pkg_malloc " + "space for copy of %.*s\n", + STR_FMT( &app_list->aor )); + pkg_free(tmp_nl); + continue; + } + SCA_STR_COPY(&tmp_nl->aor, &app_list->aor); + + /* simple insert-at-head. order doesn't matter. */ + tmp_nl->next = notify_list; + notify_list = tmp_nl; + } + } + + sca_hash_table_unlock_index(ht, i); - for ( ; notify_list != NULL; notify_list = tmp_nl ) { - tmp_nl = notify_list->next; + for (; notify_list != NULL; notify_list = tmp_nl) { + tmp_nl = notify_list->next; - LM_INFO( "sca_appearance_purge_stale: notifying %.*s call-info " - "subscribers", STR_FMT( ¬ify_list->aor )); + LM_INFO( "sca_appearance_purge_stale: notifying %.*s call-info " + "subscribers\n", STR_FMT( ¬ify_list->aor )); - if ( sca_notify_call_info_subscribers( scam, - ¬ify_list->aor ) < 0 ) { - LM_ERR( "sca_appearance_purge_stale: failed to send " - "call-info NOTIFY %.*s subscribers", - STR_FMT( ¬ify_list->aor )); - /* fall through, free memory anyway */ - } + if (sca_notify_call_info_subscribers(scam, ¬ify_list->aor) < 0) { + LM_ERR( "sca_appearance_purge_stale: failed to send " + "call-info NOTIFY %.*s subscribers\n", + STR_FMT( ¬ify_list->aor )); + /* fall through, free memory anyway */ + } - if ( notify_list->aor.s ) { - pkg_free( notify_list->aor.s ); - } - pkg_free( notify_list ); + if (notify_list->aor.s) { + pkg_free(notify_list->aor.s); + } + pkg_free(notify_list); + } } - } } diff --git a/modules/sca/sca_appearance.h b/modules/sca/sca_appearance.h index 34451832393..f4338c9d066 100644 --- a/modules/sca/sca_appearance.h +++ b/modules/sca/sca_appearance.h @@ -25,33 +25,33 @@ #include "sca_dialog.h" enum { - SCA_APPEARANCE_STATE_IDLE = 0, - SCA_APPEARANCE_STATE_SEIZED, - SCA_APPEARANCE_STATE_PROGRESSING, - SCA_APPEARANCE_STATE_ALERTING, - SCA_APPEARANCE_STATE_ACTIVE_PENDING, - SCA_APPEARANCE_STATE_ACTIVE, - SCA_APPEARANCE_STATE_HELD, - SCA_APPEARANCE_STATE_HELD_PRIVATE, - SCA_APPEARANCE_STATE_UNKNOWN = 0xff, -}; + SCA_APPEARANCE_STATE_IDLE = 0, + SCA_APPEARANCE_STATE_SEIZED, + SCA_APPEARANCE_STATE_PROGRESSING, + SCA_APPEARANCE_STATE_ALERTING, + SCA_APPEARANCE_STATE_ACTIVE_PENDING, + SCA_APPEARANCE_STATE_ACTIVE, + SCA_APPEARANCE_STATE_HELD, + SCA_APPEARANCE_STATE_HELD_PRIVATE, + SCA_APPEARANCE_STATE_UNKNOWN = 0xff, +}; #define sca_appearance_is_held( app1 ) \ ((app1) && ((app1)->state == SCA_APPEARANCE_STATE_HELD || \ - (app1)->state == SCA_APPEARANCE_STATE_HELD_PRIVATE)) + (app1)->state == SCA_APPEARANCE_STATE_HELD_PRIVATE)) enum { - SCA_APPEARANCE_FLAG_DEFAULT = 0, - SCA_APPEARANCE_FLAG_OWNER_PENDING = (1 << 0), - SCA_APPEARANCE_FLAG_CALLEE_PENDING = (1 << 1), + SCA_APPEARANCE_FLAG_DEFAULT = 0, + SCA_APPEARANCE_FLAG_OWNER_PENDING = (1 << 0), + SCA_APPEARANCE_FLAG_CALLEE_PENDING = (1 << 1), }; enum { - SCA_APPEARANCE_OK = 0, - SCA_APPEARANCE_ERR_NOT_IN_USE = 0x1001, - SCA_APPEARANCE_ERR_INDEX_INVALID = 0x1002, - SCA_APPEARANCE_ERR_INDEX_UNAVAILABLE = 0x1004, - SCA_APPEARANCE_ERR_MALLOC = 0x1008, - SCA_APPEARANCE_ERR_UNKNOWN = 0x1f00, + SCA_APPEARANCE_OK = 0, + SCA_APPEARANCE_ERR_NOT_IN_USE = 0x1001, + SCA_APPEARANCE_ERR_INDEX_INVALID = 0x1002, + SCA_APPEARANCE_ERR_INDEX_UNAVAILABLE = 0x1004, + SCA_APPEARANCE_ERR_MALLOC = 0x1008, + SCA_APPEARANCE_ERR_UNKNOWN = 0x1f00, }; #define SCA_APPEARANCE_INDEX_UNAVAILABLE -2 @@ -62,11 +62,8 @@ enum { * the SCA callee's state to active. */ enum { - /* Polycoms aggressively resubscribe line-seizes, give them time */ - SCA_APPEARANCE_STATE_SEIZED_TTL = 120, - - /* enough time to allow retransmissions (~32s) */ - SCA_APPEARANCE_STATE_PENDING_TTL = 35, + SCA_APPEARANCE_STATE_SEIZED_TTL = 120, /* Polycoms aggressively resubscribe line-seizes, give them time */ + SCA_APPEARANCE_STATE_PENDING_TTL = 35, /* enough time to allow retransmissions (~32s) */ }; extern const str SCA_APPEARANCE_INDEX_STR; @@ -81,91 +78,77 @@ extern const str SCA_APPEARANCE_STATE_STR_ACTIVE; extern const str SCA_APPEARANCE_STATE_STR_HELD; extern const str SCA_APPEARANCE_STATE_STR_HELD_PRIVATE; - struct _sca_appearance_times { - /* time of appearance creation */ - time_t ctime; - - /* time of last appearance state change */ - time_t mtime; + time_t ctime; /* time of appearance creation */ + time_t mtime; /* time of last appearance state change */ + time_t atime; /* time of last end-to-end activity */ - /* time of last end-to-end activity */ - time_t atime; }; -typedef struct _sca_appearance_times sca_appearance_times; +typedef struct _sca_appearance_times sca_appearance_times; struct _sca_appearance_list; struct _sca_appearance { - int index; - int state; - str uri; - - int flags; - - str owner; - str callee; - sca_dialog dialog; - sca_appearance_times times; - - str prev_owner; - str prev_callee; - sca_dialog prev_dialog; - - struct _sca_appearance_list *appearance_list; - struct _sca_appearance *next; + int index; + int state; + str uri; + int flags; + str owner; + str callee; + sca_dialog dialog; + sca_appearance_times times; + str prev_owner; + str prev_callee; + sca_dialog prev_dialog; + struct _sca_appearance_list *appearance_list; + struct _sca_appearance *next; }; -typedef struct _sca_appearance sca_appearance; +typedef struct _sca_appearance sca_appearance; struct _sca_appearance_list { - str aor; - int appearance_count; - sca_appearance *appearances; + str aor; + int appearance_count; + sca_appearance *appearances; }; -typedef struct _sca_appearance_list sca_appearance_list; - -void sca_appearance_state_to_str( int, str * ); -int sca_appearance_state_from_str( str * ); - -sca_appearance *sca_appearance_seize_index_unsafe( sca_mod *, str *, str *, - int, int, int * ); -int sca_appearance_seize_index( sca_mod *, str *, int, str * ); -int sca_appearance_seize_next_available_index( sca_mod *, str *, str * ); -sca_appearance *sca_appearance_seize_next_available_unsafe( sca_mod *, str *, - str *, int ); -void sca_appearance_update_state_unsafe( sca_appearance *, int ); -int sca_appearance_update_owner_unsafe( sca_appearance *, str * ); -int sca_appearance_update_callee_unsafe( sca_appearance *, str * ); -int sca_appearance_update_dialog_unsafe( sca_appearance *, str *, - str *, str * ); -int sca_appearance_update_unsafe( sca_appearance *, int, str *, str *, - sca_dialog *, str *, str * ); -int sca_appearance_update_index( sca_mod *, str *, int, int, str *, - str *, sca_dialog * ); -int sca_appearance_release_index( sca_mod *, str *, int ); -int sca_appearance_owner_release_all( str *, str * ); -int sca_appearance_state_for_index( sca_mod *, str *, int ); -sca_appearance *sca_appearance_for_index_unsafe( sca_mod *, str *, int, int ); -sca_appearance *sca_appearance_for_dialog_unsafe( sca_mod *, str *, - sca_dialog *, int ); -sca_appearance *sca_appearance_for_tags_unsafe( sca_mod *, str *, - str *, str *, str *, int ); - -int sca_appearance_register( sca_mod *, str * ); -int sca_appearance_unregister( sca_mod *, str * ); -void sca_appearance_list_insert_appearance( sca_appearance_list *, - sca_appearance * ); -sca_appearance *sca_appearance_list_unlink_index( sca_appearance_list *, int ); -int sca_appearance_list_unlink_appearance( sca_appearance_list *, - sca_appearance ** ); -sca_appearance *sca_appearance_unlink_by_tags( sca_mod *, str *, - str *, str *, str * ); - -sca_appearance *sca_appearance_create( int, str * ); -void sca_appearance_free( sca_appearance * ); - -int sca_uri_is_shared_appearance( sca_mod *, str * ); -int sca_uri_lock_shared_appearance( sca_mod *, str * ); -int sca_uri_lock_if_shared_appearance( sca_mod *, str *, int * ); - -void sca_appearance_purge_stale( unsigned int, void * ); +typedef struct _sca_appearance_list sca_appearance_list; + +void sca_appearance_state_to_str(int, str *); +int sca_appearance_state_from_str(str *); +sca_appearance *sca_appearance_seize_index_unsafe(sca_mod *, str *, str *, int, + int, int *); +int sca_appearance_seize_index(sca_mod *, str *, int, str *); +int sca_appearance_seize_next_available_index(sca_mod *, str *, str *); +sca_appearance *sca_appearance_seize_next_available_unsafe(sca_mod *, str *, + str *, int); +void sca_appearance_update_state_unsafe(sca_appearance *, int); +int sca_appearance_update_owner_unsafe(sca_appearance *, str *); +int sca_appearance_update_callee_unsafe(sca_appearance *, str *); +int sca_appearance_update_dialog_unsafe(sca_appearance *, str *, str *, str *); +int sca_appearance_update_unsafe(sca_appearance *, int, str *, str *, + sca_dialog *, str *, str *); +int sca_appearance_update_index(sca_mod *, str *, int, int, str *, str *, + sca_dialog *); +int sca_appearance_release_index(sca_mod *, str *, int); +int sca_appearance_owner_release_all(str *, str *); +int sca_appearance_state_for_index(sca_mod *, str *, int); +sca_appearance *sca_appearance_for_index_unsafe(sca_mod *, str *, int, int); +sca_appearance *sca_appearance_for_dialog_unsafe(sca_mod *, str *, sca_dialog *, + int); +sca_appearance *sca_appearance_for_tags_unsafe(sca_mod *, str *, str *, str *, + str *, int); +int sca_appearance_register(sca_mod *, str *); +int sca_appearance_unregister(sca_mod *, str *); +void sca_appearance_list_insert_appearance(sca_appearance_list *, + sca_appearance *); +sca_appearance *sca_appearance_list_unlink_index(sca_appearance_list *, int); +int sca_appearance_list_unlink_appearance(sca_appearance_list *, + sca_appearance **); +sca_appearance *sca_appearance_unlink_by_tags(sca_mod *, str *, str *, str *, + str *); +sca_appearance *sca_appearance_create(int, str *); +void sca_appearance_free(sca_appearance *); +int sca_uri_is_shared_appearance(sca_mod *, str *); +int sca_uri_lock_shared_appearance(sca_mod *, str *); +int sca_uri_lock_if_shared_appearance(sca_mod *, str *, int *); + +void sca_appearance_purge_stale(unsigned int, void *); #endif /* SCA_APPEARANCE_H */ diff --git a/modules/sca/sca_call_info.c b/modules/sca/sca_call_info.c index 1f367770d9c..d26d465e6f9 100644 --- a/modules/sca/sca_call_info.c +++ b/modules/sca/sca_call_info.c @@ -33,459 +33,436 @@ #include "sca_subscribe.h" #include "sca_util.h" -const str SCA_CALL_INFO_HEADER_STR = STR_STATIC_INIT( "Call-Info: " ); -const str SCA_CALL_INFO_HEADER_NAME = STR_STATIC_INIT( "Call-Info" ); - - static int -sca_call_info_domain_from_uri( str *uri, str *domain ) -{ - assert( !SCA_STR_EMPTY( uri )); - assert( domain != NULL ); - - domain->s = memchr( uri->s, '@', uri->len ); - if ( domain->s == NULL ) { - /* may be a sip:domain URI */ - domain->s = memchr( uri->s, ':', uri->len ); - if ( domain->s == NULL ) { - LM_ERR( "Bad URI %.*s", STR_FMT( uri )); - return( -1 ); - } - } - domain->s++; - - domain->len = ( uri->s + uri->len ) - domain->s; - /* XXX handle :port in URI? */ - - return( domain->len ); +const str SCA_CALL_INFO_HEADER_STR = STR_STATIC_INIT( "Call-Info: " ); +const str SCA_CALL_INFO_HEADER_NAME = STR_STATIC_INIT( "Call-Info" ); + +static int sca_call_info_domain_from_uri(str *uri, str *domain) { + assert(!SCA_STR_EMPTY(uri)); + assert(domain != NULL); + + domain->s = memchr(uri->s, '@', uri->len); + if (domain->s == NULL) { + /* may be a sip:domain URI */ + domain->s = memchr(uri->s, ':', uri->len); + if (domain->s == NULL) { + LM_ERR( "Bad URI %.*s\n", STR_FMT( uri )); + return (-1); + } + } + domain->s++; + + domain->len = (uri->s + uri->len) - domain->s; + /* XXX handle :port in URI? */ + + return (domain->len); } - static int -sca_call_info_header_length( int given_length ) -{ - assert( given_length >= 0 ); +static int sca_call_info_header_length(int given_length) { + assert(given_length >= 0); - /* - * given_length is assumed to contain length of index string, - * state string, and uri string, if any - */ + /* + * given_length is assumed to contain length of index string, + * state string, and uri string, if any + */ - /* ";appearance-index=N;appearance-state=STATE" */ - given_length += strlen( "=;=;" ); - given_length += SCA_APPEARANCE_INDEX_STR.len; - given_length += SCA_APPEARANCE_STATE_STR.len; + /* ";appearance-index=N;appearance-state=STATE" */ + given_length += strlen("=;=;"); + given_length += SCA_APPEARANCE_INDEX_STR.len; + given_length += SCA_APPEARANCE_STATE_STR.len; - return( given_length ); + return (given_length); } - static int -sca_call_info_header_length_for_appearance( sca_appearance *appearance, - str *aor ) -{ - int len = 0; - str domain = STR_NULL; - str state_str = STR_NULL; +static int sca_call_info_header_length_for_appearance( + sca_appearance *appearance, str *aor) { + int len = 0; + str domain = STR_NULL; + str state_str = STR_NULL; + + assert(aor != NULL); - assert( aor != NULL ); + /* get length of stringified index, since conversion's destructive */ + (void) int2str(appearance->index, &len); - /* get length of stringified index, since conversion's destructive */ - (void)int2str( appearance->index, &len ); + sca_appearance_state_to_str(appearance->state, &state_str); + len += state_str.len; - sca_appearance_state_to_str( appearance->state, &state_str ); - len += state_str.len; + if (!SCA_STR_EMPTY(&appearance->uri)) { + /* +1 for ';', +1 for '=' between param name and value, +2 for quotes */ + len += SCA_APPEARANCE_URI_STR.len + 1 + 1 + 2; + len += appearance->uri.len; + } - if ( !SCA_STR_EMPTY( &appearance->uri )) { - /* +1 for ';', +1 for '=' between param name and value, +2 for quotes */ - len += SCA_APPEARANCE_URI_STR.len + 1 + 1 + 2; - len += appearance->uri.len; - } + if (sca_call_info_domain_from_uri(aor, &domain) < 0) { + return (-1); + } + len += domain.len; - if ( sca_call_info_domain_from_uri( aor, &domain ) < 0 ) { - return( -1 ); - } - len += domain.len; - - len += sca_call_info_header_length( domain.len ); + len += sca_call_info_header_length(domain.len); - return( len ); + return (len); } - static int -sca_call_info_header_append_appearances( sca_mod *scam, sca_subscription *sub, - char *hdrbuf, int maxlen ) -{ - sca_appearance_list *app_list; - sca_appearance *app; - sca_hash_slot *slot; - str domain; - str state_str; - int slot_idx; - int len = -1; - int usedlen = -1; +static int sca_call_info_header_append_appearances(sca_mod *scam, + sca_subscription *sub, char *hdrbuf, int maxlen) { + sca_appearance_list *app_list; + sca_appearance *app; + sca_hash_slot *slot; + str domain; + str state_str; + int slot_idx; + int len = -1; + int usedlen = -1; + + slot_idx = sca_hash_table_index_for_key(scam->appearances, + &sub->target_aor); + slot = sca_hash_table_slot_for_index(scam->appearances, slot_idx); + + sca_hash_table_lock_index(scam->appearances, slot_idx); + + app_list = sca_hash_table_slot_kv_find_unsafe(slot, &sub->target_aor); + if (app_list == NULL) { + len = 0; + goto done; + } - slot_idx = sca_hash_table_index_for_key( scam->appearances, - &sub->target_aor ); - slot = sca_hash_table_slot_for_index( scam->appearances, slot_idx ); + usedlen = 0; + for (app = app_list->appearances; app != NULL; app = app->next) { + len = sca_call_info_header_length_for_appearance(app, &sub->target_aor); + if (len < 0) { + goto done; + } - sca_hash_table_lock_index( scam->appearances, slot_idx ); + if ((maxlen - len) < 0) { + LM_ERR( "Call-Info header for AoR %.*s is too long\n", + STR_FMT( &sub->target_aor )); + len = -1; + goto done; + } - app_list = sca_hash_table_slot_kv_find_unsafe( slot, &sub->target_aor ); - if ( app_list == NULL ) { - len = 0; - goto done; - } + memcpy(hdrbuf, "appearances; app != NULL; app = app->next ) { - len = sca_call_info_header_length_for_appearance( app, - &sub->target_aor ); - if ( len < 0 ) { - goto done; + if (sca_call_info_domain_from_uri(&sub->target_aor, &domain) < 0) { + return (-1); + } + + memcpy(hdrbuf + len, domain.s, domain.len); + len += domain.len; + + sca_appearance_state_to_str(app->state, &state_str); + + /* state_str.s is a nul-terminated string literal */ + len += snprintf(hdrbuf + len, maxlen - len, + ">;appearance-index=%d;appearance-state=%s", app->index, + state_str.s); + + if (!SCA_STR_EMPTY(&app->uri)) { + hdrbuf[len] = ';'; + len += 1; + + memcpy(hdrbuf + len, SCA_APPEARANCE_URI_STR.s, + SCA_APPEARANCE_URI_STR.len); + len += SCA_APPEARANCE_URI_STR.len; + + hdrbuf[len] = '='; + len += 1; + + hdrbuf[len] = '"'; + len += 1; + + memcpy(hdrbuf + len, app->uri.s, app->uri.len); + len += app->uri.len; + + hdrbuf[len] = '"'; + len += 1; + } + + if (app->next) { + memcpy(hdrbuf + len, ",", 1); + len++; + } + + maxlen -= len; + hdrbuf += len; + + usedlen += len; } - if (( maxlen - len ) < 0 ) { - LM_ERR( "Call-Info header for AoR %.*s is too long", - STR_FMT( &sub->target_aor )); - len = -1; - goto done; + done: + sca_hash_table_unlock_index(scam->appearances, slot_idx); + + return (usedlen); +} + +static int sca_call_info_build_idle_value(sca_mod *scam, str *aor, char *hdrbuf, + int maxlen) { + str idle_domain = STR_NULL; + int len; + + if (sca_call_info_domain_from_uri(aor, &idle_domain) < 0) { + LM_ERR( "Failed to extract domain from %.*s for idle domain\n", + STR_FMT( aor )); + return (-1); } - memcpy( hdrbuf, ";%s=*;%s=%s%s", + STR_FMT(&idle_domain), SCA_APPEARANCE_INDEX_STR.s, + SCA_APPEARANCE_STATE_STR.s, SCA_APPEARANCE_STATE_STR_IDLE.s, CRLF); + if (len >= maxlen) { + LM_ERR( "Failed to add idle appearance: Call-Info header too long\n" ); + len = -1; - if ( sca_call_info_domain_from_uri( &sub->target_aor, &domain ) < 0 ) { - return( -1 ); + /* snprintf can also return negative. we catch that in the caller. */ } - memcpy( hdrbuf + len, domain.s, domain.len ); - len += domain.len; + return (len); +} - sca_appearance_state_to_str( app->state, &state_str ); +int sca_call_info_build_header(sca_mod *scam, sca_subscription *sub, + char *hdrbuf, int maxlen) { + /* we send one Call-Info header, appearances separated by commas */ + int len; + int usedlen = SCA_CALL_INFO_HEADER_STR.len; - /* state_str.s is a nul-terminated string literal */ - len += snprintf( hdrbuf + len, maxlen - len, - ">;appearance-index=%d;appearance-state=%s", - app->index, state_str.s ); + /* begin with "Call-Info: " */ + memcpy(hdrbuf, SCA_CALL_INFO_HEADER_STR.s, SCA_CALL_INFO_HEADER_STR.len); - if ( !SCA_STR_EMPTY( &app->uri )) { - hdrbuf[ len ] = ';'; - len += 1; + len = sca_call_info_header_append_appearances(scam, sub, hdrbuf + usedlen, + maxlen - usedlen); + usedlen += len; + if (usedlen > SCA_CALL_INFO_HEADER_STR.len) { + /* we added an indexed appearance, append a comma */ + memcpy(hdrbuf + usedlen, ",", 1); + usedlen++; + } - memcpy( hdrbuf + len, SCA_APPEARANCE_URI_STR.s, - SCA_APPEARANCE_URI_STR.len ); - len += SCA_APPEARANCE_URI_STR.len; + /* line-seize NOTIFYs will contain only the seized appearance index */ + if (sub->event != SCA_EVENT_TYPE_LINE_SEIZE) { + /* if not all appearances in use, add *-index idle */ + len = sca_call_info_build_idle_value(scam, &sub->target_aor, + hdrbuf + usedlen, maxlen - usedlen); + if (len < 0 || len + usedlen >= maxlen) { + LM_ERR( "Cannot build idle Call-Info value: buffer too small\n" ); + return (-1); + } + usedlen += len; + } - hdrbuf[ len ] = '='; - len += 1; + return (usedlen); +} - hdrbuf[ len ] = '"'; - len += 1; +int sca_call_info_append_header_for_appearance_index(sca_subscription *sub, + int appearance_index, char *hdrbuf, int maxlen) { + str domain = STR_NULL; + char *app_index_p = NULL; + int len = 0, + idx_len; + + memcpy(hdrbuf, SCA_CALL_INFO_HEADER_STR.s, SCA_CALL_INFO_HEADER_STR.len); + len += SCA_CALL_INFO_HEADER_STR.len; + if (len >= maxlen) { + goto error; + } - memcpy( hdrbuf + len, app->uri.s, app->uri.len ); - len += app->uri.len; + memcpy(hdrbuf + len, "= maxlen) { + goto error; + } - hdrbuf[ len ] = '"'; - len += 1; + sca_call_info_domain_from_uri(&sub->target_aor, &domain); + memcpy(hdrbuf + len, domain.s, domain.len); + len += domain.len; + if (len >= maxlen) { + goto error; } - if ( app->next ) { - memcpy( hdrbuf + len, ",", 1 ); - len++; + memcpy(hdrbuf + len, ">;appearance-index=", strlen(">;appearance-index=")); + len += strlen(">;appearance-index="); + if (len >= maxlen) { + goto error; } - maxlen -= len; - hdrbuf += len; + app_index_p = int2str(appearance_index, &idx_len); + memcpy(hdrbuf + len, app_index_p, idx_len); + len += idx_len; + if (len >= maxlen) { + goto error; + } - usedlen += len; - } + memcpy(hdrbuf + len, CRLF, CRLF_LEN); + len += CRLF_LEN; + if (len >= maxlen) { + goto error; + } -done: - sca_hash_table_unlock_index( scam->appearances, slot_idx ); + return (len); - return( usedlen ); + error: + LM_ERR( "Failed to append Call-Info header for %.*s appearance index %d\n", + STR_FMT( &sub->subscriber ), appearance_index ); + return (-1); } - static int -sca_call_info_build_idle_value( sca_mod *scam, str *aor, - char *hdrbuf, int maxlen ) -{ - str idle_domain = STR_NULL; - int len; - - if ( sca_call_info_domain_from_uri( aor, &idle_domain ) < 0 ) { - LM_ERR( "Failed to extract domain from %.*s for idle domain", - STR_FMT( aor )); - return( -1 ); - } - - /* the SCA_APPEARANCE_ strs' s member are literal C strings */ - len = snprintf( hdrbuf, maxlen, - ";%s=*;%s=%s%s", - STR_FMT( &idle_domain ), - SCA_APPEARANCE_INDEX_STR.s, - SCA_APPEARANCE_STATE_STR.s, - SCA_APPEARANCE_STATE_STR_IDLE.s, CRLF ); - if ( len >= maxlen ) { - LM_ERR( "Failed to add idle appearance: Call-Info header too long" ); - len = -1; - - /* snprintf can also return negative. we catch that in the caller. */ - } - - return( len ); +hdr_field_t * +sca_call_info_header_find(hdr_field_t *msg_hdrs) { + hdr_field_t *hdr = NULL; + + for (hdr = msg_hdrs; hdr != NULL; hdr = hdr->next) { + if (hdr->type == HDR_OTHER_T + && hdr->name.len == SCA_CALL_INFO_HEADER_NAME.len) { + if (strncasecmp(hdr->name.s, SCA_CALL_INFO_HEADER_NAME.s, + SCA_CALL_INFO_HEADER_NAME.len) == 0) { + break; + } + } + } + + return (hdr); } - int -sca_call_info_build_header( sca_mod *scam, sca_subscription *sub, - char *hdrbuf, int maxlen ) -{ - /* we send one Call-Info header, appearances separated by commas */ - int len; - int usedlen = SCA_CALL_INFO_HEADER_STR.len; - - /* begin with "Call-Info: " */ - memcpy( hdrbuf, SCA_CALL_INFO_HEADER_STR.s, SCA_CALL_INFO_HEADER_STR.len ); - - len = sca_call_info_header_append_appearances( scam, sub, - hdrbuf + usedlen, maxlen - usedlen ); - usedlen += len; - if ( usedlen > SCA_CALL_INFO_HEADER_STR.len ) { - /* we added an indexed appearance, append a comma */ - memcpy( hdrbuf + usedlen, ",", 1 ); - usedlen++; - } - - /* line-seize NOTIFYs will contain only the seized appearance index */ - if ( sub->event != SCA_EVENT_TYPE_LINE_SEIZE ) { - /* if not all appearances in use, add *-index idle */ - len = sca_call_info_build_idle_value( scam, &sub->target_aor, - hdrbuf + usedlen, maxlen - usedlen ); - if ( len < 0 || len + usedlen >= maxlen ) { - LM_ERR( "Cannot build idle Call-Info value: buffer too small" ); - return( -1 ); +int sca_call_info_body_parse(str *hdr_body, sca_call_info *call_info) { + str s = STR_NULL; + char *p; + char *semi; + int len; + + assert(call_info != NULL); + + if (SCA_STR_EMPTY(hdr_body)) { + LM_ERR( "Call-Info header body is empty\n" ); + return (-1); } - usedlen += len; - } - return( usedlen ); -} + call_info->sca_uri.s = NULL; + call_info->sca_uri.len = 0; + call_info->index = -1; + call_info->state = SCA_APPEARANCE_STATE_UNKNOWN; + call_info->uri.s = NULL; + call_info->uri.len = 0; + + p = hdr_body->s; + if (memcmp(p, "len); + if (semi == NULL) { + LM_ERR( "Bad Call-Info header body: missing ';' between uri and " + "%.*s\n", STR_FMT( &SCA_APPEARANCE_INDEX_STR )); + return (-1); + } + if (*(semi - 1) != '>') { + LM_ERR( "Bad Call-Info header body: SCA URI missing '>' terminator\n" ); + return (-1); + } - int -sca_call_info_append_header_for_appearance_index( sca_subscription *sub, - int appearance_index, char *hdrbuf, int maxlen ) -{ - str domain = STR_NULL; - char *app_index_p = NULL; - int len = 0, idx_len; - - memcpy( hdrbuf, SCA_CALL_INFO_HEADER_STR.s, SCA_CALL_INFO_HEADER_STR.len ); - len += SCA_CALL_INFO_HEADER_STR.len; - if ( len >= maxlen ) { - goto error; - } - - memcpy( hdrbuf + len, "= maxlen ) { - goto error; - } - - sca_call_info_domain_from_uri( &sub->target_aor, &domain ); - memcpy( hdrbuf + len, domain.s, domain.len ); - len += domain.len; - if ( len >= maxlen ) { - goto error; - } - - memcpy( hdrbuf + len, ">;appearance-index=", - strlen( ">;appearance-index=" )); - len += strlen( ">;appearance-index=" ); - if ( len >= maxlen ) { - goto error; - } - - app_index_p = int2str( appearance_index, &idx_len ); - memcpy( hdrbuf + len, app_index_p, idx_len ); - len += idx_len; - if ( len >= maxlen ) { - goto error; - } - - memcpy( hdrbuf + len, CRLF, CRLF_LEN ); - len += CRLF_LEN; - if ( len >= maxlen ) { - goto error; - } - - return( len ); - -error: - LM_ERR( "Failed to append Call-Info header for %.*s appearance index %d", - STR_FMT( &sub->subscriber ), appearance_index ); - return( -1 ); -} + call_info->sca_uri.s = p; + call_info->sca_uri.len = semi - p; - hdr_field_t * -sca_call_info_header_find( hdr_field_t *msg_hdrs ) -{ - hdr_field_t *hdr = NULL; + p = semi; + p++; + if (memcmp(p, SCA_APPEARANCE_INDEX_STR.s, SCA_APPEARANCE_INDEX_STR.len) + != 0) { + LM_ERR( "Bad Call-Info header body: does not begin with %.*s\n", + STR_FMT( &SCA_APPEARANCE_INDEX_STR )); + return (-1); + } - for ( hdr = msg_hdrs; hdr != NULL; hdr = hdr->next ) { - if ( hdr->type == HDR_OTHER_T && - hdr->name.len == SCA_CALL_INFO_HEADER_NAME.len ) { - if ( strncasecmp( hdr->name.s, SCA_CALL_INFO_HEADER_NAME.s, - SCA_CALL_INFO_HEADER_NAME.len ) == 0) { - break; - } + p += SCA_APPEARANCE_INDEX_STR.len; + if (*p != '=') { + LM_ERR( "Bad Call-Info header body: missing '=' after %.*s\n", + STR_FMT( &SCA_APPEARANCE_INDEX_STR )); + return (-1); } - } - return( hdr ); -} + p++; + len = (hdr_body->s + hdr_body->len) - p; + semi = memchr(p, ';', len); + if (semi != NULL) { + len = semi - p; + } + s.s = p; + s.len = len; + + if (str2int(&s, (unsigned int *) &call_info->index) != 0) { + LM_ERR( "Bad Call-Info header: failed to convert %.*s %.*s to an " + "integer\n", STR_FMT( &SCA_APPEARANCE_INDEX_STR ), STR_FMT( &s )); + return (-1); + } + + if (semi == NULL) { + /* Call-Info header only contained an appearance-index */ + goto done; + } + + /* advance appearance-index value + semi-colon */ + p += (len + 1); + if (memcmp(p, SCA_APPEARANCE_STATE_STR.s, SCA_APPEARANCE_STATE_STR.len) + != 0) { + LM_ERR( "Bad Call-Info header: missing %.*s\n", + STR_FMT( &SCA_APPEARANCE_STATE_STR )); + return (-1); + } + + p += SCA_APPEARANCE_STATE_STR.len; + if (*p != '=') { + LM_ERR( "Bad Call-Info header body: missing '=' after %.*s\n", + STR_FMT( &SCA_APPEARANCE_STATE_STR )); + return (-1); + + } + + p++; + len = (hdr_body->s + hdr_body->len) - p; + semi = memchr(p, ';', len); + if (semi != NULL) { + len = semi - p; + } + s.s = p; + s.len = len; + + call_info->state = sca_appearance_state_from_str(&s); + if (call_info->state == SCA_APPEARANCE_STATE_UNKNOWN) { + LM_ERR( "Bad Call-Info header: unrecognized state \"%.*s\"\n", + STR_FMT( &s )); + return (-1); + } + + if (semi == NULL) { + /* Call-Info header only had appearance-index & appearance-state */ + goto done; + } + + /* advance length of state + semi-colon */ + p += (len + 1); + if (memcmp(p, SCA_APPEARANCE_URI_STR.s, SCA_APPEARANCE_URI_STR.len) != 0) { + LM_ERR( "Bad Call-Info header: missing %.*s\n", + STR_FMT( &SCA_APPEARANCE_URI_STR )); + return (-1); + } - int -sca_call_info_body_parse( str *hdr_body, sca_call_info *call_info ) -{ - str s = STR_NULL; - char *p; - char *semi; - int len; - - assert( call_info != NULL ); - - if ( SCA_STR_EMPTY( hdr_body )) { - LM_ERR( "Call-Info header body is empty" ); - return( -1 ); - } - - call_info->sca_uri.s = NULL; - call_info->sca_uri.len = 0; - call_info->index = -1; - call_info->state = SCA_APPEARANCE_STATE_UNKNOWN; - call_info->uri.s = NULL; - call_info->uri.len = 0; - - p = hdr_body->s; - if ( memcmp( p, "len ); - if ( semi == NULL ) { - LM_ERR( "Bad Call-Info header body: missing ';' between uri and " - "%.*s", STR_FMT( &SCA_APPEARANCE_INDEX_STR )); - return( -1 ); - } - if ( *(semi - 1) != '>' ) { - LM_ERR( "Bad Call-Info header body: SCA URI missing '>' terminator" ); - return( -1 ); - } - - call_info->sca_uri.s = p; - call_info->sca_uri.len = semi - p; - - p = semi; - p++; - if ( memcmp( p, SCA_APPEARANCE_INDEX_STR.s, - SCA_APPEARANCE_INDEX_STR.len ) != 0 ) { - LM_ERR( "Bad Call-Info header body: does not begin with %.*s", - STR_FMT( &SCA_APPEARANCE_INDEX_STR )); - return( -1 ); - } - - p += SCA_APPEARANCE_INDEX_STR.len; - if ( *p != '=' ) { - LM_ERR( "Bad Call-Info header body: missing '=' after %.*s", - STR_FMT( &SCA_APPEARANCE_INDEX_STR )); - return( -1 ); - } - - p++; - len = ( hdr_body->s + hdr_body->len ) - p; - semi = memchr( p, ';', len ); - if ( semi != NULL ) { - len = semi - p; - } - s.s = p; - s.len = len; - - if ( str2int( &s, (unsigned int *)&call_info->index ) != 0 ) { - LM_ERR( "Bad Call-Info header: failed to convert %.*s %.*s to an " - "integer", STR_FMT( &SCA_APPEARANCE_INDEX_STR ), STR_FMT( &s )); - return( -1 ); - } - - if ( semi == NULL ) { - /* Call-Info header only contained an appearance-index */ - goto done; - } - - /* advance appearance-index value + semi-colon */ - p += ( len + 1 ); - if ( memcmp( p, SCA_APPEARANCE_STATE_STR.s, - SCA_APPEARANCE_STATE_STR.len ) != 0 ) { - LM_ERR( "Bad Call-Info header: missing %.*s", - STR_FMT( &SCA_APPEARANCE_STATE_STR )); - return( -1 ); - } - - p += SCA_APPEARANCE_STATE_STR.len; - if ( *p != '=' ) { - LM_ERR( "Bad Call-Info header body: missing '=' after %.*s", - STR_FMT( &SCA_APPEARANCE_STATE_STR )); - return( -1 ); - - } - - p++; - len = ( hdr_body->s + hdr_body->len ) - p; - semi = memchr( p, ';', len ); - if ( semi != NULL ) { - len = semi - p; - } - s.s = p; - s.len = len; - - call_info->state = sca_appearance_state_from_str( &s ); - if ( call_info->state == SCA_APPEARANCE_STATE_UNKNOWN ) { - LM_ERR( "Bad Call-Info header: unrecognized state \"%.*s\"", - STR_FMT( &s )); - return( -1 ); - } - - if ( semi == NULL ) { - /* Call-Info header only had appearance-index & appearance-state */ - goto done; - } - - /* advance length of state + semi-colon */ - p += ( len + 1 ); - if ( memcmp( p, SCA_APPEARANCE_URI_STR.s, - SCA_APPEARANCE_URI_STR.len ) != 0 ) { - LM_ERR( "Bad Call-Info header: missing %.*s", - STR_FMT( &SCA_APPEARANCE_URI_STR )); - return( -1 ); - } - - p += SCA_APPEARANCE_URI_STR.len; - if ( *p != '=' ) { - LM_ERR( "Bad Call-Info header: missing '=' after %.*s", - STR_FMT( &SCA_APPEARANCE_URI_STR )); - return( -1 ); - } - - p++; - call_info->uri.s = p; - call_info->uri.len = ( hdr_body->s + hdr_body->len ) - p; - - if ( SCA_STR_EMPTY( &call_info->uri )) { - LM_ERR( "Bad Call-Info header: empty %.*s", - STR_FMT( &SCA_APPEARANCE_URI_STR )); - return( -1 ); - } - -done: - return( 0 ); + p += SCA_APPEARANCE_URI_STR.len; + if (*p != '=') { + LM_ERR( "Bad Call-Info header: missing '=' after %.*s\n", + STR_FMT( &SCA_APPEARANCE_URI_STR )); + return (-1); + } + + p++; + call_info->uri.s = p; + call_info->uri.len = (hdr_body->s + hdr_body->len) - p; + + if (SCA_STR_EMPTY(&call_info->uri)) { + LM_ERR( "Bad Call-Info header: empty %.*s\n", + STR_FMT( &SCA_APPEARANCE_URI_STR )); + return (-1); + } + + done: return (0); } /* @@ -494,1553 +471,1541 @@ sca_call_info_body_parse( str *hdr_body, sca_call_info *call_info ) * 0: no call-info headers found. * >=1: removed 1 or more call-info headers. */ - static int -sca_call_info_header_remove( sip_msg_t *msg ) -{ - hdr_field_t *hdr; - struct lump *ci_hdr_lump; - int rc = 0; - - /* all headers must be parsed before using del_lump */ - if ( parse_headers( msg, HDR_EOH_F, 0 ) < 0 ) { - LM_ERR( "Failed to parse_headers" ); - return( -1 ); - } +static int sca_call_info_header_remove(sip_msg_t *msg) { + hdr_field_t *hdr; + struct lump *ci_hdr_lump; + int rc = 0; + + /* all headers must be parsed before using del_lump */ + if (parse_headers(msg, HDR_EOH_F, 0) < 0) { + LM_ERR( "Failed to parse_headers\n" ); + return (-1); + } #ifdef notdef - for ( hdr = sca_call_info_header_find( msg->headers ); hdr != NULL; - hdr = sca_call_info_header_find( hdr->next )) { + for ( hdr = sca_call_info_header_find( msg->headers ); hdr != NULL; + hdr = sca_call_info_header_find( hdr->next )) { #endif /* notdef */ - for ( hdr = msg->headers; hdr; hdr = hdr->next ) { - if ( hdr->name.len != SCA_CALL_INFO_HEADER_NAME.len ) { - continue; + for (hdr = msg->headers; hdr; hdr = hdr->next) { + if (hdr->name.len != SCA_CALL_INFO_HEADER_NAME.len) { + continue; + } + if (memcmp(hdr->name.s, SCA_CALL_INFO_HEADER_NAME.s, hdr->name.len) + != 0) { + continue; + } + + /* del_lump takes packet, offset, lump length, & hdr type */ + ci_hdr_lump = del_lump(msg, hdr->name.s - msg->buf, hdr->len, + HDR_OTHER_T); + if (ci_hdr_lump == NULL) { + LM_ERR( "Failed to del_lump Call-Info header\n" ); + rc = -1; + break; + } + + rc++; } - if ( memcmp( hdr->name.s, SCA_CALL_INFO_HEADER_NAME.s, - hdr->name.len ) != 0 ) { - continue; + + return (rc); +} + +//static int +int sca_call_info_seize_held_call(sip_msg_t *msg, sca_call_info *call_info, + struct to_body *from, struct to_body *to, str *from_aor, str *to_aor, + str *contact_uri) { + sca_appearance *app; + struct lump *anchor; + str callee_aor = STR_NULL; + str replaces_hdr = STR_NULL; + str prev_callid = STR_NULL; + str prev_totag = STR_NULL; + char tagbuf[1024]; + char callee_buf[1024]; + int slot_idx = -1; + int rc = -1; + + LM_DBG( "From-AOR:%.*s To-AOR:%.*s From-URI:<%.*s> To-URI:<%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d", + STR_FMT( from_aor ), STR_FMT( to_aor ),STR_FMT( &from->uri ), + STR_FMT( &to->uri ), STR_FMT( contact_uri ), call_info->index ); + + slot_idx = sca_hash_table_index_for_key(sca->appearances, from_aor); + sca_hash_table_lock_index(sca->appearances, slot_idx); + + app = sca_appearance_for_index_unsafe(sca, from_aor, call_info->index, + slot_idx); + if (app == NULL) { + LM_ERR( "sca_call_info_seize_held_call: no active appearances for " + "%.*s\n", STR_FMT( from_aor )); + goto done; } - /* del_lump takes packet, offset, lump length, & hdr type */ - ci_hdr_lump = del_lump( msg, hdr->name.s - msg->buf, - hdr->len, HDR_OTHER_T ); - if ( ci_hdr_lump == NULL ) { - LM_ERR( "Failed to del_lump Call-Info header" ); - rc = -1; - break; + if (app->state == SCA_APPEARANCE_STATE_HELD_PRIVATE) { + /* + * spec calls for "403 Forbidden" when non-owner tries to + * seize a privately held call. if we get here, there's no + * to-tag in the INVITE, meaning this isn't a reINVITE + * from the owner to take the call off private hold. + */ + SCA_REPLY_ERROR(sca, 403, "Forbidden - private call", msg); + + /* rc bubbles up to script. 0 tells script to stop processing. */ + rc = 0; + goto done; } - rc++; - } + LM_DBG( "sca_call_info_seize_held_call: seizing %.*s index %d, callee %.*s\n", + STR_FMT( from_aor ), app->index, STR_FMT( &app->callee )); - return( rc ); -} + /* rewrite the RURI to use the callee in this SCA dialog */ + if (msg->new_uri.s) { + /* + * someone already rewrote the URI. shouldn't happen, but we have + * to watch for it. log our overwriting of it. + */ - //static int - int -sca_call_info_seize_held_call( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, str *to_aor, - str *contact_uri ) -{ - sca_appearance *app; - struct lump *anchor; - str callee_aor = STR_NULL; - str replaces_hdr = STR_NULL; - str prev_callid = STR_NULL; - str prev_totag = STR_NULL; - char tagbuf[ 1024 ]; - char callee_buf[ 1024 ]; - int slot_idx = -1; - int rc = -1; - - slot_idx = sca_hash_table_index_for_key( sca->appearances, from_aor ); - sca_hash_table_lock_index( sca->appearances, slot_idx ); - - app = sca_appearance_for_index_unsafe( sca, from_aor, call_info->index, - slot_idx ); - if ( app == NULL ) { - LM_ERR( "sca_call_info_seize_held_call: no active appearances for " - "%.*s", STR_FMT( from_aor )); - goto done; - } - - if ( app->state == SCA_APPEARANCE_STATE_HELD_PRIVATE ) { - /* - * spec calls for "403 Forbidden" when non-owner tries to - * seize a privately held call. if we get here, there's no - * to-tag in the INVITE, meaning this isn't a reINVITE - * from the owner to take the call off private hold. - */ - SCA_REPLY_ERROR( sca, 403, "Forbidden - private call", msg ); - - /* rc bubbles up to script. 0 tells script to stop processing. */ - rc = 0; - goto done; - } - - LM_DBG( "sca_call_info_seize_held_call: seizing %.*s index %d, callee %.*s", - STR_FMT( from_aor ), app->index, STR_FMT( &app->callee )); - - /* rewrite the RURI to use the callee in this SCA dialog */ - if ( msg->new_uri.s ) { - /* - * someone already rewrote the URI. shouldn't happen, but we have - * to watch for it. log our overwriting of it. - */ + LM_DBG( "SCA caller retrieving held call, but RURI was already " + "rewritten as %.*s. Overwriting with %.*s.\n", + STR_FMT( &msg->new_uri ), STR_FMT( &app->callee )); + + pkg_free(msg->new_uri.s); + msg->new_uri.s = NULL; + msg->new_uri.len = 0; + } - LM_DBG( "SCA caller retrieving held call, but RURI was already " - "rewritten as %.*s. Overwriting with %.*s.", - STR_FMT( &msg->new_uri ), STR_FMT( &app->callee )); - - pkg_free( msg->new_uri.s ); - msg->new_uri.s = NULL; - msg->new_uri.len = 0; - } - - /* msg->new_uri.s is free'd when transaction is torn down */ - msg->new_uri.s = (char *)pkg_malloc( app->callee.len ); - if ( msg->new_uri.s == NULL ) { - LM_ERR( "sca_call_info_seize_held_call: pkg_malloc new RURI %.*s " - "failed", STR_FMT( &app->callee )); - goto done; - } - SCA_STR_COPY( &msg->new_uri, &app->callee ); - - { - int idx; - - for ( idx = 0; get_sip_branch( idx ) != NULL; idx++ ) - ; - - for ( ; idx >= 0; idx-- ) { - drop_sip_branch( idx ); - } - } - - /* must reset to avoid using cached parsed RURI */ - msg->parsed_uri_ok = 0; - ruri_mark_new(); - - /* store the previous dialog's tags for lookup of the callee */ - prev_callid.s = tagbuf; - SCA_STR_COPY( &prev_callid, &app->dialog.call_id ); - - prev_totag.s = tagbuf + prev_callid.len; - SCA_STR_COPY( &prev_totag, &app->dialog.to_tag ); - - /* pkg_malloc's replaces_hdr.s, which is free'd if added as lump */ - if ( sca_dialog_create_replaces_header( &app->dialog, - &replaces_hdr ) < 0 ) { - LM_ERR( "sca_call_info_seize_held_call: failed to create Replaces " - "header for %.*s from dialog %.*s", - STR_FMT( from_aor ), STR_FMT( &app->dialog.id )); - goto done; - } - - /* store the callee's username for lookup of the callee by AoR */ - callee_aor.s = callee_buf; - if ( sca_uri_build_aor( &callee_aor, sizeof( callee_buf ), &app->callee, - from_aor ) < 0 ) { - LM_ERR( "sca_call_info_seize_held_call: failed to create To AoR " - "from %.*s and %.*s", STR_FMT( &app->callee ), - STR_FMT( from_aor )); - pkg_free( replaces_hdr.s ); - goto done; - } - - /* all headers must be parsed before using lump functions */ - if ( parse_headers( msg, HDR_EOH_F, 0 ) < 0 ) { - LM_ERR( "Failed to parse_headers" ); - goto done; - } - - anchor = anchor_lump( msg, msg->eoh - msg->buf, 0, HDR_OTHER_T ); - if ( anchor == NULL ) { - LM_ERR( "Failed to anchor lump" ); - goto done; - } - - /* append the Replaces header before the sdp body */ - if ( insert_new_lump_before( anchor, replaces_hdr.s, - replaces_hdr.len, HDR_OTHER_T ) == NULL ) { - LM_ERR( "Failed to add Replaces header %.*s", STR_FMT( &replaces_hdr )); - pkg_free( replaces_hdr.s ); - goto done; - } - - /* - * RFC 3891 (Replaces header) suggests, but does not require, that the - * UAS establish the dialog with the UAC replacing the existing dialog - * before sending the BYE to the original UAC. Polycom handsets appear - * to send the BYE to the original UAC first, so we save the pending - * owner here. if the 200 OK arrives first, we update the owner and - * dialog there. otherwise, we catch this in the 200 OK to the BYE - * sent by the line being replaced. - * - * if the reINVITE to seize the held line fails for some reason, - * we restore the original owner and dialog. - */ - - if ( sca_appearance_update_owner_unsafe( app, contact_uri ) < 0 ) { - LM_ERR( "sca_call_info_seize_held_call: failed to update owner" ); - pkg_free( replaces_hdr.s ); - goto done; - } - - if ( sca_appearance_update_dialog_unsafe( app, &msg->callid->body, - &from->tag_value, &to->tag_value ) < 0 ) { - LM_ERR( "sca_call_info_seize_held_call: failed to update dialog" ); - goto done; - } - - app->flags |= SCA_APPEARANCE_FLAG_OWNER_PENDING; - sca_appearance_update_state_unsafe( app, SCA_APPEARANCE_STATE_ACTIVE ); - - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - slot_idx = -1; - - if ( callee_aor.s != NULL && callee_aor.len > 0 ) { - if ( sca_uri_lock_if_shared_appearance( sca, &callee_aor, &slot_idx )) { - app = sca_appearance_for_tags_unsafe( sca, &callee_aor, - &prev_callid, &prev_totag, NULL, slot_idx ); - if ( app == NULL ) { - LM_ERR( "sca_call_info_seize_held_call: failed to find " - "appearance of %.*s with dialog %.*s;%.*s", - STR_FMT( &callee_aor ), STR_FMT( &prev_callid ), - STR_FMT( &prev_totag )); + /* msg->new_uri.s is free'd when transaction is torn down */ + msg->new_uri.s = (char *) pkg_malloc(app->callee.len); + if (msg->new_uri.s == NULL) { + LM_ERR( "sca_call_info_seize_held_call: pkg_malloc new RURI %.*s " + "failed\n", STR_FMT( &app->callee )); goto done; - } + } + SCA_STR_COPY(&msg->new_uri, &app->callee); + + { + int idx; - app->flags |= SCA_APPEARANCE_FLAG_CALLEE_PENDING; + for (idx = 0; get_sip_branch(idx) != NULL; idx++) + ; - if ( sca_appearance_update_callee_unsafe( app, contact_uri ) < 0 ) { - LM_ERR( "sca_call_info_seize_held_call: " - "failed to update callee" ); + for (; idx >= 0; idx--) { + drop_sip_branch(idx); + } + } + + /* must reset to avoid using cached parsed RURI */ + msg->parsed_uri_ok = 0; + ruri_mark_new(); + + /* store the previous dialog's tags for lookup of the callee */ + prev_callid.s = tagbuf; + SCA_STR_COPY(&prev_callid, &app->dialog.call_id); + + prev_totag.s = tagbuf + prev_callid.len; + SCA_STR_COPY(&prev_totag, &app->dialog.to_tag); + + /* pkg_malloc's replaces_hdr.s, which is free'd if added as lump */ + if (sca_dialog_create_replaces_header(&app->dialog, &replaces_hdr) < 0) { + LM_ERR( "sca_call_info_seize_held_call: failed to create Replaces " + "header for %.*s from dialog %.*s\n", + STR_FMT( from_aor ), STR_FMT( &app->dialog.id )); goto done; - } - if ( sca_appearance_update_dialog_unsafe( app, &msg->callid->body, - &to->tag_value, &from->tag_value ) < 0 ) { - LM_ERR( "sca_call_info_seize_held_call: " - "failed to update dialog" ); + } + + /* store the callee's username for lookup of the callee by AoR */ + callee_aor.s = callee_buf; + if (sca_uri_build_aor(&callee_aor, sizeof(callee_buf), &app->callee, + from_aor) < 0) { + LM_ERR( "sca_call_info_seize_held_call: failed to create To AoR " + "from %.*s and %.*s\n", STR_FMT( &app->callee ), + STR_FMT( from_aor )); + pkg_free(replaces_hdr.s); goto done; - } } - } - rc = 1; + /* all headers must be parsed before using lump functions */ + if (parse_headers(msg, HDR_EOH_F, 0) < 0) { + LM_ERR( "Failed to parse_headers\n" ); + goto done; + } -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } + anchor = anchor_lump(msg, msg->eoh - msg->buf, 0, HDR_OTHER_T); + if (anchor == NULL) { + LM_ERR( "Failed to anchor lump\n" ); + goto done; + } - return( rc ); -} + /* append the Replaces header before the sdp body */ + if (insert_new_lump_before(anchor, replaces_hdr.s, replaces_hdr.len, + HDR_OTHER_T) == NULL) { + LM_ERR( "Failed to add Replaces header %.*s\n", STR_FMT( &replaces_hdr )); + pkg_free(replaces_hdr.s); + goto done; + } - static int -sca_call_info_uri_update( str *aor, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *contact_uri, - str *call_id ) -{ - sca_appearance *app; - sca_dialog dialog; - str state_str; - str *from_tag = &from->tag_value; - str *to_tag = &to->tag_value; - char dlg_buf[ 1024 ]; - int slot_idx = -1; - int rc = -1; - - assert( aor != NULL ); - assert( call_info != NULL ); - - LM_DBG( "sca_call_info_uri_update for %.*s: From: <%.*s> To: <%.*s> " - "Contact: <%.*s> Call-ID: %.*s Call-Info: appearance-index=%d", - STR_FMT( aor ), STR_FMT( &from->uri ), STR_FMT( &to->uri ), - STR_FMT( contact_uri ), STR_FMT( call_id ), call_info->index ); - - if ( !sca_uri_is_shared_appearance( sca, aor )) { - return( 1 ); - } - - dialog.id.s = dlg_buf; - if ( sca_dialog_build_from_tags( &dialog, sizeof( dlg_buf ), call_id, - to_tag, from_tag ) < 0 ) { - LM_ERR( "sca_call_info_uri_update: Failed to build dialog from tags" ); - return( -1 ); - } - - slot_idx = sca_hash_table_index_for_key( sca->appearances, aor ); - sca_hash_table_lock_index( sca->appearances, slot_idx ); - - app = sca_appearance_for_index_unsafe( sca, aor, call_info->index, - slot_idx ); - if ( app == NULL ) { - LM_DBG( "sca_call_info_uri_update: no appearance found for %.*s " - "index %d, looking up by dialog...", STR_FMT( aor ), - call_info->index ); - app = sca_appearance_for_dialog_unsafe( sca, aor, &dialog, slot_idx ); - } - if ( app != NULL ) { - LM_DBG( "sca_call_info_uri_update: setting owner to %.*s", - STR_FMT( contact_uri )); - - if ( sca_appearance_update_unsafe( app, call_info->state, - NULL, NULL, &dialog, contact_uri, NULL ) < 0 ) { - sca_appearance_state_to_str( call_info->state, &state_str ); - LM_ERR( "sca_call_info_uri_update: failed to update appearance " - "%.*s appearance-index %d with dialog id %.*s to " - "state %.*s", STR_FMT( &app->owner ), app->index, - STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); - goto done; + /* + * RFC 3891 (Replaces header) suggests, but does not require, that the + * UAS establish the dialog with the UAC replacing the existing dialog + * before sending the BYE to the original UAC. Polycom handsets appear + * to send the BYE to the original UAC first, so we save the pending + * owner here. if the 200 OK arrives first, we update the owner and + * dialog there. otherwise, we catch this in the 200 OK to the BYE + * sent by the line being replaced. + * + * if the reINVITE to seize the held line fails for some reason, + * we restore the original owner and dialog. + */ + + if (sca_appearance_update_owner_unsafe(app, contact_uri) < 0) { + LM_ERR( "sca_call_info_seize_held_call: failed to update owner\n" ); + pkg_free(replaces_hdr.s); + goto done; } - rc = 1; - } else { - app = sca_appearance_seize_index_unsafe( sca, aor, contact_uri, - call_info->index, slot_idx, NULL ); - if ( app == NULL ) { - LM_ERR( "sca_call_info_uri_update: failed to seize index %d " - "for %.*s", call_info->index, STR_FMT( contact_uri )); - goto done; - } - - LM_DBG( "sca_call_info_uri_update: seized %d for %.*s: From: <%.*s> " - "To: <%.*s> Call-ID: <%.*s> Dialog: <%.*s>" , app->index, - STR_FMT( &app->owner ), STR_FMT( &from->uri ), - STR_FMT( &to->uri ), STR_FMT( call_id ), - STR_FMT( &app->dialog.id )); - - if ( sca_appearance_update_unsafe( app, - SCA_APPEARANCE_STATE_ACTIVE_PENDING, - &from->display, &from->uri, &dialog, contact_uri, - &from->uri ) < 0 ) { - sca_appearance_state_to_str( call_info->state, &state_str ); - LM_ERR( "sca_call_info_uri_update: failed to update appearance " - "%.*s appearance-index %d with dialog id %.*s to " - "state %.*s", STR_FMT( &app->owner ), app->index, - STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); - goto done; + if (sca_appearance_update_dialog_unsafe(app, &msg->callid->body, + &from->tag_value, &to->tag_value) < 0) { + LM_ERR( "sca_call_info_seize_held_call: failed to update dialog\n" ); + goto done; + } + + app->flags |= SCA_APPEARANCE_FLAG_OWNER_PENDING; + sca_appearance_update_state_unsafe(app, SCA_APPEARANCE_STATE_ACTIVE); + + sca_hash_table_unlock_index(sca->appearances, slot_idx); + slot_idx = -1; + + if (callee_aor.s != NULL && callee_aor.len > 0) { + if (sca_uri_lock_if_shared_appearance(sca, &callee_aor, &slot_idx)) { + app = sca_appearance_for_tags_unsafe(sca, &callee_aor, &prev_callid, + &prev_totag, NULL, slot_idx); + if (app == NULL) { + LM_ERR( "sca_call_info_seize_held_call: failed to find " + "appearance of %.*s with dialog %.*s;%.*s\n", + STR_FMT( &callee_aor ), STR_FMT( &prev_callid ), + STR_FMT( &prev_totag )); + goto done; + } + + app->flags |= SCA_APPEARANCE_FLAG_CALLEE_PENDING; + + if (sca_appearance_update_callee_unsafe(app, contact_uri) < 0) { + LM_ERR( "sca_call_info_seize_held_call: " + "failed to update callee\n" ); + goto done; + } + if (sca_appearance_update_dialog_unsafe(app, &msg->callid->body, + &to->tag_value, &from->tag_value) < 0) { + LM_ERR( "sca_call_info_seize_held_call: " + "failed to update dialog\n" ); + goto done; + } + } } rc = 1; - } -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } + + return (rc); +} + +static int sca_call_info_uri_update(str *aor, sca_call_info *call_info, + struct to_body *from, struct to_body *to, str *contact_uri, + str *call_id) { + sca_appearance *app; + sca_dialog dialog; + str state_str; + str *from_tag = &from->tag_value; + str *to_tag = &to->tag_value; + char dlg_buf[1024]; + int slot_idx = -1; + int rc = -1; + + assert(aor != NULL); + assert(call_info != NULL); + + LM_DBG( "sca_call_info_uri_update for %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-ID: %.*s Call-Info: appearance-index=%d\n", + STR_FMT( aor ), STR_FMT( &from->uri ), STR_FMT( &to->uri ), + STR_FMT( contact_uri ), STR_FMT( call_id ), call_info->index ); + + if (!sca_uri_is_shared_appearance(sca, aor)) { + return (1); + } + + dialog.id.s = dlg_buf; + if (sca_dialog_build_from_tags(&dialog, sizeof(dlg_buf), call_id, from_tag, + to_tag) < 0) { + LM_ERR( "sca_call_info_uri_update: Failed to build dialog from tags\n" ); + return (-1); + } + + slot_idx = sca_hash_table_index_for_key(sca->appearances, aor); + sca_hash_table_lock_index(sca->appearances, slot_idx); + + app = sca_appearance_for_index_unsafe(sca, aor, call_info->index, slot_idx); + if (app == NULL) { + LM_DBG( "sca_call_info_uri_update: no appearance found for %.*s " + "index %d, looking up by dialog...\n", STR_FMT( aor ), + call_info->index ); + app = sca_appearance_for_dialog_unsafe(sca, aor, &dialog, slot_idx); + } + if (app != NULL) { + LM_DBG( "sca_call_info_uri_update: setting owner to %.*s\n", + STR_FMT( contact_uri )); + + if (sca_appearance_update_unsafe(app, call_info->state, NULL, NULL, + &dialog, contact_uri, NULL) < 0) { + sca_appearance_state_to_str(call_info->state, &state_str); + LM_ERR( "sca_call_info_uri_update: failed to update appearance " + "%.*s appearance-index %d with dialog id %.*s to " + "state %.*s\n", STR_FMT( &app->owner ), app->index, + STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); + goto done; + } + + rc = 1; + } else { + app = sca_appearance_seize_index_unsafe(sca, aor, contact_uri, + call_info->index, slot_idx, NULL); + if (app == NULL) { + LM_ERR( "sca_call_info_uri_update: failed to seize index %d " + "for %.*s\n", call_info->index, STR_FMT( contact_uri )); + goto done; + } - return( rc ); + LM_DBG( "sca_call_info_uri_update: seized %d for %.*s: From: <%.*s> " + "To: <%.*s> Call-ID: <%.*s> Dialog: <%.*s>\n" , app->index, + STR_FMT( &app->owner ), STR_FMT( &from->uri ), + STR_FMT( &to->uri ), STR_FMT( call_id ), + STR_FMT( &app->dialog.id )); + + if (sca_appearance_update_unsafe(app, + SCA_APPEARANCE_STATE_ACTIVE_PENDING, &from->display, &from->uri, + &dialog, contact_uri, &from->uri) < 0) { + sca_appearance_state_to_str(call_info->state, &state_str); + LM_ERR( "sca_call_info_uri_update: failed to update appearance " + "%.*s appearance-index %d with dialog id %.*s to " + "state %.*s\n", STR_FMT( &app->owner ), app->index, + STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); + goto done; + } + + rc = 1; + } + + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } + + return (rc); } - static int -sca_call_info_is_line_seize_reinvite( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, str *to_aor ) -{ - str *ruri; - str ruri_aor; - int state; - - /* - * a handset in an SCA group is attempting to seize a held line if: - * the RURI, From URI and To URI are identical; - * the above are SCA AoRs; - * there is no to-tag; - * a Call-Info header is present - */ - - if ( SCA_CALL_INFO_EMPTY( call_info )) { - return( 0 ); - } - if ( !SCA_STR_EMPTY( &to->tag_value )) { - return( 0 ); - } - - ruri = GET_RURI( msg ); - if ( sca_uri_extract_aor( ruri, &ruri_aor ) < 0 ) { - LM_ERR( "sca_call_info_is_line_seize_reinvite: failed to extract " - "AoR from RURI %.*s", STR_FMT( ruri )); - return( 0 ); - } - - if ( !SCA_STR_EQ( from_aor, to_aor )) { - return( 0 ); - } - - state = sca_appearance_state_for_index( sca, from_aor, call_info->index ); - if ( state != SCA_APPEARANCE_STATE_HELD ) { - LM_DBG( "sca_call_info_is_line_seize_reinvite: new INVITE to " - "%.*s from %.*s appearance-index %d (not seizing held line)", - STR_FMT( to_aor ), STR_FMT( from_aor ), call_info->index ); - return( 0 ); - } - - return( 1 ); +static int sca_call_info_is_line_seize_reinvite(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor) { + str *ruri; + str ruri_aor; + int state; + + LM_DBG( "For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Call-Info: appearance-index=%d", + STR_FMT( from_aor ), STR_FMT( to_aor ), STR_FMT( &from->uri ), + STR_FMT( &to->uri ), call_info->index ); + + /* + * a handset in an SCA group is attempting to seize a held line if: + * the RURI, From URI and To URI are identical; + * the above are SCA AoRs; + * there is no to-tag; + * a Call-Info header is present + */ + + if (SCA_CALL_INFO_EMPTY(call_info)) { + return (0); + } + if (!SCA_STR_EMPTY(&to->tag_value)) { + return (0); + } + + ruri = GET_RURI(msg); + if (sca_uri_extract_aor(ruri, &ruri_aor) < 0) { + LM_ERR( "sca_call_info_is_line_seize_reinvite: failed to extract " + "AoR from RURI %.*s\n", STR_FMT( ruri )); + return (0); + } + + if (!SCA_STR_EQ(from_aor, to_aor)) { + return (0); + } + + state = sca_appearance_state_for_index(sca, from_aor, call_info->index); + if (state != SCA_APPEARANCE_STATE_HELD) { + LM_DBG( "sca_call_info_is_line_seize_reinvite: new INVITE to " + "%.*s from %.*s appearance-index %d (not seizing held line)\n", + STR_FMT( to_aor ), STR_FMT( from_aor ), call_info->index ); + return (0); + } + LM_DBG( "reINVITE to %.*s from %.*s appearance-index %d (seizing held line)\n", + STR_FMT( to_aor ), STR_FMT( from_aor ), call_info->index ); + + return (1); } /* * to be invoked only by proxy-generated replies with error status codes */ - static void -sca_call_info_local_error_reply_handler( sip_msg_t *msg, int status ) -{ - struct to_body *from; - struct to_body *to; - sca_appearance *app; - str aor = STR_NULL; - str contact_uri = STR_NULL; - int rc; - - if ( sca_get_msg_from_header( msg, &from ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to get From header from " - "request before stateless reply with %d", status ); - return; - } - if ( sca_uri_extract_aor( &from->uri, &aor ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to extract AoR " - "from URI %.*s", STR_FMT( &from->uri )); - return; - } - - if ( !sca_uri_is_shared_appearance( sca, &aor )) { - /* LM_DBG( "sca_call_info_sl_reply_cb: ignoring non-shared appearance " - "%.*s", STR_FMT( &aor )); */ - return; - } - - if ( sca_get_msg_contact_uri( msg, &contact_uri ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to get Contact from " - "request before stateless reply with %d", status ); - return; - } - - if ( sca_get_msg_to_header( msg, &to ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to get To header from " - "request before stateless reply with %d", status ); - return; - } - - /* - * two typical cases to handle. in the first case, we haven't dropped - * our line-seize subscription because a transaction exists but we - * never got a provisional 18x response before calling t_reply. calling - * sca_subscription_terminate will drop the subscription and release - * the seized appearance. - * - * in the second case, we got a 18x response and terminated the - * line-seize subscription, so we need to look up the appearance by - * tags in order to release it. - */ - rc = sca_subscription_terminate( sca, &aor, - SCA_EVENT_TYPE_LINE_SEIZE, &contact_uri, - SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, - SCA_SUBSCRIPTION_TERMINATE_OPT_DEFAULT ); - if ( rc < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to terminate " - "line-seize subscription for %.*s", STR_FMT( &contact_uri )); - } else if ( rc == 0 ) { - /* no line-seize subscription found */ - app = sca_appearance_unlink_by_tags( sca, &aor, - &msg->callid->body, &from->tag_value, &to->tag_value ); - if ( app ) { - sca_appearance_free( app ); - if ( sca_notify_call_info_subscribers( sca, &aor ) < 0 ) { - LM_ERR( "sca_call_info_local_error_reply: failed to send " - "call-info NOTIFY to %.*s subscribers", - STR_FMT( &aor )); - } - } - } +static void sca_call_info_local_error_reply_handler(sip_msg_t *msg, int status) { + struct to_body *from; + struct to_body *to; + sca_appearance *app; + str aor = STR_NULL; + str contact_uri = STR_NULL; + int rc; + + if (sca_get_msg_from_header(msg, &from) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to get From header from " + "request before stateless reply with %d\n", status ); + return; + } + if (sca_uri_extract_aor(&from->uri, &aor) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to extract AoR " + "from URI %.*s\n", STR_FMT( &from->uri )); + return; + } + + if (!sca_uri_is_shared_appearance(sca, &aor)) { + /* LM_DBG( "sca_call_info_sl_reply_cb: ignoring non-shared appearance " + "%.*s", STR_FMT( &aor )); */ + return; + } + + if (sca_get_msg_contact_uri(msg, &contact_uri) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to get Contact from " + "request before stateless reply with %d\n", status ); + return; + } + + if (sca_get_msg_to_header(msg, &to) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to get To header from " + "request before stateless reply with %d\n", status ); + return; + } + + /* + * two typical cases to handle. in the first case, we haven't dropped + * our line-seize subscription because a transaction exists but we + * never got a provisional 18x response before calling t_reply. calling + * sca_subscription_terminate will drop the subscription and release + * the seized appearance. + * + * in the second case, we got a 18x response and terminated the + * line-seize subscription, so we need to look up the appearance by + * tags in order to release it. + */ + rc = sca_subscription_terminate(sca, &aor, SCA_EVENT_TYPE_LINE_SEIZE, + &contact_uri, SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, + SCA_SUBSCRIPTION_TERMINATE_OPT_DEFAULT); + if (rc < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to terminate " + "line-seize subscription for %.*s\n", STR_FMT( &contact_uri )); + } else if (rc == 0) { + /* no line-seize subscription found */ + app = sca_appearance_unlink_by_tags(sca, &aor, &msg->callid->body, + &from->tag_value, &to->tag_value); + if (app) { + sca_appearance_free(app); + if (sca_notify_call_info_subscribers(sca, &aor) < 0) { + LM_ERR( "sca_call_info_local_error_reply: failed to send " + "call-info NOTIFY to %.*s subscribers\n", + STR_FMT( &aor )); + } + } + } } - void -sca_call_info_response_ready_cb( struct cell *t, int type, - struct tmcb_params *params ) -{ - if ( !(type & TMCB_RESPONSE_READY)) { - return; - } +void sca_call_info_response_ready_cb(struct cell *t, int type, + struct tmcb_params *params) { + if (!(type & TMCB_RESPONSE_READY)) { + return; + } - if ( params->code < 400 ) { - /* non-error final response: 1xx, 2xx, 3xx */ - return; - } + if (params->code < 400) { + /* non-error final response: 1xx, 2xx, 3xx */ + return; + } - sca_call_info_local_error_reply_handler( params->req, params->code ); + sca_call_info_local_error_reply_handler(params->req, params->code); } - int -sca_call_info_invite_request_handler( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, str *to_aor, - str *contact_uri ) -{ - sca_dialog dialog; - char dlg_buf[ 1024 ]; - str state_str = STR_NULL; - int state = SCA_APPEARANCE_STATE_UNKNOWN; - int rc = -1; - - /* - * if we get here, one of the legs is an SCA endpoint. we want to know - * when the e2e ACK comes in so we can notify other members of the group. - */ - if ( sca->tm_api->register_tmcb( msg, NULL, TMCB_E2EACK_IN, - sca_call_info_ack_cb, NULL, NULL ) < 0 ) { - LM_ERR( "sca_call_info_invite_request_handler: failed to register " - "callback for INVITE %.*s ACK", STR_FMT( from_aor )); - goto done; - } - - if ( !SCA_CALL_INFO_IS_SHARED_CALLER( call_info )) { - /* caller isn't SCA, no more to do. update callee in reply handler. */ +int sca_call_info_invite_request_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + sca_dialog dialog; + char dlg_buf[1024]; + str state_str = STR_NULL; + int state = SCA_APPEARANCE_STATE_UNKNOWN; + int rc = -1; + + LM_DBG( "For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d", + STR_FMT( from_aor ), STR_FMT( to_aor ),STR_FMT( &from->uri ), STR_FMT( &to->uri ), + STR_FMT( contact_uri ), call_info->index ); + + /* + * if we get here, one of the legs is an SCA endpoint. we want to know + * when the e2e ACK comes in so we can notify other members of the group. + */ + if (sca->tm_api->register_tmcb(msg, NULL, TMCB_E2EACK_IN, + sca_call_info_ack_cb, NULL, NULL) < 0) { + LM_ERR( "sca_call_info_invite_request_handler: failed to register " + "callback for INVITE %.*s ACK\n", STR_FMT( from_aor )); + goto done; + } + + if (!SCA_CALL_INFO_IS_SHARED_CALLER(call_info)) { + /* caller isn't SCA, no more to do. update callee in reply handler. */ + rc = 1; + goto done; + } + + /* + * register callback to handle error responses sent from script using + * t_reply. TMCB_RESPONSE_READY will only be called from t_reply(), + * so relayed responses from upstream UASs will not triggers this. + */ + if (sca->tm_api->register_tmcb(msg, NULL, TMCB_RESPONSE_READY, + sca_call_info_response_ready_cb, NULL, NULL) < 0) { + LM_ERR( "sca_call_info_invite_request_handler: failed to register " + "callback for INVITE %.*s ACK\n", STR_FMT( from_aor )); + goto done; + } + + if (sca_call_is_held(msg)) { + state = SCA_APPEARANCE_STATE_HELD; + if (call_info->state == SCA_APPEARANCE_STATE_HELD_PRIVATE) { + state = SCA_APPEARANCE_STATE_HELD_PRIVATE; + } else { + state = SCA_APPEARANCE_STATE_HELD; + } + } else if (!SCA_STR_EMPTY(&to->tag_value)) { + /* this is a reINVITE from an SCA line that put the call on hold */ + state = SCA_APPEARANCE_STATE_ACTIVE; + } else if (sca_call_info_is_line_seize_reinvite(msg, call_info, from, to, + from_aor, to_aor)) { + rc = sca_call_info_seize_held_call(msg, call_info, from, to, from_aor, + to_aor, contact_uri); + if (rc <= 0) { + goto done; + } + } + /* otherwise, this is an initial INVITE */ + + dialog.id.s = dlg_buf; + if (sca_dialog_build_from_tags(&dialog, sizeof(dlg_buf), &msg->callid->body, + &from->tag_value, &to->tag_value) < 0) { + LM_ERR( "Failed to build dialog from tags\n" ); + return (-1); + } + + if (sca_appearance_update_index(sca, from_aor, call_info->index, state, + NULL, NULL, &dialog) != SCA_APPEARANCE_OK) { + sca_appearance_state_to_str(state, &state_str); + LM_ERR( "Failed to update %.*s appearance-index %d to %.*s\n", + STR_FMT( from_aor ), call_info->index, + STR_FMT( &state_str )); + } + + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on INVITE\n", + STR_FMT( from_aor )); + goto done; + } + rc = 1; - goto done; - } - - /* - * register callback to handle error responses sent from script using - * t_reply. TMCB_RESPONSE_READY will only be called from t_reply(), - * so relayed responses from upstream UASs will not triggers this. - */ - if ( sca->tm_api->register_tmcb( msg, NULL, TMCB_RESPONSE_READY, - sca_call_info_response_ready_cb, NULL, NULL ) < 0 ) { - LM_ERR( "sca_call_info_invite_request_handler: failed to register " - "callback for INVITE %.*s ACK", STR_FMT( from_aor )); - goto done; - } - - if ( sca_call_is_held( msg )) { - state = SCA_APPEARANCE_STATE_HELD; - if ( call_info->state == SCA_APPEARANCE_STATE_HELD_PRIVATE ) { - state = SCA_APPEARANCE_STATE_HELD_PRIVATE; - } else { - state = SCA_APPEARANCE_STATE_HELD; - } - } else if ( !SCA_STR_EMPTY( &to->tag_value )) { - /* this is a reINVITE from an SCA line that put the call on hold */ - state = SCA_APPEARANCE_STATE_ACTIVE; - } else if ( sca_call_info_is_line_seize_reinvite( msg, call_info, - from, to, from_aor, to_aor )) { - rc = sca_call_info_seize_held_call( msg, call_info, from, to, - from_aor, to_aor, contact_uri ); - if ( rc <= 0 ) { - goto done; - } - } - /* otherwise, this is an initial INVITE */ - - dialog.id.s = dlg_buf; - if ( sca_dialog_build_from_tags( &dialog, sizeof( dlg_buf ), - &msg->callid->body, &from->tag_value, &to->tag_value ) < 0 ) { - LM_ERR( "Failed to build dialog from tags" ); - return( -1 ); - } - - if ( sca_appearance_update_index( sca, from_aor, call_info->index, - state, NULL, NULL, &dialog ) != SCA_APPEARANCE_OK ) { - sca_appearance_state_to_str( state, &state_str ); - LM_ERR( "Failed to update %.*s appearance-index %d to %.*s", - STR_FMT( from_aor ), call_info->index, - STR_FMT( &state_str )); - } - - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on INVITE", - STR_FMT( from_aor )); - goto done; - } - - rc = 1; - -done: - return( rc ); + + done: return (rc); } - int -sca_call_info_invite_reply_18x_handler( sip_msg_t *msg, - sca_call_info *call_info, struct to_body *from, struct to_body *to, - str *from_aor, str *to_aor, str *contact_uri ) -{ - sca_appearance *app = NULL; - str owner = STR_NULL; - int state = SCA_APPEARANCE_STATE_UNKNOWN; - int slot_idx = -1; - int rc = -1; - int notify = 0; - - switch ( msg->REPLY_STATUS ) { - case 180: - case 183: - state = SCA_APPEARANCE_STATE_PROGRESSING; - break; - - default: - goto done; - } - - if ( !sca_uri_lock_if_shared_appearance( sca, from_aor, &slot_idx )) { - LM_DBG( "sca_call_info_invite_reply_18x_handler: From-AoR %.*s is " - "not a shared appearance", STR_FMT( from_aor )); - return( 1 ); - } - - app = sca_appearance_for_tags_unsafe( sca, from_aor, &msg->callid->body, - &from->tag_value, &to->tag_value, slot_idx ); - if ( app == NULL ) { - goto done; - } - - /* clone appearance owner for subscription termination below */ - owner.s = (char *)pkg_malloc( app->owner.len ); - if ( owner.s == NULL ) { - LM_ERR( "sca_call_info_invite_18x_reply_handler: failed to " - "pkg_malloc %d bytes to clone <%.*s>", - app->owner.len, STR_FMT( &app->owner )); - goto done; - } - SCA_STR_COPY( &owner, &app->owner ); - - notify = ( app->state != state ); - if ( notify ) { - sca_appearance_update_state_unsafe( app, state ); - } - rc = 1; - -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } - - if ( rc > 0 && notify && owner.s != NULL ) { - if ( sca_subscription_terminate( sca, from_aor, - SCA_EVENT_TYPE_LINE_SEIZE, &owner, - SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, - SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE ) < 0 ) { - LM_ERR( "sca_call_info_invite_reply_18x_handler: " - "failed to terminate line-seize subscription for %.*s", - STR_FMT( &owner )); - rc = -1; - } - - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "sca_call_info_invite_reply_18x_handler: " - "failed to NOTIFY %.*s call-info subscribers", - STR_FMT( from_aor )); - rc = -1; - } - } - if ( owner.s != NULL ) { - pkg_free( owner.s ); - } - - return( rc ); +int sca_call_info_invite_reply_18x_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + sca_appearance *app = NULL; + str owner = STR_NULL; + int state = SCA_APPEARANCE_STATE_UNKNOWN; + int slot_idx = -1; + int rc = -1; + int notify = 0; + + LM_DBG( "For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d", + STR_FMT( from_aor ), STR_FMT( to_aor ),STR_FMT( &from->uri ), STR_FMT( &to->uri ), + STR_FMT( contact_uri ), call_info->index ); + + switch (msg->REPLY_STATUS) { + case 180: + case 183: + state = SCA_APPEARANCE_STATE_PROGRESSING; + break; + + default: + goto done; + } + + if (!sca_uri_lock_if_shared_appearance(sca, from_aor, &slot_idx)) { + LM_DBG( "sca_call_info_invite_reply_18x_handler: From-AoR %.*s is " + "not a shared appearance\n", STR_FMT( from_aor )); + return (1); + } + + app = sca_appearance_for_tags_unsafe(sca, from_aor, &msg->callid->body, + &from->tag_value, &to->tag_value, slot_idx); + if (app == NULL) { + goto done; + } + + /* clone appearance owner for subscription termination below */ + owner.s = (char *) pkg_malloc(app->owner.len); + if (owner.s == NULL) { + LM_ERR( "sca_call_info_invite_18x_reply_handler: failed to " + "pkg_malloc %d bytes to clone <%.*s>\n", + app->owner.len, STR_FMT( &app->owner )); + goto done; + } + SCA_STR_COPY(&owner, &app->owner); + + notify = (app->state != state); + if (notify) { + sca_appearance_update_state_unsafe(app, state); + } + rc = 1; + + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } + + if (rc > 0 && notify && owner.s != NULL) { + if (sca_subscription_terminate(sca, from_aor, SCA_EVENT_TYPE_LINE_SEIZE, + &owner, SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, + SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE) < 0) { + LM_ERR( "sca_call_info_invite_reply_18x_handler: " + "failed to terminate line-seize subscription for %.*s\n", + STR_FMT( &owner )); + rc = -1; + } + + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "sca_call_info_invite_reply_18x_handler: " + "failed to NOTIFY %.*s call-info subscribers\n", + STR_FMT( from_aor )); + rc = -1; + } + } + if (owner.s != NULL) { + pkg_free(owner.s); + } + + return (rc); } - static int -sca_call_info_insert_asserted_identity( sip_msg_t *msg, str *display, - int ua_type ) -{ - struct lump *anchor; - str aor = STR_NULL; - str hdr = STR_NULL; - int len; - int rc = -1; - - anchor = anchor_lump( msg, msg->eoh - msg->buf, 0, HDR_OTHER_T ); - if ( anchor == NULL ) { - LM_ERR( "Failed to anchor lump" ); - goto done; - } - - if ( sca_create_canonical_aor_for_ua( msg, &aor, ua_type ) < 0 ) { - LM_ERR( "sca_call_info_insert_asserted_identity: failed to create " - "canonical AoR" ); - goto done; - } +static int sca_call_info_insert_asserted_identity(sip_msg_t *msg, str *display, + int ua_type) { + struct lump *anchor; + str aor = STR_NULL; + str hdr = STR_NULL; + int len; + int rc = -1; + + anchor = anchor_lump(msg, msg->eoh - msg->buf, 0, HDR_OTHER_T); + if (anchor == NULL) { + LM_ERR( "Failed to anchor lump\n" ); + goto done; + } + + if (sca_create_canonical_aor_for_ua(msg, &aor, ua_type) < 0) { + LM_ERR( "sca_call_info_insert_asserted_identity: failed to create " + "canonical AoR\n" ); + goto done; + } #define SCA_P_ASSERTED_IDENTITY_HDR_PREFIX "P-Asserted-Identity: " #define SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN strlen("P-Asserted-Identity: ") - len = SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN; - len += display->len; - /* +1 for space, +1 for <, + 1 for > */ - len += 1 + 1 + aor.len + 1 + CRLF_LEN; + len = SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN; + len += display->len; + /* +1 for space, +1 for <, + 1 for > */ + len += 1 + 1 + aor.len + 1 + CRLF_LEN; - hdr.s = (char *)pkg_malloc( len ); - if ( hdr.s == NULL ) { - LM_ERR( "insert_asserted_identity: pkg_malloc %d bytes failed", len ); - goto done; - } + hdr.s = (char *) pkg_malloc(len); + if (hdr.s == NULL) { + LM_ERR( "insert_asserted_identity: pkg_malloc %d bytes failed\n", len ); + goto done; + } - memcpy( hdr.s, SCA_P_ASSERTED_IDENTITY_HDR_PREFIX, - SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN ); - hdr.len = SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN; + memcpy(hdr.s, SCA_P_ASSERTED_IDENTITY_HDR_PREFIX, + SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN); + hdr.len = SCA_P_ASSERTED_IDENTITY_HDR_PREFIX_LEN; - SCA_STR_APPEND( &hdr, display ); + SCA_STR_APPEND(&hdr, display); - *(hdr.s + hdr.len) = ' '; - hdr.len++; + *(hdr.s + hdr.len) = ' '; + hdr.len++; - *(hdr.s + hdr.len) = '<'; - hdr.len++; + *(hdr.s + hdr.len) = '<'; + hdr.len++; - SCA_STR_APPEND( &hdr, &aor ); + SCA_STR_APPEND(&hdr, &aor); - *(hdr.s + hdr.len) = '>'; - hdr.len++; + *(hdr.s + hdr.len) = '>'; + hdr.len++; - memcpy( hdr.s + hdr.len, CRLF, CRLF_LEN ); - hdr.len += CRLF_LEN; + memcpy(hdr.s + hdr.len, CRLF, CRLF_LEN); + hdr.len += CRLF_LEN; - /* append the PAI header before the sdp body */ - if ( insert_new_lump_before( anchor, hdr.s, hdr.len, HDR_PAI_T ) == NULL ) { - LM_ERR( "Failed to add PAI header %.*s", STR_FMT( &hdr )); - goto done; - } + /* append the PAI header before the sdp body */ + if (insert_new_lump_before(anchor, hdr.s, hdr.len, HDR_PAI_T) == NULL) { + LM_ERR( "Failed to add PAI header %.*s\n", STR_FMT( &hdr )); + goto done; + } - rc = 1; + rc = 1; -done: - if ( aor.s != NULL ) { - pkg_free( aor.s ); - } - if ( rc < 0 && hdr.s != NULL ) { - pkg_free( hdr.s ); - } + done: if (aor.s != NULL) { + pkg_free(aor.s); + } + if (rc < 0 && hdr.s != NULL) { + pkg_free(hdr.s); + } - return( rc ); + return (rc); } - static int -sca_call_info_invite_reply_200_handler( sip_msg_t *msg, - sca_call_info *call_info, struct to_body *from, struct to_body *to, - str *from_aor, str *to_aor, str *contact_uri ) -{ - sca_appearance *app; - sca_dialog dialog; - sip_uri_t c_uri; - char dlg_buf[ 1024 ]; - str app_uri_aor = STR_NULL; - str state_str = STR_NULL; - int state = SCA_APPEARANCE_STATE_UNKNOWN; - int slot_idx = -1; - int rc = -1; - - if ( SCA_CALL_INFO_IS_SHARED_CALLEE( call_info )) { - rc = sca_call_info_uri_update( to_aor, call_info, from, to, - contact_uri, &msg->callid->body ); - } - - if ( !SCA_CALL_INFO_IS_SHARED_CALLER( call_info )) { - goto done; - } - - if ( sca_call_info_insert_asserted_identity( msg, &to->display, - SCA_AOR_TYPE_UAS ) < 0 ) { - LM_WARN( "sca_call_info_invite_reply_200_handler: failed to " - "add P-Asserted-Identity header to response from %.*s", - STR_FMT( contact_uri )); - } - - /* - * XXX in a reply with no Call-Info header, we look for a matching - * dialog for the From URI. if we don't find one, this isn't an SCA - * call, and we're done processing. - * - * if there *is* a Call-Info header, we update the appearance state of - * the index in the header for the To URI (appearance owned by the URI - * in the Contact header). we still then need to check to see if the - * From URI is an SCA line, and update state if it is. - */ - - slot_idx = sca_hash_table_index_for_key( sca->appearances, from_aor ); - sca_hash_table_lock_index( sca->appearances, slot_idx ); - - app = sca_appearance_for_tags_unsafe( sca, from_aor, - &msg->callid->body, &from->tag_value, NULL, slot_idx ); - if ( app == NULL ) { - /* no SCA line is involved with this call */ - rc = 1; - goto done; - } - - if ( !sca_appearance_is_held( app )) { - state = SCA_APPEARANCE_STATE_ACTIVE; - } - /* if a Call-Info header is present, app-index goes to Contact */ - - dialog.id.s = dlg_buf; - if ( sca_dialog_build_from_tags( &dialog, sizeof( dlg_buf ), - &msg->callid->body, &from->tag_value, &to->tag_value ) < 0 ) { - LM_ERR( "sca_call_info_invite_handler: failed to build sca_dialog " - "from tags" ); - rc = -1; - goto done; - } - - if ( parse_uri( contact_uri->s, contact_uri->len, &c_uri ) < 0 ) { - LM_ERR( "sca_call_info_invite_200_reply_handler: " - "parse_uri <%.*s> failed", STR_FMT( contact_uri )); - goto done; - } - if ( sca_create_canonical_aor( msg, &app_uri_aor ) < 0 ) { - LM_ERR( "sca_call_info_invite_200_reply_handler: " - "sca_create_canonical_aor failed" ); - goto done; - } - - if ( sca_appearance_update_unsafe( app, state, &to->display, &app_uri_aor, - &dialog, NULL, contact_uri ) < 0 ) { - sca_appearance_state_to_str( state, &state_str ); - LM_ERR( "sca_call_info_invite_handler: failed to update appearance " - "%.*s appearance-index %d with dialog id %.*s to " - "state %.*s", STR_FMT( &app->owner ), app->index, - STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); - rc = -1; - goto done; - } +static int sca_call_info_invite_reply_200_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + sca_appearance *app; + sca_dialog dialog; + sip_uri_t c_uri; + char dlg_buf[1024]; + str app_uri_aor = STR_NULL; + str state_str = STR_NULL; + int state = SCA_APPEARANCE_STATE_UNKNOWN; + int slot_idx = -1; + int rc = -1; + + LM_DBG( "For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d\n", + STR_FMT( from_aor ), STR_FMT( to_aor ),STR_FMT( &from->uri ), STR_FMT( &to->uri ), + STR_FMT( contact_uri ), call_info->index ); + + if (SCA_CALL_INFO_IS_SHARED_CALLEE(call_info) && (!SCA_STR_EQ(from_aor, to_aor))) { + rc = sca_call_info_uri_update(to_aor, call_info, from, to, contact_uri, + &msg->callid->body); + } - rc = 1; + if (!SCA_CALL_INFO_IS_SHARED_CALLER(call_info)) { + goto done; + } -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } - if ( app_uri_aor.s != NULL ) { - pkg_free( app_uri_aor.s ); - } + if (sca_call_info_insert_asserted_identity(msg, &to->display, + SCA_AOR_TYPE_UAS) < 0) { + LM_WARN( "sca_call_info_invite_reply_200_handler: failed to " + "add P-Asserted-Identity header to response from %.*s\n", + STR_FMT( contact_uri )); + } - if ( rc == 1 ) { - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on " - "200 OK reply to INVITE", STR_FMT( from_aor )); - rc = -1; + /* + * XXX in a reply with no Call-Info header, we look for a matching + * dialog for the From URI. if we don't find one, this isn't an SCA + * call, and we're done processing. + * + * if there *is* a Call-Info header, we update the appearance state of + * the index in the header for the To URI (appearance owned by the URI + * in the Contact header). we still then need to check to see if the + * From URI is an SCA line, and update state if it is. + */ + + slot_idx = sca_hash_table_index_for_key(sca->appearances, from_aor); + sca_hash_table_lock_index(sca->appearances, slot_idx); + + app = sca_appearance_for_tags_unsafe(sca, from_aor, &msg->callid->body, + &from->tag_value, NULL, slot_idx); + if (app == NULL) { + /* no SCA line is involved with this call */ + rc = 1; + goto done; } - } - return( rc ); -} + if (!sca_appearance_is_held(app)) { + state = SCA_APPEARANCE_STATE_ACTIVE; + } + /* if a Call-Info header is present, app-index goes to Contact */ + + dialog.id.s = dlg_buf; + if (sca_dialog_build_from_tags(&dialog, sizeof(dlg_buf), &msg->callid->body, + &from->tag_value, &to->tag_value) < 0) { + LM_ERR( "sca_call_info_invite_handler: failed to build sca_dialog " + "from tags\n" ); + rc = -1; + goto done; + } + + if (parse_uri(contact_uri->s, contact_uri->len, &c_uri) < 0) { + LM_ERR( "sca_call_info_invite_200_reply_handler: " + "parse_uri <%.*s> failed\n", STR_FMT( contact_uri )); + goto done; + } + if (sca_create_canonical_aor(msg, &app_uri_aor) < 0) { + LM_ERR( "sca_call_info_invite_200_reply_handler: " + "sca_create_canonical_aor failed\n" ); + goto done; + } + + if (SCA_STR_EQ(from_aor, to_aor)) { + if (sca_appearance_update_unsafe(app, state, NULL, NULL, + &dialog, NULL, contact_uri) < 0) { + sca_appearance_state_to_str(state, &state_str); + LM_ERR( "sca_call_info_invite_handler: failed to update appearance " + "%.*s appearance-index %d with dialog id %.*s to " + "state %.*s", STR_FMT( &app->owner ), app->index, + STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); + rc = -1; + goto done; + } + } else { + if (sca_appearance_update_unsafe(app, state, &to->display, &app_uri_aor, + &dialog, NULL, contact_uri) < 0) { + sca_appearance_state_to_str(state, &state_str); + LM_ERR( "sca_call_info_invite_handler: failed to update appearance " + "%.*s appearance-index %d with dialog id %.*s to " + "state %.*s\n", STR_FMT( &app->owner ), app->index, + STR_FMT( &app->dialog.id ), STR_FMT( &state_str )); + rc = -1; + goto done; + } + } - static int -sca_call_info_invite_reply_error_handler( sip_msg_t *msg, - sca_call_info *call_info, struct to_body *from, struct to_body *to, - str *from_aor, str *to_aor, str *contact_uri ) -{ - /* - * XXX will need special handling here. must distinguish among the - * following: - * failed initial INVITE - * failed reINVITE from caller retrieving from hold - * failed INVITE retrieving remote UA from SCA hold - * - * for a start, we just deal with the first case. - */ - - sca_appearance *app; - - if ( msg->REPLY_STATUS == 487 ) { - /* reply status for a CANCEL'd INVITE */ - return( 1 ); - } - - if ( sca_uri_is_shared_appearance( sca, from_aor )) { - app = sca_appearance_unlink_by_tags( sca, from_aor, - &msg->callid->body, &from->tag_value, NULL ); - if ( app == NULL ) { - LM_ERR( "sca_call_info_invite_reply_error_handler: failed to " - "look up dialog for failed INVITE %.*s from %.*s", - STR_FMT( &to->uri ), STR_FMT( from_aor )); - return( -1 ); - } - sca_appearance_free( app ); - - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on " - "failed INVITE", STR_FMT( from_aor )); - return( -1 ); - } - } - - return( 1 ); + rc = 1; + + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } + if (app_uri_aor.s != NULL) { + pkg_free(app_uri_aor.s); + } + + if (rc == 1) { + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on " + "200 OK reply to INVITE\n", STR_FMT( from_aor )); + rc = -1; + } + } + + return (rc); } - void -sca_call_info_ack_from_handler( sip_msg_t *msg, str *from_aor, str *to_aor ) -{ - sca_appearance *app; - struct to_body *from; - struct to_body *to; - int slot_idx = -1; - int state = SCA_APPEARANCE_STATE_IDLE; - - if ( sca_get_msg_from_header( msg, &from ) < 0 ) { - LM_ERR( "sca_call_info_ack_cb: failed to get From-header" ); - return; - } - if ( sca_get_msg_to_header( msg, &to ) < 0 ) { - LM_ERR( "sca_call_info_ack_cb: failed to get To-header" ); - return; - } - - if ( sca_uri_lock_if_shared_appearance( sca, from_aor, &slot_idx )) { - app = sca_appearance_for_tags_unsafe( sca, from_aor, - &msg->callid->body, &from->tag_value, NULL, slot_idx ); - if ( app == NULL ) { - LM_ERR( "sca_call_info_ack_cb: No appearance for %.*s matching " - "call-id <%.*s> and from-tag <%.*s>", STR_FMT( from_aor ), - STR_FMT( &msg->callid->body ), STR_FMT( &from->tag_value )); - goto done; - } - +static int sca_call_info_invite_reply_error_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { /* - * Polycom's music-on-hold implementation uses an INVITE with - * an empty body to get the remote party's SDP info, then INVITEs - * a pre-defined URI on a media server, using the remote party's - * SDP as the INVITE body. the media server streams hold music to - * the remote party. + * XXX will need special handling here. must distinguish among the + * following: + * failed initial INVITE + * failed reINVITE from caller retrieving from hold + * failed INVITE retrieving remote UA from SCA hold * - * because the INVITE that triggers the hold in this case doesn't - * have an SDP body, our check for call hold in the INVITE returns - * false. instead, the ACK from the party placing the call on hold - * includes the sendonly SDP. detect that here, and send NOTIFYs - * as necessary. + * for a start, we just deal with the first case. */ - if ( sca_call_is_held( msg )) { - state = SCA_APPEARANCE_STATE_HELD; - sca_appearance_update_state_unsafe( app, state ); - /* can't send NOTIFYs until we unlock the slot below */ + sca_appearance *app; + + if (msg->REPLY_STATUS == 487) { + /* reply status for a CANCEL'd INVITE */ + return (1); } - } -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); + if (sca_uri_is_shared_appearance(sca, from_aor)) { + app = sca_appearance_unlink_by_tags(sca, from_aor, &msg->callid->body, + &from->tag_value, NULL); + if (app == NULL) { + LM_ERR( "sca_call_info_invite_reply_error_handler: failed to " + "look up dialog for failed INVITE %.*s from %.*s\n", + STR_FMT( &to->uri ), STR_FMT( from_aor )); + return (-1); + } + sca_appearance_free(app); - if ( state != SCA_APPEARANCE_STATE_IDLE ) { - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on INVITE", - STR_FMT( from_aor )); - } + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on " + "failed INVITE\n", STR_FMT( from_aor )); + return (-1); + } } - } -} -/* XXX needs extract routines */ - void -sca_call_info_ack_cb( struct cell *t, int type, struct tmcb_params *params ) -{ - struct to_body *to; - sca_appearance *app = NULL; - str from_aor = STR_NULL; - str to_aor = STR_NULL; - int slot_idx = -1; - - if ( !(type & TMCB_E2EACK_IN)) { - return; - } - - if ( sca_create_canonical_aor( params->req, &from_aor ) < 0 ) { - return; - } - - if ( sca_get_msg_to_header( params->req, &to ) < 0 ) { - LM_ERR( "sca_call_info_ack_cb: failed to get To-header" ); - goto done; - } - if ( sca_uri_extract_aor( &to->uri, &to_aor ) < 0 ) { - LM_ERR( "sca_call_info_ack_cb: failed to extract To AoR from %.*s", - STR_FMT( &to->uri )); - goto done; - } - - sca_call_info_ack_from_handler( params->req, &from_aor, &to_aor ); - - if ( !sca_uri_lock_if_shared_appearance( sca, &to_aor, &slot_idx )) { - LM_DBG( "sca_call_info_ack_cb: %.*s is not a shared appearance", - STR_FMT( &to_aor )); - goto done; - } - - /* on ACK, ensure SCA callee state is promoted to ACTIVE. */ - app = sca_appearance_for_tags_unsafe( sca, &to_aor, - ¶ms->req->callid->body, &to->tag_value, NULL, slot_idx ); - if ( app && app->state == SCA_APPEARANCE_STATE_ACTIVE_PENDING ) { - LM_DBG( "promoting %.*s appearance-index %d to active", - STR_FMT( &to_aor ), app->index ); - sca_appearance_update_state_unsafe( app, SCA_APPEARANCE_STATE_ACTIVE ); - } - - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } - - if ( sca_notify_call_info_subscribers( sca, &to_aor ) < 0 ) { - LM_ERR( "sca_call_info_ack_cb: failed to call-info " - "NOTIFY %.*s subscribers", STR_FMT( &to_aor )); - goto done; - } - -done: - if ( from_aor.s != NULL ) { - pkg_free( from_aor.s ); - } + return (1); } - static int -sca_call_info_invite_handler( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, str *to_aor, - str *contact_uri ) -{ - int rc = -1; - - if ( SCA_STR_EMPTY( contact_uri )) { - LM_DBG( "sca_call_info_invite_handler: Contact header is empty. " - "(From: %.*s To: %.*s)", STR_FMT( from_aor ), - STR_FMT( to_aor )); - return( 1 ); - } - - if ( msg->first_line.type == SIP_REQUEST ) { - rc = sca_call_info_invite_request_handler( msg, call_info, from, to, - from_aor, to_aor, contact_uri ); - } else { - /* XXX replace with dispatch table. */ - switch ( msg->REPLY_STATUS ) { - case 100: - rc = 1; - break; +void sca_call_info_ack_from_handler(sip_msg_t *msg, str *from_aor, str *to_aor) { + sca_appearance *app; + struct to_body *from; + struct to_body *to; + int slot_idx = -1; + int state = SCA_APPEARANCE_STATE_IDLE; - case 180: - case 183: - rc = sca_call_info_invite_reply_18x_handler( msg, call_info, - from, to, from_aor, to_aor, contact_uri ); - break; - - case 200: - rc = sca_call_info_invite_reply_200_handler( msg, call_info, - from, to, from_aor, to_aor, contact_uri ); - break; - - case 300: - case 301: - case 302: - /* - * redirection (at least on Polycoms) does not cause caller to - * release its seized appearance. pass it through. - */ - rc = 1; - break; + if (sca_get_msg_from_header(msg, &from) < 0) { + LM_ERR( "sca_call_info_ack_cb: failed to get From-header\n" ); + return; + } + if (sca_get_msg_to_header(msg, &to) < 0) { + LM_ERR( "sca_call_info_ack_cb: failed to get To-header\n" ); + return; + } - default: - rc = sca_call_info_invite_reply_error_handler( msg, call_info, - from, to, from_aor, to_aor, contact_uri ); - break; + if (sca_uri_lock_if_shared_appearance(sca, from_aor, &slot_idx)) { + app = sca_appearance_for_tags_unsafe(sca, from_aor, &msg->callid->body, + &from->tag_value, NULL, slot_idx); + if (app == NULL) { + LM_ERR( "sca_call_info_ack_cb: No appearance for %.*s matching " + "call-id <%.*s> and from-tag <%.*s>\n", STR_FMT( from_aor ), + STR_FMT( &msg->callid->body ), STR_FMT( &from->tag_value )); + goto done; + } + + /* + * Polycom's music-on-hold implementation uses an INVITE with + * an empty body to get the remote party's SDP info, then INVITEs + * a pre-defined URI on a media server, using the remote party's + * SDP as the INVITE body. the media server streams hold music to + * the remote party. + * + * because the INVITE that triggers the hold in this case doesn't + * have an SDP body, our check for call hold in the INVITE returns + * false. instead, the ACK from the party placing the call on hold + * includes the sendonly SDP. detect that here, and send NOTIFYs + * as necessary. + */ + if (sca_call_is_held(msg)) { + state = SCA_APPEARANCE_STATE_HELD; + sca_appearance_update_state_unsafe(app, state); + + /* can't send NOTIFYs until we unlock the slot below */ + } } - } - return( rc ); + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + + if (state != SCA_APPEARANCE_STATE_IDLE) { + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on INVITE\n", + STR_FMT( from_aor )); + } + } + } } - static int -sca_call_info_bye_handler( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, str *to_aor, - str *contact_uri ) -{ - sca_appearance *app = NULL; - int slot_idx = -1; - int rc = -1; - - if ( msg->first_line.type == SIP_REQUEST ) { - if ( SCA_CALL_INFO_IS_SHARED_CALLER( call_info )) { - slot_idx = sca_uri_lock_shared_appearance( sca, from_aor ); - if ( slot_idx < 0 ) { - LM_ERR( "sca_call_info_bye_handler: failed to acquire " - "lock for %.*s, appearance-index %.d", - STR_FMT( from_aor ), call_info->index ); +/* XXX needs extract routines */ +void sca_call_info_ack_cb(struct cell *t, int type, struct tmcb_params *params) { + struct to_body *to; + sca_appearance *app = NULL; + str from_aor = STR_NULL; + str to_aor = STR_NULL; + int slot_idx = -1; + + if (!(type & TMCB_E2EACK_IN)) { + return; + } + + if (sca_create_canonical_aor(params->req, &from_aor) < 0) { + return; + } + + if (sca_get_msg_to_header(params->req, &to) < 0) { + LM_ERR( "sca_call_info_ack_cb: failed to get To-header\n" ); goto done; - } - - if ( call_info->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY ) { - app = sca_appearance_for_index_unsafe( sca, from_aor, - call_info->index, slot_idx ); - } - if ( app == NULL ) { - /* try to find it by tags */ - app = sca_appearance_for_tags_unsafe( sca, from_aor, - &msg->callid->body, &from->tag_value, NULL, slot_idx ); - } - if ( app == NULL ) { - LM_ERR( "sca_call_info_bye_handler: %.*s " - "dialog leg %.*s;%.*s is not active", - STR_FMT( from_aor ), - STR_FMT( &msg->callid->body ), - STR_FMT( &from->tag_value )); + } + if (sca_uri_extract_aor(&to->uri, &to_aor) < 0) { + LM_ERR( "sca_call_info_ack_cb: failed to extract To AoR from %.*s\n", + STR_FMT( &to->uri )); goto done; - } - - if ( SCA_STR_EQ( &app->dialog.call_id, &msg->callid->body )) { - /* XXX yes, duplicated below, too */ - if ( !sca_appearance_list_unlink_appearance( - app->appearance_list, &app )) { - LM_ERR( "sca_call_info_bye_handler: failed to unlink " - "%.*s appearance-index %d, owner %.*s", - STR_FMT( &app->owner ), app->index, - STR_FMT( &app->owner )); - goto done; - } - sca_appearance_free( app ); + } - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - slot_idx = -1; + sca_call_info_ack_from_handler(params->req, &from_aor, &to_aor); - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers " - "on BYE", STR_FMT( &to->uri )); - goto done; - } - } + if (!sca_uri_lock_if_shared_appearance(sca, &to_aor, &slot_idx)) { + LM_DBG( "sca_call_info_ack_cb: %.*s is not a shared appearance\n", + STR_FMT( &to_aor )); + goto done; } - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - slot_idx = -1; + /* on ACK, ensure SCA callee state is promoted to ACTIVE. */ + app = sca_appearance_for_tags_unsafe(sca, &to_aor, + ¶ms->req->callid->body, &to->tag_value, NULL, slot_idx); + if (app && app->state == SCA_APPEARANCE_STATE_ACTIVE_PENDING) { + LM_DBG( "promoting %.*s appearance-index %d to active\n", + STR_FMT( &to_aor ), app->index ); + sca_appearance_update_state_unsafe(app, SCA_APPEARANCE_STATE_ACTIVE); } - if ( SCA_CALL_INFO_IS_SHARED_CALLEE( call_info )) { - if ( !sca_uri_lock_if_shared_appearance( sca, to_aor, &slot_idx )) { - LM_DBG( "BYE from non-SCA %.*s to non-SCA %.*s", - STR_FMT( from_aor ), STR_FMT( to_aor )); - rc = 1; - goto done; - } - - app = sca_appearance_for_tags_unsafe( sca, to_aor, - &msg->callid->body, &to->tag_value, - NULL, slot_idx ); - if ( app == NULL ) { - LM_INFO( "sca_call_info_bye_handler: no in-use callee " - "appearance for BYE %.*s from %.*s, call-ID %.*s", - STR_FMT( to_aor ), STR_FMT( from_aor ), - STR_FMT( &msg->callid->body )); - rc = 1; + if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } + + if (sca_notify_call_info_subscribers(sca, &to_aor) < 0) { + LM_ERR( "sca_call_info_ack_cb: failed to call-info " + "NOTIFY %.*s subscribers\n", STR_FMT( &to_aor )); goto done; - } - - if ( SCA_STR_EQ( &app->dialog.call_id, &msg->callid->body )) { - if ( !sca_appearance_list_unlink_appearance( - app->appearance_list, &app )) { - LM_ERR( "sca_call_info_bye_handler: failed to unlink " - "%.*s appearance-index %d, owner %.*s", - STR_FMT( &app->owner ), app->index, - STR_FMT( &app->owner )); - goto done; + } + + done: if (from_aor.s != NULL) { + pkg_free(from_aor.s); + } +} + +static int sca_call_info_invite_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + int rc = -1; + + if (SCA_STR_EMPTY(contact_uri)) { + LM_DBG( "sca_call_info_invite_handler: Contact header is empty. " + "(From: %.*s To: %.*s)\n", STR_FMT( from_aor ), + STR_FMT( to_aor )); + return (1); + } + + if (msg->first_line.type == SIP_REQUEST) { + rc = sca_call_info_invite_request_handler(msg, call_info, from, to, + from_aor, to_aor, contact_uri); + } else { + /* XXX replace with dispatch table. */ + switch (msg->REPLY_STATUS) { + case 100: + rc = 1; + break; + + case 180: + case 183: + rc = sca_call_info_invite_reply_18x_handler(msg, call_info, from, + to, from_aor, to_aor, contact_uri); + break; + + case 200: + rc = sca_call_info_invite_reply_200_handler(msg, call_info, from, + to, from_aor, to_aor, contact_uri); + break; + + case 300: + case 301: + case 302: + /* + * redirection (at least on Polycoms) does not cause caller to + * release its seized appearance. pass it through. + */ + rc = 1; + break; + + default: + rc = sca_call_info_invite_reply_error_handler(msg, call_info, from, + to, from_aor, to_aor, contact_uri); + break; } - sca_appearance_free( app ); + } - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - slot_idx = -1; + return (rc); +} - if ( sca_notify_call_info_subscribers( sca, to_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers " - "on BYE", STR_FMT( to_aor )); - goto done; +static int sca_call_info_bye_handler(sip_msg_t *msg, sca_call_info *call_info, + struct to_body *from, struct to_body *to, str *from_aor, str *to_aor, + str *contact_uri) { + sca_appearance *app = NULL; + int slot_idx = -1; + int rc = -1; + + if (msg->first_line.type == SIP_REQUEST) { + if (SCA_CALL_INFO_IS_SHARED_CALLER(call_info)) { + slot_idx = sca_uri_lock_shared_appearance(sca, from_aor); + if (slot_idx < 0) { + LM_ERR( "sca_call_info_bye_handler: failed to acquire " + "lock for %.*s, appearance-index %.d\n", + STR_FMT( from_aor ), call_info->index ); + goto done; + } + + if (call_info->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY) { + app = sca_appearance_for_index_unsafe(sca, from_aor, + call_info->index, slot_idx); + } + if (app == NULL) { + /* try to find it by tags */ + app = sca_appearance_for_tags_unsafe(sca, from_aor, + &msg->callid->body, &from->tag_value, NULL, slot_idx); + } + if (app == NULL) { + LM_ERR( "sca_call_info_bye_handler: %.*s " + "dialog leg %.*s;%.*s is not active\n", + STR_FMT( from_aor ), + STR_FMT( &msg->callid->body ), + STR_FMT( &from->tag_value )); + goto done; + } + + if (SCA_STR_EQ(&app->dialog.call_id, &msg->callid->body)) { + /* XXX yes, duplicated below, too */ + if (!sca_appearance_list_unlink_appearance(app->appearance_list, + &app)) { + LM_ERR( "sca_call_info_bye_handler: failed to unlink " + "%.*s appearance-index %d, owner %.*s\n", + STR_FMT( &app->owner ), app->index, + STR_FMT( &app->owner )); + goto done; + } + sca_appearance_free(app); + + sca_hash_table_unlock_index(sca->appearances, slot_idx); + slot_idx = -1; + + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers " + "on BYE\n", STR_FMT( &to->uri )); + goto done; + } + } } - } - } - } else { - /* this is just a backup to catch anything missed on the BYE request */ - if ( SCA_CALL_INFO_IS_SHARED_CALLEE( call_info )) { - slot_idx = sca_hash_table_index_for_key( sca->appearances, to_aor ); - sca_hash_table_lock_index( sca->appearances, slot_idx ); - - app = sca_appearance_for_index_unsafe( sca, to_aor, - call_info->index, slot_idx ); - if ( app == NULL ) { - app = sca_appearance_for_tags_unsafe( sca, to_aor, - &msg->callid->body, &to->tag_value, - NULL, slot_idx ); - } - if ( app == NULL ) { - LM_DBG( "sca_call_info_bye_handler: no appearance found " - "for callee %.*s, call-ID %.*s", - STR_FMT( to_aor ), STR_FMT( &msg->callid->body )); - rc = 1; - goto done; - } - - LM_INFO( "sca_call_info_bye_handler: found in-use call appearance " - "for callee %.*s, call-ID %.*s", - STR_FMT( to_aor ), STR_FMT( &msg->callid->body )); - - if ( SCA_STR_EQ( &app->dialog.call_id, &msg->callid->body )) { - if ( !sca_appearance_list_unlink_appearance( - app->appearance_list, &app )) { - LM_ERR( "sca_call_info_bye_handler: failed to unlink " - "%.*s appearance-index %d, owner %.*s", - STR_FMT( &app->owner ), app->index, - STR_FMT( &app->owner )); - goto done; + + if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + slot_idx = -1; } - sca_appearance_free( app ); - - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - slot_idx = -1; - - if ( sca_notify_call_info_subscribers( sca, to_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers " - "on BYE", STR_FMT( to_aor )); - goto done; + + if (SCA_CALL_INFO_IS_SHARED_CALLEE(call_info)) { + if (!sca_uri_lock_if_shared_appearance(sca, to_aor, &slot_idx)) { + LM_DBG( "BYE from non-SCA %.*s to non-SCA %.*s\n", + STR_FMT( from_aor ), STR_FMT( to_aor )); + rc = 1; + goto done; + } + + app = sca_appearance_for_tags_unsafe(sca, to_aor, + &msg->callid->body, &to->tag_value, NULL, slot_idx); + if (app == NULL) { + LM_INFO( "sca_call_info_bye_handler: no in-use callee " + "appearance for BYE %.*s from %.*s, call-ID %.*s\n", + STR_FMT( to_aor ), STR_FMT( from_aor ), + STR_FMT( &msg->callid->body )); + rc = 1; + goto done; + } + + if (SCA_STR_EQ(&app->dialog.call_id, &msg->callid->body)) { + if (!sca_appearance_list_unlink_appearance(app->appearance_list, + &app)) { + LM_ERR( "sca_call_info_bye_handler: failed to unlink " + "%.*s appearance-index %d, owner %.*s\n", + STR_FMT( &app->owner ), app->index, + STR_FMT( &app->owner )); + goto done; + } + sca_appearance_free(app); + + sca_hash_table_unlock_index(sca->appearances, slot_idx); + slot_idx = -1; + + if (sca_notify_call_info_subscribers(sca, to_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers " + "on BYE\n", STR_FMT( to_aor )); + goto done; + } + } + } + } else { + /* this is just a backup to catch anything missed on the BYE request */ + if (SCA_CALL_INFO_IS_SHARED_CALLEE(call_info)) { + slot_idx = sca_hash_table_index_for_key(sca->appearances, to_aor); + sca_hash_table_lock_index(sca->appearances, slot_idx); + + app = sca_appearance_for_index_unsafe(sca, to_aor, call_info->index, + slot_idx); + if (app == NULL) { + app = sca_appearance_for_tags_unsafe(sca, to_aor, + &msg->callid->body, &to->tag_value, NULL, slot_idx); + } + if (app == NULL) { + LM_DBG( "sca_call_info_bye_handler: no appearance found " + "for callee %.*s, call-ID %.*s\n", + STR_FMT( to_aor ), STR_FMT( &msg->callid->body )); + rc = 1; + goto done; + } + + LM_INFO( "sca_call_info_bye_handler: found in-use call appearance " + "for callee %.*s, call-ID %.*s\n", + STR_FMT( to_aor ), STR_FMT( &msg->callid->body )); + + if (SCA_STR_EQ(&app->dialog.call_id, &msg->callid->body)) { + if (!sca_appearance_list_unlink_appearance(app->appearance_list, + &app)) { + LM_ERR( "sca_call_info_bye_handler: failed to unlink " + "%.*s appearance-index %d, owner %.*s\n", + STR_FMT( &app->owner ), app->index, + STR_FMT( &app->owner )); + goto done; + } + sca_appearance_free(app); + + sca_hash_table_unlock_index(sca->appearances, slot_idx); + slot_idx = -1; + + if (sca_notify_call_info_subscribers(sca, to_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers " + "on BYE\n", STR_FMT( to_aor )); + goto done; + } + } } - } } - } - rc = 1; + rc = 1; -done: - if ( slot_idx >= 0 ) { - sca_hash_table_unlock_index( sca->appearances, slot_idx ); - } + done: if (slot_idx >= 0) { + sca_hash_table_unlock_index(sca->appearances, slot_idx); + } - return( rc ); + return (rc); } - static int -sca_call_info_cancel_handler( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, - str *from_aor, str *to_aor, str *contact_uri ) -{ - sca_appearance *app; - int rc = 1; - - if ( msg->first_line.type != SIP_REQUEST ) { - return( 1 ); - } - - /* - * Polycom SCA CANCELs as of sip.ld 3.3.4 don't include Call-Info headers; - * find appearance by dialog if Call-Info not present. - */ - /* XXX also handle CANCEL w/ Call-Info header? Some UAs might send it */ - if ( SCA_CALL_INFO_IS_SHARED_CALLER( call_info )) { - app = sca_appearance_unlink_by_tags( sca, from_aor, - &msg->callid->body, &from->tag_value, NULL ); - if ( app ) { - sca_appearance_free( app ); - - if ( sca_notify_call_info_subscribers( sca, from_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on CANCEL", - STR_FMT( from_aor )); - rc = -1; - } +static int sca_call_info_cancel_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + sca_appearance *app; + int rc = 1; + + if (msg->first_line.type != SIP_REQUEST) { + return (1); } - } - if ( !SCA_STR_EMPTY( &to->tag_value ) && - sca_uri_is_shared_appearance( sca, to_aor )) { - app = sca_appearance_unlink_by_tags( sca, to_aor, - &msg->callid->body, &to->tag_value, NULL ); - if ( app ) { - sca_appearance_free( app ); + /* + * Polycom SCA CANCELs as of sip.ld 3.3.4 don't include Call-Info headers; + * find appearance by dialog if Call-Info not present. + */ + /* XXX also handle CANCEL w/ Call-Info header? Some UAs might send it */ + if (SCA_CALL_INFO_IS_SHARED_CALLER(call_info)) { + app = sca_appearance_unlink_by_tags(sca, from_aor, &msg->callid->body, + &from->tag_value, NULL); + if (app) { + sca_appearance_free(app); + + if (sca_notify_call_info_subscribers(sca, from_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on CANCEL\n", + STR_FMT( from_aor )); + rc = -1; + } + } + } - if ( sca_notify_call_info_subscribers( sca, to_aor ) < 0 ) { - LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on CANCEL", - STR_FMT( to_aor )); - rc = -1; - } + if (!SCA_STR_EMPTY(&to->tag_value) + && sca_uri_is_shared_appearance(sca, to_aor)) { + app = sca_appearance_unlink_by_tags(sca, to_aor, &msg->callid->body, + &to->tag_value, NULL); + if (app) { + sca_appearance_free(app); + + if (sca_notify_call_info_subscribers(sca, to_aor) < 0) { + LM_ERR( "Failed to call-info NOTIFY %.*s subscribers on CANCEL\n", + STR_FMT( to_aor )); + rc = -1; + } + } } - } - return( rc ); + return (rc); } - void -sca_call_info_sl_reply_cb( void *cb_arg ) -{ - sl_cbp_t *slcbp = (sl_cbp_t *)cb_arg; - sip_msg_t *msg; - struct to_body *from; - struct to_body *to; - str aor = STR_NULL; - str contact_uri = STR_NULL; - - if ( slcbp == NULL ) { - return; - } - - if ( slcbp->type != SLCB_REPLY_READY ) { - return; - } - - /* for now, it appears we only need this during INVITEs... */ - if ( slcbp->req->REQ_METHOD != METHOD_INVITE ) { - return; - } - - /* ...and even then only on error */ - if ( slcbp->code < 400 || slcbp->code == 401 || slcbp->code == 407 ) { - /* LM_DBG( "sca_call_info_sl_reply_cb: ignoring stateless reply with " - "status %d %.*s", slcbp->code, STR_FMT( slcbp->reason )); */ - return; - } - - msg = slcbp->req; - if ( sca_get_msg_from_header( msg, &from ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to get From header from " - "request before stateless reply with %d %.*s", - slcbp->code, STR_FMT( slcbp->reason )); - return; - } - if ( sca_uri_extract_aor( &from->uri, &aor ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to extract AoR " - "from URI %.*s", STR_FMT( &from->uri )); - return; - } - - if ( !sca_uri_is_shared_appearance( sca, &aor )) { - /* LM_DBG( "sca_call_info_sl_reply_cb: ignoring non-shared appearance " - "%.*s", STR_FMT( &aor )); */ - return; - } - - if ( sca_get_msg_contact_uri( msg, &contact_uri ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to get Contact from " - "request before stateless reply with %d %.*s", - slcbp->code, STR_FMT( slcbp->reason )); - return; - } - - if ( sca_get_msg_to_header( msg, &to ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to get To header from " - "request before stateless reply with %d %.*s", - slcbp->code, STR_FMT( slcbp->reason )); - return; - } - - if ( sca_subscription_terminate( sca, &aor, - SCA_EVENT_TYPE_LINE_SEIZE, &contact_uri, - SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, - SCA_SUBSCRIPTION_TERMINATE_OPT_DEFAULT ) < 0 ) { - LM_ERR( "sca_call_info_sl_reply_cb: failed to terminate " - "line-seize subscription for %.*s", STR_FMT( &contact_uri )); - return; - } -} +void sca_call_info_sl_reply_cb(void *cb_arg) { + sl_cbp_t *slcbp = (sl_cbp_t *) cb_arg; + sip_msg_t *msg; + struct to_body *from; + struct to_body *to; + str aor = STR_NULL; + str contact_uri = STR_NULL; + + if (slcbp == NULL) { + return; + } + + if (slcbp->type != SLCB_REPLY_READY) { + return; + } + + /* for now, it appears we only need this during INVITEs... */ + if (slcbp->req->REQ_METHOD != METHOD_INVITE) { + return; + } + + /* ...and even then only on error */ + if (slcbp->code < 400 || slcbp->code == 401 || slcbp->code == 407) { + /* LM_DBG( "sca_call_info_sl_reply_cb: ignoring stateless reply with " + "status %d %.*s", slcbp->code, STR_FMT( slcbp->reason )); */ + return; + } + + msg = slcbp->req; + if (sca_get_msg_from_header(msg, &from) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to get From header from " + "request before stateless reply with %d %.*s\n", + slcbp->code, STR_FMT( slcbp->reason )); + return; + } + if (sca_uri_extract_aor(&from->uri, &aor) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to extract AoR " + "from URI %.*s\n", STR_FMT( &from->uri )); + return; + } - static inline int -sca_call_info_prack_handler( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, - str *to_aor, str *contact_uri ) -{ - return( 1 ); + if (!sca_uri_is_shared_appearance(sca, &aor)) { + /* LM_DBG( "sca_call_info_sl_reply_cb: ignoring non-shared appearance " + "%.*s", STR_FMT( &aor )); */ + return; + } + + if (sca_get_msg_contact_uri(msg, &contact_uri) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to get Contact from " + "request before stateless reply with %d %.*s\n", + slcbp->code, STR_FMT( slcbp->reason )); + return; + } + + if (sca_get_msg_to_header(msg, &to) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to get To header from " + "request before stateless reply with %d %.*s\n", + slcbp->code, STR_FMT( slcbp->reason )); + return; + } + + if (sca_subscription_terminate(sca, &aor, SCA_EVENT_TYPE_LINE_SEIZE, + &contact_uri, SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, + SCA_SUBSCRIPTION_TERMINATE_OPT_DEFAULT) < 0) { + LM_ERR( "sca_call_info_sl_reply_cb: failed to terminate " + "line-seize subscription for %.*s\n", STR_FMT( &contact_uri )); + return; + } } - static inline int -sca_call_info_refer_handler( sip_msg_t *msg, sca_call_info *call_info, - struct to_body *from, struct to_body *to, str *from_aor, - str *to_aor, str *contact_uri ) -{ - return( 1 ); +static inline int sca_call_info_prack_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + return (1); } +static inline int sca_call_info_refer_handler(sip_msg_t *msg, + sca_call_info *call_info, struct to_body *from, struct to_body *to, + str *from_aor, str *to_aor, str *contact_uri) { + return (1); +} struct sca_call_info_dispatch { - int method; - int (*handler)( sip_msg_t *, sca_call_info *, - struct to_body *, struct to_body *, - str *, str *, str * ); -}; -struct sca_call_info_dispatch call_info_dispatch[] = { - { METHOD_BYE, sca_call_info_bye_handler }, - { METHOD_CANCEL, sca_call_info_cancel_handler }, - { METHOD_INVITE, sca_call_info_invite_handler }, - { METHOD_PRACK, sca_call_info_prack_handler }, - { METHOD_REFER, sca_call_info_refer_handler }, + int method; + int (*handler)(sip_msg_t *, sca_call_info *, struct to_body *, + struct to_body *, str *, str *, str *); }; +struct sca_call_info_dispatch call_info_dispatch[] = { { METHOD_BYE, + sca_call_info_bye_handler }, { METHOD_CANCEL, + sca_call_info_cancel_handler }, { METHOD_INVITE, + sca_call_info_invite_handler }, { METHOD_PRACK, + sca_call_info_prack_handler }, { METHOD_REFER, + sca_call_info_refer_handler }, }; #define SCA_CALL_INFO_UPDATE_FLAG_DEFAULT 0 #define SCA_CALL_INFO_UPDATE_FLAG_FROM_ALLOC (1 << 0) #define SCA_CALL_INFO_UPDATE_FLAG_TO_ALLOC (1 << 1) - int -sca_call_info_update( sip_msg_t *msg, char *p1, char *p2 ) -{ - sca_call_info call_info; - hdr_field_t *call_info_hdr; - struct to_body *from; - struct to_body *to; - sip_uri_t c_uri; - str from_aor = STR_NULL; - str to_aor = STR_NULL; - str contact_uri = STR_NULL; - int aor_flags = SCA_CALL_INFO_UPDATE_FLAG_DEFAULT; - int n_dispatch; - int i; - int method; - int rc = -1; - int update_mask = SCA_CALL_INFO_SHARED_BOTH; - - method = sca_get_msg_method( msg ); - - n_dispatch = sizeof( call_info_dispatch ) / sizeof( call_info_dispatch[0] ); - for ( i = 0; i < n_dispatch; i++ ) { - if ( method == call_info_dispatch[ i ].method ) { - break; - } - } - if ( i >= n_dispatch ) { - LM_DBG( "BUG: sca module does not support Call-Info headers " - "in %.*s requests", STR_FMT( &get_cseq( msg )->method )); - return( 1 ); - } - - if ( parse_headers( msg, HDR_EOH_F, 0 ) < 0 ) { - LM_ERR( "header parsing failed: bad request" ); - return( -1 ); - } - - if ( p1 != NULL ) { - if ( get_int_fparam( &update_mask, msg, (fparam_t *)p1 ) < 0 ) { - LM_ERR( "sca_call_info_update: argument 1: bad value " - "(integer expected)" ); - return( -1 ); - } - - switch ( update_mask ) { - case SCA_CALL_INFO_SHARED_NONE: - update_mask = SCA_CALL_INFO_SHARED_BOTH; - break; - - case SCA_CALL_INFO_SHARED_CALLER: - case SCA_CALL_INFO_SHARED_CALLEE: - break; +int sca_call_info_update(sip_msg_t *msg, char *p1, char *p2) { + sca_call_info call_info; + hdr_field_t *call_info_hdr; + struct to_body *from; + struct to_body *to; + sip_uri_t c_uri; + str from_aor = STR_NULL; + str to_aor = STR_NULL; + str contact_uri = STR_NULL; + int aor_flags = SCA_CALL_INFO_UPDATE_FLAG_DEFAULT; + int n_dispatch; + int i; + int method; + int rc = -1; + int update_mask = SCA_CALL_INFO_SHARED_BOTH; + + method = sca_get_msg_method(msg); + + n_dispatch = sizeof(call_info_dispatch) / sizeof(call_info_dispatch[0]); + for (i = 0; i < n_dispatch; i++) { + if (method == call_info_dispatch[i].method) { + break; + } + } + if (i >= n_dispatch) { + LM_DBG( "BUG: sca module does not support Call-Info headers " + "in %.*s requests\n", STR_FMT( &get_cseq( msg )->method )); + return (1); + } - default: - LM_ERR( "sca_call_info_update: argument 1: invalid value " - "(0, 1 or 2 expected)" ); - return( -1 ); - } - } - - memset( &call_info, 0, sizeof( sca_call_info )); - call_info_hdr = sca_call_info_header_find( msg->headers ); - if ( !SCA_HEADER_EMPTY( call_info_hdr )) { - /* this needs to accomodate comma-separated appearance info */ - if ( sca_call_info_body_parse( &call_info_hdr->body, &call_info ) < 0) { - LM_ERR( "Bad Call-Info header body: %.*s", - STR_FMT( &call_info_hdr->body )); - return( -1 ); - } - } - - if ( sca_get_msg_from_header( msg, &from ) < 0 ) { - LM_ERR( "Bad From header" ); - return( -1 ); - } - if ( sca_get_msg_to_header( msg, &to ) < 0 ) { - LM_ERR( "Bad To header" ); - return( -1 ); - } - - memset( &c_uri, 0, sizeof( sip_uri_t )); - rc = sca_get_msg_contact_uri( msg, &contact_uri ); - if ( rc > 0 ) { - /* Contact header in packet */ - if ( parse_uri( contact_uri.s, contact_uri.len, &c_uri ) < 0 ) { - LM_ERR( "Failed to parse Contact URI %.*s", - STR_FMT( &contact_uri )); - return( -1 ); - } - } else if ( rc < 0 ) { - LM_ERR( "Bad Contact" ); - return( -1 ); - } - /* reset rc to -1 so we don't end up returning 0 to the script */ - rc = -1; - - /* reconcile mismatched Contact users and To/From URIs */ - if ( msg->first_line.type == SIP_REQUEST ) { - if ( sca_create_canonical_aor( msg, &from_aor ) < 0 ) { - return( -1 ); - } - aor_flags |= SCA_CALL_INFO_UPDATE_FLAG_FROM_ALLOC; - - if ( sca_uri_extract_aor( &to->uri, &to_aor ) < 0 ) { - LM_ERR( "Failed to extract AoR from To URI %.*s", - STR_FMT( &to->uri )); - goto done; - } - } else { - if ( sca_uri_extract_aor( &from->uri, &from_aor ) < 0 ) { - LM_ERR( "Failed to extract AoR from From URI %.*s", - STR_FMT( &from->uri )); - goto done; - } - if ( sca_create_canonical_aor( msg, &to_aor ) < 0 ) { - return( -1 ); - } - aor_flags |= SCA_CALL_INFO_UPDATE_FLAG_TO_ALLOC; - } - - /* early check to see if we're dealing with any SCA endpoints */ - if ( sca_uri_is_shared_appearance( sca, &from_aor )) { - if (( update_mask & SCA_CALL_INFO_SHARED_CALLER )) { - call_info.ua_shared |= SCA_CALL_INFO_SHARED_CALLER; - } - } - if ( sca_uri_is_shared_appearance( sca, &to_aor )) { - if (( update_mask & SCA_CALL_INFO_SHARED_CALLEE )) { - call_info.ua_shared |= SCA_CALL_INFO_SHARED_CALLEE; - } - } - - if ( call_info_hdr == NULL ) { - if ( SCA_CALL_INFO_IS_SHARED_CALLER( &call_info ) && - msg->first_line.type == SIP_REQUEST ) { - if ( !sca_subscription_aor_has_subscribers( - SCA_EVENT_TYPE_CALL_INFO, &from_aor )) { - call_info.ua_shared &= ~SCA_CALL_INFO_SHARED_CALLER; - sca_appearance_unregister( sca, &from_aor ); - } - } else if ( SCA_CALL_INFO_IS_SHARED_CALLEE( &call_info ) && - msg->first_line.type == SIP_REPLY ) { - if ( !sca_subscription_aor_has_subscribers( - SCA_EVENT_TYPE_CALL_INFO, &to_aor )) { - call_info.ua_shared &= ~SCA_CALL_INFO_SHARED_CALLEE; - sca_appearance_unregister( sca, &to_aor ); - } - } - } - - if ( sca_call_info_header_remove( msg ) < 0 ) { - LM_ERR( "Failed to remove Call-Info header" ); - return( -1 ); - } - - if ( call_info.ua_shared == SCA_CALL_INFO_SHARED_NONE ) { - LM_DBG( "Neither %.*s nor %.*s are SCA AoRs", - STR_FMT( &from_aor ), STR_FMT( &to_aor )); - goto done; - } - - rc = call_info_dispatch[ i ].handler( msg, &call_info, from, to, - &from_aor, &to_aor, &contact_uri ); - if ( rc < 0 ) { - LM_ERR( "Failed to update Call-Info state for %.*s", - STR_FMT( &contact_uri )); - } - -done: - if (( aor_flags & SCA_CALL_INFO_UPDATE_FLAG_FROM_ALLOC )) { - if ( from_aor.s != NULL ) { - pkg_free( from_aor.s ); - } - } - if (( aor_flags & SCA_CALL_INFO_UPDATE_FLAG_TO_ALLOC )) { - if ( to_aor.s != NULL ) { - pkg_free( to_aor.s ); - } - } - - return( rc ); + if (parse_headers(msg, HDR_EOH_F, 0) < 0) { + LM_ERR( "header parsing failed: bad request\n" ); + return (-1); + } + + if (p1 != NULL) { + if (get_int_fparam(&update_mask, msg, (fparam_t *) p1) < 0) { + LM_ERR( "sca_call_info_update: argument 1: bad value " + "(integer expected)\n" ); + return (-1); + } + + switch (update_mask) { + case SCA_CALL_INFO_SHARED_NONE: + update_mask = SCA_CALL_INFO_SHARED_BOTH; + break; + + case SCA_CALL_INFO_SHARED_CALLER: + case SCA_CALL_INFO_SHARED_CALLEE: + break; + + default: + LM_ERR( "sca_call_info_update: argument 1: invalid value " + "(0, 1 or 2 expected)\n" ); + return (-1); + } + } + + memset(&call_info, 0, sizeof(sca_call_info)); + call_info_hdr = sca_call_info_header_find(msg->headers); + if (!SCA_HEADER_EMPTY(call_info_hdr)) { + /* this needs to accomodate comma-separated appearance info */ + if (sca_call_info_body_parse(&call_info_hdr->body, &call_info) < 0) { + LM_ERR( "Bad Call-Info header body: %.*s\n", + STR_FMT( &call_info_hdr->body )); + return (-1); + } + } + + if (sca_get_msg_from_header(msg, &from) < 0) { + LM_ERR( "Bad From header\n" ); + return (-1); + } + if (sca_get_msg_to_header(msg, &to) < 0) { + LM_ERR( "Bad To header\n" ); + return (-1); + } + + // XXX This is double code of what a portion of the + // sca_create_canonical_aor does and is called further down + // and the c_uri var is not used anywhere in this function. + memset(&c_uri, 0, sizeof(sip_uri_t)); + rc = sca_get_msg_contact_uri(msg, &contact_uri); + if (rc > 0) { + /* Contact header in packet */ + if (parse_uri(contact_uri.s, contact_uri.len, &c_uri) < 0) { + LM_ERR( "Failed to parse Contact URI %.*s\n", + STR_FMT( &contact_uri )); + return (-1); + } + } else if (rc < 0) { + LM_ERR( "Bad Contact\n" ); + return (-1); + } + /* reset rc to -1 so we don't end up returning 0 to the script */ + rc = -1; + + /* reconcile mismatched Contact users and To/From URIs */ + if (msg->first_line.type == SIP_REQUEST) { + if (sca_create_canonical_aor(msg, &from_aor) < 0) { + return (-1); + } + aor_flags |= SCA_CALL_INFO_UPDATE_FLAG_FROM_ALLOC; + + if (sca_uri_extract_aor(&to->uri, &to_aor) < 0) { + LM_ERR( "Failed to extract AoR from To URI %.*s\n", + STR_FMT( &to->uri )); + goto done; + } + } else { + if (sca_uri_extract_aor(&from->uri, &from_aor) < 0) { + LM_ERR( "Failed to extract AoR from From URI %.*s\n", + STR_FMT( &from->uri )); + goto done; + } + if (sca_create_canonical_aor(msg, &to_aor) < 0) { + return (-1); + } + aor_flags |= SCA_CALL_INFO_UPDATE_FLAG_TO_ALLOC; + } + + /* early check to see if we're dealing with any SCA endpoints */ + if (sca_uri_is_shared_appearance(sca, &from_aor)) { + if ((update_mask & SCA_CALL_INFO_SHARED_CALLER)) { + call_info.ua_shared |= SCA_CALL_INFO_SHARED_CALLER; + } + } + if (sca_uri_is_shared_appearance(sca, &to_aor)) { + if ((update_mask & SCA_CALL_INFO_SHARED_CALLEE)) { + call_info.ua_shared |= SCA_CALL_INFO_SHARED_CALLEE; + } + } + + if (call_info_hdr == NULL) { + if ( SCA_CALL_INFO_IS_SHARED_CALLER(&call_info) && + msg->first_line.type == SIP_REQUEST) { + if (!sca_subscription_aor_has_subscribers(SCA_EVENT_TYPE_CALL_INFO, + &from_aor)) { + call_info.ua_shared &= ~SCA_CALL_INFO_SHARED_CALLER; + sca_appearance_unregister(sca, &from_aor); + } + } else if ( SCA_CALL_INFO_IS_SHARED_CALLEE(&call_info) && + msg->first_line.type == SIP_REPLY) { + if (!sca_subscription_aor_has_subscribers(SCA_EVENT_TYPE_CALL_INFO, + &to_aor)) { + call_info.ua_shared &= ~SCA_CALL_INFO_SHARED_CALLEE; + sca_appearance_unregister(sca, &to_aor); + } + } + } + + if (sca_call_info_header_remove(msg) < 0) { + LM_ERR( "Failed to remove Call-Info header\n" ); + return (-1); + } + + if (call_info.ua_shared == SCA_CALL_INFO_SHARED_NONE) { + LM_DBG( "Neither %.*s nor %.*s are SCA AoRs\n", + STR_FMT( &from_aor ), STR_FMT( &to_aor )); + goto done; + } + + LM_DBG( "Calling Dispatch Id: %d handler with From-AOR: %.*s To-AOR: %.*s " + "From-URI: <%.*s> To-URI: <%.*s> Contact-URI: <%.*s>\n", + i, STR_FMT( &from_aor ), STR_FMT( &to_aor ),STR_FMT( &from->uri ), + STR_FMT( &to->uri ), STR_FMT( &contact_uri )); + + rc = call_info_dispatch[i].handler(msg, &call_info, from, to, &from_aor, + &to_aor, &contact_uri); + if (rc < 0) { + LM_ERR( "Failed to update Call-Info state for %.*s\n", + STR_FMT( &contact_uri )); + } + + done: if ((aor_flags & SCA_CALL_INFO_UPDATE_FLAG_FROM_ALLOC)) { + if (from_aor.s != NULL) { + pkg_free(from_aor.s); + } + } + if ((aor_flags & SCA_CALL_INFO_UPDATE_FLAG_TO_ALLOC)) { + if (to_aor.s != NULL) { + pkg_free(to_aor.s); + } + } + + return (rc); } diff --git a/modules/sca/sca_call_info.h b/modules/sca/sca_call_info.h index a0a2b1e78bb..0858779dcfb 100644 --- a/modules/sca/sca_call_info.h +++ b/modules/sca/sca_call_info.h @@ -28,23 +28,23 @@ #define SCA_CALL_INFO_APPEARANCE_INDEX_ANY 0 enum { - SCA_CALL_INFO_SHARED_NONE = 0, - SCA_CALL_INFO_SHARED_CALLER = (1 << 0), - SCA_CALL_INFO_SHARED_CALLEE = (1 << 1), + SCA_CALL_INFO_SHARED_NONE = 0, + SCA_CALL_INFO_SHARED_CALLER = (1 << 0), + SCA_CALL_INFO_SHARED_CALLEE = (1 << 1), }; #define SCA_CALL_INFO_SHARED_BOTH \ (SCA_CALL_INFO_SHARED_CALLER | SCA_CALL_INFO_SHARED_CALLEE) struct _sca_call_info { - str sca_uri; - int index; - int state; - str uri; + str sca_uri; + int index; + int state; + str uri; - /* mask tracking which endpoints in a call are shared */ - int ua_shared; + /* mask tracking which endpoints in a call are shared */ + int ua_shared; }; -typedef struct _sca_call_info sca_call_info; +typedef struct _sca_call_info sca_call_info; #define SCA_CALL_INFO_EMPTY( ci1 ) \ ((void*)(ci1) == NULL || \ @@ -59,20 +59,18 @@ typedef struct _sca_call_info sca_call_info; (!SCA_CALL_INFO_EMPTY((ci1)) && \ (((sca_call_info *)(ci1))->ua_shared & SCA_CALL_INFO_SHARED_CALLEE)) +extern const str SCA_CALL_INFO_HEADER_STR; -extern const str SCA_CALL_INFO_HEADER_STR; +int sca_call_info_update(sip_msg_t *, char *, char *); +void sca_call_info_sl_reply_cb(void *); +void sca_call_info_ack_cb(struct cell *, int, struct tmcb_params *); +int sca_call_info_build_header(sca_mod *, sca_subscription *, char *, int); +int sca_call_info_append_header_for_appearance_index(sca_subscription *, int, + char *, int); -int sca_call_info_update( sip_msg_t *, char *, char * ); -void sca_call_info_sl_reply_cb( void * ); -void sca_call_info_ack_cb( struct cell *, int, struct tmcb_params * ); - -int sca_call_info_build_header( sca_mod *, sca_subscription *, char *, int ); -int sca_call_info_append_header_for_appearance_index( sca_subscription *, int, - char *, int ); - -hdr_field_t *sca_call_info_header_find( hdr_field_t * ); -int sca_call_info_body_parse( str *, sca_call_info * ); -int sca_call_info_free( sca_call_info * ); +hdr_field_t *sca_call_info_header_find(hdr_field_t *); +int sca_call_info_body_parse(str *, sca_call_info *); +int sca_call_info_free(sca_call_info *); #endif /* SCA_CALL_INFO_H */ diff --git a/modules/sca/sca_common.h b/modules/sca/sca_common.h index 8b9db4a489b..c1597998b6f 100644 --- a/modules/sca/sca_common.h +++ b/modules/sca/sca_common.h @@ -50,11 +50,10 @@ #include "../../modules/sl/sl.h" #include "../../modules/tm/tm_load.h" - /* convenience macros */ #define SCA_STRUCT_PTR_OFFSET( struct1, cast1, offset1 ) \ (cast1)(struct1) + (offset1) - + #define SCA_STR_COPY( str1, str2 ) \ memcpy((str1)->s, (str2)->s, (str2)->len ); \ (str1)->len = (str2)->len; @@ -65,7 +64,7 @@ #define SCA_STR_APPEND_L( str1, str1_lim, s2, s2_len ) \ if ((str1)->len + (s2_len) >= (str1_lim)) { \ - LM_ERR( "Failed to append to str: too long" ); \ + LM_ERR( "Failed to append to str: too long\n" ); \ } else { \ SCA_STR_APPEND((str1), (s2), (s2_len)); \ (str1_lim) -= (s2_len); \ @@ -80,7 +79,7 @@ #define SCA_STR_APPEND_CSTR_L( str1, str1_lim, cstr1 ) \ if ((str1)->len + strlen(cstr1) >= (str1_lim)) { \ - LM_ERR( "Failed to append to str: too long" ); \ + LM_ERR( "Failed to append to str: too long\n" ); \ } else { \ SCA_STR_APPEND_CSTR((str1), (cstr1)); \ } diff --git a/modules/sca/sca_db.c b/modules/sca/sca_db.c index dbd34601ddd..230eb1d5d5f 100644 --- a/modules/sca/sca_db.c +++ b/modules/sca/sca_db.c @@ -22,147 +22,131 @@ #include "sca_db.h" #include "sca_subscribe.h" - -db1_con_t *sca_db_con = NULL; - -const str SCA_DB_SUBSCRIBER_COL_NAME = STR_STATIC_INIT( "subscriber" ); -const str SCA_DB_AOR_COL_NAME = STR_STATIC_INIT( "aor" ); -const str SCA_DB_EVENT_COL_NAME = STR_STATIC_INIT( "event" ); -const str SCA_DB_EXPIRES_COL_NAME = STR_STATIC_INIT( "expires" ); -const str SCA_DB_STATE_COL_NAME = STR_STATIC_INIT( "state" ); -const str SCA_DB_APP_IDX_COL_NAME = STR_STATIC_INIT( "app_idx" ); -const str SCA_DB_CALL_ID_COL_NAME = STR_STATIC_INIT( "call_id" ); -const str SCA_DB_FROM_TAG_COL_NAME = STR_STATIC_INIT( "from_tag" ); -const str SCA_DB_TO_TAG_COL_NAME = STR_STATIC_INIT( "to_tag" ); -const str SCA_DB_RECORD_ROUTE_COL_NAME = STR_STATIC_INIT( "record_route" ); -const str SCA_DB_NOTIFY_CSEQ_COL_NAME = STR_STATIC_INIT( "notify_cseq" ); -const str SCA_DB_SUBSCRIBE_CSEQ_COL_NAME = STR_STATIC_INIT( "subscribe_cseq" ); - - void -sca_db_subscriptions_get_value_for_column( int column, db_val_t *row_values, - void *column_value ) -{ - assert( column_value != NULL ); - assert( row_values != NULL ); - assert( column >= 0 && column < SCA_DB_SUBS_BOUNDARY ); - - switch ( column ) { - case SCA_DB_SUBS_SUBSCRIBER_COL: - case SCA_DB_SUBS_AOR_COL: - case SCA_DB_SUBS_CALL_ID_COL: - case SCA_DB_SUBS_FROM_TAG_COL: - case SCA_DB_SUBS_TO_TAG_COL: - case SCA_DB_SUBS_RECORD_ROUTE_COL: - ((str *)column_value)->s = (char *)row_values[ column ].val.string_val; - ((str *)column_value)->len = strlen(((str *)column_value)->s ); - break; - - case SCA_DB_SUBS_EXPIRES_COL: - *((time_t *)column_value) = row_values[ column ].val.time_val; - break; - - case SCA_DB_SUBS_EVENT_COL: - case SCA_DB_SUBS_STATE_COL: - case SCA_DB_SUBS_NOTIFY_CSEQ_COL: - case SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL: - *((int *)column_value) = row_values[ column ].val.int_val; - break; - - default: - column_value = NULL; - } +db1_con_t *sca_db_con = NULL; + +const str SCA_DB_SUBSCRIBER_COL_NAME = STR_STATIC_INIT( "subscriber" ); +const str SCA_DB_AOR_COL_NAME = STR_STATIC_INIT( "aor" ); +const str SCA_DB_EVENT_COL_NAME = STR_STATIC_INIT( "event" ); +const str SCA_DB_EXPIRES_COL_NAME = STR_STATIC_INIT( "expires" ); +const str SCA_DB_STATE_COL_NAME = STR_STATIC_INIT( "state" ); +const str SCA_DB_APP_IDX_COL_NAME = STR_STATIC_INIT( "app_idx" ); +const str SCA_DB_CALL_ID_COL_NAME = STR_STATIC_INIT( "call_id" ); +const str SCA_DB_FROM_TAG_COL_NAME = STR_STATIC_INIT( "from_tag" ); +const str SCA_DB_TO_TAG_COL_NAME = STR_STATIC_INIT( "to_tag" ); +const str SCA_DB_RECORD_ROUTE_COL_NAME = STR_STATIC_INIT( "record_route" ); +const str SCA_DB_NOTIFY_CSEQ_COL_NAME = STR_STATIC_INIT( "notify_cseq" ); +const str SCA_DB_SUBSCRIBE_CSEQ_COL_NAME = STR_STATIC_INIT( "subscribe_cseq" ); + +void sca_db_subscriptions_get_value_for_column(int column, db_val_t *row_values, + void *column_value) { + assert(column_value != NULL); + assert(row_values != NULL); + assert(column >= 0 && column < SCA_DB_SUBS_BOUNDARY); + + switch (column) { + case SCA_DB_SUBS_SUBSCRIBER_COL: + case SCA_DB_SUBS_AOR_COL: + case SCA_DB_SUBS_CALL_ID_COL: + case SCA_DB_SUBS_FROM_TAG_COL: + case SCA_DB_SUBS_TO_TAG_COL: + case SCA_DB_SUBS_RECORD_ROUTE_COL: + ((str *) column_value)->s = (char *) row_values[column].val.string_val; + ((str *) column_value)->len = strlen(((str *) column_value)->s); + break; + + case SCA_DB_SUBS_EXPIRES_COL: + *((time_t *) column_value) = row_values[column].val.time_val; + break; + + case SCA_DB_SUBS_EVENT_COL: + case SCA_DB_SUBS_STATE_COL: + case SCA_DB_SUBS_NOTIFY_CSEQ_COL: + case SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL: + *((int *) column_value) = row_values[column].val.int_val; + break; + + default: + column_value = NULL; + } } - void -sca_db_subscriptions_set_value_for_column( int column, db_val_t *row_values, - void *column_value ) -{ - assert( column >= 0 && column < SCA_DB_SUBS_BOUNDARY ); - assert( column_value != NULL ); - assert( row_values != NULL ); - - switch ( column ) { - case SCA_DB_SUBS_SUBSCRIBER_COL: - case SCA_DB_SUBS_AOR_COL: - case SCA_DB_SUBS_CALL_ID_COL: - case SCA_DB_SUBS_FROM_TAG_COL: - case SCA_DB_SUBS_TO_TAG_COL: - case SCA_DB_SUBS_RECORD_ROUTE_COL: - row_values[ column ].val.str_val = *((str *)column_value); - row_values[ column ].type = DB1_STR; - row_values[ column ].nul = 0; - break; - - case SCA_DB_SUBS_EXPIRES_COL: - row_values[ column ].val.int_val = (int)(*((time_t *)column_value)); - row_values[ column ].type = DB1_INT; - row_values[ column ].nul = 0; - break; - - case SCA_DB_SUBS_APP_IDX_COL: - /* for now, don't save appearance index associated with subscriber */ - row_values[ column ].val.int_val = 0; - row_values[ column ].type = DB1_INT; - row_values[ column ].nul = 0; - break; - - default: - LM_WARN( "sca_db_subscriptions_set_value_for_column: unrecognized " - "column index %d, treating as INT", column ); - /* fall through */ - - case SCA_DB_SUBS_EVENT_COL: - case SCA_DB_SUBS_STATE_COL: - case SCA_DB_SUBS_NOTIFY_CSEQ_COL: - case SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL: - row_values[ column ].val.int_val = *((int *)column_value); - row_values[ column ].type = DB1_INT; - row_values[ column ].nul = 0; - break; - } +void sca_db_subscriptions_set_value_for_column(int column, db_val_t *row_values, + void *column_value) { + assert(column >= 0 && column < SCA_DB_SUBS_BOUNDARY); + assert(column_value != NULL); + assert(row_values != NULL); + + switch (column) { + case SCA_DB_SUBS_SUBSCRIBER_COL: + case SCA_DB_SUBS_AOR_COL: + case SCA_DB_SUBS_CALL_ID_COL: + case SCA_DB_SUBS_FROM_TAG_COL: + case SCA_DB_SUBS_TO_TAG_COL: + case SCA_DB_SUBS_RECORD_ROUTE_COL: + row_values[column].val.str_val = *((str *) column_value); + row_values[column].type = DB1_STR; + row_values[column].nul = 0; + break; + + case SCA_DB_SUBS_EXPIRES_COL: + row_values[column].val.int_val = (int) (*((time_t *) column_value)); + row_values[column].type = DB1_INT; + row_values[column].nul = 0; + break; + + case SCA_DB_SUBS_APP_IDX_COL: + /* for now, don't save appearance index associated with subscriber */ + row_values[column].val.int_val = 0; + row_values[column].type = DB1_INT; + row_values[column].nul = 0; + break; + + default: + LM_WARN( "sca_db_subscriptions_set_value_for_column: unrecognized " + "column index %d, treating as INT\n", column ); + /* fall through */ + + case SCA_DB_SUBS_EVENT_COL: + case SCA_DB_SUBS_STATE_COL: + case SCA_DB_SUBS_NOTIFY_CSEQ_COL: + case SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL: + row_values[column].val.int_val = *((int *) column_value); + row_values[column].type = DB1_INT; + row_values[column].nul = 0; + break; + } } - str ** -sca_db_subscriptions_columns( void ) -{ - static str *subs_columns[] = { - (str *)&SCA_DB_SUBSCRIBER_COL_NAME, - (str *)&SCA_DB_AOR_COL_NAME, - (str *)&SCA_DB_EVENT_COL_NAME, - (str *)&SCA_DB_EXPIRES_COL_NAME, - (str *)&SCA_DB_STATE_COL_NAME, - (str *)&SCA_DB_APP_IDX_COL_NAME, - (str *)&SCA_DB_CALL_ID_COL_NAME, - (str *)&SCA_DB_FROM_TAG_COL_NAME, - (str *)&SCA_DB_TO_TAG_COL_NAME, - (str *)&SCA_DB_RECORD_ROUTE_COL_NAME, - (str *)&SCA_DB_NOTIFY_CSEQ_COL_NAME, - (str *)&SCA_DB_SUBSCRIBE_CSEQ_COL_NAME, - NULL - }; - - return( subs_columns ); +str ** +sca_db_subscriptions_columns(void) { + static str *subs_columns[] = { (str *) &SCA_DB_SUBSCRIBER_COL_NAME, + (str *) &SCA_DB_AOR_COL_NAME, (str *) &SCA_DB_EVENT_COL_NAME, + (str *) &SCA_DB_EXPIRES_COL_NAME, (str *) &SCA_DB_STATE_COL_NAME, + (str *) &SCA_DB_APP_IDX_COL_NAME, (str *) &SCA_DB_CALL_ID_COL_NAME, + (str *) &SCA_DB_FROM_TAG_COL_NAME, (str *) &SCA_DB_TO_TAG_COL_NAME, + (str *) &SCA_DB_RECORD_ROUTE_COL_NAME, + (str *) &SCA_DB_NOTIFY_CSEQ_COL_NAME, + (str *) &SCA_DB_SUBSCRIBE_CSEQ_COL_NAME, NULL }; + + return (subs_columns); } - db1_con_t * -sca_db_get_connection( void ) -{ - assert( sca && sca->cfg->db_url ); - assert( sca->db_api && sca->db_api->init ); +db1_con_t * +sca_db_get_connection(void) { + assert(sca && sca->cfg->db_url); + assert(sca->db_api && sca->db_api->init); - if ( sca_db_con == NULL ) { - sca_db_con = sca->db_api->init( sca->cfg->db_url ); - /* catch connection error in caller */ - } + if (sca_db_con == NULL) { + sca_db_con = sca->db_api->init(sca->cfg->db_url); + /* catch connection error in caller */ + } - return( sca_db_con ); + return (sca_db_con); } - void -sca_db_disconnect( void ) -{ - if ( sca_db_con != NULL ) { - sca->db_api->close( sca_db_con ); - sca_db_con = NULL; - } +void sca_db_disconnect(void) { + if (sca_db_con != NULL) { + sca->db_api->close(sca_db_con); + sca_db_con = NULL; + } } diff --git a/modules/sca/sca_db.h b/modules/sca/sca_db.h index f48934bb34e..9a2b12696d0 100644 --- a/modules/sca/sca_db.h +++ b/modules/sca/sca_db.h @@ -24,7 +24,6 @@ #include "../../lib/srdb1/db.h" - #define SCA_DB_SUBSCRIPTIONS_TABLE_VERSION 1 #define SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS 12 @@ -32,27 +31,27 @@ #define SCA_DB_DEFAULT_FETCH_ROW_COUNT 1000 enum { - SCA_DB_SUBS_SUBSCRIBER_COL = 0, - SCA_DB_SUBS_AOR_COL = 1, - SCA_DB_SUBS_EVENT_COL, - SCA_DB_SUBS_EXPIRES_COL, - SCA_DB_SUBS_STATE_COL, - SCA_DB_SUBS_APP_IDX_COL, - SCA_DB_SUBS_CALL_ID_COL, - SCA_DB_SUBS_FROM_TAG_COL, - SCA_DB_SUBS_TO_TAG_COL, - SCA_DB_SUBS_RECORD_ROUTE_COL, - SCA_DB_SUBS_NOTIFY_CSEQ_COL, - SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL, + SCA_DB_SUBS_SUBSCRIBER_COL = 0, + SCA_DB_SUBS_AOR_COL = 1, + SCA_DB_SUBS_EVENT_COL, + SCA_DB_SUBS_EXPIRES_COL, + SCA_DB_SUBS_STATE_COL, + SCA_DB_SUBS_APP_IDX_COL, + SCA_DB_SUBS_CALL_ID_COL, + SCA_DB_SUBS_FROM_TAG_COL, + SCA_DB_SUBS_TO_TAG_COL, + SCA_DB_SUBS_RECORD_ROUTE_COL, + SCA_DB_SUBS_NOTIFY_CSEQ_COL, + SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL, - SCA_DB_SUBS_BOUNDARY, + SCA_DB_SUBS_BOUNDARY, }; enum { - SCA_DB_FLAG_NONE = 0, - SCA_DB_FLAG_INSERT = 1, - SCA_DB_FLAG_UPDATE, - SCA_DB_FLAG_DELETE, + SCA_DB_FLAG_NONE = 0, + SCA_DB_FLAG_INSERT = 1, + SCA_DB_FLAG_UPDATE, + SCA_DB_FLAG_DELETE, }; #define SCA_DB_BIND_STR_VALUE( cv, ct, k, v, c ) \ @@ -69,25 +68,25 @@ enum { ((db_val_t *)(v))[ (c) ].val.int_val = (int)(cv); \ (c)++; -extern const str SCA_DB_SUBSCRIBER_COL_NAME; -extern const str SCA_DB_AOR_COL_NAME; -extern const str SCA_DB_EVENT_COL_NAME; -extern const str SCA_DB_EXPIRES_COL_NAME; -extern const str SCA_DB_STATE_COL_NAME; -extern const str SCA_DB_APP_IDX_COL_NAME; -extern const str SCA_DB_CALL_ID_COL_NAME; -extern const str SCA_DB_FROM_TAG_COL_NAME; -extern const str SCA_DB_TO_TAG_COL_NAME; -extern const str SCA_DB_RECORD_ROUTE_COL_NAME; -extern const str SCA_DB_NOTIFY_CSEQ_COL_NAME; -extern const str SCA_DB_SUBSCRIBE_CSEQ_COL_NAME; +extern const str SCA_DB_SUBSCRIBER_COL_NAME; +extern const str SCA_DB_AOR_COL_NAME; +extern const str SCA_DB_EVENT_COL_NAME; +extern const str SCA_DB_EXPIRES_COL_NAME; +extern const str SCA_DB_STATE_COL_NAME; +extern const str SCA_DB_APP_IDX_COL_NAME; +extern const str SCA_DB_CALL_ID_COL_NAME; +extern const str SCA_DB_FROM_TAG_COL_NAME; +extern const str SCA_DB_TO_TAG_COL_NAME; +extern const str SCA_DB_RECORD_ROUTE_COL_NAME; +extern const str SCA_DB_NOTIFY_CSEQ_COL_NAME; +extern const str SCA_DB_SUBSCRIBE_CSEQ_COL_NAME; -str **sca_db_subscriptions_columns( void ); -void sca_db_subscriptions_get_value_for_column( int, db_val_t *, void * ); -void sca_db_subscriptions_set_value_for_column( int, db_val_t *, void * ); -void sca_db_subscriptions_bind_value_for_column( int, db_val_t *, void * ); +str **sca_db_subscriptions_columns(void); +void sca_db_subscriptions_get_value_for_column(int, db_val_t *, void *); +void sca_db_subscriptions_set_value_for_column(int, db_val_t *, void *); +void sca_db_subscriptions_bind_value_for_column(int, db_val_t *, void *); -db1_con_t *sca_db_get_connection( void ); -void sca_db_disconnect( void ); +db1_con_t *sca_db_get_connection(void); +void sca_db_disconnect(void); #endif /* SCA_DB_H */ diff --git a/modules/sca/sca_dialog.c b/modules/sca/sca_dialog.c index 9faf9e72cc7..c9c9412b1f0 100644 --- a/modules/sca/sca_dialog.c +++ b/modules/sca/sca_dialog.c @@ -23,43 +23,44 @@ #include "sca_dialog.h" - int -sca_dialog_build_from_tags( sca_dialog *dialog, int maxlen, str *call_id, - str *from_tag, str *to_tag ) -{ - int len = 0; - - assert( dialog != NULL && dialog->id.s != NULL ); - assert( call_id != NULL ); - assert( from_tag != NULL ); - - len = call_id->len + from_tag->len; - if ( !SCA_STR_EMPTY( to_tag )) { - len += to_tag->len; - } - - if ( len >= maxlen ) { - LM_ERR( "sca_dialog_build_from_tags: tags too long" ); - return( -1 ); - } - - memcpy( dialog->id.s, call_id->s, call_id->len ); - dialog->call_id.s = dialog->id.s; - dialog->call_id.len = call_id->len; - - memcpy( dialog->id.s + call_id->len, from_tag->s, from_tag->len ); - dialog->from_tag.s = dialog->id.s + call_id->len; - dialog->from_tag.len = from_tag->len; - - if ( !SCA_STR_EMPTY( to_tag )) { - memcpy( dialog->id.s + call_id->len + from_tag->len, - to_tag->s, to_tag->len ); - dialog->to_tag.s = dialog->id.s + call_id->len + from_tag->len; - dialog->to_tag.len = to_tag->len; - } - dialog->id.len = len; - - return( len ); +int sca_dialog_build_from_tags(sca_dialog *dialog, int maxlen, str *call_id, + str *from_tag, str *to_tag) { + int len = 0; + + assert(dialog != NULL && dialog->id.s != NULL); + assert(call_id != NULL); + assert(from_tag != NULL); + + LM_DBG( "From-Tag: %.*s To-Tag: %.*s CallId: %.*s\n", + STR_FMT(from_tag), STR_FMT(to_tag), STR_FMT(call_id)); + + len = call_id->len + from_tag->len; + if (!SCA_STR_EMPTY(to_tag)) { + len += to_tag->len; + } + + if (len >= maxlen) { + LM_ERR( "sca_dialog_build_from_tags: tags too long\n" ); + return (-1); + } + + memcpy(dialog->id.s, call_id->s, call_id->len); + dialog->call_id.s = dialog->id.s; + dialog->call_id.len = call_id->len; + + memcpy(dialog->id.s + call_id->len, from_tag->s, from_tag->len); + dialog->from_tag.s = dialog->id.s + call_id->len; + dialog->from_tag.len = from_tag->len; + + if (!SCA_STR_EMPTY(to_tag)) { + memcpy(dialog->id.s + call_id->len + from_tag->len, to_tag->s, + to_tag->len); + dialog->to_tag.s = dialog->id.s + call_id->len + from_tag->len; + dialog->to_tag.len = to_tag->len; + } + dialog->id.len = len; + + return (len); } #define SCA_REPLACES_HDR_PREFIX "Replaces: " @@ -68,58 +69,57 @@ sca_dialog_build_from_tags( sca_dialog *dialog, int maxlen, str *call_id, #define SCA_REPLACES_TO_TAG_LEN strlen( SCA_REPLACES_TO_TAG ) #define SCA_REPLACES_FROM_TAG "from-tag=" #define SCA_REPLACES_FROM_TAG_LEN strlen( SCA_REPLACES_FROM_TAG ) - int -sca_dialog_create_replaces_header( sca_dialog *dlg, str *replaces_hdr ) -{ - int len; +int sca_dialog_create_replaces_header(sca_dialog *dlg, str *replaces_hdr) { + int len; - assert( replaces_hdr != NULL ); + assert(replaces_hdr != NULL); - if ( SCA_STR_EMPTY( &dlg->call_id ) || SCA_STR_EMPTY( &dlg->from_tag ) || - SCA_STR_EMPTY( &dlg->to_tag )) { - LM_ERR( "sca_dialog_create_replaces_header: dialog %.*s is not a " - "confirmed dialog, cannot build Replaces header", - STR_FMT( &dlg->id )); - return( -1 ); - } + LM_DBG( "Called\n" ); - memset( replaces_hdr, 0, sizeof( str )); + if ( SCA_STR_EMPTY(&dlg->call_id) || SCA_STR_EMPTY( &dlg->from_tag ) || + SCA_STR_EMPTY( &dlg->to_tag )) { + LM_ERR( "sca_dialog_create_replaces_header: dialog %.*s is not a " + "confirmed dialog, cannot build Replaces header\n", + STR_FMT( &dlg->id )); + return (-1); + } - /* +2 for semicolons separating tags, +2 for CRLF */ - replaces_hdr->s = pkg_malloc( SCA_REPLACES_HDR_PREFIX_LEN + - SCA_REPLACES_TO_TAG_LEN + - SCA_REPLACES_FROM_TAG_LEN + - dlg->id.len + 2 + 2 ); + memset(replaces_hdr, 0, sizeof(str)); - memcpy( replaces_hdr->s, SCA_REPLACES_HDR_PREFIX, - SCA_REPLACES_HDR_PREFIX_LEN ); - len = SCA_REPLACES_HDR_PREFIX_LEN; + /* +2 for semicolons separating tags, +2 for CRLF */ + replaces_hdr->s = + pkg_malloc( + SCA_REPLACES_HDR_PREFIX_LEN + SCA_REPLACES_TO_TAG_LEN + SCA_REPLACES_FROM_TAG_LEN + dlg->id.len + 2 + 2); - memcpy( replaces_hdr->s + len, dlg->call_id.s, dlg->call_id.len ); - len += dlg->call_id.len; + memcpy(replaces_hdr->s, SCA_REPLACES_HDR_PREFIX, + SCA_REPLACES_HDR_PREFIX_LEN); + len = SCA_REPLACES_HDR_PREFIX_LEN; - memcpy( replaces_hdr->s + len, ";", strlen( ";" )); - len += strlen( ";" ); + memcpy(replaces_hdr->s + len, dlg->call_id.s, dlg->call_id.len); + len += dlg->call_id.len; - memcpy( replaces_hdr->s + len, SCA_REPLACES_TO_TAG, - SCA_REPLACES_TO_TAG_LEN ); - len += SCA_REPLACES_TO_TAG_LEN; - memcpy( replaces_hdr->s + len, dlg->to_tag.s, dlg->to_tag.len ); - len += dlg->to_tag.len; + memcpy(replaces_hdr->s + len, ";", strlen(";")); + len += strlen(";"); - memcpy( replaces_hdr->s + len, ";", strlen( ";" )); - len += strlen( ";" ); + memcpy(replaces_hdr->s + len, SCA_REPLACES_TO_TAG, + SCA_REPLACES_TO_TAG_LEN); + len += SCA_REPLACES_TO_TAG_LEN; + memcpy(replaces_hdr->s + len, dlg->to_tag.s, dlg->to_tag.len); + len += dlg->to_tag.len; - memcpy( replaces_hdr->s + len, SCA_REPLACES_FROM_TAG, - SCA_REPLACES_FROM_TAG_LEN ); - len += SCA_REPLACES_FROM_TAG_LEN; - memcpy( replaces_hdr->s + len, dlg->from_tag.s, dlg->from_tag.len ); - len += dlg->from_tag.len; + memcpy(replaces_hdr->s + len, ";", strlen(";")); + len += strlen(";"); - memcpy( replaces_hdr->s + len, CRLF, CRLF_LEN ); - len += CRLF_LEN; + memcpy(replaces_hdr->s + len, SCA_REPLACES_FROM_TAG, + SCA_REPLACES_FROM_TAG_LEN); + len += SCA_REPLACES_FROM_TAG_LEN; + memcpy(replaces_hdr->s + len, dlg->from_tag.s, dlg->from_tag.len); + len += dlg->from_tag.len; - replaces_hdr->len = len; + memcpy(replaces_hdr->s + len, CRLF, CRLF_LEN); + len += CRLF_LEN; - return( len ); + replaces_hdr->len = len; + + return (len); } diff --git a/modules/sca/sca_dialog.h b/modules/sca/sca_dialog.h index 9f7a250d446..d6cf25e6fbb 100644 --- a/modules/sca/sca_dialog.h +++ b/modules/sca/sca_dialog.h @@ -23,22 +23,22 @@ #define SCA_DIALOG_H struct _sca_dialog { - str id; /* call-id + from-tag + to-tag */ - str call_id; - str from_tag; - str to_tag; + str id; /* call-id + from-tag + to-tag */ + str call_id; + str from_tag; + str to_tag; - int notify_cseq; - int subscribe_cseq; + int notify_cseq; + int subscribe_cseq; }; -typedef struct _sca_dialog sca_dialog; +typedef struct _sca_dialog sca_dialog; #define SCA_DIALOG_EMPTY(d) \ ((d) == NULL || (SCA_STR_EMPTY( &(d)->call_id ) && \ SCA_STR_EMPTY( &(d)->from_tag ) && \ SCA_STR_EMPTY( &(d)->to_tag ))) -int sca_dialog_build_from_tags( sca_dialog *, int, str *, str *, str * ); -int sca_dialog_create_replaces_header( sca_dialog *, str * ); +int sca_dialog_build_from_tags(sca_dialog *, int, str *, str *, str *); +int sca_dialog_create_replaces_header(sca_dialog *, str *); #endif /* SCA_DIALOG_H */ diff --git a/modules/sca/sca_event.c b/modules/sca/sca_event.c index ddd9c78cdcd..a01d40a5403 100644 --- a/modules/sca/sca_event.c +++ b/modules/sca/sca_event.c @@ -23,56 +23,51 @@ #include "sca_event.h" -str SCA_EVENT_NAME_CALL_INFO = STR_STATIC_INIT( "call-info" ); -str SCA_EVENT_NAME_LINE_SEIZE = STR_STATIC_INIT( "line-seize" ); +str SCA_EVENT_NAME_CALL_INFO = STR_STATIC_INIT( "call-info" ); +str SCA_EVENT_NAME_LINE_SEIZE = STR_STATIC_INIT( "line-seize" ); - int -sca_event_from_str( str *event_str ) -{ - int event = SCA_EVENT_TYPE_UNKNOWN; +int sca_event_from_str(str *event_str) { + int event = SCA_EVENT_TYPE_UNKNOWN; - if ( event_str == NULL || event_str->s == NULL ) { - return( SCA_EVENT_TYPE_UNKNOWN ); - } + if (event_str == NULL || event_str->s == NULL) { + return (SCA_EVENT_TYPE_UNKNOWN); + } - if ( STR_EQ( *event_str, SCA_EVENT_NAME_CALL_INFO )) { - event = SCA_EVENT_TYPE_CALL_INFO; - } else if ( STR_EQ( *event_str, SCA_EVENT_NAME_LINE_SEIZE )) { - event = SCA_EVENT_TYPE_LINE_SEIZE; - } + if (STR_EQ(*event_str, SCA_EVENT_NAME_CALL_INFO)) { + event = SCA_EVENT_TYPE_CALL_INFO; + } else if (STR_EQ(*event_str, SCA_EVENT_NAME_LINE_SEIZE)) { + event = SCA_EVENT_TYPE_LINE_SEIZE; + } - return( event ); + return (event); } - char * -sca_event_name_from_type( int event_type ) -{ - switch ( event_type ) { - case SCA_EVENT_TYPE_CALL_INFO: - return( SCA_EVENT_NAME_CALL_INFO.s ); +char * +sca_event_name_from_type(int event_type) { + switch (event_type) { + case SCA_EVENT_TYPE_CALL_INFO: + return (SCA_EVENT_NAME_CALL_INFO.s); - case SCA_EVENT_TYPE_LINE_SEIZE: - return( SCA_EVENT_NAME_LINE_SEIZE.s ); + case SCA_EVENT_TYPE_LINE_SEIZE: + return (SCA_EVENT_NAME_LINE_SEIZE.s); - default: - break; - } + default: + break; + } - return( "unknown" ); + return ("unknown"); } - int -sca_event_append_header_for_type( int event_type, char *hdrbuf, int maxlen ) -{ - int len; +int sca_event_append_header_for_type(int event_type, char *hdrbuf, int maxlen) { + int len; - len = snprintf( hdrbuf, maxlen, "Event: %s%s", - sca_event_name_from_type( event_type ), CRLF ); - if ( len >= maxlen ) { - LM_ERR( "%s Event header too long", - sca_event_name_from_type( event_type)); - return( -1 ); - } + len = snprintf(hdrbuf, maxlen, "Event: %s%s", + sca_event_name_from_type(event_type), CRLF); + if (len >= maxlen) { + LM_ERR( "%s Event header too long\n", + sca_event_name_from_type( event_type)); + return (-1); + } - return( len ); + return (len); } diff --git a/modules/sca/sca_event.h b/modules/sca/sca_event.h index a5922825a85..583b94382f1 100644 --- a/modules/sca/sca_event.h +++ b/modules/sca/sca_event.h @@ -20,19 +20,19 @@ * */ enum { - SCA_EVENT_TYPE_UNKNOWN = -1, - SCA_EVENT_TYPE_CALL_INFO = 1, - SCA_EVENT_TYPE_LINE_SEIZE = 2, + SCA_EVENT_TYPE_UNKNOWN = -1, + SCA_EVENT_TYPE_CALL_INFO = 1, + SCA_EVENT_TYPE_LINE_SEIZE = 2, }; -extern str SCA_EVENT_NAME_CALL_INFO; -extern str SCA_EVENT_NAME_LINE_SEIZE; +extern str SCA_EVENT_NAME_CALL_INFO; +extern str SCA_EVENT_NAME_LINE_SEIZE; #define sca_ok_status_for_event(e1) \ (e1) == SCA_EVENT_TYPE_CALL_INFO ? 202 : 200 #define sca_ok_text_for_event(e1) \ (e1) == SCA_EVENT_TYPE_CALL_INFO ? "Accepted" : "OK" -int sca_event_from_str( str * ); -char *sca_event_name_from_type( int ); -int sca_event_append_header_for_type( int, char *, int ); +int sca_event_from_str(str *); +char *sca_event_name_from_type(int); +int sca_event_append_header_for_type(int, char *, int); diff --git a/modules/sca/sca_hash.c b/modules/sca/sca_hash.c index fa469c119e4..aa37bc4b723 100644 --- a/modules/sca/sca_hash.c +++ b/modules/sca/sca_hash.c @@ -26,340 +26,304 @@ #include "sca.h" #include "sca_hash.h" - int -sca_hash_table_create( sca_hash_table **ht, unsigned int size ) -{ - int i; - - assert( ht != NULL ); - - *ht = shm_malloc( sizeof( sca_hash_table )); - if ( *ht == NULL ) { - LM_ERR( "Failed to shm_malloc space for hash table" ); - return( -1 ); - } - - (*ht)->size = size; - (*ht)->slots = (sca_hash_slot *)shm_malloc( size * sizeof(sca_hash_slot)); - if ((*ht)->slots == NULL ) { - LM_ERR( "Failed to shm_malloc hash table slots" ); - shm_free( *ht ); *ht = NULL; - return( -1 ); - } - memset((*ht)->slots, 0, size * sizeof( sca_hash_slot )); - - for ( i = 0; i < (*ht)->size; i++ ) { - if ( lock_init( &(*ht)->slots[ i ].lock ) == NULL ) { - LM_ERR( "Failed to initialized lock in hash table slot %d", i ); - shm_free( *ht ); *ht = NULL; - return( -1 ); +int sca_hash_table_create(sca_hash_table **ht, unsigned int size) { + int i; + + assert(ht != NULL); + + *ht = shm_malloc(sizeof(sca_hash_table)); + if (*ht == NULL) { + LM_ERR( "Failed to shm_malloc space for hash table\n" ); + return (-1); } - } - return( 0 ); -} + (*ht)->size = size; + (*ht)->slots = (sca_hash_slot *) shm_malloc(size * sizeof(sca_hash_slot)); + if ((*ht)->slots == NULL) { + LM_ERR( "Failed to shm_malloc hash table slots\n" ); + shm_free(*ht); + *ht = NULL; + return (-1); + } + memset((*ht)->slots, 0, size * sizeof(sca_hash_slot)); + + for (i = 0; i < (*ht)->size; i++) { + if (lock_init(&(*ht)->slots[i].lock) == NULL) { + LM_ERR( "Failed to initialized lock in hash table slot %d\n", i ); + shm_free(*ht); + *ht = NULL; + return (-1); + } + } - int -sca_hash_table_slot_kv_insert_unsafe( sca_hash_slot *slot, void *value, - int (*e_compare)( str *, void * ), void (*e_description)( void * ), - void (*e_free)( void * )) -{ - sca_hash_entry *new_entry; - sca_hash_entry **cur_entry; - - assert( slot != NULL ); - assert( value != NULL ); - assert( e_free != NULL ); - - new_entry = (sca_hash_entry *)shm_malloc( sizeof( sca_hash_entry )); - if ( new_entry == NULL ) { - LM_ERR( "Failed to shm_malloc new hash table entry for slot %p", slot ); - return( -1 ); - } - new_entry->value = value; - new_entry->compare = e_compare; - new_entry->description = e_description; - new_entry->free_entry = e_free; - new_entry->slot = slot; - - cur_entry = &slot->entries; - new_entry->next = *cur_entry; - *cur_entry = new_entry; - - return( 0 ); + return (0); } - int -sca_hash_table_slot_kv_insert( sca_hash_slot *slot, void *value, - int (*e_compare)( str *, void * ), void (*e_description)( void * ), - void (*e_free)( void * )) -{ - int rc; +int sca_hash_table_slot_kv_insert_unsafe(sca_hash_slot *slot, void *value, + int (*e_compare)(str *, void *), void (*e_description)(void *), + void (*e_free)(void *)) { + sca_hash_entry *new_entry; + sca_hash_entry **cur_entry; - lock_get( &slot->lock ); + assert(slot != NULL); + assert(value != NULL); + assert(e_free != NULL); - rc = sca_hash_table_slot_kv_insert_unsafe( slot, value, e_compare, - e_description, e_free ); + new_entry = (sca_hash_entry *) shm_malloc(sizeof(sca_hash_entry)); + if (new_entry == NULL) { + LM_ERR( "Failed to shm_malloc new hash table entry for slot %p\n", slot ); + return (-1); + } + new_entry->value = value; + new_entry->compare = e_compare; + new_entry->description = e_description; + new_entry->free_entry = e_free; + new_entry->slot = slot; - lock_release( &slot->lock ); + cur_entry = &slot->entries; + new_entry->next = *cur_entry; + *cur_entry = new_entry; - return( rc ); + return (0); } +int sca_hash_table_slot_kv_insert(sca_hash_slot *slot, void *value, + int (*e_compare)(str *, void *), void (*e_description)(void *), + void (*e_free)(void *)) { + int rc; + + lock_get(&slot->lock); - int -sca_hash_table_index_kv_insert( sca_hash_table *ht, int slot_idx, void *value, - int (*e_compare)( str *, void * ), void (*e_description)( void * ), - void (*e_free)( void * )) -{ - assert( ht != NULL ); - assert( ht->slots != NULL ); - assert( slot_idx >= 0 && slot_idx < ht->size ); + rc = sca_hash_table_slot_kv_insert_unsafe(slot, value, e_compare, + e_description, e_free); - return( sca_hash_table_slot_kv_insert( &ht->slots[ slot_idx ], value, - e_compare, e_description, - e_free )); + lock_release(&slot->lock); + + return (rc); } +int sca_hash_table_index_kv_insert(sca_hash_table *ht, int slot_idx, + void *value, int (*e_compare)(str *, void *), + void (*e_description)(void *), void (*e_free)(void *)) { + assert(ht != NULL); + assert(ht->slots != NULL); + assert(slot_idx >= 0 && slot_idx < ht->size); - int -sca_hash_table_kv_insert( sca_hash_table *ht, str *key, void *value, - int (*e_compare)(str *, void *), void (*e_description)(void *), - void (*e_free)(void *)) -{ - int hash_idx; - int rc; + return (sca_hash_table_slot_kv_insert(&ht->slots[slot_idx], value, + e_compare, e_description, e_free)); +} - assert( ht != NULL && !SCA_STR_EMPTY( key ) && value != NULL ); +int sca_hash_table_kv_insert(sca_hash_table *ht, str *key, void *value, + int (*e_compare)(str *, void *), void (*e_description)(void *), + void (*e_free)(void *)) { + int hash_idx; + int rc; - hash_idx = sca_hash_table_index_for_key( ht, key ); - rc = sca_hash_table_index_kv_insert( ht, hash_idx, value, e_compare, - e_description, e_free ); + assert(ht != NULL && !SCA_STR_EMPTY(key) && value != NULL); - return( rc ); + hash_idx = sca_hash_table_index_for_key(ht, key); + rc = sca_hash_table_index_kv_insert(ht, hash_idx, value, e_compare, + e_description, e_free); + + return (rc); } - void * -sca_hash_table_slot_kv_find_unsafe( sca_hash_slot *slot, str *key ) -{ - sca_hash_entry *e; - void *value = NULL; +void * +sca_hash_table_slot_kv_find_unsafe(sca_hash_slot *slot, str *key) { + sca_hash_entry *e; + void *value = NULL; - assert( slot != NULL && !SCA_STR_EMPTY( key )); + assert(slot != NULL && !SCA_STR_EMPTY(key)); - for ( e = slot->entries; e != NULL; e = e->next ) { - if ( e->compare( key, e->value ) == 0 ) { - value = e->value; + for (e = slot->entries; e != NULL; e = e->next) { + if (e->compare(key, e->value) == 0) { + value = e->value; + } } - } - return( value ); + return (value); } - void * -sca_hash_table_slot_kv_find( sca_hash_slot *slot, str *key ) -{ - void *value; +void * +sca_hash_table_slot_kv_find(sca_hash_slot *slot, str *key) { + void *value; - lock_get( &slot->lock ); - value = sca_hash_table_slot_kv_find_unsafe( slot, key ); - lock_release( &slot->lock ); + lock_get(&slot->lock); + value = sca_hash_table_slot_kv_find_unsafe(slot, key); + lock_release(&slot->lock); - return( value ); + return (value); } - void * -sca_hash_table_index_kv_find_unsafe(sca_hash_table *ht, int slot_idx, str *key) -{ - assert( ht != NULL && !SCA_STR_EMPTY( key )); - assert( slot_idx >= 0 && slot_idx < ht->size ); +void * +sca_hash_table_index_kv_find_unsafe(sca_hash_table *ht, int slot_idx, str *key) { + assert(ht != NULL && !SCA_STR_EMPTY(key)); + assert(slot_idx >= 0 && slot_idx < ht->size); - return( sca_hash_table_slot_kv_find_unsafe( &ht->slots[ slot_idx ], key )); + return (sca_hash_table_slot_kv_find_unsafe(&ht->slots[slot_idx], key)); } - void * -sca_hash_table_index_kv_find( sca_hash_table *ht, int slot_idx, str *key ) -{ - assert( ht != NULL && !SCA_STR_EMPTY( key )); - assert( slot_idx >= 0 && slot_idx < ht->size ); +void * +sca_hash_table_index_kv_find(sca_hash_table *ht, int slot_idx, str *key) { + assert(ht != NULL && !SCA_STR_EMPTY(key)); + assert(slot_idx >= 0 && slot_idx < ht->size); - return( sca_hash_table_slot_kv_find( &ht->slots[ slot_idx ], key )); + return (sca_hash_table_slot_kv_find(&ht->slots[slot_idx], key)); } - void * -sca_hash_table_kv_find( sca_hash_table *ht, str *key ) -{ - int slot_idx; +void * +sca_hash_table_kv_find(sca_hash_table *ht, str *key) { + int slot_idx; - slot_idx = sca_hash_table_index_for_key( ht, key ); + slot_idx = sca_hash_table_index_for_key(ht, key); - return( sca_hash_table_index_kv_find( ht, slot_idx, key )); + return (sca_hash_table_index_kv_find(ht, slot_idx, key)); } - sca_hash_entry * -sca_hash_table_slot_kv_find_entry_unsafe( sca_hash_slot *slot, str *key ) -{ - sca_hash_entry *e = NULL; +sca_hash_entry * +sca_hash_table_slot_kv_find_entry_unsafe(sca_hash_slot *slot, str *key) { + sca_hash_entry *e = NULL; - assert( slot != NULL && !SCA_STR_EMPTY( key )); + assert(slot != NULL && !SCA_STR_EMPTY(key)); - for ( e = slot->entries; e != NULL; e = e->next ) { - if ( e->compare( key, e->value ) == 0 ) { - break; + for (e = slot->entries; e != NULL; e = e->next) { + if (e->compare(key, e->value) == 0) { + break; + } } - } - return( e ); + return (e); } - sca_hash_entry * -sca_hash_table_slot_kv_find_entry( sca_hash_slot *slot, str *key ) -{ - sca_hash_entry *e; +sca_hash_entry * +sca_hash_table_slot_kv_find_entry(sca_hash_slot *slot, str *key) { + sca_hash_entry *e; - lock_get( &slot->lock ); - e = sca_hash_table_slot_kv_find_entry( slot, key ); - lock_release( &slot->lock ); + lock_get(&slot->lock); + e = sca_hash_table_slot_kv_find_entry(slot, key); + lock_release(&slot->lock); - return( e ); + return (e); } - void -sca_hash_entry_free( sca_hash_entry *e ) -{ - assert( e != NULL ); +void sca_hash_entry_free(sca_hash_entry *e) { + assert(e != NULL); - e->free_entry( e->value ); - shm_free( e ); + e->free_entry(e->value); + shm_free(e); } - sca_hash_entry * -sca_hash_table_slot_unlink_entry_unsafe( sca_hash_slot *slot, - sca_hash_entry *e ) -{ - sca_hash_entry **cur_e; +sca_hash_entry * +sca_hash_table_slot_unlink_entry_unsafe(sca_hash_slot *slot, sca_hash_entry *e) { + sca_hash_entry **cur_e; - assert( slot != NULL ); - assert( e != NULL ); + assert(slot != NULL); + assert(e != NULL); - for ( cur_e = &slot->entries; *cur_e != NULL; cur_e = &(*cur_e)->next ) { - if ( *cur_e == e ) { - *cur_e = e->next; + for (cur_e = &slot->entries; *cur_e != NULL; cur_e = &(*cur_e)->next) { + if (*cur_e == e) { + *cur_e = e->next; - /* ensure any attempted traversal using this entry goes nowhere */ - e->next = NULL; - e->slot = NULL; + /* ensure any attempted traversal using this entry goes nowhere */ + e->next = NULL; + e->slot = NULL; - break; + break; + } } - } - return( e ); + return (e); } - int -sca_hash_table_slot_kv_delete_unsafe( sca_hash_slot *slot, str *key ) -{ - sca_hash_entry *e; +int sca_hash_table_slot_kv_delete_unsafe(sca_hash_slot *slot, str *key) { + sca_hash_entry *e; - e = sca_hash_table_slot_kv_find_entry_unsafe( slot, key ); - if ( e == NULL ) { - return( -1 ); - } + e = sca_hash_table_slot_kv_find_entry_unsafe(slot, key); + if (e == NULL) { + return (-1); + } - e = sca_hash_table_slot_unlink_entry_unsafe( slot, e ); - if ( e ) { - e->free_entry( e->value ); - shm_free( e ); - } + e = sca_hash_table_slot_unlink_entry_unsafe(slot, e); + if (e) { + e->free_entry(e->value); + shm_free(e); + } - return( 0 ); + return (0); } - int -sca_hash_table_slot_kv_delete( sca_hash_slot *slot, str *key ) -{ - int rc; +int sca_hash_table_slot_kv_delete(sca_hash_slot *slot, str *key) { + int rc; - lock_get( &slot->lock ); - rc = sca_hash_table_slot_kv_delete_unsafe( slot, key ); - lock_release( &slot->lock ); + lock_get(&slot->lock); + rc = sca_hash_table_slot_kv_delete_unsafe(slot, key); + lock_release(&slot->lock); - return( rc ); + return (rc); } - int -sca_hash_table_index_kv_delete( sca_hash_table *ht, int slot_idx, str *key ) -{ - return( sca_hash_table_slot_kv_delete( &ht->slots[ slot_idx ], key )); +int sca_hash_table_index_kv_delete(sca_hash_table *ht, int slot_idx, str *key) { + return (sca_hash_table_slot_kv_delete(&ht->slots[slot_idx], key)); } - int -sca_hash_table_kv_delete( sca_hash_table *ht, str *key ) -{ - int slot_idx; +int sca_hash_table_kv_delete(sca_hash_table *ht, str *key) { + int slot_idx; - slot_idx = sca_hash_table_index_for_key( ht, key ); + slot_idx = sca_hash_table_index_for_key(ht, key); - return( sca_hash_table_index_kv_delete( ht, slot_idx, key )); + return (sca_hash_table_index_kv_delete(ht, slot_idx, key)); } - static void -sca_hash_slot_print( sca_hash_slot *hs ) -{ - sca_hash_entry *e; +static void sca_hash_slot_print(sca_hash_slot *hs) { + sca_hash_entry *e; - for ( e = hs->entries; e != NULL; e = e->next ) { - if ( e->description != NULL ) { - e->description( e->value ); - } else { - LM_DBG( "0x%p", e->value ); + for (e = hs->entries; e != NULL; e = e->next) { + if (e->description != NULL) { + e->description(e->value); + } else { + LM_DBG( "0x%p\n", e->value ); + } } - } } - void -sca_hash_table_print( sca_hash_table *ht ) -{ - unsigned int i; +void sca_hash_table_print(sca_hash_table *ht) { + unsigned int i; - for ( i = 0; i < ht->size; i++ ) { - LM_DBG( "SLOT %d:", i ); - sca_hash_slot_print( &ht->slots[ i ] ); - } + for (i = 0; i < ht->size; i++) { + LM_DBG( "SLOT %d:", i ); + sca_hash_slot_print(&ht->slots[i]); + } } - void -sca_hash_table_free( sca_hash_table *ht ) -{ - sca_hash_entry *e, *e_tmp; - unsigned int i; +void sca_hash_table_free(sca_hash_table *ht) { + sca_hash_entry *e, *e_tmp; + unsigned int i; - if ( ht == NULL ) { - return; - } - - for ( i = 0; i < ht->size; i++ ) { - if ( ht->slots[ i ].entries == NULL ) { - continue; + if (ht == NULL) { + return; } - sca_hash_table_lock_index( ht, i ); + for (i = 0; i < ht->size; i++) { + if (ht->slots[i].entries == NULL) { + continue; + } - for ( e = ht->slots[ i ].entries; e != NULL; e = e_tmp ) { - e_tmp = e->next; + sca_hash_table_lock_index(ht, i); - e->free_entry( e->value ); + for (e = ht->slots[i].entries; e != NULL; e = e_tmp) { + e_tmp = e->next; - shm_free( e ); - } + e->free_entry(e->value); - sca_hash_table_unlock_index( ht, i ); + shm_free(e); + } - lock_destroy( &ht->slots[ i ].lock ); - lock_dealloc( &ht->slots[ i ].lock ); - } + sca_hash_table_unlock_index(ht, i); + + lock_destroy(&ht->slots[i].lock); + lock_dealloc(&ht->slots[i].lock); + } - shm_free( ht->slots ); - shm_free( ht ); + shm_free(ht->slots); + shm_free(ht); } diff --git a/modules/sca/sca_hash.h b/modules/sca/sca_hash.h index c833c5ed239..6969ad1abd3 100644 --- a/modules/sca/sca_hash.h +++ b/modules/sca/sca_hash.h @@ -24,26 +24,26 @@ struct _sca_hash_slot; struct _sca_hash_entry { - void *value; - int (*compare)( str *, void * ); - void (*description)( void * ); - void (*free_entry)( void * ); - struct _sca_hash_slot *slot; - struct _sca_hash_entry *next; + void *value; + int (*compare)(str *, void *); + void (*description)(void *); + void (*free_entry)(void *); + struct _sca_hash_slot *slot; + struct _sca_hash_entry *next; }; -typedef struct _sca_hash_entry sca_hash_entry; +typedef struct _sca_hash_entry sca_hash_entry; struct _sca_hash_slot { - gen_lock_t lock; - sca_hash_entry *entries; + gen_lock_t lock; + sca_hash_entry *entries; }; -typedef struct _sca_hash_slot sca_hash_slot; +typedef struct _sca_hash_slot sca_hash_slot; struct _sca_hash_table { - unsigned int size; /* power of two */ - sca_hash_slot *slots; + unsigned int size; /* power of two */ + sca_hash_slot *slots; }; -typedef struct _sca_hash_table sca_hash_table; +typedef struct _sca_hash_table sca_hash_table; #define sca_hash_table_index_for_key( ht1, str1 ) \ (get_hash1_raw((str1)->s, (str1)->len) & ((ht1)->size - 1 )) @@ -56,41 +56,35 @@ typedef struct _sca_hash_table sca_hash_table; #define sca_hash_table_unlock_index( ht1, idx1 ) \ lock_release( &(ht1)->slots[ (idx1) ].lock ) - /* hash table operations */ -int sca_hash_table_create( sca_hash_table **, unsigned int ); -void sca_hash_table_print( sca_hash_table * ); -void sca_hash_table_free( sca_hash_table * ); +int sca_hash_table_create(sca_hash_table **, unsigned int); +void sca_hash_table_print(sca_hash_table *); +void sca_hash_table_free(sca_hash_table *); -void sca_hash_entry_free( sca_hash_entry * ); +void sca_hash_entry_free(sca_hash_entry *); /* key-value operations */ -int sca_hash_table_slot_kv_insert_unsafe( sca_hash_slot *, void *, - int (*)(str *, void *), - void (*)(void *), void (*)(void *)); -int sca_hash_table_slot_kv_insert( sca_hash_slot *, void *, - int (*)(str *, void *), - void (*)(void *), void (*)(void *)); -int sca_hash_table_index_kv_insert( sca_hash_table *, int, void *, - int (*)(str *, void *), - void (*)(void *), void (*)(void *)); -int sca_hash_table_kv_insert( sca_hash_table *, str *, void *, - int (*)(str *, void *), - void (*)(void *), void (*)(void *)); -void *sca_hash_table_slot_kv_find_unsafe( sca_hash_slot *, str * ); -void *sca_hash_table_slot_kv_find( sca_hash_slot *, str * ); -void *sca_hash_table_index_kv_find_unsafe( sca_hash_table *, int, str * ); -void *sca_hash_table_index_kv_find( sca_hash_table *, int, str * ); -void *sca_hash_table_kv_find( sca_hash_table *, str * ); +int sca_hash_table_slot_kv_insert_unsafe(sca_hash_slot *, void *, + int (*)(str *, void *), void (*)(void *), void (*)(void *)); +int sca_hash_table_slot_kv_insert(sca_hash_slot *, void *, + int (*)(str *, void *), void (*)(void *), void (*)(void *)); +int sca_hash_table_index_kv_insert(sca_hash_table *, int, void *, + int (*)(str *, void *), void (*)(void *), void (*)(void *)); +int sca_hash_table_kv_insert(sca_hash_table *, str *, void *, + int (*)(str *, void *), void (*)(void *), void (*)(void *)); +void *sca_hash_table_slot_kv_find_unsafe(sca_hash_slot *, str *); +void *sca_hash_table_slot_kv_find(sca_hash_slot *, str *); +void *sca_hash_table_index_kv_find_unsafe(sca_hash_table *, int, str *); +void *sca_hash_table_index_kv_find(sca_hash_table *, int, str *); +void *sca_hash_table_kv_find(sca_hash_table *, str *); -sca_hash_entry *sca_hash_table_slot_kv_find_entry_unsafe( sca_hash_slot *, - str * ); -sca_hash_entry *sca_hash_table_slot_unlink_entry_unsafe( sca_hash_slot *, - sca_hash_entry * ); +sca_hash_entry *sca_hash_table_slot_kv_find_entry_unsafe(sca_hash_slot *, str *); +sca_hash_entry *sca_hash_table_slot_unlink_entry_unsafe(sca_hash_slot *, + sca_hash_entry *); -int sca_hash_table_slot_kv_delete( sca_hash_slot *, str * ); -int sca_hash_table_index_kv_delete( sca_hash_table *, int, str * ); -int sca_hash_table_kv_delete( sca_hash_table *, str * ); +int sca_hash_table_slot_kv_delete(sca_hash_slot *, str *); +int sca_hash_table_index_kv_delete(sca_hash_table *, int, str *); +int sca_hash_table_kv_delete(sca_hash_table *, str *); #endif /* SCA_HASH_H */ diff --git a/modules/sca/sca_notify.c b/modules/sca/sca_notify.c index e8cbdba3145..af9858998ba 100644 --- a/modules/sca/sca_notify.c +++ b/modules/sca/sca_notify.c @@ -24,6 +24,7 @@ #include #include "sca.h" +#include "sca_appearance.h" #include "sca_call_info.h" #include "sca_event.h" #include "sca_notify.h" @@ -31,361 +32,351 @@ #include "../../modules/tm/tm_load.h" +const str SCA_METHOD_NOTIFY = STR_STATIC_INIT( "NOTIFY" ); -const str SCA_METHOD_NOTIFY = STR_STATIC_INIT( "NOTIFY" ); +static void sca_notify_reply_cb(struct cell *t, int cb_type, + struct tmcb_params *cbp) { + struct sip_msg *notify_reply = NULL; + str to_aor = STR_NULL; + str *contact_uri; - static void -sca_notify_reply_cb( struct cell *t, int cb_type, struct tmcb_params *cbp ) -{ - struct sip_msg *notify_reply = NULL; - str to_aor = STR_NULL; - str *contact_uri; + if (cbp == NULL) { + LM_ERR( "Empty parameters passed to NOTIFY callback!\n" ); + return; + } + if ((notify_reply = cbp->rpl) == NULL) { + LM_ERR( "Empty reply passed to NOTIFY callback!\n" ); + return; + } - if ( cbp == NULL ) { - LM_ERR( "Empty parameters passed to NOTIFY callback!" ); - return; - } - if (( notify_reply = cbp->rpl ) == NULL ) { - LM_ERR( "Empty reply passed to NOTIFY callback!" ); - return; - } + contact_uri = &t->uac[0].uri; + if (notify_reply != FAKED_REPLY && REPLY_CLASS( notify_reply ) == 2) { + LM_DBG( "NOTIFY %.*s returned %d\n", STR_FMT( contact_uri ), + notify_reply->REPLY_STATUS ); + return; + } - contact_uri = &t->uac[ 0 ].uri; - if ( notify_reply != FAKED_REPLY && REPLY_CLASS( notify_reply ) == 2 ) { - LM_DBG( "NOTIFY %.*s returned %d", STR_FMT( contact_uri ), - notify_reply->REPLY_STATUS ); - return; - } - - /* - * after this, we've either gotten an error from the client, or a faked - * reply from the proxy. remove the subscription in either case. it's - * possible the client will return 481 (no such transaction), but that's - * still grounds for us to remove the subscription, since the dialog - * we have associated with the subscription is no longer valid. - */ - if ( notify_reply == FAKED_REPLY ) { - LM_ERR( "NOTIFY %.*s resulted in FAKED_REPLY from proxy: " - "failed to deliver NOTIFY to client", STR_FMT( contact_uri )); - } else { - LM_ERR( "NOTIFY %.*s returned %d %.*s removing call-info " - "subscription for %.*s", STR_FMT( contact_uri ), - notify_reply->REPLY_STATUS, - STR_FMT( ¬ify_reply->first_line.u.reply.reason ), - STR_FMT( contact_uri )); - } - return; - - if ( sca_uri_extract_aor( &t->to, &to_aor ) < 0 ) { - LM_ERR( "Failed to extract AoR from %.*s", STR_FMT( &t->to )); + /* + * after this, we've either gotten an error from the client, or a faked + * reply from the proxy. remove the subscription in either case. it's + * possible the client will return 481 (no such transaction), but that's + * still grounds for us to remove the subscription, since the dialog + * we have associated with the subscription is no longer valid. + */ + if (notify_reply == FAKED_REPLY) { + LM_ERR( "NOTIFY %.*s resulted in FAKED_REPLY from proxy: " + "failed to deliver NOTIFY to client\n", STR_FMT( contact_uri )); + } else { + LM_ERR( "NOTIFY %.*s returned %d %.*s removing call-info " + "subscription for %.*s\n", STR_FMT( contact_uri ), + notify_reply->REPLY_STATUS, + STR_FMT( ¬ify_reply->first_line.u.reply.reason ), + STR_FMT( contact_uri )); + } return; - } - /* t->to is the entire To header: "To: sip:....", so move to_aor.s ahead */ - if ( memcmp( to_aor.s, "To: ", strlen( "To: " )) == 0 ) { - to_aor.s += strlen( "To: " ); - to_aor.len -= strlen( "To: " ); - } - - if ( sca_subscription_delete_subscriber_for_event( sca, contact_uri, - &SCA_EVENT_NAME_CALL_INFO, &to_aor ) < 0 ) { - LM_ERR( "Failed to delete %.*s %.*s subscription", - STR_FMT( contact_uri ), STR_FMT( &SCA_EVENT_NAME_CALL_INFO )); - } + + if (sca_uri_extract_aor(&t->to, &to_aor) < 0) { + LM_ERR( "Failed to extract AoR from %.*s\n", STR_FMT( &t->to )); + return; + } + /* t->to is the entire To header: "To: sip:....", so move to_aor.s ahead */ + if (memcmp(to_aor.s, "To: ", strlen("To: ")) == 0) { + to_aor.s += strlen("To: "); + to_aor.len -= strlen("To: "); + } + + if (sca_subscription_delete_subscriber_for_event(sca, contact_uri, + &SCA_EVENT_NAME_CALL_INFO, &to_aor) < 0) { + LM_ERR( "Failed to delete %.*s %.*s subscription\n", + STR_FMT( contact_uri ), STR_FMT( &SCA_EVENT_NAME_CALL_INFO )); + } } - static dlg_t * -sca_notify_dlg_for_subscription( sca_subscription *sub ) -{ - dlg_t *dlg = NULL; - - dlg = (dlg_t *)pkg_malloc( sizeof( dlg_t )); - if ( dlg == NULL ) { - LM_ERR( "pkg_malloc dlg_t for %.*s failed: out of memory", - STR_FMT( &sub->subscriber )); - goto error; - } - memset( dlg, 0, sizeof( dlg_t )); - - dlg->loc_seq.value = sub->dialog.notify_cseq; - dlg->loc_seq.is_set = 1; - - dlg->id.call_id = sub->dialog.call_id; - dlg->id.rem_tag = sub->dialog.from_tag; - dlg->id.loc_tag = sub->dialog.to_tag; - - /* RURI */ - dlg->rem_target = sub->subscriber; - - /* To and From URIs are both the SCA AoR in an SCA NOTIFY */ - dlg->loc_uri = sub->target_aor; - dlg->rem_uri = sub->target_aor; - - /* restore route */ - if ( !SCA_STR_EMPTY( &sub->rr )) { - if ( parse_rr_body( sub->rr.s, sub->rr.len, &dlg->route_set ) < 0 ) { - LM_ERR( "sca_notify_dlg_for_subscription: failed to parse " - "%.*s subscription's Record-Route info", - STR_FMT( &sub->subscriber )); - goto error; +static dlg_t * +sca_notify_dlg_for_subscription(sca_subscription *sub) { + dlg_t *dlg = NULL; + + dlg = (dlg_t *) pkg_malloc(sizeof(dlg_t)); + if (dlg == NULL) { + LM_ERR( "pkg_malloc dlg_t for %.*s failed: out of memory\n", + STR_FMT( &sub->subscriber )); + goto error; + } + memset(dlg, 0, sizeof(dlg_t)); + + dlg->loc_seq.value = sub->dialog.notify_cseq; + dlg->loc_seq.is_set = 1; + + dlg->id.call_id = sub->dialog.call_id; + dlg->id.rem_tag = sub->dialog.from_tag; + dlg->id.loc_tag = sub->dialog.to_tag; + + /* RURI */ + dlg->rem_target = sub->subscriber; + + /* To and From URIs are both the SCA AoR in an SCA NOTIFY */ + dlg->loc_uri = sub->target_aor; + dlg->rem_uri = sub->target_aor; + + /* restore route */ + if (!SCA_STR_EMPTY(&sub->rr)) { + if (parse_rr_body(sub->rr.s, sub->rr.len, &dlg->route_set) < 0) { + LM_ERR( "sca_notify_dlg_for_subscription: failed to parse " + "%.*s subscription's Record-Route info\n", + STR_FMT( &sub->subscriber )); + goto error; + } } - } - /* - * the dialog state in an SCA NOTIFY should always be confirmed, - * since we generated the dialog to-tag in our response to the - * subscriber's SUBSCRIBE request. - */ - dlg->state = DLG_CONFIRMED; + /* + * the dialog state in an SCA NOTIFY should always be confirmed, + * since we generated the dialog to-tag in our response to the + * subscriber's SUBSCRIBE request. + */ + dlg->state = DLG_CONFIRMED; - return( dlg ); + return (dlg); -error: - if ( dlg != NULL ) { - pkg_free( dlg ); - } + error: if (dlg != NULL) { + pkg_free(dlg); + } - return( NULL ); + return (NULL); } - static int -sca_notify_append_subscription_state_header( sca_subscription *sub, - char *hdrbuf, int maxlen ) -{ - str state_str = STR_NULL; - int len, total = 0; - int ttl = sub->expires - time( NULL ); - - if ( ttl < 0 ) { - ttl = 0; - } - - sca_subscription_state_to_str( sub->state, &state_str ); - len = snprintf( hdrbuf, maxlen, "Subscription-State: %s", state_str.s ); - if ( len >= maxlen ) { - goto error; - } - total += len; - - if ( ttl > 0 ) { - len = snprintf( hdrbuf + total, maxlen - total, ";expires=%d", ttl ); - if ( len >= maxlen ) { - goto error; +static int sca_notify_append_subscription_state_header(sca_subscription *sub, + char *hdrbuf, int maxlen) { + str state_str = STR_NULL; + int len, + total = 0; + int ttl = sub->expires - time(NULL); + + if (ttl < 0) { + ttl = 0; + } + + sca_subscription_state_to_str(sub->state, &state_str); + len = snprintf(hdrbuf, maxlen, "Subscription-State: %s", state_str.s); + if (len >= maxlen) { + goto error; } total += len; - } - len = snprintf( hdrbuf + total, maxlen - total, "%s", CRLF ); - total += len; + if (ttl > 0) { + len = snprintf(hdrbuf + total, maxlen - total, ";expires=%d", ttl); + if (len >= maxlen) { + goto error; + } + total += len; + } + + len = snprintf(hdrbuf + total, maxlen - total, "%s", CRLF); + total += len; - return( total ); + return (total); -error: - LM_ERR( "Cannot append Subscription-State header: buffer too small" ); - return( -1 ); + error: + LM_ERR( "Cannot append Subscription-State header: buffer too small\n" ); + return (-1); } - static int -sca_notify_append_contact_header( sca_subscription *sub, - char *hdrbuf, int maxlen ) -{ - int len = strlen( "Contact: " ); +static int sca_notify_append_contact_header(sca_subscription *sub, char *hdrbuf, + int maxlen) { + int len = strlen("Contact: "); - if ( len + sub->target_aor.len + strlen( CRLF ) >= maxlen ) { - LM_ERR( "Cannot append Contact header: buffer too small" ); - return( -1 ); - } + if (len + sub->target_aor.len + strlen( CRLF) >= maxlen) { + LM_ERR( "Cannot append Contact header: buffer too small\n" ); + return (-1); + } - memcpy( hdrbuf, "Contact: ", len ); - memcpy( hdrbuf + len, sub->target_aor.s, sub->target_aor.len ); - len += sub->target_aor.len; - memcpy( hdrbuf + len, CRLF, strlen( CRLF )); - len += strlen( CRLF ); + memcpy(hdrbuf, "Contact: ", len); + memcpy(hdrbuf + len, sub->target_aor.s, sub->target_aor.len); + len += sub->target_aor.len; + memcpy(hdrbuf + len, CRLF, strlen( CRLF)); + len += strlen( CRLF); - return( len ); + return (len); } - static int -sca_notify_build_headers_from_info( str *hdrs, int max_hdrs_len, sca_mod *scam, - sca_subscription *sub, int app_idx ) -{ - int len = 0; - - assert( hdrs != NULL ); - - len = sca_notify_append_contact_header( sub, hdrs->s, max_hdrs_len ); - if ( len < 0 ) { - LM_ERR( "Failed to add Contact header to %s NOTIFY for %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - goto error; - } - hdrs->len = len; - - if ( app_idx == SCA_CALL_INFO_APPEARANCE_INDEX_ANY ) { - /* add Call-Info header with appearance state */ - if (( len = sca_call_info_build_header( scam, sub, - hdrs->s + hdrs->len, - max_hdrs_len - hdrs->len )) < 0 ) { - LM_ERR( "Failed to build Call-Info Headers for %s NOTIFY to %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - goto error; +static int sca_notify_build_headers_from_info(str *hdrs, int max_hdrs_len, + sca_mod *scam, sca_subscription *sub, int app_idx) { + int len = 0; + + assert(hdrs != NULL); + + len = sca_notify_append_contact_header(sub, hdrs->s, max_hdrs_len); + if (len < 0) { + LM_ERR( "Failed to add Contact header to %s NOTIFY for %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + goto error; } - } else { - /* just add Call-Info header with single appearance index */ - len = sca_call_info_append_header_for_appearance_index( sub, app_idx, - hdrs->s + hdrs->len, max_hdrs_len - hdrs->len ); - if ( len < 0 ) { - goto error; + hdrs->len = len; + + if (app_idx == SCA_CALL_INFO_APPEARANCE_INDEX_ANY) { + /* add Call-Info header with appearance state */ + if ((len = sca_call_info_build_header(scam, sub, hdrs->s + hdrs->len, + max_hdrs_len - hdrs->len)) < 0) { + LM_ERR( "Failed to build Call-Info Headers for %s NOTIFY to %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + goto error; + } + } else { + /* just add Call-Info header with single appearance index */ + len = sca_call_info_append_header_for_appearance_index(sub, app_idx, + hdrs->s + hdrs->len, max_hdrs_len - hdrs->len); + if (len < 0) { + goto error; + } + } + + hdrs->len += len; + + len = sca_event_append_header_for_type(sub->event, hdrs->s + hdrs->len, + max_hdrs_len - hdrs->len); + if (len < 0) { + LM_ERR( "Failed to add Event header to %s NOTIFY for %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + goto error; } - } - - hdrs->len += len; - - len = sca_event_append_header_for_type( sub->event, - hdrs->s + hdrs->len, max_hdrs_len - hdrs->len ); - if ( len < 0 ) { - LM_ERR( "Failed to add Event header to %s NOTIFY for %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - goto error; - } - hdrs->len += len; - - len = sca_notify_append_subscription_state_header( sub, - hdrs->s + hdrs->len, max_hdrs_len - hdrs->len ); - if ( len < 0 ) { - LM_ERR( "Failed to add Subscription-State header to %s NOTIFY for " - "%.*s", sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - goto error; - } - hdrs->len += len; - - return( hdrs->len ); - -error: - return( -1 ); + hdrs->len += len; + + len = sca_notify_append_subscription_state_header(sub, hdrs->s + hdrs->len, + max_hdrs_len - hdrs->len); + if (len < 0) { + LM_ERR( "Failed to add Subscription-State header to %s NOTIFY for " + "%.*s\n", sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + goto error; + } + hdrs->len += len; + + return (hdrs->len); + + error: return (-1); } - - static int -sca_notify_subscriber_internal( sca_mod *scam, sca_subscription *sub, - str *headers ) -{ - uac_req_t request; - dlg_t *dlg = NULL; - int rc = -1; - - dlg = sca_notify_dlg_for_subscription( sub ); - if ( dlg == NULL ) { - LM_ERR( "Failed to create dlg_t for %s NOTIFY to %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - goto done; - } - - set_uac_req( &request, (str *)&SCA_METHOD_NOTIFY, headers, NULL, dlg, - TMCB_LOCAL_COMPLETED, sca_notify_reply_cb, scam ); - rc = scam->tm_api->t_request_within( &request ); - if ( rc < 0 ) { - LM_ERR( "Failed to send in-dialog %s NOTIFY to %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - } - /* fall through, return rc from t_request_within */ - -done: - if ( dlg != NULL ) { - if ( dlg->route_set != NULL ) { - free_rr( &dlg->route_set ); + +static int sca_notify_subscriber_internal(sca_mod *scam, sca_subscription *sub, + str *headers) { + uac_req_t request; + dlg_t *dlg = NULL; + str state_str = STR_NULL; + int rc = -1; + + sca_appearance_state_to_str(sub->state, &state_str); + LM_DBG("SCA: NOTIFYing subscriber '%.*s' of event '%s' with a state of '%.*s' to index '%d'", + STR_FMT(&sub->subscriber), sca_event_name_from_type(sub->event), + STR_FMT(&state_str), sub->index); + + dlg = sca_notify_dlg_for_subscription(sub); + if (dlg == NULL) { + LM_ERR( "Failed to create dlg_t for %s NOTIFY to %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + goto done; } - pkg_free( dlg ); - } + set_uac_req(&request, (str * )&SCA_METHOD_NOTIFY, headers, NULL, dlg, + TMCB_LOCAL_COMPLETED, sca_notify_reply_cb, scam); + rc = scam->tm_api->t_request_within(&request); + if (rc < 0) { + LM_ERR( "Failed to send in-dialog %s NOTIFY to %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + } + /* fall through, return rc from t_request_within */ + + done: if (dlg != NULL) { + if (dlg->route_set != NULL) { + free_rr(&dlg->route_set); + } - return( rc ); + pkg_free(dlg); + } + + return (rc); } #define SCA_HEADERS_MAX_LEN 4096 - int -sca_notify_subscriber( sca_mod *scam, sca_subscription *sub, int app_idx ) -{ - str headers = STR_NULL; - char hdrbuf[ SCA_HEADERS_MAX_LEN ]; +int sca_notify_subscriber(sca_mod *scam, sca_subscription *sub, int app_idx) { + str headers = STR_NULL; + char hdrbuf[SCA_HEADERS_MAX_LEN]; - headers.s = hdrbuf; + LM_DBG("SCA: NOTIFYing subscriber because of a SUBSCRIPTION\n"); - if ( sca_notify_build_headers_from_info( &headers, sizeof( hdrbuf ), - scam, sub, app_idx ) < 0 ) { - LM_ERR( "Failed to build NOTIFY headers" ); - return( -1 ); - } + headers.s = hdrbuf; + if (sca_notify_build_headers_from_info(&headers, sizeof(hdrbuf), scam, sub, + app_idx) < 0) { + LM_ERR( "Failed to build NOTIFY headers\n" ); + return (-1); + } - return( sca_notify_subscriber_internal( scam, sub, &headers )); + return (sca_notify_subscriber_internal(scam, sub, &headers)); } /* send a call-info NOTIFY to all subscribers to a given SCA AoR. */ - int -sca_notify_call_info_subscribers( sca_mod *scam, str *subscription_aor ) -{ - sca_hash_slot *slot; - sca_hash_entry *e; - sca_subscription *sub; - str headers = STR_NULL; - str hash_key = STR_NULL; - char hdrbuf[ SCA_HEADERS_MAX_LEN ]; - char keybuf[ 512 ]; - char *event_name; - int slot_idx; - int rc = -1; - - assert( scam->subscriptions != NULL ); - assert( !SCA_STR_EMPTY( subscription_aor )); - - event_name = sca_event_name_from_type( SCA_EVENT_TYPE_CALL_INFO ); - - if ( subscription_aor->len + strlen( event_name ) >= sizeof( keybuf )) { - LM_ERR( "Hash key %.*s + %s is too long", - STR_FMT( subscription_aor ), event_name ); - return( -1 ); - } - hash_key.s = keybuf; - SCA_STR_COPY( &hash_key, subscription_aor ); - SCA_STR_APPEND_CSTR( &hash_key, event_name ); - - slot_idx = sca_hash_table_index_for_key( scam->subscriptions, &hash_key ); - slot = sca_hash_table_slot_for_index( scam->subscriptions, slot_idx ); - - sca_hash_table_lock_index( scam->subscriptions, slot_idx ); - - for ( e = slot->entries; e != NULL; e = e->next ) { - sub = (sca_subscription *)e->value; - if ( !SCA_STR_EQ( subscription_aor, &sub->target_aor )) { - continue; +int sca_notify_call_info_subscribers(sca_mod *scam, str *subscription_aor) { + sca_hash_slot *slot; + sca_hash_entry *e; + sca_subscription *sub; + str headers = STR_NULL; + str hash_key = STR_NULL; + char hdrbuf[SCA_HEADERS_MAX_LEN]; + char keybuf[512]; + char *event_name; + int slot_idx; + int rc = -1; + + assert(scam->subscriptions != NULL); + assert(!SCA_STR_EMPTY(subscription_aor)); + + LM_DBG("Notifying ALL subscribers for subscription AOR %.*s\n", + STR_FMT(subscription_aor)); + + event_name = sca_event_name_from_type(SCA_EVENT_TYPE_CALL_INFO); + if (subscription_aor->len + strlen(event_name) >= sizeof(keybuf)) { + LM_ERR( "Hash key %.*s + %s is too long\n", + STR_FMT( subscription_aor ), event_name ); + return (-1); } + hash_key.s = keybuf; + SCA_STR_COPY(&hash_key, subscription_aor); + SCA_STR_APPEND_CSTR(&hash_key, event_name); - if ( headers.len == 0 ) { - headers.s = hdrbuf; + slot_idx = sca_hash_table_index_for_key(scam->subscriptions, &hash_key); + slot = sca_hash_table_slot_for_index(scam->subscriptions, slot_idx); - if ( sca_notify_build_headers_from_info( &headers, sizeof( hdrbuf ), - scam, sub, SCA_CALL_INFO_APPEARANCE_INDEX_ANY ) < 0 ) { - LM_ERR( "Failed to build NOTIFY headers" ); - goto done; - } - } + sca_hash_table_lock_index(scam->subscriptions, slot_idx); + + for (e = slot->entries; e != NULL; e = e->next) { + sub = (sca_subscription *) e->value; + if (!SCA_STR_EQ(subscription_aor, &sub->target_aor)) { + continue; + } + if (headers.len == 0) { + headers.s = hdrbuf; - /* XXX would like this to be wrapped in one location */ - sub->dialog.notify_cseq += 1; + if (sca_notify_build_headers_from_info(&headers, sizeof(hdrbuf), + scam, sub, SCA_CALL_INFO_APPEARANCE_INDEX_ANY) < 0) { + LM_ERR( "Failed to build NOTIFY headers\n" ); + goto done; + } + } - if ( sca_notify_subscriber_internal( scam, sub, &headers ) < 0 ) { - goto done; + /* XXX would like this to be wrapped in one location */ + sub->dialog.notify_cseq += 1; + + if (sca_notify_subscriber_internal(scam, sub, &headers) < 0) { + goto done; + } } - } - rc = 1; - -done: - sca_hash_table_unlock_index( scam->subscriptions, slot_idx ); + rc = 1; + + done: + sca_hash_table_unlock_index(scam->subscriptions, slot_idx); - return( rc ); + return (rc); } diff --git a/modules/sca/sca_notify.h b/modules/sca/sca_notify.h index 1cfe40bb1cb..85a66e15339 100644 --- a/modules/sca/sca_notify.h +++ b/modules/sca/sca_notify.h @@ -24,9 +24,9 @@ #include "sca_subscribe.h" -extern const str SCA_METHOD_NOTIFY; +extern const str SCA_METHOD_NOTIFY; -int sca_notify_subscriber( sca_mod *, sca_subscription *, int ); -int sca_notify_call_info_subscribers( sca_mod *, str * ); +int sca_notify_subscriber(sca_mod *, sca_subscription *, int); +int sca_notify_call_info_subscribers(sca_mod *, str *); #endif /* SCA_NOTIFY_H */ diff --git a/modules/sca/sca_reply.c b/modules/sca/sca_reply.c index 3f443172573..9cdf021f89a 100644 --- a/modules/sca/sca_reply.c +++ b/modules/sca/sca_reply.c @@ -25,32 +25,30 @@ #include "sca_event.h" #include "sca_reply.h" - int -sca_reply( sca_mod *scam, int status_code, char *status_msg, - str *extra_headers, sip_msg_t *msg ) -{ - str status_str = STR_NULL; - - assert( scam != NULL && scam->sl_api != NULL ); - assert( msg != NULL ); - - status_str.s = status_msg; - status_str.len = strlen( status_msg ); - - if ( extra_headers && extra_headers->len ) { - if ( add_lump_rpl( msg, extra_headers->s, extra_headers->len, - LUMP_RPL_HDR ) == NULL ) { - LM_ERR("sca_subscription_reply: failed to add Retry-After header"); - return( -1 ); - } - } - - if ( scam->sl_api->freply( msg, status_code, &status_str ) < 0 ) { - LM_ERR( "Failed to send \"%d %s\" reply to %.*s", - status_code, status_msg, - get_from( msg )->body.len, get_from( msg )->body.s ); - return( -1 ); - } - - return( 0 ); +int sca_reply(sca_mod *scam, int status_code, char *status_msg, + str *extra_headers, sip_msg_t *msg) { + str status_str = STR_NULL; + + assert(scam != NULL && scam->sl_api != NULL); + assert(msg != NULL); + + status_str.s = status_msg; + status_str.len = strlen(status_msg); + + if (extra_headers && extra_headers->len) { + if (add_lump_rpl(msg, extra_headers->s, extra_headers->len, + LUMP_RPL_HDR) == NULL) { + LM_ERR("sca_subscription_reply: failed to add Retry-After header\n"); + return (-1); + } + } + + if (scam->sl_api->freply(msg, status_code, &status_str) < 0) { + LM_ERR( "Failed to send \"%d %s\" reply to %.*s\n", + status_code, status_msg, + get_from( msg )->body.len, get_from( msg )->body.s ); + return (-1); + } + + return (0); } diff --git a/modules/sca/sca_reply.h b/modules/sca/sca_reply.h index 15785bfa256..c97da7c9ee0 100644 --- a/modules/sca/sca_reply.h +++ b/modules/sca/sca_reply.h @@ -26,6 +26,6 @@ #define SCA_REPLY_ERROR( mod, scode, smsg, sreply ) \ sca_reply((mod), (scode), (smsg), NULL, (sreply)) -int sca_reply( sca_mod *, int, char *, str *, sip_msg_t * ); +int sca_reply(sca_mod *, int, char *, str *, sip_msg_t *); #endif /* SCA_REPLY_H */ diff --git a/modules/sca/sca_rpc.c b/modules/sca/sca_rpc.c index 5e6873b48c9..39a427d74aa 100644 --- a/modules/sca/sca_rpc.c +++ b/modules/sca/sca_rpc.c @@ -35,493 +35,438 @@ #include "sca_subscribe.h" const char *sca_rpc_show_all_subscriptions_doc[] = { - "Show all shared call appearance subscriptions", - NULL -}; + "Show all shared call appearance subscriptions", NULL }; const char *sca_rpc_subscription_count_doc[] = { - "Show count of call-info or line-seize subscriptions", - NULL -}; + "Show count of call-info or line-seize subscriptions", NULL }; const char *sca_rpc_show_subscription_doc[] = { - "Show details of a single shared call appearance subscription", - NULL -}; + "Show details of a single shared call appearance subscription", NULL }; const char *sca_rpc_show_subscribers_doc[] = { - "Show contact URIs for all call-info subscribers", - NULL -}; + "Show contact URIs for all call-info subscribers", NULL }; const char *sca_rpc_deactivate_all_subscriptions_doc[] = { - "Send NOTIFYs with Subscription-State: " - "terminated;reason=deactivated to all subscribers", - NULL -}; + "Send NOTIFYs with Subscription-State: " + "terminated;reason=deactivated to all subscribers", NULL }; const char *sca_rpc_deactivate_subscription_doc[] = { - "Send NOTIFY with Subscription-State: " - "terminated;reason=deactivated to a single subscriber", - NULL -}; + "Send NOTIFY with Subscription-State: " + "terminated;reason=deactivated to a single subscriber", NULL }; const char *sca_rpc_show_all_appearances_doc[] = { - "Show appearance state for all SCA accounts of record (AoR)", - NULL -}; + "Show appearance state for all SCA accounts of record (AoR)", NULL }; const char *sca_rpc_show_appearance_doc[] = { - "Show appearance state for a single SCA account of record (AoR)", - NULL -}; + "Show appearance state for a single SCA account of record (AoR)", NULL }; const char *sca_rpc_seize_appearance_doc[] = { - "Seize an appearance on an SCA line", - NULL -}; + "Seize an appearance on an SCA line", NULL }; const char *sca_rpc_update_appearance_doc[] = { - "Update the state of a seized appearance on an SCA line", - NULL -}; + "Update the state of a seized appearance on an SCA line", NULL }; const char *sca_rpc_release_appearance_doc[] = { - "Release a seized or active SCA appearance", - NULL -}; - - void -sca_rpc_show_all_subscriptions( rpc_t *rpc, void *ctx ) -{ - sca_hash_table *ht; - sca_hash_entry *ent; - sca_subscription *sub; - sip_uri_t aor_uri, sub_uri; - str sub_state = STR_NULL; - time_t now; - int i; - int rc = 0; - - if (( ht = sca->subscriptions ) == NULL ) { - rpc->fault( ctx, 500, "Empty subscription table!" ); - return; - } - - now = time( NULL ); - - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); - - for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent->next ) { - sub = (sca_subscription *)ent->value; - sca_subscription_state_to_str( sub->state, &sub_state ); - - rc = parse_uri( sub->target_aor.s, sub->target_aor.len, &aor_uri ); - if ( rc >= 0 ) { - rc = parse_uri( sub->subscriber.s, sub->subscriber.len, - &sub_uri ); - } - if ( rc >= 0 ) { - rc = rpc->rpl_printf( ctx, "%.*s %.*s%s%.*s %s %ld %.*s", - STR_FMT( &aor_uri.user ), - STR_FMT( &sub_uri.host ), - (sub_uri.port.len ? ":" : "" ), - STR_FMT( &sub_uri.port ), - sca_event_name_from_type( sub->event ), - (long)(sub->expires - now), - STR_FMT( &sub_state )); - } else { - LM_ERR( "sca_rpc_show_all_subscriptions: parse_uri %.*s " - "failed, dumping unparsed info", - STR_FMT( &sub->target_aor )); - rc = rpc->rpl_printf( ctx, "%.*s %.*s %s %ld %.*s", - STR_FMT( &sub->target_aor ), - STR_FMT( &sub->subscriber ), - sca_event_name_from_type( sub->event ), - (long)sub->expires, - STR_FMT( &sub_state )); - } - - if ( rc < 0 ) { - /* make sure we unlock below */ - break; - } - } - - sca_hash_table_unlock_index( ht, i ); - - if ( rc < 0 ) { - return; - } - } + "Release a seized or active SCA appearance", NULL }; + +void sca_rpc_show_all_subscriptions(rpc_t *rpc, void *ctx) { + sca_hash_table *ht; + sca_hash_entry *ent; + sca_subscription *sub; + sip_uri_t aor_uri, sub_uri; + str sub_state = STR_NULL; + time_t now; + int i; + int rc = 0; + + if ((ht = sca->subscriptions) == NULL) { + rpc->fault(ctx, 500, "Empty subscription table!"); + return; + } + + now = time(NULL); + + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); + + for (ent = ht->slots[i].entries; ent != NULL; ent = ent->next) { + sub = (sca_subscription *) ent->value; + sca_subscription_state_to_str(sub->state, &sub_state); + + rc = parse_uri(sub->target_aor.s, sub->target_aor.len, &aor_uri); + if (rc >= 0) { + rc = parse_uri(sub->subscriber.s, sub->subscriber.len, + &sub_uri); + } + if (rc >= 0) { + rc = rpc->rpl_printf(ctx, "%.*s %.*s%s%.*s %s %ld %.*s", + STR_FMT(&aor_uri.user), STR_FMT(&sub_uri.host), + (sub_uri.port.len ? ":" : ""), STR_FMT(&sub_uri.port), + sca_event_name_from_type(sub->event), + (long) (sub->expires - now), STR_FMT(&sub_state)); + } else { + LM_ERR( "sca_rpc_show_all_subscriptions: parse_uri %.*s " + "failed, dumping unparsed info\n", + STR_FMT( &sub->target_aor )); + rc = rpc->rpl_printf(ctx, "%.*s %.*s %s %ld %.*s", + STR_FMT(&sub->target_aor), STR_FMT(&sub->subscriber), + sca_event_name_from_type(sub->event), + (long) sub->expires, STR_FMT(&sub_state)); + } + + if (rc < 0) { + /* make sure we unlock below */ + break; + } + } + + sca_hash_table_unlock_index(ht, i); + + if (rc < 0) { + return; + } + } } - void -sca_rpc_subscription_count( rpc_t *rpc, void *ctx ) -{ - sca_hash_table *ht; - sca_hash_entry *ent; - sca_subscription *sub; - str event_name = STR_NULL; - char *usage = "usage: sca.subscription_count " - "{ call-info | line-seize }"; - unsigned long sub_count = 0; - int i; - int event_type; - - if (( ht = sca->subscriptions ) == NULL ) { - rpc->fault( ctx, 500, "Empty subscription table!" ); - return; - } +void sca_rpc_subscription_count(rpc_t *rpc, void *ctx) { + sca_hash_table *ht; + sca_hash_entry *ent; + sca_subscription *sub; + str event_name = STR_NULL; + char *usage = "usage: sca.subscription_count " + "{ call-info | line-seize }"; + unsigned long sub_count = 0; + int i; + int event_type; + + if ((ht = sca->subscriptions) == NULL) { + rpc->fault(ctx, 500, "Empty subscription table!"); + return; + } - /* AoR is required */ - if ( rpc->scan( ctx, "S", &event_name ) != 1 ) { - rpc->fault( ctx, 500, usage ); - return; - } + /* AoR is required */ + if (rpc->scan(ctx, "S", &event_name) != 1) { + rpc->fault(ctx, 500, usage); + return; + } - event_type = sca_event_from_str( &event_name ); - if ( event_type == SCA_EVENT_TYPE_UNKNOWN ) { - rpc->fault( ctx, 500, usage ); - return; - } + event_type = sca_event_from_str(&event_name); + if (event_type == SCA_EVENT_TYPE_UNKNOWN) { + rpc->fault(ctx, 500, usage); + return; + } - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); - for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent->next ) { - sub = (sca_subscription *)ent->value; + for (ent = ht->slots[i].entries; ent != NULL; ent = ent->next) { + sub = (sca_subscription *) ent->value; - if ( event_type == sub->event ) { - sub_count++; - } + if (event_type == sub->event) { + sub_count++; + } + } + sca_hash_table_unlock_index(ht, i); } - sca_hash_table_unlock_index( ht, i ); - } - rpc->rpl_printf( ctx, "%ld %.*s", sub_count, STR_FMT( &event_name )); + rpc->rpl_printf(ctx, "%ld %.*s", sub_count, STR_FMT(&event_name)); } - void -sca_rpc_deactivate_all_subscriptions( rpc_t *rpc, void *ctx ) -{ - sca_hash_table *ht; - sca_hash_entry *ent; - sca_subscription *sub; - int i; - int rc = 0; - - if (( ht = sca->subscriptions ) == NULL ) { - rpc->fault( ctx, 500, "Empty subscription table!" ); - } - - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); - - for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent->next ) { - sub = (sca_subscription *)ent->value; - sub->state = SCA_SUBSCRIPTION_STATE_TERMINATED_DEACTIVATED; - sub->expires = 0; - sub->dialog.notify_cseq += 1; - - rpc->rpl_printf( ctx, "Deactivating %s subscription from %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - if ( rc < 0 ) { - /* make sure we unlock below */ - break; - } - - rc = sca_notify_subscriber( sca, sub, - SCA_CALL_INFO_APPEARANCE_INDEX_ANY ); - if ( rc < 0 ) { - /* make sure we unlock below */ - break; - } - } - - sca_hash_table_unlock_index( ht, i ); - - if ( rc < 0 ) { - return; - } - } -} +void sca_rpc_deactivate_all_subscriptions(rpc_t *rpc, void *ctx) { + sca_hash_table *ht; + sca_hash_entry *ent; + sca_subscription *sub; + int i; + int rc = 0; - void -sca_rpc_deactivate_subscription( rpc_t *rpc, void *ctx ) -{ - rpc->fault( ctx, 600, "Not implemented" ); -} + if ((ht = sca->subscriptions) == NULL) { + rpc->fault(ctx, 500, "Empty subscription table!"); + } - void -sca_rpc_show_subscription( rpc_t *rpc, void *ctx ) -{ - sca_hash_table *ht = NULL; - sca_hash_entry *ent; - sca_subscription *sub; - str sub_key = STR_NULL; - str aor = STR_NULL; - str contact = STR_NULL; - str event_name = STR_NULL; - int event_type; - int idx = -1; - int rc = 0, opt_rc; - char keybuf[ 1024 ]; - char *usage = "usage: sca.show_subscription sip:user@domain " - "{ call-info | line-seize } [sip:user@IP]"; - char *err_msg = NULL; - int err_code = 0; - - /* AoR is required */ - if ( rpc->scan( ctx, "SS", &aor, &event_name ) != 2 ) { - rpc->fault( ctx, 500, usage ); - } - - event_type = sca_event_from_str( &event_name ); - if ( event_type == SCA_EVENT_TYPE_UNKNOWN ) { - err_code = 500; - err_msg = usage; - goto done; - } - - if (( ht = sca->subscriptions ) == NULL ) { - rpc->fault( ctx, 500, "Empty subscription table!" ); - return; - } + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); + + for (ent = ht->slots[i].entries; ent != NULL; ent = ent->next) { + sub = (sca_subscription *) ent->value; + sub->state = SCA_SUBSCRIPTION_STATE_TERMINATED_DEACTIVATED; + sub->expires = 0; + sub->dialog.notify_cseq += 1; + + rpc->rpl_printf(ctx, "Deactivating %s subscription from %.*s", + sca_event_name_from_type(sub->event), + STR_FMT(&sub->subscriber)); + if (rc < 0) { + /* make sure we unlock below */ + break; + } + + rc = sca_notify_subscriber(sca, sub, + SCA_CALL_INFO_APPEARANCE_INDEX_ANY); + if (rc < 0) { + /* make sure we unlock below */ + break; + } + } - sub_key.s = keybuf; - if ( aor.len + event_name.len >= sizeof( keybuf )) { - rpc->fault( ctx, 500, "AoR length + event name length: too long" ); - } - SCA_STR_COPY( &sub_key, &aor ); - SCA_STR_APPEND( &sub_key, &event_name ); + sca_hash_table_unlock_index(ht, i); - idx = sca_hash_table_index_for_key( ht, &sub_key ); - sca_hash_table_lock_index( ht, idx ); + if (rc < 0) { + return; + } + } +} - /* Contact is optional */ - opt_rc = rpc->scan( ctx, "*S", &contact ); +void sca_rpc_deactivate_subscription(rpc_t *rpc, void *ctx) { + rpc->fault(ctx, 600, "Not implemented"); +} - for ( ent = ht->slots[ idx ].entries; ent != NULL; ent = ent->next ) { - sub = (sca_subscription *)ent->value; - if ( ent->compare( &aor, &sub->target_aor ) != 0 ) { - continue; +void sca_rpc_show_subscription(rpc_t *rpc, void *ctx) { + sca_hash_table *ht = NULL; + sca_hash_entry *ent; + sca_subscription *sub; + str sub_key = STR_NULL; + str aor = STR_NULL; + str contact = STR_NULL; + str event_name = STR_NULL; + int event_type; + int idx = -1; + int rc = 0, + opt_rc; + char keybuf[1024]; + char *usage = "usage: sca.show_subscription sip:user@domain " + "{ call-info | line-seize } [sip:user@IP]"; + char *err_msg = NULL; + int err_code = 0; + + /* AoR is required */ + if (rpc->scan(ctx, "SS", &aor, &event_name) != 2) { + rpc->fault(ctx, 500, usage); } - if ( opt_rc == 1 ) { - if ( !SCA_STR_EQ( &contact, &sub->subscriber )) { - continue; - } + event_type = sca_event_from_str(&event_name); + if (event_type == SCA_EVENT_TYPE_UNKNOWN) { + err_code = 500; + err_msg = usage; + goto done; } - rc = rpc->rpl_printf( ctx, "%.*s %s %.*s %d", - STR_FMT( &sub->target_aor ), - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber ), - sub->expires ); + if ((ht = sca->subscriptions) == NULL) { + rpc->fault(ctx, 500, "Empty subscription table!"); + return; + } - if ( rc < 0 ) { - /* make sure we unlock below */ - break; + sub_key.s = keybuf; + if (aor.len + event_name.len >= sizeof(keybuf)) { + rpc->fault(ctx, 500, "AoR length + event name length: too long"); } - } + SCA_STR_COPY(&sub_key, &aor); + SCA_STR_APPEND(&sub_key, &event_name); -done: - if ( ht && idx >= 0 ) { - sca_hash_table_unlock_index( ht, idx ); - } + idx = sca_hash_table_index_for_key(ht, &sub_key); + sca_hash_table_lock_index(ht, idx); - if ( err_code != 0 ) { - rpc->fault( ctx, err_code, err_msg ); - } -} + /* Contact is optional */ + opt_rc = rpc->scan(ctx, "*S", &contact); - void -sca_rpc_show_subscribers( rpc_t *rpc, void *ctx ) -{ - rpc->fault( ctx, 600, "Not yet implemented" ); -} + for (ent = ht->slots[idx].entries; ent != NULL; ent = ent->next) { + sub = (sca_subscription *) ent->value; + if (ent->compare(&aor, &sub->target_aor) != 0) { + continue; + } - void -sca_rpc_show_all_appearances( rpc_t *rpc, void *ctx ) -{ - sca_hash_table *ht; - sca_hash_entry *ent; - sca_appearance_list *app_list; - sca_appearance *app; - str state_str = STR_NULL; - int i; - int rc = 0; - - if (( ht = sca->appearances ) == NULL ) { - return; - } - - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); - - for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent->next ) { - app_list = (sca_appearance_list *)ent->value; - for ( app = app_list->appearances; app != NULL; app = app->next ) { - sca_appearance_state_to_str( app->state, &state_str ); - rc = rpc->rpl_printf( ctx, "%.*s %d %.*s %ld %.*s %.*s " - "%.*s %.*s %.*s", - STR_FMT( &app_list->aor ), - app->index, - STR_FMT( &state_str ), - (long)app->times.mtime, - STR_FMT( &app->owner ), - STR_FMT( &app->callee ), - STR_FMT( &app->dialog.call_id ), - STR_FMT( &app->dialog.from_tag ), - STR_FMT( &app->dialog.to_tag )); - - if ( rc < 0 ) { - /* make sure we unlock below */ - goto error; + if (opt_rc == 1) { + if (!SCA_STR_EQ(&contact, &sub->subscriber)) { + continue; + } } - } - } - sca_hash_table_unlock_index( ht, i ); - } + rc = rpc->rpl_printf(ctx, "%.*s %s %.*s %d", STR_FMT(&sub->target_aor), + sca_event_name_from_type(sub->event), STR_FMT(&sub->subscriber), + sub->expires); - return; + if (rc < 0) { + /* make sure we unlock below */ + break; + } + } -error: - sca_hash_table_unlock_index( ht, i ); - return; + done: if (ht && idx >= 0) { + sca_hash_table_unlock_index(ht, idx); + } + + if (err_code != 0) { + rpc->fault(ctx, err_code, err_msg); + } } - void -sca_rpc_show_appearance( rpc_t *rpc, void *ctx ) -{ - rpc->fault( ctx, 600, "Not yet implemented" ); +void sca_rpc_show_subscribers(rpc_t *rpc, void *ctx) { + rpc->fault(ctx, 600, "Not yet implemented"); } - void -sca_rpc_seize_appearance( rpc_t *rpc, void *ctx ) -{ - str aor = STR_NULL; - str owner = STR_NULL; - int app_idx; - char *usage = "usage: sca.seize_appearance sip:user@domain"; - - /* AoR & Contact are required */ - if ( rpc->scan( ctx, "SS", &aor, &owner ) != 2 ) { - rpc->fault( ctx, 500, usage ); +void sca_rpc_show_all_appearances(rpc_t *rpc, void *ctx) { + sca_hash_table *ht; + sca_hash_entry *ent; + sca_appearance_list *app_list; + sca_appearance *app; + str state_str = STR_NULL; + int i; + int rc = 0; + + if ((ht = sca->appearances) == NULL) { + return; + } + + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); + + for (ent = ht->slots[i].entries; ent != NULL; ent = ent->next) { + app_list = (sca_appearance_list *) ent->value; + for (app = app_list->appearances; app != NULL; app = app->next) { + sca_appearance_state_to_str(app->state, &state_str); + rc = rpc->rpl_printf(ctx, "%.*s %d %.*s %ld %.*s %.*s " + "%.*s %.*s %.*s", STR_FMT(&app_list->aor), app->index, + STR_FMT(&state_str), (long) app->times.mtime, + STR_FMT(&app->owner), STR_FMT(&app->callee), + STR_FMT(&app->dialog.call_id), + STR_FMT(&app->dialog.from_tag), + STR_FMT(&app->dialog.to_tag)); + + if (rc < 0) { + /* make sure we unlock below */ + goto error; + } + } + } + + sca_hash_table_unlock_index(ht, i); + } + return; - } - app_idx = sca_appearance_seize_next_available_index( sca, &aor, &owner ); - if ( app_idx < 0 ) { - rpc->fault( ctx, 500, "Failed to seize line" ); + error: + sca_hash_table_unlock_index(ht, i); return; - } +} + +void sca_rpc_show_appearance(rpc_t *rpc, void *ctx) { + rpc->fault(ctx, 600, "Not yet implemented"); +} + +void sca_rpc_seize_appearance(rpc_t *rpc, void *ctx) { + str aor = STR_NULL; + str owner = STR_NULL; + int app_idx; + char *usage = "usage: sca.seize_appearance sip:user@domain"; + + /* AoR & Contact are required */ + if (rpc->scan(ctx, "SS", &aor, &owner) != 2) { + rpc->fault(ctx, 500, usage); + return; + } + + app_idx = sca_appearance_seize_next_available_index(sca, &aor, &owner); + if (app_idx < 0) { + rpc->fault(ctx, 500, "Failed to seize line"); + return; + } - rpc->rpl_printf( ctx, "Seized %.*s appearance-index %d for %.*s", - STR_FMT( &aor ), app_idx, STR_FMT( &owner )); + rpc->rpl_printf(ctx, "Seized %.*s appearance-index %d for %.*s", + STR_FMT(&aor), app_idx, STR_FMT(&owner)); - if ( sca_notify_call_info_subscribers( sca, &aor ) < 0 ) { - rpc->fault( ctx, 500, "Failed to NOTIFY subscribers to %.*s", - STR_FMT( &aor )); - } + if (sca_notify_call_info_subscribers(sca, &aor) < 0) { + rpc->fault(ctx, 500, "Failed to NOTIFY subscribers to %.*s", + STR_FMT(&aor)); + } } - void -sca_rpc_update_appearance( rpc_t *rpc, void *ctx ) -{ - str aor = STR_NULL; - str app_state_str = STR_NULL; - str app_uri = STR_NULL, *app_uri_p = NULL; - int app_idx; - int app_state; - int rc; - char *usage = "Usage: sca.update_appearance " - "sip:user@domain appearance-index " - "appearance-state [appearance-uri]"; - - if ( rpc->scan( ctx, "SdS", &aor, &app_idx, &app_state_str ) < 3 ) { - rpc->fault( ctx, 500, "%s", usage ); - return; - } - if ( rpc->scan( ctx, "*S", &app_uri ) == 1 ) { - app_uri_p = &app_uri; - } - - app_state = sca_appearance_state_from_str( &app_state_str ); - if ( app_state == SCA_APPEARANCE_STATE_UNKNOWN ) { - rpc->fault( ctx, 500, "%.*s: invalid state", STR_FMT( &app_state_str )); - return; - } +void sca_rpc_update_appearance(rpc_t *rpc, void *ctx) { + str aor = STR_NULL; + str app_state_str = STR_NULL; + str app_uri = STR_NULL, + *app_uri_p = NULL; + int app_idx; + int app_state; + int rc; + char *usage = "Usage: sca.update_appearance " + "sip:user@domain appearance-index " + "appearance-state [appearance-uri]"; + + if (rpc->scan(ctx, "SdS", &aor, &app_idx, &app_state_str) < 3) { + rpc->fault(ctx, 500, "%s", usage); + return; + } + if (rpc->scan(ctx, "*S", &app_uri) == 1) { + app_uri_p = &app_uri; + } - rc = sca_appearance_update_index( sca, &aor, app_idx, - app_state, NULL, app_uri_p, NULL ); - if ( rc != SCA_APPEARANCE_OK ) { - rpc->fault( ctx, 500, "Failed to update %.*s appearance-index %d", - STR_FMT( &aor ), app_idx ); - return; - } + app_state = sca_appearance_state_from_str(&app_state_str); + if (app_state == SCA_APPEARANCE_STATE_UNKNOWN) { + rpc->fault(ctx, 500, "%.*s: invalid state", STR_FMT(&app_state_str)); + return; + } + + rc = sca_appearance_update_index(sca, &aor, app_idx, app_state, NULL, + app_uri_p, NULL); + if (rc != SCA_APPEARANCE_OK) { + rpc->fault(ctx, 500, "Failed to update %.*s appearance-index %d", + STR_FMT(&aor), app_idx); + return; + } - if ( sca_notify_call_info_subscribers( sca, &aor ) < 0 ) { - rpc->fault( ctx, 500, "Failed to NOTIFY subscribers to %.*s", - STR_FMT( &aor )); - } + if (sca_notify_call_info_subscribers(sca, &aor) < 0) { + rpc->fault(ctx, 500, "Failed to NOTIFY subscribers to %.*s", + STR_FMT(&aor)); + } } - void -sca_rpc_release_appearance( rpc_t *rpc, void *ctx ) -{ - sca_hash_table *ht = NULL; - sca_hash_entry *ent; - sca_appearance_list *app_list = NULL; - sca_appearance *app = NULL; - str aor = STR_NULL; - int idx = -1; - int app_idx; - char *usage = "usage: sca.release_appearance user@domain " - "appearance-index"; - char *err_msg = NULL; - int err_code = 0; - - /* AoR & appearance-index are required */ - if ( rpc->scan( ctx, "Sd", &aor, &app_idx ) != 2 ) { - rpc->fault( ctx, 500, usage ); - return; - } +void sca_rpc_release_appearance(rpc_t *rpc, void *ctx) { + sca_hash_table *ht = NULL; + sca_hash_entry *ent; + sca_appearance_list *app_list = NULL; + sca_appearance *app = NULL; + str aor = STR_NULL; + int idx = -1; + int app_idx; + char *usage = "usage: sca.release_appearance user@domain " + "appearance-index"; + char *err_msg = NULL; + int err_code = 0; + + /* AoR & appearance-index are required */ + if (rpc->scan(ctx, "Sd", &aor, &app_idx) != 2) { + rpc->fault(ctx, 500, usage); + return; + } - if (( ht = sca->appearances ) == NULL ) { - rpc->fault( ctx, 500, "No active appearances" ); - return; - } - - idx = sca_hash_table_index_for_key( ht, &aor ); - sca_hash_table_lock_index( ht, idx ); - - for ( ent = ht->slots[ idx ].entries; ent != NULL; ent = ent->next ) { - if ( ent->compare( &aor, ent->value ) == 0 ) { - app_list = (sca_appearance_list *)ent->value; - break; - } - } - if ( app_list == NULL ) { - rpc->fault( ctx, 500, "No appearances for %.*s", STR_FMT( &aor )); - goto done; - } - - app = sca_appearance_list_unlink_index( app_list, app_idx ); - if ( app == NULL ) { - rpc->fault( ctx, 500, "%.*s appearance index %d is not in use", - STR_FMT( &aor ), app_idx ); - goto done; - } - sca_appearance_free( app ); - -done: - if ( ht && idx >= 0 ) { - sca_hash_table_unlock_index( ht, idx ); - } - - if ( app != NULL ) { - if ( sca_notify_call_info_subscribers( sca, &aor ) < 0 ) { - rpc->fault( ctx, 500, "Failed to NOTIFY subscribers to %.*s", - STR_FMT( &aor )); - } - } - - if ( err_code != 0 ) { - rpc->fault( ctx, err_code, err_msg ); - } + if ((ht = sca->appearances) == NULL) { + rpc->fault(ctx, 500, "No active appearances"); + return; + } + + idx = sca_hash_table_index_for_key(ht, &aor); + sca_hash_table_lock_index(ht, idx); + + for (ent = ht->slots[idx].entries; ent != NULL; ent = ent->next) { + if (ent->compare(&aor, ent->value) == 0) { + app_list = (sca_appearance_list *) ent->value; + break; + } + } + if (app_list == NULL) { + rpc->fault(ctx, 500, "No appearances for %.*s", STR_FMT(&aor)); + goto done; + } + + app = sca_appearance_list_unlink_index(app_list, app_idx); + if (app == NULL) { + rpc->fault(ctx, 500, "%.*s appearance index %d is not in use", + STR_FMT(&aor), app_idx); + goto done; + } + sca_appearance_free(app); + + done: if (ht && idx >= 0) { + sca_hash_table_unlock_index(ht, idx); + } + + if (app != NULL) { + if (sca_notify_call_info_subscribers(sca, &aor) < 0) { + rpc->fault(ctx, 500, "Failed to NOTIFY subscribers to %.*s", + STR_FMT(&aor)); + } + } + + if (err_code != 0) { + rpc->fault(ctx, err_code, err_msg); + } } diff --git a/modules/sca/sca_rpc.h b/modules/sca/sca_rpc.h index ff3db39a5e9..a14f301c98c 100644 --- a/modules/sca/sca_rpc.h +++ b/modules/sca/sca_rpc.h @@ -25,28 +25,28 @@ #include "../../rpc.h" #include "../../rpc_lookup.h" -extern const char *sca_rpc_show_all_subscriptions_doc[]; -extern const char *sca_rpc_subscription_count_doc[]; -extern const char *sca_rpc_show_subscription_doc[]; -extern const char *sca_rpc_show_subscribers_doc[]; -extern const char *sca_rpc_deactivate_all_subscriptions_doc[]; -extern const char *sca_rpc_deactivate_subscription_doc[]; -extern const char *sca_rpc_show_all_appearances_doc[]; -extern const char *sca_rpc_show_appearance_doc[]; -extern const char *sca_rpc_seize_appearance_doc[]; -extern const char *sca_rpc_update_appearance_doc[]; -extern const char *sca_rpc_release_appearance_doc[]; +extern const char *sca_rpc_show_all_subscriptions_doc[]; +extern const char *sca_rpc_subscription_count_doc[]; +extern const char *sca_rpc_show_subscription_doc[]; +extern const char *sca_rpc_show_subscribers_doc[]; +extern const char *sca_rpc_deactivate_all_subscriptions_doc[]; +extern const char *sca_rpc_deactivate_subscription_doc[]; +extern const char *sca_rpc_show_all_appearances_doc[]; +extern const char *sca_rpc_show_appearance_doc[]; +extern const char *sca_rpc_seize_appearance_doc[]; +extern const char *sca_rpc_update_appearance_doc[]; +extern const char *sca_rpc_release_appearance_doc[]; -void sca_rpc_show_all_subscriptions( rpc_t *, void * ); -void sca_rpc_subscription_count( rpc_t *, void * ); -void sca_rpc_show_subscription( rpc_t *, void * ); -void sca_rpc_show_subscribers( rpc_t *, void * ); -void sca_rpc_deactivate_all_subscriptions( rpc_t *, void * ); -void sca_rpc_deactivate_subscription( rpc_t *, void * ); -void sca_rpc_show_all_appearances( rpc_t *, void * ); -void sca_rpc_show_appearance( rpc_t *, void * ); -void sca_rpc_seize_appearance( rpc_t *, void * ); -void sca_rpc_update_appearance( rpc_t *, void * ); -void sca_rpc_release_appearance( rpc_t *, void * ); +void sca_rpc_show_all_subscriptions(rpc_t *, void *); +void sca_rpc_subscription_count(rpc_t *, void *); +void sca_rpc_show_subscription(rpc_t *, void *); +void sca_rpc_show_subscribers(rpc_t *, void *); +void sca_rpc_deactivate_all_subscriptions(rpc_t *, void *); +void sca_rpc_deactivate_subscription(rpc_t *, void *); +void sca_rpc_show_all_appearances(rpc_t *, void *); +void sca_rpc_show_appearance(rpc_t *, void *); +void sca_rpc_seize_appearance(rpc_t *, void *); +void sca_rpc_update_appearance(rpc_t *, void *); +void sca_rpc_release_appearance(rpc_t *, void *); #endif /* SCA_RPC_H */ diff --git a/modules/sca/sca_subscribe.c b/modules/sca/sca_subscribe.c index 9dfb5ce719c..c7177c6357b 100644 --- a/modules/sca/sca_subscribe.c +++ b/modules/sca/sca_subscribe.c @@ -33,1396 +33,1340 @@ #include "../../modules/tm/tm_load.h" -extern int errno; +extern int errno; - -static int sca_subscription_copy_subscription_key( sca_subscription *, str * ); -int sca_subscription_save_unsafe( sca_mod *, sca_subscription *, - int, int ); -void sca_subscription_print( void * ); +static int sca_subscription_copy_subscription_key(sca_subscription *, str *); +int sca_subscription_save_unsafe(sca_mod *, sca_subscription *, int, int); +void sca_subscription_print(void *); const str SCA_METHOD_SUBSCRIBE = STR_STATIC_INIT( "SUBSCRIBE" ); struct sca_sub_state_table { - int state; - char *state_name; + int state; + char *state_name; } state_table[] = { - { SCA_SUBSCRIPTION_STATE_ACTIVE, "active" }, - { SCA_SUBSCRIPTION_STATE_PENDING, "pending" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED, "terminated" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED_DEACTIVATED, "terminated;reason=deactivated" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED_GIVEUP, "terminated;reason=giveup" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, "terminated;reason=noresource" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED_PROBATION, "terminated;reason=probation" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED_REJECTED, "terminated;reason=rejected" }, - { SCA_SUBSCRIPTION_STATE_TERMINATED_TIMEOUT, "terminated;reason=timeout" }, - - { -1, NULL }, + {SCA_SUBSCRIPTION_STATE_ACTIVE, "active" }, + {SCA_SUBSCRIPTION_STATE_PENDING, "pending" }, + {SCA_SUBSCRIPTION_STATE_TERMINATED, "terminated" }, + {SCA_SUBSCRIPTION_STATE_TERMINATED_DEACTIVATED, + "terminated;reason=deactivated" }, + {SCA_SUBSCRIPTION_STATE_TERMINATED_GIVEUP, + "terminated;reason=giveup" }, + { SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, + "terminated;reason=noresource" }, + {SCA_SUBSCRIPTION_STATE_TERMINATED_PROBATION, + "terminated;reason=probation" },\ + {SCA_SUBSCRIPTION_STATE_TERMINATED_REJECTED, + "terminated;reason=rejected" }, + {SCA_SUBSCRIPTION_STATE_TERMINATED_TIMEOUT, + "terminated;reason=timeout" }, + { -1, NULL }, }; - void -sca_subscription_state_to_str( int state, str *state_str_out ) -{ - assert( state >= 0 ); - assert( state < ( sizeof( state_table ) / sizeof( state_table[ 0 ] ))); - assert( state_str_out != NULL ); +void sca_subscription_state_to_str(int state, str *state_str_out) { + assert(state >= 0); + assert(state < (sizeof(state_table) / sizeof(state_table[0]))); + assert(state_str_out != NULL); - state_str_out->len = strlen( state_table[ state ].state_name ); - state_str_out->s = state_table[ state ].state_name; + state_str_out->len = strlen(state_table[state].state_name); + state_str_out->s = state_table[state].state_name; } - void -sca_subscription_purge_expired( unsigned int ticks, void *param ) -{ - sca_mod *scam = (sca_mod *)param; - sca_hash_table *ht; - sca_hash_entry *ent, *ent_tmp; - sca_subscription *sub; - time_t now = time( NULL ); - int state; - int i; - - assert( scam != NULL ); - assert( scam->subscriptions != NULL ); - - LM_INFO( "SCA: purging expired subscriptions" ); - - ht = scam->subscriptions; - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); - - for ( ent = ht->slots[ i ].entries; ent != NULL; ent = ent_tmp ) { - ent_tmp = ent->next; - - sub = (sca_subscription *)ent->value; - if ( sub == NULL || sub->expires > now ) { - continue; - } - - if ( !SCA_SUBSCRIPTION_IS_TERMINATED( sub )) { - sub->state = SCA_SUBSCRIPTION_STATE_TERMINATED_TIMEOUT; - sub->expires = 0; - sub->dialog.notify_cseq += 1; - - if ( sca_notify_subscriber( scam, sub, sub->index ) < 0 ) { - LM_ERR( "Failed to send subscription expired " - "NOTIFY %s subscriber %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - - /* remove from subscribers list anyway */ - } - if ( sub->event == SCA_EVENT_TYPE_LINE_SEIZE ) { - /* only notify if the line is just seized */ - state = sca_appearance_state_for_index( sca, - &sub->target_aor, sub->index ); - if ( state == SCA_APPEARANCE_STATE_SEIZED ) { - if ( sca_appearance_release_index( sca, - &sub->target_aor, sub->index ) < 0 ) { - LM_ERR( "Failed to release seized %.*s " - "appearance-index %d", - STR_FMT( &sub->target_aor ), sub->index ); +void sca_subscription_purge_expired(unsigned int ticks, void *param) { + sca_mod *scam = (sca_mod *) param; + sca_hash_table *ht; + sca_hash_entry *ent, *ent_tmp; + sca_subscription *sub; + time_t now = time(NULL); + int state; + int i; + + assert(scam != NULL); + assert(scam->subscriptions != NULL); + + LM_INFO( "SCA: purging expired subscriptions\n" ); + + ht = scam->subscriptions; + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); + + for (ent = ht->slots[i].entries; ent != NULL; ent = ent_tmp) { + ent_tmp = ent->next; + + sub = (sca_subscription *) ent->value; + if (sub == NULL || sub->expires > now) { + continue; } - if ( sca_notify_call_info_subscribers( sca, - &sub->target_aor ) < 0 ) { - LM_ERR( "SCA %s NOTIFY to all %.*s " - "subscribers failed", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->target_aor )); - /* - * fall through anyway. the state should propagate - * to subscribers when they renew call-info. - */ + if (!SCA_SUBSCRIPTION_IS_TERMINATED(sub)) { + sub->state = SCA_SUBSCRIPTION_STATE_TERMINATED_TIMEOUT; + sub->expires = 0; + sub->dialog.notify_cseq += 1; + + if (sca_notify_subscriber(scam, sub, sub->index) < 0) { + LM_ERR( "Failed to send subscription expired " + "NOTIFY %s subscriber %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + + /* remove from subscribers list anyway */ + } + if (sub->event == SCA_EVENT_TYPE_LINE_SEIZE) { + /* only notify if the line is just seized */ + state = sca_appearance_state_for_index(sca, + &sub->target_aor, sub->index); + if (state == SCA_APPEARANCE_STATE_SEIZED) { + if (sca_appearance_release_index(sca, &sub->target_aor, + sub->index) < 0) { + LM_ERR( "Failed to release seized %.*s " + "appearance-index %d\n", + STR_FMT( &sub->target_aor ), sub->index ); + } + + if (sca_notify_call_info_subscribers(sca, + &sub->target_aor) < 0) { + LM_ERR( "SCA %s NOTIFY to all %.*s " + "subscribers failed\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->target_aor )); + /* + * fall through anyway. the state should propagate + * to subscribers when they renew call-info. + */ + } + } + } } - } - } - } - /* - * XXX should be in a separate subscription deletion routine. - * will need to detect whether subscriber has active appearances, - * send notifies to others in group if necessary. - */ + /* + * XXX should be in a separate subscription deletion routine. + * will need to detect whether subscriber has active appearances, + * send notifies to others in group if necessary. + */ - LM_INFO( "%s subscription from %.*s expired, deleting", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); + LM_INFO( "%s subscription from %.*s expired, deleting\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); - sca_hash_table_slot_unlink_entry_unsafe( &ht->slots[ i ], ent ); - sca_hash_entry_free( ent ); - } + sca_hash_table_slot_unlink_entry_unsafe(&ht->slots[i], ent); + sca_hash_entry_free(ent); + } - sca_hash_table_unlock_index( ht, i ); - } + sca_hash_table_unlock_index(ht, i); + } } - int -sca_subscription_from_db_row_values( db_val_t *values, sca_subscription *sub ) -{ - assert( values != NULL ); - assert( sub != NULL ); - - /* XXX condense to loop with preprocessor macros when there's time */ - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_SUBSCRIBER_COL, - values, &sub->subscriber ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_AOR_COL, - values, &sub->target_aor ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_EVENT_COL, values, - &sub->event ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_EXPIRES_COL, values, - &sub->expires ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_STATE_COL, values, - &sub->state ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_APP_IDX_COL, values, - &sub->index ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_CALL_ID_COL, values, - &sub->dialog.call_id ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_FROM_TAG_COL, values, - &sub->dialog.from_tag ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_TO_TAG_COL, values, - &sub->dialog.to_tag ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_RECORD_ROUTE_COL, - values, &sub->rr ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_NOTIFY_CSEQ_COL, - values, &sub->dialog.notify_cseq ); - sca_db_subscriptions_get_value_for_column( SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL, - values, &sub->dialog.subscribe_cseq ); - - return( 0 ); +int sca_subscription_from_db_row_values(db_val_t *values, sca_subscription *sub) { + assert(values != NULL); + assert(sub != NULL); + + /* XXX condense to loop with preprocessor macros when there's time */ + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_SUBSCRIBER_COL, + values, &sub->subscriber); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_AOR_COL, values, + &sub->target_aor); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_EVENT_COL, values, + &sub->event); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_EXPIRES_COL, values, + &sub->expires); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_STATE_COL, values, + &sub->state); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_APP_IDX_COL, values, + &sub->index); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_CALL_ID_COL, values, + &sub->dialog.call_id); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_FROM_TAG_COL, values, + &sub->dialog.from_tag); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_TO_TAG_COL, values, + &sub->dialog.to_tag); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_RECORD_ROUTE_COL, + values, &sub->rr); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_NOTIFY_CSEQ_COL, + values, &sub->dialog.notify_cseq); + sca_db_subscriptions_get_value_for_column(SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL, + values, &sub->dialog.subscribe_cseq); + + return (0); } - int -sca_subscription_to_db_row_values( sca_subscription *sub, db_val_t *values ) -{ - int notify_cseq, subscribe_cseq; - - assert( sub != NULL ); - assert( values != NULL ); - - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_SUBSCRIBER_COL, - values, &sub->subscriber ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_AOR_COL, - values, &sub->target_aor ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_EVENT_COL, values, - &sub->event ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_EXPIRES_COL, values, - &sub->expires ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_STATE_COL, values, - &sub->state ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_APP_IDX_COL, values, - &sub->index ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_CALL_ID_COL, values, - &sub->dialog.call_id ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_FROM_TAG_COL, values, - &sub->dialog.from_tag ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_TO_TAG_COL, values, - &sub->dialog.to_tag ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_RECORD_ROUTE_COL, - values, &sub->rr ); - - notify_cseq = sub->dialog.notify_cseq + 1; - subscribe_cseq = sub->dialog.subscribe_cseq + 1; - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_NOTIFY_CSEQ_COL, - values, ¬ify_cseq ); - sca_db_subscriptions_set_value_for_column( SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL, - values, &subscribe_cseq ); - - return( 0 ); +int sca_subscription_to_db_row_values(sca_subscription *sub, db_val_t *values) { + int notify_cseq, subscribe_cseq; + + assert(sub != NULL); + assert(values != NULL); + + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_SUBSCRIBER_COL, + values, &sub->subscriber); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_AOR_COL, values, + &sub->target_aor); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_EVENT_COL, values, + &sub->event); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_EXPIRES_COL, values, + &sub->expires); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_STATE_COL, values, + &sub->state); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_APP_IDX_COL, values, + &sub->index); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_CALL_ID_COL, values, + &sub->dialog.call_id); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_FROM_TAG_COL, values, + &sub->dialog.from_tag); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_TO_TAG_COL, values, + &sub->dialog.to_tag); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_RECORD_ROUTE_COL, + values, &sub->rr); + + notify_cseq = sub->dialog.notify_cseq + 1; + subscribe_cseq = sub->dialog.subscribe_cseq + 1; + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_NOTIFY_CSEQ_COL, + values, ¬ify_cseq); + sca_db_subscriptions_set_value_for_column(SCA_DB_SUBS_SUBSCRIBE_CSEQ_COL, + values, &subscribe_cseq); + + return (0); } - int -sca_subscriptions_restore_from_db( sca_mod *scam ) -{ - db1_con_t *db_con; - db_key_t result_columns[ SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS ]; - db1_res_t *result = NULL; - db_row_t *rows = NULL; - db_val_t *row_values = NULL; - sca_subscription sub; - str sub_key = STR_NULL; - str **column_names; - int num_rows; - int i; - int idx; - int rc = -1; - time_t now = time( NULL ); - - db_con = scam->db_api->init( scam->cfg->db_url ); - if ( db_con == NULL ) { - LM_ERR( "sca_subscriptions_restore_from_db: failed to connect " - "to DB %.*s", STR_FMT( scam->cfg->db_url )); - return( -1 ); - } - - scam->db_api->use_table( db_con, scam->cfg->subs_table ); - - column_names = sca_db_subscriptions_columns(); - if ( column_names == NULL ) { - LM_ERR( "sca_subscriptions_restore_from_db: failed to get " - "column names for SCA subscriptions table" ); - goto done; - } - - for ( i = 0; i < SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS; i++ ) { - result_columns[ i ] = column_names[ i ]; - } - - rc = db_fetch_query( scam->db_api, SCA_DB_DEFAULT_FETCH_ROW_COUNT, - db_con, NULL, NULL, NULL, result_columns, - 0, SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS, - 0, &result ); - switch ( rc ) { - default: - case -1: - LM_ERR( "sca_subscriptions_restore_from_db: query failed" ); - goto done; - - case 0: - LM_WARN( "sca_subscriptions_restore_from_db: DB module does " - "not support fetch, query returning all values..." ); - /* fall through */ - - case 1: - break; - } - - do { - rows = RES_ROWS( result ); - num_rows = RES_ROW_N( result ); - - for ( i = 0; i < num_rows; i++ ) { - memset( &sub, 0, sizeof( sca_subscription )); - - row_values = ROW_VALUES( rows + i ); - - sub.expires = row_values[ SCA_DB_SUBS_EXPIRES_COL ].val.time_val; - if ( sub.expires < now ) { - continue; - } - - if ( sca_subscription_from_db_row_values( row_values, &sub ) < 0 ) { - LM_ERR( "sca_subscriptions_restore_from_db: skipping bad result " - "at index %d", i ); - continue; - } - - if ( sca_subscription_copy_subscription_key( &sub, &sub_key ) < 0 ) { - LM_ERR( "sca_subscriptions_restore_from_db: failed to copy " - "subscription key %.*s%s", STR_FMT( &sub.subscriber ), - sca_event_name_from_type( sub.event )); - continue; - } - - idx = sca_hash_table_index_for_key( sca->subscriptions, &sub_key ); - pkg_free( sub_key.s ); - - sca_hash_table_lock_index( sca->subscriptions, idx ); - - if ( sca_subscription_save_unsafe( scam, &sub, idx, - SCA_SUBSCRIPTION_CREATE_OPT_RAW_EXPIRES ) < 0 ) { - LM_ERR( "sca_subscriptions_restore_from_db: failed to restore " - "%s subscription from %.*s to the hash table", - sca_event_name_from_type( sub.event ), - STR_FMT( &sub.subscriber )); - - /* fall through to unlock index */ - } - - sca_hash_table_unlock_index( sca->subscriptions, idx ); - } - } while ( db_fetch_next( scam->db_api, SCA_DB_DEFAULT_FETCH_ROW_COUNT, - db_con, &result ) == 1 && num_rows > 0 ); - - scam->db_api->free_result( db_con, result ); - - /* clear all records from table, let timer process repopulate it */ - if ( scam->db_api->delete( db_con, NULL, NULL, NULL, 0 ) < 0 ) { - LM_ERR( "sca_subscriptions_restore_from_db: failed to delete " - "records from table after restoring" ); - goto done; - } - - rc = 0; - -done: - scam->db_api->close( db_con ); - db_con = NULL; - - return( rc ); -} +int sca_subscriptions_restore_from_db(sca_mod *scam) { + db1_con_t *db_con; + db_key_t result_columns[SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS]; + db1_res_t *result = NULL; + db_row_t *rows = NULL; + db_val_t *row_values = NULL; + sca_subscription sub; + str sub_key = STR_NULL; + str **column_names; + int num_rows; + int i; + int idx; + int rc = -1; + time_t now = time(NULL); + + db_con = scam->db_api->init(scam->cfg->db_url); + if (db_con == NULL) { + LM_ERR( "sca_subscriptions_restore_from_db: failed to connect " + "to DB %.*s\n", STR_FMT( scam->cfg->db_url )); + return (-1); + } - static int -sca_subscription_db_update_subscriber( db1_con_t *db_con, - sca_subscription *sub ) -{ - db_key_t query_columns[ 1 ]; - db_val_t query_values[ 1 ]; - int query_column_idx = 0; - db_key_t update_columns[ 6 ]; - db_val_t update_values[ 6 ]; - int update_column_idx = 0; - - assert( db_con != NULL ); - assert( sub != NULL ); - - /* bind the lookup key and value */ - SCA_DB_BIND_STR_VALUE( sub->subscriber, &SCA_DB_SUBSCRIBER_COL_NAME, - query_columns, query_values, query_column_idx ); - - /* bind updated keys and values */ - SCA_DB_BIND_INT_VALUE( sub->expires, &SCA_DB_EXPIRES_COL_NAME, - update_columns, update_values, update_column_idx ); - SCA_DB_BIND_STR_VALUE( sub->dialog.call_id, &SCA_DB_CALL_ID_COL_NAME, - update_columns, update_values, update_column_idx ); - SCA_DB_BIND_STR_VALUE( sub->dialog.from_tag, &SCA_DB_FROM_TAG_COL_NAME, - update_columns, update_values, update_column_idx ); - SCA_DB_BIND_STR_VALUE( sub->dialog.to_tag, &SCA_DB_TO_TAG_COL_NAME, - update_columns, update_values, update_column_idx ); - SCA_DB_BIND_INT_VALUE( sub->dialog.notify_cseq + 1, - &SCA_DB_NOTIFY_CSEQ_COL_NAME, - update_columns, update_values, update_column_idx ); - SCA_DB_BIND_INT_VALUE( sub->dialog.subscribe_cseq + 1, - &SCA_DB_SUBSCRIBE_CSEQ_COL_NAME, - update_columns, update_values, update_column_idx ); - - - if ( sca->db_api->update( db_con, query_columns,NULL, query_values, - update_columns, update_values, - query_column_idx, update_column_idx ) < 0 ) { - LM_ERR( "sca_subscription_db_update_subscriber: failed to update " - "%s subscriber %.*s in DB", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - return( -1 ); - } - - /* no further DB updates until subscription is changed */ - sub->db_cmd_flag = SCA_DB_FLAG_NONE; - - return( 0 ); -} + scam->db_api->use_table(db_con, scam->cfg->subs_table); - static int -sca_subscription_db_insert_subscriber( db1_con_t *db_con, - sca_subscription *sub ) -{ - db_key_t insert_columns[ SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS ]; - db_val_t insert_values[ SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS ]; - str **column_names; - int i; - - assert( db_con != NULL ); - assert( sub != NULL ); - - column_names = sca_db_subscriptions_columns(); - if ( column_names == NULL ) { - LM_ERR( "sca_subscriptions_restore_from_db: failed to get " - "column names for SCA subscriptions table" ); - return( -1 ); - } - - for ( i = 0; i < SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS; i++ ) { - insert_columns[ i ] = column_names[ i ]; - } - - /* XXX array boundary checking */ - if ( sca_subscription_to_db_row_values( sub, insert_values ) != 0 ) { - LM_ERR( "sca_subscription_db_insert_subscriber: failed to set " - "DB row values for INSERT of %s subscriber %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - return( -1 ); - } - - if ( sca->db_api->insert( db_con, insert_columns, insert_values, - SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS ) < 0 ) { - LM_ERR( "sca_subscription_db_insert_subscriber: failed to insert " - "%s subscriber %.*s in DB subscription table", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - return( -1 ); - } - - /* no further DB actions needed until subscription is changed */ - sub->db_cmd_flag = SCA_DB_FLAG_NONE; - - return( 0 ); -} + column_names = sca_db_subscriptions_columns(); + if (column_names == NULL) { + LM_ERR( "sca_subscriptions_restore_from_db: failed to get " + "column names for SCA subscriptions table\n" ); + goto done; + } - int -sca_subscription_db_delete_expired( db1_con_t *db_con ) -{ - db_key_t delete_columns[ 1 ]; - db_val_t delete_values[ 1 ]; - db_op_t delete_ops[ 1 ]; - time_t now = time( NULL ); - int kv_count = 0; - - delete_columns[ 0 ] = (str *)&SCA_DB_EXPIRES_COL_NAME; - delete_ops[ 0 ] = OP_LT; - - SCA_DB_BIND_INT_VALUE( now, &SCA_DB_EXPIRES_COL_NAME, - delete_columns, delete_values, kv_count ); - - if ( sca->db_api->delete( db_con, delete_columns, delete_ops, - delete_values, kv_count ) < 0 ) { - LM_ERR( "sca_subscription_db_delete_expired: failed to delete " - "subscriptions expired before %ld", (long int)now ); - return( -1 ); - } - - return( 0 ); -} + for (i = 0; i < SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS; i++) { + result_columns[i] = column_names[i]; + } - int -sca_subscription_db_update( void ) -{ - db1_con_t *db_con = NULL; - sca_hash_table *ht; - sca_hash_entry *entry; - sca_subscription *sub; - int i; - int rc = -1; - time_t now = time( NULL ); - - db_con = sca_db_get_connection(); - if ( db_con == NULL ){ - LM_ERR( "sca_subscription_db_update: failed to connect to DB %.*s", - STR_FMT( sca->cfg->db_url )); - goto done; - } - if ( sca->db_api->use_table( db_con, sca->cfg->subs_table ) < 0 ) { - LM_ERR( "sca_subscription_db_update: failed to in-use table " - "for DB %.*s", STR_FMT( sca->cfg->db_url )); - goto done; - } - - ht = sca->subscriptions; - for ( i = 0; i < ht->size; i++ ) { - sca_hash_table_lock_index( ht, i ); - - for ( entry = ht->slots[ i ].entries; entry != NULL; - entry = entry->next ) { - sub = (sca_subscription *)entry->value; - - if ( sub == NULL || sub->expires < now ) { - continue; - } - - /* we only do call-info subscriptions for now */ - if ( sub->event != SCA_EVENT_TYPE_CALL_INFO ) { - continue; - } - - if ( SCA_SUBSCRIPTION_IS_TERMINATED( sub )) { - continue; - } - - if ( sub->db_cmd_flag == SCA_DB_FLAG_INSERT ) { - if ( sca_subscription_db_insert_subscriber( db_con, sub ) < 0) { - LM_ERR( "sca_subscription_db_update: failed to insert " - "%s subscriber %.*s into subscription DB", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); - } - } else if ( sub->db_cmd_flag == SCA_DB_FLAG_UPDATE ) { - if ( sca_subscription_db_update_subscriber( db_con, sub ) < 0) { - LM_ERR( "sca_subscription_db_update: failed to insert " - "%s subscriber %.*s into subscription DB", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); + rc = db_fetch_query(scam->db_api, SCA_DB_DEFAULT_FETCH_ROW_COUNT, db_con, + NULL, NULL, NULL, result_columns, 0, + SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS, 0, &result); + switch (rc) { + default: + case -1: + LM_ERR( "sca_subscriptions_restore_from_db: query failed\n" ); + goto done; + + case 0: + LM_WARN( "sca_subscriptions_restore_from_db: DB module does " + "not support fetch, query returning all values...\n" ); + /* fall through */ + + case 1: + break; + } + + do { + rows = RES_ROWS(result); + num_rows = RES_ROW_N(result); + + for (i = 0; i < num_rows; i++) { + memset(&sub, 0, sizeof(sca_subscription)); + + row_values = ROW_VALUES(rows + i); + + sub.expires = row_values[SCA_DB_SUBS_EXPIRES_COL].val.time_val; + if (sub.expires < now) { + continue; + } + + if (sca_subscription_from_db_row_values(row_values, &sub) < 0) { + LM_ERR( "sca_subscriptions_restore_from_db: skipping bad result " + "at index %d\n", i ); + continue; + } + + if (sca_subscription_copy_subscription_key(&sub, &sub_key) < 0) { + LM_ERR( "sca_subscriptions_restore_from_db: failed to copy " + "subscription key %.*s%s\n", STR_FMT( &sub.subscriber ), + sca_event_name_from_type( sub.event )); + continue; + } + + idx = sca_hash_table_index_for_key(sca->subscriptions, &sub_key); + pkg_free(sub_key.s); + + sca_hash_table_lock_index(sca->subscriptions, idx); + + if (sca_subscription_save_unsafe(scam, &sub, idx, + SCA_SUBSCRIPTION_CREATE_OPT_RAW_EXPIRES) < 0) { + LM_ERR( "sca_subscriptions_restore_from_db: failed to restore " + "%s subscription from %.*s to the hash table\n", + sca_event_name_from_type( sub.event ), + STR_FMT( &sub.subscriber )); + + /* fall through to unlock index */ + } + + sca_hash_table_unlock_index(sca->subscriptions, idx); } - } + } while (db_fetch_next(scam->db_api, SCA_DB_DEFAULT_FETCH_ROW_COUNT, db_con, + &result) == 1 && num_rows > 0); + + scam->db_api->free_result(db_con, result); + + /* clear all records from table, let timer process repopulate it */ + if (scam->db_api->delete(db_con, NULL, NULL, NULL, 0) < 0) { + LM_ERR( "sca_subscriptions_restore_from_db: failed to delete " + "records from table after restoring\n" ); + goto done; } - sca_hash_table_unlock_index( ht, i ); - } + rc = 0; - rc = sca_subscription_db_delete_expired( db_con ); + done: scam->db_api->close(db_con); + db_con = NULL; -done: - return( rc ); + return (rc); } - void -sca_subscription_db_update_timer( unsigned int ticks, void *param ) -{ - if ( sca_subscription_db_update() != 0 ) { - LM_ERR( "sca_subscription_db_update_timer: failed to update " - "subscriptions in DB %.*s", STR_FMT( sca->cfg->db_url )); - } -} +static int sca_subscription_db_update_subscriber(db1_con_t *db_con, + sca_subscription *sub) { + db_key_t query_columns[1]; + db_val_t query_values[1]; + int query_column_idx = 0; + db_key_t update_columns[6]; + db_val_t update_values[6]; + int update_column_idx = 0; + + assert(db_con != NULL); + assert(sub != NULL); + + /* bind the lookup key and value */ + SCA_DB_BIND_STR_VALUE(sub->subscriber, &SCA_DB_SUBSCRIBER_COL_NAME, + query_columns, query_values, query_column_idx); + + /* bind updated keys and values */ + SCA_DB_BIND_INT_VALUE(sub->expires, &SCA_DB_EXPIRES_COL_NAME, + update_columns, update_values, update_column_idx); + SCA_DB_BIND_STR_VALUE(sub->dialog.call_id, &SCA_DB_CALL_ID_COL_NAME, + update_columns, update_values, update_column_idx); + SCA_DB_BIND_STR_VALUE(sub->dialog.from_tag, &SCA_DB_FROM_TAG_COL_NAME, + update_columns, update_values, update_column_idx); + SCA_DB_BIND_STR_VALUE(sub->dialog.to_tag, &SCA_DB_TO_TAG_COL_NAME, + update_columns, update_values, update_column_idx); + SCA_DB_BIND_INT_VALUE(sub->dialog.notify_cseq + 1, + &SCA_DB_NOTIFY_CSEQ_COL_NAME, update_columns, update_values, + update_column_idx); + SCA_DB_BIND_INT_VALUE(sub->dialog.subscribe_cseq + 1, + &SCA_DB_SUBSCRIBE_CSEQ_COL_NAME, update_columns, update_values, + update_column_idx); + + if (sca->db_api->update(db_con, query_columns, NULL, query_values, + update_columns, update_values, query_column_idx, update_column_idx) + < 0) { + LM_ERR( "sca_subscription_db_update_subscriber: failed to update " + "%s subscriber %.*s in DB\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + return (-1); + } - int -sca_subscription_aor_has_subscribers( int event, str *aor ) -{ - sca_hash_slot *slot; - sca_hash_entry *e; - sca_subscription *sub; - str sub_key = STR_NULL; - char *event_name; - int len; - int subscribers = 0; - int slot_idx = -1; - - event_name = sca_event_name_from_type( event ); - len = aor->len + strlen( event_name ); - sub_key.s = (char *)pkg_malloc( len ); - if ( sub_key.s == NULL ) { - LM_ERR( "Failed to pkg_malloc key to look up %s " - "subscription for %.*s", event_name, STR_FMT( aor )); - return( -1 ); - } - SCA_STR_COPY( &sub_key, aor ); - SCA_STR_APPEND_CSTR( &sub_key, event_name ); - - slot_idx = sca_hash_table_index_for_key( sca->subscriptions, &sub_key ); - pkg_free( sub_key.s ); - sub_key.len = 0; - - slot = sca_hash_table_slot_for_index( sca->subscriptions, slot_idx ); - sca_hash_table_lock_index( sca->subscriptions, slot_idx ); - - for ( e = slot->entries; e != NULL; e = e->next ) { - sub = (sca_subscription *)e->value; - - if ( SCA_STR_EQ( &sub->target_aor, aor )) { - subscribers = 1; - break; - } - } - - sca_hash_table_unlock_index( sca->subscriptions, slot_idx ); - - return( subscribers ); + /* no further DB updates until subscription is changed */ + sub->db_cmd_flag = SCA_DB_FLAG_NONE; + + return (0); } - sca_subscription * -sca_subscription_create( str *aor, int event, str *subscriber, - unsigned int notify_cseq, unsigned int subscribe_cseq, int expire_delta, - str *call_id, str *from_tag, str *to_tag, str *rr, int opts ) -{ - sca_subscription *sub = NULL; - int len = 0; - - len += sizeof( sca_subscription ); - len += sizeof( char ) * ( aor->len + subscriber->len ); - if ( !SCA_STR_EMPTY( rr )) { - len += sizeof( char ) * rr->len; - } - - sub = (sca_subscription *)shm_malloc( len ); - if ( sub == NULL ) { - LM_ERR( "Failed to create %s subscription for %.*s: out of memory", - sca_event_name_from_type( event ), STR_FMT( subscriber )); - goto error; - } - memset( sub, 0, len ); - - sub->event = event; - sub->state = SCA_SUBSCRIPTION_STATE_ACTIVE; - sub->index = SCA_CALL_INFO_APPEARANCE_INDEX_ANY; - if ( opts & SCA_SUBSCRIPTION_CREATE_OPT_RAW_EXPIRES ) { - sub->expires = expire_delta; - } else { - sub->expires = time( NULL ) + expire_delta; - } - sub->dialog.notify_cseq = notify_cseq; - sub->dialog.subscribe_cseq = subscribe_cseq; - sub->db_cmd_flag = SCA_DB_FLAG_INSERT; - - len = sizeof( sca_subscription ); - - sub->subscriber.s = (char *)sub + len; - SCA_STR_COPY( &sub->subscriber, subscriber ); - len += subscriber->len; - - sub->target_aor.s = (char *)sub + len; - SCA_STR_COPY( &sub->target_aor, aor ); - len += aor->len; - - if ( !SCA_STR_EMPTY( rr )) { - sub->rr.s = (char *)sub + len; - SCA_STR_COPY( &sub->rr, rr ); - len += rr->len; - } - - /* - * dialog.id holds call-id + from-tag + to-tag; dialog.call_id, - * dialog.from_tag, and dialog.to_tag point to offsets within - * dialog.id. - * - * we shm_malloc this separately in case we need to update in-memory - * dialog saved for this subscriber. this is likely to happen if the - * subscriber goes off-line for some reason. - */ - len = sizeof( char ) * ( call_id->len + from_tag->len + to_tag->len ); - sub->dialog.id.s = (char *)shm_malloc( len ); - if ( sub->dialog.id.s == NULL ) { - LM_ERR( "Failed to shm_malloc space for %.*s %s subscription dialog: " - "out of memory", STR_FMT( &sub->subscriber ), - sca_event_name_from_type( sub->event )); - goto error; - } - sub->dialog.id.len = len; - - SCA_STR_COPY( &sub->dialog.id, call_id ); - SCA_STR_APPEND( &sub->dialog.id, from_tag ); - SCA_STR_APPEND( &sub->dialog.id, to_tag ); - - sub->dialog.call_id.s = sub->dialog.id.s; - sub->dialog.call_id.len = call_id->len; - - sub->dialog.from_tag.s = sub->dialog.id.s + call_id->len; - sub->dialog.from_tag.len = from_tag->len; - - sub->dialog.to_tag.s = sub->dialog.id.s + call_id->len + from_tag->len; - sub->dialog.to_tag.len = to_tag->len; - - return( sub ); - -error: - if ( sub != NULL ) { - if ( sub->dialog.id.s != NULL ) { - shm_free( sub->dialog.id.s ); - } - shm_free( sub ); - } - - return( NULL ); +static int sca_subscription_db_insert_subscriber(db1_con_t *db_con, + sca_subscription *sub) { + db_key_t insert_columns[SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS]; + db_val_t insert_values[SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS]; + str **column_names; + int i; + + assert(db_con != NULL); + assert(sub != NULL); + + column_names = sca_db_subscriptions_columns(); + if (column_names == NULL) { + LM_ERR( "sca_subscriptions_restore_from_db: failed to get " + "column names for SCA subscriptions table\n" ); + return (-1); + } + + for (i = 0; i < SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS; i++) { + insert_columns[i] = column_names[i]; + } + + /* XXX array boundary checking */ + if (sca_subscription_to_db_row_values(sub, insert_values) != 0) { + LM_ERR( "sca_subscription_db_insert_subscriber: failed to set " + "DB row values for INSERT of %s subscriber %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + return (-1); + } + + if (sca->db_api->insert(db_con, insert_columns, insert_values, + SCA_DB_SUBSCRIPTIONS_NUM_COLUMNS) < 0) { + LM_ERR( "sca_subscription_db_insert_subscriber: failed to insert " + "%s subscriber %.*s in DB subscription table\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + return (-1); + } + + /* no further DB actions needed until subscription is changed */ + sub->db_cmd_flag = SCA_DB_FLAG_NONE; + + return (0); } - int -sca_subscription_subscriber_cmp( str *subscriber, void *cmp_value ) -{ - sca_subscription *sub = (sca_subscription *)cmp_value; - int cmp; - - if (( cmp = subscriber->len - sub->subscriber.len ) != 0 ) { - return( cmp ); - } - - return( memcmp( subscriber->s, sub->subscriber.s, subscriber->len )); +int sca_subscription_db_delete_expired(db1_con_t *db_con) { + db_key_t delete_columns[1]; + db_val_t delete_values[1]; + db_op_t delete_ops[1]; + time_t now = time(NULL); + int kv_count = 0; + + delete_columns[0] = (str *) &SCA_DB_EXPIRES_COL_NAME; + delete_ops[0] = OP_LT; + + SCA_DB_BIND_INT_VALUE(now, &SCA_DB_EXPIRES_COL_NAME, delete_columns, + delete_values, kv_count); + + if (sca->db_api->delete(db_con, delete_columns, delete_ops, delete_values, + kv_count) < 0) { + LM_ERR( "sca_subscription_db_delete_expired: failed to delete " + "subscriptions expired before %ld\n", (long int)now ); + return (-1); + } + + return (0); } - void -sca_subscription_free( void *value ) -{ - sca_subscription *sub = (sca_subscription *)value; +int sca_subscription_db_update(void) { + db1_con_t *db_con = NULL; + sca_hash_table *ht; + sca_hash_entry *entry; + sca_subscription *sub; + int i; + int rc = -1; + time_t now = time(NULL); + + db_con = sca_db_get_connection(); + if (db_con == NULL) { + LM_ERR( "sca_subscription_db_update: failed to connect to DB %.*s\n", + STR_FMT( sca->cfg->db_url )); + goto done; + } + if (sca->db_api->use_table(db_con, sca->cfg->subs_table) < 0) { + LM_ERR( "sca_subscription_db_update: failed to in-use table " + "for DB %.*s\n", STR_FMT( sca->cfg->db_url )); + goto done; + } + + ht = sca->subscriptions; + for (i = 0; i < ht->size; i++) { + sca_hash_table_lock_index(ht, i); + + for (entry = ht->slots[i].entries; entry != NULL; entry = entry->next) { + sub = (sca_subscription *) entry->value; + + if (sub == NULL || sub->expires < now) { + continue; + } + + /* we only do call-info subscriptions for now */ + if (sub->event != SCA_EVENT_TYPE_CALL_INFO) { + continue; + } + + if (SCA_SUBSCRIPTION_IS_TERMINATED(sub)) { + continue; + } - if ( sub == NULL ) { - return; - } + if (sub->db_cmd_flag == SCA_DB_FLAG_INSERT) { + if (sca_subscription_db_insert_subscriber(db_con, sub) < 0) { + LM_ERR( "sca_subscription_db_update: failed to insert " + "%s subscriber %.*s into subscription DB\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + } + } else if (sub->db_cmd_flag == SCA_DB_FLAG_UPDATE) { + if (sca_subscription_db_update_subscriber(db_con, sub) < 0) { + LM_ERR( "sca_subscription_db_update: failed to insert " + "%s subscriber %.*s into subscription DB\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); + } + } + } - LM_DBG( "Freeing %s subscription from %.*s", - sca_event_name_from_type( sub->event ), - STR_FMT( &sub->subscriber )); + sca_hash_table_unlock_index(ht, i); + } - if ( !SCA_STR_EMPTY( &sub->dialog.id )) { - shm_free( sub->dialog.id.s ); - } + rc = sca_subscription_db_delete_expired(db_con); - shm_free( sub ); + done: return (rc); } - void -sca_subscription_print( void *value ) -{ - sca_subscription *sub = (sca_subscription *)value; - - LM_DBG( "%.*s %s (%d) %.*s, expires: %ld, index: %d, " - "dialog %.*s;%.*s;%.*s, record_route: %.*s, " - "notify_cseq: %d, subscribe_cseq: %d", - STR_FMT( &sub->target_aor ), - sca_event_name_from_type( sub->event ), - sub->event, - STR_FMT( &sub->subscriber ), - (long int)sub->expires, sub->index, - STR_FMT( &sub->dialog.call_id ), - STR_FMT( &sub->dialog.from_tag ), - STR_FMT( &sub->dialog.to_tag ), - SCA_STR_EMPTY( &sub->rr ) ? 4 : sub->rr.len, - SCA_STR_EMPTY( &sub->rr ) ? "null" : sub->rr.s, - sub->dialog.notify_cseq, - sub->dialog.subscribe_cseq ); +void sca_subscription_db_update_timer(unsigned int ticks, void *param) { + if (sca_subscription_db_update() != 0) { + LM_ERR( "sca_subscription_db_update_timer: failed to update " + "subscriptions in DB %.*s\n", STR_FMT( sca->cfg->db_url )); + } } - int -sca_subscription_save_unsafe( sca_mod *scam, sca_subscription *sub, - int save_idx, int opts ) -{ - sca_subscription *new_sub = NULL; - sca_hash_slot *slot; - int rc = -1; - - assert( save_idx >= 0 ); - - new_sub = sca_subscription_create( &sub->target_aor, sub->event, - &sub->subscriber, - sub->dialog.notify_cseq, - sub->dialog.subscribe_cseq, - sub->expires, - &sub->dialog.call_id, - &sub->dialog.from_tag, - &sub->dialog.to_tag, - &sub->rr, opts ); - if ( new_sub == NULL ) { - return( -1 ); - } - if ( sub->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY ) { - new_sub->index = sub->index; - } - - if ( sca_appearance_register( scam, &sub->target_aor ) < 0 ) { - LM_ERR( "sca_subscription_save: sca_appearance_register failed, " - "still saving subscription from %.*s", - STR_FMT( &sub->subscriber )); - } - - slot = sca_hash_table_slot_for_index( scam->subscriptions, save_idx ); - rc = sca_hash_table_slot_kv_insert_unsafe( slot, new_sub, - sca_subscription_subscriber_cmp, - sca_subscription_print, - sca_subscription_free ); - if ( rc < 0 ) { - shm_free( new_sub ); - new_sub = NULL; - } - - return( rc ); +int sca_subscription_aor_has_subscribers(int event, str *aor) { + sca_hash_slot *slot; + sca_hash_entry *e; + sca_subscription *sub; + str sub_key = STR_NULL; + char *event_name; + int len; + int subscribers = 0; + int slot_idx = -1; + + event_name = sca_event_name_from_type(event); + len = aor->len + strlen(event_name); + sub_key.s = (char *) pkg_malloc(len); + if (sub_key.s == NULL) { + LM_ERR( "Failed to pkg_malloc key to look up %s " + "subscription for %.*s\n", event_name, STR_FMT( aor )); + return (-1); + } + SCA_STR_COPY(&sub_key, aor); + SCA_STR_APPEND_CSTR(&sub_key, event_name); + + slot_idx = sca_hash_table_index_for_key(sca->subscriptions, &sub_key); + pkg_free(sub_key.s); + sub_key.len = 0; + + slot = sca_hash_table_slot_for_index(sca->subscriptions, slot_idx); + sca_hash_table_lock_index(sca->subscriptions, slot_idx); + + for (e = slot->entries; e != NULL; e = e->next) { + sub = (sca_subscription *) e->value; + + if (SCA_STR_EQ(&sub->target_aor, aor)) { + subscribers = 1; + break; + } + } + + sca_hash_table_unlock_index(sca->subscriptions, slot_idx); + + return (subscribers); } - static int -sca_subscription_update_unsafe( sca_mod *scam, sca_subscription *saved_sub, - sca_subscription *update_sub, int sub_idx ) -{ - int rc = -1; - int len; - char *dlg_id_tmp; - - if ( sub_idx < 0 || sub_idx > scam->subscriptions->size ) { - LM_ERR( "Invalid hash table index %d", sub_idx ); - goto done; - } - - /* sanity checks first */ - if ( saved_sub->event != update_sub->event ) { - LM_ERR( "Event mismatch for in-dialog SUBSCRIBE from %.*s: " - "%s != %s", STR_FMT( &update_sub->subscriber ), - sca_event_name_from_type( saved_sub->event ), - sca_event_name_from_type( update_sub->event )); - goto done; - } - if ( !STR_EQ( saved_sub->subscriber, update_sub->subscriber )) { - LM_ERR( "Contact mismatch for in-dialog SUBSCRIBE from %.*s: " - "%.*s != %.*s", STR_FMT( &update_sub->subscriber ), - STR_FMT( &update_sub->subscriber ), - STR_FMT( &saved_sub->subscriber )); - goto done; - } - if ( !STR_EQ( saved_sub->target_aor, update_sub->target_aor )) { - LM_ERR( "AoR mismatch for in-dialog SUBSCRIBE from %.*s: " - "%.*s != %.*s", STR_FMT( &update_sub->subscriber ), - STR_FMT( &update_sub->target_aor ), - STR_FMT( &saved_sub->target_aor )); - goto done; - } - - if ( !STR_EQ( saved_sub->dialog.call_id, update_sub->dialog.call_id ) || - !STR_EQ( saved_sub->dialog.from_tag, - update_sub->dialog.from_tag ) || - !STR_EQ( saved_sub->dialog.to_tag, update_sub->dialog.to_tag )) { +sca_subscription * +sca_subscription_create(str *aor, int event, str *subscriber, + unsigned int notify_cseq, unsigned int subscribe_cseq, int expire_delta, + str *call_id, str *from_tag, str *to_tag, str *rr, int opts) { + sca_subscription *sub = NULL; + int len = 0; + + len += sizeof(sca_subscription); + len += sizeof(char) * (aor->len + subscriber->len); + if (!SCA_STR_EMPTY(rr)) { + len += sizeof(char) * rr->len; + } + + sub = (sca_subscription *) shm_malloc(len); + if (sub == NULL) { + LM_ERR( "Failed to create %s subscription for %.*s: out of memory\n", + sca_event_name_from_type( event ), STR_FMT( subscriber )); + goto error; + } + memset(sub, 0, len); + + sub->event = event; + sub->state = SCA_SUBSCRIPTION_STATE_ACTIVE; + sub->index = SCA_CALL_INFO_APPEARANCE_INDEX_ANY; + if (opts & SCA_SUBSCRIPTION_CREATE_OPT_RAW_EXPIRES) { + sub->expires = expire_delta; + } else { + sub->expires = time(NULL) + expire_delta; + } + sub->dialog.notify_cseq = notify_cseq; + sub->dialog.subscribe_cseq = subscribe_cseq; + sub->db_cmd_flag = SCA_DB_FLAG_INSERT; + + len = sizeof(sca_subscription); + + sub->subscriber.s = (char *) sub + len; + SCA_STR_COPY(&sub->subscriber, subscriber); + len += subscriber->len; + + sub->target_aor.s = (char *) sub + len; + SCA_STR_COPY(&sub->target_aor, aor); + len += aor->len; + + if (!SCA_STR_EMPTY(rr)) { + sub->rr.s = (char *) sub + len; + SCA_STR_COPY(&sub->rr, rr); + len += rr->len; + } + /* - * mismatched dialog. we assume a subscriber can hold only one - * subscription per event at any given time, so we replace the old - * one with the new. + * dialog.id holds call-id + from-tag + to-tag; dialog.call_id, + * dialog.from_tag, and dialog.to_tag point to offsets within + * dialog.id. + * + * we shm_malloc this separately in case we need to update in-memory + * dialog saved for this subscriber. this is likely to happen if the + * subscriber goes off-line for some reason. */ - assert( !SCA_STR_EMPTY( &saved_sub->dialog.id )); - - /* this is allocated separately from the rest of the subscription */ - - len = sizeof( char * ) * ( update_sub->dialog.call_id.len + - update_sub->dialog.from_tag.len + - update_sub->dialog.to_tag.len ); - - dlg_id_tmp = (char *)shm_malloc( len ); - if ( dlg_id_tmp == NULL ) { - LM_ERR( "Failed to replace %.*s %s subscription dialog: " - "shm_malloc failed", STR_FMT( &update_sub->subscriber ), - sca_event_name_from_type( update_sub->event )); - /* XXX should remove subscription entirely here? */ - } else { - shm_free( saved_sub->dialog.id.s ); - saved_sub->dialog.id.s = dlg_id_tmp; - saved_sub->dialog.id.len = len; + len = sizeof(char) * (call_id->len + from_tag->len + to_tag->len); + sub->dialog.id.s = (char *) shm_malloc(len); + if (sub->dialog.id.s == NULL) { + LM_ERR( "Failed to shm_malloc space for %.*s %s subscription dialog: " + "out of memory\n", STR_FMT( &sub->subscriber ), + sca_event_name_from_type( sub->event )); + goto error; + } + sub->dialog.id.len = len; + + SCA_STR_COPY(&sub->dialog.id, call_id); + SCA_STR_APPEND(&sub->dialog.id, from_tag); + SCA_STR_APPEND(&sub->dialog.id, to_tag); - SCA_STR_COPY( &saved_sub->dialog.id, &update_sub->dialog.call_id ); - SCA_STR_APPEND( &saved_sub->dialog.id, - &update_sub->dialog.from_tag ); - SCA_STR_APPEND( &saved_sub->dialog.id, &update_sub->dialog.to_tag ); + sub->dialog.call_id.s = sub->dialog.id.s; + sub->dialog.call_id.len = call_id->len; - saved_sub->dialog.call_id.s = saved_sub->dialog.id.s; - saved_sub->dialog.call_id.len = update_sub->dialog.call_id.len; + sub->dialog.from_tag.s = sub->dialog.id.s + call_id->len; + sub->dialog.from_tag.len = from_tag->len; - saved_sub->dialog.from_tag.s = saved_sub->dialog.id.s + - update_sub->dialog.call_id.len; - saved_sub->dialog.from_tag.len = update_sub->dialog.from_tag.len; + sub->dialog.to_tag.s = sub->dialog.id.s + call_id->len + from_tag->len; + sub->dialog.to_tag.len = to_tag->len; - saved_sub->dialog.to_tag.s = saved_sub->dialog.id.s + - update_sub->dialog.call_id.len + - update_sub->dialog.from_tag.len; - saved_sub->dialog.to_tag.len = update_sub->dialog.to_tag.len; + return (sub); + + error: if (sub != NULL) { + if (sub->dialog.id.s != NULL) { + shm_free(sub->dialog.id.s); + } + shm_free(sub); } - } - saved_sub->state = update_sub->state; - saved_sub->dialog.subscribe_cseq = update_sub->dialog.subscribe_cseq; - saved_sub->dialog.notify_cseq += 1; - saved_sub->expires = time( NULL ) + update_sub->expires; + return (NULL); +} + +int sca_subscription_subscriber_cmp(str *subscriber, void *cmp_value) { + sca_subscription *sub = (sca_subscription *) cmp_value; + int cmp; - /* flag subscription for DB update only if we've already inserted */ - if ( saved_sub->db_cmd_flag == SCA_DB_FLAG_NONE ) { - saved_sub->db_cmd_flag = SCA_DB_FLAG_UPDATE; - } + if ((cmp = subscriber->len - sub->subscriber.len) != 0) { + return (cmp); + } - if ( update_sub->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY ) { - saved_sub->index = update_sub->index; - } + return (memcmp(subscriber->s, sub->subscriber.s, subscriber->len)); +} - /* set notify_cseq in update_sub, since we use it to send the NOTIFY */ - update_sub->dialog.notify_cseq = saved_sub->dialog.notify_cseq; +void sca_subscription_free(void *value) { + sca_subscription *sub = (sca_subscription *) value; - /* ensure we send the NOTIFY back through the same path as the SUBSCRIBE */ - if ( SCA_STR_EMPTY( &update_sub->rr ) && !SCA_STR_EMPTY( &saved_sub->rr )) { - update_sub->rr.s = (char *)pkg_malloc( saved_sub->rr.len ); - if ( update_sub->rr.s == NULL ) { - LM_ERR( "sca_subscription_update_unsafe: pkg_malloc record-route " - "value %.*s failed", STR_FMT( &saved_sub->rr )); - goto done; + if (sub == NULL) { + return; } - SCA_STR_COPY( &update_sub->rr, &saved_sub->rr ); - } + LM_DBG( "Freeing %s subscription from %.*s\n", + sca_event_name_from_type( sub->event ), + STR_FMT( &sub->subscriber )); - rc = 1; + if (!SCA_STR_EMPTY(&sub->dialog.id)) { + shm_free(sub->dialog.id.s); + } -done: - return( rc ); + shm_free(sub); } - static int -sca_subscription_copy_subscription_key( sca_subscription *sub, str *key_out ) -{ - char *event_name; - int len; - - assert( sub != NULL ); - assert( key_out != NULL ); - - len = sub->target_aor.len; - event_name = sca_event_name_from_type( sub->event ); - len += strlen( event_name ); - - key_out->s = (char *)pkg_malloc( len ); - if ( key_out->s == NULL ) { - LM_ERR( "Failed to pkg_malloc space for subscription key" ); - return( -1 ); - } - - SCA_STR_COPY( key_out, &sub->target_aor ); - SCA_STR_APPEND_CSTR( key_out, event_name ); - - return( key_out->len ); +void sca_subscription_print(void *value) { + sca_subscription *sub = (sca_subscription *) value; + + LM_DBG( "%.*s %s (%d) %.*s, expires: %ld, index: %d, " + "dialog %.*s;%.*s;%.*s, record_route: %.*s, " + "notify_cseq: %d, subscribe_cseq: %d\n", + STR_FMT( &sub->target_aor ), + sca_event_name_from_type( sub->event ), + sub->event, + STR_FMT( &sub->subscriber ), + (long int)sub->expires, sub->index, + STR_FMT( &sub->dialog.call_id ), + STR_FMT( &sub->dialog.from_tag ), + STR_FMT( &sub->dialog.to_tag ), + SCA_STR_EMPTY( &sub->rr ) ? 4 : sub->rr.len, + SCA_STR_EMPTY( &sub->rr ) ? "null" : sub->rr.s, + sub->dialog.notify_cseq, + sub->dialog.subscribe_cseq ); +} + +int sca_subscription_save_unsafe(sca_mod *scam, sca_subscription *sub, + int save_idx, int opts) { + sca_subscription *new_sub = NULL; + sca_hash_slot *slot; + int rc = -1; + + assert(save_idx >= 0); + + new_sub = sca_subscription_create(&sub->target_aor, sub->event, + &sub->subscriber, sub->dialog.notify_cseq, + sub->dialog.subscribe_cseq, sub->expires, &sub->dialog.call_id, + &sub->dialog.from_tag, &sub->dialog.to_tag, &sub->rr, opts); + if (new_sub == NULL) { + return (-1); + } + if (sub->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY) { + new_sub->index = sub->index; + } + + if (sca_appearance_register(scam, &sub->target_aor) < 0) { + LM_ERR( "sca_subscription_save: sca_appearance_register failed, " + "still saving subscription from %.*s\n", + STR_FMT( &sub->subscriber )); + } + + slot = sca_hash_table_slot_for_index(scam->subscriptions, save_idx); + rc = sca_hash_table_slot_kv_insert_unsafe(slot, new_sub, + sca_subscription_subscriber_cmp, sca_subscription_print, + sca_subscription_free); + if (rc < 0) { + shm_free(new_sub); + new_sub = NULL; + } + + return (rc); } - int -sca_subscription_delete_subscriber_for_event( sca_mod *scam, str *subscriber, - str *event, str *aor ) -{ - sca_hash_slot *slot; - sca_hash_entry *ent; - str subkey = STR_NULL; - char skbuf[ 1024 ]; - int slot_idx; - int len; +static int sca_subscription_update_unsafe(sca_mod *scam, + sca_subscription *saved_sub, sca_subscription *update_sub, int sub_idx) { + int rc = -1; + int len; + char *dlg_id_tmp; + + if (sub_idx < 0 || sub_idx > scam->subscriptions->size) { + LM_ERR( "Invalid hash table index %d\n", sub_idx ); + goto done; + } + + /* sanity checks first */ + if (saved_sub->event != update_sub->event) { + LM_ERR( "Event mismatch for in-dialog SUBSCRIBE from %.*s: " + "%s != %s\n", STR_FMT( &update_sub->subscriber ), + sca_event_name_from_type( saved_sub->event ), + sca_event_name_from_type( update_sub->event )); + goto done; + } + if (!STR_EQ(saved_sub->subscriber, update_sub->subscriber)) { + LM_ERR( "Contact mismatch for in-dialog SUBSCRIBE from %.*s: " + "%.*s != %.*s\n", STR_FMT( &update_sub->subscriber ), + STR_FMT( &update_sub->subscriber ), + STR_FMT( &saved_sub->subscriber )); + goto done; + } + if (!STR_EQ(saved_sub->target_aor, update_sub->target_aor)) { + LM_ERR( "AoR mismatch for in-dialog SUBSCRIBE from %.*s: " + "%.*s != %.*s\n", STR_FMT( &update_sub->subscriber ), + STR_FMT( &update_sub->target_aor ), + STR_FMT( &saved_sub->target_aor )); + goto done; + } - len = aor->len; - len += event->len; + if (!STR_EQ(saved_sub->dialog.call_id, update_sub->dialog.call_id) || + !STR_EQ( saved_sub->dialog.from_tag, + update_sub->dialog.from_tag ) || + !STR_EQ( saved_sub->dialog.to_tag, update_sub->dialog.to_tag )) { + /* + * mismatched dialog. we assume a subscriber can hold only one + * subscription per event at any given time, so we replace the old + * one with the new. + */ + assert(!SCA_STR_EMPTY(&saved_sub->dialog.id)); + + /* this is allocated separately from the rest of the subscription */ + + len = sizeof(char *) + * (update_sub->dialog.call_id.len + + update_sub->dialog.from_tag.len + + update_sub->dialog.to_tag.len); + + dlg_id_tmp = (char *) shm_malloc(len); + if (dlg_id_tmp == NULL) { + LM_ERR( "Failed to replace %.*s %s subscription dialog: " + "shm_malloc failed\n", STR_FMT( &update_sub->subscriber ), + sca_event_name_from_type( update_sub->event )); + /* XXX should remove subscription entirely here? */ + } else { + shm_free(saved_sub->dialog.id.s); + saved_sub->dialog.id.s = dlg_id_tmp; + saved_sub->dialog.id.len = len; + + SCA_STR_COPY(&saved_sub->dialog.id, &update_sub->dialog.call_id); + SCA_STR_APPEND(&saved_sub->dialog.id, &update_sub->dialog.from_tag); + SCA_STR_APPEND(&saved_sub->dialog.id, &update_sub->dialog.to_tag); + + saved_sub->dialog.call_id.s = saved_sub->dialog.id.s; + saved_sub->dialog.call_id.len = update_sub->dialog.call_id.len; + + saved_sub->dialog.from_tag.s = saved_sub->dialog.id.s + + update_sub->dialog.call_id.len; + saved_sub->dialog.from_tag.len = update_sub->dialog.from_tag.len; + + saved_sub->dialog.to_tag.s = saved_sub->dialog.id.s + + update_sub->dialog.call_id.len + + update_sub->dialog.from_tag.len; + saved_sub->dialog.to_tag.len = update_sub->dialog.to_tag.len; + } + } - if ( len >= sizeof( skbuf )) { - LM_ERR( "Subscription key %.*s%.*s: too long", - STR_FMT( aor ), STR_FMT( event )); - return( -1 ); - } + saved_sub->state = update_sub->state; + saved_sub->dialog.subscribe_cseq = update_sub->dialog.subscribe_cseq; + saved_sub->dialog.notify_cseq += 1; + saved_sub->expires = time(NULL) + update_sub->expires; - subkey.s = skbuf; - SCA_STR_COPY( &subkey, aor ); - SCA_STR_APPEND( &subkey, event ); + /* flag subscription for DB update only if we've already inserted */ + if (saved_sub->db_cmd_flag == SCA_DB_FLAG_NONE) { + saved_sub->db_cmd_flag = SCA_DB_FLAG_UPDATE; + } - slot_idx = sca_hash_table_index_for_key( scam->subscriptions, &subkey ); + if (update_sub->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY) { + saved_sub->index = update_sub->index; + } - slot = sca_hash_table_slot_for_index( sca->subscriptions, slot_idx ); - sca_hash_table_lock_index( scam->subscriptions, slot_idx ); + /* set notify_cseq in update_sub, since we use it to send the NOTIFY */ + update_sub->dialog.notify_cseq = saved_sub->dialog.notify_cseq; - ent = sca_hash_table_slot_kv_find_entry_unsafe( slot, subscriber ); - if ( ent != NULL ) { - ent = sca_hash_table_slot_unlink_entry_unsafe( slot, ent ); - } + /* ensure we send the NOTIFY back through the same path as the SUBSCRIBE */ + if ( SCA_STR_EMPTY( &update_sub->rr ) && !SCA_STR_EMPTY(&saved_sub->rr)) { + update_sub->rr.s = (char *) pkg_malloc(saved_sub->rr.len); + if (update_sub->rr.s == NULL) { + LM_ERR( "sca_subscription_update_unsafe: pkg_malloc record-route " + "value %.*s failed\n", STR_FMT( &saved_sub->rr )); + goto done; + } - sca_hash_table_unlock_index( sca->subscriptions, slot_idx ); + SCA_STR_COPY(&update_sub->rr, &saved_sub->rr); + } - if ( ent != NULL ) { - sca_hash_entry_free( ent ); - } + rc = 1; - return( 1 ); + done: return (rc); +} + +static int sca_subscription_copy_subscription_key(sca_subscription *sub, + str *key_out) { + char *event_name; + int len; + + assert(sub != NULL); + assert(key_out != NULL); + + len = sub->target_aor.len; + event_name = sca_event_name_from_type(sub->event); + len += strlen(event_name); + + key_out->s = (char *) pkg_malloc(len); + if (key_out->s == NULL) { + LM_ERR( "Failed to pkg_malloc space for subscription key\n" ); + return (-1); + } + + SCA_STR_COPY(key_out, &sub->target_aor); + SCA_STR_APPEND_CSTR(key_out, event_name); + + return (key_out->len); +} + +int sca_subscription_delete_subscriber_for_event(sca_mod *scam, str *subscriber, + str *event, str *aor) { + sca_hash_slot *slot; + sca_hash_entry *ent; + str subkey = STR_NULL; + char skbuf[1024]; + int slot_idx; + int len; + + len = aor->len; + len += event->len; + + if (len >= sizeof(skbuf)) { + LM_ERR( "Subscription key %.*s%.*s: too long\n", + STR_FMT( aor ), STR_FMT( event )); + return (-1); + } + + subkey.s = skbuf; + SCA_STR_COPY(&subkey, aor); + SCA_STR_APPEND(&subkey, event); + + slot_idx = sca_hash_table_index_for_key(scam->subscriptions, &subkey); + + slot = sca_hash_table_slot_for_index(sca->subscriptions, slot_idx); + sca_hash_table_lock_index(scam->subscriptions, slot_idx); + + ent = sca_hash_table_slot_kv_find_entry_unsafe(slot, subscriber); + if (ent != NULL) { + ent = sca_hash_table_slot_unlink_entry_unsafe(slot, ent); + } + + sca_hash_table_unlock_index(sca->subscriptions, slot_idx); + + if (ent != NULL) { + sca_hash_entry_free(ent); + } + + return (1); } /* caller must pkg_free req_sub->rr.s and req_sub->dialog.to_tag.s */ - int -sca_subscription_from_request( sca_mod *scam, sip_msg_t *msg, int event_type, - sca_subscription *req_sub ) -{ - struct to_body tmp_to = { 0 }; - struct to_body *to, *from; - str contact_uri; - str to_tag = STR_NULL; - unsigned int expires = 0, max_expires; - unsigned int cseq; - - assert( req_sub != NULL ); - - memset( req_sub, 0, sizeof( sca_subscription )); - - /* parse required info first */ - if ( !SCA_HEADER_EMPTY( msg->expires )) { - if ( parse_expires( msg->expires ) < 0 ) { - LM_ERR( "Failed to parse Expires header" ); - goto error; - } - - expires = ((exp_body_t *)msg->expires->parsed)->val; - } - - switch ( event_type ) { - case SCA_EVENT_TYPE_CALL_INFO: - default: - max_expires = scam->cfg->call_info_max_expires; - break; - - case SCA_EVENT_TYPE_LINE_SEIZE: - max_expires = scam->cfg->line_seize_max_expires; - break; - } - - if ( expires && expires > max_expires ) { - expires = max_expires; - } - - if ( SCA_HEADER_EMPTY( msg->to )) { - LM_ERR( "Empty To header" ); - goto error; - } - if ( SCA_HEADER_EMPTY( msg->callid )) { - LM_ERR( "Empty Call-ID header" ); - goto error; - } - - /* XXX move to static inline function */ - if ( SCA_HEADER_EMPTY( msg->cseq )) { - LM_ERR( "Empty CSeq header" ); - goto error; - } - if ( str2int( &(get_cseq( msg )->number), &cseq ) != 0 ) { - LM_ERR( "Bad Cseq header: %.*s", - msg->cseq->body.len, msg->cseq->body.s ); - goto error; - } - - if ( sca_get_msg_contact_uri( msg, &contact_uri ) < 0 ) { - /* above logs error */ - goto error; - } - - if ( SCA_HEADER_EMPTY( msg->from )) { - LM_ERR( "Empty From header" ); - goto error; - } - if ( parse_from_header( msg ) < 0 ) { - LM_ERR( "Bad From header" ); - goto error; - } - from = (struct to_body *)msg->from->parsed; - if ( SCA_STR_EMPTY( &from->tag_value )) { - LM_ERR( "No from-tag in From header" ); - goto error; - } - - if (( to = (struct to_body *)msg->to->parsed ) == NULL ) { - parse_to( msg->to->body.s, - msg->to->body.s + msg->to->body.len + 1, /* end of buffer */ - &tmp_to ); - - if ( tmp_to.error != PARSE_OK ) { - LM_ERR( "Bad To header" ); - goto error; - } - to = &tmp_to; - } - - to_tag = to->tag_value; - if ( to_tag.s == NULL ) { - /* - * XXX need hook to detect when we have a subscription and the - * subscriber sends an out-of-dialog SUBSCRIBE, which indicates the - * old subscription should be dumped & appropriate NOTIFYs sent. - */ - if ( scam->sl_api->get_reply_totag( msg, &to_tag ) < 0 ) { - LM_ERR( "Failed to generate to-tag for reply to SUBSCRIBE %.*s", - STR_FMT( &REQ_LINE( msg ).uri )); - goto error; - } - - if ( !SCA_HEADER_EMPTY( msg->record_route )) { - if ( print_rr_body( msg->record_route, &req_sub->rr, - 0, NULL ) < 0 ) { - LM_ERR( "Failed to parse Record-Route header %.*s in " - "SUBSCRIBE %.*s from %.*s", - STR_FMT( &msg->record_route->body ), - STR_FMT( &REQ_LINE( msg ).uri ), - STR_FMT( &contact_uri )); +int sca_subscription_from_request(sca_mod *scam, sip_msg_t *msg, int event_type, + sca_subscription *req_sub) { + struct to_body tmp_to = { 0 }; + struct to_body *to, *from; + str contact_uri; + str to_tag = STR_NULL; + unsigned int expires = 0, + max_expires; + unsigned int cseq; + + assert(req_sub != NULL); + + memset(req_sub, 0, sizeof(sca_subscription)); + + /* parse required info first */ + if (!SCA_HEADER_EMPTY(msg->expires)) { + if (parse_expires(msg->expires) < 0) { + LM_ERR( "Failed to parse Expires header\n" ); + goto error; + } + + expires = ((exp_body_t *) msg->expires->parsed)->val; + } + + switch (event_type) { + case SCA_EVENT_TYPE_CALL_INFO: + default: + max_expires = scam->cfg->call_info_max_expires; + break; + + case SCA_EVENT_TYPE_LINE_SEIZE: + max_expires = scam->cfg->line_seize_max_expires; + break; + } + + if (expires && expires > max_expires) { + expires = max_expires; + } + + if (SCA_HEADER_EMPTY(msg->to)) { + LM_ERR( "Empty To header\n" ); goto error; - } - } - } - - req_sub->subscriber = contact_uri; - if ( sca_uri_extract_aor( &REQ_LINE( msg ).uri, &req_sub->target_aor) < 0) { - LM_ERR( "Failed to extract AoR from RURI %.*s", - STR_FMT( &REQ_LINE( msg ).uri )); - goto error; - } - req_sub->event = event_type; - req_sub->index = SCA_CALL_INFO_APPEARANCE_INDEX_ANY; - req_sub->expires = expires; - if ( req_sub->expires > 0 ) { - req_sub->state = SCA_SUBSCRIPTION_STATE_ACTIVE; - expires += time( NULL ); - } else { - /* subscriber requested subscription termination, see rfc3265 3.2.4 */ - req_sub->state = SCA_SUBSCRIPTION_STATE_TERMINATED; - } - - req_sub->dialog.id.s = NULL; - req_sub->dialog.id.len = 0; - req_sub->dialog.call_id = msg->callid->body; - req_sub->dialog.from_tag = from->tag_value; - - req_sub->dialog.to_tag.s = pkg_malloc( to_tag.len ); - if ( req_sub->dialog.to_tag.s == NULL ) { - LM_ERR( "Failed to pkg_malloc space for to-tag %.*s", - STR_FMT( &to_tag )); - goto error; - } - SCA_STR_COPY( &req_sub->dialog.to_tag, &to_tag ); - - req_sub->dialog.subscribe_cseq = 0; - req_sub->dialog.notify_cseq = 0; - - free_to_params( &tmp_to ); - - return( 1 ); - -error: - free_to_params( &tmp_to ); - - if ( !SCA_STR_EMPTY( &req_sub->rr )) { - pkg_free( req_sub->rr.s ); - req_sub->rr.s = NULL; - } - - return( -1 ); -} + } + if (SCA_HEADER_EMPTY(msg->callid)) { + LM_ERR( "Empty Call-ID header\n" ); + goto error; + } - int -sca_handle_subscribe( sip_msg_t *msg, char *p1, char *p2 ) -{ - sca_subscription req_sub; - sca_subscription *sub = NULL; - sca_call_info call_info; - hdr_field_t *call_info_hdr = NULL; - str sub_key = STR_NULL; - str *to_tag = NULL; - char *status_text; - int event_type; - int status; - int app_idx = SCA_CALL_INFO_APPEARANCE_INDEX_ANY; - int idx = -1; - int rc = -1; - int released = 0; - - if ( parse_headers( msg, HDR_EOH_F, 0 ) < 0 ) { - LM_ERR( "header parsing failed: bad request" ); - SCA_SUB_REPLY_ERROR( sca, 400, "Bad Request", msg ); - return( -1 ); - } - - if ( !STR_EQ( REQ_LINE( msg ).method, SCA_METHOD_SUBSCRIBE )) { - LM_ERR( "bad request method %.*s", STR_FMT( &REQ_LINE( msg ).method )); - SCA_SUB_REPLY_ERROR( sca, 500, "Internal server error - config", msg ); - return( -1 ); - } - - if ( SCA_HEADER_EMPTY( msg->event )) { - SCA_SUB_REPLY_ERROR( sca, 400, "Missing Event", msg ); - return( -1 ); - } - - event_type = sca_event_from_str( &msg->event->body ); - if ( event_type == SCA_EVENT_TYPE_UNKNOWN ) { - SCA_SUB_REPLY_ERROR( sca, 400, "Bad Event", msg ); - return( -1 ); - } - - if ( sca_subscription_from_request( sca, msg, event_type, &req_sub ) < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 400, - "Bad Shared Call Appearance Request", msg ); - return( -1 ); - } - if ( sca_subscription_copy_subscription_key( &req_sub, &sub_key ) < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 500, "Internal Server Error - " - "copy dialog id", msg ); - goto done; - } - sca_subscription_print( &req_sub ); - - /* check to see if the message has a to-tag */ - to_tag = &(get_to( msg )->tag_value); - - /* XXX should lock starting here and use unsafe methods below? */ - - /* ensure we only calculate the hash table index once */ - idx = sca_hash_table_index_for_key( sca->subscriptions, - &sub_key ); - /* pkg_malloc'd in sca_subscription_copy_subscription_key above */ - pkg_free( sub_key.s ); - - if ( req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE ) { - call_info_hdr = sca_call_info_header_find( msg->headers ); - if ( call_info_hdr ) { - if ( sca_call_info_body_parse( &call_info_hdr->body, - &call_info ) < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 400, "Bad Request - " - "Invalid Call-Info header", msg ); - goto done; - } - req_sub.index = app_idx = call_info.index; + /* XXX move to static inline function */ + if (SCA_HEADER_EMPTY(msg->cseq)) { + LM_ERR( "Empty CSeq header\n" ); + goto error; + } + if (str2int(&(get_cseq( msg )->number), &cseq) != 0) { + LM_ERR( "Bad Cseq header: %.*s\n", + msg->cseq->body.len, msg->cseq->body.s ); + goto error; + } + + if (sca_get_msg_contact_uri(msg, &contact_uri) < 0) { + /* above logs error */ + goto error; + } + + if (SCA_HEADER_EMPTY(msg->from)) { + LM_ERR( "Empty From header\n" ); + goto error; + } + if (parse_from_header(msg) < 0) { + LM_ERR( "Bad From header\n" ); + goto error; + } + from = (struct to_body *) msg->from->parsed; + if (SCA_STR_EMPTY(&from->tag_value)) { + LM_ERR( "No from-tag in From header\n" ); + goto error; + } + + if ((to = (struct to_body *) msg->to->parsed) == NULL) { + parse_to(msg->to->body.s, msg->to->body.s + msg->to->body.len + 1, /* end of buffer */ + &tmp_to); + + if (tmp_to.error != PARSE_OK) { + LM_ERR( "Bad To header\n" ); + goto error; + } + to = &tmp_to; + } + + to_tag = to->tag_value; + if (to_tag.s == NULL) { + /* + * XXX need hook to detect when we have a subscription and the + * subscriber sends an out-of-dialog SUBSCRIBE, which indicates the + * old subscription should be dumped & appropriate NOTIFYs sent. + */ + if (scam->sl_api->get_reply_totag(msg, &to_tag) < 0) { + LM_ERR( "Failed to generate to-tag for reply to SUBSCRIBE %.*s\n", + STR_FMT( &REQ_LINE( msg ).uri )); + goto error; + } + + if (!SCA_HEADER_EMPTY(msg->record_route)) { + if (print_rr_body(msg->record_route, &req_sub->rr, 0, NULL) < 0) { + LM_ERR( "Failed to parse Record-Route header %.*s in " + "SUBSCRIBE %.*s from %.*s\n", + STR_FMT( &msg->record_route->body ), + STR_FMT( &REQ_LINE( msg ).uri ), + STR_FMT( &contact_uri )); + goto error; + } + } + } + + req_sub->subscriber = contact_uri; + if (sca_uri_extract_aor(&REQ_LINE( msg ).uri, &req_sub->target_aor) < 0) { + LM_ERR( "Failed to extract AoR from RURI %.*s\n", + STR_FMT( &REQ_LINE( msg ).uri )); + goto error; + } + req_sub->event = event_type; + req_sub->index = SCA_CALL_INFO_APPEARANCE_INDEX_ANY; + req_sub->expires = expires; + if (req_sub->expires > 0) { + req_sub->state = SCA_SUBSCRIPTION_STATE_ACTIVE; + expires += time(NULL); } else { - SCA_SUB_REPLY_ERROR( sca, 400, "Bad Request - " - "missing Call-Info header", msg ); - goto done; + /* subscriber requested subscription termination, see rfc3265 3.2.4 */ + req_sub->state = SCA_SUBSCRIPTION_STATE_TERMINATED; } - } - sca_hash_table_lock_index( sca->subscriptions, idx ); + req_sub->dialog.id.s = NULL; + req_sub->dialog.id.len = 0; + req_sub->dialog.call_id = msg->callid->body; + req_sub->dialog.from_tag = from->tag_value; - sub = sca_hash_table_index_kv_find_unsafe( sca->subscriptions, idx, - &req_sub.subscriber ); + req_sub->dialog.to_tag.s = pkg_malloc(to_tag.len); + if (req_sub->dialog.to_tag.s == NULL) { + LM_ERR( "Failed to pkg_malloc space for to-tag %.*s\n", + STR_FMT( &to_tag )); + goto error; + } + SCA_STR_COPY(&req_sub->dialog.to_tag, &to_tag); + + req_sub->dialog.subscribe_cseq = 0; + req_sub->dialog.notify_cseq = 0; + + free_to_params(&tmp_to); + + return (1); - if ( sub != NULL ) { - /* this will remove the subscription if expires == 0 */ - if ( sca_subscription_update_unsafe( sca, sub, &req_sub, idx ) < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 500, "Internal Server Error - " - "update subscription", msg ); - goto done; + error: free_to_params(&tmp_to); + + if (!SCA_STR_EMPTY(&req_sub->rr)) { + pkg_free(req_sub->rr.s); + req_sub->rr.s = NULL; } - if ( req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE ) { - if ( req_sub.expires == 0 ) { - /* release the seized appearance */ - if ( call_info_hdr == NULL ) { - SCA_SUB_REPLY_ERROR( sca, 400, "Bad Request - " - "missing Call-Info header", msg ); - goto done; + return (-1); +} + +int sca_handle_subscribe(sip_msg_t *msg, char *p1, char *p2) { + sca_subscription req_sub; + sca_subscription *sub = NULL; + sca_call_info call_info; + hdr_field_t *call_info_hdr = NULL; + str sub_key = STR_NULL; + str *to_tag = NULL; + char *status_text; + int event_type; + int status; + int app_idx = SCA_CALL_INFO_APPEARANCE_INDEX_ANY; + int idx = -1; + int rc = -1; + int released = 0; + + if (parse_headers(msg, HDR_EOH_F, 0) < 0) { + LM_ERR( "header parsing failed: bad request\n" ); + SCA_SUB_REPLY_ERROR(sca, 400, "Bad Request", msg); + return (-1); + } + + if (!STR_EQ(REQ_LINE( msg ).method, SCA_METHOD_SUBSCRIBE)) { + LM_ERR( "bad request method %.*s\n", STR_FMT( &REQ_LINE( msg ).method )); + SCA_SUB_REPLY_ERROR(sca, 500, "Internal server error - config", msg); + return (-1); + } + + if (SCA_HEADER_EMPTY(msg->event)) { + SCA_SUB_REPLY_ERROR(sca, 400, "Missing Event", msg); + return (-1); + } + + event_type = sca_event_from_str(&msg->event->body); + if (event_type == SCA_EVENT_TYPE_UNKNOWN) { + SCA_SUB_REPLY_ERROR(sca, 400, "Bad Event", msg); + return (-1); + } + + if (sca_subscription_from_request(sca, msg, event_type, &req_sub) < 0) { + SCA_SUB_REPLY_ERROR(sca, 400, "Bad Shared Call Appearance Request", + msg); + return (-1); + } + if (sca_subscription_copy_subscription_key(&req_sub, &sub_key) < 0) { + SCA_SUB_REPLY_ERROR(sca, 500, "Internal Server Error - " + "copy dialog id", msg); + goto done; + } + sca_subscription_print(&req_sub); + + /* check to see if the message has a to-tag */ + to_tag = &(get_to( msg )->tag_value); + + /* XXX should lock starting here and use unsafe methods below? */ + + /* ensure we only calculate the hash table index once */ + idx = sca_hash_table_index_for_key(sca->subscriptions, &sub_key); + /* pkg_malloc'd in sca_subscription_copy_subscription_key above */ + pkg_free(sub_key.s); + + if (req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE) { + call_info_hdr = sca_call_info_header_find(msg->headers); + if (call_info_hdr) { + if (sca_call_info_body_parse(&call_info_hdr->body, &call_info) + < 0) { + SCA_SUB_REPLY_ERROR(sca, 400, "Bad Request - " + "Invalid Call-Info header", msg); + goto done; + } + req_sub.index = app_idx = call_info.index; + } else { + SCA_SUB_REPLY_ERROR(sca, 400, "Bad Request - " + "missing Call-Info header", msg); + goto done; } - - if ( sca_appearance_release_index( sca, &req_sub.target_aor, - call_info.index ) != SCA_APPEARANCE_OK ) { - SCA_SUB_REPLY_ERROR( sca, 500, "Internal Server Error - " - "release seized line", msg ); - goto done; + } + + sca_hash_table_lock_index(sca->subscriptions, idx); + + sub = sca_hash_table_index_kv_find_unsafe(sca->subscriptions, idx, + &req_sub.subscriber); + + if (sub != NULL) { + /* this will remove the subscription if expires == 0 */ + if (sca_subscription_update_unsafe(sca, sub, &req_sub, idx) < 0) { + SCA_SUB_REPLY_ERROR(sca, 500, "Internal Server Error - " + "update subscription", msg); + goto done; } - } else if ( SCA_STR_EMPTY( to_tag )) { - /* don't seize new index if this is a line-seize reSUBSCRIBE */ - app_idx = sca_appearance_seize_index( sca, &req_sub.target_aor, - app_idx, &req_sub.subscriber ); - if ( app_idx == SCA_APPEARANCE_INDEX_UNAVAILABLE ) { - SCA_SUB_REPLY_ERROR( sca, 480, - "Temporarily Unavailable", msg ); - goto done; - } else if ( app_idx < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 500, - "Internal Server Error - seize appearance index", msg ); - goto done; + + if (req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE) { + if (req_sub.expires == 0) { + /* release the seized appearance */ + if (call_info_hdr == NULL) { + SCA_SUB_REPLY_ERROR(sca, 400, "Bad Request - " + "missing Call-Info header", msg); + goto done; + } + + if (sca_appearance_release_index(sca, &req_sub.target_aor, + call_info.index) != SCA_APPEARANCE_OK) { + SCA_SUB_REPLY_ERROR(sca, 500, "Internal Server Error - " + "release seized line", msg); + goto done; + } + } else if (SCA_STR_EMPTY(to_tag)) { + /* don't seize new index if this is a line-seize reSUBSCRIBE */ + app_idx = sca_appearance_seize_index(sca, &req_sub.target_aor, + app_idx, &req_sub.subscriber); + if (app_idx == SCA_APPEARANCE_INDEX_UNAVAILABLE) { + SCA_SUB_REPLY_ERROR(sca, 480, "Temporarily Unavailable", + msg); + goto done; + } else if (app_idx < 0) { + SCA_SUB_REPLY_ERROR(sca, 500, + "Internal Server Error - seize appearance index", + msg); + goto done; + } + req_sub.index = app_idx; + } + } else { + if (SCA_STR_EMPTY(to_tag)) { + /* + * if the subscriber owns any active appearances, clear them. + * we assume that an out-of-dialog SUBSCRIBE for a subscriber + * with active appearances is indicative of a reboot. + */ + released = sca_appearance_owner_release_all(&req_sub.target_aor, + &req_sub.subscriber); + if (released) { + LM_INFO( "sca_handle_subscribe: released %d appearances " + "for subscriber %.*s\n", released, + STR_FMT( &req_sub.subscriber )); + } + } } - req_sub.index = app_idx; - } } else { - if ( SCA_STR_EMPTY( to_tag )) { - /* - * if the subscriber owns any active appearances, clear them. - * we assume that an out-of-dialog SUBSCRIBE for a subscriber - * with active appearances is indicative of a reboot. - */ - released = sca_appearance_owner_release_all( - &req_sub.target_aor, - &req_sub.subscriber ); - if ( released ) { - LM_INFO( "sca_handle_subscribe: released %d appearances " - "for subscriber %.*s", released, - STR_FMT( &req_sub.subscriber )); + /* in-dialog request, but we didn't find it. */ + if (!SCA_STR_EMPTY(to_tag)) { + SCA_SUB_REPLY_ERROR(sca, 481, "Call Leg/Transaction Does Not Exist", + msg); + goto done; } - } - } - } else { - /* in-dialog request, but we didn't find it. */ - if ( !SCA_STR_EMPTY( to_tag )) { - SCA_SUB_REPLY_ERROR( sca, 481, - "Call Leg/Transaction Does Not Exist", msg ); - goto done; - } - - if ( req_sub.expires > 0 ) { - if ( req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE ) { - app_idx = sca_appearance_seize_index( sca, &req_sub.target_aor, - app_idx, &req_sub.subscriber ); - if ( app_idx == SCA_APPEARANCE_INDEX_UNAVAILABLE ) { - SCA_SUB_REPLY_ERROR( sca, 480, - "Temporarily Unavailable", msg ); - goto done; - } else if ( app_idx < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 500, "Internal Server Error - " - "seize appearance index", msg ); - goto done; + + if (req_sub.expires > 0) { + if (req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE) { + app_idx = sca_appearance_seize_index(sca, &req_sub.target_aor, + app_idx, &req_sub.subscriber); + if (app_idx == SCA_APPEARANCE_INDEX_UNAVAILABLE) { + SCA_SUB_REPLY_ERROR(sca, 480, "Temporarily Unavailable", + msg); + goto done; + } else if (app_idx < 0) { + SCA_SUB_REPLY_ERROR(sca, 500, "Internal Server Error - " + "seize appearance index", msg); + goto done; + } + req_sub.index = app_idx; + } + + if (sca_subscription_save_unsafe(sca, &req_sub, idx, + SCA_SUBSCRIPTION_CREATE_OPT_DEFAULT) < 0) { + SCA_SUB_REPLY_ERROR(sca, 500, "Internal Server Error - " + "save subscription", msg); + goto done; + } + } else { + /* + * we got an in-dialog SUBSCRIBE with an "Expires: 0" header, + * but the dialog wasn't in our table. just reply with the + * subscription info we got, without saving or creating anything. + */ + sub = &req_sub; } - req_sub.index = app_idx; - } + } + + sca_hash_table_unlock_index(sca->subscriptions, idx); + idx = -1; - if ( sca_subscription_save_unsafe( sca, &req_sub, idx, - SCA_SUBSCRIPTION_CREATE_OPT_DEFAULT ) < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 500, "Internal Server Error - " - "save subscription", msg ); + status = sca_ok_status_for_event(event_type); + status_text = sca_ok_text_for_event(event_type); + if (sca_subscription_reply(sca, status, status_text, event_type, + req_sub.expires, msg) < 0) { + SCA_SUB_REPLY_ERROR(sca, 500, "Internal server error", msg); goto done; - } - } else { - /* - * we got an in-dialog SUBSCRIBE with an "Expires: 0" header, - * but the dialog wasn't in our table. just reply with the - * subscription info we got, without saving or creating anything. - */ - sub = &req_sub; - } - } - - sca_hash_table_unlock_index( sca->subscriptions, idx ); - idx = -1; - - status = sca_ok_status_for_event( event_type ); - status_text = sca_ok_text_for_event( event_type ); - if ( sca_subscription_reply( sca, status, status_text, event_type, - req_sub.expires, msg ) < 0 ) { - SCA_SUB_REPLY_ERROR( sca, 500, "Internal server error", msg ); - goto done; - } - - if ( sca_notify_subscriber( sca, &req_sub, app_idx ) < 0 ) { - LM_ERR( "SCA %s SUBSCRIBE+NOTIFY for %.*s failed", - sca_event_name_from_type( req_sub.event ), - STR_FMT( &req_sub.subscriber )); - /* - * XXX - what does subscriber do in this case? drop subscription? - * sub is already saved/updated in hash table. let it rot? - */ - goto done; - } + } + + if (sca_notify_subscriber(sca, &req_sub, app_idx) < 0) { + LM_ERR( "SCA %s SUBSCRIBE+NOTIFY for %.*s failed\n", + sca_event_name_from_type( req_sub.event ), + STR_FMT( &req_sub.subscriber )); + /* + * XXX - what does subscriber do in this case? drop subscription? + * sub is already saved/updated in hash table. let it rot? + */ + goto done; + } - if ( req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE || released ) { - if ( sca_notify_call_info_subscribers( sca, &req_sub.target_aor) < 0 ) { - LM_ERR( "SCA %s NOTIFY to all %.*s subscribers failed", - sca_event_name_from_type( req_sub.event ), - STR_FMT( &req_sub.target_aor )); - goto done; + if (req_sub.event == SCA_EVENT_TYPE_LINE_SEIZE || released) { + if (sca_notify_call_info_subscribers(sca, &req_sub.target_aor) < 0) { + LM_ERR( "SCA %s NOTIFY to all %.*s subscribers failed\n", + sca_event_name_from_type( req_sub.event ), + STR_FMT( &req_sub.target_aor )); + goto done; + } } - } - rc = 1; + rc = 1; -done: - if ( idx >= 0 ) { - sca_hash_table_unlock_index( sca->subscriptions, idx ); - } + done: if (idx >= 0) { + sca_hash_table_unlock_index(sca->subscriptions, idx); + } - if ( req_sub.dialog.to_tag.s != NULL ) { - pkg_free( req_sub.dialog.to_tag.s ); - } - if ( req_sub.rr.s != NULL ) { - pkg_free( req_sub.rr.s ); - } + if (req_sub.dialog.to_tag.s != NULL) { + pkg_free(req_sub.dialog.to_tag.s); + } + if (req_sub.rr.s != NULL) { + pkg_free(req_sub.rr.s); + } - return( rc ); + return (rc); } - int -sca_subscription_reply( sca_mod *scam, int status_code, char *status_msg, - int event_type, int expires, sip_msg_t *msg ) -{ - str extra_headers = STR_NULL; - char hdr_buf[ 1024 ]; - int len; - - if ( event_type != SCA_EVENT_TYPE_CALL_INFO && - event_type != SCA_EVENT_TYPE_LINE_SEIZE ) { - LM_ERR( "sca_subscription_reply: unrecognized event type %d", - event_type ); - return( -1 ); - } - - if ( status_code < 300 ) { - /* Add Event, Contact, Allow-Events and Expires headers */ - extra_headers.s = hdr_buf; - len = snprintf( extra_headers.s, sizeof( hdr_buf ), - "Event: %s%s", sca_event_name_from_type( event_type ), CRLF ); - if ( len >= sizeof( hdr_buf ) || len < 0 ) { - LM_ERR( "sca_subscription_reply: extra headers too long" ); - return( -1 ); - } - extra_headers.len = len; - - SCA_STR_APPEND_CSTR( &extra_headers, "Contact: " ); - SCA_STR_APPEND( &extra_headers, &REQ_LINE( msg ).uri ); - SCA_STR_APPEND_CSTR( &extra_headers, CRLF ); - - SCA_STR_COPY_CSTR( &extra_headers, - "Allow-Events: call-info, line-seize" CRLF ); - - len = snprintf( extra_headers.s + extra_headers.len, - sizeof( hdr_buf ) - extra_headers.len, - "Expires: %d%s", expires, CRLF ); - if ( len >= (sizeof( hdr_buf ) - extra_headers.len) || len < 0 ) { - LM_ERR( "sca_subscription_reply: extra headers too long" ); - return( -1 ); - } - extra_headers.len += len; - } else if ( status_code == 480 ) { - /* tell loser of line-seize SUBSCRIBE race to try again shortly */ - extra_headers.s = hdr_buf; - len = snprintf( extra_headers.s, sizeof( hdr_buf ), - "Retry-After: %d%s", 1, CRLF ); - extra_headers.len = len; - } - - return( sca_reply( scam, status_code, status_msg, &extra_headers, msg )); +int sca_subscription_reply(sca_mod *scam, int status_code, char *status_msg, + int event_type, int expires, sip_msg_t *msg) { + str extra_headers = STR_NULL; + char hdr_buf[1024]; + int len; + + if (event_type != SCA_EVENT_TYPE_CALL_INFO + && event_type != SCA_EVENT_TYPE_LINE_SEIZE) { + LM_ERR( "sca_subscription_reply: unrecognized event type %d\n", + event_type ); + return (-1); + } + + if (status_code < 300) { + /* Add Event, Contact, Allow-Events and Expires headers */ + extra_headers.s = hdr_buf; + len = snprintf(extra_headers.s, sizeof(hdr_buf), "Event: %s%s", + sca_event_name_from_type(event_type), CRLF); + if (len >= sizeof(hdr_buf) || len < 0) { + LM_ERR( "sca_subscription_reply: extra headers too long\n" ); + return (-1); + } + extra_headers.len = len; + + SCA_STR_APPEND_CSTR(&extra_headers, "Contact: "); + SCA_STR_APPEND(&extra_headers, &REQ_LINE( msg ).uri); + SCA_STR_APPEND_CSTR(&extra_headers, CRLF); + + SCA_STR_COPY_CSTR(&extra_headers, + "Allow-Events: call-info, line-seize" CRLF); + + len = snprintf(extra_headers.s + extra_headers.len, + sizeof(hdr_buf) - extra_headers.len, "Expires: %d%s", expires, + CRLF); + if (len >= (sizeof(hdr_buf) - extra_headers.len) || len < 0) { + LM_ERR( "sca_subscription_reply: extra headers too long\n" ); + return (-1); + } + extra_headers.len += len; + } else if (status_code == 480) { + /* tell loser of line-seize SUBSCRIBE race to try again shortly */ + extra_headers.s = hdr_buf; + len = snprintf(extra_headers.s, sizeof(hdr_buf), "Retry-After: %d%s", 1, + CRLF); + extra_headers.len = len; + } + + return (sca_reply(scam, status_code, status_msg, &extra_headers, msg)); } /* @@ -1431,83 +1375,81 @@ sca_subscription_reply( sca_mod *scam, int status_code, char *status_msg, * 0: no subscription found to terminate * 1: subscription terminated */ - int -sca_subscription_terminate( sca_mod *scam, str *aor, int event, - str *subscriber, int termination_state, int opts ) -{ - sca_hash_slot *slot; - sca_hash_entry *ent; - sca_subscription *sub; - str sub_key = STR_NULL; - char *event_name; - int slot_idx; - int len; - - if ( !(opts & SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE)) { - LM_ERR( "sca_subscription_terminate: invalid opts 0x%x", opts ); - return( -1 ); - } - - event_name = sca_event_name_from_type( event ); - len = aor->len + strlen( event_name ); - sub_key.s = (char *)pkg_malloc( len ); - if ( sub_key.s == NULL ) { - LM_ERR( "Failed to pkg_malloc key to look up %s " - "subscription for %.*s", event_name, STR_FMT( aor )); - return( -1 ); - } - SCA_STR_COPY( &sub_key, aor ); - SCA_STR_APPEND_CSTR( &sub_key, event_name ); - - slot_idx = sca_hash_table_index_for_key( scam->subscriptions, &sub_key ); - pkg_free( sub_key.s ); - sub_key.len = 0; - - slot = sca_hash_table_slot_for_index( sca->subscriptions, slot_idx ); - sca_hash_table_lock_index( scam->subscriptions, slot_idx ); - - ent = sca_hash_table_slot_kv_find_entry_unsafe( slot, subscriber ); - if ( ent != NULL ) { - ent = sca_hash_table_slot_unlink_entry_unsafe( slot, ent ); - } - - sca_hash_table_unlock_index( sca->subscriptions, slot_idx ); - - if ( ent == NULL ) { - LM_DBG( "No %s subscription for %.*s", event_name, - STR_FMT( subscriber )); - return( 0 ); - } - - sub = (sca_subscription *)ent->value; - sub->expires = 0; - sub->dialog.notify_cseq += 1; - sub->state = termination_state; - - sca_subscription_print( sub ); - - if ( sca_notify_subscriber( sca, sub, sub->index ) < 0 ) { - LM_ERR( "SCA %s NOTIFY to %.*s failed", - event_name, STR_FMT( &sub->subscriber )); - - /* fall through, we might be able to notify the others */ - } - - if (( opts & SCA_SUBSCRIPTION_TERMINATE_OPT_RELEASE_APPEARANCE ) && - sub->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY ) { - if ( sca_appearance_release_index( sca, &sub->target_aor, - sub->index ) == SCA_APPEARANCE_OK ) { - if ( sca_notify_call_info_subscribers( sca, &sub->target_aor) < 0) { - LM_ERR( "SCA %s NOTIFY to all %.*s subscribers failed", - event_name, STR_FMT( &sub->target_aor )); - /* fall through, not much we can do about it */ - } - } - } - - if ( ent ) { - sca_hash_entry_free( ent ); - } - - return( 1 ); +int sca_subscription_terminate(sca_mod *scam, str *aor, int event, + str *subscriber, int termination_state, int opts) { + sca_hash_slot *slot; + sca_hash_entry *ent; + sca_subscription *sub; + str sub_key = STR_NULL; + char *event_name; + int slot_idx; + int len; + + if (!(opts & SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE)) { + LM_ERR( "sca_subscription_terminate: invalid opts 0x%x\n", opts ); + return (-1); + } + + event_name = sca_event_name_from_type(event); + len = aor->len + strlen(event_name); + sub_key.s = (char *) pkg_malloc(len); + if (sub_key.s == NULL) { + LM_ERR( "Failed to pkg_malloc key to look up %s " + "subscription for %.*s\n", event_name, STR_FMT( aor )); + return (-1); + } + SCA_STR_COPY(&sub_key, aor); + SCA_STR_APPEND_CSTR(&sub_key, event_name); + + slot_idx = sca_hash_table_index_for_key(scam->subscriptions, &sub_key); + pkg_free(sub_key.s); + sub_key.len = 0; + + slot = sca_hash_table_slot_for_index(sca->subscriptions, slot_idx); + sca_hash_table_lock_index(scam->subscriptions, slot_idx); + + ent = sca_hash_table_slot_kv_find_entry_unsafe(slot, subscriber); + if (ent != NULL) { + ent = sca_hash_table_slot_unlink_entry_unsafe(slot, ent); + } + + sca_hash_table_unlock_index(sca->subscriptions, slot_idx); + + if (ent == NULL) { + LM_DBG( "No %s subscription for %.*s\n", event_name, + STR_FMT( subscriber )); + return (0); + } + + sub = (sca_subscription *) ent->value; + sub->expires = 0; + sub->dialog.notify_cseq += 1; + sub->state = termination_state; + + sca_subscription_print(sub); + + if (sca_notify_subscriber(sca, sub, sub->index) < 0) { + LM_ERR( "SCA %s NOTIFY to %.*s failed\n", + event_name, STR_FMT( &sub->subscriber )); + + /* fall through, we might be able to notify the others */ + } + + if ((opts & SCA_SUBSCRIPTION_TERMINATE_OPT_RELEASE_APPEARANCE) + && sub->index != SCA_CALL_INFO_APPEARANCE_INDEX_ANY) { + if (sca_appearance_release_index(sca, &sub->target_aor, sub->index) + == SCA_APPEARANCE_OK) { + if (sca_notify_call_info_subscribers(sca, &sub->target_aor) < 0) { + LM_ERR( "SCA %s NOTIFY to all %.*s subscribers failed\n", + event_name, STR_FMT( &sub->target_aor )); + /* fall through, not much we can do about it */ + } + } + } + + if (ent) { + sca_hash_entry_free(ent); + } + + return (1); } diff --git a/modules/sca/sca_subscribe.h b/modules/sca/sca_subscribe.h index 6fbf7b92099..bf3b1eed266 100644 --- a/modules/sca/sca_subscribe.h +++ b/modules/sca/sca_subscribe.h @@ -24,47 +24,47 @@ #include "sca_dialog.h" enum { - SCA_SUBSCRIPTION_STATE_ACTIVE, - SCA_SUBSCRIPTION_STATE_PENDING, - SCA_SUBSCRIPTION_STATE_TERMINATED, - SCA_SUBSCRIPTION_STATE_TERMINATED_DEACTIVATED, - SCA_SUBSCRIPTION_STATE_TERMINATED_GIVEUP, - SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, - SCA_SUBSCRIPTION_STATE_TERMINATED_PROBATION, - SCA_SUBSCRIPTION_STATE_TERMINATED_REJECTED, - SCA_SUBSCRIPTION_STATE_TERMINATED_TIMEOUT, + SCA_SUBSCRIPTION_STATE_ACTIVE, + SCA_SUBSCRIPTION_STATE_PENDING, + SCA_SUBSCRIPTION_STATE_TERMINATED, + SCA_SUBSCRIPTION_STATE_TERMINATED_DEACTIVATED, + SCA_SUBSCRIPTION_STATE_TERMINATED_GIVEUP, + SCA_SUBSCRIPTION_STATE_TERMINATED_NORESOURCE, + SCA_SUBSCRIPTION_STATE_TERMINATED_PROBATION, + SCA_SUBSCRIPTION_STATE_TERMINATED_REJECTED, + SCA_SUBSCRIPTION_STATE_TERMINATED_TIMEOUT, }; struct _sca_subscription { - str subscriber; /* contact: user@ip */ - str target_aor; /* account of record to watch: user@domain */ - int event; /* "call-info", "line-seize" */ - time_t expires; /* expiration date of subscription */ - int state; /* active, pending, terminated */ - int index; /* seized appearance-index, line-seize only */ + str subscriber; /* contact: user@ip */ + str target_aor; /* account of record to watch: user@domain */ + int event; /* "call-info", "line-seize" */ + time_t expires; /* expiration date of subscription */ + int state; /* active, pending, terminated */ + int index; /* seized appearance-index, line-seize only */ - sca_dialog dialog; /* call-id, to- and from-tags, cseq */ + sca_dialog dialog; /* call-id, to- and from-tags, cseq */ - str rr; /* Record-Route header values */ + str rr; /* Record-Route header values */ - int db_cmd_flag; /* track whether to INSERT or UPDATE */ + int db_cmd_flag; /* track whether to INSERT or UPDATE */ }; -typedef struct _sca_subscription sca_subscription; +typedef struct _sca_subscription sca_subscription; enum { - SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE = (1 << 0), - SCA_SUBSCRIPTION_TERMINATE_OPT_RELEASE_APPEARANCE = (1 << 1), + SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE = (1 << 0), + SCA_SUBSCRIPTION_TERMINATE_OPT_RELEASE_APPEARANCE = (1 << 1), }; #define SCA_SUBSCRIPTION_TERMINATE_OPT_DEFAULT \ (SCA_SUBSCRIPTION_TERMINATE_OPT_UNSUBSCRIBE | \ SCA_SUBSCRIPTION_TERMINATE_OPT_RELEASE_APPEARANCE) enum { - SCA_SUBSCRIPTION_CREATE_OPT_DEFAULT = 0, - SCA_SUBSCRIPTION_CREATE_OPT_RAW_EXPIRES = (1 << 0), + SCA_SUBSCRIPTION_CREATE_OPT_DEFAULT = 0, + SCA_SUBSCRIPTION_CREATE_OPT_RAW_EXPIRES = (1 << 0), }; -extern const str SCA_METHOD_SUBSCRIBE; +extern const str SCA_METHOD_SUBSCRIBE; #define SCA_SUBSCRIPTION_IS_TERMINATED( sub1 ) \ ((sub1)->state >= SCA_SUBSCRIPTION_STATE_TERMINATED && \ @@ -74,19 +74,18 @@ extern const str SCA_METHOD_SUBSCRIBE; sca_subscription_reply((mod), (scode), (smsg), \ SCA_EVENT_TYPE_CALL_INFO, -1, (sreply)) -int sca_handle_subscribe( sip_msg_t *, char *, char * ); -int sca_subscription_reply( sca_mod *, int, char *, int, int, sip_msg_t * ); +int sca_handle_subscribe(sip_msg_t *, char *, char *); +int sca_subscription_reply(sca_mod *, int, char *, int, int, sip_msg_t *); -int sca_subscription_from_db_result( db1_res_t *, sca_subscription * ); -int sca_subscriptions_restore_from_db( sca_mod * ); -int sca_subscription_db_update( void ); -void sca_subscription_db_update_timer( unsigned, void * ); -void sca_subscription_purge_expired( unsigned int, void * ); -void sca_subscription_state_to_str( int, str * ); +int sca_subscription_from_db_result(db1_res_t *, sca_subscription *); +int sca_subscriptions_restore_from_db(sca_mod *); +int sca_subscription_db_update(void); +void sca_subscription_db_update_timer(unsigned, void *); +void sca_subscription_purge_expired(unsigned int, void *); +void sca_subscription_state_to_str(int, str *); -int sca_subscription_aor_has_subscribers( int, str * ); -int sca_subscription_delete_subscriber_for_event( sca_mod *, str *, str *, - str * ); -int sca_subscription_terminate( sca_mod *, str *, int, str *, int, int ); +int sca_subscription_aor_has_subscribers(int, str *); +int sca_subscription_delete_subscriber_for_event(sca_mod *, str *, str *, str *); +int sca_subscription_terminate(sca_mod *, str *, int, str *, int, int); #endif /* SCA_SUBSCRIBE_H */ diff --git a/modules/sca/sca_util.c b/modules/sca/sca_util.c index 1a5643e3836..59ae9880aa0 100644 --- a/modules/sca/sca_util.c +++ b/modules/sca/sca_util.c @@ -25,436 +25,404 @@ #include "../../parser/sdp/sdp.h" - int -sca_get_msg_method( sip_msg_t *msg ) -{ - assert( msg != NULL ); +int sca_get_msg_method(sip_msg_t *msg) { + assert(msg != NULL); - if ( msg->first_line.type == SIP_REQUEST ) { - return( msg->REQ_METHOD ); - } + if (msg->first_line.type == SIP_REQUEST) { + return (msg->REQ_METHOD); + } - return( sca_get_msg_cseq_method( msg )); + return (sca_get_msg_cseq_method(msg)); } - int -sca_get_msg_contact_uri( sip_msg_t *msg, str *contact_uri ) -{ - contact_body_t *contact_body; - - assert( msg != NULL ); - assert( contact_uri != NULL ); - - if ( SCA_HEADER_EMPTY( msg->contact )) { - LM_DBG( "Empty Contact header" ); - contact_uri->s = NULL; - contact_uri->len = 0; - - return( 0 ); - } - - if ( parse_contact( msg->contact ) < 0 ) { - LM_ERR( "Failed to parse Contact header: %.*s", - STR_FMT( &msg->contact->body )); - return( -1 ); - } - if (( contact_body = (contact_body_t *)msg->contact->parsed ) == NULL ) { - LM_ERR( "Invalid Contact header: %.*s", STR_FMT( &msg->contact->body )); - return( -1 ); - } - if ( contact_body->star ) { - LM_ERR( "Invalid Contact header: SCA Contact must not be \"*\"" ); - return( -1 ); - } - if ( contact_body->contacts == NULL ) { - LM_ERR( "Invalid Contact header: parser found no contacts" ); - return( -1 ); - } - if ( contact_body->contacts->next ) { - LM_ERR( "Invalid Contact header: Contact may only contain one URI" ); - return( -1 ); - } - - contact_uri->s = contact_body->contacts->uri.s; - contact_uri->len = contact_body->contacts->uri.len; - - return( 1 ); +int sca_get_msg_contact_uri(sip_msg_t *msg, str *contact_uri) { + contact_body_t *contact_body; + + assert(msg != NULL); + assert(contact_uri != NULL); + + if (SCA_HEADER_EMPTY(msg->contact)) { + LM_DBG( "Empty Contact header\n" ); + contact_uri->s = NULL; + contact_uri->len = 0; + + return (0); + } + + if (parse_contact(msg->contact) < 0) { + LM_ERR( "Failed to parse Contact header: %.*s\n", + STR_FMT( &msg->contact->body )); + return (-1); + } + if ((contact_body = (contact_body_t *) msg->contact->parsed) == NULL) { + LM_ERR( "Invalid Contact header: %.*s\n", STR_FMT( &msg->contact->body )); + return (-1); + } + if (contact_body->star) { + LM_ERR( "Invalid Contact header: SCA Contact must not be \"*\"\n" ); + return (-1); + } + if (contact_body->contacts == NULL) { + LM_ERR( "Invalid Contact header: parser found no contacts\n" ); + return (-1); + } + if (contact_body->contacts->next) { + LM_ERR( "Invalid Contact header: Contact may only contain one URI\n" ); + return (-1); + } + + contact_uri->s = contact_body->contacts->uri.s; + contact_uri->len = contact_body->contacts->uri.len; + + return (1); } - int -sca_get_msg_cseq_number( sip_msg_t *msg ) -{ - int cseq; +int sca_get_msg_cseq_number(sip_msg_t *msg) { + int cseq; - assert( msg != NULL ); + assert(msg != NULL); - if ( SCA_HEADER_EMPTY( msg->cseq )) { - LM_ERR( "Empty Cseq header" ); - return( -1 ); - } - if ( str2int( &(get_cseq( msg )->number), (unsigned int *)&cseq ) != 0 ) { - LM_ERR( "Bad Cseq header: %.*s", STR_FMT( &msg->cseq->body )); - return( -1 ); - } + if (SCA_HEADER_EMPTY(msg->cseq)) { + LM_ERR( "Empty Cseq header\n" ); + return (-1); + } + if (str2int(&(get_cseq( msg )->number), (unsigned int *) &cseq) != 0) { + LM_ERR( "Bad Cseq header: %.*s\n", STR_FMT( &msg->cseq->body )); + return (-1); + } - return( cseq ); + return (cseq); } /* assumes cseq header in msg is already parsed */ - int -sca_get_msg_cseq_method( sip_msg_t *msg ) -{ - assert( msg != NULL ); +int sca_get_msg_cseq_method(sip_msg_t *msg) { + assert(msg != NULL); - if ( SCA_HEADER_EMPTY( msg->cseq )) { - LM_ERR( "Empty Cseq header" ); - return( -1 ); - } + if (SCA_HEADER_EMPTY(msg->cseq)) { + LM_ERR( "Empty Cseq header\n" ); + return (-1); + } - return( get_cseq( msg )->method_id ); + return ( get_cseq( msg )->method_id); } +int sca_get_msg_from_header(sip_msg_t *msg, struct to_body **from) { + struct to_body *f; + + assert(msg != NULL); + assert(from != NULL); + + if (SCA_HEADER_EMPTY(msg->from)) { + LM_ERR( "Empty From header\n" ); + return (-1); + } + if (parse_from_header(msg) < 0) { + LM_ERR( "Bad From header\n" ); + return (-1); + } + f = get_from(msg); + if (SCA_STR_EMPTY(&f->tag_value)) { + LM_ERR( "Bad From header: no tag parameter\n" ); + return (-1); + } + + /* ensure the URI is parsed for future use */ + if (parse_uri(f->uri.s, f->uri.len, GET_FROM_PURI(msg)) < 0) { + LM_ERR( "Failed to parse From URI %.*s\n", STR_FMT( &f->uri )); + return (-1); + } + + *from = f; - int -sca_get_msg_from_header( sip_msg_t *msg, struct to_body **from ) -{ - struct to_body *f; - - assert( msg != NULL ); - assert( from != NULL ); - - if ( SCA_HEADER_EMPTY( msg->from )) { - LM_ERR( "Empty From header" ); - return( -1 ); - } - if ( parse_from_header( msg ) < 0 ) { - LM_ERR( "Bad From header" ); - return( -1 ); - } - f = get_from( msg ); - if ( SCA_STR_EMPTY( &f->tag_value )) { - LM_ERR( "Bad From header: no tag parameter" ); - return( -1 ); - } - - /* ensure the URI is parsed for future use */ - if ( parse_uri( f->uri.s, f->uri.len, GET_FROM_PURI( msg )) < 0 ) { - LM_ERR( "Failed to parse From URI %.*s", STR_FMT( &f->uri )); - return( -1 ); - } - - *from = f; - - return( 0 ); + return (0); } - int -sca_get_msg_to_header( sip_msg_t *msg, struct to_body **to ) -{ - struct to_body parsed_to; - struct to_body *t = NULL; - - assert( msg != NULL ); - assert( to != NULL ); - - if ( SCA_HEADER_EMPTY( msg->to )) { - LM_ERR( "Empty To header" ); - return( -1 ); - } - t = get_to( msg ); - if ( t == NULL ) { - parse_to( msg->to->body.s, - msg->to->body.s + msg->to->body.len + 1, /* end of buffer */ - &parsed_to ); - if ( parsed_to.error != PARSE_OK ) { - LM_ERR( "Bad To header" ); - return( -1 ); - } - t = &parsed_to; - } - - /* ensure the URI is parsed for future use */ - if ( parse_uri( t->uri.s, t->uri.len, GET_TO_PURI( msg )) < 0 ) { - LM_ERR( "Failed to parse To URI %.*s", STR_FMT( &t->uri )); - return( -1 ); - } - - *to = t; - - return( 0 ); +int sca_get_msg_to_header(sip_msg_t *msg, struct to_body **to) { + struct to_body parsed_to; + struct to_body *t = NULL; + + assert(msg != NULL); + assert(to != NULL); + + if (SCA_HEADER_EMPTY(msg->to)) { + LM_ERR( "Empty To header\n" ); + return (-1); + } + t = get_to(msg); + if (t == NULL) { + parse_to(msg->to->body.s, msg->to->body.s + msg->to->body.len + 1, /* end of buffer */ + &parsed_to); + if (parsed_to.error != PARSE_OK) { + LM_ERR( "Bad To header\n" ); + return (-1); + } + t = &parsed_to; + } + + /* ensure the URI is parsed for future use */ + if (parse_uri(t->uri.s, t->uri.len, GET_TO_PURI(msg)) < 0) { + LM_ERR( "Failed to parse To URI %.*s\n", STR_FMT( &t->uri )); + return (-1); + } + + *to = t; + + return (0); } /* count characters requiring escape as defined by escape_common */ - int -sca_uri_display_escapes_count( str *display ) -{ - int c = 0; - int i; - - if ( SCA_STR_EMPTY( display )) { - return( 0 ); - } - - for ( i = 0; i < display->len; i++ ) { - switch ( display->s[ i ] ) { - case '\'': - case '"': - case '\\': - case '\0': - c++; - - default: - break; - } - } - - return( c ); +int sca_uri_display_escapes_count(str *display) { + int c = 0; + int i; + + if (SCA_STR_EMPTY(display)) { + return (0); + } + + for (i = 0; i < display->len; i++) { + switch (display->s[i]) { + case '\'': + case '"': + case '\\': + case '\0': + c++; + + default: + break; + } + } + + return (c); } - int -sca_uri_extract_aor( str *uri, str *aor ) -{ - char *semi; +int sca_uri_extract_aor(str *uri, str *aor) { + char *semi; - assert( aor != NULL ); + assert(aor != NULL); - if ( uri == NULL ) { - aor->s = NULL; - aor->len = 0; - return( -1 ); - } + if (uri == NULL) { + aor->s = NULL; + aor->len = 0; + return (-1); + } - aor->s = uri->s; - semi = memchr( uri->s, ';', uri->len ); - if ( semi != NULL ) { - aor->len = semi - uri->s; - } else { - aor->len = uri->len; - } + aor->s = uri->s; + semi = memchr(uri->s, ';', uri->len); + if (semi != NULL) { + aor->len = semi - uri->s; + } else { + aor->len = uri->len; + } - return( 0 ); + return (0); } - int -sca_uri_build_aor( str *aor, int maxlen, str *contact_uri, str *domain_uri ) -{ - char *p; - char *dp; - int len; - - assert( aor != NULL ); - assert( contact_uri != NULL ); - assert( domain_uri != NULL ); - - if ( contact_uri->len + domain_uri->len >= maxlen ) { - return( -1 ); - } - - p = memchr( contact_uri->s, '@', contact_uri->len ); - if ( p == NULL ) { - /* no username, by definition can't be an SCA line */ - aor->s = NULL; - aor->len = 0; - - return( 0 ); - } - dp = memchr( domain_uri->s, '@', domain_uri->len ); - if ( dp == NULL ) { - /* may be nameless URI */ - dp = memchr( domain_uri->s, ':', domain_uri->len ); - if ( dp == NULL ) { - /* bad domain URI */ - return( -1 ); - } - } - dp++; - - len = p - contact_uri->s; - memcpy( aor->s, contact_uri->s, len ); - aor->s[ len ] = '@'; - len += 1; - aor->len = len; - - len = domain_uri->len - ( dp - domain_uri->s ); - memcpy( aor->s + aor->len, dp, len ); - aor->len += len; - - return( aor->len ); -} +int sca_uri_build_aor(str *aor, int maxlen, str *contact_uri, str *domain_uri) { + char *p; + char *dp; + int len; - int -sca_aor_create_from_info( str *aor, uri_type type, str *user, str *domain, - str *port ) -{ - str scheme = STR_NULL; - int len = 0; + assert(aor != NULL); + assert(contact_uri != NULL); + assert(domain_uri != NULL); + + if (contact_uri->len + domain_uri->len >= maxlen) { + return (-1); + } - assert( aor != NULL ); + p = memchr(contact_uri->s, '@', contact_uri->len); + if (p == NULL) { + /* no username, by definition can't be an SCA line */ + aor->s = NULL; + aor->len = 0; - uri_type_to_str( type, &scheme ); + return (0); + } + dp = memchr(domain_uri->s, '@', domain_uri->len); + if (dp == NULL) { + /* may be nameless URI */ + dp = memchr(domain_uri->s, ':', domain_uri->len); + if (dp == NULL) { + /* bad domain URI */ + return (-1); + } + } + dp++; - /* +1 for ':', +1 for '@' */ - len = scheme.len + 1 + user->len + 1 + domain->len; - if ( !SCA_STR_EMPTY( port )) { - /* +1 for ':' */ - len += 1 + port->len; - } + len = p - contact_uri->s; + memcpy(aor->s, contact_uri->s, len); + aor->s[len] = '@'; + len += 1; + aor->len = len; - aor->s = (char *)pkg_malloc( len ); - if ( aor->s == NULL ) { - LM_ERR( "sca_aor_create_from_info: pkg_malloc %d bytes failed", len ); - return( -1 ); - } + len = domain_uri->len - (dp - domain_uri->s); + memcpy(aor->s + aor->len, dp, len); + aor->len += len; - len = 0; - SCA_STR_COPY( aor, &scheme ); - len += scheme.len; + return (aor->len); +} - *(aor->s + len) = ':'; - aor->len++; - len++; +int sca_aor_create_from_info(str *aor, uri_type type, str *user, str *domain, + str *port) { + str scheme = STR_NULL; + int len = 0; - SCA_STR_APPEND( aor, user ); - len += user->len; + assert(aor != NULL); - *(aor->s + len) = '@'; - aor->len++; - len++; + uri_type_to_str(type, &scheme); - SCA_STR_APPEND( aor, domain ); - len += domain->len; + /* +1 for ':', +1 for '@' */ + len = scheme.len + 1 + user->len + 1 + domain->len; + if (!SCA_STR_EMPTY(port)) { + /* +1 for ':' */ + len += 1 + port->len; + } + + aor->s = (char *) pkg_malloc(len); + if (aor->s == NULL) { + LM_ERR( "sca_aor_create_from_info: pkg_malloc %d bytes failed\n", len ); + return (-1); + } + + len = 0; + SCA_STR_COPY(aor, &scheme); + len += scheme.len; - if ( !SCA_STR_EMPTY( port )) { *(aor->s + len) = ':'; - len += 1; + aor->len++; + len++; + + SCA_STR_APPEND(aor, user); + len += user->len; - SCA_STR_APPEND( aor, port ); - len += port->len; - } + *(aor->s + len) = '@'; + aor->len++; + len++; + + SCA_STR_APPEND(aor, domain); + len += domain->len; + + if (!SCA_STR_EMPTY(port)) { + *(aor->s + len) = ':'; + len += 1; + + SCA_STR_APPEND(aor, port); + len += port->len; + } - return( aor->len ); + return (aor->len); } - int -sca_create_canonical_aor_for_ua( sip_msg_t *msg, str *c_aor, int ua_opts ) -{ - struct to_body *tf = NULL; - sip_uri_t c_uri; - str tf_aor = STR_NULL; - str contact_uri = STR_NULL; - int rc = -1; +int sca_create_canonical_aor_for_ua(sip_msg_t *msg, str *c_aor, int ua_opts) { + struct to_body *tf = NULL; + sip_uri_t c_uri; + str tf_aor = STR_NULL; + str contact_uri = STR_NULL; + int rc = -1; - assert( msg != NULL ); - assert( c_aor != NULL ); + assert(msg != NULL); + assert(c_aor != NULL); - memset( c_aor, 0, sizeof( str )); + memset(c_aor, 0, sizeof(str)); - if (( ua_opts & SCA_AOR_TYPE_AUTO )) { - if ( msg->first_line.type == SIP_REQUEST ) { - ua_opts = SCA_AOR_TYPE_UAC; + if ((ua_opts & SCA_AOR_TYPE_AUTO)) { + if (msg->first_line.type == SIP_REQUEST) { + ua_opts = SCA_AOR_TYPE_UAC; + } else { + ua_opts = SCA_AOR_TYPE_UAS; + } + } + + if ((ua_opts & SCA_AOR_TYPE_UAC)) { + if (sca_get_msg_from_header(msg, &tf) < 0) { + LM_ERR( "sca_create_canonical_aor: failed to get From header\n" ); + goto done; + } + } else { + if (sca_get_msg_to_header(msg, &tf) < 0) { + LM_ERR( "sca_create_canonical_aor: failed to get To header\n" ); + goto done; + } + } + + if (sca_uri_extract_aor(&tf->uri, &tf_aor) < 0) { + LM_ERR( "sca_create_canonical_aor: failed to extract AoR from " + "URI <%.*s>\n", STR_FMT( &tf->uri )); + goto done; + } + + memset(&c_uri, 0, sizeof(sip_uri_t)); + if ((rc = sca_get_msg_contact_uri(msg, &contact_uri)) < 0) { + LM_ERR( "sca_create_canonical_aor: failed to get contact URI from " + "Contact <%.*s>\n", STR_FMT( &msg->contact->body )); + goto done; + } + if (rc > 0) { + if (parse_uri(contact_uri.s, contact_uri.len, &c_uri) < 0) { + LM_ERR( "sca_create_canonical_aor: failed to parse Contact URI " + "<%.*s>\n", STR_FMT( &contact_uri )); + rc = -1; + goto done; + } + } + + if ( SCA_STR_EMPTY(&c_uri.user) || + SCA_STR_EQ( &c_uri.user, &tf->parsed_uri.user )) { + /* empty contact header or Contact user matches To/From AoR */ + c_aor->s = (char *) pkg_malloc(tf_aor.len); + c_aor->len = tf_aor.len; + memcpy(c_aor->s, tf_aor.s, tf_aor.len); } else { - ua_opts = SCA_AOR_TYPE_UAS; - } - } - - if (( ua_opts & SCA_AOR_TYPE_UAC )) { - if ( sca_get_msg_from_header( msg, &tf ) < 0 ) { - LM_ERR( "sca_create_canonical_aor: failed to get From header" ); - goto done; - } - } else { - if ( sca_get_msg_to_header( msg, &tf ) < 0 ) { - LM_ERR( "sca_create_canonical_aor: failed to get To header" ); - goto done; - } - } - - if ( sca_uri_extract_aor( &tf->uri, &tf_aor ) < 0 ) { - LM_ERR( "sca_create_canonical_aor: failed to extract AoR from " - "URI <%.*s>", STR_FMT( &tf->uri )); - goto done; - } - - memset( &c_uri, 0, sizeof( sip_uri_t )); - if (( rc = sca_get_msg_contact_uri( msg, &contact_uri )) < 0 ) { - LM_ERR( "sca_create_canonical_aor: failed to get contact URI from " - "Contact <%.*s>", STR_FMT( &msg->contact->body )); - goto done; - } - if ( rc > 0 ) { - if ( parse_uri( contact_uri.s, contact_uri.len, &c_uri ) < 0 ) { - LM_ERR( "sca_create_canonical_aor: failed to parse Contact URI " - "<%.*s>", STR_FMT( &contact_uri )); - rc = -1; - goto done; - } - } - - if ( SCA_STR_EMPTY( &c_uri.user ) || - SCA_STR_EQ( &c_uri.user, &tf->parsed_uri.user )) { - /* empty contact header or Contact user matches To/From AoR */ - c_aor->s = (char *)pkg_malloc( tf_aor.len ); - c_aor->len = tf_aor.len; - memcpy( c_aor->s, tf_aor.s, tf_aor.len ); - } else { - /* Contact user and To/From user mismatch */ - if ( sca_aor_create_from_info( c_aor, c_uri.type, - &c_uri.user, &tf->parsed_uri.host, - &tf->parsed_uri.port ) < 0 ) { - LM_ERR( "sca_create_canonical_aor: failed to create AoR from " - "Contact <%.*s> and URI <%.*s>", - STR_FMT( &contact_uri ), STR_FMT( &tf_aor )); - goto done; - } - } - - rc = 1; - -done: - return( rc ); + /* Contact user and To/From user mismatch */ + if (sca_aor_create_from_info(c_aor, c_uri.type, &c_uri.user, + &tf->parsed_uri.host, &tf->parsed_uri.port) < 0) { + LM_ERR( "sca_create_canonical_aor: failed to create AoR from " + "Contact <%.*s> and URI <%.*s>\n", + STR_FMT( &contact_uri ), STR_FMT( &tf_aor )); + goto done; + } + } + + rc = 1; + + done: return (rc); } - int -sca_create_canonical_aor( sip_msg_t *msg, str *c_aor ) -{ - return( sca_create_canonical_aor_for_ua( msg, c_aor, SCA_AOR_TYPE_AUTO )); +int sca_create_canonical_aor(sip_msg_t *msg, str *c_aor) { + return (sca_create_canonical_aor_for_ua(msg, c_aor, SCA_AOR_TYPE_AUTO)); } /* XXX this considers any held stream to mean the call is on hold. correct? */ - int -sca_call_is_held( sip_msg_t *msg ) -{ - sdp_session_cell_t *session; - sdp_stream_cell_t *stream; - int n_sess; - int n_str; - int is_held = 0; - int rc; - - rc = parse_sdp( msg ); - if ( rc < 0 ) { - LM_ERR( "sca_call_is_held: parse_sdp body failed" ); - return( 0 ); - } else if ( rc > 0 ) { - LM_DBG( "sca_call_is_held: parse_sdp returned %d, no SDP body", rc ); - return( 0 ); - } - - /* Cf. modules_k/textops's exported is_audio_on_hold */ - for ( n_sess = 0, session = get_sdp_session( msg, n_sess ); - session != NULL; - n_sess++, session = get_sdp_session( msg, n_sess )) { - - for ( n_str = 0, stream = get_sdp_stream( msg, n_sess, n_str ); - stream != NULL; - n_str++, stream = get_sdp_stream( msg, n_sess, n_str )) { - if ( stream->is_on_hold ) { - is_held = 1; - goto done; - } +int sca_call_is_held(sip_msg_t *msg) { + sdp_session_cell_t *session; + sdp_stream_cell_t *stream; + int n_sess; + int n_str; + int is_held = 0; + int rc; + + rc = parse_sdp(msg); + if (rc < 0) { + LM_ERR( "sca_call_is_held: parse_sdp body failed\n" ); + return (0); + } else if (rc > 0) { + LM_DBG( "sca_call_is_held: parse_sdp returned %d, no SDP body\n", rc ); + return (0); + } + + /* Cf. modules_k/textops's exported is_audio_on_hold */ + for (n_sess = 0, session = get_sdp_session(msg, n_sess); session != NULL; + n_sess++, session = get_sdp_session(msg, n_sess)) { + + for (n_str = 0, stream = get_sdp_stream(msg, n_sess, n_str); + stream != NULL; + n_str++, stream = get_sdp_stream(msg, n_sess, n_str)) { + if (stream->is_on_hold) { + is_held = 1; + goto done; + } + } } - } -done: - return( is_held ); + done: return (is_held); } diff --git a/modules/sca/sca_util.h b/modules/sca/sca_util.h index eb1664d08f7..89b9eb044b1 100644 --- a/modules/sca/sca_util.h +++ b/modules/sca/sca_util.h @@ -23,44 +23,44 @@ #include "sca_common.h" enum { - SCA_AOR_TYPE_AUTO = (1 << 0), - SCA_AOR_TYPE_UAC = (1 << 1), - SCA_AOR_TYPE_UAS = (1 << 2), + SCA_AOR_TYPE_AUTO = (1 << 0), + SCA_AOR_TYPE_UAC = (1 << 1), + SCA_AOR_TYPE_UAS = (1 << 2), }; /* get method, regardless of whether message is a request or response */ -int sca_get_msg_method( sip_msg_t * ); +int sca_get_msg_method(sip_msg_t *); /* populate a str pointer with contact uri from a SIP request or response */ -int sca_get_msg_contact_uri( sip_msg_t *, str * ); +int sca_get_msg_contact_uri(sip_msg_t *, str *); /* convenient extraction of cseq number from Cseq header */ -int sca_get_msg_cseq_number( sip_msg_t * ); +int sca_get_msg_cseq_number(sip_msg_t *); /* convenient extraction of cseq method from Cseq header */ -int sca_get_msg_cseq_method( sip_msg_t * ); +int sca_get_msg_cseq_method(sip_msg_t *); /* convenient From header parsing and extraction */ -int sca_get_msg_from_header( sip_msg_t *, struct to_body ** ); +int sca_get_msg_from_header(sip_msg_t *, struct to_body **); /* convenient To header parsing and extraction */ -int sca_get_msg_to_header( sip_msg_t *, struct to_body ** ); +int sca_get_msg_to_header(sip_msg_t *, struct to_body **); /* count number of characters requiring escape as defined by escape_common */ -int sca_uri_display_escapes_count( str * ); +int sca_uri_display_escapes_count(str *); /* convenient AoR extraction from sip: URIs */ -int sca_uri_extract_aor( str *, str * ); +int sca_uri_extract_aor(str *, str *); /* convenient AoR creation from a Contact URI and another AoR */ -int sca_uri_build_aor( str *, int, str *, str * ); +int sca_uri_build_aor(str *, int, str *, str *); -int sca_aor_create_from_info( str *, uri_type, str *, str *, str * ); +int sca_aor_create_from_info(str *, uri_type, str *, str *, str *); -int sca_create_canonical_aor_for_ua( sip_msg_t *, str *, int ); -int sca_create_canonical_aor( sip_msg_t *, str * ); +int sca_create_canonical_aor_for_ua(sip_msg_t *, str *, int); +int sca_create_canonical_aor(sip_msg_t *, str *); /* convenient call hold detection */ -int sca_call_is_held( sip_msg_t * ); +int sca_call_is_held(sip_msg_t *); #endif /* SCA_UTIL_H */