diff options
author | eliel <eliel@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-07-08 14:48:42 +0000 |
---|---|---|
committer | eliel <eliel@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-07-08 14:48:42 +0000 |
commit | 7a61a43adbc1ef91229e7757f6ac88619adff202 (patch) | |
tree | 80476efaba3fcc99c7526182d9ad935264c099eb | |
parent | f28601a4b00a7b39fd8d3826b02c98e4b868e476 (diff) |
Implement AstData API data providers as part of the GSOC 2010 project,
midterm evaluation.
Review: https://reviewboard.asterisk.org/r/757/
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@274727 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | apps/app_meetme.c | 180 | ||||
-rw-r--r-- | apps/app_queue.c | 89 | ||||
-rw-r--r-- | apps/app_voicemail.c | 107 | ||||
-rw-r--r-- | channels/chan_agent.c | 88 | ||||
-rw-r--r-- | channels/chan_dahdi.c | 233 | ||||
-rw-r--r-- | channels/chan_iax2.c | 329 | ||||
-rw-r--r-- | channels/chan_sip.c | 242 | ||||
-rw-r--r-- | include/asterisk/cdr.h | 12 | ||||
-rw-r--r-- | include/asterisk/channel.h | 3 | ||||
-rw-r--r-- | include/asterisk/data.h | 243 | ||||
-rw-r--r-- | include/asterisk/indications.h | 10 | ||||
-rw-r--r-- | main/cdr.c | 48 | ||||
-rw-r--r-- | main/channel.c | 301 | ||||
-rw-r--r-- | main/data.c | 494 | ||||
-rw-r--r-- | main/indications.c | 41 | ||||
-rw-r--r-- | main/pbx.c | 52 | ||||
-rw-r--r-- | res/res_odbc.c | 101 |
17 files changed, 2244 insertions, 329 deletions
diff --git a/apps/app_meetme.c b/apps/app_meetme.c index 36c29cfe4..be1d36375 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -59,6 +59,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/dial.h" #include "asterisk/causes.h" #include "asterisk/paths.h" +#include "asterisk/data.h" +#include "asterisk/test.h" #include "enter.h" #include "leave.h" @@ -1775,6 +1777,7 @@ static int conf_free(struct ast_conference *conf) } ast_mutex_destroy(&conf->announcelistlock); } + if (conf->origframe) ast_frfree(conf->origframe); if (conf->lchan) @@ -1786,6 +1789,7 @@ static int conf_free(struct ast_conference *conf) if (conf->recordingfilename) { ast_free(conf->recordingfilename); } + if (conf->recordingformat) { ast_free(conf->recordingformat); } @@ -6723,6 +6727,170 @@ static int load_config(int reload) return sla_load_config(0); } +#define MEETME_DATA_EXPORT(MEMBER) \ + MEMBER(ast_conference, confno, AST_DATA_STRING) \ + MEMBER(ast_conference, dahdiconf, AST_DATA_INTEGER) \ + MEMBER(ast_conference, users, AST_DATA_INTEGER) \ + MEMBER(ast_conference, markedusers, AST_DATA_INTEGER) \ + MEMBER(ast_conference, maxusers, AST_DATA_INTEGER) \ + MEMBER(ast_conference, isdynamic, AST_DATA_BOOLEAN) \ + MEMBER(ast_conference, locked, AST_DATA_BOOLEAN) \ + MEMBER(ast_conference, recordingfilename, AST_DATA_STRING) \ + MEMBER(ast_conference, recordingformat, AST_DATA_STRING) \ + MEMBER(ast_conference, pin, AST_DATA_PASSWORD) \ + MEMBER(ast_conference, pinadmin, AST_DATA_PASSWORD) \ + MEMBER(ast_conference, start, AST_DATA_TIMESTAMP) \ + MEMBER(ast_conference, endtime, AST_DATA_TIMESTAMP) + +AST_DATA_STRUCTURE(ast_conference, MEETME_DATA_EXPORT); + +#define MEETME_USER_DATA_EXPORT(MEMBER) \ + MEMBER(ast_conf_user, user_no, AST_DATA_INTEGER) \ + MEMBER(ast_conf_user, talking, AST_DATA_BOOLEAN) \ + MEMBER(ast_conf_user, dahdichannel, AST_DATA_BOOLEAN) \ + MEMBER(ast_conf_user, jointime, AST_DATA_TIMESTAMP) \ + MEMBER(ast_conf_user, kicktime, AST_DATA_TIMESTAMP) \ + MEMBER(ast_conf_user, timelimit, AST_DATA_MILLISECONDS) \ + MEMBER(ast_conf_user, play_warning, AST_DATA_MILLISECONDS) \ + MEMBER(ast_conf_user, warning_freq, AST_DATA_MILLISECONDS) + +AST_DATA_STRUCTURE(ast_conf_user, MEETME_USER_DATA_EXPORT); + +/*! + * \internal + * \brief Implements the meetme data provider. + */ +static int meetme_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct ast_conference *cnf; + struct ast_conf_user *user; + struct ast_data *data_meetme, *data_meetme_users, *data_meetme_user; + struct ast_data *data_meetme_user_channel, *data_meetme_user_volume; + + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, cnf, list) { + data_meetme = ast_data_add_node(data_root, "meetme"); + if (!data_meetme) { + continue; + } + + ast_data_add_structure(ast_conference, data_meetme, cnf); + + if (!AST_LIST_EMPTY(&cnf->userlist)) { + data_meetme_users = ast_data_add_node(data_meetme, "users"); + if (!data_meetme_users) { + ast_data_remove_node(data_root, data_meetme); + continue; + } + + AST_LIST_TRAVERSE(&cnf->userlist, user, list) { + data_meetme_user = ast_data_add_node(data_meetme_users, "user"); + if (!data_meetme_user) { + continue; + } + /* user structure. */ + ast_data_add_structure(ast_conf_user, data_meetme_user, user); + + /* user's channel */ + data_meetme_user_channel = ast_data_add_node(data_meetme_user, "channel"); + if (!data_meetme_user_channel) { + continue; + } + + ast_channel_data_add_structure(data_meetme_user_channel, user->chan, 1); + + /* volume structure */ + data_meetme_user_volume = ast_data_add_node(data_meetme_user, "listen-volume"); + if (!data_meetme_user_volume) { + continue; + } + ast_data_add_int(data_meetme_user_volume, "desired", user->listen.desired); + ast_data_add_int(data_meetme_user_volume, "actual", user->listen.actual); + + data_meetme_user_volume = ast_data_add_node(data_meetme_user, "talk-volume"); + if (!data_meetme_user_volume) { + continue; + } + ast_data_add_int(data_meetme_user_volume, "desired", user->talk.desired); + ast_data_add_int(data_meetme_user_volume, "actual", user->talk.actual); + } + } + + if (!ast_data_search_match(search, data_meetme)) { + ast_data_remove_node(data_root, data_meetme); + } + } + AST_LIST_UNLOCK(&confs); + + return 0; +} + +static const struct ast_data_handler meetme_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = meetme_data_provider_get +}; + +static const struct ast_data_entry meetme_data_providers[] = { + AST_DATA_ENTRY("asterisk/application/meetme/list", &meetme_data_provider), +}; + +#ifdef TEST_FRAMEWORK +AST_TEST_DEFINE(test_meetme_data_provider) +{ + struct ast_channel *chan; + struct ast_conference *cnf; + struct ast_data *node; + struct ast_data_query query = { + .path = "/asterisk/application/meetme/list", + .search = "list/meetme/confno=9898" + }; + + switch (cmd) { + case TEST_INIT: + info->name = "meetme_get_data_test"; + info->category = "main/data/app_meetme/list"; + info->summary = "Meetme data provider unit test"; + info->description = + "Tests whether the Meetme data provider implementation works as expected."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL, NULL, 0, 0, "MeetMeTest"); + if (!chan) { + return AST_TEST_FAIL; + } + + cnf = build_conf("9898", "", "1234", 1, 1, 1, chan); + if (!cnf) { + ast_hangup(chan); + return AST_TEST_FAIL; + } + + node = ast_data_get(&query); + if (!node) { + dispose_conf(cnf); + ast_hangup(chan); + return AST_TEST_FAIL; + } + + if (strcmp(ast_data_retrieve_string(node, "meetme/confno"), "9898")) { + dispose_conf(cnf); + ast_hangup(chan); + ast_data_free(node); + return AST_TEST_FAIL; + } + + ast_data_free(node); + dispose_conf(cnf); + ast_hangup(chan); + + return AST_TEST_PASS; +} +#endif + static int unload_module(void) { int res = 0; @@ -6738,6 +6906,11 @@ static int unload_module(void) res |= ast_unregister_application(slastation_app); res |= ast_unregister_application(slatrunk_app); +#ifdef TEST_FRAMEWORK + AST_TEST_UNREGISTER(test_meetme_data_provider); +#endif + ast_data_unregister(NULL); + ast_devstate_prov_del("Meetme"); ast_devstate_prov_del("SLA"); @@ -6749,6 +6922,8 @@ static int unload_module(void) return res; } + + static int load_module(void) { int res = 0; @@ -6766,6 +6941,11 @@ static int load_module(void) res |= ast_register_application_xml(slastation_app, sla_station_exec); res |= ast_register_application_xml(slatrunk_app, sla_trunk_exec); +#ifdef TEST_FRAMEWORK + AST_TEST_REGISTER(test_meetme_data_provider); +#endif + ast_data_register_multiple(meetme_data_providers, ARRAY_LEN(meetme_data_providers)); + res |= ast_devstate_prov_add("Meetme", meetmestate); res |= ast_devstate_prov_add("SLA", sla_state); diff --git a/apps/app_queue.c b/apps/app_queue.c index 4c7c42212..e91e13a06 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -7762,7 +7762,7 @@ static struct ast_cli_entry cli_queue[] = { MEMBER(call_queue, sound_reporthold, AST_DATA_STRING) \ MEMBER(call_queue, dead, AST_DATA_BOOLEAN) \ MEMBER(call_queue, eventwhencalled, AST_DATA_BOOLEAN) \ - MEMBER(call_queue, ringinuse, AST_DATA_INTEGER) \ + MEMBER(call_queue, ringinuse, AST_DATA_BOOLEAN) \ MEMBER(call_queue, setinterfacevar, AST_DATA_BOOLEAN) \ MEMBER(call_queue, setqueuevar, AST_DATA_BOOLEAN) \ MEMBER(call_queue, setqueueentryvar, AST_DATA_BOOLEAN) \ @@ -7770,20 +7770,18 @@ static struct ast_cli_entry cli_queue[] = { MEMBER(call_queue, wrapped, AST_DATA_BOOLEAN) \ MEMBER(call_queue, timeoutrestart, AST_DATA_BOOLEAN) \ MEMBER(call_queue, announceholdtime, AST_DATA_INTEGER) \ - MEMBER(call_queue, announceposition, AST_DATA_INTEGER) \ - MEMBER(call_queue, strategy, AST_DATA_INTEGER) \ MEMBER(call_queue, maskmemberstatus, AST_DATA_BOOLEAN) \ MEMBER(call_queue, realtime, AST_DATA_BOOLEAN) \ MEMBER(call_queue, found, AST_DATA_BOOLEAN) \ MEMBER(call_queue, announcepositionlimit, AST_DATA_INTEGER) \ - MEMBER(call_queue, announcefrequency, AST_DATA_INTEGER) \ - MEMBER(call_queue, minannouncefrequency, AST_DATA_INTEGER) \ - MEMBER(call_queue, periodicannouncefrequency, AST_DATA_INTEGER) \ + MEMBER(call_queue, announcefrequency, AST_DATA_SECONDS) \ + MEMBER(call_queue, minannouncefrequency, AST_DATA_SECONDS) \ + MEMBER(call_queue, periodicannouncefrequency, AST_DATA_SECONDS) \ MEMBER(call_queue, numperiodicannounce, AST_DATA_INTEGER) \ MEMBER(call_queue, randomperiodicannounce, AST_DATA_INTEGER) \ - MEMBER(call_queue, roundingseconds, AST_DATA_INTEGER) \ - MEMBER(call_queue, holdtime, AST_DATA_INTEGER) \ - MEMBER(call_queue, talktime, AST_DATA_INTEGER) \ + MEMBER(call_queue, roundingseconds, AST_DATA_SECONDS) \ + MEMBER(call_queue, holdtime, AST_DATA_SECONDS) \ + MEMBER(call_queue, talktime, AST_DATA_SECONDS) \ MEMBER(call_queue, callscompleted, AST_DATA_INTEGER) \ MEMBER(call_queue, callsabandoned, AST_DATA_INTEGER) \ MEMBER(call_queue, servicelevel, AST_DATA_INTEGER) \ @@ -7792,9 +7790,9 @@ static struct ast_cli_entry cli_queue[] = { MEMBER(call_queue, montype, AST_DATA_INTEGER) \ MEMBER(call_queue, count, AST_DATA_INTEGER) \ MEMBER(call_queue, maxlen, AST_DATA_INTEGER) \ - MEMBER(call_queue, wrapuptime, AST_DATA_INTEGER) \ - MEMBER(call_queue, retry, AST_DATA_INTEGER) \ - MEMBER(call_queue, timeout, AST_DATA_INTEGER) \ + MEMBER(call_queue, wrapuptime, AST_DATA_SECONDS) \ + MEMBER(call_queue, retry, AST_DATA_SECONDS) \ + MEMBER(call_queue, timeout, AST_DATA_SECONDS) \ MEMBER(call_queue, weight, AST_DATA_INTEGER) \ MEMBER(call_queue, autopause, AST_DATA_INTEGER) \ MEMBER(call_queue, timeoutpriority, AST_DATA_INTEGER) \ @@ -7856,19 +7854,12 @@ AST_DATA_STRUCTURE(queue_ent, DATA_EXPORT_QUEUE_ENT); static void queues_data_provider_get_helper(const struct ast_data_search *search, struct ast_data *data_root, struct call_queue *queue) { - int member_notmatch, caller_notmatch, caller_channel_notmatch; struct ao2_iterator im; struct member *member; struct queue_ent *qe; - struct ast_data *data_queue, *data_members = NULL; + struct ast_data *data_queue, *data_members = NULL, *enum_node; struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel; - /* compare the search pattern. */ - if (ast_data_search_cmp_structure(search, call_queue, queue, "queue")) { - /* this doesn't match! continue! */ - return; - } - data_queue = ast_data_add_node(data_root, "queue"); if (!data_queue) { return; @@ -7876,16 +7867,35 @@ static void queues_data_provider_get_helper(const struct ast_data_search *search ast_data_add_structure(call_queue, data_queue, queue); - member_notmatch = ast_data_search_has_condition(search, "queue/members/member"); + ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy)); + + /* announce position */ + enum_node = ast_data_add_node(data_queue, "announceposition"); + if (!enum_node) { + return; + } + switch (queue->announceposition) { + case ANNOUNCEPOSITION_LIMIT: + ast_data_add_str(enum_node, "text", "limit"); + break; + case ANNOUNCEPOSITION_MORE_THAN: + ast_data_add_str(enum_node, "text", "more"); + break; + case ANNOUNCEPOSITION_YES: + ast_data_add_str(enum_node, "text", "yes"); + break; + case ANNOUNCEPOSITION_NO: + ast_data_add_str(enum_node, "text", "no"); + break; + default: + ast_data_add_str(enum_node, "text", "unknown"); + break; + } + ast_data_add_int(enum_node, "value", queue->announceposition); + /* add queue members */ im = ao2_iterator_init(queue->members, 0); while ((member = ao2_iterator_next(&im))) { - /* compare the member structure. */ - if (!ast_data_search_cmp_structure(search, member, member, - "queue/members/member")) { - member_notmatch = 0; - } - if (!data_members) { data_members = ast_data_add_node(data_queue, "members"); if (!data_members) { @@ -7905,28 +7915,9 @@ static void queues_data_provider_get_helper(const struct ast_data_search *search ao2_ref(member, -1); } - if (member_notmatch) { - ast_data_remove_node(data_root, data_queue); - return; - } - - caller_notmatch = ast_data_search_has_condition(search, "queue/callers/caller"); - caller_channel_notmatch = ast_data_search_has_condition(search, - "queue/callers/caller/channel"); /* include the callers inside the result. */ if (queue->head) { for (qe = queue->head; qe; qe = qe->next) { - /* compare the member structure. */ - if (!ast_data_search_cmp_structure(search, queue_ent, qe, - "queue/callers/caller")) { - caller_notmatch = 0; - } - - if (!ast_channel_data_cmp_structure(search, qe->chan, - "queue/callers/caller/channel")) { - caller_channel_notmatch = 0; - } - if (!data_callers) { data_callers = ast_data_add_node(data_queue, "callers"); if (!data_callers) { @@ -7947,12 +7938,12 @@ static void queues_data_provider_get_helper(const struct ast_data_search *search continue; } - ast_channel_data_add_structure(data_caller_channel, qe->chan); + ast_channel_data_add_structure(data_caller_channel, qe->chan, 1); } } /* if this queue doesn't match remove the added queue. */ - if (caller_notmatch || caller_channel_notmatch) { + if (!ast_data_search_match(search, data_queue)) { ast_data_remove_node(data_root, data_queue); } } @@ -8011,7 +8002,7 @@ static const struct ast_data_handler queues_data_provider = { }; static const struct ast_data_entry queue_data_providers[] = { - AST_DATA_ENTRY("asterisk/application/app_queue/queues", &queues_data_provider), + AST_DATA_ENTRY("asterisk/application/queue/list", &queues_data_provider), }; static int unload_module(void) diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index 892c865a9..4e7a26e79 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -10815,7 +10815,7 @@ static struct ast_cli_entry cli_voicemail[] = { #define DATA_EXPORT_VM_USERS(USER) \ USER(ast_vm_user, context, AST_DATA_STRING) \ USER(ast_vm_user, mailbox, AST_DATA_STRING) \ - USER(ast_vm_user, password, AST_DATA_STRING) \ + USER(ast_vm_user, password, AST_DATA_PASSWORD) \ USER(ast_vm_user, fullname, AST_DATA_STRING) \ USER(ast_vm_user, email, AST_DATA_STRING) \ USER(ast_vm_user, emailsubject, AST_DATA_STRING) \ @@ -10843,7 +10843,7 @@ static struct ast_cli_entry cli_voicemail[] = { #define DATA_EXPORT_VM_USERS(USER) \ USER(ast_vm_user, context, AST_DATA_STRING) \ USER(ast_vm_user, mailbox, AST_DATA_STRING) \ - USER(ast_vm_user, password, AST_DATA_STRING) \ + USER(ast_vm_user, password, AST_DATA_PASSWORD) \ USER(ast_vm_user, fullname, AST_DATA_STRING) \ USER(ast_vm_user, email, AST_DATA_STRING) \ USER(ast_vm_user, emailsubject, AST_DATA_STRING) \ @@ -10875,50 +10875,6 @@ AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS); AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES); -#ifdef IMAP_STORAGE - #define DATA_EXPORT_VM_STATES(STATE) \ - STATE(vm_state, curbox, AST_DATA_STRING) \ - STATE(vm_state, username, AST_DATA_STRING) \ - STATE(vm_state, context, AST_DATA_STRING) \ - STATE(vm_state, curdir, AST_DATA_STRING) \ - STATE(vm_state, vmbox, AST_DATA_STRING) \ - STATE(vm_state, fn, AST_DATA_STRING) \ - STATE(vm_state, intro, AST_DATA_STRING) \ - STATE(vm_state, curmsg, AST_DATA_INTEGER) \ - STATE(vm_state, lastmsg, AST_DATA_INTEGER) \ - STATE(vm_state, newmessages, AST_DATA_INTEGER) \ - STATE(vm_state, oldmessages, AST_DATA_INTEGER) \ - STATE(vm_state, urgentmessages, AST_DATA_INTEGER) \ - STATE(vm_state, starting, AST_DATA_INTEGER) \ - STATE(vm_state, repeats, AST_DATA_INTEGER) \ - STATE(vm_state, updated, AST_DATA_INTEGER) \ - STATE(vm_state, msgArray, AST_DATA_CONTAINER) \ - STATE(vm_state, vmArrayIndex, AST_DATA_INTEGER) \ - STATE(vm_state, imapuser, AST_DATA_STRING) \ - STATE(vm_state, interactive, AST_DATA_INTEGER) \ - STATE(vm_state, introfn, AST_DATA_STRING) \ - STATE(vm_state, quota_limit, AST_DATA_UNSIGNED_INTEGER) \ - STATE(vm_state, quota_usage, AST_DATA_UNSIGNED_INTEGER) -#else - #define DATA_EXPORT_VM_STATES(STATE) \ - STATE(vm_state, curbox, AST_DATA_STRING) \ - STATE(vm_state, username, AST_DATA_STRING) \ - STATE(vm_state, context, AST_DATA_STRING) \ - STATE(vm_state, curdir, AST_DATA_STRING) \ - STATE(vm_state, vmbox, AST_DATA_STRING) \ - STATE(vm_state, fn, AST_DATA_STRING) \ - STATE(vm_state, intro, AST_DATA_STRING) \ - STATE(vm_state, curmsg, AST_DATA_INTEGER) \ - STATE(vm_state, lastmsg, AST_DATA_INTEGER) \ - STATE(vm_state, newmessages, AST_DATA_INTEGER) \ - STATE(vm_state, oldmessages, AST_DATA_INTEGER) \ - STATE(vm_state, urgentmessages, AST_DATA_INTEGER) \ - STATE(vm_state, starting, AST_DATA_INTEGER) \ - STATE(vm_state, repeats, AST_DATA_INTEGER) -#endif - -AST_DATA_STRUCTURE(vm_state, DATA_EXPORT_VM_STATES); - /*! * \internal * \brief Add voicemail user to the data_root. @@ -10926,32 +10882,22 @@ AST_DATA_STRUCTURE(vm_state, DATA_EXPORT_VM_STATES); * \param[in] data_root The main result node. * \param[in] user The voicemail user. */ -static void vm_users_data_provider_get_helper(const struct ast_data_search *search, +static int vm_users_data_provider_get_helper(const struct ast_data_search *search, struct ast_data *data_root, struct ast_vm_user *user) { struct ast_data *data_user, *data_zone; -#ifdef IMAP_STORAGE struct ast_data *data_state; - struct vm_state *state; -#endif struct vm_zone *zone = NULL; - - /* check the search pattern to make sure it's valid to add it */ - if (ast_data_search_cmp_structure(search, ast_vm_user, user, "user")) { - return; - } + int urgentmsg = 0, newmsg = 0, oldmsg = 0; + char ext_context[256] = ""; data_user = ast_data_add_node(data_root, "user"); if (!data_user) { - return; + return -1; } ast_data_add_structure(ast_vm_user, data_user, user); -#ifdef IMAP_STORAGE - state = get_vm_state_by_mailbox(user->mailbox, user->context, 0); -#endif - AST_LIST_LOCK(&zones); AST_LIST_TRAVERSE(&zones, zone, list) { if (!strcmp(zone->name, user->zonetag)) { @@ -10960,35 +10906,30 @@ static void vm_users_data_provider_get_helper(const struct ast_data_search *sear } AST_LIST_UNLOCK(&zones); - /* TODO: Should a user's vm state be accessible without compiling in - * IMAP support? */ - - if ( -#ifdef IMAP_STORAGE - !ast_data_search_cmp_structure(search, vm_state, state, "user/state") || -#endif - (zone && !ast_data_search_cmp_structure(search, vm_zone, - zone, "user/zone"))) { - ast_data_remove_node(data_root, data_user); - return; - } - -#ifdef IMAP_STORAGE + /* state */ data_state = ast_data_add_node(data_user, "state"); - ast_data_add_structure(vm_state, data_state, state); - ast_data_add_int(data_state, "deleted", *(state->deleted)); - ast_data_add_int(data_state, "heard", *(state->heard)); -#endif + if (!data_state) { + return -1; + } + snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context); + inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg); + ast_data_add_int(data_state, "urgentmsg", urgentmsg); + ast_data_add_int(data_state, "newmsg", newmsg); + ast_data_add_int(data_state, "oldmsg", oldmsg); if (zone) { data_zone = ast_data_add_node(data_user, "zone"); ast_data_add_structure(vm_zone, data_zone, zone); } - return; + if (!ast_data_search_match(search, data_user)) { + ast_data_remove_node(data_root, data_user); + } + + return 0; } -static int vm_data_provider_get(const struct ast_data_search *search, +static int vm_users_data_provider_get(const struct ast_data_search *search, struct ast_data *data_root) { struct ast_vm_user *user; @@ -11002,13 +10943,13 @@ static int vm_data_provider_get(const struct ast_data_search *search, return 0; } -static const struct ast_data_handler vm_data_provider = { +static const struct ast_data_handler vm_users_data_provider = { .version = AST_DATA_HANDLER_VERSION, - .get = vm_data_provider_get + .get = vm_users_data_provider_get }; static const struct ast_data_entry vm_data_providers[] = { - AST_DATA_ENTRY("asterisk/application/app_voicemail/voicemail", &vm_data_provider) + AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider) }; static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub) diff --git a/channels/chan_agent.c b/channels/chan_agent.c index fd05bed19..dfbf2284d 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/monitor.h" #include "asterisk/stringfields.h" #include "asterisk/event.h" +#include "asterisk/data.h" /*** DOCUMENTATION <application name="AgentLogin" language="en_US"> @@ -278,6 +279,19 @@ struct agent_pvt { AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */ }; +#define DATA_EXPORT_AGENT(MEMBER) \ + MEMBER(agent_pvt, autologoff, AST_DATA_INTEGER) \ + MEMBER(agent_pvt, ackcall, AST_DATA_BOOLEAN) \ + MEMBER(agent_pvt, deferlogoff, AST_DATA_BOOLEAN) \ + MEMBER(agent_pvt, wrapuptime, AST_DATA_MILLISECONDS) \ + MEMBER(agent_pvt, acknowledged, AST_DATA_BOOLEAN) \ + MEMBER(agent_pvt, name, AST_DATA_STRING) \ + MEMBER(agent_pvt, password, AST_DATA_PASSWORD) \ + MEMBER(agent_pvt, acceptdtmf, AST_DATA_CHARACTER) \ + MEMBER(agent_pvt, logincallerid, AST_DATA_STRING) + +AST_DATA_STRUCTURE(agent_pvt, DATA_EXPORT_AGENT); + static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */ #define CHECK_FORMATS(ast, p) do { \ @@ -2321,6 +2335,75 @@ static struct ast_custom_function agent_function = { .read = function_agent, }; +/*! + * \internal + * \brief Callback used to generate the agents tree. + * \param[in] search The search pattern tree. + * \retval NULL on error. + * \retval non-NULL The generated tree. + */ +static int agents_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct agent_pvt *p; + struct ast_data *data_agent, *data_channel, *data_talkingto; + + AST_LIST_LOCK(&agents); + AST_LIST_TRAVERSE(&agents, p, list) { + data_agent = ast_data_add_node(data_root, "agent"); + if (!data_agent) { + continue; + } + + ast_mutex_lock(&p->lock); + if (!(p->pending)) { + ast_data_add_str(data_agent, "id", p->agent); + ast_data_add_structure(agent_pvt, data_agent, p); + + ast_data_add_bool(data_agent, "logged", p->chan ? 1 : 0); + if (p->chan) { + data_channel = ast_data_add_node(data_agent, "loggedon"); + if (!data_channel) { + ast_mutex_unlock(&p->lock); + ast_data_remove_node(data_root, data_agent); + continue; + } + ast_channel_data_add_structure(data_channel, p->chan, 0); + if (p->owner && ast_bridged_channel(p->owner)) { + data_talkingto = ast_data_add_node(data_agent, "talkingto"); + if (!data_talkingto) { + ast_mutex_unlock(&p->lock); + ast_data_remove_node(data_root, data_agent); + continue; + } + ast_channel_data_add_structure(data_talkingto, ast_bridged_channel(p->owner), 0); + } + } else { + ast_data_add_node(data_agent, "talkingto"); + ast_data_add_node(data_agent, "loggedon"); + } + ast_data_add_str(data_agent, "musiconhold", p->moh); + } + ast_mutex_unlock(&p->lock); + + /* if this agent doesn't match remove the added agent. */ + if (!ast_data_search_match(search, data_agent)) { + ast_data_remove_node(data_root, data_agent); + } + } + AST_LIST_UNLOCK(&agents); + + return 0; +} + +static const struct ast_data_handler agents_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = agents_data_provider_get +}; + +static const struct ast_data_entry agents_data_providers[] = { + AST_DATA_ENTRY("asterisk/channel/agent/list", &agents_data_provider), +}; /*! * \brief Initialize the Agents module. @@ -2343,6 +2426,9 @@ static int load_module(void) ast_register_application_xml(app, login_exec); ast_register_application_xml(app3, agentmonitoroutgoing_exec); + /* data tree */ + ast_data_register_multiple(agents_data_providers, ARRAY_LEN(agents_data_providers)); + /* Manager commands */ ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents); ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff); @@ -2376,6 +2462,8 @@ static int unload_module(void) /* Unregister manager command */ ast_manager_unregister("Agents"); ast_manager_unregister("AgentLogoff"); + /* Unregister the data tree */ + ast_data_unregister(NULL); /* Unregister channel */ AST_LIST_LOCK(&agents); /* Hangup all interfaces if they have an owner */ diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 7167010aa..e035b4bfe 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -117,6 +117,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/devicestate.h" #include "asterisk/paths.h" #include "asterisk/ccss.h" +#include "asterisk/data.h" /*** DOCUMENTATION <application name="DAHDISendKeypadFacility" language="en_US"> @@ -1204,6 +1205,77 @@ struct dahdi_pvt { char dialstring[AST_CHANNEL_NAME]; }; +#define DATA_EXPORT_DAHDI_PVT(MEMBER) \ + MEMBER(dahdi_pvt, cid_rxgain, AST_DATA_DOUBLE) \ + MEMBER(dahdi_pvt, rxgain, AST_DATA_DOUBLE) \ + MEMBER(dahdi_pvt, txgain, AST_DATA_DOUBLE) \ + MEMBER(dahdi_pvt, txdrc, AST_DATA_DOUBLE) \ + MEMBER(dahdi_pvt, rxdrc, AST_DATA_DOUBLE) \ + MEMBER(dahdi_pvt, adsi, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, answeronpolarityswitch, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, busydetect, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, callreturn, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, callwaiting, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, callwaitingcallerid, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, cancallforward, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, canpark, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, confirmanswer, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, destroy, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, didtdd, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, dialednone, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, dialing, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, digital, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, dnd, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, echobreak, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, echocanbridged, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, echocanon, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, faxhandled, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, usefaxbuffers, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, bufferoverrideinuse, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, firstradio, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, hanguponpolarityswitch, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, hardwaredtmf, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, hidecallerid, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, hidecalleridname, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, ignoredtmf, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, immediate, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, inalarm, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, mate, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, outgoing, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, permcallwaiting, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, priindication_oob, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, priexclusive, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, pulse, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, pulsedial, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, restartpending, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, restrictcid, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, threewaycalling, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, transfer, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, use_callerid, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, use_callingpres, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, usedistinctiveringdetection, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, dahditrcallerid, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, transfertobusy, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, mwimonitor_neon, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, mwimonitor_fsk, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, mwimonitor_rpas, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, mwimonitoractive, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, mwisendactive, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, inservice, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, locallyblocked, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, manages_span_alarms, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, use_smdi, AST_DATA_BOOLEAN) \ + MEMBER(dahdi_pvt, context, AST_DATA_STRING) \ + MEMBER(dahdi_pvt, defcontext, AST_DATA_STRING) \ + MEMBER(dahdi_pvt, exten, AST_DATA_STRING) \ + MEMBER(dahdi_pvt, language, AST_DATA_STRING) \ + MEMBER(dahdi_pvt, mohinterpret, AST_DATA_STRING) \ + MEMBER(dahdi_pvt, mohsuggest, AST_DATA_STRING) \ + MEMBER(dahdi_pvt, parkinglot, AST_DATA_STRING) + +AST_DATA_STRUCTURE(dahdi_pvt, DATA_EXPORT_DAHDI_PVT); + static struct dahdi_pvt *iflist = NULL; /*!< Main interface list start */ static struct dahdi_pvt *ifend = NULL; /*!< Main interface list end */ @@ -15823,6 +15895,7 @@ static int __unload_module(void) ast_manager_unregister("DAHDIDNDon"); ast_manager_unregister("DAHDIShowChannels"); ast_manager_unregister("DAHDIRestart"); + ast_data_unregister(NULL); ast_channel_unregister(&dahdi_tech); /* Hangup all interfaces if they have an owner */ @@ -17402,6 +17475,163 @@ static int setup_dahdi(int reload) return res; } +/*! + * \internal + * \brief Callback used to generate the dahdi status tree. + * \param[in] search The search pattern tree. + * \retval NULL on error. + * \retval non-NULL The generated tree. + */ +static int dahdi_status_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + int ctl, res, span; + struct ast_data *data_span, *data_alarms; + struct dahdi_spaninfo s; + + ctl = open("/dev/dahdi/ctl", O_RDWR); + if (ctl < 0) { + ast_log(LOG_ERROR, "No DAHDI found. Unable to open /dev/dahdi/ctl: %s\n", strerror(errno)); + return -1; + } + for (span = 1; span < DAHDI_MAX_SPANS; ++span) { + s.spanno = span; + res = ioctl(ctl, DAHDI_SPANSTAT, &s); + if (res) { + continue; + } + + data_span = ast_data_add_node(data_root, "span"); + if (!data_span) { + continue; + } + ast_data_add_str(data_span, "description", s.desc); + + /* insert the alarms status */ + data_alarms = ast_data_add_node(data_span, "alarms"); + if (!data_alarms) { + continue; + } + + ast_data_add_bool(data_alarms, "BLUE", s.alarms & DAHDI_ALARM_BLUE); + ast_data_add_bool(data_alarms, "YELLOW", s.alarms & DAHDI_ALARM_YELLOW); + ast_data_add_bool(data_alarms, "RED", s.alarms & DAHDI_ALARM_RED); + ast_data_add_bool(data_alarms, "LOOPBACK", s.alarms & DAHDI_ALARM_LOOPBACK); + ast_data_add_bool(data_alarms, "RECOVER", s.alarms & DAHDI_ALARM_RECOVER); + ast_data_add_bool(data_alarms, "NOTOPEN", s.alarms & DAHDI_ALARM_NOTOPEN); + + ast_data_add_int(data_span, "irqmisses", s.irqmisses); + ast_data_add_int(data_span, "bpviol", s.bpvcount); + ast_data_add_int(data_span, "crc4", s.crc4count); + ast_data_add_str(data_span, "framing", s.lineconfig & DAHDI_CONFIG_D4 ? "D4" : + s.lineconfig & DAHDI_CONFIG_ESF ? "ESF" : + s.lineconfig & DAHDI_CONFIG_CCS ? "CCS" : + "CAS"); + ast_data_add_str(data_span, "coding", s.lineconfig & DAHDI_CONFIG_B8ZS ? "B8ZS" : + s.lineconfig & DAHDI_CONFIG_HDB3 ? "HDB3" : + s.lineconfig & DAHDI_CONFIG_AMI ? "AMI" : + "Unknown"); + ast_data_add_str(data_span, "options", s.lineconfig & DAHDI_CONFIG_CRC4 ? + s.lineconfig & DAHDI_CONFIG_NOTOPEN ? "CRC4/YEL" : "CRC4" : + s.lineconfig & DAHDI_CONFIG_NOTOPEN ? "YEL" : ""); + ast_data_add_str(data_span, "lbo", lbostr[s.lbo]); + + /* if this span doesn't match remove it. */ + if (!ast_data_search_match(search, data_span)) { + ast_data_remove_node(data_root, data_span); + } + } + close(ctl); + + return 0; +} + +/*! + * \internal + * \brief Callback used to generate the dahdi channels tree. + * \param[in] search The search pattern tree. + * \retval NULL on error. + * \retval non-NULL The generated tree. + */ +static int dahdi_channels_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct dahdi_pvt *tmp; + struct ast_data *data_channel; + + ast_mutex_lock(&iflock); + for (tmp = iflist; tmp; tmp = tmp->next) { + data_channel = ast_data_add_node(data_root, "channel"); + if (!data_channel) { + continue; + } + + ast_data_add_structure(dahdi_pvt, data_channel, tmp); + + /* if this channel doesn't match remove it. */ + if (!ast_data_search_match(search, data_channel)) { + ast_data_remove_node(data_root, data_channel); + } + } + ast_mutex_unlock(&iflock); + + return 0; +} + +/*! + * \internal + * \brief Callback used to generate the dahdi channels tree. + * \param[in] search The search pattern tree. + * \retval NULL on error. + * \retval non-NULL The generated tree. + */ +static int dahdi_version_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + int pseudo_fd = -1; + struct dahdi_versioninfo vi = { + .version = "Unknown", + .echo_canceller = "Unknown" + }; + + if ((pseudo_fd = open("/dev/dahdi/ctl", O_RDONLY)) < 0) { + ast_log(LOG_ERROR, "Failed to open control file to get version.\n"); + return -1; + } + + if (ioctl(pseudo_fd, DAHDI_GETVERSION, &vi)) { + ast_log(LOG_ERROR, "Failed to get DAHDI version: %s\n", strerror(errno)); + } + + close(pseudo_fd); + + ast_data_add_str(data_root, "value", vi.version); + ast_data_add_str(data_root, "echocanceller", vi.echo_canceller); + + return 0; +} + +static const struct ast_data_handler dahdi_status_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = dahdi_status_data_provider_get +}; + +static const struct ast_data_handler dahdi_channels_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = dahdi_channels_data_provider_get +}; + +static const struct ast_data_handler dahdi_version_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = dahdi_version_data_provider_get +}; + +static const struct ast_data_entry dahdi_data_providers[] = { + AST_DATA_ENTRY("asterisk/channel/dahdi/status", &dahdi_status_data_provider), + AST_DATA_ENTRY("asterisk/channel/dahdi/channels", &dahdi_channels_data_provider), + AST_DATA_ENTRY("asterisk/channel/dahdi/version", &dahdi_version_data_provider) +}; + static int load_module(void) { int res; @@ -17467,7 +17697,8 @@ static int load_module(void) #endif ast_cli_register_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli)); - + /* register all the data providers */ + ast_data_register_multiple(dahdi_data_providers, ARRAY_LEN(dahdi_data_providers)); memset(round_robin, 0, sizeof(round_robin)); ast_manager_register_xml("DAHDITransfer", 0, action_transfer); ast_manager_register_xml("DAHDIHangup", 0, action_transferhangup); diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index d69194df4..ba482a065 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -90,6 +90,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/astobj2.h" #include "asterisk/timing.h" #include "asterisk/taskprocessor.h" +#include "asterisk/test.h" +#include "asterisk/data.h" #include "iax2.h" #include "iax2-parser.h" @@ -13852,6 +13854,125 @@ static struct ast_cli_entry cli_iax2[] = { #endif /* IAXTESTS */ }; +#ifdef TEST_FRAMEWORK +AST_TEST_DEFINE(test_iax2_peers_get) +{ + struct ast_data_query query = { + .path = "/asterisk/channel/iax2/peers", + .search = "peers/peer/name=test_peer_data_provider" + }; + struct ast_data *node; + struct iax2_peer *peer; + + switch (cmd) { + case TEST_INIT: + info->name = "iax2_peers_get_data_test"; + info->category = "main/data/iax2/peers"; + info->summary = "IAX2 peers data providers unit test"; + info->description = + "Tests whether the IAX2 peers data provider implementation works as expected."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* build a test peer */ + peer = build_peer("test_peer_data_provider", NULL, NULL, 0); + if (!peer) { + return AST_TEST_FAIL; + } + peer->expiry= 1010; + ao2_link(peers, peer); + + node = ast_data_get(&query); + if (!node) { + ao2_unlink(peers, peer); + peer_unref(peer); + return AST_TEST_FAIL; + } + + /* check returned data node. */ + if (strcmp(ast_data_retrieve_string(node, "peer/name"), "test_peer_data_provider")) { + ao2_unlink(peers, peer); + peer_unref(peer); + ast_data_free(node); + return AST_TEST_FAIL; + } + + if (ast_data_retrieve_int(node, "peer/expiry") != 1010) { + ao2_unlink(peers, peer); + peer_unref(peer); + ast_data_free(node); + return AST_TEST_FAIL; + } + + /* release resources */ + ast_data_free(node); + + ao2_unlink(peers, peer); + peer_unref(peer); + + return AST_TEST_PASS; +} + +AST_TEST_DEFINE(test_iax2_users_get) +{ + struct ast_data_query query = { + .path = "/asterisk/channel/iax2/users", + .search = "users/user/name=test_user_data_provider" + }; + struct ast_data *node; + struct iax2_user *user; + + switch (cmd) { + case TEST_INIT: + info->name = "iax2_users_get_data_test"; + info->category = "main/data/iax2/users"; + info->summary = "IAX2 users data providers unit test"; + info->description = + "Tests whether the IAX2 users data provider implementation works as expected."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + user = build_user("test_user_data_provider", NULL, NULL, 0); + if (!user) { + return AST_TEST_FAIL; + } + user->amaflags = 1010; + ao2_link(users, user); + + node = ast_data_get(&query); + if (!node) { + ao2_unlink(users, user); + user_unref(user); + return AST_TEST_FAIL; + } + + if (strcmp(ast_data_retrieve_string(node, "user/name"), "test_user_data_provider")) { + ao2_unlink(users, user); + user_unref(user); + ast_data_free(node); + return AST_TEST_FAIL; + } + + if (ast_data_retrieve_int(node, "user/amaflags") != 1010) { + ao2_unlink(users, user); + user_unref(user); + ast_data_free(node); + return AST_TEST_FAIL; + } + + ast_data_free(node); + + ao2_unlink(users, user); + user_unref(user); + + return AST_TEST_PASS; +} +#endif + static void cleanup_thread_list(void *head) { AST_LIST_HEAD(iax2_thread_list, iax2_thread); @@ -13907,7 +14028,24 @@ static int __unload_module(void) ast_netsock_release(netsock); ast_netsock_release(outsock); - + for (x = 0; x < ARRAY_LEN(iaxs); x++) { + if (iaxs[x]) { + iax2_destroy(x); + } + } + ast_manager_unregister( "IAXpeers" ); + ast_manager_unregister( "IAXpeerlist" ); + ast_manager_unregister( "IAXnetstats" ); + ast_manager_unregister( "IAXregistry" ); + ast_unregister_application(papp); +#ifdef TEST_FRAMEWORK + AST_TEST_UNREGISTER(test_iax2_peers_get); + AST_TEST_UNREGISTER(test_iax2_users_get); +#endif + ast_data_unregister(NULL); + ast_cli_unregister_multiple(cli_iax2, ARRAY_LEN(cli_iax2)); + ast_unregister_switch(&iax2_switch); + ast_channel_unregister(&iax2_tech); delete_users(); iax_provision_unload(); reload_firmware(1); @@ -14048,6 +14186,188 @@ container_fail: return AST_MODULE_LOAD_FAILURE; } + +#define DATA_EXPORT_IAX2_PEER(MEMBER) \ + MEMBER(iax2_peer, name, AST_DATA_STRING) \ + MEMBER(iax2_peer, username, AST_DATA_STRING) \ + MEMBER(iax2_peer, secret, AST_DATA_PASSWORD) \ + MEMBER(iax2_peer, dbsecret, AST_DATA_PASSWORD) \ + MEMBER(iax2_peer, outkey, AST_DATA_STRING) \ + MEMBER(iax2_peer, regexten, AST_DATA_STRING) \ + MEMBER(iax2_peer, context, AST_DATA_STRING) \ + MEMBER(iax2_peer, peercontext, AST_DATA_STRING) \ + MEMBER(iax2_peer, mailbox, AST_DATA_STRING) \ + MEMBER(iax2_peer, mohinterpret, AST_DATA_STRING) \ + MEMBER(iax2_peer, mohsuggest, AST_DATA_STRING) \ + MEMBER(iax2_peer, inkeys, AST_DATA_STRING) \ + MEMBER(iax2_peer, cid_num, AST_DATA_STRING) \ + MEMBER(iax2_peer, cid_name, AST_DATA_STRING) \ + MEMBER(iax2_peer, zonetag, AST_DATA_STRING) \ + MEMBER(iax2_peer, parkinglot, AST_DATA_STRING) \ + MEMBER(iax2_peer, expiry, AST_DATA_SECONDS) \ + MEMBER(iax2_peer, callno, AST_DATA_INTEGER) \ + MEMBER(iax2_peer, lastms, AST_DATA_MILLISECONDS) \ + MEMBER(iax2_peer, maxms, AST_DATA_MILLISECONDS) \ + MEMBER(iax2_peer, pokefreqok, AST_DATA_MILLISECONDS) \ + MEMBER(iax2_peer, pokefreqnotok, AST_DATA_MILLISECONDS) \ + MEMBER(iax2_peer, historicms, AST_DATA_INTEGER) \ + MEMBER(iax2_peer, smoothing, AST_DATA_BOOLEAN) \ + MEMBER(iax2_peer, maxcallno, AST_DATA_INTEGER) + +AST_DATA_STRUCTURE(iax2_peer, DATA_EXPORT_IAX2_PEER); + +static int peers_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct ast_data *data_peer; + struct iax2_peer *peer; + struct ao2_iterator i; + char status[20]; + struct ast_str *encmethods = ast_str_alloca(256); + + i = ao2_iterator_init(peers, 0); + while ((peer = ao2_iterator_next(&i))) { + data_peer = ast_data_add_node(data_root, "peer"); + if (!data_peer) { + peer_unref(peer); + continue; + } + + ast_data_add_structure(iax2_peer, data_peer, peer); + + ast_data_add_codecs(data_peer, "codecs", peer->capability); + + peer_status(peer, status, sizeof(status)); + ast_data_add_str(data_peer, "status", status); + + ast_data_add_str(data_peer, "host", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : ""); + + ast_data_add_str(data_peer, "mask", ast_inet_ntoa(peer->mask)); + + ast_data_add_int(data_peer, "port", ntohs(peer->addr.sin_port)); + + ast_data_add_bool(data_peer, "trunk", ast_test_flag64(peer, IAX_TRUNK)); + + ast_data_add_bool(data_peer, "dynamic", ast_test_flag64(peer, IAX_DYNAMIC)); + + encmethods_to_str(peer->encmethods, encmethods); + ast_data_add_str(data_peer, "encryption", peer->encmethods ? ast_str_buffer(encmethods) : "no"); + + peer_unref(peer); + + if (!ast_data_search_match(search, data_peer)) { + ast_data_remove_node(data_root, data_peer); + } + } + ao2_iterator_destroy(&i); + + return 0; +} + +#define DATA_EXPORT_IAX2_USER(MEMBER) \ + MEMBER(iax2_user, name, AST_DATA_STRING) \ + MEMBER(iax2_user, dbsecret, AST_DATA_PASSWORD) \ + MEMBER(iax2_user, accountcode, AST_DATA_STRING) \ + MEMBER(iax2_user, mohinterpret, AST_DATA_STRING) \ + MEMBER(iax2_user, mohsuggest, AST_DATA_STRING) \ + MEMBER(iax2_user, inkeys, AST_DATA_STRING) \ + MEMBER(iax2_user, language, AST_DATA_STRING) \ + MEMBER(iax2_user, cid_num, AST_DATA_STRING) \ + MEMBER(iax2_user, cid_name, AST_DATA_STRING) \ + MEMBER(iax2_user, parkinglot, AST_DATA_STRING) \ + MEMBER(iax2_user, maxauthreq, AST_DATA_INTEGER) \ + MEMBER(iax2_user, curauthreq, AST_DATA_INTEGER) + +AST_DATA_STRUCTURE(iax2_user, DATA_EXPORT_IAX2_USER); + +static int users_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct ast_data *data_user, *data_authmethods, *data_enum_node; + struct iax2_user *user; + struct ao2_iterator i; + char auth[90]; + char *pstr = ""; + + i = ao2_iterator_init(users, 0); + while ((user = ao2_iterator_next(&i))) { + data_user = ast_data_add_node(data_root, "user"); + if (!data_user) { + user_unref(user); + continue; + } + + ast_data_add_structure(iax2_user, data_user, user); + + ast_data_add_codecs(data_user, "codecs", user->capability); + + if (!ast_strlen_zero(user->secret)) { + ast_copy_string(auth, user->secret, sizeof(auth)); + } else if (!ast_strlen_zero(user->inkeys)) { + snprintf(auth, sizeof(auth), "Key: %s", user->inkeys); + } else { + ast_copy_string(auth, "no secret", sizeof(auth)); + } + ast_data_add_password(data_user, "secret", auth); + + ast_data_add_str(data_user, "context", user->contexts ? user->contexts->context : DEFAULT_CONTEXT); + + /* authmethods */ + data_authmethods = ast_data_add_node(data_user, "authmethods"); + if (!data_authmethods) { + ast_data_remove_node(data_root, data_user); + continue; + } + ast_data_add_bool(data_authmethods, "rsa", user->authmethods & IAX_AUTH_RSA); + ast_data_add_bool(data_authmethods, "md5", user->authmethods & IAX_AUTH_MD5); + ast_data_add_bool(data_authmethods, "plaintext", user->authmethods & IAX_AUTH_PLAINTEXT); + + /* amaflags */ + data_enum_node = ast_data_add_node(data_user, "amaflags"); + if (!data_enum_node) { + ast_data_remove_node(data_root, data_user); + continue; + } + ast_data_add_int(data_enum_node, "value", user->amaflags); + ast_data_add_str(data_enum_node, "text", ast_cdr_flags2str(user->amaflags)); + + ast_data_add_bool(data_user, "access-control", user->ha ? 1 : 0); + + if (ast_test_flag64(user, IAX_CODEC_NOCAP)) { + pstr = "REQ only"; + } else if (ast_test_flag64(user, IAX_CODEC_NOPREFS)) { + pstr = "disabled"; + } else { + pstr = ast_test_flag64(user, IAX_CODEC_USER_FIRST) ? "caller" : "host"; + } + ast_data_add_str(data_user, "codec-preferences", pstr); + + user_unref(user); + + if (!ast_data_search_match(search, data_user)) { + ast_data_remove_node(data_root, data_user); + } + } + ao2_iterator_destroy(&i); + + return 0; +} + +static const struct ast_data_handler peers_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = peers_data_provider_get +}; + +static const struct ast_data_handler users_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = users_data_provider_get +}; + +static const struct ast_data_entry iax2_data_providers[] = { + AST_DATA_ENTRY("asterisk/channel/iax2/peers", &peers_data_provider), + AST_DATA_ENTRY("asterisk/channel/iax2/users", &users_data_provider), +}; + /*! \brief Load IAX2 module, load configuraiton ---*/ static int load_module(void) { @@ -14101,6 +14421,13 @@ static int load_module(void) } ast_netsock_init(outsock); +#ifdef TEST_FRAMEWORK + AST_TEST_REGISTER(test_iax2_peers_get); + AST_TEST_REGISTER(test_iax2_users_get); +#endif + /* Register AstData providers */ + ast_data_register_multiple(iax2_data_providers, ARRAY_LEN(iax2_data_providers)); + ast_cli_register_multiple(cli_iax2, ARRAY_LEN(cli_iax2)); ast_register_application_xml(papp, iax2_prov_app); diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 5d8b7b4e9..c65d85b9f 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -261,6 +261,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/event.h" #include "asterisk/stun.h" #include "asterisk/cel.h" +#include "asterisk/data.h" #include "asterisk/aoc.h" #include "sip/include/sip.h" #include "sip/include/globals.h" @@ -27475,6 +27476,234 @@ static void sip_unregister_tests(void) sip_dialplan_function_unregister_tests(); } +#ifdef TEST_FRAMEWORK +AST_TEST_DEFINE(test_sip_peers_get) +{ + struct sip_peer *peer; + struct ast_data *node; + struct ast_data_query query = { + .path = "/asterisk/channel/sip/peers", + .search = "peers/peer/name=test_peer_data_provider" + }; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_peers_get_data_test"; + info->category = "main/data/sip/peers"; + info->summary = "SIP peers data providers unit test"; + info->description = + "Tests whether the SIP peers data provider implementation works as expected."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* Create the peer that we will retrieve. */ + peer = build_peer("test_peer_data_provider", NULL, NULL, 0, 0); + if (!peer) { + return AST_TEST_FAIL; + } + peer->type = SIP_TYPE_USER; + peer->call_limit = 10; + ao2_link(peers, peer); + + /* retrieve the chan_sip/peers tree and check the created peer. */ + node = ast_data_get(&query); + if (!node) { + ao2_unlink(peers, peer); + ao2_ref(peer, -1); + return AST_TEST_FAIL; + } + + /* compare item. */ + if (strcmp(ast_data_retrieve_string(node, "peer/name"), "test_peer_data_provider")) { + ao2_unlink(peers, peer); + ao2_ref(peer, -1); + ast_data_free(node); + return AST_TEST_FAIL; + } + + if (strcmp(ast_data_retrieve_string(node, "peer/type"), "user")) { + ao2_unlink(peers, peer); + ao2_ref(peer, -1); + ast_data_free(node); + return AST_TEST_FAIL; + } + + if (ast_data_retrieve_int(node, "peer/call_limit") != 10) { + ao2_unlink(peers, peer); + ao2_ref(peer, -1); + ast_data_free(node); + return AST_TEST_FAIL; + } + + /* release resources */ + ast_data_free(node); + + ao2_unlink(peers, peer); + ao2_ref(peer, -1); + + return AST_TEST_PASS; +} + +#endif + +#define DATA_EXPORT_SIP_PEER(MEMBER) \ + MEMBER(sip_peer, name, AST_DATA_STRING) \ + MEMBER(sip_peer, secret, AST_DATA_PASSWORD) \ + MEMBER(sip_peer, md5secret, AST_DATA_PASSWORD) \ + MEMBER(sip_peer, remotesecret, AST_DATA_PASSWORD) \ + MEMBER(sip_peer, context, AST_DATA_STRING) \ + MEMBER(sip_peer, subscribecontext, AST_DATA_STRING) \ + MEMBER(sip_peer, username, AST_DATA_STRING) \ + MEMBER(sip_peer, accountcode, AST_DATA_STRING) \ + MEMBER(sip_peer, tohost, AST_DATA_STRING) \ + MEMBER(sip_peer, regexten, AST_DATA_STRING) \ + MEMBER(sip_peer, fromuser, AST_DATA_STRING) \ + MEMBER(sip_peer, fromdomain, AST_DATA_STRING) \ + MEMBER(sip_peer, fullcontact, AST_DATA_STRING) \ + MEMBER(sip_peer, cid_num, AST_DATA_STRING) \ + MEMBER(sip_peer, cid_name, AST_DATA_STRING) \ + MEMBER(sip_peer, vmexten, AST_DATA_STRING) \ + MEMBER(sip_peer, language, AST_DATA_STRING) \ + MEMBER(sip_peer, mohinterpret, AST_DATA_STRING) \ + MEMBER(sip_peer, mohsuggest, AST_DATA_STRING) \ + MEMBER(sip_peer, parkinglot, AST_DATA_STRING) \ + MEMBER(sip_peer, useragent, AST_DATA_STRING) \ + MEMBER(sip_peer, mwi_from, AST_DATA_STRING) \ + MEMBER(sip_peer, engine, AST_DATA_STRING) \ + MEMBER(sip_peer, unsolicited_mailbox, AST_DATA_STRING) \ + MEMBER(sip_peer, is_realtime, AST_DATA_BOOLEAN) \ + MEMBER(sip_peer, host_dynamic, AST_DATA_BOOLEAN) \ + MEMBER(sip_peer, autoframing, AST_DATA_BOOLEAN) \ + MEMBER(sip_peer, inUse, AST_DATA_INTEGER) \ + MEMBER(sip_peer, inRinging, AST_DATA_INTEGER) \ + MEMBER(sip_peer, onHold, AST_DATA_INTEGER) \ + MEMBER(sip_peer, call_limit, AST_DATA_INTEGER) \ + MEMBER(sip_peer, t38_maxdatagram, AST_DATA_INTEGER) \ + MEMBER(sip_peer, maxcallbitrate, AST_DATA_INTEGER) \ + MEMBER(sip_peer, rtptimeout, AST_DATA_SECONDS) \ + MEMBER(sip_peer, rtpholdtimeout, AST_DATA_SECONDS) \ + MEMBER(sip_peer, rtpkeepalive, AST_DATA_SECONDS) \ + MEMBER(sip_peer, lastms, AST_DATA_MILLISECONDS) \ + MEMBER(sip_peer, maxms, AST_DATA_MILLISECONDS) \ + MEMBER(sip_peer, qualifyfreq, AST_DATA_MILLISECONDS) \ + MEMBER(sip_peer, timer_t1, AST_DATA_MILLISECONDS) \ + MEMBER(sip_peer, timer_b, AST_DATA_MILLISECONDS) + +AST_DATA_STRUCTURE(sip_peer, DATA_EXPORT_SIP_PEER); + +static int peers_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct sip_peer *peer; + struct ao2_iterator i; + struct ast_data *data_peer, *data_peer_mailboxes = NULL, *data_peer_mailbox, *enum_node; + struct ast_data *data_sip_options; + int total_mailboxes, x; + struct sip_mailbox *mailbox; + + i = ao2_iterator_init(peers, 0); + while ((peer = ao2_iterator_next(&i))) { + ao2_lock(peer); + + data_peer = ast_data_add_node(data_root, "peer"); + if (!data_peer) { + ao2_unlock(peer); + ao2_ref(peer, -1); + continue; + } + + ast_data_add_structure(sip_peer, data_peer, peer); + + /* transfer mode */ + enum_node = ast_data_add_node(data_peer, "allowtransfer"); + if (!enum_node) { + continue; + } + ast_data_add_str(enum_node, "text", transfermode2str(peer->allowtransfer)); + ast_data_add_int(enum_node, "value", peer->allowtransfer); + + /* transports */ + ast_data_add_str(data_peer, "transports", get_transport_list(peer->transports)); + + /* peer type */ + if ((peer->type & SIP_TYPE_USER) && (peer->type & SIP_TYPE_PEER)) { + ast_data_add_str(data_peer, "type", "friend"); + } else if (peer->type & SIP_TYPE_PEER) { + ast_data_add_str(data_peer, "type", "peer"); + } else if (peer->type & SIP_TYPE_USER) { + ast_data_add_str(data_peer, "type", "user"); + } + + /* mailboxes */ + total_mailboxes = 0; + AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) { + if (!total_mailboxes) { + data_peer_mailboxes = ast_data_add_node(data_peer, "mailboxes"); + if (!data_peer_mailboxes) { + break; + } + total_mailboxes++; + } + + data_peer_mailbox = ast_data_add_node(data_peer_mailboxes, "mailbox"); + if (!data_peer_mailbox) { + continue; + } + ast_data_add_str(data_peer_mailbox, "mailbox", mailbox->mailbox); + ast_data_add_str(data_peer_mailbox, "context", mailbox->context); + } + + /* amaflags */ + enum_node = ast_data_add_node(data_peer, "amaflags"); + if (!enum_node) { + continue; + } + ast_data_add_int(enum_node, "value", peer->amaflags); + ast_data_add_str(enum_node, "text", ast_cdr_flags2str(peer->amaflags)); + + /* sip options */ + data_sip_options = ast_data_add_node(data_peer, "sipoptions"); + if (!data_sip_options) { + continue; + } + for (x = 0 ; x < ARRAY_LEN(sip_options); x++) { + ast_data_add_bool(data_sip_options, sip_options[x].text, peer->sipoptions & sip_options[x].id); + } + + /* callingpres */ + enum_node = ast_data_add_node(data_peer, "callingpres"); + if (!enum_node) { + continue; + } + ast_data_add_int(enum_node, "value", peer->callingpres); + ast_data_add_str(enum_node, "text", ast_describe_caller_presentation(peer->callingpres)); + + /* codecs */ + ast_data_add_codecs(data_peer, "codecs", peer->capability); + + if (!ast_data_search_match(search, data_peer)) { + ast_data_remove_node(data_root, data_peer); + } + + ao2_unlock(peer); + ao2_ref(peer, -1); + } + ao2_iterator_destroy(&i); + + return 0; +} + +static const struct ast_data_handler peers_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = peers_data_provider_get +}; + +static const struct ast_data_entry sip_data_providers[] = { + AST_DATA_ENTRY("asterisk/channel/sip/peers", &peers_data_provider), +}; + /*! \brief PBX load module - initialization */ static int load_module(void) { @@ -27521,6 +27750,13 @@ static int load_module(void) return AST_MODULE_LOAD_FAILURE; } +#ifdef TEST_FRAMEWORK + AST_TEST_REGISTER(test_sip_peers_get); +#endif + + /* Register AstData providers */ + ast_data_register_multiple(sip_data_providers, ARRAY_LEN(sip_data_providers)); + /* Register all CLI functions for SIP */ ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip)); @@ -27614,6 +27850,12 @@ static int unload_module(void) ast_unregister_application(app_sipaddheader); ast_unregister_application(app_sipremoveheader); +#ifdef TEST_FRAMEWORK + AST_TEST_UNREGISTER(test_sip_peers_get); +#endif + /* Unregister all the AstData providers */ + ast_data_unregister(NULL); + /* Unregister CLI commands */ ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip)); diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h index 4ca479728..5442fdb0c 100644 --- a/include/asterisk/cdr.h +++ b/include/asterisk/cdr.h @@ -28,6 +28,8 @@ #include <sys/time.h> +#include "asterisk/data.h" + /*! * \brief CDR Flags */ @@ -435,4 +437,14 @@ int ast_cdr_engine_init(void); /*! Submit any remaining CDRs and prepare for shutdown */ void ast_cdr_engine_term(void); +/*! + * \brief + * \param[in] tree Where to insert the cdr. + * \param[in] cdr The cdr structure to insert in 'tree'. + * \param[in] recur Go throw all the cdr levels. + * \retval <0 on error. + * \retval 0 on success. + */ +int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur); + #endif /* _ASTERISK_CDR_H */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 1e96aa66c..0ea2cb11a 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -2821,10 +2821,11 @@ int ast_channel_connected_line_macro(struct ast_channel *autoservice_chan, struc * \brief Insert into an astdata tree, the channel structure. * \param[in] tree The ast data tree. * \param[in] chan The channel structure to add to tree. + * \param[in] add_bridged Add the bridged channel to the structure. * \retval <0 on error. * \retval 0 on success. */ -int ast_channel_data_add_structure(struct ast_data *tree, struct ast_channel *chan); +int ast_channel_data_add_structure(struct ast_data *tree, struct ast_channel *chan, int add_bridged); /*! * \brief Compare to channel structures using the data api. diff --git a/include/asterisk/data.h b/include/asterisk/data.h index c52cee96a..6851bd511 100644 --- a/include/asterisk/data.h +++ b/include/asterisk/data.h @@ -25,6 +25,8 @@ #ifndef ASTERISK_DATA_H #define ASTERISK_DATA_H +#include "asterisk/frame.h" + /*! * \page AstDataRetrieval The Asterisk DATA retrieval API. * @@ -106,10 +108,6 @@ * .b = 20 * }; * - * if (ast_data_search_cmp_structure(search, test_structure, "test_node")) { - * return 0; - * } - * * internal_node = ast_data_add_node(root_node, "test_node"); * if (!internal_node) { * return -1; @@ -117,6 +115,10 @@ * * ast_data_add_structure(test_structure, internal_node, ts); * + * if (!ast_data_search_match(search, internal_node)) { + * ast_data_remove_node(root_node, internal_node); + * } + * * return 0; * } * @@ -189,7 +191,12 @@ enum ast_data_type { AST_DATA_DOUBLE, AST_DATA_BOOLEAN, AST_DATA_STRING, + AST_DATA_CHARACTER, + AST_DATA_PASSWORD, AST_DATA_IPADDR, + AST_DATA_TIMESTAMP, + AST_DATA_SECONDS, + AST_DATA_MILLISECONDS, AST_DATA_POINTER }; @@ -212,8 +219,13 @@ struct ast_data_retrieve { enum ast_data_type type; union { + char AST_DATA_CHARACTER; char *AST_DATA_STRING; + char *AST_DATA_PASSWORD; int AST_DATA_INTEGER; + unsigned int AST_DATA_TIMESTAMP; + unsigned int AST_DATA_SECONDS; + unsigned int AST_DATA_MILLISECONDS; double AST_DATA_DOUBLE; unsigned int AST_DATA_UNSIGNED_INTEGER; unsigned int AST_DATA_BOOLEAN; @@ -268,8 +280,13 @@ struct ast_data_mapping_structure { enum ast_data_type type; /*! \brief member getter. */ union { + char (*AST_DATA_CHARACTER)(void *ptr); char *(*AST_DATA_STRING)(void *ptr); + char *(*AST_DATA_PASSWORD)(void *ptr); int (*AST_DATA_INTEGER)(void *ptr); + int (*AST_DATA_TIMESTAMP)(void *ptr); + int (*AST_DATA_SECONDS)(void *ptr); + int (*AST_DATA_MILLISECONDS)(void *ptr); double (*AST_DATA_DOUBLE)(void *ptr); unsigned int (*AST_DATA_UNSIGNED_INTEGER)(void *ptr); unsigned int (*AST_DATA_BOOLEAN)(void *ptr); @@ -292,10 +309,20 @@ struct ast_data_mapping_structure { .type = __type }, /* based on the data type, specifify the type of return value for the getter function. */ +#define __AST_DATA_MAPPING_FUNCTION_AST_DATA_PASSWORD(__structure, __member) \ + __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_PASSWORD, char *) #define __AST_DATA_MAPPING_FUNCTION_AST_DATA_STRING(__structure, __member) \ __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_STRING, char *) +#define __AST_DATA_MAPPING_FUNCTION_AST_DATA_CHARACTER(__structure, __member) \ + __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_CHARACTER, char) #define __AST_DATA_MAPPING_FUNCTION_AST_DATA_INTEGER(__structure, __member) \ __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_INTEGER, int) +#define __AST_DATA_MAPPING_FUNCTION_AST_DATA_TIMESTAMP(__structure, __member) \ + __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_INTEGER, int) +#define __AST_DATA_MAPPING_FUNCTION_AST_DATA_SECONDS(__structure, __member) \ + __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_INTEGER, int) +#define __AST_DATA_MAPPING_FUNCTION_AST_DATA_MILLISECONDS(__structure, __member) \ + __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_INTEGER, int) #define __AST_DATA_MAPPING_FUNCTION_AST_DATA_UNSIGNED_INTEGER(__structure, __member) \ __AST_DATA_MAPPING_FUNCTION_TYPE(__structure, __member, AST_DATA_UNSIGNED_INTEGER, unsigned int) #define __AST_DATA_MAPPING_FUNCTION_AST_DATA_BOOLEAN(__structure, __member) \ @@ -367,100 +394,15 @@ int __ast_data_unregister(const char *path, const char *registrar); #define ast_data_unregister(path) __ast_data_unregister(path, __FILE__) /*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current string value. - * .search = "somename=somestring" - * name = "somename" - * value is the current value of something and will be evaluated against "somestring". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] value The value to compare. - * \returns The strcmp return value. - */ -int ast_data_search_cmp_string(const struct ast_data_search *root, const char *name, char *value); - -/*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current pointer address value. - * .search = "something=0x32323232" - * name = "something" - * value is the current value of something and will be evaluated against "0x32323232". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] ptr The pointer address to compare. - * \returns The (value - current_value) result. - */ -int ast_data_search_cmp_ptr(const struct ast_data_search *root, const char *name, - void *ptr); - -/*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current ipv4 address value. - * .search = "something=192.168.2.2" - * name = "something" - * value is the current value of something and will be evaluated against "192.168.2.2". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] addr The ipv4 address value to compare. - * \returns The (value - current_value) result. - */ -int ast_data_search_cmp_ipaddr(const struct ast_data_search *root, const char *name, - struct in_addr addr); - -/*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current double value. - * .search = "something=222" - * name = "something" - * value is the current value of something and will be evaluated against "222". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] value The double value to compare. - * \returns The (value - current_value) result. - */ -int ast_data_search_cmp_dbl(const struct ast_data_search *root, const char *name, - double value); - -/*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current boolean value. - * .search = "something=true" - * name = "something" - * value is the current value of something and will be evaluated against "true". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] value The boolean value to compare. - * \returns The (value - current_value) result. - */ -int ast_data_search_cmp_bool(const struct ast_data_search *root, const char *name, - unsigned int value); - -/*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current unsigned integer value. - * .search = "something=10" - * name = "something" - * value is the current value of something and will be evaluated against "10". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] value The unsigned value to compare. - * \returns The strcmp return value. + * \brief Check the current generated node to know if it matches the search + * condition. + * \param[in] search The search condition. + * \param[in] data The AstData node generated. + * \return 1 If the "data" node matches the search condition. + * \return 0 If the "data" node does not matches the search condition. + * \see ast_data_remove_node */ -int ast_data_search_cmp_uint(const struct ast_data_search *root, const char *name, - unsigned int value); - -/*! - * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the - * current signed integer value. - * .search = "something=10" - * name = "something" - * value is the current value of something and will be evaluated against "10". - * \param[in] root The root node pointer of the search tree. - * \param[in] name The name of the specific. - * \param[in] value The value to compare. - * \returns The strcmp return value. - */ -int ast_data_search_cmp_int(const struct ast_data_search *root, const char *name, int value); +int ast_data_search_match(const struct ast_data_search *search, struct ast_data *data); /*! * \brief Based on a search tree, evaluate every member of a structure against it. @@ -480,17 +422,6 @@ int __ast_data_search_cmp_structure(const struct ast_data_search *search, ARRAY_LEN(__data_mapping_structure_##structure_name), structure, structure_name_cmp) /*! - * \brief Check if there is a compare condition inside the search tree with the - * passed 'compare_condition' node names. - * \param[in] search The search tree. - * \param[in] compare_condition The path of the compare condition. - * \retval 0 There is no compare condition. - * \retval 1 There is a compare condition. - */ -int ast_data_search_has_condition(const struct ast_data_search *search, - const char *compare_condition); - -/*! * \brief Retrieve a subtree from the asterisk data API. * \param[in] query The query structure specifying what nodes to retrieve. * \retval NULL on error. @@ -555,6 +486,17 @@ struct ast_data *ast_data_add_int(struct ast_data *root, const char *childname, int value); /*! + * \brief Add a char node type. + * \param[in] root The root of the ast_data to insert into. + * \param[in] childname The name of the child element to be added. + * \param[in] value The value for the new node. + * \retval NULL on error (memory exhaustion only). + * \retval non-NULL a newly allocated node. + */ +struct ast_data *ast_data_add_char(struct ast_data *root, const char *childname, + char value); + +/*! * \brief Add an unsigned integer node type. * \param[in] root The root of the ast_data to insert into. * \param[in] childname The name of the child element to be added. @@ -598,6 +540,50 @@ struct ast_data *ast_data_add_ptr(struct ast_data *root, const char *childname, void *ptr); /*! + * \brief Add a password node type. + * \param[in] root The root of the ast_data to insert into. + * \param[in] childname The name of the child element to be added. + * \param[in] string The value for the new node. + * \retval NULL on error (memory exhaustion only). + * \retval non-NULL a newly allocated node. + */ +struct ast_data *ast_data_add_password(struct ast_data *root, const char *childname, + const char *string); + +/*! + * \brief Add a timestamp node type. + * \param[in] root The root of the ast_data to insert into. + * \param[in] childname The name of the child element to be added. + * \param[in] timestamp The value for the new node. + * \retval NULL on error (memory exhaustion only). + * \retval non-NULL a newly allocated node. + */ +struct ast_data *ast_data_add_timestamp(struct ast_data *root, const char *childname, + unsigned int timestamp); + +/*! + * \brief Add a seconds node type. + * \param[in] root The root of the ast_data to insert into. + * \param[in] childname The name of the child element to be added. + * \param[in] seconds The value for the new node. + * \retval NULL on error (memory exhaustion only). + * \retval non-NULL a newly allocated node. + */ +struct ast_data *ast_data_add_seconds(struct ast_data *root, const char *childname, + unsigned int seconds); + +/*! + * \brief Add a milliseconds node type. + * \param[in] root The root of the ast_data to insert into. + * \param[in] childname The name of the child element to be added. + * \param[in] milliseconds The value for the new node. + * \retval NULL on error (memory exhaustion only). + * \retval non-NULL a newly allocated node. + */ +struct ast_data *ast_data_add_milliseconds(struct ast_data *root, const char *childname, + unsigned int milliseconds); + +/*! * \brief Add a string node type. * \param[in] root The root of the ast_data to insert into. * \param[in] childname The name of the child element to be added. @@ -694,6 +680,21 @@ static inline int ast_data_retrieve_int(struct ast_data *tree, const char *path) } /*! + * \brief Retrieve the character value of a node. + * \param[in] tree The tree from where to get the value. + * \param[in] path The node name or path. + * \returns The value of the node. + */ +static inline char ast_data_retrieve_char(struct ast_data *tree, const char *path) +{ + struct ast_data_retrieve ret; + + ast_data_retrieve(tree, path, &ret); + + return ret.value.AST_DATA_CHARACTER; +} + +/*! * \brief Retrieve the boolean value of a node. * \param[in] tree The tree from where to get the value. * \param[in] path The node name or path. @@ -724,6 +725,21 @@ static inline unsigned int ast_data_retrieve_uint(struct ast_data *tree, const c } /*! + * \brief Retrieve the password value of a node. + * \param[in] tree The tree from where to get the value. + * \param[in] path The node name or path. + * \returns The value of the node. + */ +static inline const char *ast_data_retrieve_password(struct ast_data *tree, const char *path) +{ + struct ast_data_retrieve ret; + + ast_data_retrieve(tree, path, &ret); + + return ret.value.AST_DATA_PASSWORD; +} + +/*! * \brief Retrieve the string value of a node. * \param[in] tree The tree from where to get the value. * \param[in] path The node name or path. @@ -783,6 +799,17 @@ static inline struct in_addr ast_data_retrieve_ipaddr(struct ast_data *tree, con return ret.value.AST_DATA_IPADDR; } +/*! + * \brief Add the list of codecs in the root node based on the capability parameter. + * \param[in] root The astdata root node where to add the codecs node. + * \param[in] node_name The name of the node where we are going to add the list of + * codecs. + * \param[in] capability The codecs allowed. + * \return < 0 on error. + * \return 0 on success. + */ +int ast_data_add_codecs(struct ast_data *root, const char *node_name, format_t capability); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/indications.h b/include/asterisk/indications.h index 41a77210c..f69eb86c6 100644 --- a/include/asterisk/indications.h +++ b/include/asterisk/indications.h @@ -27,6 +27,8 @@ #define _ASTERISK_INDICATIONS_H #include "asterisk/astobj2.h" +#include "asterisk/utils.h" +#include "asterisk/data.h" /*! * \brief Description of a tone @@ -237,4 +239,12 @@ static inline struct ast_tone_zone_sound *ast_tone_zone_sound_ref(struct ast_ton return ts; } +/*! + * \brief Add a tone_zone structure to the data tree specified. + * + * \retval <0 on error. + * \retval 0 on success. + */ +int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone); + #endif /* _ASTERISK_INDICATIONS_H */ diff --git a/main/cdr.c b/main/cdr.c index db277af29..f14af2cb9 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/config.h" #include "asterisk/cli.h" #include "asterisk/stringfields.h" +#include "asterisk/data.h" /*! Default AMA flag for billing records (CDR's) */ int ast_default_amaflags = AST_CDR_DOCUMENTATION; @@ -1635,3 +1636,50 @@ int ast_cdr_engine_reload(void) return do_reload(1); } +int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur) +{ + struct ast_cdr *tmpcdr; + struct ast_data *level; + struct ast_var_t *variables; + const char *var, *val; + int x = 1, i; + char workspace[256]; + char *tmp; + + if (!cdr) { + return -1; + } + + for (tmpcdr = cdr; tmpcdr; tmpcdr = (recur ? tmpcdr->next : NULL)) { + level = ast_data_add_node(tree, "level"); + if (!level) { + continue; + } + + ast_data_add_int(level, "level_number", x); + + AST_LIST_TRAVERSE(&tmpcdr->varshead, variables, entries) { + if (variables && (var = ast_var_name(variables)) && + (val = ast_var_value(variables)) && !ast_strlen_zero(var) + && !ast_strlen_zero(val)) { + ast_data_add_str(level, var, val); + } else { + break; + } + } + + for (i = 0; cdr_readonly_vars[i]; i++) { + workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */ + ast_cdr_getvar(tmpcdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0); + if (!tmp) { + continue; + } + ast_data_add_str(level, cdr_readonly_vars[i], tmp); + } + + x++; + } + + return 0; +} + diff --git a/main/channel.c b/main/channel.c index af2c036a9..f6d9ada94 100644 --- a/main/channel.c +++ b/main/channel.c @@ -72,6 +72,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <sys/epoll.h> #endif +#ifdef HAVE_PRI +#include "sig_pri.h" +#endif + struct ast_epoll_data { struct ast_channel *chan; int which; @@ -135,6 +139,17 @@ static AST_RWLIST_HEAD_STATIC(backends, chanlist); #define NUM_CHANNEL_BUCKETS 1567 #endif +#define DATA_EXPORT_CALLERID(MEMBER) \ + MEMBER(ast_callerid, cid_dnid, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_num, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_name, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_ani, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_pres, AST_DATA_INTEGER) \ + MEMBER(ast_callerid, cid_ani2, AST_DATA_INTEGER) \ + MEMBER(ast_callerid, cid_tag, AST_DATA_STRING) + +AST_DATA_STRUCTURE(ast_callerid, DATA_EXPORT_CALLERID); + #define DATA_EXPORT_CHANNEL(MEMBER) \ MEMBER(ast_channel, blockproc, AST_DATA_STRING) \ MEMBER(ast_channel, appl, AST_DATA_STRING) \ @@ -151,28 +166,14 @@ static AST_RWLIST_HEAD_STATIC(backends, chanlist); MEMBER(ast_channel, parkinglot, AST_DATA_STRING) \ MEMBER(ast_channel, hangupsource, AST_DATA_STRING) \ MEMBER(ast_channel, dialcontext, AST_DATA_STRING) \ - MEMBER(ast_channel, _softhangup, AST_DATA_INTEGER) \ - MEMBER(ast_channel, streamid, AST_DATA_INTEGER) \ - MEMBER(ast_channel, vstreamid, AST_DATA_INTEGER) \ - MEMBER(ast_channel, oldwriteformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, _state, AST_DATA_INTEGER) \ MEMBER(ast_channel, rings, AST_DATA_INTEGER) \ MEMBER(ast_channel, priority, AST_DATA_INTEGER) \ MEMBER(ast_channel, macropriority, AST_DATA_INTEGER) \ - MEMBER(ast_channel, amaflags, AST_DATA_INTEGER) \ MEMBER(ast_channel, adsicpe, AST_DATA_INTEGER) \ MEMBER(ast_channel, fin, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, fout, AST_DATA_UNSIGNED_INTEGER) \ - MEMBER(ast_channel, hangupcause, AST_DATA_INTEGER) \ - MEMBER(ast_channel, flags, AST_DATA_UNSIGNED_INTEGER) \ - MEMBER(ast_channel, nativeformats, AST_DATA_INTEGER) \ - MEMBER(ast_channel, readformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, writeformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, rawreadformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, rawwriteformat, AST_DATA_INTEGER) \ MEMBER(ast_channel, emulate_dtmf_duration, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, visible_indication, AST_DATA_INTEGER) \ - MEMBER(ast_channel, transfercapability, AST_DATA_INTEGER) \ MEMBER(ast_channel, context, AST_DATA_STRING) \ MEMBER(ast_channel, exten, AST_DATA_STRING) \ MEMBER(ast_channel, macrocontext, AST_DATA_STRING) \ @@ -259,9 +260,170 @@ struct ast_variable *ast_channeltype_list(void) return var; } -int ast_channel_data_add_structure(struct ast_data *tree, struct ast_channel *chan) +static void channel_data_add_flags(struct ast_data *tree, + struct ast_channel *chan) +{ + ast_data_add_bool(tree, "DEFER_DTMF", ast_test_flag(chan, AST_FLAG_DEFER_DTMF)); + ast_data_add_bool(tree, "WRITE_INT", ast_test_flag(chan, AST_FLAG_WRITE_INT)); + ast_data_add_bool(tree, "BLOCKING", ast_test_flag(chan, AST_FLAG_BLOCKING)); + ast_data_add_bool(tree, "ZOMBIE", ast_test_flag(chan, AST_FLAG_ZOMBIE)); + ast_data_add_bool(tree, "EXCEPTION", ast_test_flag(chan, AST_FLAG_EXCEPTION)); + ast_data_add_bool(tree, "MOH", ast_test_flag(chan, AST_FLAG_MOH)); + ast_data_add_bool(tree, "SPYING", ast_test_flag(chan, AST_FLAG_SPYING)); + ast_data_add_bool(tree, "NBRIDGE", ast_test_flag(chan, AST_FLAG_NBRIDGE)); + ast_data_add_bool(tree, "IN_AUTOLOOP", ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)); + ast_data_add_bool(tree, "OUTGOING", ast_test_flag(chan, AST_FLAG_OUTGOING)); + ast_data_add_bool(tree, "IN_DTMF", ast_test_flag(chan, AST_FLAG_IN_DTMF)); + ast_data_add_bool(tree, "EMULATE_DTMF", ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)); + ast_data_add_bool(tree, "END_DTMF_ONLY", ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)); + ast_data_add_bool(tree, "ANSWERED_ELSEWHERE", ast_test_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE)); + ast_data_add_bool(tree, "MASQ_NOSTREAM", ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM)); + ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN)); + ast_data_add_bool(tree, "BRIDGE_HANGUP_DONT", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT)); + ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS)); +} + +static const char *callerid_ton2str(int ton) +{ +#ifdef HAVE_PRI + switch (ton) { + case PRI_TON_INTERNATIONAL: + return "International Number"; + case PRI_TON_NATIONAL: + return "National Number"; + case PRI_TON_NET_SPECIFIC: + return "Network Specific Number"; + case PRI_TON_SUBSCRIBER: + return "Subscriber Number"; + case PRI_TON_ABBREVIATED: + return "Abbreviated number"; + case PRI_TON_RESERVED: + return "Reserved Number"; + case PRI_TON_UNKNOWN: + default: + return "Unknown Number Type"; + } +#endif + return ""; +} + +int ast_channel_data_add_structure(struct ast_data *tree, + struct ast_channel *chan, int add_bridged) { - return ast_data_add_structure(ast_channel, tree, chan); + struct ast_channel *bc; + struct ast_data *data_bridged, *data_cdr, *data_flags, *data_zones; + struct ast_data *data_callerid, *enum_node, *data_softhangup; + + if (!tree) { + return -1; + } + + ast_data_add_structure(ast_channel, tree, chan); + + if (add_bridged) { + bc = ast_bridged_channel(chan); + if (bc) { + data_bridged = ast_data_add_node(tree, "bridged"); + if (!data_bridged) { + return -1; + } + ast_channel_data_add_structure(data_bridged, bc, 0); + } + } + + ast_data_add_codecs(tree, "oldwriteformat", chan->oldwriteformat); + ast_data_add_codecs(tree, "nativeformats", chan->nativeformats); + ast_data_add_codecs(tree, "readformat", chan->readformat); + ast_data_add_codecs(tree, "writeformat", chan->writeformat); + ast_data_add_codecs(tree, "rawreadformat", chan->rawreadformat); + ast_data_add_codecs(tree, "rawwriteformat", chan->rawwriteformat); + + /* state */ + enum_node = ast_data_add_node(tree, "state"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_state2str(chan->_state)); + ast_data_add_int(enum_node, "value", chan->_state); + + /* hangupcause */ + enum_node = ast_data_add_node(tree, "hangupcause"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_cause2str(chan->hangupcause)); + ast_data_add_int(enum_node, "value", chan->hangupcause); + + /* amaflags */ + enum_node = ast_data_add_node(tree, "amaflags"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_cdr_flags2str(chan->amaflags)); + ast_data_add_int(enum_node, "value", chan->amaflags); + + /* transfercapability */ + enum_node = ast_data_add_node(tree, "transfercapability"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_transfercapability2str(chan->transfercapability)); + ast_data_add_int(enum_node, "value", chan->transfercapability); + + /* _softphangup */ + data_softhangup = ast_data_add_node(tree, "softhangup"); + if (!data_softhangup) { + return -1; + } + ast_data_add_bool(data_softhangup, "dev", chan->_softhangup & AST_SOFTHANGUP_DEV); + ast_data_add_bool(data_softhangup, "asyncgoto", chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO); + ast_data_add_bool(data_softhangup, "shutdown", chan->_softhangup & AST_SOFTHANGUP_SHUTDOWN); + ast_data_add_bool(data_softhangup, "timeout", chan->_softhangup & AST_SOFTHANGUP_TIMEOUT); + ast_data_add_bool(data_softhangup, "appunload", chan->_softhangup & AST_SOFTHANGUP_APPUNLOAD); + ast_data_add_bool(data_softhangup, "explicit", chan->_softhangup & AST_SOFTHANGUP_EXPLICIT); + ast_data_add_bool(data_softhangup, "unbridge", chan->_softhangup & AST_SOFTHANGUP_UNBRIDGE); + + /* channel flags */ + data_flags = ast_data_add_node(tree, "flags"); + if (!data_flags) { + return -1; + } + channel_data_add_flags(data_flags, chan); + + ast_data_add_uint(tree, "timetohangup", chan->whentohangup.tv_sec); + + /* callerid */ + data_callerid = ast_data_add_node(tree, "callerid"); + if (!data_callerid) { + return -1; + } + ast_data_add_structure(ast_callerid, data_callerid, &(chan->cid)); + /* insert the callerid ton */ + enum_node = ast_data_add_node(data_callerid, "cid_ton"); + if (!enum_node) { + return -1; + } + ast_data_add_int(enum_node, "value", chan->cid.cid_ton); + ast_data_add_str(enum_node, "text", callerid_ton2str(chan->cid.cid_ton)); + + /* tone zone */ + if (chan->zone) { + data_zones = ast_data_add_node(tree, "zone"); + if (!data_zones) { + return -1; + } + ast_tone_zone_data_add_structure(data_zones, chan->zone); + } + + /* insert cdr */ + data_cdr = ast_data_add_node(tree, "cdr"); + if (!data_cdr) { + return -1; + } + + ast_cdr_data_add_structure(data_cdr, chan->cdr, 1); + + return 0; } int ast_channel_data_cmp_structure(const struct ast_data_search *tree, @@ -6790,52 +6952,29 @@ int ast_plc_reload(void) static int data_channels_provider_handler(const struct ast_data_search *search, struct ast_data *root) { - struct ast_channel *c, *bc; + struct ast_channel *c; struct ast_channel_iterator *iter = NULL; - struct ast_data *data_channel, *data_bridged; - int channel_match, bridged_match; - - channel_match = ast_data_search_has_condition(search, - "channel"); - bridged_match = ast_data_search_has_condition(search, - "channel/bridged"); + struct ast_data *data_channel; for (iter = ast_channel_iterator_all_new(); iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) { ast_channel_lock(c); - if (channel_match && - ast_channel_data_cmp_structure(search, c, "channel")) { - ast_channel_unlock(c); - continue; - } - - bc = ast_bridged_channel(c); - - if (bridged_match && bc && - ast_channel_data_cmp_structure(search, bc, "channel/bridged")) { - ast_channel_unlock(c); - continue; - } - data_channel = ast_data_add_node(root, "channel"); if (!data_channel) { ast_channel_unlock(c); continue; } - ast_channel_data_add_structure(data_channel, c); - - if (bc) { - data_bridged = ast_data_add_node(data_channel, "bridged"); - if (!data_bridged) { - ast_channel_unlock(c); - continue; - } - ast_channel_data_add_structure(data_bridged, bc); + if (ast_channel_data_add_structure(data_channel, c, 1) < 0) { + ast_log(LOG_ERROR, "Unable to add channel structure for channel: %s\n", c->name); } ast_channel_unlock(c); + + if (!ast_data_search_match(search, data_channel)) { + ast_data_remove_node(root, data_channel); + } } if (iter) { ast_channel_iterator_destroy(iter); @@ -6846,6 +6985,64 @@ static int data_channels_provider_handler(const struct ast_data_search *search, /*! * \internal + * \brief Implements the channeltypes provider. + */ +static int data_channeltypes_provider_handler(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct chanlist *cl; + struct ast_data *data_type; + + AST_RWLIST_RDLOCK(&backends); + AST_RWLIST_TRAVERSE(&backends, cl, list) { + data_type = ast_data_add_node(data_root, "type"); + if (!data_type) { + continue; + } + ast_data_add_str(data_type, "name", cl->tech->type); + ast_data_add_str(data_type, "description", cl->tech->description); + ast_data_add_bool(data_type, "devicestate", cl->tech->devicestate ? 1 : 0); + ast_data_add_bool(data_type, "indications", cl->tech->indicate ? 1 : 0); + ast_data_add_bool(data_type, "transfer", cl->tech->transfer ? 1 : 0); + ast_data_add_bool(data_type, "send_digit_begin", cl->tech->send_digit_begin ? 1 : 0); + ast_data_add_bool(data_type, "send_digit_end", cl->tech->send_digit_end ? 1 : 0); + ast_data_add_bool(data_type, "call", cl->tech->call ? 1 : 0); + ast_data_add_bool(data_type, "hangup", cl->tech->hangup ? 1 : 0); + ast_data_add_bool(data_type, "answer", cl->tech->answer ? 1 : 0); + ast_data_add_bool(data_type, "read", cl->tech->read ? 1 : 0); + ast_data_add_bool(data_type, "write", cl->tech->write ? 1 : 0); + ast_data_add_bool(data_type, "send_text", cl->tech->send_text ? 1 : 0); + ast_data_add_bool(data_type, "send_image", cl->tech->send_image ? 1 : 0); + ast_data_add_bool(data_type, "send_html", cl->tech->send_html ? 1 : 0); + ast_data_add_bool(data_type, "exception", cl->tech->exception ? 1 : 0); + ast_data_add_bool(data_type, "bridge", cl->tech->bridge ? 1 : 0); + ast_data_add_bool(data_type, "early_bridge", cl->tech->early_bridge ? 1 : 0); + ast_data_add_bool(data_type, "fixup", cl->tech->fixup ? 1 : 0); + ast_data_add_bool(data_type, "setoption", cl->tech->setoption ? 1 : 0); + ast_data_add_bool(data_type, "queryoption", cl->tech->queryoption ? 1 : 0); + ast_data_add_bool(data_type, "write_video", cl->tech->write_video ? 1 : 0); + ast_data_add_bool(data_type, "write_text", cl->tech->write_text ? 1 : 0); + ast_data_add_bool(data_type, "bridged_channel", cl->tech->bridged_channel ? 1 : 0); + ast_data_add_bool(data_type, "func_channel_read", cl->tech->func_channel_read ? 1 : 0); + ast_data_add_bool(data_type, "func_channel_write", cl->tech->func_channel_write ? 1 : 0); + ast_data_add_bool(data_type, "get_base_channel", cl->tech->get_base_channel ? 1 : 0); + ast_data_add_bool(data_type, "set_base_channel", cl->tech->set_base_channel ? 1 : 0); + ast_data_add_bool(data_type, "get_pvt_uniqueid", cl->tech->get_pvt_uniqueid ? 1 : 0); + ast_data_add_bool(data_type, "cc_callback", cl->tech->cc_callback ? 1 : 0); + + ast_data_add_codecs(data_type, "capabilities", cl->tech->capabilities); + + if (!ast_data_search_match(search, data_type)) { + ast_data_remove_node(data_root, data_type); + } + } + AST_RWLIST_UNLOCK(&backends); + + return 0; +} + +/*! + * \internal * \brief /asterisk/core/channels provider. */ static const struct ast_data_handler channels_provider = { @@ -6853,8 +7050,18 @@ static const struct ast_data_handler channels_provider = { .get = data_channels_provider_handler }; +/*! + * \internal + * \brief /asterisk/core/channeltypes provider. + */ +static const struct ast_data_handler channeltypes_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = data_channeltypes_provider_handler +}; + static const struct ast_data_entry channel_providers[] = { AST_DATA_ENTRY("/asterisk/core/channels", &channels_provider), + AST_DATA_ENTRY("/asterisk/core/channeltypes", &channeltypes_provider), }; void ast_channels_init(void) diff --git a/main/data.c b/main/data.c index c402c4fa6..2a4559c04 100644 --- a/main/data.c +++ b/main/data.c @@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/term.h" #include "asterisk/manager.h" #include "asterisk/test.h" +#include "asterisk/frame.h" /*** DOCUMENTATION <manager name="DataGet" language="en_US"> @@ -85,8 +86,9 @@ struct ast_data { int32_t sint; uint32_t uint; double dbl; - unsigned int boolean:1; + unsigned int boolean; char *str; + char character; struct in_addr ipaddr; void *ptr; } payload; @@ -981,7 +983,19 @@ static struct ast_data_search *data_search_get_node(const struct ast_data_search return current; } -int ast_data_search_cmp_string(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current string value. + * .search = "somename=somestring" + * name = "somename" + * value is the current value of something and will be evaluated against "somestring". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The value to compare. + * \returns The strcmp return value. + */ +static int data_search_cmp_string(const struct ast_data_search *root, const char *name, char *value) { struct ast_data_search *child; @@ -1001,7 +1015,19 @@ int ast_data_search_cmp_string(const struct ast_data_search *root, const char *n return data_search_comparison_result(ret, cmp_type); } -int ast_data_search_cmp_ptr(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current pointer address value. + * .search = "something=0x32323232" + * name = "something" + * value is the current value of something and will be evaluated against "0x32323232". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] ptr The pointer address to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_ptr(const struct ast_data_search *root, const char *name, void *ptr) { struct ast_data_search *child; @@ -1024,7 +1050,19 @@ int ast_data_search_cmp_ptr(const struct ast_data_search *root, const char *name return data_search_comparison_result((node_ptr - ptr), cmp_type); } -int ast_data_search_cmp_ipaddr(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current ipv4 address value. + * .search = "something=192.168.2.2" + * name = "something" + * value is the current value of something and will be evaluated against "192.168.2.2". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] addr The ipv4 address value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_ipaddr(const struct ast_data_search *root, const char *name, struct in_addr addr) { struct ast_data_search *child; @@ -1044,7 +1082,19 @@ int ast_data_search_cmp_ipaddr(const struct ast_data_search *root, const char *n return data_search_comparison_result((node_addr.s_addr - addr.s_addr), cmp_type); } -int ast_data_search_cmp_bool(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current boolean value. + * .search = "something=true" + * name = "something" + * value is the current value of something and will be evaluated against "true". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The boolean value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_bool(const struct ast_data_search *root, const char *name, unsigned int value) { struct ast_data_search *child; @@ -1064,7 +1114,19 @@ int ast_data_search_cmp_bool(const struct ast_data_search *root, const char *nam return data_search_comparison_result(value - node_value, cmp_type); } -int ast_data_search_cmp_dbl(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current double value. + * .search = "something=222" + * name = "something" + * value is the current value of something and will be evaluated against "222". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The double value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_dbl(const struct ast_data_search *root, const char *name, double value) { struct ast_data_search *child; @@ -1084,7 +1146,19 @@ int ast_data_search_cmp_dbl(const struct ast_data_search *root, const char *name return data_search_comparison_result(value - node_value, cmp_type); } -int ast_data_search_cmp_uint(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current unsigned integer value. + * .search = "something=10" + * name = "something" + * value is the current value of something and will be evaluated against "10". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The unsigned value to compare. + * \returns The strcmp return value. + */ +static int data_search_cmp_uint(const struct ast_data_search *root, const char *name, unsigned int value) { struct ast_data_search *child; @@ -1104,7 +1178,19 @@ int ast_data_search_cmp_uint(const struct ast_data_search *root, const char *nam return data_search_comparison_result(value - node_value, cmp_type); } -int ast_data_search_cmp_int(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current signed integer value. + * .search = "something=10" + * name = "something" + * value is the current value of something and will be evaluated against "10". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The value to compare. + * \returns The strcmp return value. + */ +static int data_search_cmp_int(const struct ast_data_search *root, const char *name, int value) { struct ast_data_search *child; @@ -1126,6 +1212,38 @@ int ast_data_search_cmp_int(const struct ast_data_search *root, const char *name /*! * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current character value. + * .search = "something=c" + * name = "something" + * value is the current value of something and will be evaluated against "c". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The boolean value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_char(const struct ast_data_search *root, const char *name, + char value) +{ + struct ast_data_search *child; + char node_value; + enum data_search_comparison cmp_type; + + child = data_search_get_node(root, name); + if (!child) { + return 0; + } + + node_value = *(child->value); + cmp_type = child->cmp_type; + + ao2_ref(child, -1); + + return data_search_comparison_result(value - node_value, cmp_type); +} + +/*! + * \internal * \brief Get the member pointer, from a mapping structure, based on its name. * \XXX We will need to improve performance here!!. * \retval <0 if the member was not found. @@ -1146,21 +1264,6 @@ static inline int data_search_mapping_find(const struct ast_data_mapping_structu return -1; } -int ast_data_search_has_condition(const struct ast_data_search *search, - const char *compare_condition) -{ - struct ast_data_search *child; - - child = data_search_get_node(search, compare_condition); - if (!child) { - return 0; - } - - ao2_ref(child, -1); - - return 1; -} - int __ast_data_search_cmp_structure(const struct ast_data_search *search, const struct ast_data_mapping_structure *mapping, size_t mapping_len, void *structure, const char *structure_name) @@ -1191,38 +1294,63 @@ int __ast_data_search_cmp_structure(const struct ast_data_search *search, notmatch = 0; switch (mapping[member].type) { + case AST_DATA_PASSWORD: + notmatch = data_search_cmp_string(struct_children, + node->name, + mapping[member].get.AST_DATA_PASSWORD(structure)); + break; + case AST_DATA_TIMESTAMP: + notmatch = data_search_cmp_uint(struct_children, + node->name, + mapping[member].get.AST_DATA_TIMESTAMP(structure)); + break; + case AST_DATA_SECONDS: + notmatch = data_search_cmp_uint(struct_children, + node->name, + mapping[member].get.AST_DATA_SECONDS(structure)); + break; + case AST_DATA_MILLISECONDS: + notmatch = data_search_cmp_uint(struct_children, + node->name, + mapping[member].get.AST_DATA_MILLISECONDS(structure)); + break; case AST_DATA_STRING: - notmatch = ast_data_search_cmp_string(struct_children, + notmatch = data_search_cmp_string(struct_children, node->name, mapping[member].get.AST_DATA_STRING(structure)); break; + case AST_DATA_CHARACTER: + notmatch = data_search_cmp_char(struct_children, + node->name, + mapping[member].get.AST_DATA_CHARACTER(structure)); + break; case AST_DATA_INTEGER: - notmatch = ast_data_search_cmp_int(struct_children, + notmatch = data_search_cmp_int(struct_children, node->name, mapping[member].get.AST_DATA_INTEGER(structure)); break; case AST_DATA_BOOLEAN: - notmatch = ast_data_search_cmp_bool(struct_children, + notmatch = data_search_cmp_bool(struct_children, node->name, mapping[member].get.AST_DATA_BOOLEAN(structure)); break; case AST_DATA_UNSIGNED_INTEGER: - notmatch = ast_data_search_cmp_uint(struct_children, + notmatch = data_search_cmp_uint(struct_children, node->name, mapping[member].get.AST_DATA_UNSIGNED_INTEGER(structure)); break; case AST_DATA_DOUBLE: - notmatch = ast_data_search_cmp_dbl(struct_children, + notmatch = data_search_cmp_dbl(struct_children, node->name, mapping[member].get.AST_DATA_DOUBLE(structure)); break; case AST_DATA_IPADDR: - notmatch = ast_data_search_cmp_ipaddr(struct_children, + notmatch = data_search_cmp_ipaddr(struct_children, node->name, mapping[member].get.AST_DATA_IPADDR(structure)); break; case AST_DATA_POINTER: - notmatch = ast_data_search_cmp_ptr(struct_children, + notmatch = data_search_cmp_ptr(struct_children, node->name, mapping[member].get.AST_DATA_POINTER(structure)); break; @@ -1248,11 +1376,18 @@ static void data_result_destructor(void *obj) struct ast_data *root = obj; switch (root->type) { - case AST_DATA_POINTER: + case AST_DATA_PASSWORD: case AST_DATA_STRING: - ast_free(root->payload.ptr); + ast_free(root->payload.str); + ao2_ref(root->children, -1); + break; + case AST_DATA_POINTER: + case AST_DATA_CHARACTER: case AST_DATA_CONTAINER: case AST_DATA_INTEGER: + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: case AST_DATA_UNSIGNED_INTEGER: case AST_DATA_DOUBLE: case AST_DATA_BOOLEAN: @@ -1315,6 +1450,105 @@ static struct ast_data *data_result_find_child(struct ast_data *root, const char return found; } +int ast_data_search_match(const struct ast_data_search *search, struct ast_data *data) +{ + struct ao2_iterator i, ii; + struct ast_data_search *s, *s_child; + struct ast_data *d_child; + int notmatch = 1; + + if (!search) { + return 1; + } + + s_child = data_search_find(search->children, data->name); + if (!s_child) { + /* nothing to compare */ + ao2_ref(s_child, -1); + return 1; + } + + i = ao2_iterator_init(s_child->children, 0); + while ((s = ao2_iterator_next(&i))) { + if (!ao2_container_count(s->children)) { + /* compare this search node with every data node */ + d_child = data_result_find_child(data, s->name); + if (!d_child) { + ao2_ref(s, -1); + notmatch = 1; + continue; + } + + switch (d_child->type) { + case AST_DATA_PASSWORD: + case AST_DATA_STRING: + notmatch = data_search_cmp_string(s_child, d_child->name, + d_child->payload.str); + break; + case AST_DATA_CHARACTER: + notmatch = data_search_cmp_char(s_child, d_child->name, + d_child->payload.character); + break; + case AST_DATA_INTEGER: + notmatch = data_search_cmp_int(s_child, d_child->name, + d_child->payload.sint); + break; + case AST_DATA_BOOLEAN: + notmatch = data_search_cmp_bool(s_child, d_child->name, + d_child->payload.boolean); + break; + case AST_DATA_UNSIGNED_INTEGER: + notmatch = data_search_cmp_uint(s_child, d_child->name, + d_child->payload.uint); + break; + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: + case AST_DATA_DOUBLE: + notmatch = data_search_cmp_uint(s_child, d_child->name, + d_child->payload.dbl); + break; + case AST_DATA_IPADDR: + notmatch = data_search_cmp_ipaddr(s_child, d_child->name, + d_child->payload.ipaddr); + break; + case AST_DATA_POINTER: + notmatch = data_search_cmp_ptr(s_child, d_child->name, + d_child->payload.ptr); + break; + case AST_DATA_CONTAINER: + break; + } + ao2_ref(d_child, -1); + } else { + ii = ao2_iterator_init(data->children, 0); + while ((d_child = ao2_iterator_next(&ii))) { + if (strcmp(d_child->name, s->name)) { + ao2_ref(d_child, -1); + continue; + } + if (!(notmatch = !ast_data_search_match(s_child, d_child))) { + /* do not continue if we have a match. */ + ao2_ref(d_child, -1); + break; + } + ao2_ref(d_child, -1); + } + ao2_iterator_destroy(&ii); + } + ao2_ref(s, -1); + if (notmatch) { + /* do not continue if we don't have a match. */ + break; + } + } + ao2_iterator_destroy(&i); + + ao2_ref(s_child, -1); + + return !notmatch; +} + /*! * \internal * \brief Get an internal node, from the result set. @@ -1872,9 +2106,32 @@ static void data_get_xml_add_child(struct ast_data *parent_data, case AST_DATA_CONTAINER: data_get_xml_add_child(node, child_xml); break; + case AST_DATA_PASSWORD: + ast_xml_set_text(child_xml, node->payload.str); + break; + case AST_DATA_TIMESTAMP: + snprintf(node_content, sizeof(node_content), "%d", + node->payload.uint); + ast_xml_set_text(child_xml, node_content); + break; + case AST_DATA_SECONDS: + snprintf(node_content, sizeof(node_content), "%d", + node->payload.uint); + ast_xml_set_text(child_xml, node_content); + break; + case AST_DATA_MILLISECONDS: + snprintf(node_content, sizeof(node_content), "%d", + node->payload.uint); + ast_xml_set_text(child_xml, node_content); + break; case AST_DATA_STRING: ast_xml_set_text(child_xml, node->payload.str); break; + case AST_DATA_CHARACTER: + snprintf(node_content, sizeof(node_content), "%c", + node->payload.character); + ast_xml_set_text(child_xml, node_content); + break; case AST_DATA_INTEGER: snprintf(node_content, sizeof(node_content), "%d", node->payload.sint); @@ -2006,15 +2263,24 @@ static struct ast_data *__ast_data_add(struct ast_data *root, const char *name, node->payload.boolean = *(unsigned int *) ptr; break; case AST_DATA_INTEGER: - node->payload.sint = *(unsigned int *) ptr; + node->payload.sint = *(int *) ptr; break; + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: case AST_DATA_UNSIGNED_INTEGER: - node->payload.sint = *(unsigned int *) ptr; + node->payload.uint = *(unsigned int *) ptr; break; case AST_DATA_DOUBLE: node->payload.dbl = *(double *) ptr; break; + case AST_DATA_PASSWORD: case AST_DATA_STRING: + node->payload.str = (char *) ptr; + break; + case AST_DATA_CHARACTER: + node->payload.character = *(char *) ptr; + break; case AST_DATA_POINTER: node->payload.ptr = ptr; break; @@ -2052,6 +2318,11 @@ struct ast_data *ast_data_add_int(struct ast_data *root, const char *name, int v return __ast_data_add(root, name, AST_DATA_INTEGER, &value); } +struct ast_data *ast_data_add_char(struct ast_data *root, const char *name, char value) +{ + return __ast_data_add(root, name, AST_DATA_CHARACTER, &value); +} + struct ast_data *ast_data_add_uint(struct ast_data *root, const char *name, unsigned int value) { @@ -2082,6 +2353,45 @@ struct ast_data *ast_data_add_ptr(struct ast_data *root, const char *childname, return __ast_data_add(root, childname, AST_DATA_POINTER, ptr); } +struct ast_data *ast_data_add_timestamp(struct ast_data *root, const char *childname, + unsigned int timestamp) +{ + return __ast_data_add(root, childname, AST_DATA_TIMESTAMP, ×tamp); +} + +struct ast_data *ast_data_add_seconds(struct ast_data *root, const char *childname, + unsigned int seconds) +{ + return __ast_data_add(root, childname, AST_DATA_SECONDS, &seconds); +} + +struct ast_data *ast_data_add_milliseconds(struct ast_data *root, const char *childname, + unsigned int milliseconds) +{ + return __ast_data_add(root, childname, AST_DATA_MILLISECONDS, &milliseconds); +} + +struct ast_data *ast_data_add_password(struct ast_data *root, const char *childname, + const char *value) +{ + char *name; + size_t namelen = 1 + (ast_strlen_zero(value) ? 0 : strlen(value)); + struct ast_data *res; + + if (!(name = ast_malloc(namelen))) { + return NULL; + } + + strcpy(name, (ast_strlen_zero(value) ? "" : value)); + + res = __ast_data_add(root, childname, AST_DATA_PASSWORD, name); + if (!res) { + ast_free(name); + } + + return res; +} + struct ast_data *ast_data_add_str(struct ast_data *root, const char *childname, const char *value) { @@ -2127,10 +2437,30 @@ int __ast_data_add_structure(struct ast_data *root, ast_data_add_bool(root, mapping[i].name, mapping[i].get.AST_DATA_BOOLEAN(structure)); break; + case AST_DATA_PASSWORD: + ast_data_add_password(root, mapping[i].name, + mapping[i].get.AST_DATA_PASSWORD(structure)); + break; + case AST_DATA_TIMESTAMP: + ast_data_add_timestamp(root, mapping[i].name, + mapping[i].get.AST_DATA_TIMESTAMP(structure)); + break; + case AST_DATA_SECONDS: + ast_data_add_seconds(root, mapping[i].name, + mapping[i].get.AST_DATA_SECONDS(structure)); + break; + case AST_DATA_MILLISECONDS: + ast_data_add_milliseconds(root, mapping[i].name, + mapping[i].get.AST_DATA_MILLISECONDS(structure)); + break; case AST_DATA_STRING: ast_data_add_str(root, mapping[i].name, mapping[i].get.AST_DATA_STRING(structure)); break; + case AST_DATA_CHARACTER: + ast_data_add_char(root, mapping[i].name, + mapping[i].get.AST_DATA_CHARACTER(structure)); + break; case AST_DATA_CONTAINER: break; case AST_DATA_IPADDR: @@ -2277,6 +2607,21 @@ int ast_data_retrieve(struct ast_data *tree, const char *path, case AST_DATA_STRING: content->value.AST_DATA_STRING = node->payload.str; break; + case AST_DATA_PASSWORD: + content->value.AST_DATA_PASSWORD = node->payload.str; + break; + case AST_DATA_TIMESTAMP: + content->value.AST_DATA_TIMESTAMP = node->payload.uint; + break; + case AST_DATA_SECONDS: + content->value.AST_DATA_SECONDS = node->payload.uint; + break; + case AST_DATA_MILLISECONDS: + content->value.AST_DATA_MILLISECONDS = node->payload.uint; + break; + case AST_DATA_CHARACTER: + content->value.AST_DATA_CHARACTER = node->payload.character; + break; case AST_DATA_INTEGER: content->value.AST_DATA_INTEGER = node->payload.sint; break; @@ -2310,7 +2655,12 @@ static const struct { enum ast_data_type type; int color; } data_result_color[] = { - { AST_DATA_STRING, COLOR_CYAN }, + { AST_DATA_STRING, COLOR_BLUE }, + { AST_DATA_PASSWORD, COLOR_BRBLUE }, + { AST_DATA_TIMESTAMP, COLOR_CYAN }, + { AST_DATA_SECONDS, COLOR_MAGENTA }, + { AST_DATA_MILLISECONDS, COLOR_BRMAGENTA }, + { AST_DATA_CHARACTER, COLOR_GRAY }, { AST_DATA_INTEGER, COLOR_RED }, { AST_DATA_UNSIGNED_INTEGER, COLOR_RED }, { AST_DATA_DOUBLE, COLOR_RED }, @@ -2373,16 +2723,43 @@ static void data_result_print_cli_node(int fd, const struct ast_data *node, uint ast_str_append(&output, 0, "%s%s: %p\n", ast_str_buffer(tabs), node->name, node->payload.ptr); break; + case AST_DATA_PASSWORD: + ast_str_append(&output, 0, "%s%s: \"%s\"\n", + ast_str_buffer(tabs), + node->name, + node->payload.str); + break; case AST_DATA_STRING: ast_str_append(&output, 0, "%s%s: \"%s\"\n", ast_str_buffer(tabs), node->name, node->payload.str); break; + case AST_DATA_CHARACTER: + ast_str_append(&output, 0, "%s%s: \'%c\'\n", + ast_str_buffer(tabs), + node->name, + node->payload.character); + break; case AST_DATA_CONTAINER: ast_str_append(&output, 0, "%s%s\n", ast_str_buffer(tabs), node->name); break; + case AST_DATA_TIMESTAMP: + ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), + node->name, + node->payload.uint); + break; + case AST_DATA_SECONDS: + ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), + node->name, + node->payload.uint); + break; + case AST_DATA_MILLISECONDS: + ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), + node->name, + node->payload.uint); + break; case AST_DATA_INTEGER: ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), node->name, @@ -2470,6 +2847,8 @@ static void data_result_print_cli(int fd, const struct ast_data *root) ast_free(output); __data_result_print_cli(fd, root, 0); + + ast_cli(fd, "\n"); } /*! @@ -2644,12 +3023,21 @@ static void data_result_manager_output(struct mansession *s, const char *name, case AST_DATA_INTEGER: astman_append(s, ": %d\r\n", node->payload.sint); break; + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: case AST_DATA_UNSIGNED_INTEGER: astman_append(s, ": %u\r\n", node->payload.uint); break; + case AST_DATA_PASSWORD: + astman_append(s, ": %s\r\n", node->payload.str); + break; case AST_DATA_STRING: astman_append(s, ": %s\r\n", node->payload.str); break; + case AST_DATA_CHARACTER: + astman_append(s, ": %c\r\n", node->payload.character); + break; case AST_DATA_IPADDR: astman_append(s, ": %s\r\n", ast_inet_ntoa(node->payload.ipaddr)); break; @@ -2712,6 +3100,34 @@ static int manager_data_get(struct mansession *s, const struct message *m) return RESULT_SUCCESS; } +int ast_data_add_codecs(struct ast_data *root, const char *node_name, format_t capability) +{ + struct ast_data *codecs, *codec; + size_t fmlist_size; + const struct ast_format_list *fmlist; + int x; + + codecs = ast_data_add_node(root, node_name); + if (!codecs) { + return -1; + } + fmlist = ast_get_format_list(&fmlist_size); + for (x = 0; x < fmlist_size; x++) { + if (fmlist[x].bits & capability) { + codec = ast_data_add_node(codecs, "codec"); + if (!codec) { + return -1; + } + ast_data_add_str(codec, "name", fmlist[x].name); + ast_data_add_int(codec, "samplespersecond", fmlist[x].samplespersecond); + ast_data_add_str(codec, "description", fmlist[x].desc); + ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); + } + } + + return 0; +} + #ifdef TEST_FRAMEWORK /*! @@ -2753,10 +3169,6 @@ static int test_data_full_provider(const struct ast_data_search *search, .a_uint = 20 }; - if (ast_data_search_cmp_structure(search, test_structure, &local_test_structure, "test_structure")) { - return 0; - } - test_structure = ast_data_add_node(root, "test_structure"); if (!test_structure) { ast_debug(1, "Internal data api error\n"); @@ -2766,6 +3178,10 @@ static int test_data_full_provider(const struct ast_data_search *search, /* add the complete structure. */ ast_data_add_structure(test_structure, test_structure, &local_test_structure); + if (!ast_data_search_match(search, test_structure)) { + ast_data_remove_node(root, test_structure); + } + return 0; } diff --git a/main/indications.c b/main/indications.c index 0687648bc..b2d65bcc7 100644 --- a/main/indications.c +++ b/main/indications.c @@ -38,9 +38,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/module.h" #include "asterisk/astobj2.h" +#include "asterisk/data.h" #include "asterisk/_private.h" /* _init(), _reload() */ +#define DATA_EXPORT_TONE_ZONE(MEMBER) \ + MEMBER(ast_tone_zone, country, AST_DATA_STRING) \ + MEMBER(ast_tone_zone, description, AST_DATA_STRING) \ + MEMBER(ast_tone_zone, nrringcadence, AST_DATA_UNSIGNED_INTEGER) + +AST_DATA_STRUCTURE(ast_tone_zone, DATA_EXPORT_TONE_ZONE); + +#define DATA_EXPORT_TONE_ZONE_SOUND(MEMBER) \ + MEMBER(ast_tone_zone_sound, name, AST_DATA_STRING) \ + MEMBER(ast_tone_zone_sound, data, AST_DATA_STRING) + +AST_DATA_STRUCTURE(ast_tone_zone_sound, DATA_EXPORT_TONE_ZONE_SOUND); + /* Globals */ static const char config[] = "indications.conf"; @@ -1102,6 +1116,33 @@ static int ast_tone_zone_cmp(void *obj, void *arg, int flags) CMP_MATCH | CMP_STOP : 0; } +int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone) +{ + struct ast_data *data_zone_sound; + struct ast_tone_zone_sound *s; + + ast_data_add_structure(ast_tone_zone, tree, zone); + + if (AST_LIST_EMPTY(&zone->tones)) { + return 0; + } + + data_zone_sound = ast_data_add_node(tree, "tones"); + if (!data_zone_sound) { + return -1; + } + + ast_tone_zone_lock(zone); + + AST_LIST_TRAVERSE(&zone->tones, s, entry) { + ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s); + } + + ast_tone_zone_unlock(zone); + + return 0; +} + /*! \brief Load indications module */ int ast_indications_init(void) { diff --git a/main/pbx.c b/main/pbx.c index 24203ede7..dbc4db497 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -9717,6 +9717,57 @@ static void device_state_cb(const struct ast_event *event, void *unused) } } +/*! + * \internal + * \brief Implements the hints data provider. + */ +static int hints_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct ast_data *data_hint; + struct ast_hint *hint; + int watchers; + struct ast_state_cb *watcher; + + AST_RWLIST_RDLOCK(&hints); + if (AST_RWLIST_EMPTY(&hints)) { + AST_RWLIST_UNLOCK(&hints); + return 0; + } + + AST_RWLIST_TRAVERSE(&hints, hint, list) { + watchers = 0; + AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) { + watchers++; + } + data_hint = ast_data_add_node(data_root, "hint"); + if (!data_hint) { + continue; + } + ast_data_add_str(data_hint, "extension", ast_get_extension_name(hint->exten)); + ast_data_add_str(data_hint, "context", ast_get_context_name(ast_get_extension_context(hint->exten))); + ast_data_add_str(data_hint, "application", ast_get_extension_app(hint->exten)); + ast_data_add_str(data_hint, "state", ast_extension_state2str(hint->laststate)); + ast_data_add_int(data_hint, "watchers", watchers); + + if (!ast_data_search_match(search, data_hint)) { + ast_data_remove_node(data_root, data_hint); + } + } + AST_RWLIST_UNLOCK(&hints); + + return 0; +} + +static const struct ast_data_handler hints_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = hints_data_provider_get +}; + +static const struct ast_data_entry pbx_data_providers[] = { + AST_DATA_ENTRY("asterisk/core/hints", &hints_data_provider), +}; + int load_pbx(void) { int x; @@ -9729,6 +9780,7 @@ int load_pbx(void) ast_verb(1, "Registering builtin applications:\n"); ast_cli_register_multiple(pbx_cli, ARRAY_LEN(pbx_cli)); + ast_data_register_multiple_core(pbx_data_providers, ARRAY_LEN(pbx_data_providers)); __ast_custom_function_register(&exception_function, NULL); __ast_custom_function_register(&testtime_function, NULL); diff --git a/res/res_odbc.c b/res/res_odbc.c index 1859f766b..ed7aa986d 100644 --- a/res/res_odbc.c +++ b/res/res_odbc.c @@ -52,6 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/app.h" #include "asterisk/strings.h" #include "asterisk/threadstorage.h" +#include "asterisk/data.h" /*** DOCUMENTATION <function name="ODBC" language="en_US"> @@ -167,6 +168,17 @@ struct odbc_txn_frame { char name[0]; /*!< Name of this transaction ID */ }; +#define DATA_EXPORT_ODBC_CLASS(MEMBER) \ + MEMBER(odbc_class, name, AST_DATA_STRING) \ + MEMBER(odbc_class, dsn, AST_DATA_STRING) \ + MEMBER(odbc_class, username, AST_DATA_STRING) \ + MEMBER(odbc_class, password, AST_DATA_PASSWORD) \ + MEMBER(odbc_class, limit, AST_DATA_INTEGER) \ + MEMBER(odbc_class, count, AST_DATA_INTEGER) \ + MEMBER(odbc_class, forcecommit, AST_DATA_BOOLEAN) + +AST_DATA_STRUCTURE(odbc_class, DATA_EXPORT_ODBC_CLASS); + static const char *isolation2text(int iso) { if (iso == SQL_TXN_READ_COMMITTED) { @@ -1589,6 +1601,94 @@ static struct ast_custom_function odbc_function = { static const char * const app_commit = "ODBC_Commit"; static const char * const app_rollback = "ODBC_Rollback"; +/*! + * \internal + * \brief Implements the channels provider. + */ +static int data_odbc_provider_handler(const struct ast_data_search *search, + struct ast_data *root) +{ + struct ao2_iterator aoi, aoi2; + struct odbc_class *class; + struct odbc_obj *current; + struct ast_data *data_odbc_class, *data_odbc_connections, *data_odbc_connection; + struct ast_data *enum_node; + int count; + + aoi = ao2_iterator_init(class_container, 0); + while ((class = ao2_iterator_next(&aoi))) { + data_odbc_class = ast_data_add_node(root, "class"); + if (!data_odbc_class) { + ao2_ref(class, -1); + continue; + } + + ast_data_add_structure(odbc_class, data_odbc_class, class); + + if (!ao2_container_count(class->obj_container)) { + ao2_ref(class, -1); + continue; + } + + data_odbc_connections = ast_data_add_node(data_odbc_class, "connections"); + if (!data_odbc_connections) { + ao2_ref(class, -1); + continue; + } + + ast_data_add_bool(data_odbc_class, "shared", !class->haspool); + /* isolation */ + enum_node = ast_data_add_node(data_odbc_class, "isolation"); + if (!enum_node) { + ao2_ref(class, -1); + continue; + } + ast_data_add_int(enum_node, "value", class->isolation); + ast_data_add_str(enum_node, "text", isolation2text(class->isolation)); + + count = 0; + aoi2 = ao2_iterator_init(class->obj_container, 0); + while ((current = ao2_iterator_next(&aoi2))) { + data_odbc_connection = ast_data_add_node(data_odbc_connections, "connection"); + if (!data_odbc_connection) { + ao2_ref(current, -1); + continue; + } + + ast_mutex_lock(¤t->lock); + ast_data_add_str(data_odbc_connection, "status", current->used ? "in use" : + current->up && ast_odbc_sanity_check(current) ? "connected" : "disconnected"); + ast_data_add_bool(data_odbc_connection, "transactional", current->tx); + ast_mutex_unlock(¤t->lock); + + if (class->haspool) { + ast_data_add_int(data_odbc_connection, "number", ++count); + } + + ao2_ref(current, -1); + } + ao2_ref(class, -1); + + if (!ast_data_search_match(search, data_odbc_class)) { + ast_data_remove_node(root, data_odbc_class); + } + } + return 0; +} + +/*! + * \internal + * \brief /asterisk/res/odbc/listprovider. + */ +static const struct ast_data_handler odbc_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = data_odbc_provider_handler +}; + +static const struct ast_data_entry odbc_providers[] = { + AST_DATA_ENTRY("/asterisk/res/odbc", &odbc_provider), +}; + static int reload(void) { struct odbc_cache_tables *table; @@ -1676,6 +1776,7 @@ static int load_module(void) if (load_odbc_config() == -1) return AST_MODULE_LOAD_DECLINE; ast_cli_register_multiple(cli_odbc, ARRAY_LEN(cli_odbc)); + ast_data_register_multiple(odbc_providers, ARRAY_LEN(odbc_providers)); ast_register_application_xml(app_commit, commit_exec); ast_register_application_xml(app_rollback, rollback_exec); ast_custom_function_register(&odbc_function); |