Implement AstData API data providers as part of the GSOC 2010 project,
authorEliel C. Sardanons <eliels@gmail.com>
Thu, 8 Jul 2010 14:48:42 +0000 (14:48 +0000)
committerEliel C. Sardanons <eliels@gmail.com>
Thu, 8 Jul 2010 14:48:42 +0000 (14:48 +0000)
midterm evaluation.

Review: https://reviewboard.asterisk.org/r/757/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@274727 65c4cc65-6c06-0410-ace0-fbb531ad65f3

17 files changed:
apps/app_meetme.c
apps/app_queue.c
apps/app_voicemail.c
channels/chan_agent.c
channels/chan_dahdi.c
channels/chan_iax2.c
channels/chan_sip.c
include/asterisk/cdr.h
include/asterisk/channel.h
include/asterisk/data.h
include/asterisk/indications.h
main/cdr.c
main/channel.c
main/data.c
main/indications.c
main/pbx.c
res/res_odbc.c

index 36c29cf..be1d363 100644 (file)
@@ -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);
 
index 4c7c422..e91e13a 100644 (file)
@@ -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)
index 892c865..4e7a26e 100644 (file)
@@ -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)
index fd05bed..dfbf228 100644 (file)
@@ -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 */
index 7167010..e035b4b 100644 (file)
@@ -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);
index d69194d..ba482a0 100644 (file)
@@ -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);
index 5d8b7b4..c65d85b 100644 (file)
@@ -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));
 
index 4ca4797..5442fdb 100644 (file)
@@ -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 */
index 1e96aa6..0ea2cb1 100644 (file)
@@ -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.
index c52cee9..6851bd5 100644 (file)
@@ -25,6 +25,8 @@
 #ifndef ASTERISK_DATA_H
 #define ASTERISK_DATA_H
 
+#include "asterisk/frame.h"
+
 /*!
  * \page AstDataRetrieval The Asterisk DATA retrieval API.
  *
  *                     .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;
  *
  *             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
index 41a7721..f69eb86 100644 (file)
@@ -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 */
index db277af..f14af2c 100644 (file)
@@ -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;
+}
+
index af2c036..f6d9ada 100644 (file)
@@ -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)
index c402c4f..2a4559c 100644 (file)
@@ -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;
 }
 
index 0687648..b2d65bc 100644 (file)
@@ -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)
 {
index 24203ed..dbc4db4 100644 (file)
@@ -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);
 
index 1859f76..ed7aa98 100644 (file)
@@ -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);