summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
Diffstat (limited to 'main')
-rw-r--r--main/app.c70
-rw-r--r--main/asterisk.c6
-rw-r--r--main/callerid.c1
-rw-r--r--main/channel.c1
-rw-r--r--main/config.c123
-rw-r--r--main/event.c22
-rw-r--r--main/features.c54
-rw-r--r--main/file.c44
-rw-r--r--main/manager.c66
-rw-r--r--main/message.c191
-rw-r--r--main/pbx.c388
-rw-r--r--main/presencestate.c317
12 files changed, 1249 insertions, 34 deletions
diff --git a/main/app.c b/main/app.c
index 3d2fa52c3..31484a18d 100644
--- a/main/app.c
+++ b/main/app.c
@@ -423,18 +423,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)
@@ -444,6 +447,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)
@@ -459,6 +463,28 @@ 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)
{
@@ -709,10 +735,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;
@@ -784,7 +816,11 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
ast_seekstream(ast_channel_stream(chan), 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) {
@@ -848,6 +884,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;
diff --git a/main/asterisk.c b/main/asterisk.c
index 849cbdd80..6122028bf 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -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"
@@ -4028,6 +4029,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();
diff --git a/main/callerid.c b/main/callerid.c
index dc3a91093..37edd992c 100644
--- a/main/callerid.c
+++ b/main/callerid.c
@@ -1203,6 +1203,7 @@ static const struct ast_value_translation redirecting_reason_types[] = {
{ AST_REDIRECTING_REASON_OUT_OF_ORDER, "out_of_order", "Called DTE Out-Of-Order" },
{ AST_REDIRECTING_REASON_AWAY, "away", "Callee is Away" },
{ AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte", "Call Forwarding By The Called DTE" },
+ { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm", "Call is being redirected to user's voicemail"},
/* *INDENT-ON* */
};
diff --git a/main/channel.c b/main/channel.c
index 8bd973501..320947da7 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -2514,6 +2514,7 @@ void ast_channel_clear_softhangup(struct ast_channel *chan, int flag)
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
{
ast_debug(1, "Soft-Hanging up channel '%s'\n", ast_channel_name(chan));
+ ast_backtrace();
/* Inform channel driver that we need to be hung up, if it cares */
ast_channel_softhangup_internal_flag_add(chan, cause);
ast_queue_frame(chan, &ast_null_frame);
diff --git a/main/config.c b/main/config.c
index 9d9eefc25..127d89bb1 100644
--- a/main/config.c
+++ b/main/config.c
@@ -64,6 +64,8 @@ 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 {
@@ -2278,6 +2280,39 @@ 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];
@@ -2310,10 +2345,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;
}
@@ -2968,3 +3005,85 @@ int register_config_cli(void)
ast_cli_register_multiple(cli_config, ARRAY_LEN(cli_config));
return 0;
}
+
+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;
+}
diff --git a/main/event.c b/main/event.c
index 9ecf71028..d79b07a89 100644
--- a/main/event.c
+++ b/main/event.c
@@ -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, },
+ },
+
};
/*!
@@ -1587,6 +1593,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;
diff --git a/main/features.c b/main/features.c
index 8b69d1385..04bc5326a 100644
--- a/main/features.c
+++ b/main/features.c
@@ -409,6 +409,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>
<function name="FEATURE" language="en_US">
<synopsis>
Get or set a feature option on a channel.
@@ -7347,7 +7358,42 @@ 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
* \param m
@@ -7363,6 +7409,7 @@ static int manager_parking_status(struct mansession *s, const struct message *m)
struct ao2_iterator iter;
struct ast_parkinglot *curlot;
int numparked = 0;
+ long now = time(NULL);
if (!ast_strlen_zero(id))
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
@@ -7379,6 +7426,7 @@ static int manager_parking_status(struct mansession *s, const struct message *m)
"Channel: %s\r\n"
"From: %s\r\n"
"Timeout: %ld\r\n"
+ "Duration: %ld\r\n"
"CallerIDNum: %s\r\n"
"CallerIDName: %s\r\n"
"ConnectedLineNum: %s\r\n"
@@ -7387,7 +7435,8 @@ static int manager_parking_status(struct mansession *s, const struct message *m)
"\r\n",
curlot->name,
cur->parkingnum, ast_channel_name(cur->chan), cur->peername,
- (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
+ (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - now,
+ now - (long) cur->start.tv_sec,
S_COR(ast_channel_caller(cur->chan)->id.number.valid, ast_channel_caller(cur->chan)->id.number.str, ""), /* XXX in other places it is <unknown> */
S_COR(ast_channel_caller(cur->chan)->id.name.valid, ast_channel_caller(cur->chan)->id.name.str, ""),
S_COR(ast_channel_connected(cur->chan)->id.number.valid, ast_channel_connected(cur->chan)->id.number.str, ""), /* XXX in other places it is <unknown> */
@@ -8669,6 +8718,7 @@ int ast_features_init(void)
res = ast_register_application2(parkcall, park_call_exec, NULL, NULL, NULL);
if (!res) {
ast_manager_register_xml_core("ParkedCalls", 0, manager_parking_status);
+ ast_manager_register_xml_core("Parkinglots", 0, manager_parkinglot_list);
ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
ast_manager_register_xml_core("Bridge", EVENT_FLAG_CALL, action_bridge);
}
diff --git a/main/file.c b/main/file.c
index 52f5af58d..076b31dd7 100644
--- a/main/file.c
+++ b/main/file.c
@@ -1237,11 +1237,18 @@ 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;
if (!breakon)
@@ -1257,6 +1264,11 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
if (ast_test_flag(ast_channel_flags(c), AST_FLAG_MASQ_NOSTREAM))
orig_chan_name = ast_strdupa(ast_channel_name(c));
+ if (ast_channel_stream(c) && cb) {
+ long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_rate(&ast_channel_stream(c)->fmt->format) / 1000);
+ cb(c, ms_len, AST_WAITSTREAM_CB_START);
+ }
+
while (ast_channel_stream(c)) {
int res;
int ms;
@@ -1318,6 +1330,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;
@@ -1328,13 +1341,19 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
} else {
ungetc(eoftest, ast_channel_stream(c)->f);
}
+ cb_val = AST_WAITSTREAM_CB_FASTFORWARD;
} else if (strchr(reverse, res)) {
ast_stream_rewind(ast_channel_stream(c), skip_ms);
+ cb_val = AST_WAITSTREAM_CB_REWIND;
} else if (strchr(breakon, res)) {
ast_frfree(fr);
ast_clear_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY);
return res;
}
+ if (cb_val && cb) {
+ long ms_len = ast_tellstream(ast_channel_stream(c)) / (ast_format_rate(&ast_channel_stream(c)->fmt->format) / 1000);
+ cb(c, ms_len, cb_val);
+ }
}
break;
case AST_FRAME_CONTROL:
@@ -1385,21 +1404,32 @@ static int waitstream_core(struct ast_channel *c, const char *breakon,
return (err || ast_channel_softhangup_internal_flag(c)) ? -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)
@@ -1410,7 +1440,7 @@ int ast_waitstream_exten(struct ast_channel *c, const char *context)
if (!context)
context = ast_channel_context(c);
return waitstream_core(c, NULL, NULL, NULL, 0,
- -1, -1, context);
+ -1, -1, context, NULL /* no callback */);
}
/*
diff --git a/main/manager.c b/main/manager.c
index 2861c6d9b..4bf859e2e 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -82,6 +82,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/security_events.h"
#include "asterisk/aoc.h"
#include "asterisk/stringfields.h"
+#include "asterisk/presencestate.h"
/*** DOCUMENTATION
<manager name="Ping" language="en_US">
@@ -510,6 +511,22 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
the hint for the extension and the status.</para>
</description>
</manager>
+ <manager name="PresenceState" language="en_US">
+ <synopsis>
+ Check Presence State
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Provider" required="true">
+ <para>Presence Provider to check the state of</para>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Report the presence state for the given presence provider.</para>
+ <para>Will return a <literal>Presence State</literal> message. The response will include the
+ presence state and, if set, a presence subtype and custom message.</para>
+ </description>
+ </manager>
<manager name="AbsoluteTimeout" language="en_US">
<synopsis>
Set absolute timeout.
@@ -1218,6 +1235,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" },
};
@@ -3211,6 +3229,7 @@ static int action_hangup(struct mansession *s, const struct message *m)
if (name_or_regex[0] != '/') {
if (!(c = ast_channel_get_by_name(name_or_regex))) {
+ ast_log(LOG_NOTICE, "!!!!!!!!!! Can't find channel to hang up!\n");
astman_send_error(s, m, "No such channel");
return 0;
}
@@ -4384,6 +4403,43 @@ static int action_extensionstate(struct mansession *s, const struct message *m)
return 0;
}
+static int action_presencestate(struct mansession *s, const struct message *m)
+{
+ const char *provider = astman_get_header(m, "Provider");
+ enum ast_presence_state state;
+ char *subtype;
+ char *message;
+ char subtype_header[256] = "";
+ char message_header[256] = "";
+
+ if (ast_strlen_zero(provider)) {
+ astman_send_error(s, m, "No provider specified");
+ return 0;
+ }
+
+ state = ast_presence_state(provider, &subtype, &message);
+
+ if (!ast_strlen_zero(subtype)) {
+ snprintf(subtype_header, sizeof(subtype_header),
+ "Subtype: %s\r\n", subtype);
+ }
+
+ if (!ast_strlen_zero(message)) {
+ snprintf(message_header, sizeof(message_header),
+ "Message: %s\r\n", message);
+ }
+
+ astman_append(s, "Message: Presence State\r\n"
+ "State: %s\r\n"
+ "%s"
+ "%s"
+ "\r\n",
+ ast_presence_state2str(state),
+ subtype_header,
+ message_header);
+ return 0;
+}
+
static int action_timeout(struct mansession *s, const struct message *m)
{
struct ast_channel *c;
@@ -5485,10 +5541,17 @@ int ast_manager_unregister(const 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);
@@ -6850,6 +6913,7 @@ static int __init_manager(int reload)
ast_manager_register_xml_core("Originate", EVENT_FLAG_ORIGINATE, action_originate);
ast_manager_register_xml_core("Command", EVENT_FLAG_COMMAND, action_command);
ast_manager_register_xml_core("ExtensionState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_extensionstate);
+ ast_manager_register_xml_core("PresenceState", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_presencestate);
ast_manager_register_xml_core("AbsoluteTimeout", EVENT_FLAG_SYSTEM | EVENT_FLAG_CALL, action_timeout);
ast_manager_register_xml_core("MailboxStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxstatus);
ast_manager_register_xml_core("MailboxCount", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, action_mailboxcount);
diff --git a/main/message.c b/main/message.c
index 5722969d0..e26b8c375 100644
--- a/main/message.c
+++ b/main/message.c
@@ -32,6 +32,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/module.h"
#include "asterisk/datastore.h"
#include "asterisk/pbx.h"
+#include "asterisk/manager.h"
#include "asterisk/strings.h"
#include "asterisk/astobj2.h"
#include "asterisk/app.h"
@@ -56,6 +57,18 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<para>Read-only. The source of the message. When processing an
incoming message, this will be set to the source of the message.</para>
</enum>
+ <enum name="custom_data">
+ <para>Write-only. Mark or unmark all message headers for an outgoing
+ message. The following values can be set:</para>
+ <enumlist>
+ <enum name="mark_all_outbound">
+ <para>Mark all headers for an outgoing message.</para>
+ </enum>
+ <enum name="clear_all_outbound">
+ <para>Unmark all headers for an outgoing message.</para>
+ </enum>
+ </enumlist>
+ </enum>
<enum name="body">
<para>Read/Write. The message body. When processing an incoming
message, this includes the body of the message that Asterisk
@@ -139,6 +152,39 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</variablelist>
</description>
</application>
+ <manager name="MessageSend" language="en_US">
+ <synopsis>
+ Send an out of call message to an endpoint.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="To" required="true">
+ <para>The URI the message is to be sent to.</para>
+ </parameter>
+ <parameter name="From">
+ <para>A From URI for the message if needed for the
+ message technology being used to send this message.</para>
+ <note>
+ <para>For SIP the from parameter can be a configured peer name
+ or in the form of "display-name" &lt;URI&gt;.</para>
+ </note>
+ </parameter>
+ <parameter name="Body">
+ <para>The message body text. This must not contain any newlines as that
+ conflicts with the AMI protocol.</para>
+ </parameter>
+ <parameter name="Base64Body">
+ <para>Text bodies requiring the use of newlines have to be base64 encoded
+ in this field. Base64Body will be decoded before being sent out.
+ Base64Body takes precedence over Body.</para>
+ </parameter>
+ <parameter name="Variable">
+ <para>Message variable to set, multiple Variable: headers are
+ allowed. The header value is a comma separated list of
+ name=value pairs.</para>
+ </parameter>
+ </syntax>
+ </manager>
***/
struct msg_data {
@@ -393,6 +439,12 @@ struct ast_msg *ast_msg_alloc(void)
return msg;
}
+struct ast_msg *ast_msg_ref(struct ast_msg *msg)
+{
+ ao2_ref(msg, 1);
+ return msg;
+}
+
struct ast_msg *ast_msg_destroy(struct ast_msg *msg)
{
ao2_ref(msg, -1);
@@ -519,7 +571,7 @@ static int msg_set_var_full(struct ast_msg *msg, const char *name, const char *v
return 0;
}
-static int msg_set_var_outbound(struct ast_msg *msg, const char *name, const char *value)
+int ast_msg_set_var_outbound(struct ast_msg *msg, const char *name, const char *value)
{
return msg_set_var_full(msg, name, value, 1);
}
@@ -850,6 +902,26 @@ static int msg_func_write(struct ast_channel *chan, const char *function,
ast_msg_set_from(msg, "%s", value);
} else if (!strcasecmp(data, "body")) {
ast_msg_set_body(msg, "%s", value);
+ } else if (!strcasecmp(data, "custom_data")) {
+ int outbound = -1;
+ if (!strcasecmp(value, "mark_all_outbound")) {
+ outbound = 1;
+ } else if (!strcasecmp(value, "clear_all_outbound")) {
+ outbound = 0;
+ } else {
+ ast_log(LOG_WARNING, "'%s' is not a valid value for custom_data\n", value);
+ }
+
+ if (outbound != -1) {
+ struct msg_data *hdr_data;
+ struct ao2_iterator iter = ao2_iterator_init(msg->vars, 0);
+
+ while ((hdr_data = ao2_iterator_next(&iter))) {
+ hdr_data->send = outbound;
+ ao2_ref(hdr_data, -1);
+ }
+ ao2_iterator_destroy(&iter);
+ }
} else {
ast_log(LOG_WARNING, "'%s' is not a valid write argument.\n", data);
}
@@ -910,7 +982,7 @@ static int msg_data_func_write(struct ast_channel *chan, const char *function,
ao2_lock(msg);
- msg_set_var_outbound(msg, data, value);
+ ast_msg_set_var_outbound(msg, data, value);
ao2_unlock(msg);
ao2_ref(msg, -1);
@@ -1041,6 +1113,120 @@ exit_cleanup:
return 0;
}
+static int action_messagesend(struct mansession *s, const struct message *m)
+{
+ const char *to = ast_strdupa(astman_get_header(m, "To"));
+ const char *from = astman_get_header(m, "From");
+ const char *body = astman_get_header(m, "Body");
+ const char *base64body = astman_get_header(m, "Base64Body");
+ char base64decoded[1301] = { 0, };
+ char *tech_name = NULL;
+ struct ast_variable *vars = NULL;
+ struct ast_variable *data = NULL;
+ struct ast_msg_tech_holder *tech_holder = NULL;
+ struct ast_msg *msg;
+ int res = -1;
+
+ if (ast_strlen_zero(to)) {
+ astman_send_error(s, m, "No 'To' address specified.");
+ return -1;
+ }
+
+ if (!ast_strlen_zero(base64body)) {
+ ast_base64decode((unsigned char *) base64decoded, base64body, sizeof(base64decoded) - 1);
+ body = base64decoded;
+ }
+
+ tech_name = ast_strdupa(to);
+ tech_name = strsep(&tech_name, ":");
+ {
+ struct ast_msg_tech tmp_msg_tech = {
+ .name = tech_name,
+ };
+ struct ast_msg_tech_holder tmp_tech_holder = {
+ .tech = &tmp_msg_tech,
+ };
+
+ tech_holder = ao2_find(msg_techs, &tmp_tech_holder, OBJ_POINTER);
+ }
+
+ if (!tech_holder) {
+ astman_send_error(s, m, "Message technology not found.");
+ return -1;
+ }
+
+ if (!(msg = ast_msg_alloc())) {
+ ao2_ref(tech_holder, -1);
+ astman_send_error(s, m, "Internal failure\n");
+ return -1;
+ }
+
+ data = astman_get_variables(m);
+ for (vars = data; vars; vars = vars->next) {
+ ast_msg_set_var_outbound(msg, vars->name, vars->value);
+ }
+
+ ast_msg_set_body(msg, "%s", body);
+
+ ast_rwlock_rdlock(&tech_holder->tech_lock);
+ if (tech_holder->tech) {
+ res = tech_holder->tech->msg_send(msg, S_OR(to, ""), S_OR(from, ""));
+ }
+ ast_rwlock_unlock(&tech_holder->tech_lock);
+
+ ast_variables_destroy(vars);
+ ao2_ref(tech_holder, -1);
+ ao2_ref(msg, -1);
+
+ if (res) {
+ astman_send_error(s, m, "Message failed to send.");
+ } else {
+ astman_send_ack(s, m, "Message successfully sent");
+ }
+ return res;
+}
+
+int ast_msg_send(struct ast_msg *msg, const char *to, const char *from)
+{
+ char *tech_name = NULL;
+ struct ast_msg_tech_holder *tech_holder = NULL;
+ int res = -1;
+
+ if (ast_strlen_zero(to)) {
+ ao2_ref(msg, -1);
+ return -1;
+ }
+
+ tech_name = ast_strdupa(to);
+ tech_name = strsep(&tech_name, ":");
+ {
+ struct ast_msg_tech tmp_msg_tech = {
+ .name = tech_name,
+ };
+ struct ast_msg_tech_holder tmp_tech_holder = {
+ .tech = &tmp_msg_tech,
+ };
+
+ tech_holder = ao2_find(msg_techs, &tmp_tech_holder, OBJ_POINTER);
+ }
+
+ if (!tech_holder) {
+ ao2_ref(msg, -1);
+ return -1;
+ }
+
+ ast_rwlock_rdlock(&tech_holder->tech_lock);
+ if (tech_holder->tech) {
+ res = tech_holder->tech->msg_send(msg, S_OR(to, ""), S_OR(from, ""));
+ }
+ ast_rwlock_unlock(&tech_holder->tech_lock);
+
+ ao2_ref(tech_holder, -1);
+ ao2_ref(msg, -1);
+
+ return res;
+}
+
int ast_msg_tech_register(const struct ast_msg_tech *tech)
{
struct ast_msg_tech_holder tmp_tech_holder = {
@@ -1125,6 +1311,7 @@ int ast_msg_init(void)
res = __ast_custom_function_register(&msg_function, NULL);
res |= __ast_custom_function_register(&msg_data_function, NULL);
res |= ast_register_application2(app_msg_send, msg_send_exec, NULL, NULL, NULL);
+ res |= ast_manager_register_xml_core("MessageSend", EVENT_FLAG_MESSAGE, action_messagesend);
return res;
}
diff --git a/main/pbx.c b/main/pbx.c
index bdaea7288..3ed7df4ed 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -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. */
};
@@ -1024,6 +1033,7 @@ static int remove_hintdevice(struct ast_hint *hint)
return 0;
}
+static char *parse_hint_device(struct ast_str *hint_args);
/*!
* \internal
* \brief Destroy the given hintdevice object.
@@ -1060,7 +1070,7 @@ static int add_hintdevice(struct ast_hint *hint, const char *devicelist)
return -1;
}
ast_str_set(&str, 0, "%s", devicelist);
- parse = ast_str_buffer(str);
+ parse = parse_hint_device(str);
while ((cur = strsep(&parse, "&"))) {
devicelength = strlen(cur);
@@ -1094,6 +1104,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 +1281,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;
@@ -4474,6 +4493,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;
@@ -4481,7 +4536,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, "&"))) {
@@ -4539,6 +4594,209 @@ 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;
@@ -4626,14 +4884,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);
}
@@ -4805,7 +5073,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;
@@ -4840,14 +5107,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 */
@@ -4890,6 +5164,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;
@@ -4913,6 +5190,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);
@@ -7432,6 +7715,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];
};
@@ -7633,6 +7920,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);
}
@@ -7686,8 +7980,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);
}
}
@@ -7702,11 +8003,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);
}
@@ -10469,6 +10776,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;
@@ -10483,7 +10835,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);
}
}
@@ -10515,6 +10867,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)) {
@@ -10541,7 +10896,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");
}
@@ -10568,6 +10923,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;
}
diff --git a/main/presencestate.c b/main/presencestate.c
new file mode 100644
index 000000000..df64dab24
--- /dev/null
+++ b/main/presencestate.c
@@ -0,0 +1,317 @@
+/*
+ * 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 AST_PRESENCE_INVALID;
+}
+
+static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
+{
+ enum ast_presence_state res = AST_PRESENCE_INVALID;
+ 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 = AST_PRESENCE_INVALID;
+
+ if (check_cache) {
+ res = presence_state_cached(presence_provider, subtype, message);
+ if (res != AST_PRESENCE_INVALID) {
+ 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);
+}
+
+enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
+{
+ return ast_presence_state_helper(presence_provider, subtype, message, 0);
+}
+
+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 presence_state_event(const char *provider,
+ enum ast_presence_state state,
+ const char *subtype,
+ const char *message)
+{
+ struct ast_event *event;
+
+ if (!(event = ast_event_new(AST_EVENT_PRESENCE_STATE,
+ 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);
+}
+
+static void do_presence_state_change(const char *provider)
+{
+ char *subtype = NULL;
+ char *message = NULL;
+ enum ast_presence_state state;
+
+ state = ast_presence_state_helper(provider, &subtype, &message, 0);
+
+ if (state < 0) {
+ return;
+ }
+
+ presence_state_event(provider, state, subtype, message);
+ ast_free(subtype);
+ ast_free(message);
+}
+
+int ast_presence_state_changed_literal(enum ast_presence_state state,
+ const char *subtype,
+ const char *message,
+ const char *presence_provider)
+{
+ struct state_change *change;
+
+ if (state != AST_PRESENCE_NOT_SET) {
+ presence_state_event(presence_provider, state, subtype, message);
+ } else 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;
+}
+
+int ast_presence_state_changed(enum ast_presence_state state,
+ const char *subtype,
+ const char *message,
+ const char *fmt, ...)
+{
+ char buf[AST_MAX_EXTENSION];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ return ast_presence_state_changed_literal(state, subtype, message, buf);
+}
+
+/*! \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;
+}
+