func_presencestate.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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. /*! \file
  19. *
  20. * \brief Custom presence provider
  21. * \ingroup functions
  22. */
  23. /*** MODULEINFO
  24. <support_level>core</support_level>
  25. ***/
  26. #include "asterisk.h"
  27. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  28. #include "asterisk/module.h"
  29. #include "asterisk/channel.h"
  30. #include "asterisk/pbx.h"
  31. #include "asterisk/utils.h"
  32. #include "asterisk/linkedlists.h"
  33. #include "asterisk/presencestate.h"
  34. #include "asterisk/cli.h"
  35. #include "asterisk/astdb.h"
  36. #include "asterisk/app.h"
  37. #ifdef TEST_FRAMEWORK
  38. #include "asterisk/test.h"
  39. #include <semaphore.h>
  40. #endif
  41. /*** DOCUMENTATION
  42. <function name="PRESENCE_STATE" language="en_US">
  43. <synopsis>
  44. Get or Set a presence state.
  45. </synopsis>
  46. <syntax>
  47. <parameter name="provider" required="true">
  48. <para>The provider of the presence, such as <literal>CustomPresence</literal></para>
  49. </parameter>
  50. <parameter name="field" required="true">
  51. <para>Which field of the presence state information is wanted.</para>
  52. <optionlist>
  53. <option name="value">
  54. <para>The current presence, such as <literal>away</literal></para>
  55. </option>
  56. <option name="subtype">
  57. <para>Further information about the current presence</para>
  58. </option>
  59. <option name="message">
  60. <para>A custom message that may indicate further details about the presence</para>
  61. </option>
  62. </optionlist>
  63. </parameter>
  64. <parameter name="options" required="false">
  65. <optionlist>
  66. <option name="e">
  67. <para>On Write - Use this option when the subtype and message provided are Base64
  68. encoded. On Read - Retrieves message/subtype in Base64 encoded form.</para>
  69. </option>
  70. </optionlist>
  71. </parameter>
  72. </syntax>
  73. <description>
  74. <para>The PRESENCE_STATE function can be used to retrieve the presence from any
  75. presence provider. For example:</para>
  76. <para>NoOp(SIP/mypeer has presence ${PRESENCE_STATE(SIP/mypeer,value)})</para>
  77. <para>NoOp(Conference number 1234 has presence message ${PRESENCE_STATE(MeetMe:1234,message)})</para>
  78. <para>The PRESENCE_STATE function can also be used to set custom presence state from
  79. the dialplan. The <literal>CustomPresence:</literal> prefix must be used. For example:</para>
  80. <para>Set(PRESENCE_STATE(CustomPresence:lamp1)=away,temporary,Out to lunch)</para>
  81. <para>Set(PRESENCE_STATE(CustomPresence:lamp2)=dnd,,Trying to get work done)</para>
  82. <para>Set(PRESENCE_STATE(CustomPresence:lamp3)=xa,T24gdmFjYXRpb24=,,e)</para>
  83. <para>Set(BASE64_LAMP3_PRESENCE=${PRESENCE_STATE(CustomPresence:lamp3,subtype,e)})</para>
  84. <para>You can subscribe to the status of a custom presence state using a hint in
  85. the dialplan:</para>
  86. <para>exten => 1234,hint,,CustomPresence:lamp1</para>
  87. <para>The possible values for both uses of this function are:</para>
  88. <para>not_set | unavailable | available | away | xa | chat | dnd</para>
  89. </description>
  90. </function>
  91. ***/
  92. static const char astdb_family[] = "CustomPresence";
  93. static int presence_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
  94. {
  95. int state;
  96. char *message = NULL;
  97. char *subtype = NULL;
  98. char *parse;
  99. int base64encode = 0;
  100. AST_DECLARE_APP_ARGS(args,
  101. AST_APP_ARG(provider);
  102. AST_APP_ARG(field);
  103. AST_APP_ARG(options);
  104. );
  105. if (ast_strlen_zero(data)) {
  106. ast_log(LOG_WARNING, "PRESENCE_STATE reading requires an argument \n");
  107. return -1;
  108. }
  109. parse = ast_strdupa(data);
  110. AST_STANDARD_APP_ARGS(args, parse);
  111. if (ast_strlen_zero(args.provider) || ast_strlen_zero(args.field)) {
  112. ast_log(LOG_WARNING, "PRESENCE_STATE reading requires both presence provider and presence field arguments. \n");
  113. return -1;
  114. }
  115. state = ast_presence_state_nocache(args.provider, &subtype, &message);
  116. if (state == AST_PRESENCE_INVALID) {
  117. ast_log(LOG_WARNING, "PRESENCE_STATE unknown \n");
  118. return -1;
  119. }
  120. if (!(ast_strlen_zero(args.options)) && (strchr(args.options, 'e'))) {
  121. base64encode = 1;
  122. }
  123. if (!ast_strlen_zero(subtype) && !strcasecmp(args.field, "subtype")) {
  124. if (base64encode) {
  125. ast_base64encode(buf, (unsigned char *) subtype, strlen(subtype), len);
  126. } else {
  127. ast_copy_string(buf, subtype, len);
  128. }
  129. } else if (!ast_strlen_zero(message) && !strcasecmp(args.field, "message")) {
  130. if (base64encode) {
  131. ast_base64encode(buf, (unsigned char *) message, strlen(message), len);
  132. } else {
  133. ast_copy_string(buf, message, len);
  134. }
  135. } else if (!strcasecmp(args.field, "value")) {
  136. ast_copy_string(buf, ast_presence_state2str(state), len);
  137. }
  138. ast_free(message);
  139. ast_free(subtype);
  140. return 0;
  141. }
  142. static int parse_data(char *data, enum ast_presence_state *state, char **subtype, char **message, char **options)
  143. {
  144. char *state_str;
  145. /* data syntax is state,subtype,message,options */
  146. *subtype = "";
  147. *message = "";
  148. *options = "";
  149. state_str = strsep(&data, ",");
  150. if (ast_strlen_zero(state_str)) {
  151. return -1; /* state is required */
  152. }
  153. *state = ast_presence_state_val(state_str);
  154. /* not a valid state */
  155. if (*state == AST_PRESENCE_INVALID) {
  156. ast_log(LOG_WARNING, "Unknown presence state value %s\n", state_str);
  157. return -1;
  158. }
  159. if (!(*subtype = strsep(&data,","))) {
  160. *subtype = "";
  161. return 0;
  162. }
  163. if (!(*message = strsep(&data, ","))) {
  164. *message = "";
  165. return 0;
  166. }
  167. if (!(*options = strsep(&data, ","))) {
  168. *options = "";
  169. return 0;
  170. }
  171. if (!ast_strlen_zero(*options) && !(strchr(*options, 'e'))) {
  172. ast_log(LOG_NOTICE, "Invalid options '%s'\n", *options);
  173. return -1;
  174. }
  175. return 0;
  176. }
  177. static int presence_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
  178. {
  179. size_t len = strlen("CustomPresence:");
  180. char *tmp = data;
  181. char *args = ast_strdupa(value);
  182. enum ast_presence_state state;
  183. char *options, *message, *subtype;
  184. if (strncasecmp(data, "CustomPresence:", len)) {
  185. ast_log(LOG_WARNING, "The PRESENCE_STATE function can only set CustomPresence: presence providers.\n");
  186. return -1;
  187. }
  188. data += len;
  189. if (ast_strlen_zero(data)) {
  190. ast_log(LOG_WARNING, "PRESENCE_STATE function called with no custom device name!\n");
  191. return -1;
  192. }
  193. if (parse_data(args, &state, &subtype, &message, &options)) {
  194. ast_log(LOG_WARNING, "Invalid arguments to PRESENCE_STATE\n");
  195. return -1;
  196. }
  197. ast_db_put(astdb_family, data, value);
  198. ast_presence_state_changed_literal(state, subtype, message, tmp);
  199. return 0;
  200. }
  201. static enum ast_presence_state custom_presence_callback(const char *data, char **subtype, char **message)
  202. {
  203. char buf[1301] = "";
  204. enum ast_presence_state state;
  205. char *_options;
  206. char *_message;
  207. char *_subtype;
  208. ast_db_get(astdb_family, data, buf, sizeof(buf));
  209. if (parse_data(buf, &state, &_subtype, &_message, &_options)) {
  210. return AST_PRESENCE_INVALID;
  211. }
  212. if ((strchr(_options, 'e'))) {
  213. char tmp[1301];
  214. if (ast_strlen_zero(_subtype)) {
  215. *subtype = NULL;
  216. } else {
  217. memset(tmp, 0, sizeof(tmp));
  218. ast_base64decode((unsigned char *) tmp, _subtype, sizeof(tmp) - 1);
  219. *subtype = ast_strdup(tmp);
  220. }
  221. if (ast_strlen_zero(_message)) {
  222. *message = NULL;
  223. } else {
  224. memset(tmp, 0, sizeof(tmp));
  225. ast_base64decode((unsigned char *) tmp, _message, sizeof(tmp) - 1);
  226. *message = ast_strdup(tmp);
  227. }
  228. } else {
  229. *subtype = ast_strlen_zero(_subtype) ? NULL : ast_strdup(_subtype);
  230. *message = ast_strlen_zero(_message) ? NULL : ast_strdup(_message);
  231. }
  232. return state;
  233. }
  234. static struct ast_custom_function presence_function = {
  235. .name = "PRESENCE_STATE",
  236. .read = presence_read,
  237. .write = presence_write,
  238. };
  239. static char *handle_cli_presencestate_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  240. {
  241. struct ast_db_entry *db_entry, *db_tree;
  242. switch (cmd) {
  243. case CLI_INIT:
  244. e->command = "presencestate list";
  245. e->usage =
  246. "Usage: presencestate list\n"
  247. " List all custom presence states that have been set by using\n"
  248. " the PRESENCE_STATE dialplan function.\n";
  249. return NULL;
  250. case CLI_GENERATE:
  251. return NULL;
  252. }
  253. if (a->argc != e->args) {
  254. return CLI_SHOWUSAGE;
  255. }
  256. ast_cli(a->fd, "\n"
  257. "---------------------------------------------------------------------\n"
  258. "--- Custom Presence States ------------------------------------------\n"
  259. "---------------------------------------------------------------------\n"
  260. "---\n");
  261. db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
  262. if (!db_entry) {
  263. ast_cli(a->fd, "No custom presence states defined\n");
  264. return CLI_SUCCESS;
  265. }
  266. for (; db_entry; db_entry = db_entry->next) {
  267. const char *object_name = strrchr(db_entry->key, '/') + 1;
  268. char state_info[1301];
  269. enum ast_presence_state state;
  270. char *subtype;
  271. char *message;
  272. char *options;
  273. ast_copy_string(state_info, db_entry->data, sizeof(state_info));
  274. if (parse_data(state_info, &state, &subtype, &message, &options)) {
  275. ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
  276. continue;
  277. }
  278. if (object_name <= (const char *) 1) {
  279. continue;
  280. }
  281. ast_cli(a->fd, "--- Name: 'CustomPresence:%s'\n"
  282. " --- State: '%s'\n"
  283. " --- Subtype: '%s'\n"
  284. " --- Message: '%s'\n"
  285. " --- Base64 Encoded: '%s'\n"
  286. "---\n",
  287. object_name,
  288. ast_presence_state2str(state),
  289. subtype,
  290. message,
  291. AST_CLI_YESNO(strchr(options, 'e')));
  292. }
  293. ast_db_freetree(db_tree);
  294. db_tree = NULL;
  295. ast_cli(a->fd,
  296. "---------------------------------------------------------------------\n"
  297. "---------------------------------------------------------------------\n"
  298. "\n");
  299. return CLI_SUCCESS;
  300. }
  301. static char *handle_cli_presencestate_change(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
  302. {
  303. size_t len;
  304. const char *dev, *state, *full_dev;
  305. enum ast_presence_state state_val;
  306. char *message;
  307. char *subtype;
  308. char *options;
  309. char *args;
  310. switch (cmd) {
  311. case CLI_INIT:
  312. e->command = "presencestate change";
  313. e->usage =
  314. "Usage: presencestate change <entity> <state>[,<subtype>[,message[,options]]]\n"
  315. " Change a custom presence to a new state.\n"
  316. " The possible values for the state are:\n"
  317. "NOT_SET | UNAVAILABLE | AVAILABLE | AWAY | XA | CHAT | DND\n"
  318. "Optionally, a custom subtype and message may be provided, along with any options\n"
  319. "accepted by func_presencestate. If the subtype or message provided contain spaces,\n"
  320. "be sure to enclose the data in quotation marks (\"\")\n"
  321. "\n"
  322. "Examples:\n"
  323. " presencestate change CustomPresence:mystate1 AWAY\n"
  324. " presencestate change CustomPresence:mystate1 AVAILABLE\n"
  325. " presencestate change CustomPresence:mystate1 \"Away,upstairs,eating lunch\"\n"
  326. " \n";
  327. return NULL;
  328. case CLI_GENERATE:
  329. {
  330. static const char * const cmds[] = { "NOT_SET", "UNAVAILABLE", "AVAILABLE", "AWAY",
  331. "XA", "CHAT", "DND", NULL };
  332. if (a->pos == e->args + 1) {
  333. return ast_cli_complete(a->word, cmds, a->n);
  334. }
  335. return NULL;
  336. }
  337. }
  338. if (a->argc != e->args + 2) {
  339. return CLI_SHOWUSAGE;
  340. }
  341. len = strlen("CustomPresence:");
  342. full_dev = dev = a->argv[e->args];
  343. state = a->argv[e->args + 1];
  344. if (strncasecmp(dev, "CustomPresence:", len)) {
  345. ast_cli(a->fd, "The presencestate command can only be used to set 'CustomPresence:' presence state!\n");
  346. return CLI_FAILURE;
  347. }
  348. dev += len;
  349. if (ast_strlen_zero(dev)) {
  350. return CLI_SHOWUSAGE;
  351. }
  352. args = ast_strdupa(state);
  353. if (parse_data(args, &state_val, &subtype, &message, &options)) {
  354. return CLI_SHOWUSAGE;
  355. }
  356. if (state_val == AST_PRESENCE_NOT_SET) {
  357. return CLI_SHOWUSAGE;
  358. }
  359. ast_cli(a->fd, "Changing %s to %s\n", dev, args);
  360. ast_db_put(astdb_family, dev, state);
  361. ast_presence_state_changed_literal(state_val, subtype, message, full_dev);
  362. return CLI_SUCCESS;
  363. }
  364. static struct ast_cli_entry cli_funcpresencestate[] = {
  365. AST_CLI_DEFINE(handle_cli_presencestate_list, "List currently know custom presence states"),
  366. AST_CLI_DEFINE(handle_cli_presencestate_change, "Change a custom presence state"),
  367. };
  368. #ifdef TEST_FRAMEWORK
  369. struct test_string {
  370. char *parse_string;
  371. struct {
  372. int value;
  373. const char *subtype;
  374. const char *message;
  375. const char *options;
  376. } outputs;
  377. };
  378. AST_TEST_DEFINE(test_valid_parse_data)
  379. {
  380. int i;
  381. enum ast_presence_state state;
  382. char *subtype;
  383. char *message;
  384. char *options;
  385. enum ast_test_result_state res = AST_TEST_PASS;
  386. struct test_string tests [] = {
  387. { "away",
  388. { AST_PRESENCE_AWAY,
  389. "",
  390. "",
  391. ""
  392. }
  393. },
  394. { "not_set",
  395. { AST_PRESENCE_NOT_SET,
  396. "",
  397. "",
  398. ""
  399. }
  400. },
  401. { "unavailable",
  402. { AST_PRESENCE_UNAVAILABLE,
  403. "",
  404. "",
  405. ""
  406. }
  407. },
  408. { "available",
  409. { AST_PRESENCE_AVAILABLE,
  410. "",
  411. "",
  412. ""
  413. }
  414. },
  415. { "xa",
  416. { AST_PRESENCE_XA,
  417. "",
  418. "",
  419. ""
  420. }
  421. },
  422. { "chat",
  423. { AST_PRESENCE_CHAT,
  424. "",
  425. "",
  426. ""
  427. }
  428. },
  429. { "dnd",
  430. { AST_PRESENCE_DND,
  431. "",
  432. "",
  433. ""
  434. }
  435. },
  436. { "away,down the hall",
  437. { AST_PRESENCE_AWAY,
  438. "down the hall",
  439. "",
  440. ""
  441. }
  442. },
  443. { "away,down the hall,Quarterly financial meeting",
  444. { AST_PRESENCE_AWAY,
  445. "down the hall",
  446. "Quarterly financial meeting",
  447. ""
  448. }
  449. },
  450. { "away,,Quarterly financial meeting",
  451. { AST_PRESENCE_AWAY,
  452. "",
  453. "Quarterly financial meeting",
  454. ""
  455. }
  456. },
  457. { "away,,,e",
  458. { AST_PRESENCE_AWAY,
  459. "",
  460. "",
  461. "e",
  462. }
  463. },
  464. { "away,down the hall,,e",
  465. { AST_PRESENCE_AWAY,
  466. "down the hall",
  467. "",
  468. "e"
  469. }
  470. },
  471. { "away,down the hall,Quarterly financial meeting,e",
  472. { AST_PRESENCE_AWAY,
  473. "down the hall",
  474. "Quarterly financial meeting",
  475. "e"
  476. }
  477. },
  478. { "away,,Quarterly financial meeting,e",
  479. { AST_PRESENCE_AWAY,
  480. "",
  481. "Quarterly financial meeting",
  482. "e"
  483. }
  484. }
  485. };
  486. switch (cmd) {
  487. case TEST_INIT:
  488. info->name = "parse_valid_presence_data";
  489. info->category = "/funcs/func_presence/";
  490. info->summary = "PRESENCESTATE parsing test";
  491. info->description =
  492. "Ensure that parsing function accepts proper values, and gives proper outputs";
  493. return AST_TEST_NOT_RUN;
  494. case TEST_EXECUTE:
  495. break;
  496. }
  497. for (i = 0; i < ARRAY_LEN(tests); ++i) {
  498. int parse_result;
  499. char *parse_string = ast_strdup(tests[i].parse_string);
  500. if (!parse_string) {
  501. res = AST_TEST_FAIL;
  502. break;
  503. }
  504. parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
  505. if (parse_result == -1) {
  506. res = AST_TEST_FAIL;
  507. ast_free(parse_string);
  508. break;
  509. }
  510. if (tests[i].outputs.value != state ||
  511. strcmp(tests[i].outputs.subtype, subtype) ||
  512. strcmp(tests[i].outputs.message, message) ||
  513. strcmp(tests[i].outputs.options, options)) {
  514. res = AST_TEST_FAIL;
  515. ast_free(parse_string);
  516. break;
  517. }
  518. ast_free(parse_string);
  519. }
  520. return res;
  521. }
  522. AST_TEST_DEFINE(test_invalid_parse_data)
  523. {
  524. int i;
  525. enum ast_presence_state state;
  526. char *subtype;
  527. char *message;
  528. char *options;
  529. enum ast_test_result_state res = AST_TEST_PASS;
  530. char *tests[] = {
  531. "",
  532. "bored",
  533. "away,,,i",
  534. /* XXX The following actually is parsed correctly. Should that
  535. * be changed?
  536. * "away,,,,e",
  537. */
  538. };
  539. switch (cmd) {
  540. case TEST_INIT:
  541. info->name = "parse_invalid_presence_data";
  542. info->category = "/funcs/func_presence/";
  543. info->summary = "PRESENCESTATE parsing test";
  544. info->description =
  545. "Ensure that parsing function rejects improper values";
  546. return AST_TEST_NOT_RUN;
  547. case TEST_EXECUTE:
  548. break;
  549. }
  550. for (i = 0; i < ARRAY_LEN(tests); ++i) {
  551. int parse_result;
  552. char *parse_string = ast_strdup(tests[i]);
  553. if (!parse_string) {
  554. res = AST_TEST_FAIL;
  555. break;
  556. }
  557. parse_result = parse_data(parse_string, &state, &subtype, &message, &options);
  558. if (parse_result == 0) {
  559. ast_log(LOG_WARNING, "Invalid string parsing failed on %s\n", tests[i]);
  560. res = AST_TEST_FAIL;
  561. ast_free(parse_string);
  562. break;
  563. }
  564. ast_free(parse_string);
  565. }
  566. return res;
  567. }
  568. struct test_cb_data {
  569. struct ast_presence_state_message *presence_state;
  570. /* That's right. I'm using a semaphore */
  571. sem_t sem;
  572. };
  573. static void test_cb(void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
  574. {
  575. struct test_cb_data *cb_data = userdata;
  576. if (stasis_message_type(msg) != ast_presence_state_message_type()) {
  577. return;
  578. }
  579. cb_data->presence_state = stasis_message_data(msg);
  580. ao2_ref(cb_data->presence_state, +1);
  581. sem_post(&cb_data->sem);
  582. }
  583. /* XXX This test could probably stand to be moved since
  584. * it does not test func_presencestate but rather code in
  585. * presencestate.h and presencestate.c. However, the convenience
  586. * of presence_write() makes this a nice location for this test.
  587. */
  588. AST_TEST_DEFINE(test_presence_state_change)
  589. {
  590. struct stasis_subscription *test_sub;
  591. struct test_cb_data *cb_data;
  592. switch (cmd) {
  593. case TEST_INIT:
  594. info->name = "test_presence_state_change";
  595. info->category = "/funcs/func_presence/";
  596. info->summary = "presence state change subscription";
  597. info->description =
  598. "Ensure that presence state changes are communicated to subscribers";
  599. return AST_TEST_NOT_RUN;
  600. case TEST_EXECUTE:
  601. break;
  602. }
  603. cb_data = ast_calloc(1, sizeof(*cb_data));
  604. if (!cb_data) {
  605. return AST_TEST_FAIL;
  606. }
  607. if (!(test_sub = stasis_subscribe(ast_presence_state_topic_all(), test_cb, cb_data))) {
  608. return AST_TEST_FAIL;
  609. }
  610. if (sem_init(&cb_data->sem, 0, 0)) {
  611. return AST_TEST_FAIL;
  612. }
  613. presence_write(NULL, "PRESENCESTATE", "CustomPresence:TestPresenceStateChange", "away,down the hall,Quarterly financial meeting");
  614. sem_wait(&cb_data->sem);
  615. if (cb_data->presence_state->state != AST_PRESENCE_AWAY ||
  616. strcmp(cb_data->presence_state->provider, "CustomPresence:TestPresenceStateChange") ||
  617. strcmp(cb_data->presence_state->subtype, "down the hall") ||
  618. strcmp(cb_data->presence_state->message, "Quarterly financial meeting")) {
  619. return AST_TEST_FAIL;
  620. }
  621. test_sub = stasis_unsubscribe_and_join(test_sub);
  622. ao2_cleanup(cb_data->presence_state);
  623. ast_free((char *)cb_data);
  624. ast_db_del("CustomPresence", "TestPresenceStateChange");
  625. return AST_TEST_PASS;
  626. }
  627. #endif
  628. static int unload_module(void)
  629. {
  630. int res = 0;
  631. res |= ast_custom_function_unregister(&presence_function);
  632. res |= ast_presence_state_prov_del("CustomPresence");
  633. res |= ast_cli_unregister_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
  634. #ifdef TEST_FRAMEWORK
  635. AST_TEST_UNREGISTER(test_valid_parse_data);
  636. AST_TEST_UNREGISTER(test_invalid_parse_data);
  637. AST_TEST_UNREGISTER(test_presence_state_change);
  638. #endif
  639. return res;
  640. }
  641. static int load_module(void)
  642. {
  643. int res = 0;
  644. struct ast_db_entry *db_entry, *db_tree;
  645. /* Populate the presence state cache on the system with all of the currently
  646. * known custom presence states. */
  647. db_entry = db_tree = ast_db_gettree(astdb_family, NULL);
  648. for (; db_entry; db_entry = db_entry->next) {
  649. const char *dev_name = strrchr(db_entry->key, '/') + 1;
  650. char state_info[1301];
  651. enum ast_presence_state state;
  652. char *message;
  653. char *subtype;
  654. char *options;
  655. if (dev_name <= (const char *) 1) {
  656. continue;
  657. }
  658. ast_copy_string(state_info, db_entry->data, sizeof(state_info));
  659. if (parse_data(state_info, &state, &subtype, &message, &options)) {
  660. ast_log(LOG_WARNING, "Invalid CustomPresence entry %s encountered\n", db_entry->data);
  661. continue;
  662. }
  663. ast_presence_state_changed(state, subtype, message, "CustomPresence:%s", dev_name);
  664. }
  665. ast_db_freetree(db_tree);
  666. db_tree = NULL;
  667. res |= ast_custom_function_register(&presence_function);
  668. res |= ast_presence_state_prov_add("CustomPresence", custom_presence_callback);
  669. res |= ast_cli_register_multiple(cli_funcpresencestate, ARRAY_LEN(cli_funcpresencestate));
  670. #ifdef TEST_FRAMEWORK
  671. AST_TEST_REGISTER(test_valid_parse_data);
  672. AST_TEST_REGISTER(test_invalid_parse_data);
  673. AST_TEST_REGISTER(test_presence_state_change);
  674. #endif
  675. return res;
  676. }
  677. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gets or sets a presence state in the dialplan",
  678. .load = load_module,
  679. .unload = unload_module,
  680. .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
  681. );