mirror of https://github.com/asterisk/asterisk
Presence support has been added. This is accomplished by allowing for presence hints in addition to device state hints. A dialplan function called PRESENCE_STATE has been added to allow for setting and reading presence. Presence can be transmitted to Digium phones using custom XML elements in a PIDF presence document. Voicemail has new APIs that allow for moving, removing, forwarding, and playing messages. Messages have had a new unique message ID added to them so that the APIs will work reliably. The state of a voicemail mailbox can be obtained using an API that allows one to get a snapshot of the mailbox. A voicemail Dialplan App called VoiceMailPlayMsg has been added to be able to play back a specific message. Configuration hooks have been added. Configuration hooks allow for a piece of code to be executed when a specific configuration file is loaded by a specific module. This is useful for modules that are dependent on the configuration of other modules. chan_sip now has a public method that allows for a custom SIP INFO request to be sent mid-dialog. Digium phones use this in order to display progress bars when files are played. Messaging support has been expanded a bit. The main visible difference is the addition of an AMI action MessageSend. Finally, a ParkingLots manager action has been added in order to get a list of parking lots. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@368435 65c4cc65-6c06-0410-ace0-fbb531ad65f3certified/11.2
parent
c1bbe79748
commit
14a985560e
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
{
|
||||
global:
|
||||
LINKER_SYMBOL_PREFIX*ast_sipinfo_send;
|
||||
local:
|
||||
*;
|
||||
};
|
@ -0,0 +1,781 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2011, 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 Custom presence provider
|
||||
* \ingroup functions
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/presencestate.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/astdb.h"
|
||||
#include "asterisk/app.h"
|
||||
#ifdef TEST_FRAMEWORK
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/event.h"
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<function name="PRESENCE_STATE" language="en_US">
|
||||
<synopsis>
|
||||
Get or Set a presence state.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="provider" required="true">
|
||||
<para>The provider of the presence, such as <literal>CustomPresence</literal></para>
|
||||
</parameter>
|
||||
<parameter name="field" required="true">
|
||||
<para>Which field of the presence state information is wanted.</para>
|
||||
<optionlist>
|
||||
<option name="value">
|
||||
<para>The current presence, such as <literal>away</literal></para>
|
||||
</option>
|
||||
<option name="subtype">
|
||||
<para>Further information about the current presence</para>
|
||||
</option>
|
||||
<option name="message">
|
||||
<para>A custom message that may indicate further details about the presence</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
<parameter name="options" required="false">
|
||||
<optionlist>
|
||||
<option name="e">
|
||||
<para>Base-64 encode the data.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>The PRESENCE_STATE function can be used to retrieve the presence from any
|
||||
presence provider. For example:</para>
|
||||
<para>NoOp(SIP/mypeer has presence ${PRESENCE_STATE(SIP/mypeer,value)})</para>
|
||||
<para>NoOp(Conference number 1234 has presence message ${PRESENCE_STATE(MeetMe:1234,message)})</para>
|
||||
<para>The PRESENCE_STATE function can also be used to set custom presence state from
|
||||
the dialplan. The <literal>CustomPresence:</literal> prefix must be used. For example:</para>
|
||||
<para>Set(PRESENCE_STATE(CustomPresence:lamp1)=away,temporary,Out to lunch)</para>
|
||||
<para>Set(PRESENCE_STATE(CustomPresence:lamp2)=dnd,,Trying to get work done)</para>
|
||||
<para>You can subscribe to the status of a custom presence state using a hint in
|
||||
the dialplan:</para>
|
||||
<para>exten => 1234,hint,CustomPresence:lamp1</para>
|
||||
<para>The possible values for both uses of this function are:</para>
|
||||
<para>not_set | unavailable | available | away | xa | chat | dnd</para>
|
||||
</description>
|
||||
</function>
|
||||
***/
|
||||
|
||||
|
||||
static const char astdb_family[] = "CustomPresence";
|
||||
|
||||
static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
int state;
|
||||
char *message = NULL;
|
||||
char *subtype = NULL;
|
||||
char *parse;
|
||||
int base64encode = 0;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(provider);
|
||||
AST_APP_ARG(field);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "PRESENCE_STATE reading requires an argument \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
parse = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(args, parse);
|
||||
|
||||
if (ast_strlen_zero(args.provider) || ast_strlen_zero(args.field)) {
|
||||
ast_log(LOG_WARNING, "PRESENCE_STATE reading requires both presence provider and presence field arguments. \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
state = ast_presence_state_nocache(args.provider, &subtype, &message);
|
||||
if (state == AST_PRESENCE_INVALID) {
|
||||
ast_log(LOG_WARNING, "PRESENCE_STATE unknown \n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(ast_strlen_zero(args.options)) && (strchr(args.options, 'e'))) {
|
||||
base64encode = 1;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(subtype) && !strcasecmp(args.field, "subtype")) {
|
||||
if (base64encode) {
|
||||
ast_base64encode(buf, (unsigned char *) subtype, strlen(subtype), len);
|
||||
} else {
|
||||
ast_copy_string(buf, subtype, len);
|
||||
}
|
||||
} else if (!ast_strlen_zero(message) && !strcasecmp(args.field, "message")) {
|
||||
if (base64encode) {
|
||||
ast_base64encode(buf, (unsigned char *) message, strlen(message), len);
|
||||
} else {
|
||||
ast_copy_string(buf, message, len);
|
||||
}
|
||||
|
||||
} else if (!strcasecmp(args.field, "value")) {
|
||||
ast_copy_string(buf, ast_presence_state2str(state), len);
|
||||
}
|
||||
|
||||
ast_free(message);
|
||||
ast_free(subtype);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
|
||||
{
|
||||
char *state_str;
|
||||
|
||||
/* data syntax is state,subtype,message,options */
|
||||
*subtype = "";
|
||||
*message = "";
|
||||
*options = "";
|
||||
|
||||
state_str = strsep(&data, ",");
|
||||
if (ast_strlen_zero(state_str)) {
|
||||
return -1; /* state is required */
|
||||
}
|
||||
|
||||
*state = ast_presence_state_val(state_str);
|
||||
|
||||
/* not a valid state */
|
||||
if (*state == AST_PRESENCE_INVALID) {
|
||||
ast_log(LOG_WARNING, "Unknown presence state value %s\n", state_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(*subtype = strsep(&data,","))) {
|
||||
*subtype = "";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(*message = strsep(&data, ","))) {
|
||||
*message = "";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(*options = strsep(&data, ","))) {
|
||||
*options = "";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(*options) && !(strchr(*options, 'e'))) {
|
||||
ast_log(LOG_NOTICE, "Invalid options '%s'\n", *options);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
|
||||
{
|
||||
size_t len = strlen("CustomPresence:");
|
||||
char *tmp = data;
|
||||
char *args = ast_strdupa(value);
|
||||
enum ast_presence_state state;
|
||||
char *options, *message, *subtype;
|
||||
|
||||
if (strncasecmp(data, "CustomPresence:", len)) {
|
||||
ast_log(LOG_WARNING, "The PRESENCE_STATE function can only set CustomPresence: presence providers.\n");
|
||||
return -1;
|
||||
}
|
||||
data += len;
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "PRESENCE_STATE function called with no custom device name!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (parse_data(args, &state, &subtype, &message, &options)) {
|
||||
ast_log(LOG_WARNING, "Invalid arguments to PRESENCE_STATE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_db_put(astdb_family, data, value);
|
||||
|
||||
ast_presence_state_changed_literal(state, subtype, message, tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
|
||||
{
|
||||
char buf[1301] = "";
|
||||
enum ast_presence_state state;
|
||||
char *_options;
|
||||
char *_message;
|
||||
char *_subtype;
|
||||
|
||||
ast_log(LOG_NOTICE, "TITTY BOMBS!\n");
|
||||
|
||||
ast_db_get(astdb_family, data, buf, sizeof(buf));
|
||||
|
||||
if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((strchr(_options, 'e'))) {
|
||||
char tmp[1301];
|
||||
if (ast_strlen_zero(_subtype)) {
|
||||
*subtype = NULL;
|
||||
} else {
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
ast_log(LOG_NOTICE, "Hey there, I'm doing some base64 decoding\n");
|
||||
ast_base64decode((unsigned char *) tmp, _subtype, sizeof(tmp) - 1);
|
||||
*subtype = ast_strdup(tmp);
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(_message)) {
|
||||
*message = NULL;
|
||||
} else {
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
ast_log(LOG_NOTICE, "Hey there, I'm doing some more base64 decoding\n");
|
||||
ast_base64decode((unsigned char *) tmp, _message, sizeof(tmp) - 1);
|
||||
*message = ast_strdup(tmp);
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_NOTICE, "Not doing any base64 decoding\n");
|
||||
*subtype = ast_strlen_zero(_subtype) ? NULL : ast_strdup(_subtype);
|
||||
*message = ast_strlen_zero(_message) ? NULL : ast_strdup(_message);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
static struct ast_custom_function presence_function = {
|
||||
.name = "PRESENCE_STATE",
|
||||
.read = presence_read,
|
||||
.write = presence_write,
|
||||
};
|
||||
|
||||
static char *handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct ast_db_entry *db_entry, *db_tree;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "presencestate list";
|
||||
e->usage =
|
||||
"Usage: presencestate list\n"
|
||||
" List all custom presence states that have been set by using\n"
|
||||
" the PRESENCE_STATE dialplan function.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != e->args) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "\n"
|
||||
"---------------------------------------------------------------------\n"
|
||||
"--- Custom Presence States ------------------------------------------\n"
|
||||
"---------------------------------------------------------------------\n"
|
||||
"---\n");
|
||||
|
||||
db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
|
||||
if (!db_entry) {
|
||||
ast_cli(a->fd, "No custom presence states defined\n");
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
for (; db_entry; db_entry = db_entry->next) {
|
||||
const char *object_name = strrchr(db_entry->key, '/') + 1;
|
||||
char state_info[1301];
|
||||
enum ast_presence_state state;
|
||||
char *subtype;
|
||||
char *message;
|
||||
char *options;
|
||||
|
||||
ast_copy_string(state_info, db_entry->data, sizeof(state_info));
|
||||
if (parse_data(state_info, &state, &subtype, &message, &options)) {
|
||||
ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (object_name <= (const char *) 1) {
|
||||
continue;
|
||||
}
|
||||
ast_cli(a->fd, "--- Name: 'CustomPresence:%s'\n"
|
||||
" --- State: '%s'\n"
|
||||
" --- Subtype: '%s'\n"
|
||||
" --- Message: '%s'\n"
|
||||
" --- Base64 Encoded: '%s'\n"
|
||||
"---\n",
|
||||
object_name,
|
||||
ast_presence_state2str(state),
|
||||
subtype,
|
||||
message,
|
||||
AST_CLI_YESNO(strchr(options, 'e')));
|
||||
}
|
||||
ast_db_freetree(db_tree);
|
||||
db_tree = NULL;
|
||||
|
||||
ast_cli(a->fd,
|
||||
"---------------------------------------------------------------------\n"
|
||||
"---------------------------------------------------------------------\n"
|
||||
"\n");
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static char *handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
size_t len;
|
||||
const char *dev, *state, *full_dev;
|
||||
enum ast_presence_state state_val;
|
||||
char *message;
|
||||
char *subtype;
|
||||
char *options;
|
||||
char *args;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "presencestate change";
|
||||
e->usage =
|
||||
"Usage: presencestate change <entity> <state>[,<subtype>[,message[,options]]]\n"
|
||||
" Change a custom presence to a new state.\n"
|
||||
" The possible values for the state are:\n"
|
||||
"NOT_SET | UNAVAILABLE | AVAILABLE | AWAY | XA | CHAT | DND\n"
|
||||
"Optionally, a custom subtype and message may be provided, along with any options\n"
|
||||
"accepted by func_presencestate. If the subtype or message provided contain spaces,\n"
|
||||
"be sure to enclose the data in quotation marks (\"\")\n"
|
||||
"\n"
|
||||
"Examples:\n"
|
||||
" presencestate change CustomPresence:mystate1 AWAY\n"
|
||||
" presencestate change CustomPresence:mystate1 AVAILABLE\n"
|
||||
" presencestate change CustomPresence:mystate1 \"Away,upstairs,eating lunch\"\n"
|
||||
" \n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
{
|
||||
static const char * const cmds[] = { "NOT_SET", "UNAVAILABLE", "AVAILABLE", "AWAY",
|
||||
"XA", "CHAT", "DND", NULL };
|
||||
|
||||
if (a->pos == e->args + 1) {
|
||||
return ast_cli_complete(a->word, cmds, a->n);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->argc != e->args + 2) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
len = strlen("CustomPresence:");
|
||||
full_dev = dev = a->argv[e->args];
|
||||
state = a->argv[e->args + 1];
|
||||
|
||||
if (strncasecmp(dev, "CustomPresence:", len)) {
|
||||
ast_cli(a->fd, "The presencestate command can only be used to set 'CustomPresence:' presence state!\n");
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
dev += len;
|
||||
if (ast_strlen_zero(dev)) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
args = ast_strdupa(state);
|
||||
if (parse_data(args, &state_val, &subtype, &message, &options)) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
if (state_val == AST_PRESENCE_NOT_SET) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "Changing %s to %s\n", dev, args);
|
||||
|
||||
ast_db_put(astdb_family, dev, state);
|
||||
|
||||
ast_presence_state_changed_literal(state_val, subtype, message, full_dev);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry cli_funcpresencestate[] = {
|
||||
AST_CLI_DEFINE(handle_cli_presencestate_list, "List currently know custom presence states"),
|
||||
AST_CLI_DEFINE(handle_cli_presencestate_change, "Change a custom presence state"),
|
||||
};
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
|
||||
struct test_string {
|
||||
char *parse_string;
|
||||
struct {
|
||||
int value;
|
||||
const char *subtype;
|
||||
const char *message;
|
||||
const char *options;
|
||||
} outputs;
|
||||
};
|
||||
|
||||
AST_TEST_DEFINE(test_valid_parse_data)
|
||||
{
|
||||
int i;
|
||||
enum ast_presence_state state;
|
||||
char *subtype;
|
||||
char *message;
|
||||
char *options;
|
||||
enum ast_test_result_state res = AST_TEST_PASS;
|
||||
|
||||
struct test_string tests [] = {
|
||||
{ "away",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "not_set",
|
||||
{ AST_PRESENCE_NOT_SET,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "unavailable",
|
||||
{ AST_PRESENCE_UNAVAILABLE,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "available",
|
||||
{ AST_PRESENCE_AVAILABLE,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "xa",
|
||||
{ AST_PRESENCE_XA,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "chat",
|
||||
{ AST_PRESENCE_CHAT,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "dnd",
|
||||
{ AST_PRESENCE_DND,
|
||||
"",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "away,down the hall",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"down the hall",
|
||||
"",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "away,down the hall,Quarterly financial meeting",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"down the hall",
|
||||
"Quarterly financial meeting",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "away,,Quarterly financial meeting",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"",
|
||||
"Quarterly financial meeting",
|
||||
""
|
||||
}
|
||||
},
|
||||
{ "away,,,e",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"",
|
||||
"",
|
||||
"e",
|
||||
}
|
||||
},
|
||||
{ "away,down the hall,,e",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"down the hall",
|
||||
"",
|
||||
"e"
|
||||
}
|
||||
},
|
||||
{ "away,down the hall,Quarterly financial meeting,e",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"down the hall",
|
||||
"Quarterly financial meeting",
|
||||
"e"
|
||||
}
|
||||
},
|
||||
{ "away,,Quarterly financial meeting,e",
|
||||
{ AST_PRESENCE_AWAY,
|
||||
"",
|
||||
"Quarterly financial meeting",
|
||||
"e"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "parse_valid_presence_data";
|
||||
info->category = "/funcs/func_presence";
|
||||
info->summary = "PRESENCESTATE parsing test";
|
||||
info->description =
|
||||
"Ensure that parsing function accepts proper values, and gives proper outputs";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(tests); ++i) {
|
||||
int parse_result;
|
||||
char *parse_string = ast_strdup(tests[i].parse_string);
|
||||
if (!parse_string) {
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
|
||||
if (parse_result == -1) {
|
||||
res = AST_TEST_FAIL;
|
||||
ast_free(parse_string);
|
||||
break;
|
||||
}
|
||||
if (tests[i].outputs.value != state ||
|
||||
strcmp(tests[i].outputs.subtype, subtype) ||
|
||||
strcmp(tests[i].outputs.message, message) ||
|
||||
strcmp(tests[i].outputs.options, options)) {
|
||||
res = AST_TEST_FAIL;
|
||||
ast_free(parse_string);
|
||||
break;
|
||||
}
|
||||
ast_free(parse_string);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_TEST_DEFINE(test_invalid_parse_data)
|
||||
{
|
||||
int i;
|
||||
enum ast_presence_state state;
|
||||
char *subtype;
|
||||
char *message;
|
||||
char *options;
|
||||
enum ast_test_result_state res = AST_TEST_PASS;
|
||||
|
||||
char *tests[] = {
|
||||
"",
|
||||
"bored",
|
||||
"away,,,i",
|
||||
/* XXX The following actually is parsed correctly. Should that
|
||||
* be changed?
|
||||
* "away,,,,e",
|
||||
*/
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "parse_invalid_presence_data";
|
||||
info->category = "/funcs/func_presence";
|
||||
info->summary = "PRESENCESTATE parsing test";
|
||||
info->description =
|
||||
"Ensure that parsing function rejects improper values";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_LEN(tests); ++i) {
|
||||
int parse_result;
|
||||
char *parse_string = ast_strdup(tests[i]);
|
||||
if (!parse_string) {
|
||||
res = AST_TEST_FAIL;
|
||||
break;
|
||||
}
|
||||
parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
|
||||
if (parse_result == 0) {
|
||||
ast_log(LOG_WARNING, "Invalid string parsing failed on %s\n", tests[i]);
|
||||
res = AST_TEST_FAIL;
|
||||
ast_free(parse_string);
|
||||
break;
|
||||
}
|
||||
ast_free(parse_string);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
struct test_cb_data {
|
||||
enum ast_presence_state presence;
|
||||
const char *provider;
|
||||
const char *subtype;
|
||||
const char *message;
|
||||
/* That's right. I'm using a semaphore */
|
||||
sem_t sem;
|
||||
};
|
||||
|
||||
static void test_cb(const struct ast_event *event, void *userdata)
|
||||
{
|
||||
struct test_cb_data *cb_data = userdata;
|
||||
cb_data->presence = ast_event_get_ie_uint(event, AST_EVENT_IE_PRESENCE_STATE);
|
||||
cb_data->provider = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_PROVIDER));
|
||||
cb_data->subtype = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_SUBTYPE));
|
||||
cb_data->message = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_PRESENCE_MESSAGE));
|
||||
sem_post(&cb_data->sem);
|
||||
}
|
||||
|
||||
/* XXX This test could probably stand to be moved since
|
||||
* it does not test func_presencestate but rather code in
|
||||
* presencestate.h and presencestate.c. However, the convenience
|
||||
* of presence_write() makes this a nice location for this test.
|
||||
*/
|
||||
AST_TEST_DEFINE(test_presence_state_change)
|
||||
{
|
||||
struct ast_event_sub *test_sub;
|
||||
struct test_cb_data *cb_data;
|
||||
|
||||
switch (cmd) {
|
||||
case TEST_INIT:
|
||||
info->name = "test_presence_state_change";
|
||||
info->category = "/funcs/func_presence";
|
||||
info->summary = "presence state change subscription";
|
||||
info->description =
|
||||
"Ensure that presence state changes are communicated to subscribers";
|
||||
return AST_TEST_NOT_RUN;
|
||||
case TEST_EXECUTE:
|
||||
break;
|
||||
}
|
||||
|
||||
cb_data = ast_calloc(1, sizeof(*cb_data));
|
||||
if (!cb_data) {
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
if (!(test_sub = ast_event_subscribe(AST_EVENT_PRESENCE_STATE,
|
||||
test_cb, "Test presence state callbacks", cb_data, AST_EVENT_IE_END))) {
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
if (sem_init(&cb_data->sem, 0, 0)) {
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
presence_write(NULL, "PRESENCESTATE", "CustomPresence:TestPresenceStateChange", "away,down the hall,Quarterly financial meeting");
|
||||
sem_wait(&cb_data->sem);
|
||||
if (cb_data->presence != AST_PRESENCE_AWAY ||
|
||||
strcmp(cb_data->provider, "CustomPresence:TestPresenceStateChange") ||
|
||||
strcmp(cb_data->subtype, "down the hall") ||
|
||||
strcmp(cb_data->message, "Quarterly financial meeting")) {
|
||||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
ast_free((char *)cb_data->provider);
|
||||
ast_free((char *)cb_data->subtype);
|
||||
ast_free((char *)cb_data->message);
|
||||
ast_free((char *)cb_data);
|
||||
|
||||
ast_db_del("CustomPresence", "TestPresenceStateChange");
|
||||
|
||||
return AST_TEST_PASS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
res |= ast_custom_function_unregister(&presence_function);
|
||||
res |= ast_presence_state_prov_del("CustomPresence");
|
||||
res |= ast_cli_unregister_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_UNREGISTER(test_valid_parse_data);
|
||||
AST_TEST_UNREGISTER(test_invalid_parse_data);
|
||||
AST_TEST_UNREGISTER(test_presence_state_change);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
struct ast_db_entry *db_entry, *db_tree;
|
||||
|
||||
/* Populate the presence state cache on the system with all of the currently
|
||||
* known custom presence states. */
|
||||
db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
|
||||
for (; db_entry; db_entry = db_entry->next) {
|
||||
const char *dev_name = strrchr(db_entry->key, '/') + 1;
|
||||
char state_info[1301];
|
||||
enum ast_presence_state state;
|
||||
char *message;
|
||||
char *subtype;
|
||||
char *options;
|
||||
if (dev_name <= (const char *) 1) {
|
||||
continue;
|
||||
}
|
||||
ast_copy_string(state_info, db_entry->data, sizeof(state_info));
|
||||
if (parse_data(state_info, &state, &subtype, &message, &options)) {
|
||||
ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
|
||||
continue;
|
||||
}
|
||||
ast_presence_state_changed(state, subtype, message, "CustomPresence:%s", dev_name);
|
||||
}
|
||||
ast_db_freetree(db_tree);
|
||||
db_tree = NULL;
|
||||
|
||||
res |= ast_custom_function_register(&presence_function);
|
||||
res |= ast_presence_state_prov_add("CustomPresence", custom_presence_callback);
|
||||
res |= ast_cli_register_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
|
||||
#ifdef TEST_FRAMEWORK
|
||||
AST_TEST_REGISTER(test_valid_parse_data);
|
||||
AST_TEST_REGISTER(test_invalid_parse_data);
|
||||
AST_TEST_REGISTER(test_presence_state_change);
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gets or sets a presence state in the dialplan",
|
||||
.load = load_module,
|
||||
.unload = unload_module,
|
||||
.load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
|
||||
);
|
||||
|
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2011, 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 Voice Mail API
|
||||
* \author David Vossel <dvossel@digium.com>
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_VM_H
|
||||
#define _ASTERISK_VM_H
|
||||
|
||||
#include "asterisk/stringfields.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
|
||||
#define AST_VM_FOLDER_NUMBER 12
|
||||
|
||||
enum ast_vm_snapshot_sort_val {
|
||||
AST_VM_SNAPSHOT_SORT_BY_ID = 0,
|
||||
AST_VM_SNAPSHOT_SORT_BY_TIME,
|
||||
};
|
||||
|
||||
struct ast_vm_msg_snapshot {
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
AST_STRING_FIELD(msg_id);
|
||||
AST_STRING_FIELD(callerid);
|
||||
AST_STRING_FIELD(callerchan);
|
||||
AST_STRING_FIELD(exten);
|
||||
AST_STRING_FIELD(origdate);
|
||||
AST_STRING_FIELD(origtime);
|
||||
AST_STRING_FIELD(duration);
|
||||
AST_STRING_FIELD(folder_name);
|
||||
AST_STRING_FIELD(flag);
|
||||
);
|
||||
unsigned int msg_number;
|
||||
|
||||
AST_LIST_ENTRY(ast_vm_msg_snapshot) msg;
|
||||
};
|
||||
|
||||
struct ast_vm_mailbox_snapshot {
|
||||
int total_msg_num;
|
||||
AST_LIST_HEAD_NOLOCK(, ast_vm_msg_snapshot) snapshots[AST_VM_FOLDER_NUMBER];
|
||||
};
|
||||
|
||||
/*
|
||||
* \brief Create a snapshot of a mailbox which contains information about every msg.
|
||||
*
|
||||
* \param mailbox, the mailbox to look for
|
||||
* \param context, the context to look for the mailbox in
|
||||
* \param folder, OPTIONAL. When not NULL only msgs from the specified folder will be included.
|
||||
* \param desending, list the msgs in descending order rather than ascending order.
|
||||
* \param combine_INBOX_and_OLD, When this argument is set, The OLD folder will be represented
|
||||
* in the INBOX folder of the snapshot. This allows the snapshot to represent the
|
||||
* OLD and INBOX messages in sorted order merged together.
|
||||
*
|
||||
* \retval snapshot on success
|
||||
* \retval NULL on failure
|
||||
*/
|
||||
struct ast_vm_mailbox_snapshot *ast_vm_mailbox_snapshot_create(const char *mailbox,
|
||||
const char *context,
|
||||
const char *folder,
|
||||
int descending,
|
||||
enum ast_vm_snapshot_sort_val sort_val,
|
||||
int combine_INBOX_and_OLD);
|
||||
|
||||
/*
|
||||
* \brief destroy a snapshot
|
||||
*
|
||||
* \param mailbox_snapshot The snapshot to destroy.
|
||||
* \retval NULL
|
||||
*/
|
||||
struct ast_vm_mailbox_snapshot *ast_vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
|
||||
|
||||
/*!
|
||||
* \brief Move messages from one folder to another
|
||||
*
|
||||
* \param mailbox The mailbox to which the folders belong
|
||||
* \param context The voicemail context for the mailbox
|
||||
* \param num_msgs The number of messages to move
|
||||
* \param oldfolder The folder from where messages should be moved
|
||||
* \param old_msg_nums The message IDs of the messages to move
|
||||
* \param newfolder The folder to which messages should be moved
|
||||
* \param new_msg_ids[out] An array of message IDs for the messages as they are in the
|
||||
* new folder. This array must be num_msgs sized.
|
||||
*
|
||||
* \retval -1 Failure
|
||||
* \retval 0 Success
|
||||
*/
|
||||
int ast_vm_msg_move(const char *mailbox,
|
||||
const char *context,
|
||||
size_t num_msgs,
|
||||
const char *oldfolder,
|
||||
const char *old_msg_ids [],
|
||||
const char *newfolder);
|
||||
|
||||
/*!
|
||||
* \brief Remove/delete messages from a mailbox folder.
|
||||
*
|
||||
* \param mailbox The mailbox from which to delete messages
|
||||
* \param context The voicemail context for the mailbox
|
||||
* \param num_msgs The number of messages to delete
|
||||
* \param folder The folder from which to remove messages
|
||||
* \param msgs The message IDs of the messages to delete
|
||||
*
|
||||
* \retval -1 Failure
|
||||
* \retval 0 Success
|
||||
*/
|
||||
int ast_vm_msg_remove(const char *mailbox,
|
||||
const char *context,
|
||||
size_t num_msgs,
|
||||
const char *folder,
|
||||
const char *msgs []);
|
||||
|
||||
/*!
|
||||
* \brief forward a message from one mailbox to another.
|
||||
*
|
||||
* \brief from_mailbox The original mailbox the message is being forwarded from
|
||||
* \brief from_context The voicemail context of the from_mailbox
|
||||
* \brief from_folder The folder from which the message is being forwarded
|
||||
* \brief to_mailbox The mailbox to forward the message to
|
||||
* \brief to_context The voicemail context of the to_mailbox
|
||||
* \brief to_folder The voicemail folder to forward the message to
|
||||
* \brief num_msgs The number of messages being forwarded
|
||||
* \brief msg_ids The message IDs of the messages in from_mailbox to forward
|
||||
* \brief delete_old If non-zero, the forwarded messages are also deleted from from_mailbox.
|
||||
* Otherwise, the messages will remain in the from_mailbox.
|
||||
*
|
||||
* \retval -1 Failure
|
||||
* \retval 0 Success
|
||||
*/
|
||||
int ast_vm_msg_forward(const char *from_mailbox,
|
||||
const char *from_context,
|
||||
const char *from_folder,
|
||||
const char *to_mailbox,
|
||||
const char *to_context,
|
||||
const char *to_folder,
|
||||
size_t num_msgs,
|
||||
const char *msg_ids [],
|
||||
int delete_old);
|
||||
|
||||
/*!
|
||||
* \brief Voicemail playback callback function definition
|
||||
*
|
||||
* \param channel to play the file back on.
|
||||
* \param location of file on disk
|
||||
* \param duration of file in seconds. This will be zero if msg is very short or
|
||||
* has an unknown duration.
|
||||
*/
|
||||
typedef void (ast_vm_msg_play_cb)(struct ast_channel *chan, const char *playfile, int duration);
|
||||
|
||||
/*!
|
||||
* \brief Play a voicemail msg back on a channel.
|
||||
*
|
||||
* \param mailbox msg is in.
|
||||
* \param context of mailbox.
|
||||
* \param voicemail folder to look in.
|
||||
* \param message number in the voicemailbox to playback to the channel.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_vm_msg_play(struct ast_channel *chan,
|
||||
const char *mailbox,
|
||||
const char *context,
|
||||
const char *folder,
|
||||
const char *msg_id,
|
||||
ast_vm_msg_play_cb cb);
|
||||
|
||||
/*!
|
||||
* \brief Get the name of a folder given its numeric index
|
||||
*
|
||||
* \param index The integer value of the mailbox.
|
||||
* \retval "" Invalid index provided
|
||||
* \retval other The name of the mailbox
|
||||
*/
|
||||
const char *ast_vm_index_to_foldername(unsigned int index);
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
/*!
|
||||
* \brief Add a user to the voicemail system for test purposes
|
||||
* \param context The context of the mailbox
|
||||
* \param mailbox The mailbox for the user
|
||||
* \retval 0 success
|
||||
* \retval other failure
|
||||
*/
|
||||
int ast_vm_test_create_user(const char *context, const char *mailbox);
|
||||
|
||||
/*!
|
||||
* \brief Dispose of a user. This should be used to destroy a user that was
|
||||
* previously created using ast_vm_test_create_user
|
||||
* \param context The context of the mailbox
|
||||
* \param mailbox The mailbox for the user to destroy
|
||||
*/
|
||||
int ast_vm_test_destroy_user(const char *context, const char *mailbox);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2011, 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
|
||||
*/
|
||||
|
||||
#ifndef _ASTERISK_PRESSTATE_H
|
||||
#define _ASTERISK_PRESSTATE_H
|
||||
|
||||
enum ast_presence_state {
|
||||
AST_PRESENCE_NOT_SET = 0,
|
||||
AST_PRESENCE_UNAVAILABLE,
|
||||
AST_PRESENCE_AVAILABLE,
|
||||
AST_PRESENCE_AWAY,
|
||||
AST_PRESENCE_XA,
|
||||
AST_PRESENCE_CHAT,
|
||||
AST_PRESENCE_DND,
|
||||
/* This is not something that a user can
|
||||
* set his presence to. Rather, this is returned
|
||||
* to indicate that presence is in some invalid
|
||||
* state
|
||||
*/
|
||||
AST_PRESENCE_INVALID,
|
||||
};
|
||||
|
||||
/*! \brief Presence state provider call back */
|
||||
typedef enum ast_presence_state (*ast_presence_state_prov_cb_type)(const char *data, char **subtype, char **message);
|
||||
|
||||
/*!
|
||||
* \brief Convert presence state to text string for output
|
||||
*
|
||||
* \param state Current presence state
|
||||
*/
|
||||
const char *ast_presence_state2str(enum ast_presence_state state);
|
||||
|
||||
/*!
|
||||
* \brief Convert presence state from text to integer value
|
||||
*
|
||||
* \param val The text representing the presence state. Valid values are anything
|
||||
* that comes after AST_PRESENCE_ in one of the defined values.
|
||||
*
|
||||
* \return The AST_PRESENCE_ integer value
|
||||
*/
|
||||
enum ast_presence_state ast_presence_state_val(const char *val);
|
||||
|
||||
/*!
|
||||
* \brief Asks a presence state provider for the current presence state.
|
||||
*
|
||||
* \param presence_provider, The presence provider to retrieve the state from.
|
||||
* \param subtype, The output paramenter to store the subtype string in. Must be freed if returned
|
||||
* \param message, The output paramenter to store the message string in. Must be freed if returned
|
||||
*
|
||||
* \retval presence state value on success,
|
||||
* \retval -1 on failure.
|
||||
*/
|
||||
enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message);
|
||||
|
||||
/*!
|
||||
* \brief Asks a presence state provider for the current presence state, bypassing the event cache
|
||||
*
|
||||
* \details Some presence state providers may perform transformations on presence data when it is
|
||||
* requested (such as a base64 decode). In such instances, use of the event cache is not suitable
|
||||
* and should be bypassed.
|
||||
*
|
||||
* \param presence_provider, The presence provider to retrieve the state from.
|
||||
* \param subtype, The output paramenter to store the subtype string in. Must be freed if returned
|
||||
* \param message, The output paramenter to store the message string in. Must be freed if returned
|
||||
*
|
||||
* \retval presence state value on success,
|
||||
* \retval -1 on failure.
|
||||
*/
|
||||
enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message);
|
||||
|
||||
/*!
|
||||
* \brief Notify the world that a presence provider state changed.
|
||||
*
|
||||
* \param state the new presence state
|
||||
* \param subtype the new presence subtype
|
||||
* \param message the new presence message
|
||||
* \param fmt Presence entity whose state has changed
|
||||
*
|
||||
* The new state of the entity will be sent off to any subscribers
|
||||
* of the presence state. It will also be stored in the internal event
|
||||
* cache.
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval -1 Failure
|
||||
*/
|
||||
int ast_presence_state_changed(enum ast_presence_state state,
|
||||
const char *subtype,
|
||||
const char *message,
|
||||
const char *fmt, ...)
|
||||
__attribute__((format(printf, 4, 5)));
|
||||
|
||||
/*!
|
||||
* \brief Notify the world that a presence provider state changed.
|
||||
*
|
||||
* \param state the new presence state
|
||||
* \param subtype the new presence subtype
|
||||
* \param message the new presence message
|
||||
* \param presence_provider Presence entity whose state has changed
|
||||
*
|
||||
* The new state of the entity will be sent off to any subscribers
|
||||
* of the presence state. It will also be stored in the internal event
|
||||
* cache.
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval -1 Failure
|
||||
*/
|
||||
int ast_presence_state_changed_literal(enum ast_presence_state state,
|
||||
const char *subtype,
|
||||
const char *message,
|
||||
const char *presence_provider);
|
||||
|
||||
/*!
|
||||
* \brief Add presence state provider
|
||||
*
|
||||
* \param label to use in hint, like label:object
|
||||
* \param callback Callback
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
*/
|
||||
int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback);
|
||||
|
||||
/*!
|
||||
* \brief Remove presence state provider
|
||||
*
|
||||
* \param label to use in hint, like label:object
|
||||
*
|
||||
* \retval -1 on failure
|
||||
* \retval 0 on success
|
||||
*/
|
||||
int ast_presence_state_prov_del(const char *label);
|
||||
|
||||
int ast_presence_state_engine_init(void);
|
||||
#endif
|
||||
|
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2012, Digium, Inc.
|
||||
*
|
||||
* Mark Michelson <mmichelson@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.
|
||||
*/
|
||||
|
||||
#ifndef __ASTERISK_SIP_H
|
||||
#define __ASTERISK_SIP_H
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "asterisk/optional_api.h"
|
||||
#include "asterisk/config.h"
|
||||
|
||||
/*!
|
||||
* \brief Send a customized SIP INFO request
|
||||
*
|
||||
* \param headers The headers to add to the INFO request
|
||||
* \param content_type The content type header to add
|
||||
* \param conten The body of the INFO request
|
||||
* \param useragent_filter If non-NULL, only send the INFO if the
|
||||
* recipient's User-Agent contains useragent_filter as a substring
|
||||
*
|
||||
* \retval 0 Success
|
||||
* \retval non-zero Failure
|
||||
*/
|
||||
int ast_sipinfo_send(struct ast_channel *chan,
|
||||
struct ast_variable *headers,
|
||||
const char *content_type,
|
||||
const char *content,
|
||||
const char *useragent_filter);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ASTERISK_SIP_H */
|
@ -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;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue