Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
presencestate.c
Go to the documentation of this file.
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
19/*! \file
20 *
21 * \brief Presence state management
22 */
23
24/*** MODULEINFO
25 <support_level>core</support_level>
26 ***/
27
28/*** DOCUMENTATION
29 <managerEvent language="en_US" name="PresenceStateChange">
30 <managerEventInstance class="EVENT_FLAG_CALL">
31 <since>
32 <version>13.0.0</version>
33 </since>
34 <synopsis>Raised when a presence state changes</synopsis>
35 <syntax>
36 <parameter name="Presentity">
37 <para>The entity whose presence state has changed</para>
38 </parameter>
39 <parameter name="Status">
40 <para>The new status of the presentity</para>
41 </parameter>
42 <parameter name="Subtype">
43 <para>The new subtype of the presentity</para>
44 </parameter>
45 <parameter name="Message">
46 <para>The new message of the presentity</para>
47 </parameter>
48 </syntax>
49 <description>
50 <para>This differs from the <literal>PresenceStatus</literal>
51 event because this event is raised for all presence state changes,
52 not only for changes that affect dialplan hints.</para>
53 </description>
54 <see-also>
55 <ref type="managerEvent">PresenceStatus</ref>
56 </see-also>
57 </managerEventInstance>
58 </managerEvent>
59***/
60
61#include "asterisk.h"
62
63#include "asterisk/_private.h"
64#include "asterisk/utils.h"
65#include "asterisk/lock.h"
68#include "asterisk/pbx.h"
69#include "asterisk/app.h"
70#include "asterisk/test.h"
71
72/*! \brief Device state strings for printing */
73static const struct {
74 const char *string;
76
77} state2string[] = {
78 { "not_set", AST_PRESENCE_NOT_SET},
79 { "unavailable", AST_PRESENCE_UNAVAILABLE },
80 { "available", AST_PRESENCE_AVAILABLE},
81 { "away", AST_PRESENCE_AWAY},
82 { "xa", AST_PRESENCE_XA},
83 { "chat", AST_PRESENCE_CHAT},
84 { "dnd", AST_PRESENCE_DND},
85};
86
88
91);
95
96/*! \brief A presence state provider */
98 char label[40];
101};
102
103/*! \brief A list of providers */
105
107{
108 int i;
109 for (i = 0; i < ARRAY_LEN(state2string); i++) {
110 if (state == state2string[i].state) {
111 return state2string[i].string;
112 }
113 }
114 return "";
115}
116
118{
119 int i;
120 for (i = 0; i < ARRAY_LEN(state2string); i++) {
121 if (!strcasecmp(val, state2string[i].string)) {
122 return state2string[i].state;
123 }
124 }
126}
127
128static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
129{
131 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
132 struct ast_presence_state_message *presence_state;
133
135
136 if (!msg) {
137 return res;
138 }
139
140 presence_state = stasis_message_data(msg);
141 res = presence_state->state;
142
143 *subtype = !ast_strlen_zero(presence_state->subtype) ? ast_strdup(presence_state->subtype) : NULL;
144 *message = !ast_strlen_zero(presence_state->message) ? ast_strdup(presence_state->message) : NULL;
145
146 return res;
147}
148
149static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
150{
151 char *labels = ast_strdupa(presence_provider);
152 char *label;
154 enum ast_presence_state state_order[] = {
159 [AST_PRESENCE_CHAT] = 4,
160 [AST_PRESENCE_AWAY] = 5,
161 [AST_PRESENCE_XA] = 6,
162 [AST_PRESENCE_DND] = 7
163 };
164
165 *subtype = NULL;
166 *message = NULL;
167
168 while ((label = strsep(&labels, "&"))) {
170 char *next_subtype = NULL;
171 char *next_message = NULL;
172
173 if (check_cache) {
174 next_state = presence_state_cached(label, &next_subtype, &next_message);
175 }
176
177 if (next_state == AST_PRESENCE_INVALID) {
179 const struct ast_channel_tech *chan_tech;
180 char *address;
181
182 if ((address = strchr(label, '/'))) {
183 *address++ = '\0';
184
185 if ((chan_tech = ast_get_channel_tech(label)) && chan_tech->presencestate) {
186 next_state = chan_tech->presencestate(address, &next_subtype, &next_message);
187 }
188 } else if ((address = strchr(label, ':'))) {
189 *address++ = '\0';
190
193 ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
194
195 if (!strcasecmp(provider->label, label)) {
196 next_state = provider->callback(address, &next_subtype, &next_message);
197 break;
198 }
199 }
201
202 if (!provider) {
203 ast_log(LOG_WARNING, "No provider found for label: %s\n", label);
204 }
205 } else {
206 ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", label);
207 }
208 }
209
210 if (state_order[next_state] > state_order[state]) {
211 state = next_state;
212
213 ast_free(*subtype);
215
216 *subtype = next_subtype;
217 *message = next_message;
218 }
219 }
220
221 return state;
222}
223
224enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
225{
226 return ast_presence_state_helper(presence_provider, subtype, message, 1);
227}
228
229enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
230{
231 return ast_presence_state_helper(presence_provider, subtype, message, 0);
232}
233
235{
237
238 if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
239 return -1;
240 }
241
242 provider->callback = callback;
243 ast_copy_string(provider->label, label, sizeof(provider->label));
244
248
249 return 0;
250}
252{
254 int res = -1;
255
258 if (!strcasecmp(provider->label, label)) {
261 res = 0;
262 break;
263 }
264 }
267
268 return res;
269}
270
271static void presence_state_dtor(void *obj)
272{
273 struct ast_presence_state_message *presence_state = obj;
274 ast_string_field_free_memory(presence_state);
275}
276
279 const char *subtype,
280 const char *message)
281{
282 RAII_VAR(struct ast_presence_state_message *, presence_state, ao2_alloc(sizeof(*presence_state), presence_state_dtor), ao2_cleanup);
283
284 if (!presence_state || ast_string_field_init(presence_state, 256)) {
285 return NULL;
286 }
287
288 presence_state->state = state;
289 ast_string_field_set(presence_state, provider, provider);
290 ast_string_field_set(presence_state, subtype, S_OR(subtype, ""));
291 ast_string_field_set(presence_state, message, S_OR(message, ""));
292
293 ao2_ref(presence_state, +1);
294 return presence_state;
295}
296
297static void presence_state_event(const char *provider,
299 const char *subtype,
300 const char *message)
301{
302 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
303 RAII_VAR(struct ast_presence_state_message *, presence_state, NULL, ao2_cleanup);
304
306 return;
307 }
308
309 presence_state = presence_state_alloc(provider, state, subtype, message);
310 if (!presence_state) {
311 return;
312 }
313
315 if (!msg) {
316 return;
317 }
318
320}
321
322static void do_presence_state_change(const char *provider)
323{
324 char *subtype = NULL;
325 char *message = NULL;
327
329
331 return;
332 }
333
335 ast_free(subtype);
337}
338
340 const char *subtype,
341 const char *message,
342 const char *presence_provider)
343{
345 do_presence_state_change(presence_provider);
346 } else {
347 presence_state_event(presence_provider, state, subtype, message);
348 }
349
350 return 0;
351}
352
354 const char *subtype,
355 const char *message,
356 const char *fmt, ...)
357{
359 va_list ap;
360
361 va_start(ap, fmt);
362 vsnprintf(buf, sizeof(buf), fmt, ap);
363 va_end(ap);
364
366}
367
369{
371}
372
374{
376}
377
379{
381}
382
383static const char *presence_state_get_id(struct stasis_message *msg)
384{
385 struct ast_presence_state_message *presence_state = stasis_message_data(msg);
386
388 return NULL;
389 }
390
391 return presence_state->provider;
392}
393
394#if defined(TEST_FRAMEWORK)
395
396#define TEST_CATEGORY "/main/presence/"
397
398static int presence_test_alice_state = AST_PRESENCE_UNAVAILABLE;
399static int presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
400
401static int presence_test_presencestate(const char *label, char **subtype, char **message)
402{
403 if (!strcmp(label, "Alice")) {
404 return presence_test_alice_state;
405 } else if (!strcmp(label, "Bob")) {
406 return presence_test_bob_state;
407 } else {
409 }
410}
411
412static struct ast_channel_tech presence_test_tech = {
413 .type = "PresenceTestChannel",
414 .description = "Presence test technology",
415 .presencestate = presence_test_presencestate,
416};
417
418AST_TEST_DEFINE(test_presence_chan)
419{
420 int res = AST_TEST_FAIL;
421 char provider[80];
423 char *subtype = NULL, *message = NULL;
424
425 switch (cmd) {
426 case TEST_INIT:
427 info->name = "channel_presence";
428 info->category = TEST_CATEGORY;
429 info->summary = "Channel presence state tests";
430 info->description = "Creates test channel technology and then test the presence state callback";
431 return AST_TEST_NOT_RUN;
432 case TEST_EXECUTE:
433 break;
434 }
435
436 if (ast_channel_register(&presence_test_tech)) {
437 ast_log(LOG_WARNING, "Unable to register channel type '%s'\n", presence_test_tech.type);
438 goto presence_test_cleanup;
439 }
440
441 /* Check Alice's state */
442 snprintf(provider, sizeof(provider), "%s/Alice", presence_test_tech.type);
443
444 presence_test_alice_state = AST_PRESENCE_AVAILABLE;
446
447 if (state != presence_test_alice_state) {
448 ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
449 provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
450 goto presence_test_cleanup;
451 }
452
453 /* Check Alice's and Bob's state, Alice's should win as DND > AVAILABLE */
454 snprintf(provider, sizeof(provider), "%s/Alice&%s/Bob", presence_test_tech.type, presence_test_tech.type);
455
456 presence_test_alice_state = AST_PRESENCE_DND;
457 presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
459
460 if (state != presence_test_alice_state) {
461 ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
462 provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
463 goto presence_test_cleanup;
464 }
465
466 /* Check Alice's and Bob's state, Bob's should now win as AVAILABLE < UNAVAILABLE */
467 presence_test_alice_state = AST_PRESENCE_AVAILABLE;
469
470 if (state != presence_test_bob_state) {
471 ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
472 provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_bob_state));
473 goto presence_test_cleanup;
474 }
475
476 res = AST_TEST_PASS;
477
478presence_test_cleanup:
479 ast_channel_unregister(&presence_test_tech);
480 ast_free(subtype);
482
483 return res;
484}
485#endif
486
488{
495 AST_TEST_UNREGISTER(test_presence_chan);
496}
497
499{
501
503 return -1;
504 }
505
506 presence_state_topic_all = stasis_topic_create("presence_state:all");
508 return -1;
509 }
510
513 return -1;
514 }
515
518 return -1;
519 }
522
523 AST_TEST_REGISTER(test_presence_chan);
524
525 return 0;
526}
527
529{
530 struct ast_presence_state_message *presence_state = stasis_message_data(msg);
531 struct ast_manager_event_blob *res;
532
533 char *subtype = ast_escape_c_alloc(presence_state->subtype);
534 char *message = ast_escape_c_alloc(presence_state->message);
535
536 res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
537 "Presentity: %s\r\n"
538 "Status: %s\r\n"
539 "Subtype: %s\r\n"
540 "Message: %s\r\n",
541 presence_state->provider,
542 ast_presence_state2str(presence_state->state),
543 subtype ?: "",
544 message ?: "");
545
546 ast_free(subtype);
548
549 return res;
550}
Prototypes for public functions only of internal interest,.
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
#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
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static struct prometheus_metrics_provider provider
Definition: bridges.c:201
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition: channel.c:570
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
Definition: channel.c:539
const struct ast_channel_tech * ast_get_channel_tech(const char *name)
Get a channel technology structure by name.
Definition: channel.c:592
#define AST_MAX_EXTENSION
Definition: channel.h:134
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
char * address
Definition: f2c.h:59
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_WARNING
A set of macros to manage forward-linked lists.
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:570
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:78
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:545
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:52
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:151
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized.
Definition: linkedlists.h:333
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:617
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:494
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:718
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:415
Asterisk locking-related definitions:
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Definition: manager.c:10237
#define EVENT_FLAG_CALL
Definition: manager.h:76
def info(msg)
Core PBX routines and definitions.
static void presence_state_engine_cleanup(void)
struct stasis_cache * ast_presence_state_cache(void)
Backend cache for ast_presence_state_topic_cached()
struct stasis_caching_topic * presence_state_topic_cached
Definition: presencestate.c:94
static void presence_state_dtor(void *obj)
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.
static void do_presence_state_change(const char *provider)
struct stasis_topic * presence_state_topic_all
Definition: presencestate.c:92
static const struct @383 state2string[]
Device state strings for printing.
struct stasis_topic * ast_presence_state_topic_cached(void)
Get caching presence state topic.
enum ast_presence_state ast_presence_state_val(const char *val)
Convert presence state from text to integer value.
static const char * presence_state_get_id(struct stasis_message *msg)
static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
enum ast_presence_state state
Definition: presencestate.c:75
static struct ast_manager_event_blob * presence_state_to_ami(struct stasis_message *msg)
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
int ast_presence_state_engine_init(void)
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.
struct stasis_cache * presence_state_cache
Definition: presencestate.c:93
static void presence_state_event(const char *provider, enum ast_presence_state state, const char *subtype, const char *message)
enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
Asks a presence state provider for the current presence state.
static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
static struct ast_presence_state_message * presence_state_alloc(const char *provider, enum ast_presence_state state, const char *subtype, const char *message)
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 * string
Definition: presencestate.c:74
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
STASIS_MESSAGE_TYPE_DEFN(ast_presence_state_message_type,.to_ami=presence_state_to_ami,)
Presence state management.
enum ast_presence_state(* ast_presence_state_prov_cb_type)(const char *data, char **subtype, char **message)
Presence state provider call back.
Definition: presencestate.h:43
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
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
#define NULL
Definition: resample.c:96
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
struct stasis_topic * stasis_caching_get_topic(struct stasis_caching_topic *caching_topic)
Returns the topic of cached events from a caching topics.
Definition: stasis_cache.c:85
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1515
@ STASIS_SUBSCRIPTION_FILTER_SELECTIVE
Definition: stasis.h:297
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
Definition: stasis.c:644
struct stasis_caching_topic * stasis_caching_topic_create(struct stasis_topic *original_topic, struct stasis_cache *cache)
Create a topic which monitors and caches messages from another topic.
Definition: stasis_cache.c:948
int stasis_caching_set_filter(struct stasis_caching_topic *caching_topic, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a cache.
Definition: stasis_cache.c:109
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
struct stasis_caching_topic * stasis_caching_unsubscribe_and_join(struct stasis_caching_topic *caching_topic)
Unsubscribes a caching topic from its upstream topic, blocking until all messages have been forwarded...
Definition: stasis_cache.c:146
struct stasis_cache * stasis_cache_create(snapshot_get_id id_fn)
Create a cache.
Definition: stasis_cache.c:360
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition: stasis.c:1538
struct stasis_message * stasis_cache_get(struct stasis_cache *cache, struct stasis_message_type *type, const char *id)
Retrieve an item from the cache for the ast_eid_default entity.
Definition: stasis_cache.c:686
int stasis_caching_accept_message_type(struct stasis_caching_topic *caching_topic, struct stasis_message_type *type)
Indicate to a caching topic that we are interested in a message type.
Definition: stasis_cache.c:90
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
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
char * ast_escape_c_alloc(const char *s)
Escape standard 'C' sequences in the given string.
Definition: utils.c:2140
Structure to describe a channel "technology", ie a channel driver See for examples:
Definition: channel.h:648
const char *const type
Definition: channel.h:649
int(*const presencestate)(const char *presence_provider, char **subtype, char **message)
Definition: channel.h:696
Struct containing info for an AMI event to send out.
Definition: manager.h:503
Stasis message payload representing a presence state update.
enum ast_presence_state state
A presence state provider.
Definition: presencestate.c:97
ast_presence_state_prov_cb_type callback
Definition: presencestate.c:99
A list of providers.
Definition: ast_expr2.c:325
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_UNREGISTER(cb)
Definition: test.h:128
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
#define TEST_CATEGORY
Definition: test_bridging.c:43
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
#define ARRAY_LEN(a)
Definition: utils.h:666