100 static const char astdb_family[] =
"CustomPresence";
102 static int presence_read(
struct ast_channel *chan,
const char *cmd,
char *data,
char *buf,
size_t len)
106 char *subtype = NULL;
108 int base64encode = 0;
115 if (ast_strlen_zero(data)) {
116 ast_log(LOG_WARNING,
"PRESENCE_STATE reading requires an argument \n");
124 if (ast_strlen_zero(args.provider) || ast_strlen_zero(args.field)) {
125 ast_log(LOG_WARNING,
"PRESENCE_STATE reading requires both presence provider and presence field arguments. \n");
130 if (state == AST_PRESENCE_INVALID) {
131 ast_log(LOG_WARNING,
"PRESENCE_STATE unknown \n");
135 if (!(ast_strlen_zero(args.options)) && (strchr(args.options,
'e'))) {
139 if (!ast_strlen_zero(subtype) && !strcasecmp(args.field,
"subtype")) {
145 }
else if (!ast_strlen_zero(message) && !strcasecmp(args.field,
"message")) {
152 }
else if (!strcasecmp(args.field,
"value")) {
162 static int parse_data(
char *data,
enum ast_presence_state *state,
char **subtype,
char **message,
char **options)
171 state_str = strsep(&data,
",");
172 if (ast_strlen_zero(state_str)) {
179 if (*state == AST_PRESENCE_INVALID) {
180 ast_log(LOG_WARNING,
"Unknown presence state value %s\n", state_str);
184 if (!(*subtype = strsep(&data,
","))) {
189 if (!(*message = strsep(&data,
","))) {
194 if (!(*options = strsep(&data,
","))) {
199 if (!ast_strlen_zero(*options) && !(strchr(*options,
'e'))) {
200 ast_log(LOG_NOTICE,
"Invalid options '%s'\n", *options);
207 static int presence_write(
struct ast_channel *chan,
const char *cmd,
char *data,
const char *value)
209 size_t len = strlen(
"CustomPresence:");
212 enum ast_presence_state state;
213 char *options, *message, *subtype;
215 if (strncasecmp(data,
"CustomPresence:", len)) {
216 ast_log(LOG_WARNING,
"The PRESENCE_STATE function can only set CustomPresence: presence providers.\n");
220 if (ast_strlen_zero(data)) {
221 ast_log(LOG_WARNING,
"PRESENCE_STATE function called with no custom device name!\n");
225 if (parse_data(args, &state, &subtype, &message, &options)) {
226 ast_log(LOG_WARNING,
"Invalid arguments to PRESENCE_STATE\n");
232 if (strchr(options,
'e')) {
234 char decoded_subtype[256] = { 0, };
235 char decoded_message[256] = { 0, };
237 ast_base64decode((
unsigned char *) decoded_subtype, subtype,
sizeof(decoded_subtype) -1);
238 ast_base64decode((
unsigned char *) decoded_message, message,
sizeof(decoded_message) -1);
248 static enum ast_presence_state custom_presence_callback(
const char *data,
char **subtype,
char **message)
251 enum ast_presence_state state;
256 if (
ast_db_get(astdb_family, data, buf,
sizeof(buf))) {
257 return AST_PRESENCE_NOT_SET;
260 if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
261 return AST_PRESENCE_INVALID;
264 if ((strchr(_options,
'e'))) {
267 if (ast_strlen_zero(_subtype)) {
270 memset(tmp, 0,
sizeof(tmp));
275 if (ast_strlen_zero(_message)) {
278 memset(tmp, 0,
sizeof(tmp));
283 *subtype = ast_strlen_zero(_subtype) ? NULL :
ast_strdup(_subtype);
284 *message = ast_strlen_zero(_message) ? NULL :
ast_strdup(_message);
290 .
name =
"PRESENCE_STATE",
291 .read = presence_read,
292 .write = presence_write,
301 e->
command =
"presencestate list";
303 "Usage: presencestate list\n"
304 " List all custom presence states that have been set by using\n"
305 " the PRESENCE_STATE dialplan function.\n";
311 if (a->argc != e->
args) {
312 return CLI_SHOWUSAGE;
316 "---------------------------------------------------------------------\n"
317 "--- Custom Presence States ------------------------------------------\n"
318 "---------------------------------------------------------------------\n"
323 ast_cli(a->fd,
"No custom presence states defined\n");
326 for (; db_entry; db_entry = db_entry->next) {
327 const char *object_name = strrchr(db_entry->key,
'/') + 1;
328 char state_info[1301];
329 enum ast_presence_state state;
335 if (parse_data(state_info, &state, &subtype, &message, &options)) {
336 ast_log(LOG_WARNING,
"Invalid CustomPresence entry %s encountered\n", db_entry->data);
340 if (object_name <= (
const char *) 1) {
343 ast_cli(a->fd,
"--- Name: 'CustomPresence:%s'\n"
345 " --- Subtype: '%s'\n"
346 " --- Message: '%s'\n"
347 " --- Base64 Encoded: '%s'\n"
359 "---------------------------------------------------------------------\n"
360 "---------------------------------------------------------------------\n"
369 const char *dev, *state, *full_dev;
370 enum ast_presence_state state_val;
378 e->
command =
"presencestate change";
380 "Usage: presencestate change <entity> <state>[,<subtype>[,message[,options]]]\n"
381 " Change a custom presence to a new state.\n"
382 " The possible values for the state are:\n"
383 "NOT_SET | UNAVAILABLE | AVAILABLE | AWAY | XA | CHAT | DND\n"
384 "Optionally, a custom subtype and message may be provided, along with any options\n"
385 "accepted by func_presencestate. If the subtype or message provided contain spaces,\n"
386 "be sure to enclose the data in quotation marks (\"\")\n"
389 " presencestate change CustomPresence:mystate1 AWAY\n"
390 " presencestate change CustomPresence:mystate1 AVAILABLE\n"
391 " presencestate change CustomPresence:mystate1 \"Away,upstairs,eating lunch\"\n"
396 static const char *
const cmds[] = {
"NOT_SET",
"UNAVAILABLE",
"AVAILABLE",
"AWAY",
397 "XA",
"CHAT",
"DND", NULL };
399 if (a->pos == e->
args + 1) {
407 if (a->argc != e->
args + 2) {
408 return CLI_SHOWUSAGE;
411 len = strlen(
"CustomPresence:");
412 full_dev = dev = a->argv[e->
args];
413 state = a->argv[e->
args + 1];
415 if (strncasecmp(dev,
"CustomPresence:", len)) {
416 ast_cli(a->fd,
"The presencestate command can only be used to set 'CustomPresence:' presence state!\n");
421 if (ast_strlen_zero(dev)) {
422 return CLI_SHOWUSAGE;
426 if (parse_data(args, &state_val, &subtype, &message, &options)) {
427 return CLI_SHOWUSAGE;
430 if (state_val == AST_PRESENCE_NOT_SET) {
431 return CLI_SHOWUSAGE;
434 ast_cli(a->fd,
"Changing %s to %s\n", dev, args);
444 AST_CLI_DEFINE(handle_cli_presencestate_list,
"List currently know custom presence states"),
445 AST_CLI_DEFINE(handle_cli_presencestate_change,
"Change a custom presence state"),
448 #ifdef TEST_FRAMEWORK
463 enum ast_presence_state state;
467 enum ast_test_result_state res = AST_TEST_PASS;
469 struct test_string tests [] = {
478 { AST_PRESENCE_NOT_SET,
485 { AST_PRESENCE_UNAVAILABLE,
492 { AST_PRESENCE_AVAILABLE,
519 {
"away,down the hall",
526 {
"away,down the hall,Quarterly financial meeting",
529 "Quarterly financial meeting",
533 {
"away,,Quarterly financial meeting",
536 "Quarterly financial meeting",
547 {
"away,down the hall,,e",
554 {
"away,down the hall,Quarterly financial meeting,e",
557 "Quarterly financial meeting",
561 {
"away,,Quarterly financial meeting,e",
564 "Quarterly financial meeting",
572 info->name =
"parse_valid_presence_data";
573 info->category =
"/funcs/func_presence/";
574 info->summary =
"PRESENCESTATE parsing test";
576 "Ensure that parsing function accepts proper values, and gives proper outputs";
577 return AST_TEST_NOT_RUN;
582 for (i = 0; i < ARRAY_LEN(tests); ++i) {
584 char *parse_string =
ast_strdup(tests[i].parse_string);
589 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
590 if (parse_result == -1) {
592 ast_free(parse_string);
595 if (tests[i].outputs.value != state ||
596 strcmp(tests[i].outputs.subtype, subtype) ||
597 strcmp(tests[i].outputs.message, message) ||
598 strcmp(tests[i].outputs.options, options)) {
600 ast_free(parse_string);
603 ast_free(parse_string);
612 enum ast_presence_state state;
616 enum ast_test_result_state res = AST_TEST_PASS;
630 info->name =
"parse_invalid_presence_data";
631 info->category =
"/funcs/func_presence/";
632 info->summary =
"PRESENCESTATE parsing test";
634 "Ensure that parsing function rejects improper values";
635 return AST_TEST_NOT_RUN;
640 for (i = 0; i < ARRAY_LEN(tests); ++i) {
647 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
648 if (parse_result == 0) {
649 ast_log(LOG_WARNING,
"Invalid string parsing failed on %s\n", tests[i]);
651 ast_free(parse_string);
654 ast_free(parse_string);
660 #define PRES_STATE "away"
661 #define PRES_SUBTYPE "down the hall"
662 #define PRES_MESSAGE "Quarterly financial meeting"
664 struct test_cb_data {
670 static struct test_cb_data *test_cb_data_alloc(
void)
672 struct test_cb_data *cb_data =
ast_calloc(1,
sizeof(*cb_data));
686 static void test_cb_data_destroy(
struct test_cb_data *cb_data)
688 ao2_cleanup(cb_data->presence_state);
695 struct test_cb_data *cb_data = userdata;
700 ao2_ref(cb_data->presence_state, +1);
705 static enum ast_test_result_state presence_change_common(
struct ast_test *
test,
706 const char *state,
const char *subtype,
const char *message,
const char *options,
707 char *out_state,
size_t out_state_size,
708 char *out_subtype,
size_t out_subtype_size,
709 char *out_message,
size_t out_message_size)
711 RAII_VAR(
struct test_cb_data *, cb_data, test_cb_data_alloc(), test_cb_data_destroy);
716 return AST_TEST_FAIL;
719 if (ast_strlen_zero(options)) {
720 snprintf(pres,
sizeof(pres),
"%s,%s,%s", state, subtype, message);
722 snprintf(pres,
sizeof(pres),
"%s,%s,%s,%s", state, subtype, message, options);
725 if (presence_write(NULL,
"PRESENCESTATE",
"CustomPresence:TestPresenceStateChange", pres)) {
727 return AST_TEST_FAIL;
733 ast_copy_string(out_subtype, cb_data->presence_state->subtype, out_subtype_size);
734 ast_copy_string(out_message, cb_data->presence_state->message, out_message_size);
737 ast_db_del(
"CustomPresence",
"TestPresenceStateChange");
739 return AST_TEST_PASS;
745 char out_subtype[32];
746 char out_message[32];
750 info->name =
"test_presence_state_change";
751 info->category =
"/funcs/func_presence/";
752 info->summary =
"presence state change subscription";
754 "Ensure that presence state changes are communicated to subscribers";
755 return AST_TEST_NOT_RUN;
760 if (presence_change_common(test, PRES_STATE, PRES_SUBTYPE, PRES_MESSAGE, NULL,
761 out_state,
sizeof(out_state),
762 out_subtype,
sizeof(out_subtype),
763 out_message,
sizeof(out_message)) == AST_TEST_FAIL) {
764 return AST_TEST_FAIL;
767 if (strcmp(out_state, PRES_STATE) ||
768 strcmp(out_subtype, PRES_SUBTYPE) ||
769 strcmp(out_message, PRES_MESSAGE)) {
770 ast_test_status_update(test,
"Unexpected presence values, %s != %s, %s != %s, or %s != %s\n",
771 PRES_STATE, out_state,
772 PRES_SUBTYPE, out_subtype,
773 PRES_MESSAGE, out_message);
774 return AST_TEST_FAIL;
777 return AST_TEST_PASS;
783 char out_subtype[32];
784 char out_message[32];
785 char encoded_subtype[64];
786 char encoded_message[64];
790 info->name =
"test_presence_state_base64_encode";
791 info->category =
"/funcs/func_presence/";
792 info->summary =
"presence state base64 encoding";
794 "Ensure that base64-encoded presence state is stored base64-encoded but\n"
795 "is presented to consumers decoded.";
796 return AST_TEST_NOT_RUN;
801 ast_base64encode(encoded_subtype, (
unsigned char *) PRES_SUBTYPE, strlen(PRES_SUBTYPE),
sizeof(encoded_subtype) - 1);
802 ast_base64encode(encoded_message, (
unsigned char *) PRES_MESSAGE, strlen(PRES_MESSAGE),
sizeof(encoded_message) - 1);
804 if (presence_change_common(test, PRES_STATE, encoded_subtype, encoded_message,
"e",
805 out_state,
sizeof(out_state),
806 out_subtype,
sizeof(out_subtype),
807 out_message,
sizeof(out_message)) == AST_TEST_FAIL) {
808 return AST_TEST_FAIL;
811 if (strcmp(out_state, PRES_STATE) ||
812 strcmp(out_subtype, PRES_SUBTYPE) ||
813 strcmp(out_message, PRES_MESSAGE)) {
814 ast_test_status_update(test,
"Unexpected presence values, %s != %s, %s != %s, or %s != %s\n",
815 PRES_STATE, out_state,
816 PRES_SUBTYPE, out_subtype,
817 PRES_MESSAGE, out_message);
818 return AST_TEST_FAIL;
821 return AST_TEST_PASS;
826 static int unload_module(
void)
833 #ifdef TEST_FRAMEWORK
834 AST_TEST_UNREGISTER(test_valid_parse_data);
835 AST_TEST_UNREGISTER(test_invalid_parse_data);
836 AST_TEST_UNREGISTER(test_presence_state_change);
837 AST_TEST_UNREGISTER(test_presence_state_base64_encode);
842 static int load_module(
void)
850 for (; db_entry; db_entry = db_entry->next) {
851 const char *dev_name = strrchr(db_entry->key,
'/') + 1;
852 enum ast_presence_state state;
853 char *message = NULL;
854 char *subtype = NULL;
855 if (dev_name <= (
const char *) 1) {
858 state = custom_presence_callback(dev_name, &subtype, &message);
869 #ifdef TEST_FRAMEWORK
870 AST_TEST_REGISTER(test_valid_parse_data);
871 AST_TEST_REGISTER(test_invalid_parse_data);
872 AST_TEST_REGISTER(test_presence_state_change);
873 AST_TEST_REGISTER(test_presence_state_base64_encode);
879 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,
"Gets or sets a presence state in the dialplan",
880 .support_level = AST_MODULE_SUPPORT_CORE,
882 .unload = unload_module,
Main Channel structure associated with a channel.
int ast_sem_destroy(struct ast_sem *sem)
Destroy a semaphore.
Asterisk main include file. File version handling, generic pbx functions.
int ast_presence_state_changed_literal(enum ast_presence_state state, const char *subtype, const char *message, const char *presence_provider)
Notify the world that a presence provider state changed.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
enum ast_presence_state ast_presence_state_val(const char *val)
Convert presence state from text to integer value.
int ast_sem_post(struct ast_sem *sem)
Increments the semaphore, unblocking a waiter if necessary.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
descriptor for a cli entry.
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
Asks a presence state provider for the current presence state, bypassing the event cache...
Stasis message payload representing a presence state update.
#define ast_strdup(str)
A wrapper for strdup()
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int args
This gets set in ast_cli_register()
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
int ast_base64decode(unsigned char *dst, const char *src, int max)
Decode data from base64.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Data structure associated with a custom dialplan function.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
Add presence state provider.
A set of macros to manage forward-linked lists.
Core PBX routines and definitions.
int ast_sem_wait(struct ast_sem *sem)
Decrements the semaphore.
Presence state management.
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_sem_init(struct ast_sem *sem, int pshared, unsigned int value)
Initialize a semaphore.
int ast_presence_state_prov_del(const char *label)
Remove presence state provider.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Standard Command Line Interface.
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
#define AST_TEST_DEFINE(hdr)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
int ast_presence_state_changed(enum ast_presence_state state, const char *subtype, const char *message, const char *fmt,...)
Notify the world that a presence provider state changed.
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Persistent data storage (akin to *doze registry)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
#define AST_APP_ARG(name)
Define an application argument.