Update everything in the main directory for Digium phone additions.

git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10-digiumphones@361199 65c4cc65-6c06-0410-ace0-fbb531ad65f3
10-digiumphones
Mark Michelson 14 years ago
parent c71f0ac4b1
commit c343f0be25

@ -274,18 +274,21 @@ static int (*ast_inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsg
static int (*ast_inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs) = NULL;
static int (*ast_sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context) = NULL;
static int (*ast_messagecount_func)(const char *context, const char *mailbox, const char *folder) = NULL;
static int (*ast_copy_recording_to_vm_func)(struct ast_vm_recording_data *vm_rec_data) = NULL;
void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder),
int (*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs),
int (*inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs),
int (*messagecount_func)(const char *context, const char *mailbox, const char *folder),
int (*sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context))
int (*sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context),
int (*copy_recording_to_vm_func)(struct ast_vm_recording_data *vm_rec_data))
{
ast_has_voicemail_func = has_voicemail_func;
ast_inboxcount_func = inboxcount_func;
ast_inboxcount2_func = inboxcount2_func;
ast_messagecount_func = messagecount_func;
ast_sayname_func = sayname_func;
ast_copy_recording_to_vm_func = copy_recording_to_vm_func;
}
void ast_uninstall_vm_functions(void)
@ -295,6 +298,7 @@ void ast_uninstall_vm_functions(void)
ast_inboxcount2_func = NULL;
ast_messagecount_func = NULL;
ast_sayname_func = NULL;
ast_copy_recording_to_vm_func = NULL;
}
int ast_app_has_voicemail(const char *mailbox, const char *folder)
@ -309,7 +313,29 @@ int ast_app_has_voicemail(const char *mailbox, const char *folder)
}
return 0;
}
/*!
* \internal
* \brief Function used as a callback for ast_copy_recording_to_vm when a real one isn't installed.
* \param vm_rec_data Stores crucial information about the voicemail that will basically just be used
* to figure out what the name of the recipient was supposed to be
*/
int ast_app_copy_recording_to_vm(struct ast_vm_recording_data *vm_rec_data)
{
static int warned = 0;
if (ast_copy_recording_to_vm_func) {
return ast_copy_recording_to_vm_func(vm_rec_data);
}
if (warned++ % 10 == 0) {
ast_verb(3, "copy recording to voicemail called to copy %s.%s to %s@%s, but voicemail not loaded.\n",
vm_rec_data->recording_file, vm_rec_data->recording_ext,
vm_rec_data->mailbox, vm_rec_data->context);
}
return -1;
}
int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
{
@ -560,10 +586,16 @@ int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, in
return res;
}
int ast_control_streamfile(struct ast_channel *chan, const char *file,
const char *fwd, const char *rev,
const char *stop, const char *suspend,
const char *restart, int skipms, long *offsetms)
static int control_streamfile(struct ast_channel *chan,
const char *file,
const char *fwd,
const char *rev,
const char *stop,
const char *suspend,
const char *restart,
int skipms,
long *offsetms,
ast_waitstream_fr_cb cb)
{
char *breaks = NULL;
char *end = NULL;
@ -634,7 +666,11 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
ast_seekstream(chan->stream, offset, SEEK_SET);
offset = 0;
}
res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
if (cb) {
res = ast_waitstream_fr_w_cb(chan, breaks, fwd, rev, skipms, cb);
} else {
res = ast_waitstream_fr(chan, breaks, fwd, rev, skipms);
}
}
if (res < 1) {
@ -698,6 +734,28 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
return res;
}
int ast_control_streamfile_w_cb(struct ast_channel *chan,
const char *file,
const char *fwd,
const char *rev,
const char *stop,
const char *suspend,
const char *restart,
int skipms,
long *offsetms,
ast_waitstream_fr_cb cb)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, cb);
}
int ast_control_streamfile(struct ast_channel *chan, const char *file,
const char *fwd, const char *rev,
const char *stop, const char *suspend,
const char *restart, int skipms, long *offsetms)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, NULL);
}
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
{
int d = 0;

@ -136,6 +136,7 @@ int daemon(int, int); /* defined in libresolv of all places */
#include "asterisk/ast_version.h"
#include "asterisk/linkedlists.h"
#include "asterisk/devicestate.h"
#include "asterisk/presencestate.h"
#include "asterisk/module.h"
#include "asterisk/dsp.h"
#include "asterisk/buildinfo.h"
@ -3893,6 +3894,11 @@ int main(int argc, char *argv[])
exit(1);
}
if (ast_presence_state_engine_init()) {
printf("%s", term_quit());
exit(1);
}
ast_dsp_init();
ast_udptl_init();

@ -4365,6 +4365,7 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
case AST_CONTROL_CC:
case AST_CONTROL_READ_ACTION:
case AST_CONTROL_AOC:
case AST_CONTROL_CUSTOM:
case AST_CONTROL_END_OF_Q:
case AST_CONTROL_MCID:
case AST_CONTROL_UPDATE_RTP_PEER:
@ -4554,6 +4555,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
case AST_CONTROL_CC:
case AST_CONTROL_READ_ACTION:
case AST_CONTROL_AOC:
case AST_CONTROL_CUSTOM:
case AST_CONTROL_END_OF_Q:
case AST_CONTROL_MCID:
case AST_CONTROL_UPDATE_RTP_PEER:

@ -65,6 +65,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
static char *extconfig_conf = "extconfig.conf";
static struct ao2_container *cfg_hooks;
static void config_hook_exec(const char *filename, const char *module, struct ast_config *cfg);
/*! \brief Structure to keep comments for rewriting configuration files */
struct ast_comment {
struct ast_comment *next;
@ -2265,12 +2268,44 @@ static struct ast_config_engine text_file_engine = {
.load_func = config_text_file_load,
};
struct ast_config *ast_config_copy(const struct ast_config *old)
{
struct ast_config *new_config = ast_config_new();
struct ast_category *cat_iter;
if (!new_config) {
return NULL;
}
for (cat_iter = old->root; cat_iter; cat_iter = cat_iter->next) {
struct ast_category *new_cat =
ast_category_new(cat_iter->name, cat_iter->file, cat_iter->lineno);
if (!new_cat) {
goto fail;
}
ast_category_append(new_config, new_cat);
if (cat_iter->root) {
new_cat->root = ast_variables_dup(cat_iter->root);
if (!new_cat->root) {
goto fail;
}
new_cat->last = cat_iter->last;
}
}
return new_config;
fail:
ast_config_destroy(new_config);
return NULL;
}
struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, struct ast_flags flags, const char *suggested_include_file, const char *who_asked)
{
char db[256];
char table[256];
struct ast_config_engine *loader = &text_file_engine;
struct ast_config *result;
struct ast_config *result;
/* The config file itself bumps include_level by 1 */
if (cfg->max_include_level > 0 && cfg->include_level == cfg->max_include_level + 1) {
@ -2297,10 +2332,12 @@ struct ast_config *ast_config_internal_load(const char *filename, struct ast_con
result = loader->load_func(db, table, filename, cfg, flags, suggested_include_file, who_asked);
if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED)
if (result && result != CONFIG_STATUS_FILEINVALID && result != CONFIG_STATUS_FILEUNCHANGED) {
result->include_level--;
else if (result != CONFIG_STATUS_FILEINVALID)
config_hook_exec(filename, who_asked, result);
} else if (result != CONFIG_STATUS_FILEINVALID) {
cfg->include_level--;
}
return result;
}
@ -2893,6 +2930,89 @@ static struct ast_cli_entry cli_config[] = {
AST_CLI_DEFINE(handle_cli_config_reload, "Force a reload on modules using a particular configuration file"),
AST_CLI_DEFINE(handle_cli_config_list, "Show all files that have loaded a configuration file"),
};
struct cfg_hook {
const char *name;
const char *filename;
const char *module;
config_hook_cb hook_cb;
};
static void hook_destroy(void *obj)
{
struct cfg_hook *hook = obj;
ast_free((void *) hook->name);
ast_free((void *) hook->filename);
ast_free((void *) hook->module);
}
static int hook_cmp(void *obj, void *arg, int flags)
{
struct cfg_hook *hook1 = obj;
struct cfg_hook *hook2 = arg;
return !(strcasecmp(hook1->name, hook2->name)) ? CMP_MATCH | CMP_STOP : 0;
}
static int hook_hash(const void *obj, const int flags)
{
const struct cfg_hook *hook = obj;
return ast_str_hash(hook->name);
}
void ast_config_hook_unregister(const char *name)
{
struct cfg_hook tmp;
tmp.name = ast_strdupa(name);
ao2_find(cfg_hooks, &tmp, OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
}
static void config_hook_exec(const char *filename, const char *module, struct ast_config *cfg)
{
struct ao2_iterator it;
struct cfg_hook *hook;
if (!(cfg_hooks)) {
return;
}
it = ao2_iterator_init(cfg_hooks, 0);
while ((hook = ao2_iterator_next(&it))) {
if (!strcasecmp(hook->filename, filename) &&
!strcasecmp(hook->module, module)) {
struct ast_config *copy = ast_config_copy(cfg);
hook->hook_cb(copy);
}
ao2_ref(hook, -1);
}
ao2_iterator_destroy(&it);
}
int ast_config_hook_register(const char *name,
const char *filename,
const char *module,
enum config_hook_flags flags,
config_hook_cb hook_cb)
{
struct cfg_hook *hook;
if (!cfg_hooks && !(cfg_hooks = ao2_container_alloc(17, hook_hash, hook_cmp))) {
return -1;
}
if (!(hook = ao2_alloc(sizeof(*hook), hook_destroy))) {
return -1;
}
hook->hook_cb = hook_cb;
hook->filename = ast_strdup(filename);
hook->name = ast_strdup(name);
hook->module = ast_strdup(module);
ao2_link(cfg_hooks, hook);
return 0;
}
int register_config_cli(void)
{

@ -0,0 +1,191 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2011-2012, Digium, Inc.
*
* David Vossel <dvosse@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Encode and Decode custom control frame payload types.
*
* \author David Vossel <dvossel@digium.com>
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/_private.h"
#include "asterisk/custom_control_frame.h"
struct ast_custom_payload {
enum ast_custom_payload_type type;
/*! length of data portion only */
size_t datalen;
char *data;
};
enum ast_custom_payload_type ast_custom_payload_type(struct ast_custom_payload *type)
{
return type->type;
}
size_t ast_custom_payload_len(struct ast_custom_payload *type)
{
return type->datalen + sizeof(struct ast_custom_payload);
}
struct custom_sipinfo {
size_t num_headers;
int content_present;
int useragent_filter_present;
char *data;
};
struct ast_custom_payload *ast_custom_payload_sipinfo_encode(struct ast_variable *headers,
const char *content_type,
const char *content,
const char *useragent_filter)
{
int num_headers = 0;
int content_present = 0;
int content_strlen = 0;
int content_type_strlen = 0;
int useragent_filter_present = 0;
int useragent_filter_len = 0;
size_t datalen = 0;
struct ast_variable *var;
struct ast_custom_payload *payload;
struct custom_sipinfo *sipinfo;
char *data;
datalen += sizeof(struct custom_sipinfo);
for (var = headers; var; var = var->next) {
datalen += strlen(var->name) + 1;
datalen += strlen(var->value) + 1;
num_headers++;
}
if (!ast_strlen_zero(content_type) && !ast_strlen_zero(content)) {
content_type_strlen = strlen(content_type);
content_strlen = strlen(content);
datalen += content_type_strlen + 1;
datalen += content_strlen + 1;
content_present = 1;
}
if (!ast_strlen_zero(useragent_filter)) {
useragent_filter_len = strlen(useragent_filter);
datalen += useragent_filter_len + 1;
useragent_filter_present = 1;
}
if (!(payload = ast_calloc(1, datalen + sizeof(*payload)))) {
return NULL;
}
payload->type = AST_CUSTOM_SIP_INFO;
payload->datalen = datalen;
payload->data = (char *) payload + sizeof(struct ast_custom_payload);
sipinfo = (struct custom_sipinfo *) payload->data;
sipinfo->num_headers = num_headers;
sipinfo->content_present = content_present;
sipinfo->useragent_filter_present = useragent_filter_present;
sipinfo->data = (char *) sipinfo + sizeof(struct custom_sipinfo);
/* store string buffers in payload data
* headers are put in first, followed by content type and then content body. */
data = sipinfo->data;
for (var = headers; var; var = var->next) {
int namelen = strlen(var->name);
int vallen = strlen(var->value);
/*! we already know we have enough room for each of these */
ast_copy_string(data, var->name, namelen+1);
data += namelen + 1; /* skip over the '\0' character */
ast_copy_string(data, var->value, vallen+1);
data += vallen + 1; /* skip over the '\0' character */
}
if (content_present) {
ast_copy_string(data, content_type, content_type_strlen+1);
data += content_type_strlen + 1;
ast_copy_string(data, content, content_strlen+1);
data += content_strlen + 1;
}
if (useragent_filter_present) {
ast_copy_string(data, useragent_filter, useragent_filter_len+1);
}
return payload;
}
int ast_custom_payload_sipinfo_decode(struct ast_custom_payload *pl,
struct ast_variable **headers,
char **content_type,
char **content,
char **useragent_filter)
{
struct custom_sipinfo *sipinfo;
struct ast_variable *cur = NULL;
char *data;
int i;
*headers = NULL;
*content_type = NULL;
*content = NULL;
*useragent_filter = NULL;
if (pl->type != AST_CUSTOM_SIP_INFO) {
return -1;
}
sipinfo = (struct custom_sipinfo *) pl->data;
data = sipinfo->data;
for (i = 0; i < sipinfo->num_headers; i++) {
const char *name;
const char *value;
name = data;
data += strlen(name) + 1;
value = data;
data += strlen(value) + 1;
if (*headers) {
if ((cur->next = ast_variable_new(name, value, ""))) {
cur = cur->next;
}
} else {
*headers = cur = ast_variable_new(name, value, "");
}
}
if (sipinfo->content_present) {
*content_type = ast_strdup(data);
data += strlen(data) + 1;
*content = ast_strdup(data);
data += strlen(data) + 1;
}
if (sipinfo->useragent_filter_present) {
*useragent_filter = ast_strdup(data);
}
return 0;
}

@ -137,6 +137,7 @@ static int ast_event_cmp(void *obj, void *arg, int flags);
static int ast_event_hash_mwi(const void *obj, const int flags);
static int ast_event_hash_devstate(const void *obj, const int flags);
static int ast_event_hash_devstate_change(const void *obj, const int flags);
static int ast_event_hash_presence_state_change(const void *obj, const int flags);
#ifdef LOW_MEMORY
#define NUM_CACHE_BUCKETS 17
@ -181,6 +182,11 @@ static struct {
.hash_fn = ast_event_hash_devstate_change,
.cache_args = { AST_EVENT_IE_DEVICE, AST_EVENT_IE_EID, },
},
[AST_EVENT_PRESENCE_STATE] = {
.hash_fn = ast_event_hash_presence_state_change,
.cache_args = { AST_EVENT_IE_PRESENCE_STATE, },
},
};
/*!
@ -1585,6 +1591,22 @@ static int ast_event_hash_devstate_change(const void *obj, const int flags)
return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE));
}
/*!
* \internal
* \brief Hash function for AST_EVENT_PRESENCE_STATE
*
* \param[in] obj an ast_event
* \param[in] flags unused
*
* \return hash value
*/
static int ast_event_hash_presence_state_change(const void *obj, const int flags)
{
const struct ast_event *event = obj;
return ast_str_hash(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_PROVIDER));
}
static int ast_event_hash(const void *obj, const int flags)
{
const struct ast_event_ref *event_ref;

@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/app.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/custom_control_frame.h"
#include "asterisk/musiconhold.h"
#include "asterisk/config.h"
#include "asterisk/cli.h"
@ -378,6 +379,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Bridge together two channels already in the PBX.</para>
</description>
</manager>
<manager name="Parkinglots" language="en_US">
<synopsis>
Get a list of parking lots
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
<para>List all parking lots as a series of AMI events</para>
</description>
</manager>
***/
#define DEFAULT_PARK_TIME 45000 /*!< ms */
@ -7003,6 +7015,41 @@ static struct ast_cli_entry cli_features[] = {
AST_CLI_DEFINE(handle_parkedcalls, "List currently parked calls"),
};
static int manager_parkinglot_list(struct mansession *s, const struct message *m)
{
const char *id = astman_get_header(m, "ActionID");
char idText[256] = "";
struct ao2_iterator iter;
struct ast_parkinglot *curlot;
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
astman_send_ack(s, m, "Parking lots will follow");
iter = ao2_iterator_init(parkinglots, 0);
while ((curlot = ao2_iterator_next(&iter))) {
astman_append(s, "Event: Parkinglot\r\n"
"Name: %s\r\n"
"StartExten: %d\r\n"
"StopExten: %d\r\n"
"Timeout: %d\r\n"
"\r\n",
curlot->name,
curlot->cfg.parking_start,
curlot->cfg.parking_stop,
curlot->cfg.parkingtime ? curlot->cfg.parkingtime / 1000 : curlot->cfg.parkingtime);
ao2_ref(curlot, -1);
}
astman_append(s,
"Event: ParkinglotsComplete\r\n"
"%s"
"\r\n",idText);
return RESULT_SUCCESS;
}
/*!
* \brief Dump parking lot status
* \param s
@ -8137,6 +8184,7 @@ int ast_features_init(void)
res = ast_register_application2(parkcall, park_call_exec, NULL, NULL, NULL);
if (!res) {
ast_manager_register_xml("ParkedCalls", 0, manager_parking_status);
ast_manager_register_xml("Parkinglots", 0, manager_parkinglot_list);
ast_manager_register_xml("Park", EVENT_FLAG_CALL, manager_park);
ast_manager_register_xml("Bridge", EVENT_FLAG_CALL, action_bridge);
}

@ -1240,9 +1240,15 @@ struct ast_filestream *ast_writefile(const char *filename, const char *type, con
/*!
* \brief the core of all waitstream() functions
*/
static int waitstream_core(struct ast_channel *c, const char *breakon,
const char *forward, const char *reverse, int skip_ms,
int audiofd, int cmdfd, const char *context)
static int waitstream_core(struct ast_channel *c,
const char *breakon,
const char *forward,
const char *reverse,
int skip_ms,
int audiofd,
int cmdfd,
const char *context,
ast_waitstream_fr_cb cb)
{
const char *orig_chan_name = NULL;
int err = 0;
@ -1260,6 +1266,11 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
if (ast_test_flag(c, AST_FLAG_MASQ_NOSTREAM))
orig_chan_name = ast_strdupa(c->name);
if (c->stream && cb) {
long ms_len = ast_tellstream(c->stream) / (ast_format_rate(c->stream->fmt->format) / 1000);
cb(c, ms_len, AST_WAITSTREAM_CB_START);
}
while (c->stream) {
int res;
int ms;
@ -1321,6 +1332,7 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
return res;
}
} else {
enum ast_waitstream_fr_cb_values cb_val = 0;
res = fr->subclass.integer;
if (strchr(forward, res)) {
int eoftest;
@ -1331,13 +1343,19 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
} else {
ungetc(eoftest, c->stream->f);
}
cb_val = AST_WAITSTREAM_CB_FASTFORWARD;
} else if (strchr(reverse, res)) {
ast_stream_rewind(c->stream, skip_ms);
cb_val = AST_WAITSTREAM_CB_REWIND;
} else if (strchr(breakon, res)) {
ast_frfree(fr);
ast_clear_flag(c, AST_FLAG_END_DTMF_ONLY);
return res;
}
}
if (cb_val && cb) {
long ms_len = ast_tellstream(c->stream) / (ast_format_rate(c->stream->fmt->format) / 1000);
cb(c, ms_len, cb_val);
}
}
break;
case AST_FRAME_CONTROL:
@ -1387,21 +1405,32 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
return (err || c->_softhangup) ? -1 : 0;
}
int ast_waitstream_fr_w_cb(struct ast_channel *c,
const char *breakon,
const char *forward,
const char *reverse,
int ms,
ast_waitstream_fr_cb cb)
{
return waitstream_core(c, breakon, forward, reverse, ms,
-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, cb);
}
int ast_waitstream_fr(struct ast_channel *c, const char *breakon, const char *forward, const char *reverse, int ms)
{
return waitstream_core(c, breakon, forward, reverse, ms,
-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */);
-1 /* no audiofd */, -1 /* no cmdfd */, NULL /* no context */, NULL /* no callback */);
}
int ast_waitstream(struct ast_channel *c, const char *breakon)
{
return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL);
return waitstream_core(c, breakon, NULL, NULL, 0, -1, -1, NULL, NULL /* no callback */);
}
int ast_waitstream_full(struct ast_channel *c, const char *breakon, int audiofd, int cmdfd)
{
return waitstream_core(c, breakon, NULL, NULL, 0,
audiofd, cmdfd, NULL /* no context */);
audiofd, cmdfd, NULL /* no context */, NULL /* no callback */);
}
int ast_waitstream_exten(struct ast_channel *c, const char *context)
@ -1412,7 +1441,7 @@ int ast_waitstream_exten(struct ast_channel *c, const char *context)
if (!context)
context = c->context;
return waitstream_core(c, NULL, NULL, NULL, 0,
-1, -1, context);
-1, -1, context, NULL /* no callback */);
}
/*

@ -1216,6 +1216,7 @@ static const struct permalias {
{ EVENT_FLAG_CC, "cc" },
{ EVENT_FLAG_AOC, "aoc" },
{ EVENT_FLAG_TEST, "test" },
{ EVENT_FLAG_MESSAGE, "message" },
{ INT_MAX, "all" },
{ 0, "none" },
};
@ -5330,10 +5331,17 @@ int ast_manager_unregister(char *action)
return 0;
}
static int manager_state_cb(const char *context, const char *exten, enum ast_extension_states state, void *data)
static int manager_state_cb(char *context, char *exten, struct ast_state_cb_info *info, void *data)
{
/* Notify managers of change */
char hint[512];
int state = info->exten_state;
/* only interested in device state for this right now */
if (info->reason != AST_HINT_UPDATE_DEVICE) {
return 0;
}
ast_get_hint(hint, sizeof(hint), NULL, 0, NULL, context, exten);
manager_event(EVENT_FLAG_CALL, "ExtensionStatus", "Exten: %s\r\nContext: %s\r\nHint: %s\r\nStatus: %d\r\n", exten, context, hint, state);

@ -59,6 +59,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/musiconhold.h"
#include "asterisk/app.h"
#include "asterisk/devicestate.h"
#include "asterisk/presencestate.h"
#include "asterisk/event.h"
#include "asterisk/hashtab.h"
#include "asterisk/module.h"
@ -801,7 +802,7 @@ AST_APP_OPTIONS(waitexten_opts, {
struct ast_context;
struct ast_app;
static struct ast_taskprocessor *device_state_tps;
static struct ast_taskprocessor *extension_state_tps;
AST_THREADSTORAGE(switch_data);
AST_THREADSTORAGE(extensionstate_buf);
@ -946,8 +947,16 @@ struct ast_hint {
* Will never be NULL while the hint is in the hints container.
*/
struct ast_exten *exten;
struct ao2_container *callbacks; /*!< Callback container for this extension */
int laststate; /*!< Last known state */
struct ao2_container *callbacks; /*!< Device state callback container for this extension */
/*! Dev state variables */
int laststate; /*!< Last known device state */
/*! Presence state variables */
int last_presence_state; /*!< Last known presence state */
char *last_presence_subtype; /*!< Last known presence subtype string */
char *last_presence_message; /*!< Last known presence message string */
char context_name[AST_MAX_CONTEXT];/*!< Context of destroyed hint extension. */
char exten_name[AST_MAX_EXTENSION];/*!< Extension of destroyed hint extension. */
};
@ -1094,6 +1103,13 @@ static const struct cfextension_states {
{ AST_EXTENSION_INUSE | AST_EXTENSION_ONHOLD, "InUse&Hold" }
};
struct presencechange {
char *provider;
int state;
char *subtype;
char *message;
};
struct statechange {
AST_LIST_ENTRY(statechange) entry;
char dev[0];
@ -1264,6 +1280,8 @@ static char *overrideswitch = NULL;
/*! \brief Subscription for device state change events */
static struct ast_event_sub *device_state_sub;
/*! \brief Subscription for presence state change events */
static struct ast_event_sub *presence_state_sub;
AST_MUTEX_DEFINE_STATIC(maxcalllock);
static int countcalls;
@ -3257,7 +3275,6 @@ const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, stru
int offset, length;
int i, need_substring;
struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
char workspace[20];
if (c) {
ast_channel_lock(c);
@ -3353,6 +3370,7 @@ const char *ast_str_retrieve_variable(struct ast_str **str, ssize_t maxlen, stru
} else if (!strcmp(var, "ASTLOGDIR")) {
s = ast_config_AST_LOG_DIR;
} else if (!strcmp(var, "ENTITYID")) {
char workspace[20];
ast_eid_to_str(workspace, sizeof(workspace), &ast_eid_default);
s = workspace;
}
@ -4473,6 +4491,42 @@ enum ast_extension_states ast_devstate_to_extenstate(enum ast_device_state devst
return AST_EXTENSION_NOT_INUSE;
}
/*!
* \internal
* \brief Parse out the presence portion of the hint string
*/
static char *parse_hint_presence(struct ast_str *hint_args)
{
char *copy = ast_strdupa(ast_str_buffer(hint_args));
char *tmp = "";
if ((tmp = strrchr(copy, ','))) {
*tmp = '\0';
tmp++;
} else {
return NULL;
}
ast_str_set(&hint_args, 0, "%s", tmp);
return ast_str_buffer(hint_args);
}
/*!
* \internal
* \brief Parse out the device portion of the hint string
*/
static char *parse_hint_device(struct ast_str *hint_args)
{
char *copy = ast_strdupa(ast_str_buffer(hint_args));
char *tmp;
if ((tmp = strrchr(copy, ','))) {
*tmp = '\0';
}
ast_str_set(&hint_args, 0, "%s", copy);
return ast_str_buffer(hint_args);
}
static int ast_extension_state3(struct ast_str *hint_app)
{
char *cur;
@ -4480,7 +4534,7 @@ static int ast_extension_state3(struct ast_str *hint_app)
struct ast_devstate_aggregate agg;
/* One or more devices separated with a & character */
rest = ast_str_buffer(hint_app);
rest = parse_hint_device(hint_app);
ast_devstate_aggregate_init(&agg);
while ((cur = strsep(&rest, "&"))) {
@ -4538,6 +4592,206 @@ int ast_extension_state(struct ast_channel *c, const char *context, const char *
return ast_extension_state2(e); /* Check all devices in the hint */
}
static int extension_presence_state_helper(struct ast_exten *e, char **subtype, char **message)
{
struct ast_str *hint_app = ast_str_thread_get(&extensionstate_buf, 32);
char *presence_provider;
const char *app;
if (!e || !hint_app) {
return -1;
}
app = ast_get_extension_app(e);
if (ast_strlen_zero(app)) {
return -1;
}
ast_str_set(&hint_app, 0, "%s", app);
presence_provider = parse_hint_presence(hint_app);
if (ast_strlen_zero(presence_provider)) {
/* No presence string in the hint */
return 0;
}
return ast_presence_state(presence_provider, subtype, message);
}
int ast_hint_presence_state(struct ast_channel *c, const char *context, const char *exten, char **subtype, char **message)
{
struct ast_exten *e;
if (!(e = ast_hint_extension(c, context, exten))) { /* Do we have a hint for this extension ? */
return -1; /* No hint, return -1 */
}
if (e->exten[0] == '_') {
/* Create this hint on-the-fly */
ast_add_extension(e->parent->name, 0, exten, e->priority, e->label,
e->matchcid ? e->cidmatch : NULL, e->app, ast_strdup(e->data), ast_free_ptr,
e->registrar);
if (!(e = ast_hint_extension(c, context, exten))) {
/* Improbable, but not impossible */
return -1;
}
}
return extension_presence_state_helper(e, subtype, message);
}
static int execute_state_callback(ast_state_cb_type cb,
const char *context,
const char *exten,
void *data,
enum ast_state_cb_update_reason reason,
struct ast_hint *hint)
{
int res = 0;
struct ast_state_cb_info info = { 0, };
info.reason = reason;
/* Copy over current hint data */
if (hint) {
ao2_lock(hint);
info.exten_state = hint->laststate;
info.presence_state = hint->last_presence_state;
if (!(ast_strlen_zero(hint->last_presence_subtype))) {
info.presence_subtype = ast_strdupa(hint->last_presence_subtype);
} else {
info.presence_subtype = "";
}
if (!(ast_strlen_zero(hint->last_presence_message))) {
info.presence_message = ast_strdupa(hint->last_presence_message);
} else {
info.presence_message = "";
}
ao2_unlock(hint);
} else {
info.exten_state = AST_EXTENSION_REMOVED;
}
/* NOTE: The casts will not be needed for v10 and later */
res = cb((char *) context, (char *) exten, &info, data);
return res;
}
static int handle_presencechange(void *datap)
{
struct ast_hint *hint;
struct ast_str *hint_app = NULL;
struct presencechange *pc = datap;
struct ao2_iterator i;
struct ao2_iterator cb_iter;
char context_name[AST_MAX_CONTEXT];
char exten_name[AST_MAX_EXTENSION];
int res = -1;
hint_app = ast_str_create(1024);
if (!hint_app) {
goto presencechange_cleanup;
}
ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
i = ao2_iterator_init(hints, 0);
for (; (hint = ao2_iterator_next(&i)); ao2_ref(hint, -1)) {
struct ast_state_cb *state_cb;
const char *app;
char *parse;
ao2_lock(hint);
if (!hint->exten) {
/* The extension has already been destroyed */
ao2_unlock(hint);
continue;
}
/* Does this hint monitor the device that changed state? */
app = ast_get_extension_app(hint->exten);
if (ast_strlen_zero(app)) {
/* The hint does not monitor presence at all. */
ao2_unlock(hint);
continue;
}
ast_str_set(&hint_app, 0, "%s", app);
parse = parse_hint_presence(hint_app);
if (ast_strlen_zero(parse)) {
ao2_unlock(hint);
continue;
}
if (strcasecmp(parse, pc->provider)) {
/* The hint does not monitor the presence provider. */
ao2_unlock(hint);
continue;
}
/*
* Save off strings in case the hint extension gets destroyed
* while we are notifying the watchers.
*/
ast_copy_string(context_name,
ast_get_context_name(ast_get_extension_context(hint->exten)),
sizeof(context_name));
ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
sizeof(exten_name));
ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
/* Check to see if update is necessary */
if ((hint->last_presence_state == pc->state) &&
((hint->last_presence_subtype && pc->subtype && !strcmp(hint->last_presence_subtype, pc->subtype)) || (!hint->last_presence_subtype && !pc->subtype)) &&
((hint->last_presence_message && pc->message && !strcmp(hint->last_presence_message, pc->message)) || (!hint->last_presence_message && !pc->message))) {
/* this update is the same as the last, do nothing */
ao2_unlock(hint);
continue;
}
/* update new values */
ast_free(hint->last_presence_subtype);
ast_free(hint->last_presence_message);
hint->last_presence_state = pc->state;
hint->last_presence_subtype = pc->subtype ? ast_strdup(pc->subtype) : NULL;
hint->last_presence_message = pc->message ? ast_strdup(pc->message) : NULL;
ao2_unlock(hint);
/* For general callbacks */
cb_iter = ao2_iterator_init(statecbs, 0);
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
execute_state_callback(state_cb->change_cb,
context_name,
exten_name,
state_cb->data,
AST_HINT_UPDATE_PRESENCE,
hint);
}
ao2_iterator_destroy(&cb_iter);
/* For extension callbacks */
cb_iter = ao2_iterator_init(hint->callbacks, 0);
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
execute_state_callback(state_cb->change_cb,
context_name,
exten_name,
state_cb->data,
AST_HINT_UPDATE_PRESENCE,
hint);
}
ao2_iterator_destroy(&cb_iter);
}
ao2_iterator_destroy(&i);
ast_mutex_unlock(&context_merge_lock);
res = 0;
presencechange_cleanup:
ast_free(hint_app);
ao2_ref(pc, -1);
return res;
}
static int handle_statechange(void *datap)
{
struct ast_hint *hint;
@ -4625,14 +4879,24 @@ static int handle_statechange(void *datap)
/* For general callbacks */
cb_iter = ao2_iterator_init(statecbs, 0);
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
state_cb->change_cb(context_name, exten_name, state, state_cb->data);
execute_state_callback(state_cb->change_cb,
context_name,
exten_name,
state_cb->data,
AST_HINT_UPDATE_DEVICE,
hint);
}
ao2_iterator_destroy(&cb_iter);
/* For extension callbacks */
cb_iter = ao2_iterator_init(hint->callbacks, 0);
for (; (state_cb = ao2_iterator_next(&cb_iter)); ao2_ref(state_cb, -1)) {
state_cb->change_cb(context_name, exten_name, state, state_cb->data);
execute_state_callback(state_cb->change_cb,
context_name,
exten_name,
state_cb->data,
AST_HINT_UPDATE_DEVICE,
hint);
}
ao2_iterator_destroy(&cb_iter);
}
@ -4804,7 +5068,6 @@ int ast_extension_state_del(int id, ast_state_cb_type change_cb)
return ret;
}
static int hint_id_cmp(void *obj, void *arg, int flags)
{
const struct ast_state_cb *cb = obj;
@ -4839,14 +5102,21 @@ static void destroy_hint(void *obj)
context_name = hint->context_name;
exten_name = hint->exten_name;
}
hint->laststate = AST_EXTENSION_DEACTIVATED;
while ((state_cb = ao2_callback(hint->callbacks, OBJ_UNLINK, NULL, NULL))) {
/* Notify with -1 and remove all callbacks */
state_cb->change_cb(context_name, exten_name, AST_EXTENSION_DEACTIVATED,
state_cb->data);
execute_state_callback(state_cb->change_cb,
context_name,
exten_name,
state_cb->data,
AST_HINT_UPDATE_DEVICE,
hint);
ao2_ref(state_cb, -1);
}
ao2_ref(hint->callbacks, -1);
}
ast_free(hint->last_presence_subtype);
ast_free(hint->last_presence_message);
}
/*! \brief Remove hint from extension */
@ -4889,6 +5159,9 @@ static int ast_add_hint(struct ast_exten *e)
{
struct ast_hint *hint_new;
struct ast_hint *hint_found;
char *message = NULL;
char *subtype = NULL;
int presence_state;
if (!e) {
return -1;
@ -4912,6 +5185,12 @@ static int ast_add_hint(struct ast_exten *e)
}
hint_new->exten = e;
hint_new->laststate = ast_extension_state2(e);
if ((presence_state = extension_presence_state_helper(e, &subtype, &message)) > 0) {
hint_new->last_presence_state = presence_state;
hint_new->last_presence_subtype = subtype;
hint_new->last_presence_message = message;
message = subtype = NULL;
}
/* Prevent multiple add hints from adding the same hint at the same time. */
ao2_lock(hints);
@ -7402,6 +7681,10 @@ struct store_hint {
char *exten;
AST_LIST_HEAD_NOLOCK(, ast_state_cb) callbacks;
int laststate;
int last_presence_state;
char *last_presence_subtype;
char *last_presence_message;
AST_LIST_ENTRY(store_hint) list;
char data[1];
};
@ -7601,6 +7884,13 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
strcpy(saved_hint->data, hint->exten->parent->name);
saved_hint->exten = saved_hint->data + strlen(saved_hint->context) + 1;
strcpy(saved_hint->exten, hint->exten->exten);
if (hint->last_presence_subtype) {
saved_hint->last_presence_subtype = ast_strdup(hint->last_presence_subtype);
}
if (hint->last_presence_message) {
saved_hint->last_presence_message = ast_strdup(hint->last_presence_message);
}
saved_hint->last_presence_state = hint->last_presence_state;
ao2_unlock(hint);
AST_LIST_INSERT_HEAD(&hints_stored, saved_hint, list);
}
@ -7654,8 +7944,15 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
ao2_ref(thiscb, -1);
}
hint->laststate = saved_hint->laststate;
hint->last_presence_state = saved_hint->last_presence_state;
hint->last_presence_subtype = saved_hint->last_presence_subtype;
hint->last_presence_message = saved_hint->last_presence_message;
ao2_unlock(hint);
ao2_ref(hint, -1);
/*
* The free of saved_hint->last_presence_subtype and
* saved_hint->last_presence_message is not necessary here.
*/
ast_free(saved_hint);
}
}
@ -7670,11 +7967,17 @@ void ast_merge_contexts_and_delete(struct ast_context **extcontexts, struct ast_
while ((saved_hint = AST_LIST_REMOVE_HEAD(&hints_removed, list))) {
/* this hint has been removed, notify the watchers */
while ((thiscb = AST_LIST_REMOVE_HEAD(&saved_hint->callbacks, entry))) {
thiscb->change_cb(saved_hint->context, saved_hint->exten,
AST_EXTENSION_REMOVED, thiscb->data);
execute_state_callback(thiscb->change_cb,
saved_hint->context,
saved_hint->exten,
thiscb->data,
AST_HINT_UPDATE_DEVICE,
NULL);
/* Ref that we added when putting into saved_hint->callbacks */
ao2_ref(thiscb, -1);
}
ast_free(saved_hint->last_presence_subtype);
ast_free(saved_hint->last_presence_message);
ast_free(saved_hint);
}
@ -10369,6 +10672,51 @@ static int pbx_builtin_sayphonetic(struct ast_channel *chan, const char *data)
return res;
}
static void presencechange_destroy(void *data)
{
struct presencechange *pc = data;
ast_free(pc->provider);
ast_free(pc->subtype);
ast_free(pc->message);
}
static void presence_state_cb(const struct ast_event *event, void *unused)
{
struct presencechange *pc;
const char *tmp;
if (!(pc = ao2_alloc(sizeof(*pc), presencechange_destroy))) {
return;
}
tmp = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_PROVIDER);
if (ast_strlen_zero(tmp)) {
ast_log(LOG_ERROR, "Received invalid event that had no presence provider IE\n");
ao2_ref(pc, -1);
return;
}
pc->provider = ast_strdup(tmp);
pc->state = ast_event_get_ie_uint(event, AST_EVENT_IE_PRESENCE_STATE);
if (pc->state < 0) {
ao2_ref(pc, -1);
return;
}
if ((tmp = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_SUBTYPE))) {
pc->subtype = ast_strdup(tmp);
}
if ((tmp = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_MESSAGE))) {
pc->message = ast_strdup(tmp);
}
/* The task processor thread is taking our reference to the presencechange object. */
if (ast_taskprocessor_push(extension_state_tps, handle_presencechange, pc) < 0) {
ao2_ref(pc, -1);
}
}
static void device_state_cb(const struct ast_event *event, void *unused)
{
const char *device;
@ -10383,7 +10731,7 @@ static void device_state_cb(const struct ast_event *event, void *unused)
if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(device) + 1)))
return;
strcpy(sc->dev, device);
if (ast_taskprocessor_push(device_state_tps, handle_statechange, sc) < 0) {
if (ast_taskprocessor_push(extension_state_tps, handle_statechange, sc) < 0) {
ast_free(sc);
}
}
@ -10415,6 +10763,9 @@ static int hints_data_provider_get(const struct ast_data_search *search,
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_str(data_hint, "presence_state", ast_presence_state2str(hint->last_presence_state));
ast_data_add_str(data_hint, "presence_subtype", S_OR(hint->last_presence_subtype, ""));
ast_data_add_str(data_hint, "presence_subtype", S_OR(hint->last_presence_message, ""));
ast_data_add_int(data_hint, "watchers", watchers);
if (!ast_data_search_match(search, data_hint)) {
@ -10441,7 +10792,7 @@ int load_pbx(void)
/* Initialize the PBX */
ast_verb(1, "Asterisk PBX Core Initializing\n");
if (!(device_state_tps = ast_taskprocessor_get("pbx-core", 0))) {
if (!(extension_state_tps = ast_taskprocessor_get("pbx-core", 0))) {
ast_log(LOG_WARNING, "failed to create pbx-core taskprocessor\n");
}
@ -10468,6 +10819,11 @@ int load_pbx(void)
return -1;
}
if (!(presence_state_sub = ast_event_subscribe(AST_EVENT_PRESENCE_STATE, presence_state_cb, "pbx Presence State Change", NULL,
AST_EVENT_IE_END))) {
return -1;
}
return 0;
}

@ -0,0 +1,285 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2011-2012, Digium, Inc.
*
* David Vossel <dvossel@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief Presence state management
*/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/_private.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/linkedlists.h"
#include "asterisk/presencestate.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/event.h"
/*! \brief Device state strings for printing */
static const struct {
const char *string;
enum ast_presence_state state;
} state2string[] = {
{ "not_set", AST_PRESENCE_NOT_SET},
{ "unavailable", AST_PRESENCE_UNAVAILABLE },
{ "available", AST_PRESENCE_AVAILABLE},
{ "away", AST_PRESENCE_AWAY},
{ "xa", AST_PRESENCE_XA},
{ "chat", AST_PRESENCE_CHAT},
{ "dnd", AST_PRESENCE_DND},
};
/*! \brief Flag for the queue */
static ast_cond_t change_pending;
struct state_change {
AST_LIST_ENTRY(state_change) list;
char provider[1];
};
/*! \brief A presence state provider */
struct presence_state_provider {
char label[40];
ast_presence_state_prov_cb_type callback;
AST_RWLIST_ENTRY(presence_state_provider) list;
};
/*! \brief A list of providers */
static AST_RWLIST_HEAD_STATIC(presence_state_providers, presence_state_provider);
/*! \brief The state change queue. State changes are queued
for processing by a separate thread */
static AST_LIST_HEAD_STATIC(state_changes, state_change);
/*! \brief The presence state change notification thread */
static pthread_t change_thread = AST_PTHREADT_NULL;
const char *ast_presence_state2str(enum ast_presence_state state)
{
int i;
for (i = 0; i < ARRAY_LEN(state2string); i++) {
if (state == state2string[i].state) {
return state2string[i].string;
}
}
return "";
}
enum ast_presence_state ast_presence_state_val(const char *val)
{
int i;
for (i = 0; i < ARRAY_LEN(state2string); i++) {
if (!strcasecmp(val, state2string[i].string)) {
return state2string[i].state;
}
}
return -1;
}
static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
{
enum ast_presence_state res = -1;
struct ast_event *event;
const char *_subtype;
const char *_message;
event = ast_event_get_cached(AST_EVENT_PRESENCE_STATE,
AST_EVENT_IE_PRESENCE_PROVIDER, AST_EVENT_IE_PLTYPE_STR, presence_provider,
AST_EVENT_IE_END);
if (!event) {
return res;
}
res = ast_event_get_ie_uint(event, AST_EVENT_IE_PRESENCE_STATE);
_subtype = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_SUBTYPE);
_message = ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_MESSAGE);
*subtype = !ast_strlen_zero(_subtype) ? ast_strdup(_subtype) : NULL;
*message = !ast_strlen_zero(_message) ? ast_strdup(_message) : NULL;
ast_event_destroy(event);
return res;
}
static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
{
struct presence_state_provider *provider;
char *address;
char *label = ast_strdupa(presence_provider);
int res = -1;
if (check_cache) {
res = presence_state_cached(presence_provider, subtype, message);
if (res > 0) {
return res;
}
}
if ((address = strchr(label, ':'))) {
*address = '\0';
address++;
} else {
ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", presence_provider);
return res;
}
AST_RWLIST_RDLOCK(&presence_state_providers);
AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
if (!strcasecmp(provider->label, label)) {
res = provider->callback(address, subtype, message);
break;
}
}
AST_RWLIST_UNLOCK(&presence_state_providers);
return res;
}
enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
{
return ast_presence_state_helper(presence_provider, subtype, message, 1);
}
int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
{
struct presence_state_provider *provider;
if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
return -1;
}
provider->callback = callback;
ast_copy_string(provider->label, label, sizeof(provider->label));
AST_RWLIST_WRLOCK(&presence_state_providers);
AST_RWLIST_INSERT_HEAD(&presence_state_providers, provider, list);
AST_RWLIST_UNLOCK(&presence_state_providers);
return 0;
}
int ast_presence_state_prov_del(const char *label)
{
struct presence_state_provider *provider;
int res = -1;
AST_RWLIST_WRLOCK(&presence_state_providers);
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&presence_state_providers, provider, list) {
if (!strcasecmp(provider->label, label)) {
AST_RWLIST_REMOVE_CURRENT(list);
ast_free(provider);
res = 0;
break;
}
}
AST_RWLIST_TRAVERSE_SAFE_END;
AST_RWLIST_UNLOCK(&presence_state_providers);
return res;
}
static void do_presence_state_change(const char *provider)
{
struct ast_event *event;
enum ast_event_type event_type;
char *subtype = NULL;
char *message = NULL;
int state;
state = ast_presence_state_helper(provider, &subtype, &message, 0);
if (state < 0) {
return;
}
event_type = AST_EVENT_PRESENCE_STATE;
if (!(event = ast_event_new(event_type,
AST_EVENT_IE_PRESENCE_PROVIDER, AST_EVENT_IE_PLTYPE_STR, provider,
AST_EVENT_IE_PRESENCE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
AST_EVENT_IE_PRESENCE_SUBTYPE, AST_EVENT_IE_PLTYPE_STR, S_OR(subtype, ""),
AST_EVENT_IE_PRESENCE_MESSAGE, AST_EVENT_IE_PLTYPE_STR, S_OR(message, ""),
AST_EVENT_IE_END))) {
return;
}
ast_event_queue_and_cache(event);
ast_free(subtype);
ast_free(message);
}
int ast_presence_state_changed(const char *presence_provider)
{
struct state_change *change;
if ((change_thread == AST_PTHREADT_NULL) ||
!(change = ast_calloc(1, sizeof(*change) + strlen(presence_provider)))) {
do_presence_state_change(presence_provider);
} else {
strcpy(change->provider, presence_provider);
AST_LIST_LOCK(&state_changes);
AST_LIST_INSERT_TAIL(&state_changes, change, list);
ast_cond_signal(&change_pending);
AST_LIST_UNLOCK(&state_changes);
}
return 0;
}
/*! \brief Go through the presence state change queue and update changes in the presence state thread */
static void *do_presence_changes(void *data)
{
struct state_change *next, *current;
for (;;) {
/* This basically pops off any state change entries, resets the list back to NULL, unlocks, and processes each state change */
AST_LIST_LOCK(&state_changes);
if (AST_LIST_EMPTY(&state_changes))
ast_cond_wait(&change_pending, &state_changes.lock);
next = AST_LIST_FIRST(&state_changes);
AST_LIST_HEAD_INIT_NOLOCK(&state_changes);
AST_LIST_UNLOCK(&state_changes);
/* Process each state change */
while ((current = next)) {
next = AST_LIST_NEXT(current, list);
do_presence_state_change(current->provider);
ast_free(current);
}
}
return NULL;
}
int ast_presence_state_engine_init(void)
{
ast_cond_init(&change_pending, NULL);
if (ast_pthread_create_background(&change_thread, NULL, do_presence_changes, NULL) < 0) {
ast_log(LOG_ERROR, "Unable to start presence state change thread.\n");
return -1;
}
return 0;
}
Loading…
Cancel
Save