Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 <synopsis>Raised when a presence state changes</synopsis>
32 <syntax>
33 <parameter name="Presentity">
34 <para>The entity whose presence state has changed</para>
35 </parameter>
36 <parameter name="Status">
37 <para>The new status of the presentity</para>
38 </parameter>
39 <parameter name="Subtype">
40 <para>The new subtype of the presentity</para>
41 </parameter>
42 <parameter name="Message">
43 <para>The new message of the presentity</para>
44 </parameter>
45 </syntax>
46 <description>
47 <para>This differs from the <literal>PresenceStatus</literal>
48 event because this event is raised for all presence state changes,
49 not only for changes that affect dialplan hints.</para>
50 </description>
51 <see-also>
52 <ref type="managerEvent">PresenceStatus</ref>
53 </see-also>
54 </managerEventInstance>
55 </managerEvent>
56***/
57
58#include "asterisk.h"
59
60#include "asterisk/_private.h"
61#include "asterisk/utils.h"
62#include "asterisk/lock.h"
65#include "asterisk/pbx.h"
66#include "asterisk/app.h"
67#include "asterisk/test.h"
68
69/*! \brief Device state strings for printing */
70static const struct {
71 const char *string;
73
74} state2string[] = {
75 { "not_set", AST_PRESENCE_NOT_SET},
76 { "unavailable", AST_PRESENCE_UNAVAILABLE },
77 { "available", AST_PRESENCE_AVAILABLE},
78 { "away", AST_PRESENCE_AWAY},
79 { "xa", AST_PRESENCE_XA},
80 { "chat", AST_PRESENCE_CHAT},
81 { "dnd", AST_PRESENCE_DND},
82};
83
85
88);
92
93/*! \brief A presence state provider */
95 char label[40];
98};
99
100/*! \brief A list of providers */
102
104{
105 int i;
106 for (i = 0; i < ARRAY_LEN(state2string); i++) {
107 if (state == state2string[i].state) {
108 return state2string[i].string;
109 }
110 }
111 return "";
112}
113
115{
116 int i;
117 for (i = 0; i < ARRAY_LEN(state2string); i++) {
118 if (!strcasecmp(val, state2string[i].string)) {
119 return state2string[i].state;
120 }
121 }
123}
124
125static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
126{
128 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
129 struct ast_presence_state_message *presence_state;
130
132
133 if (!msg) {
134 return res;
135 }
136
137 presence_state = stasis_message_data(msg);
138 res = presence_state->state;
139
140 *subtype = !ast_strlen_zero(presence_state->subtype) ? ast_strdup(presence_state->subtype) : NULL;
141 *message = !ast_strlen_zero(presence_state->message) ? ast_strdup(presence_state->message) : NULL;
142
143 return res;
144}
145
146static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
147{
148 char *labels = ast_strdupa(presence_provider);
149 char *label;
151 enum ast_presence_state state_order[] = {
156 [AST_PRESENCE_CHAT] = 4,
157 [AST_PRESENCE_AWAY] = 5,
158 [AST_PRESENCE_XA] = 6,
159 [AST_PRESENCE_DND] = 7
160 };
161
162 *subtype = NULL;
163 *message = NULL;
164
165 while ((label = strsep(&labels, "&"))) {
167 char *next_subtype = NULL;
168 char *next_message = NULL;
169
170 if (check_cache) {
171 next_state = presence_state_cached(label, &next_subtype, &next_message);
172 }
173
174 if (next_state == AST_PRESENCE_INVALID) {
176 const struct ast_channel_tech *chan_tech;
177 char *address;
178
179 if ((address = strchr(label, '/'))) {
180 *address++ = '\0';
181
182 if ((chan_tech = ast_get_channel_tech(label)) && chan_tech->presencestate) {
183 next_state = chan_tech->presencestate(address, &next_subtype, &next_message);
184 }
185 } else if ((address = strchr(label, ':'))) {
186 *address++ = '\0';
187
190 ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
191
192 if (!strcasecmp(provider->label, label)) {
193 next_state = provider->callback(address, &next_subtype, &next_message);
194 break;
195 }
196 }
198
199 if (!provider) {
200 ast_log(LOG_WARNING, "No provider found for label: %s\n", label);
201 }
202 } else {
203 ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", label);
204 }
205 }
206
207 if (state_order[next_state] > state_order[state]) {
208 state = next_state;
209
210 ast_free(*subtype);
212
213 *subtype = next_subtype;
214 *message = next_message;
215 }
216 }
217
218 return state;
219}
220
221enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
222{
223 return ast_presence_state_helper(presence_provider, subtype, message, 1);
224}
225
226enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
227{
228 return ast_presence_state_helper(presence_provider, subtype, message, 0);
229}
230
232{
234
235 if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
236 return -1;
237 }
238
239 provider->callback = callback;
240 ast_copy_string(provider->label, label, sizeof(provider->label));
241
245
246 return 0;
247}
249{
251 int res = -1;
252
255 if (!strcasecmp(provider->label, label)) {
258 res = 0;
259 break;
260 }
261 }
264
265 return res;
266}
267
268static void presence_state_dtor(void *obj)
269{
270 struct ast_presence_state_message *presence_state = obj;
271 ast_string_field_free_memory(presence_state);
272}
273
276 const char *subtype,
277 const char *message)
278{
279 RAII_VAR(struct ast_presence_state_message *, presence_state, ao2_alloc(sizeof(*presence_state), presence_state_dtor), ao2_cleanup);
280
281 if (!presence_state || ast_string_field_init(presence_state, 256)) {
282 return NULL;
283 }
284
285 presence_state->state = state;
286 ast_string_field_set(presence_state, provider, provider);
287 ast_string_field_set(presence_state, subtype, S_OR(subtype, ""));
288 ast_string_field_set(presence_state, message, S_OR(message, ""));
289
290 ao2_ref(presence_state, +1);
291 return presence_state;
292}
293
294static void presence_state_event(const char *provider,
296 const char *subtype,
297 const char *message)
298{
299 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
300 RAII_VAR(struct ast_presence_state_message *, presence_state, NULL, ao2_cleanup);
301
303 return;
304 }
305
306 presence_state = presence_state_alloc(provider, state, subtype, message);
307 if (!presence_state) {
308 return;
309 }
310
312 if (!msg) {
313 return;
314 }
315
317}
318
319static void do_presence_state_change(const char *provider)
320{
321 char *subtype = NULL;
322 char *message = NULL;
324
326
328 return;
329 }
330
332 ast_free(subtype);
334}
335
337 const char *subtype,
338 const char *message,
339 const char *presence_provider)
340{
342 do_presence_state_change(presence_provider);
343 } else {
344 presence_state_event(presence_provider, state, subtype, message);
345 }
346
347 return 0;
348}
349
351 const char *subtype,
352 const char *message,
353 const char *fmt, ...)
354{
356 va_list ap;
357
358 va_start(ap, fmt);
359 vsnprintf(buf, sizeof(buf), fmt, ap);
360 va_end(ap);
361
363}
364
366{
368}
369
371{
373}
374
376{
378}
379
380static const char *presence_state_get_id(struct stasis_message *msg)
381{
382 struct ast_presence_state_message *presence_state = stasis_message_data(msg);
383
385 return NULL;
386 }
387
388 return presence_state->provider;
389}
390
391#if defined(TEST_FRAMEWORK)
392
393#define TEST_CATEGORY "/main/presence/"
394
395static int presence_test_alice_state = AST_PRESENCE_UNAVAILABLE;
396static int presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
397
398static int presence_test_presencestate(const char *label, char **subtype, char **message)
399{
400 if (!strcmp(label, "Alice")) {
401 return presence_test_alice_state;
402 } else if (!strcmp(label, "Bob")) {
403 return presence_test_bob_state;
404 } else {
406 }
407}
408
409static struct ast_channel_tech presence_test_tech = {
410 .type = "PresenceTestChannel",
411 .description = "Presence test technology",
412 .presencestate = presence_test_presencestate,
413};
414
415AST_TEST_DEFINE(test_presence_chan)
416{
417 int res = AST_TEST_FAIL;
418 char provider[80];
420 char *subtype = NULL, *message = NULL;
421
422 switch (cmd) {
423 case TEST_INIT:
424 info->name = "channel_presence";
425 info->category = TEST_CATEGORY;
426 info->summary = "Channel presence state tests";
427 info->description = "Creates test channel technology and then test the presence state callback";
428 return AST_TEST_NOT_RUN;
429 case TEST_EXECUTE:
430 break;
431 }
432
433 if (ast_channel_register(&presence_test_tech)) {
434 ast_log(LOG_WARNING, "Unable to register channel type '%s'\n", presence_test_tech.type);
435 goto presence_test_cleanup;
436 }
437
438 /* Check Alice's state */
439 snprintf(provider, sizeof(provider), "%s/Alice", presence_test_tech.type);
440
441 presence_test_alice_state = AST_PRESENCE_AVAILABLE;
443
444 if (state != presence_test_alice_state) {
445 ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
446 provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
447 goto presence_test_cleanup;
448 }
449
450 /* Check Alice's and Bob's state, Alice's should win as DND > AVAILABLE */
451 snprintf(provider, sizeof(provider), "%s/Alice&%s/Bob", presence_test_tech.type, presence_test_tech.type);
452
453 presence_test_alice_state = AST_PRESENCE_DND;
454 presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
456
457 if (state != presence_test_alice_state) {
458 ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
459 provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
460 goto presence_test_cleanup;
461 }
462
463 /* Check Alice's and Bob's state, Bob's should now win as AVAILABLE < UNAVAILABLE */
464 presence_test_alice_state = AST_PRESENCE_AVAILABLE;
466
467 if (state != presence_test_bob_state) {
468 ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
469 provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_bob_state));
470 goto presence_test_cleanup;
471 }
472
473 res = AST_TEST_PASS;
474
475presence_test_cleanup:
476 ast_channel_unregister(&presence_test_tech);
477 ast_free(subtype);
479
480 return res;
481}
482#endif
483
485{
492 AST_TEST_UNREGISTER(test_presence_chan);
493}
494
496{
498
500 return -1;
501 }
502
503 presence_state_topic_all = stasis_topic_create("presence_state:all");
505 return -1;
506 }
507
510 return -1;
511 }
512
515 return -1;
516 }
519
520 AST_TEST_REGISTER(test_presence_chan);
521
522 return 0;
523}
524
526{
527 struct ast_presence_state_message *presence_state = stasis_message_data(msg);
528 struct ast_manager_event_blob *res;
529
530 char *subtype = ast_escape_c_alloc(presence_state->subtype);
531 char *message = ast_escape_c_alloc(presence_state->message);
532
533 res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
534 "Presentity: %s\r\n"
535 "Status: %s\r\n"
536 "Subtype: %s\r\n"
537 "Message: %s\r\n",
538 presence_state->provider,
539 ast_presence_state2str(presence_state->state),
540 subtype ?: "",
541 message ?: "");
542
543 ast_free(subtype);
545
546 return res;
547}
Prototypes for public functions only of internal interest,.
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.
char * strsep(char **str, const char *delims)
#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:10128
#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:91
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.
static const struct @380 state2string[]
Device state strings for printing.
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:89
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:72
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:90
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:71
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:618
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:1512
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:502
Stasis message payload representing a presence state update.
enum ast_presence_state state
A presence state provider.
Definition: presencestate.c:94
ast_presence_state_prov_cb_type callback
Definition: presencestate.c:96
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