Asterisk - The Open Source Telephony Project GIT-master-a358458
func_presencestate.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2011, Digium, Inc.
5 *
6 * David Vossel <dvossel@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief Custom presence provider
22 * \ingroup functions
23 */
24
25/*** MODULEINFO
26 <support_level>core</support_level>
27 ***/
28
29#include "asterisk.h"
30
31#include "asterisk/module.h"
32#include "asterisk/channel.h"
33#include "asterisk/pbx.h"
34#include "asterisk/utils.h"
37#include "asterisk/cli.h"
38#include "asterisk/astdb.h"
39#include "asterisk/app.h"
40#ifdef TEST_FRAMEWORK
41#include "asterisk/test.h"
42#include "asterisk/sem.h"
43#endif
44
45/*** DOCUMENTATION
46 <function name="PRESENCE_STATE" language="en_US">
47 <synopsis>
48 Get or Set a presence state.
49 </synopsis>
50 <syntax>
51 <parameter name="provider" required="true">
52 <para>The provider of the presence, such as <literal>CustomPresence</literal></para>
53 </parameter>
54 <parameter name="field" required="true">
55 <para>Which field of the presence state information is wanted.</para>
56 <optionlist>
57 <option name="value">
58 <para>The current presence, such as <literal>away</literal></para>
59 </option>
60 <option name="subtype">
61 <para>Further information about the current presence</para>
62 </option>
63 <option name="message">
64 <para>A custom message that may indicate further details about the presence</para>
65 </option>
66 </optionlist>
67 </parameter>
68 <parameter name="options" required="false">
69 <optionlist>
70 <option name="e">
71 <para>On Write - Use this option when the subtype and message provided are Base64
72 encoded. The values will be stored encoded within Asterisk, but all consumers of
73 the presence state (e.g. the SIP presence event package) will receive decoded values.</para>
74 <para>On Read - Retrieves unencoded message/subtype in Base64 encoded form.</para>
75 </option>
76 </optionlist>
77 </parameter>
78 </syntax>
79 <description>
80 <para>The PRESENCE_STATE function can be used to retrieve the presence from any
81 presence provider. For example:</para>
82 <para>NoOp(SIP/mypeer has presence ${PRESENCE_STATE(SIP/mypeer,value)})</para>
83 <para>NoOp(Conference number 1234 has presence message ${PRESENCE_STATE(MeetMe:1234,message)})</para>
84 <para>The PRESENCE_STATE function can also be used to set custom presence state from
85 the dialplan. The <literal>CustomPresence:</literal> prefix must be used. For example:</para>
86 <para>Set(PRESENCE_STATE(CustomPresence:lamp1)=away,temporary,Out to lunch)</para>
87 <para>Set(PRESENCE_STATE(CustomPresence:lamp2)=dnd,,Trying to get work done)</para>
88 <para>Set(PRESENCE_STATE(CustomPresence:lamp3)=xa,T24gdmFjYXRpb24=,,e)</para>
89 <para>Set(BASE64_LAMP3_PRESENCE=${PRESENCE_STATE(CustomPresence:lamp3,subtype,e)})</para>
90 <para>You can subscribe to the status of a custom presence state using a hint in
91 the dialplan:</para>
92 <para>exten => 1234,hint,,CustomPresence:lamp1</para>
93 <para>The possible values for both uses of this function are:</para>
94 <para>not_set | unavailable | available | away | xa | chat | dnd</para>
95 </description>
96 </function>
97 ***/
98
99
100static const char astdb_family[] = "CustomPresence";
101
102static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
103{
104 int state;
105 char *message = NULL;
106 char *subtype = NULL;
107 char *parse;
108 int base64encode = 0;
111 AST_APP_ARG(field);
113 );
114
115 if (ast_strlen_zero(data)) {
116 ast_log(LOG_WARNING, "PRESENCE_STATE reading requires an argument \n");
117 return -1;
118 }
119
120 parse = ast_strdupa(data);
121
123
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");
126 return -1;
127 }
128
129 state = ast_presence_state_nocache(args.provider, &subtype, &message);
131 ast_log(LOG_WARNING, "PRESENCE_STATE unknown \n");
132 return -1;
133 }
134
135 if (!(ast_strlen_zero(args.options)) && (strchr(args.options, 'e'))) {
136 base64encode = 1;
137 }
138
139 if (!ast_strlen_zero(subtype) && !strcasecmp(args.field, "subtype")) {
140 if (base64encode) {
141 ast_base64encode(buf, (unsigned char *) subtype, strlen(subtype), len);
142 } else {
143 ast_copy_string(buf, subtype, len);
144 }
145 } else if (!ast_strlen_zero(message) && !strcasecmp(args.field, "message")) {
146 if (base64encode) {
147 ast_base64encode(buf, (unsigned char *) message, strlen(message), len);
148 } else {
150 }
151
152 } else if (!strcasecmp(args.field, "value")) {
154 }
155
157 ast_free(subtype);
158
159 return 0;
160}
161
162static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
163{
164 char *state_str;
165
166 /* data syntax is state,subtype,message,options */
167 *subtype = "";
168 *message = "";
169 *options = "";
170
171 state_str = strsep(&data, ",");
172 if (ast_strlen_zero(state_str)) {
173 return -1; /* state is required */
174 }
175
176 *state = ast_presence_state_val(state_str);
177
178 /* not a valid state */
179 if (*state == AST_PRESENCE_INVALID) {
180 ast_log(LOG_WARNING, "Unknown presence state value %s\n", state_str);
181 return -1;
182 }
183
184 if (!(*subtype = strsep(&data,","))) {
185 *subtype = "";
186 return 0;
187 }
188
189 if (!(*message = strsep(&data, ","))) {
190 *message = "";
191 return 0;
192 }
193
194 if (!(*options = strsep(&data, ","))) {
195 *options = "";
196 return 0;
197 }
198
199 if (!ast_strlen_zero(*options) && !(strchr(*options, 'e'))) {
200 ast_log(LOG_NOTICE, "Invalid options '%s'\n", *options);
201 return -1;
202 }
203
204 return 0;
205}
206
207static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
208{
209 size_t len = strlen("CustomPresence:");
210 char *tmp = data;
211 char *args = ast_strdupa(value);
213 char *options, *message, *subtype;
214
215 if (strncasecmp(data, "CustomPresence:", len)) {
216 ast_log(LOG_WARNING, "The PRESENCE_STATE function can only set CustomPresence: presence providers.\n");
217 return -1;
218 }
219 data += len;
220 if (ast_strlen_zero(data)) {
221 ast_log(LOG_WARNING, "PRESENCE_STATE function called with no custom device name!\n");
222 return -1;
223 }
224
225 if (parse_data(args, &state, &subtype, &message, &options)) {
226 ast_log(LOG_WARNING, "Invalid arguments to PRESENCE_STATE\n");
227 return -1;
228 }
229
231
232 if (strchr(options, 'e')) {
233 /* Let's decode the values before sending them to stasis, yes? */
234 char decoded_subtype[256] = { 0, };
235 char decoded_message[256] = { 0, };
236
237 ast_base64decode((unsigned char *) decoded_subtype, subtype, sizeof(decoded_subtype) -1);
238 ast_base64decode((unsigned char *) decoded_message, message, sizeof(decoded_message) -1);
239
240 ast_presence_state_changed_literal(state, decoded_subtype, decoded_message, tmp);
241 } else {
243 }
244
245 return 0;
246}
247
248static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
249{
250 char buf[1301] = "";
252 char *_options;
253 char *_message;
254 char *_subtype;
255
256 if (ast_db_get(astdb_family, data, buf, sizeof(buf))) {
258 }
259
260 if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
262 }
263
264 if ((strchr(_options, 'e'))) {
265 char tmp[1301];
266
267 if (ast_strlen_zero(_subtype)) {
268 *subtype = NULL;
269 } else {
270 memset(tmp, 0, sizeof(tmp));
271 ast_base64decode((unsigned char *) tmp, _subtype, sizeof(tmp) - 1);
272 *subtype = ast_strdup(tmp);
273 }
274
275 if (ast_strlen_zero(_message)) {
276 *message = NULL;
277 } else {
278 memset(tmp, 0, sizeof(tmp));
279 ast_base64decode((unsigned char *) tmp, _message, sizeof(tmp) - 1);
281 }
282 } else {
283 *subtype = ast_strlen_zero(_subtype) ? NULL : ast_strdup(_subtype);
284 *message = ast_strlen_zero(_message) ? NULL : ast_strdup(_message);
285 }
286 return state;
287}
288
290 .name = "PRESENCE_STATE",
291 .read = presence_read,
292 .write = presence_write,
293};
294
295static char *handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
296{
297 struct ast_db_entry *db_entry, *db_tree;
298
299 switch (cmd) {
300 case CLI_INIT:
301 e->command = "presencestate list";
302 e->usage =
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";
306 return NULL;
307 case CLI_GENERATE:
308 return NULL;
309 }
310
311 if (a->argc != e->args) {
312 return CLI_SHOWUSAGE;
313 }
314
315 ast_cli(a->fd, "\n"
316 "---------------------------------------------------------------------\n"
317 "--- Custom Presence States ------------------------------------------\n"
318 "---------------------------------------------------------------------\n"
319 "---\n");
320
321 db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
322 if (!db_entry) {
323 ast_cli(a->fd, "No custom presence states defined\n");
324 return CLI_SUCCESS;
325 }
326 for (; db_entry; db_entry = db_entry->next) {
327 const char *object_name = strrchr(db_entry->key, '/') + 1;
328 char state_info[1301];
330 char *subtype;
331 char *message;
332 char *options;
333
334 ast_copy_string(state_info, db_entry->data, sizeof(state_info));
335 if (parse_data(state_info, &state, &subtype, &message, &options)) {
336 ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
337 continue;
338 }
339
340 if (object_name <= (const char *) 1) {
341 continue;
342 }
343 ast_cli(a->fd, "--- Name: 'CustomPresence:%s'\n"
344 " --- State: '%s'\n"
345 " --- Subtype: '%s'\n"
346 " --- Message: '%s'\n"
347 " --- Base64 Encoded: '%s'\n"
348 "---\n",
349 object_name,
351 subtype,
352 message,
353 AST_CLI_YESNO(strchr(options, 'e')));
354 }
355 ast_db_freetree(db_tree);
356 db_tree = NULL;
357
358 ast_cli(a->fd,
359 "---------------------------------------------------------------------\n"
360 "---------------------------------------------------------------------\n"
361 "\n");
362
363 return CLI_SUCCESS;
364}
365
366static char *handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
367{
368 size_t len;
369 const char *dev, *state, *full_dev;
370 enum ast_presence_state state_val;
371 char *message;
372 char *subtype;
373 char *options;
374 char *args;
375
376 switch (cmd) {
377 case CLI_INIT:
378 e->command = "presencestate change";
379 e->usage =
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"
387 "\n"
388 "Examples:\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"
392 " \n";
393 return NULL;
394 case CLI_GENERATE:
395 {
396 static const char * const cmds[] = { "NOT_SET", "UNAVAILABLE", "AVAILABLE", "AWAY",
397 "XA", "CHAT", "DND", NULL };
398
399 if (a->pos == e->args + 1) {
400 return ast_cli_complete(a->word, cmds, a->n);
401 }
402
403 return NULL;
404 }
405 }
406
407 if (a->argc != e->args + 2) {
408 return CLI_SHOWUSAGE;
409 }
410
411 len = strlen("CustomPresence:");
412 full_dev = dev = a->argv[e->args];
413 state = a->argv[e->args + 1];
414
415 if (strncasecmp(dev, "CustomPresence:", len)) {
416 ast_cli(a->fd, "The presencestate command can only be used to set 'CustomPresence:' presence state!\n");
417 return CLI_FAILURE;
418 }
419
420 dev += len;
421 if (ast_strlen_zero(dev)) {
422 return CLI_SHOWUSAGE;
423 }
424
426 if (parse_data(args, &state_val, &subtype, &message, &options)) {
427 return CLI_SHOWUSAGE;
428 }
429
430 if (state_val == AST_PRESENCE_NOT_SET) {
431 return CLI_SHOWUSAGE;
432 }
433
434 ast_cli(a->fd, "Changing %s to %s\n", dev, args);
435
437
438 ast_presence_state_changed_literal(state_val, subtype, message, full_dev);
439
440 return CLI_SUCCESS;
441}
442
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"),
446};
447
448#ifdef TEST_FRAMEWORK
449
450struct test_string {
451 char *parse_string;
452 struct {
453 int value;
454 const char *subtype;
455 const char *message;
456 const char *options;
457 } outputs;
458};
459
460AST_TEST_DEFINE(test_valid_parse_data)
461{
462 int i;
464 char *subtype;
465 char *message;
466 char *options;
468
469 struct test_string tests [] = {
470 { "away",
472 "",
473 "",
474 ""
475 }
476 },
477 { "not_set",
479 "",
480 "",
481 ""
482 }
483 },
484 { "unavailable",
486 "",
487 "",
488 ""
489 }
490 },
491 { "available",
493 "",
494 "",
495 ""
496 }
497 },
498 { "xa",
500 "",
501 "",
502 ""
503 }
504 },
505 { "chat",
507 "",
508 "",
509 ""
510 }
511 },
512 { "dnd",
514 "",
515 "",
516 ""
517 }
518 },
519 { "away,down the hall",
521 "down the hall",
522 "",
523 ""
524 }
525 },
526 { "away,down the hall,Quarterly financial meeting",
528 "down the hall",
529 "Quarterly financial meeting",
530 ""
531 }
532 },
533 { "away,,Quarterly financial meeting",
535 "",
536 "Quarterly financial meeting",
537 ""
538 }
539 },
540 { "away,,,e",
542 "",
543 "",
544 "e",
545 }
546 },
547 { "away,down the hall,,e",
549 "down the hall",
550 "",
551 "e"
552 }
553 },
554 { "away,down the hall,Quarterly financial meeting,e",
556 "down the hall",
557 "Quarterly financial meeting",
558 "e"
559 }
560 },
561 { "away,,Quarterly financial meeting,e",
563 "",
564 "Quarterly financial meeting",
565 "e"
566 }
567 }
568 };
569
570 switch (cmd) {
571 case TEST_INIT:
572 info->name = "parse_valid_presence_data";
573 info->category = "/funcs/func_presence/";
574 info->summary = "PRESENCESTATE parsing test";
575 info->description =
576 "Ensure that parsing function accepts proper values, and gives proper outputs";
577 return AST_TEST_NOT_RUN;
578 case TEST_EXECUTE:
579 break;
580 }
581
582 for (i = 0; i < ARRAY_LEN(tests); ++i) {
583 int parse_result;
584 char *parse_string = ast_strdup(tests[i].parse_string);
585 if (!parse_string) {
586 res = AST_TEST_FAIL;
587 break;
588 }
589 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
590 if (parse_result == -1) {
591 res = AST_TEST_FAIL;
592 ast_free(parse_string);
593 break;
594 }
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)) {
599 res = AST_TEST_FAIL;
600 ast_free(parse_string);
601 break;
602 }
603 ast_free(parse_string);
604 }
605
606 return res;
607}
608
609AST_TEST_DEFINE(test_invalid_parse_data)
610{
611 int i;
613 char *subtype;
614 char *message;
615 char *options;
617
618 char *tests[] = {
619 "",
620 "bored",
621 "away,,,i",
622 /* XXX The following actually is parsed correctly. Should that
623 * be changed?
624 * "away,,,,e",
625 */
626 };
627
628 switch (cmd) {
629 case TEST_INIT:
630 info->name = "parse_invalid_presence_data";
631 info->category = "/funcs/func_presence/";
632 info->summary = "PRESENCESTATE parsing test";
633 info->description =
634 "Ensure that parsing function rejects improper values";
635 return AST_TEST_NOT_RUN;
636 case TEST_EXECUTE:
637 break;
638 }
639
640 for (i = 0; i < ARRAY_LEN(tests); ++i) {
641 int parse_result;
642 char *parse_string = ast_strdup(tests[i]);
643 if (!parse_string) {
644 res = AST_TEST_FAIL;
645 break;
646 }
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]);
650 res = AST_TEST_FAIL;
651 ast_free(parse_string);
652 break;
653 }
654 ast_free(parse_string);
655 }
656
657 return res;
658}
659
660#define PRES_STATE "away"
661#define PRES_SUBTYPE "down the hall"
662#define PRES_MESSAGE "Quarterly financial meeting"
663
664struct test_cb_data {
665 struct ast_presence_state_message *presence_state;
666 /* That's right. I'm using a semaphore */
667 struct ast_sem sem;
668};
669
670static struct test_cb_data *test_cb_data_alloc(void)
671{
672 struct test_cb_data *cb_data = ast_calloc(1, sizeof(*cb_data));
673
674 if (!cb_data) {
675 return NULL;
676 }
677
678 if (ast_sem_init(&cb_data->sem, 0, 0)) {
679 ast_free(cb_data);
680 return NULL;
681 }
682
683 return cb_data;
684}
685
686static void test_cb_data_destroy(struct test_cb_data *cb_data)
687{
688 ao2_cleanup(cb_data->presence_state);
689 ast_sem_destroy(&cb_data->sem);
690 ast_free(cb_data);
691}
692
693static void test_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
694{
695 struct test_cb_data *cb_data = userdata;
697 return;
698 }
699 cb_data->presence_state = stasis_message_data(msg);
700 ao2_ref(cb_data->presence_state, +1);
701
702 ast_sem_post(&cb_data->sem);
703}
704
705static 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)
710{
711 RAII_VAR(struct test_cb_data *, cb_data, test_cb_data_alloc(), test_cb_data_destroy);
713 char pres[1301];
714
715 if (!(test_sub = stasis_subscribe(ast_presence_state_topic_all(), test_cb, cb_data))) {
716 return AST_TEST_FAIL;
717 }
718
720 snprintf(pres, sizeof(pres), "%s,%s,%s", state, subtype, message);
721 } else {
722 snprintf(pres, sizeof(pres), "%s,%s,%s,%s", state, subtype, message, options);
723 }
724
725 if (presence_write(NULL, "PRESENCESTATE", "CustomPresence:TestPresenceStateChange", pres)) {
727 return AST_TEST_FAIL;
728 }
729
730 ast_sem_wait(&cb_data->sem);
731
732 ast_copy_string(out_state, ast_presence_state2str(cb_data->presence_state->state), out_state_size);
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);
735
737 ast_db_del("CustomPresence", "TestPresenceStateChange");
738
739 return AST_TEST_PASS;
740}
741
742AST_TEST_DEFINE(test_presence_state_change)
743{
744 char out_state[32];
745 char out_subtype[32];
746 char out_message[32];
747
748 switch (cmd) {
749 case TEST_INIT:
750 info->name = "test_presence_state_change";
751 info->category = "/funcs/func_presence/";
752 info->summary = "presence state change subscription";
753 info->description =
754 "Ensure that presence state changes are communicated to subscribers";
755 return AST_TEST_NOT_RUN;
756 case TEST_EXECUTE:
757 break;
758 }
759
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;
765 }
766
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;
775 }
776
777 return AST_TEST_PASS;
778}
779
780AST_TEST_DEFINE(test_presence_state_base64_encode)
781{
782 char out_state[32];
783 char out_subtype[32];
784 char out_message[32];
785 char encoded_subtype[64];
786 char encoded_message[64];
787
788 switch (cmd) {
789 case TEST_INIT:
790 info->name = "test_presence_state_base64_encode";
791 info->category = "/funcs/func_presence/";
792 info->summary = "presence state base64 encoding";
793 info->description =
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;
797 case TEST_EXECUTE:
798 break;
799 }
800
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);
803
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;
809 }
810
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;
819 }
820
821 return AST_TEST_PASS;
822}
823
824#endif
825
826static int unload_module(void)
827{
828 int res = 0;
829
831 res |= ast_presence_state_prov_del("CustomPresence");
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);
838#endif
839 return res;
840}
841
842static int load_module(void)
843{
844 int res = 0;
845 struct ast_db_entry *db_entry, *db_tree;
846
847 /* Populate the presence state cache on the system with all of the currently
848 * known custom presence states. */
849 db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
850 for (; db_entry; db_entry = db_entry->next) {
851 const char *dev_name = strrchr(db_entry->key, '/') + 1;
853 char *message = NULL;
854 char *subtype = NULL;
855 if (dev_name <= (const char *) 1) {
856 continue;
857 }
858 state = custom_presence_callback(dev_name, &subtype, &message);
859 ast_presence_state_changed(state, subtype, message, "CustomPresence:%s", dev_name);
860 ast_free(subtype);
862 }
863 ast_db_freetree(db_tree);
864 db_tree = NULL;
865
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);
874#endif
875
876 return res;
877}
878
879AST_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,
881 .load = load_module,
882 .unload = unload_module,
Persistent data storage (akin to *doze registry)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition: main/db.c:427
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: main/db.c:610
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:677
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
static struct prometheus_metrics_provider provider
Definition: bridges.c:201
static int tmp()
Definition: bt_open.c:389
enum cc_state state
Definition: ccss.c:393
General Asterisk PBX channel definitions.
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
Definition: cli.h:71
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition: main/cli.c:1841
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static char * handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
static struct ast_cli_entry cli_funcpresencestate[]
static const char astdb_family[]
static int load_module(void)
static int unload_module(void)
static struct ast_custom_function presence_function
static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
char * strsep(char **str, const char *delims)
#define LOG_NOTICE
#define LOG_WARNING
A set of macros to manage forward-linked lists.
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_DEVSTATE_PROVIDER
Definition: module.h:329
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
def info(msg)
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Presence state management.
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_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
Add presence state provider.
enum ast_presence_state ast_presence_state_val(const char *val)
Convert presence state from text to integer value.
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
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.
ast_presence_state
Definition: presencestate.h:26
@ AST_PRESENCE_UNAVAILABLE
Definition: presencestate.h:28
@ AST_PRESENCE_INVALID
Definition: presencestate.h:39
@ AST_PRESENCE_DND
Definition: presencestate.h:33
@ AST_PRESENCE_AVAILABLE
Definition: presencestate.h:29
@ AST_PRESENCE_AWAY
Definition: presencestate.h:30
@ AST_PRESENCE_NOT_SET
Definition: presencestate.h:27
@ AST_PRESENCE_CHAT
Definition: presencestate.h:32
@ AST_PRESENCE_XA
Definition: presencestate.h:31
int ast_presence_state_prov_del(const char *label)
Remove presence state provider.
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.
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
struct stasis_forward * sub
Definition: res_corosync.c:240
#define NULL
Definition: resample.c:96
Asterisk semaphore API.
int ast_sem_init(struct ast_sem *sem, int pshared, unsigned int value)
Initialize a semaphore.
int ast_sem_destroy(struct ast_sem *sem)
Destroy a semaphore.
int ast_sem_wait(struct ast_sem *sem)
Decrements the semaphore.
int ast_sem_post(struct ast_sem *sem)
Increments the semaphore, unblocking a waiter if necessary.
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1134
#define stasis_subscribe(topic, callback, data)
Definition: stasis.h:649
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
int args
This gets set in ast_cli_register()
Definition: cli.h:185
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Definition: astdb.h:31
struct ast_db_entry * next
Definition: astdb.h:32
char * key
Definition: astdb.h:33
char data[0]
Definition: astdb.h:34
Stasis message payload representing a presence state update.
Definition: sem.h:81
int value
Definition: syslog.c:37
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
static void test_sub(struct ast_event *event)
Definition: test_cel.c:1790
const char * args
static struct test_options options
static struct test_val a
Utility functions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int ast_base64decode(unsigned char *dst, const char *src, int max)
Decode data from base64.
Definition: utils.c:296
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: utils.c:406
#define ARRAY_LEN(a)
Definition: utils.h:666