Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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"
35 #include "asterisk/linkedlists.h"
36 #include "asterisk/presencestate.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 
100 static const char astdb_family[] = "CustomPresence";
101 
102 static 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 
122  AST_STANDARD_APP_ARGS(args, parse);
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);
130  if (state == AST_PRESENCE_INVALID) {
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 {
149  ast_copy_string(buf, message, len);
150  }
151 
152  } else if (!strcasecmp(args.field, "value")) {
153  ast_copy_string(buf, ast_presence_state2str(state), len);
154  }
155 
156  ast_free(message);
157  ast_free(subtype);
158 
159  return 0;
160 }
161 
162 static 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 
207 static 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 
230  ast_db_put(astdb_family, data, value);
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 {
242  ast_presence_state_changed_literal(state, subtype, message, tmp);
243  }
244 
245  return 0;
246 }
247 
248 static 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))) {
257  return AST_PRESENCE_NOT_SET;
258  }
259 
260  if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
261  return AST_PRESENCE_INVALID;
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);
280  *message = ast_strdup(tmp);
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 
295 static 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,
350  ast_presence_state2str(state),
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 
366 static 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 
425  args = ast_strdupa(state);
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 
436  ast_db_put(astdb_family, dev, state);
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 
450 struct test_string {
452  struct {
453  int value;
454  const char *subtype;
455  const char *message;
456  const char *options;
457  } outputs;
458 };
459 
460 AST_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",
499  { AST_PRESENCE_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 
609 AST_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 
664 struct test_cb_data {
666  /* That's right. I'm using a semaphore */
667  struct ast_sem sem;
668 };
669 
670 static 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 
686 static 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 
693 static 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 
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 {
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 
719  if (ast_strlen_zero(options)) {
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)) {
726  test_sub = stasis_unsubscribe_and_join(test_sub);
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 
736  test_sub = stasis_unsubscribe_and_join(test_sub);
737  ast_db_del("CustomPresence", "TestPresenceStateChange");
738 
739  return AST_TEST_PASS;
740 }
741 
742 AST_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 
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 
780 AST_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 
826 static int unload_module(void)
827 {
828  int res = 0;
829 
830  res |= ast_custom_function_unregister(&presence_function);
831  res |= ast_presence_state_prov_del("CustomPresence");
832  res |= ast_cli_unregister_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
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 
842 static 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;
854  char *subtype;
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);
861  ast_free(message);
862  }
863  ast_db_freetree(db_tree);
864  db_tree = NULL;
865 
866  res |= ast_custom_function_register(&presence_function);
867  res |= ast_presence_state_prov_add("CustomPresence", custom_presence_callback);
868  res |= ast_cli_register_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
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 
879 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gets or sets a presence state in the dialplan",
880  .support_level = AST_MODULE_SUPPORT_CORE,
881  .load = load_module,
882  .unload = unload_module,
883  .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
884 );
const char * name
Definition: pbx.h:119
static struct ast_custom_function presence_function
enum sip_cc_notify_state state
Definition: chan_sip.c:959
static struct test_cb_data * test_cb_data_alloc(void)
Main Channel structure associated with a channel.
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
#define PRES_MESSAGE
int ast_sem_destroy(struct ast_sem *sem)
Destroy a semaphore.
Asterisk main include file. File version handling, generic pbx functions.
struct ast_presence_state_message * presence_state
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static const char astdb_family[]
static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
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.
AST_TEST_DEFINE(test_valid_parse_data)
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
enum ast_presence_state ast_presence_state_val(const char *val)
Convert presence state from text to integer value.
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
int ast_sem_post(struct ast_sem *sem)
Increments the semaphore, unblocking a waiter if necessary.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:598
const char * subtype
static int tmp()
Definition: bt_open.c:389
Test Framework API.
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Definition: cli.h:152
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
Asks a presence state provider for the current presence state, bypassing the event cache...
Stasis message payload representing a presence state update.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
const char * args
#define NULL
Definition: resample.c:96
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Utility functions.
int args
This gets set in ast_cli_register()
Definition: cli.h:185
#define ast_strlen_zero(foo)
Definition: strings.h:52
static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
char * ast_cli_complete(const char *word, const char *const choices[], int pos)
Definition: main/cli.c:1811
Asterisk semaphore API.
int ast_base64decode(unsigned char *dst, const char *src, int max)
Decode data from base64.
Definition: main/utils.c:294
#define ast_log
Definition: astobj2.c:42
struct ast_db_entry * next
Definition: astdb.h:32
General Asterisk PBX channel definitions.
#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:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
const int fd
Definition: cli.h:159
static char * handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
ast_presence_state
Definition: presencestate.h:26
const int n
Definition: cli.h:165
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
static void test_sub(struct ast_event *event)
Definition: test_cel.c:1790
int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
Add presence state provider.
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static enum ast_test_result_state presence_change_common(struct ast_test *test, const char *state, const char *subtype, const char *message, const char *options, char *out_state, size_t out_state_size, char *out_subtype, size_t out_subtype_size, char *out_message, size_t out_message_size)
A set of macros to manage forward-linked lists.
const char * options
Core PBX routines and definitions.
int ast_sem_wait(struct ast_sem *sem)
Decrements the semaphore.
#define stasis_subscribe(topic, callback, data)
Definition: stasis.h:652
const char *const * argv
Definition: cli.h:161
Presence state management.
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: main/utils.c:404
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:531
#define CLI_SHOWUSAGE
Definition: cli.h:45
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
def info(msg)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
struct test_string::@215 outputs
#define LOG_NOTICE
Definition: logger.h:263
Definition: astdb.h:31
#define CLI_FAILURE
Definition: cli.h:46
static int load_module(void)
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
char data[0]
Definition: astdb.h:34
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
int ast_sem_init(struct ast_sem *sem, int pshared, unsigned int value)
Initialize a semaphore.
int ast_presence_state_prov_del(const char *label)
Remove presence state provider.
const char * message
const char * word
Definition: cli.h:163
struct stasis_subscription * stasis_unsubscribe_and_join(struct stasis_subscription *subscription)
Cancel a subscription, blocking until the last message is processed.
Definition: stasis.c:1136
#define PRES_STATE
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:412
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
const char * usage
Definition: cli.h:177
#define CLI_SUCCESS
Definition: cli.h:44
char * strsep(char **str, const char *delims)
Definition: sem.h:81
Definition: test.c:65
Definition: test.c:104
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:429
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
const int pos
Definition: cli.h:164
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
static int unload_module(void)
struct stasis_forward * sub
Definition: res_corosync.c:240
#define PRES_SUBTYPE
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:327
static char * handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define AST_CLI_YESNO(x)
Return Yes or No depending on the argument.
Definition: cli.h:71
static struct test_options options
int ast_presence_state_changed(enum ast_presence_state state, const char *subtype, const char *message, const char *fmt,...)
Notify the world that a presence provider state changed.
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_sem sem
Asterisk module definitions.
static void test_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
Persistant data storage (akin to *doze registry)
static struct prometheus_metrics_provider provider
Definition: bridges.c:178
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
ast_test_result_state
Definition: test.h:200
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
char * key
Definition: astdb.h:33
#define AST_APP_ARG(name)
Define an application argument.
static struct ast_cli_entry cli_funcpresencestate[]
static struct test_val a
static void test_cb_data_destroy(struct test_cb_data *cb_data)