Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 if (state_val == AST_PRESENCE_NOT_SET) {
434 return CLI_SHOWUSAGE;
435 }
436
437 ast_cli(a->fd, "Changing %s to %s\n", dev, args);
438
440
441 ast_presence_state_changed_literal(state_val, subtype, message, full_dev);
442
443 return CLI_SUCCESS;
444}
445
447 AST_CLI_DEFINE(handle_cli_presencestate_list, "List currently know custom presence states"),
448 AST_CLI_DEFINE(handle_cli_presencestate_change, "Change a custom presence state"),
449};
450
451#ifdef TEST_FRAMEWORK
452
453struct test_string {
454 char *parse_string;
455 struct {
456 int value;
457 const char *subtype;
458 const char *message;
459 const char *options;
460 } outputs;
461};
462
463AST_TEST_DEFINE(test_valid_parse_data)
464{
465 int i;
467 char *subtype;
468 char *message;
469 char *options;
471
472 struct test_string tests [] = {
473 { "away",
475 "",
476 "",
477 ""
478 }
479 },
480 { "not_set",
482 "",
483 "",
484 ""
485 }
486 },
487 { "unavailable",
489 "",
490 "",
491 ""
492 }
493 },
494 { "available",
496 "",
497 "",
498 ""
499 }
500 },
501 { "xa",
503 "",
504 "",
505 ""
506 }
507 },
508 { "chat",
510 "",
511 "",
512 ""
513 }
514 },
515 { "dnd",
517 "",
518 "",
519 ""
520 }
521 },
522 { "away,down the hall",
524 "down the hall",
525 "",
526 ""
527 }
528 },
529 { "away,down the hall,Quarterly financial meeting",
531 "down the hall",
532 "Quarterly financial meeting",
533 ""
534 }
535 },
536 { "away,,Quarterly financial meeting",
538 "",
539 "Quarterly financial meeting",
540 ""
541 }
542 },
543 { "away,,,e",
545 "",
546 "",
547 "e",
548 }
549 },
550 { "away,down the hall,,e",
552 "down the hall",
553 "",
554 "e"
555 }
556 },
557 { "away,down the hall,Quarterly financial meeting,e",
559 "down the hall",
560 "Quarterly financial meeting",
561 "e"
562 }
563 },
564 { "away,,Quarterly financial meeting,e",
566 "",
567 "Quarterly financial meeting",
568 "e"
569 }
570 }
571 };
572
573 switch (cmd) {
574 case TEST_INIT:
575 info->name = "parse_valid_presence_data";
576 info->category = "/funcs/func_presence/";
577 info->summary = "PRESENCESTATE parsing test";
578 info->description =
579 "Ensure that parsing function accepts proper values, and gives proper outputs";
580 return AST_TEST_NOT_RUN;
581 case TEST_EXECUTE:
582 break;
583 }
584
585 for (i = 0; i < ARRAY_LEN(tests); ++i) {
586 int parse_result;
587 char *parse_string = ast_strdup(tests[i].parse_string);
588 if (!parse_string) {
589 res = AST_TEST_FAIL;
590 break;
591 }
592 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
593 if (parse_result == -1) {
594 res = AST_TEST_FAIL;
595 ast_free(parse_string);
596 break;
597 }
598 if (tests[i].outputs.value != state ||
599 strcmp(tests[i].outputs.subtype, subtype) ||
600 strcmp(tests[i].outputs.message, message) ||
601 strcmp(tests[i].outputs.options, options)) {
602 res = AST_TEST_FAIL;
603 ast_free(parse_string);
604 break;
605 }
606 ast_free(parse_string);
607 }
608
609 return res;
610}
611
612AST_TEST_DEFINE(test_invalid_parse_data)
613{
614 int i;
616 char *subtype;
617 char *message;
618 char *options;
620
621 char *tests[] = {
622 "",
623 "bored",
624 "away,,,i",
625 /* XXX The following actually is parsed correctly. Should that
626 * be changed?
627 * "away,,,,e",
628 */
629 };
630
631 switch (cmd) {
632 case TEST_INIT:
633 info->name = "parse_invalid_presence_data";
634 info->category = "/funcs/func_presence/";
635 info->summary = "PRESENCESTATE parsing test";
636 info->description =
637 "Ensure that parsing function rejects improper values";
638 return AST_TEST_NOT_RUN;
639 case TEST_EXECUTE:
640 break;
641 }
642
643 for (i = 0; i < ARRAY_LEN(tests); ++i) {
644 int parse_result;
645 char *parse_string = ast_strdup(tests[i]);
646 if (!parse_string) {
647 res = AST_TEST_FAIL;
648 break;
649 }
650 parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
651 if (parse_result == 0) {
652 ast_log(LOG_WARNING, "Invalid string parsing failed on %s\n", tests[i]);
653 res = AST_TEST_FAIL;
654 ast_free(parse_string);
655 break;
656 }
657 ast_free(parse_string);
658 }
659
660 return res;
661}
662
663#define PRES_STATE "away"
664#define PRES_SUBTYPE "down the hall"
665#define PRES_MESSAGE "Quarterly financial meeting"
666
667struct test_cb_data {
668 struct ast_presence_state_message *presence_state;
669 /* That's right. I'm using a semaphore */
670 struct ast_sem sem;
671};
672
673static struct test_cb_data *test_cb_data_alloc(void)
674{
675 struct test_cb_data *cb_data = ast_calloc(1, sizeof(*cb_data));
676
677 if (!cb_data) {
678 return NULL;
679 }
680
681 if (ast_sem_init(&cb_data->sem, 0, 0)) {
682 ast_free(cb_data);
683 return NULL;
684 }
685
686 return cb_data;
687}
688
689static void test_cb_data_destroy(struct test_cb_data *cb_data)
690{
691 ao2_cleanup(cb_data->presence_state);
692 ast_sem_destroy(&cb_data->sem);
693 ast_free(cb_data);
694}
695
696static void test_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
697{
698 struct test_cb_data *cb_data = userdata;
700 return;
701 }
702 cb_data->presence_state = stasis_message_data(msg);
703 ao2_ref(cb_data->presence_state, +1);
704
705 ast_sem_post(&cb_data->sem);
706}
707
708static enum ast_test_result_state presence_change_common(struct ast_test *test,
709 const char *state, const char *subtype, const char *message, const char *options,
710 char *out_state, size_t out_state_size,
711 char *out_subtype, size_t out_subtype_size,
712 char *out_message, size_t out_message_size)
713{
714 RAII_VAR(struct test_cb_data *, cb_data, test_cb_data_alloc(), test_cb_data_destroy);
716 char pres[1301];
717
718 if (!(test_sub = stasis_subscribe(ast_presence_state_topic_all(), test_cb, cb_data))) {
719 return AST_TEST_FAIL;
720 }
721
723 snprintf(pres, sizeof(pres), "%s,%s,%s", state, subtype, message);
724 } else {
725 snprintf(pres, sizeof(pres), "%s,%s,%s,%s", state, subtype, message, options);
726 }
727
728 if (presence_write(NULL, "PRESENCESTATE", "CustomPresence:TestPresenceStateChange", pres)) {
730 return AST_TEST_FAIL;
731 }
732
733 ast_sem_wait(&cb_data->sem);
734
735 ast_copy_string(out_state, ast_presence_state2str(cb_data->presence_state->state), out_state_size);
736 ast_copy_string(out_subtype, cb_data->presence_state->subtype, out_subtype_size);
737 ast_copy_string(out_message, cb_data->presence_state->message, out_message_size);
738
740 ast_db_del("CustomPresence", "TestPresenceStateChange");
741
742 return AST_TEST_PASS;
743}
744
745AST_TEST_DEFINE(test_presence_state_change)
746{
747 char out_state[32];
748 char out_subtype[32];
749 char out_message[32];
750
751 switch (cmd) {
752 case TEST_INIT:
753 info->name = "test_presence_state_change";
754 info->category = "/funcs/func_presence/";
755 info->summary = "presence state change subscription";
756 info->description =
757 "Ensure that presence state changes are communicated to subscribers";
758 return AST_TEST_NOT_RUN;
759 case TEST_EXECUTE:
760 break;
761 }
762
763 if (presence_change_common(test, PRES_STATE, PRES_SUBTYPE, PRES_MESSAGE, NULL,
764 out_state, sizeof(out_state),
765 out_subtype, sizeof(out_subtype),
766 out_message, sizeof(out_message)) == AST_TEST_FAIL) {
767 return AST_TEST_FAIL;
768 }
769
770 if (strcmp(out_state, PRES_STATE) ||
771 strcmp(out_subtype, PRES_SUBTYPE) ||
772 strcmp(out_message, PRES_MESSAGE)) {
773 ast_test_status_update(test, "Unexpected presence values, %s != %s, %s != %s, or %s != %s\n",
774 PRES_STATE, out_state,
775 PRES_SUBTYPE, out_subtype,
776 PRES_MESSAGE, out_message);
777 return AST_TEST_FAIL;
778 }
779
780 return AST_TEST_PASS;
781}
782
783AST_TEST_DEFINE(test_presence_state_base64_encode)
784{
785 char out_state[32];
786 char out_subtype[32];
787 char out_message[32];
788 char encoded_subtype[64];
789 char encoded_message[64];
790
791 switch (cmd) {
792 case TEST_INIT:
793 info->name = "test_presence_state_base64_encode";
794 info->category = "/funcs/func_presence/";
795 info->summary = "presence state base64 encoding";
796 info->description =
797 "Ensure that base64-encoded presence state is stored base64-encoded but\n"
798 "is presented to consumers decoded.";
799 return AST_TEST_NOT_RUN;
800 case TEST_EXECUTE:
801 break;
802 }
803
804 ast_base64encode(encoded_subtype, (unsigned char *) PRES_SUBTYPE, strlen(PRES_SUBTYPE), sizeof(encoded_subtype) - 1);
805 ast_base64encode(encoded_message, (unsigned char *) PRES_MESSAGE, strlen(PRES_MESSAGE), sizeof(encoded_message) - 1);
806
807 if (presence_change_common(test, PRES_STATE, encoded_subtype, encoded_message, "e",
808 out_state, sizeof(out_state),
809 out_subtype, sizeof(out_subtype),
810 out_message, sizeof(out_message)) == AST_TEST_FAIL) {
811 return AST_TEST_FAIL;
812 }
813
814 if (strcmp(out_state, PRES_STATE) ||
815 strcmp(out_subtype, PRES_SUBTYPE) ||
816 strcmp(out_message, PRES_MESSAGE)) {
817 ast_test_status_update(test, "Unexpected presence values, %s != %s, %s != %s, or %s != %s\n",
818 PRES_STATE, out_state,
819 PRES_SUBTYPE, out_subtype,
820 PRES_MESSAGE, out_message);
821 return AST_TEST_FAIL;
822 }
823
824 return AST_TEST_PASS;
825}
826
827#endif
828
829static int unload_module(void)
830{
831 int res = 0;
832
834 res |= ast_presence_state_prov_del("CustomPresence");
836#ifdef TEST_FRAMEWORK
837 AST_TEST_UNREGISTER(test_valid_parse_data);
838 AST_TEST_UNREGISTER(test_invalid_parse_data);
839 AST_TEST_UNREGISTER(test_presence_state_change);
840 AST_TEST_UNREGISTER(test_presence_state_base64_encode);
841#endif
842 return res;
843}
844
845static int load_module(void)
846{
847 int res = 0;
848 struct ast_db_entry *db_entry, *db_tree;
849
850 /* Populate the presence state cache on the system with all of the currently
851 * known custom presence states. */
852 db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
853 for (; db_entry; db_entry = db_entry->next) {
854 const char *dev_name = strrchr(db_entry->key, '/') + 1;
856 char *message = NULL;
857 char *subtype = NULL;
858 if (dev_name <= (const char *) 1) {
859 continue;
860 }
861 state = custom_presence_callback(dev_name, &subtype, &message);
862 ast_presence_state_changed(state, subtype, message, "CustomPresence:%s", dev_name);
863 ast_free(subtype);
865 }
866 ast_db_freetree(db_tree);
867 db_tree = NULL;
868
872#ifdef TEST_FRAMEWORK
873 AST_TEST_REGISTER(test_valid_parse_data);
874 AST_TEST_REGISTER(test_invalid_parse_data);
875 AST_TEST_REGISTER(test_presence_state_change);
876 AST_TEST_REGISTER(test_presence_state_base64_encode);
877#endif
878
879 return res;
880}
881
882AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gets or sets a presence state in the dialplan",
883 .support_level = AST_MODULE_SUPPORT_CORE,
884 .load = load_module,
885 .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: 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
enum cc_state state
Definition: ccss.c:399
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:1853
@ 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
def info(msg)
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
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:1161
#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