aboutsummaryrefslogtreecommitdiffstats
path: root/channels
diff options
context:
space:
mode:
authoreliel <eliel@f38db490-d61c-443f-a65b-d21fe96a405b>2010-07-08 14:48:42 +0000
committereliel <eliel@f38db490-d61c-443f-a65b-d21fe96a405b>2010-07-08 14:48:42 +0000
commit7a61a43adbc1ef91229e7757f6ac88619adff202 (patch)
tree80476efaba3fcc99c7526182d9ad935264c099eb /channels
parentf28601a4b00a7b39fd8d3826b02c98e4b868e476 (diff)
Implement AstData API data providers as part of the GSOC 2010 project,
midterm evaluation. Review: https://reviewboard.asterisk.org/r/757/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@274727 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r--channels/chan_agent.c88
-rw-r--r--channels/chan_dahdi.c233
-rw-r--r--channels/chan_iax2.c329
-rw-r--r--channels/chan_sip.c242
4 files changed, 890 insertions, 2 deletions
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index fd05bed19..dfbf2284d 100644
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -67,6 +67,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/monitor.h"
#include "asterisk/stringfields.h"
#include "asterisk/event.h"
+#include "asterisk/data.h"
/*** DOCUMENTATION
<application name="AgentLogin" language="en_US">
@@ -278,6 +279,19 @@ struct agent_pvt {
AST_LIST_ENTRY(agent_pvt) list; /**< Next Agent in the linked list. */
};
+#define DATA_EXPORT_AGENT(MEMBER) \
+ MEMBER(agent_pvt, autologoff, AST_DATA_INTEGER) \
+ MEMBER(agent_pvt, ackcall, AST_DATA_BOOLEAN) \
+ MEMBER(agent_pvt, deferlogoff, AST_DATA_BOOLEAN) \
+ MEMBER(agent_pvt, wrapuptime, AST_DATA_MILLISECONDS) \
+ MEMBER(agent_pvt, acknowledged, AST_DATA_BOOLEAN) \
+ MEMBER(agent_pvt, name, AST_DATA_STRING) \
+ MEMBER(agent_pvt, password, AST_DATA_PASSWORD) \
+ MEMBER(agent_pvt, acceptdtmf, AST_DATA_CHARACTER) \
+ MEMBER(agent_pvt, logincallerid, AST_DATA_STRING)
+
+AST_DATA_STRUCTURE(agent_pvt, DATA_EXPORT_AGENT);
+
static AST_LIST_HEAD_STATIC(agents, agent_pvt); /*!< Holds the list of agents (loaded form agents.conf). */
#define CHECK_FORMATS(ast, p) do { \
@@ -2321,6 +2335,75 @@ static struct ast_custom_function agent_function = {
.read = function_agent,
};
+/*!
+ * \internal
+ * \brief Callback used to generate the agents tree.
+ * \param[in] search The search pattern tree.
+ * \retval NULL on error.
+ * \retval non-NULL The generated tree.
+ */
+static int agents_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ struct agent_pvt *p;
+ struct ast_data *data_agent, *data_channel, *data_talkingto;
+
+ AST_LIST_LOCK(&agents);
+ AST_LIST_TRAVERSE(&agents, p, list) {
+ data_agent = ast_data_add_node(data_root, "agent");
+ if (!data_agent) {
+ continue;
+ }
+
+ ast_mutex_lock(&p->lock);
+ if (!(p->pending)) {
+ ast_data_add_str(data_agent, "id", p->agent);
+ ast_data_add_structure(agent_pvt, data_agent, p);
+
+ ast_data_add_bool(data_agent, "logged", p->chan ? 1 : 0);
+ if (p->chan) {
+ data_channel = ast_data_add_node(data_agent, "loggedon");
+ if (!data_channel) {
+ ast_mutex_unlock(&p->lock);
+ ast_data_remove_node(data_root, data_agent);
+ continue;
+ }
+ ast_channel_data_add_structure(data_channel, p->chan, 0);
+ if (p->owner && ast_bridged_channel(p->owner)) {
+ data_talkingto = ast_data_add_node(data_agent, "talkingto");
+ if (!data_talkingto) {
+ ast_mutex_unlock(&p->lock);
+ ast_data_remove_node(data_root, data_agent);
+ continue;
+ }
+ ast_channel_data_add_structure(data_talkingto, ast_bridged_channel(p->owner), 0);
+ }
+ } else {
+ ast_data_add_node(data_agent, "talkingto");
+ ast_data_add_node(data_agent, "loggedon");
+ }
+ ast_data_add_str(data_agent, "musiconhold", p->moh);
+ }
+ ast_mutex_unlock(&p->lock);
+
+ /* if this agent doesn't match remove the added agent. */
+ if (!ast_data_search_match(search, data_agent)) {
+ ast_data_remove_node(data_root, data_agent);
+ }
+ }
+ AST_LIST_UNLOCK(&agents);
+
+ return 0;
+}
+
+static const struct ast_data_handler agents_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = agents_data_provider_get
+};
+
+static const struct ast_data_entry agents_data_providers[] = {
+ AST_DATA_ENTRY("asterisk/channel/agent/list", &agents_data_provider),
+};
/*!
* \brief Initialize the Agents module.
@@ -2343,6 +2426,9 @@ static int load_module(void)
ast_register_application_xml(app, login_exec);
ast_register_application_xml(app3, agentmonitoroutgoing_exec);
+ /* data tree */
+ ast_data_register_multiple(agents_data_providers, ARRAY_LEN(agents_data_providers));
+
/* Manager commands */
ast_manager_register_xml("Agents", EVENT_FLAG_AGENT, action_agents);
ast_manager_register_xml("AgentLogoff", EVENT_FLAG_AGENT, action_agent_logoff);
@@ -2376,6 +2462,8 @@ static int unload_module(void)
/* Unregister manager command */
ast_manager_unregister("Agents");
ast_manager_unregister("AgentLogoff");
+ /* Unregister the data tree */
+ ast_data_unregister(NULL);
/* Unregister channel */
AST_LIST_LOCK(&agents);
/* Hangup all interfaces if they have an owner */
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 7167010aa..e035b4bfe 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -117,6 +117,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/devicestate.h"
#include "asterisk/paths.h"
#include "asterisk/ccss.h"
+#include "asterisk/data.h"
/*** DOCUMENTATION
<application name="DAHDISendKeypadFacility" language="en_US">
@@ -1204,6 +1205,77 @@ struct dahdi_pvt {
char dialstring[AST_CHANNEL_NAME];
};
+#define DATA_EXPORT_DAHDI_PVT(MEMBER) \
+ MEMBER(dahdi_pvt, cid_rxgain, AST_DATA_DOUBLE) \
+ MEMBER(dahdi_pvt, rxgain, AST_DATA_DOUBLE) \
+ MEMBER(dahdi_pvt, txgain, AST_DATA_DOUBLE) \
+ MEMBER(dahdi_pvt, txdrc, AST_DATA_DOUBLE) \
+ MEMBER(dahdi_pvt, rxdrc, AST_DATA_DOUBLE) \
+ MEMBER(dahdi_pvt, adsi, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, answeronpolarityswitch, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, busydetect, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, callreturn, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, callwaiting, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, callwaitingcallerid, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, cancallforward, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, canpark, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, confirmanswer, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, destroy, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, didtdd, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, dialednone, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, dialing, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, digital, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, dnd, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, echobreak, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, echocanbridged, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, echocanon, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, faxhandled, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, usefaxbuffers, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, bufferoverrideinuse, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, firstradio, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, hanguponpolarityswitch, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, hardwaredtmf, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, hidecallerid, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, hidecalleridname, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, ignoredtmf, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, immediate, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, inalarm, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, mate, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, outgoing, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, permcallwaiting, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, priindication_oob, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, priexclusive, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, pulse, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, pulsedial, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, restartpending, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, restrictcid, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, threewaycalling, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, transfer, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, use_callerid, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, use_callingpres, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, usedistinctiveringdetection, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, dahditrcallerid, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, transfertobusy, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, mwimonitor_neon, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, mwimonitor_fsk, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, mwimonitor_rpas, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, mwimonitoractive, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, mwisendactive, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, inservice, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, locallyblocked, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, remotelyblocked, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, manages_span_alarms, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, use_smdi, AST_DATA_BOOLEAN) \
+ MEMBER(dahdi_pvt, context, AST_DATA_STRING) \
+ MEMBER(dahdi_pvt, defcontext, AST_DATA_STRING) \
+ MEMBER(dahdi_pvt, exten, AST_DATA_STRING) \
+ MEMBER(dahdi_pvt, language, AST_DATA_STRING) \
+ MEMBER(dahdi_pvt, mohinterpret, AST_DATA_STRING) \
+ MEMBER(dahdi_pvt, mohsuggest, AST_DATA_STRING) \
+ MEMBER(dahdi_pvt, parkinglot, AST_DATA_STRING)
+
+AST_DATA_STRUCTURE(dahdi_pvt, DATA_EXPORT_DAHDI_PVT);
+
static struct dahdi_pvt *iflist = NULL; /*!< Main interface list start */
static struct dahdi_pvt *ifend = NULL; /*!< Main interface list end */
@@ -15823,6 +15895,7 @@ static int __unload_module(void)
ast_manager_unregister("DAHDIDNDon");
ast_manager_unregister("DAHDIShowChannels");
ast_manager_unregister("DAHDIRestart");
+ ast_data_unregister(NULL);
ast_channel_unregister(&dahdi_tech);
/* Hangup all interfaces if they have an owner */
@@ -17402,6 +17475,163 @@ static int setup_dahdi(int reload)
return res;
}
+/*!
+ * \internal
+ * \brief Callback used to generate the dahdi status tree.
+ * \param[in] search The search pattern tree.
+ * \retval NULL on error.
+ * \retval non-NULL The generated tree.
+ */
+static int dahdi_status_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ int ctl, res, span;
+ struct ast_data *data_span, *data_alarms;
+ struct dahdi_spaninfo s;
+
+ ctl = open("/dev/dahdi/ctl", O_RDWR);
+ if (ctl < 0) {
+ ast_log(LOG_ERROR, "No DAHDI found. Unable to open /dev/dahdi/ctl: %s\n", strerror(errno));
+ return -1;
+ }
+ for (span = 1; span < DAHDI_MAX_SPANS; ++span) {
+ s.spanno = span;
+ res = ioctl(ctl, DAHDI_SPANSTAT, &s);
+ if (res) {
+ continue;
+ }
+
+ data_span = ast_data_add_node(data_root, "span");
+ if (!data_span) {
+ continue;
+ }
+ ast_data_add_str(data_span, "description", s.desc);
+
+ /* insert the alarms status */
+ data_alarms = ast_data_add_node(data_span, "alarms");
+ if (!data_alarms) {
+ continue;
+ }
+
+ ast_data_add_bool(data_alarms, "BLUE", s.alarms & DAHDI_ALARM_BLUE);
+ ast_data_add_bool(data_alarms, "YELLOW", s.alarms & DAHDI_ALARM_YELLOW);
+ ast_data_add_bool(data_alarms, "RED", s.alarms & DAHDI_ALARM_RED);
+ ast_data_add_bool(data_alarms, "LOOPBACK", s.alarms & DAHDI_ALARM_LOOPBACK);
+ ast_data_add_bool(data_alarms, "RECOVER", s.alarms & DAHDI_ALARM_RECOVER);
+ ast_data_add_bool(data_alarms, "NOTOPEN", s.alarms & DAHDI_ALARM_NOTOPEN);
+
+ ast_data_add_int(data_span, "irqmisses", s.irqmisses);
+ ast_data_add_int(data_span, "bpviol", s.bpvcount);
+ ast_data_add_int(data_span, "crc4", s.crc4count);
+ ast_data_add_str(data_span, "framing", s.lineconfig & DAHDI_CONFIG_D4 ? "D4" :
+ s.lineconfig & DAHDI_CONFIG_ESF ? "ESF" :
+ s.lineconfig & DAHDI_CONFIG_CCS ? "CCS" :
+ "CAS");
+ ast_data_add_str(data_span, "coding", s.lineconfig & DAHDI_CONFIG_B8ZS ? "B8ZS" :
+ s.lineconfig & DAHDI_CONFIG_HDB3 ? "HDB3" :
+ s.lineconfig & DAHDI_CONFIG_AMI ? "AMI" :
+ "Unknown");
+ ast_data_add_str(data_span, "options", s.lineconfig & DAHDI_CONFIG_CRC4 ?
+ s.lineconfig & DAHDI_CONFIG_NOTOPEN ? "CRC4/YEL" : "CRC4" :
+ s.lineconfig & DAHDI_CONFIG_NOTOPEN ? "YEL" : "");
+ ast_data_add_str(data_span, "lbo", lbostr[s.lbo]);
+
+ /* if this span doesn't match remove it. */
+ if (!ast_data_search_match(search, data_span)) {
+ ast_data_remove_node(data_root, data_span);
+ }
+ }
+ close(ctl);
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Callback used to generate the dahdi channels tree.
+ * \param[in] search The search pattern tree.
+ * \retval NULL on error.
+ * \retval non-NULL The generated tree.
+ */
+static int dahdi_channels_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ struct dahdi_pvt *tmp;
+ struct ast_data *data_channel;
+
+ ast_mutex_lock(&iflock);
+ for (tmp = iflist; tmp; tmp = tmp->next) {
+ data_channel = ast_data_add_node(data_root, "channel");
+ if (!data_channel) {
+ continue;
+ }
+
+ ast_data_add_structure(dahdi_pvt, data_channel, tmp);
+
+ /* if this channel doesn't match remove it. */
+ if (!ast_data_search_match(search, data_channel)) {
+ ast_data_remove_node(data_root, data_channel);
+ }
+ }
+ ast_mutex_unlock(&iflock);
+
+ return 0;
+}
+
+/*!
+ * \internal
+ * \brief Callback used to generate the dahdi channels tree.
+ * \param[in] search The search pattern tree.
+ * \retval NULL on error.
+ * \retval non-NULL The generated tree.
+ */
+static int dahdi_version_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ int pseudo_fd = -1;
+ struct dahdi_versioninfo vi = {
+ .version = "Unknown",
+ .echo_canceller = "Unknown"
+ };
+
+ if ((pseudo_fd = open("/dev/dahdi/ctl", O_RDONLY)) < 0) {
+ ast_log(LOG_ERROR, "Failed to open control file to get version.\n");
+ return -1;
+ }
+
+ if (ioctl(pseudo_fd, DAHDI_GETVERSION, &vi)) {
+ ast_log(LOG_ERROR, "Failed to get DAHDI version: %s\n", strerror(errno));
+ }
+
+ close(pseudo_fd);
+
+ ast_data_add_str(data_root, "value", vi.version);
+ ast_data_add_str(data_root, "echocanceller", vi.echo_canceller);
+
+ return 0;
+}
+
+static const struct ast_data_handler dahdi_status_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = dahdi_status_data_provider_get
+};
+
+static const struct ast_data_handler dahdi_channels_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = dahdi_channels_data_provider_get
+};
+
+static const struct ast_data_handler dahdi_version_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = dahdi_version_data_provider_get
+};
+
+static const struct ast_data_entry dahdi_data_providers[] = {
+ AST_DATA_ENTRY("asterisk/channel/dahdi/status", &dahdi_status_data_provider),
+ AST_DATA_ENTRY("asterisk/channel/dahdi/channels", &dahdi_channels_data_provider),
+ AST_DATA_ENTRY("asterisk/channel/dahdi/version", &dahdi_version_data_provider)
+};
+
static int load_module(void)
{
int res;
@@ -17467,7 +17697,8 @@ static int load_module(void)
#endif
ast_cli_register_multiple(dahdi_cli, ARRAY_LEN(dahdi_cli));
-
+ /* register all the data providers */
+ ast_data_register_multiple(dahdi_data_providers, ARRAY_LEN(dahdi_data_providers));
memset(round_robin, 0, sizeof(round_robin));
ast_manager_register_xml("DAHDITransfer", 0, action_transfer);
ast_manager_register_xml("DAHDIHangup", 0, action_transferhangup);
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index d69194df4..ba482a065 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -90,6 +90,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/astobj2.h"
#include "asterisk/timing.h"
#include "asterisk/taskprocessor.h"
+#include "asterisk/test.h"
+#include "asterisk/data.h"
#include "iax2.h"
#include "iax2-parser.h"
@@ -13852,6 +13854,125 @@ static struct ast_cli_entry cli_iax2[] = {
#endif /* IAXTESTS */
};
+#ifdef TEST_FRAMEWORK
+AST_TEST_DEFINE(test_iax2_peers_get)
+{
+ struct ast_data_query query = {
+ .path = "/asterisk/channel/iax2/peers",
+ .search = "peers/peer/name=test_peer_data_provider"
+ };
+ struct ast_data *node;
+ struct iax2_peer *peer;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "iax2_peers_get_data_test";
+ info->category = "main/data/iax2/peers";
+ info->summary = "IAX2 peers data providers unit test";
+ info->description =
+ "Tests whether the IAX2 peers data provider implementation works as expected.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ /* build a test peer */
+ peer = build_peer("test_peer_data_provider", NULL, NULL, 0);
+ if (!peer) {
+ return AST_TEST_FAIL;
+ }
+ peer->expiry= 1010;
+ ao2_link(peers, peer);
+
+ node = ast_data_get(&query);
+ if (!node) {
+ ao2_unlink(peers, peer);
+ peer_unref(peer);
+ return AST_TEST_FAIL;
+ }
+
+ /* check returned data node. */
+ if (strcmp(ast_data_retrieve_string(node, "peer/name"), "test_peer_data_provider")) {
+ ao2_unlink(peers, peer);
+ peer_unref(peer);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_data_retrieve_int(node, "peer/expiry") != 1010) {
+ ao2_unlink(peers, peer);
+ peer_unref(peer);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ /* release resources */
+ ast_data_free(node);
+
+ ao2_unlink(peers, peer);
+ peer_unref(peer);
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(test_iax2_users_get)
+{
+ struct ast_data_query query = {
+ .path = "/asterisk/channel/iax2/users",
+ .search = "users/user/name=test_user_data_provider"
+ };
+ struct ast_data *node;
+ struct iax2_user *user;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "iax2_users_get_data_test";
+ info->category = "main/data/iax2/users";
+ info->summary = "IAX2 users data providers unit test";
+ info->description =
+ "Tests whether the IAX2 users data provider implementation works as expected.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ user = build_user("test_user_data_provider", NULL, NULL, 0);
+ if (!user) {
+ return AST_TEST_FAIL;
+ }
+ user->amaflags = 1010;
+ ao2_link(users, user);
+
+ node = ast_data_get(&query);
+ if (!node) {
+ ao2_unlink(users, user);
+ user_unref(user);
+ return AST_TEST_FAIL;
+ }
+
+ if (strcmp(ast_data_retrieve_string(node, "user/name"), "test_user_data_provider")) {
+ ao2_unlink(users, user);
+ user_unref(user);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_data_retrieve_int(node, "user/amaflags") != 1010) {
+ ao2_unlink(users, user);
+ user_unref(user);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ ast_data_free(node);
+
+ ao2_unlink(users, user);
+ user_unref(user);
+
+ return AST_TEST_PASS;
+}
+#endif
+
static void cleanup_thread_list(void *head)
{
AST_LIST_HEAD(iax2_thread_list, iax2_thread);
@@ -13907,7 +14028,24 @@ static int __unload_module(void)
ast_netsock_release(netsock);
ast_netsock_release(outsock);
-
+ for (x = 0; x < ARRAY_LEN(iaxs); x++) {
+ if (iaxs[x]) {
+ iax2_destroy(x);
+ }
+ }
+ ast_manager_unregister( "IAXpeers" );
+ ast_manager_unregister( "IAXpeerlist" );
+ ast_manager_unregister( "IAXnetstats" );
+ ast_manager_unregister( "IAXregistry" );
+ ast_unregister_application(papp);
+#ifdef TEST_FRAMEWORK
+ AST_TEST_UNREGISTER(test_iax2_peers_get);
+ AST_TEST_UNREGISTER(test_iax2_users_get);
+#endif
+ ast_data_unregister(NULL);
+ ast_cli_unregister_multiple(cli_iax2, ARRAY_LEN(cli_iax2));
+ ast_unregister_switch(&iax2_switch);
+ ast_channel_unregister(&iax2_tech);
delete_users();
iax_provision_unload();
reload_firmware(1);
@@ -14048,6 +14186,188 @@ container_fail:
return AST_MODULE_LOAD_FAILURE;
}
+
+#define DATA_EXPORT_IAX2_PEER(MEMBER) \
+ MEMBER(iax2_peer, name, AST_DATA_STRING) \
+ MEMBER(iax2_peer, username, AST_DATA_STRING) \
+ MEMBER(iax2_peer, secret, AST_DATA_PASSWORD) \
+ MEMBER(iax2_peer, dbsecret, AST_DATA_PASSWORD) \
+ MEMBER(iax2_peer, outkey, AST_DATA_STRING) \
+ MEMBER(iax2_peer, regexten, AST_DATA_STRING) \
+ MEMBER(iax2_peer, context, AST_DATA_STRING) \
+ MEMBER(iax2_peer, peercontext, AST_DATA_STRING) \
+ MEMBER(iax2_peer, mailbox, AST_DATA_STRING) \
+ MEMBER(iax2_peer, mohinterpret, AST_DATA_STRING) \
+ MEMBER(iax2_peer, mohsuggest, AST_DATA_STRING) \
+ MEMBER(iax2_peer, inkeys, AST_DATA_STRING) \
+ MEMBER(iax2_peer, cid_num, AST_DATA_STRING) \
+ MEMBER(iax2_peer, cid_name, AST_DATA_STRING) \
+ MEMBER(iax2_peer, zonetag, AST_DATA_STRING) \
+ MEMBER(iax2_peer, parkinglot, AST_DATA_STRING) \
+ MEMBER(iax2_peer, expiry, AST_DATA_SECONDS) \
+ MEMBER(iax2_peer, callno, AST_DATA_INTEGER) \
+ MEMBER(iax2_peer, lastms, AST_DATA_MILLISECONDS) \
+ MEMBER(iax2_peer, maxms, AST_DATA_MILLISECONDS) \
+ MEMBER(iax2_peer, pokefreqok, AST_DATA_MILLISECONDS) \
+ MEMBER(iax2_peer, pokefreqnotok, AST_DATA_MILLISECONDS) \
+ MEMBER(iax2_peer, historicms, AST_DATA_INTEGER) \
+ MEMBER(iax2_peer, smoothing, AST_DATA_BOOLEAN) \
+ MEMBER(iax2_peer, maxcallno, AST_DATA_INTEGER)
+
+AST_DATA_STRUCTURE(iax2_peer, DATA_EXPORT_IAX2_PEER);
+
+static int peers_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ struct ast_data *data_peer;
+ struct iax2_peer *peer;
+ struct ao2_iterator i;
+ char status[20];
+ struct ast_str *encmethods = ast_str_alloca(256);
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ data_peer = ast_data_add_node(data_root, "peer");
+ if (!data_peer) {
+ peer_unref(peer);
+ continue;
+ }
+
+ ast_data_add_structure(iax2_peer, data_peer, peer);
+
+ ast_data_add_codecs(data_peer, "codecs", peer->capability);
+
+ peer_status(peer, status, sizeof(status));
+ ast_data_add_str(data_peer, "status", status);
+
+ ast_data_add_str(data_peer, "host", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "");
+
+ ast_data_add_str(data_peer, "mask", ast_inet_ntoa(peer->mask));
+
+ ast_data_add_int(data_peer, "port", ntohs(peer->addr.sin_port));
+
+ ast_data_add_bool(data_peer, "trunk", ast_test_flag64(peer, IAX_TRUNK));
+
+ ast_data_add_bool(data_peer, "dynamic", ast_test_flag64(peer, IAX_DYNAMIC));
+
+ encmethods_to_str(peer->encmethods, encmethods);
+ ast_data_add_str(data_peer, "encryption", peer->encmethods ? ast_str_buffer(encmethods) : "no");
+
+ peer_unref(peer);
+
+ if (!ast_data_search_match(search, data_peer)) {
+ ast_data_remove_node(data_root, data_peer);
+ }
+ }
+ ao2_iterator_destroy(&i);
+
+ return 0;
+}
+
+#define DATA_EXPORT_IAX2_USER(MEMBER) \
+ MEMBER(iax2_user, name, AST_DATA_STRING) \
+ MEMBER(iax2_user, dbsecret, AST_DATA_PASSWORD) \
+ MEMBER(iax2_user, accountcode, AST_DATA_STRING) \
+ MEMBER(iax2_user, mohinterpret, AST_DATA_STRING) \
+ MEMBER(iax2_user, mohsuggest, AST_DATA_STRING) \
+ MEMBER(iax2_user, inkeys, AST_DATA_STRING) \
+ MEMBER(iax2_user, language, AST_DATA_STRING) \
+ MEMBER(iax2_user, cid_num, AST_DATA_STRING) \
+ MEMBER(iax2_user, cid_name, AST_DATA_STRING) \
+ MEMBER(iax2_user, parkinglot, AST_DATA_STRING) \
+ MEMBER(iax2_user, maxauthreq, AST_DATA_INTEGER) \
+ MEMBER(iax2_user, curauthreq, AST_DATA_INTEGER)
+
+AST_DATA_STRUCTURE(iax2_user, DATA_EXPORT_IAX2_USER);
+
+static int users_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ struct ast_data *data_user, *data_authmethods, *data_enum_node;
+ struct iax2_user *user;
+ struct ao2_iterator i;
+ char auth[90];
+ char *pstr = "";
+
+ i = ao2_iterator_init(users, 0);
+ while ((user = ao2_iterator_next(&i))) {
+ data_user = ast_data_add_node(data_root, "user");
+ if (!data_user) {
+ user_unref(user);
+ continue;
+ }
+
+ ast_data_add_structure(iax2_user, data_user, user);
+
+ ast_data_add_codecs(data_user, "codecs", user->capability);
+
+ if (!ast_strlen_zero(user->secret)) {
+ ast_copy_string(auth, user->secret, sizeof(auth));
+ } else if (!ast_strlen_zero(user->inkeys)) {
+ snprintf(auth, sizeof(auth), "Key: %s", user->inkeys);
+ } else {
+ ast_copy_string(auth, "no secret", sizeof(auth));
+ }
+ ast_data_add_password(data_user, "secret", auth);
+
+ ast_data_add_str(data_user, "context", user->contexts ? user->contexts->context : DEFAULT_CONTEXT);
+
+ /* authmethods */
+ data_authmethods = ast_data_add_node(data_user, "authmethods");
+ if (!data_authmethods) {
+ ast_data_remove_node(data_root, data_user);
+ continue;
+ }
+ ast_data_add_bool(data_authmethods, "rsa", user->authmethods & IAX_AUTH_RSA);
+ ast_data_add_bool(data_authmethods, "md5", user->authmethods & IAX_AUTH_MD5);
+ ast_data_add_bool(data_authmethods, "plaintext", user->authmethods & IAX_AUTH_PLAINTEXT);
+
+ /* amaflags */
+ data_enum_node = ast_data_add_node(data_user, "amaflags");
+ if (!data_enum_node) {
+ ast_data_remove_node(data_root, data_user);
+ continue;
+ }
+ ast_data_add_int(data_enum_node, "value", user->amaflags);
+ ast_data_add_str(data_enum_node, "text", ast_cdr_flags2str(user->amaflags));
+
+ ast_data_add_bool(data_user, "access-control", user->ha ? 1 : 0);
+
+ if (ast_test_flag64(user, IAX_CODEC_NOCAP)) {
+ pstr = "REQ only";
+ } else if (ast_test_flag64(user, IAX_CODEC_NOPREFS)) {
+ pstr = "disabled";
+ } else {
+ pstr = ast_test_flag64(user, IAX_CODEC_USER_FIRST) ? "caller" : "host";
+ }
+ ast_data_add_str(data_user, "codec-preferences", pstr);
+
+ user_unref(user);
+
+ if (!ast_data_search_match(search, data_user)) {
+ ast_data_remove_node(data_root, data_user);
+ }
+ }
+ ao2_iterator_destroy(&i);
+
+ return 0;
+}
+
+static const struct ast_data_handler peers_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = peers_data_provider_get
+};
+
+static const struct ast_data_handler users_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = users_data_provider_get
+};
+
+static const struct ast_data_entry iax2_data_providers[] = {
+ AST_DATA_ENTRY("asterisk/channel/iax2/peers", &peers_data_provider),
+ AST_DATA_ENTRY("asterisk/channel/iax2/users", &users_data_provider),
+};
+
/*! \brief Load IAX2 module, load configuraiton ---*/
static int load_module(void)
{
@@ -14101,6 +14421,13 @@ static int load_module(void)
}
ast_netsock_init(outsock);
+#ifdef TEST_FRAMEWORK
+ AST_TEST_REGISTER(test_iax2_peers_get);
+ AST_TEST_REGISTER(test_iax2_users_get);
+#endif
+ /* Register AstData providers */
+ ast_data_register_multiple(iax2_data_providers, ARRAY_LEN(iax2_data_providers));
+
ast_cli_register_multiple(cli_iax2, ARRAY_LEN(cli_iax2));
ast_register_application_xml(papp, iax2_prov_app);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 5d8b7b4e9..c65d85b9f 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -261,6 +261,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/event.h"
#include "asterisk/stun.h"
#include "asterisk/cel.h"
+#include "asterisk/data.h"
#include "asterisk/aoc.h"
#include "sip/include/sip.h"
#include "sip/include/globals.h"
@@ -27475,6 +27476,234 @@ static void sip_unregister_tests(void)
sip_dialplan_function_unregister_tests();
}
+#ifdef TEST_FRAMEWORK
+AST_TEST_DEFINE(test_sip_peers_get)
+{
+ struct sip_peer *peer;
+ struct ast_data *node;
+ struct ast_data_query query = {
+ .path = "/asterisk/channel/sip/peers",
+ .search = "peers/peer/name=test_peer_data_provider"
+ };
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "sip_peers_get_data_test";
+ info->category = "main/data/sip/peers";
+ info->summary = "SIP peers data providers unit test";
+ info->description =
+ "Tests whether the SIP peers data provider implementation works as expected.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ /* Create the peer that we will retrieve. */
+ peer = build_peer("test_peer_data_provider", NULL, NULL, 0, 0);
+ if (!peer) {
+ return AST_TEST_FAIL;
+ }
+ peer->type = SIP_TYPE_USER;
+ peer->call_limit = 10;
+ ao2_link(peers, peer);
+
+ /* retrieve the chan_sip/peers tree and check the created peer. */
+ node = ast_data_get(&query);
+ if (!node) {
+ ao2_unlink(peers, peer);
+ ao2_ref(peer, -1);
+ return AST_TEST_FAIL;
+ }
+
+ /* compare item. */
+ if (strcmp(ast_data_retrieve_string(node, "peer/name"), "test_peer_data_provider")) {
+ ao2_unlink(peers, peer);
+ ao2_ref(peer, -1);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ if (strcmp(ast_data_retrieve_string(node, "peer/type"), "user")) {
+ ao2_unlink(peers, peer);
+ ao2_ref(peer, -1);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ if (ast_data_retrieve_int(node, "peer/call_limit") != 10) {
+ ao2_unlink(peers, peer);
+ ao2_ref(peer, -1);
+ ast_data_free(node);
+ return AST_TEST_FAIL;
+ }
+
+ /* release resources */
+ ast_data_free(node);
+
+ ao2_unlink(peers, peer);
+ ao2_ref(peer, -1);
+
+ return AST_TEST_PASS;
+}
+
+#endif
+
+#define DATA_EXPORT_SIP_PEER(MEMBER) \
+ MEMBER(sip_peer, name, AST_DATA_STRING) \
+ MEMBER(sip_peer, secret, AST_DATA_PASSWORD) \
+ MEMBER(sip_peer, md5secret, AST_DATA_PASSWORD) \
+ MEMBER(sip_peer, remotesecret, AST_DATA_PASSWORD) \
+ MEMBER(sip_peer, context, AST_DATA_STRING) \
+ MEMBER(sip_peer, subscribecontext, AST_DATA_STRING) \
+ MEMBER(sip_peer, username, AST_DATA_STRING) \
+ MEMBER(sip_peer, accountcode, AST_DATA_STRING) \
+ MEMBER(sip_peer, tohost, AST_DATA_STRING) \
+ MEMBER(sip_peer, regexten, AST_DATA_STRING) \
+ MEMBER(sip_peer, fromuser, AST_DATA_STRING) \
+ MEMBER(sip_peer, fromdomain, AST_DATA_STRING) \
+ MEMBER(sip_peer, fullcontact, AST_DATA_STRING) \
+ MEMBER(sip_peer, cid_num, AST_DATA_STRING) \
+ MEMBER(sip_peer, cid_name, AST_DATA_STRING) \
+ MEMBER(sip_peer, vmexten, AST_DATA_STRING) \
+ MEMBER(sip_peer, language, AST_DATA_STRING) \
+ MEMBER(sip_peer, mohinterpret, AST_DATA_STRING) \
+ MEMBER(sip_peer, mohsuggest, AST_DATA_STRING) \
+ MEMBER(sip_peer, parkinglot, AST_DATA_STRING) \
+ MEMBER(sip_peer, useragent, AST_DATA_STRING) \
+ MEMBER(sip_peer, mwi_from, AST_DATA_STRING) \
+ MEMBER(sip_peer, engine, AST_DATA_STRING) \
+ MEMBER(sip_peer, unsolicited_mailbox, AST_DATA_STRING) \
+ MEMBER(sip_peer, is_realtime, AST_DATA_BOOLEAN) \
+ MEMBER(sip_peer, host_dynamic, AST_DATA_BOOLEAN) \
+ MEMBER(sip_peer, autoframing, AST_DATA_BOOLEAN) \
+ MEMBER(sip_peer, inUse, AST_DATA_INTEGER) \
+ MEMBER(sip_peer, inRinging, AST_DATA_INTEGER) \
+ MEMBER(sip_peer, onHold, AST_DATA_INTEGER) \
+ MEMBER(sip_peer, call_limit, AST_DATA_INTEGER) \
+ MEMBER(sip_peer, t38_maxdatagram, AST_DATA_INTEGER) \
+ MEMBER(sip_peer, maxcallbitrate, AST_DATA_INTEGER) \
+ MEMBER(sip_peer, rtptimeout, AST_DATA_SECONDS) \
+ MEMBER(sip_peer, rtpholdtimeout, AST_DATA_SECONDS) \
+ MEMBER(sip_peer, rtpkeepalive, AST_DATA_SECONDS) \
+ MEMBER(sip_peer, lastms, AST_DATA_MILLISECONDS) \
+ MEMBER(sip_peer, maxms, AST_DATA_MILLISECONDS) \
+ MEMBER(sip_peer, qualifyfreq, AST_DATA_MILLISECONDS) \
+ MEMBER(sip_peer, timer_t1, AST_DATA_MILLISECONDS) \
+ MEMBER(sip_peer, timer_b, AST_DATA_MILLISECONDS)
+
+AST_DATA_STRUCTURE(sip_peer, DATA_EXPORT_SIP_PEER);
+
+static int peers_data_provider_get(const struct ast_data_search *search,
+ struct ast_data *data_root)
+{
+ struct sip_peer *peer;
+ struct ao2_iterator i;
+ struct ast_data *data_peer, *data_peer_mailboxes = NULL, *data_peer_mailbox, *enum_node;
+ struct ast_data *data_sip_options;
+ int total_mailboxes, x;
+ struct sip_mailbox *mailbox;
+
+ i = ao2_iterator_init(peers, 0);
+ while ((peer = ao2_iterator_next(&i))) {
+ ao2_lock(peer);
+
+ data_peer = ast_data_add_node(data_root, "peer");
+ if (!data_peer) {
+ ao2_unlock(peer);
+ ao2_ref(peer, -1);
+ continue;
+ }
+
+ ast_data_add_structure(sip_peer, data_peer, peer);
+
+ /* transfer mode */
+ enum_node = ast_data_add_node(data_peer, "allowtransfer");
+ if (!enum_node) {
+ continue;
+ }
+ ast_data_add_str(enum_node, "text", transfermode2str(peer->allowtransfer));
+ ast_data_add_int(enum_node, "value", peer->allowtransfer);
+
+ /* transports */
+ ast_data_add_str(data_peer, "transports", get_transport_list(peer->transports));
+
+ /* peer type */
+ if ((peer->type & SIP_TYPE_USER) && (peer->type & SIP_TYPE_PEER)) {
+ ast_data_add_str(data_peer, "type", "friend");
+ } else if (peer->type & SIP_TYPE_PEER) {
+ ast_data_add_str(data_peer, "type", "peer");
+ } else if (peer->type & SIP_TYPE_USER) {
+ ast_data_add_str(data_peer, "type", "user");
+ }
+
+ /* mailboxes */
+ total_mailboxes = 0;
+ AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
+ if (!total_mailboxes) {
+ data_peer_mailboxes = ast_data_add_node(data_peer, "mailboxes");
+ if (!data_peer_mailboxes) {
+ break;
+ }
+ total_mailboxes++;
+ }
+
+ data_peer_mailbox = ast_data_add_node(data_peer_mailboxes, "mailbox");
+ if (!data_peer_mailbox) {
+ continue;
+ }
+ ast_data_add_str(data_peer_mailbox, "mailbox", mailbox->mailbox);
+ ast_data_add_str(data_peer_mailbox, "context", mailbox->context);
+ }
+
+ /* amaflags */
+ enum_node = ast_data_add_node(data_peer, "amaflags");
+ if (!enum_node) {
+ continue;
+ }
+ ast_data_add_int(enum_node, "value", peer->amaflags);
+ ast_data_add_str(enum_node, "text", ast_cdr_flags2str(peer->amaflags));
+
+ /* sip options */
+ data_sip_options = ast_data_add_node(data_peer, "sipoptions");
+ if (!data_sip_options) {
+ continue;
+ }
+ for (x = 0 ; x < ARRAY_LEN(sip_options); x++) {
+ ast_data_add_bool(data_sip_options, sip_options[x].text, peer->sipoptions & sip_options[x].id);
+ }
+
+ /* callingpres */
+ enum_node = ast_data_add_node(data_peer, "callingpres");
+ if (!enum_node) {
+ continue;
+ }
+ ast_data_add_int(enum_node, "value", peer->callingpres);
+ ast_data_add_str(enum_node, "text", ast_describe_caller_presentation(peer->callingpres));
+
+ /* codecs */
+ ast_data_add_codecs(data_peer, "codecs", peer->capability);
+
+ if (!ast_data_search_match(search, data_peer)) {
+ ast_data_remove_node(data_root, data_peer);
+ }
+
+ ao2_unlock(peer);
+ ao2_ref(peer, -1);
+ }
+ ao2_iterator_destroy(&i);
+
+ return 0;
+}
+
+static const struct ast_data_handler peers_data_provider = {
+ .version = AST_DATA_HANDLER_VERSION,
+ .get = peers_data_provider_get
+};
+
+static const struct ast_data_entry sip_data_providers[] = {
+ AST_DATA_ENTRY("asterisk/channel/sip/peers", &peers_data_provider),
+};
+
/*! \brief PBX load module - initialization */
static int load_module(void)
{
@@ -27521,6 +27750,13 @@ static int load_module(void)
return AST_MODULE_LOAD_FAILURE;
}
+#ifdef TEST_FRAMEWORK
+ AST_TEST_REGISTER(test_sip_peers_get);
+#endif
+
+ /* Register AstData providers */
+ ast_data_register_multiple(sip_data_providers, ARRAY_LEN(sip_data_providers));
+
/* Register all CLI functions for SIP */
ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip));
@@ -27614,6 +27850,12 @@ static int unload_module(void)
ast_unregister_application(app_sipaddheader);
ast_unregister_application(app_sipremoveheader);
+#ifdef TEST_FRAMEWORK
+ AST_TEST_UNREGISTER(test_sip_peers_get);
+#endif
+ /* Unregister all the AstData providers */
+ ast_data_unregister(NULL);
+
/* Unregister CLI commands */
ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip));