aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreliel <eliel@f38db490-d61c-443f-a65b-d21fe96a405b>2010-07-08 14:48:42 +0000
committereliel <eliel@f38db490-d61c-443f-a65b-d21fe96a405b>2010-07-08 14:48:42 +0000
commit7a61a43adbc1ef91229e7757f6ac88619adff202 (patch)
tree80476efaba3fcc99c7526182d9ad935264c099eb
parentf28601a4b00a7b39fd8d3826b02c98e4b868e476 (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.c180
-rw-r--r--apps/app_queue.c89
-rw-r--r--apps/app_voicemail.c107
-rw-r--r--channels/chan_agent.c88
-rw-r--r--channels/chan_dahdi.c233
-rw-r--r--channels/chan_iax2.c329
-rw-r--r--channels/chan_sip.c242
-rw-r--r--include/asterisk/cdr.h12
-rw-r--r--include/asterisk/channel.h3
-rw-r--r--include/asterisk/data.h243
-rw-r--r--include/asterisk/indications.h10
-rw-r--r--main/cdr.c48
-rw-r--r--main/channel.c301
-rw-r--r--main/data.c494
-rw-r--r--main/indications.c41
-rw-r--r--main/pbx.c52
-rw-r--r--res/res_odbc.c101
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, &timestamp);
+}
+
+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(&current->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(&current->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);