Asterisk - The Open Source Telephony Project GIT-master-4f2b068
Loading...
Searching...
No Matches
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 <since>
48 <version>11.0.0</version>
49 </since>
50 <synopsis>
51 Get or Set a presence state.
52 </synopsis>
53 <syntax>
54 <parameter name="provider" required="true">
55 <para>The provider of the presence, such as <literal>CustomPresence</literal></para>
56 </parameter>
57 <parameter name="field" required="true">
58 <para>Which field of the presence state information is wanted.</para>
59 <optionlist>
60 <option name="value">
61 <para>The current presence, such as <literal>away</literal></para>
62 </option>
63 <option name="subtype">
64 <para>Further information about the current presence</para>
65 </option>
66 <option name="message">
67 <para>A custom message that may indicate further details about the presence</para>
68 </option>
69 </optionlist>
70 </parameter>
71 <parameter name="options" required="false">
72 <optionlist>
73 <option name="e">
74 <para>On Write - Use this option when the subtype and message provided are Base64
75 encoded. The values will be stored encoded within Asterisk, but all consumers of
76 the presence state (e.g. the SIP presence event package) will receive decoded values.</para>
77 <para>On Read - Retrieves unencoded message/subtype in Base64 encoded form.</para>
78 </option>
79 </optionlist>
80 </parameter>
81 </syntax>
82 <description>
83 <para>The PRESENCE_STATE function can be used to retrieve the presence from any
84 presence provider. For example:</para>
85 <para>NoOp(SIP/mypeer has presence ${PRESENCE_STATE(SIP/mypeer,value)})</para>
86 <para>NoOp(Conference number 1234 has presence message ${PRESENCE_STATE(MeetMe:1234,message)})</para>
87 <para>The PRESENCE_STATE function can also be used to set custom presence state from
88 the dialplan. The <literal>CustomPresence:</literal> prefix must be used. For example:</para>
89 <para>Set(PRESENCE_STATE(CustomPresence:lamp1)=away,temporary,Out to lunch)</para>
90 <para>Set(PRESENCE_STATE(CustomPresence:lamp2)=dnd,,Trying to get work done)</para>
91 <para>Set(PRESENCE_STATE(CustomPresence:lamp3)=xa,T24gdmFjYXRpb24=,,e)</para>
92 <para>Set(BASE64_LAMP3_PRESENCE=${PRESENCE_STATE(CustomPresence:lamp3,subtype,e)})</para>
93 <para>You can subscribe to the status of a custom presence state using a hint in
94 the dialplan:</para>
95 <para>exten => 1234,hint,,CustomPresence:lamp1</para>
96 <para>The possible values for both uses of this function are:</para>
97 <para>not_set | unavailable | available | away | xa | chat | dnd</para>
98 </description>
99 </function>
100 ***/
101
102
103static const char astdb_family[] = "CustomPresence";
104
105static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
106{
107 int state;
108 char *message = NULL;
109 char *subtype = NULL;
110 char *parse;
111 int base64encode = 0;
114 AST_APP_ARG(field);
116 );
117
118 if (ast_strlen_zero(data)) {
119 ast_log(LOG_WARNING, "PRESENCE_STATE reading requires an argument \n");
120 return -1;
121 }
122
123 parse = ast_strdupa(data);
124
126
127 if (ast_strlen_zero(args.provider) || ast_strlen_zero(args.field)) {
128 ast_log(LOG_WARNING, "PRESENCE_STATE reading requires both presence provider and presence field arguments. \n");
129 return -1;
130 }
131
132 state = ast_presence_state_nocache(args.provider, &subtype, &message);
134 ast_log(LOG_WARNING, "PRESENCE_STATE unknown \n");
135 return -1;
136 }
137
138 if (!(ast_strlen_zero(args.options)) && (strchr(args.options, 'e'))) {
139 base64encode = 1;
140 }
141
142 if (!ast_strlen_zero(subtype) && !strcasecmp(args.field, "subtype")) {
143 if (base64encode) {
144 ast_base64encode(buf, (unsigned char *) subtype, strlen(subtype), len);
145 } else {
146 ast_copy_string(buf, subtype, len);
147 }
148 } else if (!ast_strlen_zero(message) && !strcasecmp(args.field, "message")) {
149 if (base64encode) {
150 ast_base64encode(buf, (unsigned char *) message, strlen(message), len);
151 } else {
153 }
154
155 } else if (!strcasecmp(args.field, "value")) {
157 }
158
160 ast_free(subtype);
161
162 return 0;
163}
164
165static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
166{
167 char *state_str;
168
169 /* data syntax is state,subtype,message,options */
170 *subtype = "";
171 *message = "";
172 *options = "";
173
174 state_str = strsep(&data, ",");
175 if (ast_strlen_zero(state_str)) {
176 return -1; /* state is required */
177 }
178
179 *state = ast_presence_state_val(state_str);
180
181 /* not a valid state */
182 if (*state == AST_PRESENCE_INVALID) {
183 ast_log(LOG_WARNING, "Unknown presence state value %s\n", state_str);
184 return -1;
185 }
186
187 if (!(*subtype = strsep(&data,","))) {
188 *subtype = "";
189 return 0;
190 }
191
192 if (!(*message = strsep(&data, ","))) {
193 *message = "";
194 return 0;
195 }
196
197 if (!(*options = strsep(&data, ","))) {
198 *options = "";
199 return 0;
200 }
201
202 if (!ast_strlen_zero(*options) && !(strchr(*options, 'e'))) {
203 ast_log(LOG_NOTICE, "Invalid options '%s'\n", *options);
204 return -1;
205 }
206
207 return 0;
208}
209
210static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
211{
212 size_t len = strlen("CustomPresence:");
213 char *tmp = data;
214 char *args = ast_strdupa(value);
216 char *options, *message, *subtype;
217
218 if (strncasecmp(data, "CustomPresence:", len)) {
219 ast_log(LOG_WARNING, "The PRESENCE_STATE function can only set CustomPresence: presence providers.\n");
220 return -1;
221 }
222 data += len;
223 if (ast_strlen_zero(data)) {
224 ast_log(LOG_WARNING, "PRESENCE_STATE function called with no custom device name!\n");
225 return -1;
226 }
227
228 if (parse_data(args, &state, &subtype, &message, &options)) {
229 ast_log(LOG_WARNING, "Invalid arguments to PRESENCE_STATE\n");
230 return -1;
231 }
232
234
235 if (strchr(options, 'e')) {
236 /* Let's decode the values before sending them to stasis, yes? */
237 char decoded_subtype[256] = { 0, };
238 char decoded_message[256] = { 0, };
239
240 ast_base64decode((unsigned char *) decoded_subtype, subtype, sizeof(decoded_subtype) -1);
241 ast_base64decode((unsigned char *) decoded_message, message, sizeof(decoded_message) -1);
242
243 ast_presence_state_changed_literal(state, decoded_subtype, decoded_message, tmp);
244 } else {
246 }
247
248 return 0;
249}
250
251static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
252{
253 char buf[1301] = "";
255 char *_options;
256 char *_message;
257 char *_subtype;
258
259 if (ast_db_get(astdb_family, data, buf, sizeof(buf))) {
261 }
262
263 if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
265 }
266
267 if ((strchr(_options, 'e'))) {
268 char tmp[1301];
269
270 if (ast_strlen_zero(_subtype)) {
271 *subtype = NULL;
272 } else {
273 memset(tmp, 0, sizeof(tmp));
274 ast_base64decode((unsigned char *) tmp, _subtype, sizeof(tmp) - 1);
275 *subtype = ast_strdup(tmp);
276 }
277
278 if (ast_strlen_zero(_message)) {
279 *message = NULL;
280 } else {
281 memset(tmp, 0, sizeof(tmp));
282 ast_base64decode((unsigned char *) tmp, _message, sizeof(tmp) - 1);
283 *message = ast_strdup(tmp);
284 }
285 } else {
286 *subtype = ast_strlen_zero(_subtype) ? NULL : ast_strdup(_subtype);
287 *message = ast_strlen_zero(_message) ? NULL : ast_strdup(_message);
288 }
289 return state;
290}
291
293 .name = "PRESENCE_STATE",
294 .read = presence_read,
295 .write = presence_write,
296};
297
298static char *handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
299{
300 struct ast_db_entry *db_entry, *db_tree;
301
302 switch (cmd) {
303 case CLI_INIT:
304 e->command = "presencestate list";
305 e->usage =
306 "Usage: presencestate list\n"
307 " List all custom presence states that have been set by using\n"
308 " the PRESENCE_STATE dialplan function.\n";
309 return NULL;
310 case CLI_GENERATE:
311 return NULL;
312 }
313
314 if (a->argc != e->args) {
315 return CLI_SHOWUSAGE;
316 }
317
318 ast_cli(a->fd, "\n"
319 "---------------------------------------------------------------------\n"
320 "--- Custom Presence States ------------------------------------------\n"
321 "---------------------------------------------------------------------\n"
322 "---\n");
323
324 db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
325 if (!db_entry) {
326 ast_cli(a->fd, "No custom presence states defined\n");
327 return CLI_SUCCESS;
328 }
329 for (; db_entry; db_entry = db_entry->next) {
330 const char *object_name = strrchr(db_entry->key, '/') + 1;
331 char state_info[1301];
333 char *subtype;
334 char *message;
335 char *options;
336
337 ast_copy_string(state_info, db_entry->data, sizeof(state_info));
338 if (parse_data(state_info, &state, &subtype, &message, &options)) {
339 ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
340 continue;
341 }
342
343 if (object_name <= (const char *) 1) {
344 continue;
345 }
346 ast_cli(a->fd, "--- Name: 'CustomPresence:%s'\n"
347 " --- State: '%s'\n"
348 " --- Subtype: '%s'\n"
349 " --- Message: '%s'\n"
350 " --- Base64 Encoded: '%s'\n"
351 "---\n",
352 object_name,
354 subtype,
355 message,
356 AST_CLI_YESNO(strchr(options, 'e')));
357 }
358 ast_db_freetree(db_tree);
359 db_tree = NULL;
360
361 ast_cli(a->fd,
362 "---------------------------------------------------------------------\n"
363 "---------------------------------------------------------------------\n"
364 "\n");
365
366 return CLI_SUCCESS;
367}
368
369static char *handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
370{
371 size_t len;
372 const char *dev, *state, *full_dev;
373 enum ast_presence_state state_val;
374 char *message;
375 char *subtype;
376 char *options;
377 char *args;
378
379 switch (cmd) {
380 case CLI_INIT:
381 e->command = "presencestate change";
382 e->usage =
383 "Usage: presencestate change <entity> <state>[,<subtype>[,message[,options]]]\n"
384 " Change a custom presence to a new state.\n"
385 " The possible values for the state are:\n"
386 "NOT_SET | UNAVAILABLE | AVAILABLE | AWAY | XA | CHAT | DND\n"
387 "Optionally, a custom subtype and message may be provided, along with any options\n"
388 "accepted by func_presencestate. If the subtype or message provided contain spaces,\n"
389 "be sure to enclose the data in quotation marks (\"\")\n"
390 "\n"
391 "Examples:\n"
392 " presencestate change CustomPresence:mystate1 AWAY\n"
393 " presencestate change CustomPresence:mystate1 AVAILABLE\n"
394 " presencestate change CustomPresence:mystate1 \"Away,upstairs,eating lunch\"\n"
395 " \n";
396 return NULL;
397 case CLI_GENERATE:
398 {
399 static const char * const cmds[] = { "NOT_SET", "UNAVAILABLE", "AVAILABLE", "AWAY",
400 "XA", "CHAT", "DND", NULL };
401
402 if (a->pos == e->args + 1) {
403 return ast_cli_complete(a->word, cmds, a->n);
404 }
405
406 return NULL;
407 }
408 }
409
410 if (a->argc != e->args + 2) {
411 return CLI_SHOWUSAGE;
412 }
413
414 len = strlen("CustomPresence:");
415 full_dev = dev = a->argv[e->args];
416 state = a->argv[e->args + 1];
417
418 if (strncasecmp(dev, "CustomPresence:", len)) {
419 ast_cli(a->fd, "The presencestate command can only be used to set 'CustomPresence:' presence state!\n");
420 return CLI_FAILURE;
421 }
422
423 dev += len;
424 if (ast_strlen_zero(dev)) {
425 return CLI_SHOWUSAGE;
426 }
427
429 if (parse_data(args, &state_val, &subtype, &message, &options)) {
430 return CLI_SHOWUSAGE;
431 }
432
433 ast_cli(a->fd, "Changing %s to %s\n", dev, args);
434
436
437 ast_presence_state_changed_literal(state_val, subtype, message, full_dev);
438
439 return CLI_SUCCESS;
440}
441
443 AST_CLI_DEFINE(handle_cli_presencestate_list, "List currently know custom presence states"),
444 AST_CLI_DEFINE(handle_cli_presencestate_change, "Change a custom presence state"),
445};
446
447#ifdef TEST_FRAMEWORK
448
449struct test_string {
450 char *parse_string;
451 struct {
452 int value;
453 const char *subtype;
454 const char *message;
455 const char *options;
456 } outputs;
457};
458
459AST_TEST_DEFINE(test_valid_parse_data)
460{
461 int i;
463 char *subtype;
464 char *message;
465 char *options;
467
468 struct test_string tests [] = {
469 { "away",
471 "",
472 "",
473 ""
474 }
475 },
476 { "not_set",
478 "",
479 "",
480 ""
481 }
482 },
483 { "unavailable",
485 "",
486 "",
487 ""
488 }
489 },
490 { "available",
492 "",
493 "",
494 ""
495 }
496 },
497 { "xa",
499 "",
500 "",
501 ""
502 }
503 },
504 { "chat",
506 "",
507 "",
508 ""
509 }
510 },
511 { "dnd",
513 "",
514 "",
515 ""
516 }
517 },
518 { "away,down the hall",
520 "down the hall",
521 "",
522 ""
523 }
524 },
525 { "away,down the hall,Quarterly financial meeting",
527 "down the hall",
528 "Quarterly financial meeting",
529 ""
530 }
531 },
532 { "away,,Quarterly financial meeting",
534 "",
535 "Quarterly financial meeting",
536 ""
537 }
538 },
539 { "away,,,e",
541 "",
542 "",
543 "e",
544 }
545 },
546 { "away,down the hall,,e",
548 "down the hall",
549 "",
550 "e"
551 }
552 },
553 { "away,down the hall,Quarterly financial meeting,e",
555 "down the hall",
556 "Quarterly financial meeting",
557 "e"
558 }
559 },
560 { "away,,Quarterly financial meeting,e",
562 "",
563 "Quarterly financial meeting",
564 "e"
565 }
566 }
567 };
568
569 switch (cmd) {
570 case TEST_INIT:
571 info->name = "parse_valid_presence_data";
572 info->category = "/funcs/func_presence/";
573 info->summary = "PRESENCESTATE parsing test";
574 info->description =
575 "Ensure that parsing function accepts proper values, and gives proper outputs";
576 return AST_TEST_NOT_RUN;
577 case TEST_EXECUTE:
578 break;
579 }
580
581 for (i = 0; i < ARRAY_LEN(tests); ++i) {
582 int parse_result;
583 char *parse_string = ast_strdup(tests[i].parse_string);
584 if (!parse_string) {
585 res = AST_TEST_FAIL;
586 break;
587 }
588 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
589 if (parse_result == -1) {
590 res = AST_TEST_FAIL;
591 ast_free(parse_string);
592 break;
593 }
594 if (tests[i].outputs.value != state ||
595 strcmp(tests[i].outputs.subtype, subtype) ||
596 strcmp(tests[i].outputs.message, message) ||
597 strcmp(tests[i].outputs.options, options)) {
598 res = AST_TEST_FAIL;
599 ast_free(parse_string);
600 break;
601 }
602 ast_free(parse_string);
603 }
604
605 return res;
606}
607
608AST_TEST_DEFINE(test_invalid_parse_data)
609{
610 int i;
612 char *subtype;
613 char *message;
614 char *options;
616
617 char *tests[] = {
618 "",
619 "bored",
620 "away,,,i",
621 /* XXX The following actually is parsed correctly. Should that
622 * be changed?
623 * "away,,,,e",
624 */
625 };
626
627 switch (cmd) {
628 case TEST_INIT:
629 info->name = "parse_invalid_presence_data";
630 info->category = "/funcs/func_presence/";
631 info->summary = "PRESENCESTATE parsing test";
632 info->description =
633 "Ensure that parsing function rejects improper values";
634 return AST_TEST_NOT_RUN;
635 case TEST_EXECUTE:
636 break;
637 }
638
639 for (i = 0; i < ARRAY_LEN(tests); ++i) {
640 int parse_result;
641 char *parse_string = ast_strdup(tests[i]);
642 if (!parse_string) {
643 res = AST_TEST_FAIL;
644 break;
645 }
646 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
647 if (parse_result == 0) {
648 ast_log(LOG_WARNING, "Invalid string parsing failed on %s\n", tests[i]);
649 res = AST_TEST_FAIL;
650 ast_free(parse_string);
651 break;
652 }
653 ast_free(parse_string);
654 }
655
656 return res;
657}
658
659#define PRES_STATE "away"
660#define PRES_SUBTYPE "down the hall"
661#define PRES_MESSAGE "Quarterly financial meeting"
662
663struct test_cb_data {
664 struct ast_presence_state_message *presence_state;
665 /* That's right. I'm using a semaphore */
666 struct ast_sem sem;
667};
668
669static struct test_cb_data *test_cb_data_alloc(void)
670{
671 struct test_cb_data *cb_data = ast_calloc(1, sizeof(*cb_data));
672
673 if (!cb_data) {
674 return NULL;
675 }
676
677 if (ast_sem_init(&cb_data->sem, 0, 0)) {
678 ast_free(cb_data);
679 return NULL;
680 }
681
682 return cb_data;
683}
684
685static void test_cb_data_destroy(struct test_cb_data *cb_data)
686{
687 ao2_cleanup(cb_data->presence_state);
688 ast_sem_destroy(&cb_data->sem);
689 ast_free(cb_data);
690}
691
692static void test_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
693{
694 struct test_cb_data *cb_data = userdata;
696 return;
697 }
698 cb_data->presence_state = stasis_message_data(msg);
699 ao2_ref(cb_data->presence_state, +1);
700
701 ast_sem_post(&cb_data->sem);
702}
703
704static enum ast_test_result_state presence_change_common(struct ast_test *test,
705 const char *state, const char *subtype, const char *message, const char *options,
706 char *out_state, size_t out_state_size,
707 char *out_subtype, size_t out_subtype_size,
708 char *out_message, size_t out_message_size)
709{
710 RAII_VAR(struct test_cb_data *, cb_data, test_cb_data_alloc(), test_cb_data_destroy);
712 char pres[1301];
713
714 if (!(test_sub = stasis_subscribe(ast_presence_state_topic_all(), test_cb, cb_data))) {
715 return AST_TEST_FAIL;
716 }
717
719 snprintf(pres, sizeof(pres), "%s,%s,%s", state, subtype, message);
720 } else {
721 snprintf(pres, sizeof(pres), "%s,%s,%s,%s", state, subtype, message, options);
722 }
723
724 if (presence_write(NULL, "PRESENCESTATE", "CustomPresence:TestPresenceStateChange", pres)) {
726 return AST_TEST_FAIL;
727 }
728
729 ast_sem_wait(&cb_data->sem);
730
731 ast_copy_string(out_state, ast_presence_state2str(cb_data->presence_state->state), out_state_size);
732 ast_copy_string(out_subtype, cb_data->presence_state->subtype, out_subtype_size);
733 ast_copy_string(out_message, cb_data->presence_state->message, out_message_size);
734
736 ast_db_del("CustomPresence", "TestPresenceStateChange");
737
738 return AST_TEST_PASS;
739}
740
741AST_TEST_DEFINE(test_presence_state_change)
742{
743 char out_state[32];
744 char out_subtype[32];
745 char out_message[32];
746
747 switch (cmd) {
748 case TEST_INIT:
749 info->name = "test_presence_state_change";
750 info->category = "/funcs/func_presence/";
751 info->summary = "presence state change subscription";
752 info->description =
753 "Ensure that presence state changes are communicated to subscribers";
754 return AST_TEST_NOT_RUN;
755 case TEST_EXECUTE:
756 break;
757 }
758
759 if (presence_change_common(test, PRES_STATE, PRES_SUBTYPE, PRES_MESSAGE, NULL,
760 out_state, sizeof(out_state),
761 out_subtype, sizeof(out_subtype),
762 out_message, sizeof(out_message)) == AST_TEST_FAIL) {
763 return AST_TEST_FAIL;
764 }
765
766 if (strcmp(out_state, PRES_STATE) ||
767 strcmp(out_subtype, PRES_SUBTYPE) ||
768 strcmp(out_message, PRES_MESSAGE)) {
769 ast_test_status_update(test, "Unexpected presence values, %s != %s, %s != %s, or %s != %s\n",
770 PRES_STATE, out_state,
771 PRES_SUBTYPE, out_subtype,
772 PRES_MESSAGE, out_message);
773 return AST_TEST_FAIL;
774 }
775
776 return AST_TEST_PASS;
777}
778
779AST_TEST_DEFINE(test_presence_state_base64_encode)
780{
781 char out_state[32];
782 char out_subtype[32];
783 char out_message[32];
784 char encoded_subtype[64];
785 char encoded_message[64];
786
787 switch (cmd) {
788 case TEST_INIT:
789 info->name = "test_presence_state_base64_encode";
790 info->category = "/funcs/func_presence/";
791 info->summary = "presence state base64 encoding";
792 info->description =
793 "Ensure that base64-encoded presence state is stored base64-encoded but\n"
794 "is presented to consumers decoded.";
795 return AST_TEST_NOT_RUN;
796 case TEST_EXECUTE:
797 break;
798 }
799
800 ast_base64encode(encoded_subtype, (unsigned char *) PRES_SUBTYPE, strlen(PRES_SUBTYPE), sizeof(encoded_subtype) - 1);
801 ast_base64encode(encoded_message, (unsigned char *) PRES_MESSAGE, strlen(PRES_MESSAGE), sizeof(encoded_message) - 1);
802
803 if (presence_change_common(test, PRES_STATE, encoded_subtype, encoded_message, "e",
804 out_state, sizeof(out_state),
805 out_subtype, sizeof(out_subtype),
806 out_message, sizeof(out_message)) == AST_TEST_FAIL) {
807 return AST_TEST_FAIL;
808 }
809
810 if (strcmp(out_state, PRES_STATE) ||
811 strcmp(out_subtype, PRES_SUBTYPE) ||
812 strcmp(out_message, PRES_MESSAGE)) {
813 ast_test_status_update(test, "Unexpected presence values, %s != %s, %s != %s, or %s != %s\n",
814 PRES_STATE, out_state,
815 PRES_SUBTYPE, out_subtype,
816 PRES_MESSAGE, out_message);
817 return AST_TEST_FAIL;
818 }
819
820 return AST_TEST_PASS;
821}
822
823#endif
824
825static int unload_module(void)
826{
827 int res = 0;
828
830 res |= ast_presence_state_prov_del("CustomPresence");
832#ifdef TEST_FRAMEWORK
833 AST_TEST_UNREGISTER(test_valid_parse_data);
834 AST_TEST_UNREGISTER(test_invalid_parse_data);
835 AST_TEST_UNREGISTER(test_presence_state_change);
836 AST_TEST_UNREGISTER(test_presence_state_base64_encode);
837#endif
838 return res;
839}
840
841static int load_module(void)
842{
843 int res = 0;
844 struct ast_db_entry *db_entry, *db_tree;
845
846 /* Populate the presence state cache on the system with all of the currently
847 * known custom presence states. */
848 db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
849 for (; db_entry; db_entry = db_entry->next) {
850 const char *dev_name = strrchr(db_entry->key, '/') + 1;
852 char *message = NULL;
853 char *subtype = NULL;
854 if (dev_name <= (const char *) 1) {
855 continue;
856 }
857 state = custom_presence_callback(dev_name, &subtype, &message);
858 ast_presence_state_changed(state, subtype, message, "CustomPresence:%s", dev_name);
859 ast_free(subtype);
861 }
862 ast_db_freetree(db_tree);
863 db_tree = NULL;
864
868#ifdef TEST_FRAMEWORK
869 AST_TEST_REGISTER(test_valid_parse_data);
870 AST_TEST_REGISTER(test_invalid_parse_data);
871 AST_TEST_REGISTER(test_presence_state_change);
872 AST_TEST_REGISTER(test_presence_state_base64_encode);
873#endif
874
875 return res;
876}
877
878AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gets or sets a presence state in the dialplan",
879 .support_level = AST_MODULE_SUPPORT_CORE,
880 .load = load_module,
881 .unload = unload_module,
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
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 db.c:335
int ast_db_get(const char *family, const char *key, char *value, int valuelen)
Get key value specified by family/key.
Definition db.c:421
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition db.c:472
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition db.c:635
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition db.c:695
char * strsep(char **str, const char *delims)
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
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
#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:1931
@ 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.
#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:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODPRI_DEVSTATE_PROVIDER
Definition module.h:343
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition pbx.h:1562
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
@ AST_PRESENCE_UNAVAILABLE
@ AST_PRESENCE_INVALID
@ AST_PRESENCE_DND
@ AST_PRESENCE_AVAILABLE
@ AST_PRESENCE_AWAY
@ AST_PRESENCE_NOT_SET
@ AST_PRESENCE_CHAT
@ AST_PRESENCE_XA
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.
static struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
static struct @522 args
#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.
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:1201
#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
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:981
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:706