presencestate.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2011-2012, 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 Presence state management
  21. */
  22. /*** MODULEINFO
  23. <support_level>core</support_level>
  24. ***/
  25. /*** DOCUMENTATION
  26. <managerEvent language="en_US" name="PresenceStateChange">
  27. <managerEventInstance class="EVENT_FLAG_CALL">
  28. <synopsis>Raised when a presence state changes</synopsis>
  29. <syntax>
  30. <parameter name="Presentity">
  31. <para>The entity whose presence state has changed</para>
  32. </parameter>
  33. <parameter name="Status">
  34. <para>The new status of the presentity</para>
  35. </parameter>
  36. <parameter name="Subtype">
  37. <para>The new subtype of the presentity</para>
  38. </parameter>
  39. <parameter name="Message">
  40. <para>The new message of the presentity</para>
  41. </parameter>
  42. </syntax>
  43. <description>
  44. <para>This differs from the <literal>PresenceStatus</literal>
  45. event because this event is raised for all presence state changes,
  46. not only for changes that affect dialplan hints.</para>
  47. </description>
  48. <see-also>
  49. <ref type="managerEvent">PresenceStatus</ref>
  50. </see-also>
  51. </managerEventInstance>
  52. </managerEvent>
  53. ***/
  54. #include "asterisk.h"
  55. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  56. #include "asterisk/_private.h"
  57. #include "asterisk/utils.h"
  58. #include "asterisk/lock.h"
  59. #include "asterisk/linkedlists.h"
  60. #include "asterisk/presencestate.h"
  61. #include "asterisk/pbx.h"
  62. #include "asterisk/app.h"
  63. #include "asterisk/test.h"
  64. /*! \brief Device state strings for printing */
  65. static const struct {
  66. const char *string;
  67. enum ast_presence_state state;
  68. } state2string[] = {
  69. { "not_set", AST_PRESENCE_NOT_SET},
  70. { "unavailable", AST_PRESENCE_UNAVAILABLE },
  71. { "available", AST_PRESENCE_AVAILABLE},
  72. { "away", AST_PRESENCE_AWAY},
  73. { "xa", AST_PRESENCE_XA},
  74. { "chat", AST_PRESENCE_CHAT},
  75. { "dnd", AST_PRESENCE_DND},
  76. };
  77. static struct ast_manager_event_blob *presence_state_to_ami(struct stasis_message *msg);
  78. STASIS_MESSAGE_TYPE_DEFN(ast_presence_state_message_type,
  79. .to_ami = presence_state_to_ami,
  80. );
  81. struct stasis_topic *presence_state_topic_all;
  82. struct stasis_cache *presence_state_cache;
  83. struct stasis_caching_topic *presence_state_topic_cached;
  84. /*! \brief A presence state provider */
  85. struct presence_state_provider {
  86. char label[40];
  87. ast_presence_state_prov_cb_type callback;
  88. AST_RWLIST_ENTRY(presence_state_provider) list;
  89. };
  90. /*! \brief A list of providers */
  91. static AST_RWLIST_HEAD_STATIC(presence_state_providers, presence_state_provider);
  92. const char *ast_presence_state2str(enum ast_presence_state state)
  93. {
  94. int i;
  95. for (i = 0; i < ARRAY_LEN(state2string); i++) {
  96. if (state == state2string[i].state) {
  97. return state2string[i].string;
  98. }
  99. }
  100. return "";
  101. }
  102. enum ast_presence_state ast_presence_state_val(const char *val)
  103. {
  104. int i;
  105. for (i = 0; i < ARRAY_LEN(state2string); i++) {
  106. if (!strcasecmp(val, state2string[i].string)) {
  107. return state2string[i].state;
  108. }
  109. }
  110. return AST_PRESENCE_INVALID;
  111. }
  112. static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
  113. {
  114. enum ast_presence_state res = AST_PRESENCE_INVALID;
  115. RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
  116. struct ast_presence_state_message *presence_state;
  117. msg = stasis_cache_get(ast_presence_state_cache(), ast_presence_state_message_type(), presence_provider);
  118. if (!msg) {
  119. return res;
  120. }
  121. presence_state = stasis_message_data(msg);
  122. res = presence_state->state;
  123. *subtype = !ast_strlen_zero(presence_state->subtype) ? ast_strdup(presence_state->subtype) : NULL;
  124. *message = !ast_strlen_zero(presence_state->message) ? ast_strdup(presence_state->message) : NULL;
  125. return res;
  126. }
  127. static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
  128. {
  129. char *labels = ast_strdupa(presence_provider);
  130. char *label;
  131. enum ast_presence_state state = AST_PRESENCE_INVALID;
  132. enum ast_presence_state state_order[] = {
  133. [AST_PRESENCE_INVALID] = 0,
  134. [AST_PRESENCE_NOT_SET] = 1,
  135. [AST_PRESENCE_AVAILABLE] = 2,
  136. [AST_PRESENCE_UNAVAILABLE] = 3,
  137. [AST_PRESENCE_CHAT] = 4,
  138. [AST_PRESENCE_AWAY] = 5,
  139. [AST_PRESENCE_XA] = 6,
  140. [AST_PRESENCE_DND] = 7
  141. };
  142. while ((label = strsep(&labels, "&"))) {
  143. enum ast_presence_state next_state = AST_PRESENCE_INVALID;
  144. char *next_subtype = NULL;
  145. char *next_message = NULL;
  146. if (check_cache) {
  147. next_state = presence_state_cached(label, &next_subtype, &next_message);
  148. }
  149. if (next_state == AST_PRESENCE_INVALID) {
  150. struct presence_state_provider *provider;
  151. const struct ast_channel_tech *chan_tech;
  152. char *address;
  153. if ((address = strchr(label, '/'))) {
  154. *address++ = '\0';
  155. if ((chan_tech = ast_get_channel_tech(label)) && chan_tech->presencestate) {
  156. next_state = chan_tech->presencestate(address, &next_subtype, &next_message);
  157. }
  158. } else if ((address = strchr(label, ':'))) {
  159. *address++ = '\0';
  160. AST_RWLIST_RDLOCK(&presence_state_providers);
  161. AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
  162. ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
  163. if (!strcasecmp(provider->label, label)) {
  164. next_state = provider->callback(address, &next_subtype, &next_message);
  165. break;
  166. }
  167. }
  168. AST_RWLIST_UNLOCK(&presence_state_providers);
  169. if (!provider) {
  170. ast_log(LOG_WARNING, "No provider found for label: %s\n", label);
  171. }
  172. } else {
  173. ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", label);
  174. }
  175. }
  176. if (state_order[next_state] > state_order[state]) {
  177. state = next_state;
  178. ast_free(*subtype);
  179. ast_free(*message);
  180. *subtype = next_subtype;
  181. *message = next_message;
  182. }
  183. }
  184. return state;
  185. }
  186. enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
  187. {
  188. return ast_presence_state_helper(presence_provider, subtype, message, 1);
  189. }
  190. enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
  191. {
  192. return ast_presence_state_helper(presence_provider, subtype, message, 0);
  193. }
  194. int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
  195. {
  196. struct presence_state_provider *provider;
  197. if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
  198. return -1;
  199. }
  200. provider->callback = callback;
  201. ast_copy_string(provider->label, label, sizeof(provider->label));
  202. AST_RWLIST_WRLOCK(&presence_state_providers);
  203. AST_RWLIST_INSERT_HEAD(&presence_state_providers, provider, list);
  204. AST_RWLIST_UNLOCK(&presence_state_providers);
  205. return 0;
  206. }
  207. int ast_presence_state_prov_del(const char *label)
  208. {
  209. struct presence_state_provider *provider;
  210. int res = -1;
  211. AST_RWLIST_WRLOCK(&presence_state_providers);
  212. AST_RWLIST_TRAVERSE_SAFE_BEGIN(&presence_state_providers, provider, list) {
  213. if (!strcasecmp(provider->label, label)) {
  214. AST_RWLIST_REMOVE_CURRENT(list);
  215. ast_free(provider);
  216. res = 0;
  217. break;
  218. }
  219. }
  220. AST_RWLIST_TRAVERSE_SAFE_END;
  221. AST_RWLIST_UNLOCK(&presence_state_providers);
  222. return res;
  223. }
  224. static void presence_state_dtor(void *obj)
  225. {
  226. struct ast_presence_state_message *presence_state = obj;
  227. ast_string_field_free_memory(presence_state);
  228. }
  229. static struct ast_presence_state_message *presence_state_alloc(const char *provider,
  230. enum ast_presence_state state,
  231. const char *subtype,
  232. const char *message)
  233. {
  234. RAII_VAR(struct ast_presence_state_message *, presence_state, ao2_alloc(sizeof(*presence_state), presence_state_dtor), ao2_cleanup);
  235. if (!presence_state || ast_string_field_init(presence_state, 256)) {
  236. return NULL;
  237. }
  238. presence_state->state = state;
  239. ast_string_field_set(presence_state, provider, provider);
  240. ast_string_field_set(presence_state, subtype, S_OR(subtype, ""));
  241. ast_string_field_set(presence_state, message, S_OR(message, ""));
  242. ao2_ref(presence_state, +1);
  243. return presence_state;
  244. }
  245. static void presence_state_event(const char *provider,
  246. enum ast_presence_state state,
  247. const char *subtype,
  248. const char *message)
  249. {
  250. RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
  251. RAII_VAR(struct ast_presence_state_message *, presence_state, NULL, ao2_cleanup);
  252. if (!ast_presence_state_message_type()) {
  253. return;
  254. }
  255. presence_state = presence_state_alloc(provider, state, subtype, message);
  256. if (!presence_state) {
  257. return;
  258. }
  259. msg = stasis_message_create(ast_presence_state_message_type(), presence_state);
  260. if (!msg) {
  261. return;
  262. }
  263. stasis_publish(ast_presence_state_topic_all(), msg);
  264. }
  265. static void do_presence_state_change(const char *provider)
  266. {
  267. char *subtype = NULL;
  268. char *message = NULL;
  269. enum ast_presence_state state;
  270. state = ast_presence_state_helper(provider, &subtype, &message, 0);
  271. if (state == AST_PRESENCE_INVALID) {
  272. return;
  273. }
  274. presence_state_event(provider, state, subtype, message);
  275. ast_free(subtype);
  276. ast_free(message);
  277. }
  278. int ast_presence_state_changed_literal(enum ast_presence_state state,
  279. const char *subtype,
  280. const char *message,
  281. const char *presence_provider)
  282. {
  283. if (state == AST_PRESENCE_NOT_SET) {
  284. do_presence_state_change(presence_provider);
  285. } else {
  286. presence_state_event(presence_provider, state, subtype, message);
  287. }
  288. return 0;
  289. }
  290. int ast_presence_state_changed(enum ast_presence_state state,
  291. const char *subtype,
  292. const char *message,
  293. const char *fmt, ...)
  294. {
  295. char buf[AST_MAX_EXTENSION];
  296. va_list ap;
  297. va_start(ap, fmt);
  298. vsnprintf(buf, sizeof(buf), fmt, ap);
  299. va_end(ap);
  300. return ast_presence_state_changed_literal(state, subtype, message, buf);
  301. }
  302. struct stasis_topic *ast_presence_state_topic_all(void)
  303. {
  304. return presence_state_topic_all;
  305. }
  306. struct stasis_cache *ast_presence_state_cache(void)
  307. {
  308. return presence_state_cache;
  309. }
  310. struct stasis_topic *ast_presence_state_topic_cached(void)
  311. {
  312. return stasis_caching_get_topic(presence_state_topic_cached);
  313. }
  314. static const char *presence_state_get_id(struct stasis_message *msg)
  315. {
  316. struct ast_presence_state_message *presence_state = stasis_message_data(msg);
  317. if (stasis_message_type(msg) != ast_presence_state_message_type()) {
  318. return NULL;
  319. }
  320. return presence_state->provider;
  321. }
  322. #if defined(TEST_FRAMEWORK)
  323. #define TEST_CATEGORY "/main/presence"
  324. static int presence_test_alice_state = AST_PRESENCE_UNAVAILABLE;
  325. static int presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
  326. static int presence_test_presencestate(const char *label, char **subtype, char **message)
  327. {
  328. if (!strcmp(label, "Alice")) {
  329. return presence_test_alice_state;
  330. } else if (!strcmp(label, "Bob")) {
  331. return presence_test_bob_state;
  332. } else {
  333. return AST_PRESENCE_UNAVAILABLE;
  334. }
  335. }
  336. static struct ast_channel_tech presence_test_tech = {
  337. .type = "PresenceTestChannel",
  338. .description = "Presence test technology",
  339. .presencestate = presence_test_presencestate,
  340. };
  341. AST_TEST_DEFINE(test_presence_chan)
  342. {
  343. int res = AST_TEST_FAIL;
  344. char provider[80];
  345. enum ast_presence_state state;
  346. char *subtype = NULL, *message = NULL;
  347. switch (cmd) {
  348. case TEST_INIT:
  349. info->name = "channel_presence";
  350. info->category = TEST_CATEGORY;
  351. info->summary = "Channel presence state tests";
  352. info->description = "Creates test channel technology and then test the presence state callback";
  353. return AST_TEST_NOT_RUN;
  354. case TEST_EXECUTE:
  355. break;
  356. }
  357. if (ast_channel_register(&presence_test_tech)) {
  358. ast_log(LOG_WARNING, "Unable to register channel type '%s'\n", presence_test_tech.type);
  359. goto presence_test_cleanup;
  360. }
  361. /* Check Alice's state */
  362. snprintf(provider, sizeof(provider), "%s/Alice", presence_test_tech.type);
  363. presence_test_alice_state = AST_PRESENCE_AVAILABLE;
  364. state = ast_presence_state_nocache(provider, &subtype, &message);
  365. if (state != presence_test_alice_state) {
  366. ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
  367. provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
  368. goto presence_test_cleanup;
  369. }
  370. /* Check Alice's and Bob's state, Alice's should win as DND > AVAILABLE */
  371. snprintf(provider, sizeof(provider), "%s/Alice&%s/Bob", presence_test_tech.type, presence_test_tech.type);
  372. presence_test_alice_state = AST_PRESENCE_DND;
  373. presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
  374. state = ast_presence_state_nocache(provider, &subtype, &message);
  375. if (state != presence_test_alice_state) {
  376. ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
  377. provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
  378. goto presence_test_cleanup;
  379. }
  380. /* Check Alice's and Bob's state, Bob's should now win as AVAILABLE < UNAVAILABLE */
  381. presence_test_alice_state = AST_PRESENCE_AVAILABLE;
  382. state = ast_presence_state_nocache(provider, &subtype, &message);
  383. if (state != presence_test_bob_state) {
  384. ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
  385. provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_bob_state));
  386. goto presence_test_cleanup;
  387. }
  388. res = AST_TEST_PASS;
  389. presence_test_cleanup:
  390. ast_channel_unregister(&presence_test_tech);
  391. ast_free(subtype);
  392. ast_free(message);
  393. return res;
  394. }
  395. #endif
  396. static void presence_state_engine_cleanup(void)
  397. {
  398. ao2_cleanup(presence_state_topic_all);
  399. presence_state_topic_all = NULL;
  400. ao2_cleanup(presence_state_cache);
  401. presence_state_cache = NULL;
  402. presence_state_topic_cached = stasis_caching_unsubscribe_and_join(presence_state_topic_cached);
  403. STASIS_MESSAGE_TYPE_CLEANUP(ast_presence_state_message_type);
  404. AST_TEST_UNREGISTER(test_presence_chan);
  405. }
  406. int ast_presence_state_engine_init(void)
  407. {
  408. ast_register_cleanup(presence_state_engine_cleanup);
  409. if (STASIS_MESSAGE_TYPE_INIT(ast_presence_state_message_type) != 0) {
  410. return -1;
  411. }
  412. presence_state_topic_all = stasis_topic_create("ast_presence_state_topic_all");
  413. if (!presence_state_topic_all) {
  414. return -1;
  415. }
  416. presence_state_cache = stasis_cache_create(presence_state_get_id);
  417. if (!presence_state_cache) {
  418. return -1;
  419. }
  420. presence_state_topic_cached = stasis_caching_topic_create(presence_state_topic_all, presence_state_cache);
  421. if (!presence_state_topic_cached) {
  422. return -1;
  423. }
  424. AST_TEST_REGISTER(test_presence_chan);
  425. return 0;
  426. }
  427. static struct ast_manager_event_blob *presence_state_to_ami(struct stasis_message *msg)
  428. {
  429. struct ast_presence_state_message *presence_state = stasis_message_data(msg);
  430. return ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
  431. "Presentity: %s\r\n"
  432. "Status: %s\r\n"
  433. "Subtype: %s\r\n"
  434. "Message: %s\r\n",
  435. presence_state->provider,
  436. ast_presence_state2str(presence_state->state),
  437. presence_state->subtype,
  438. presence_state->message);
  439. }