Refactored the rest of the message types to use the STASIS_MESSAGE_TYPE_*
[asterisk/asterisk.git] / main / presencestate.c
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 #include "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include "asterisk/_private.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/lock.h"
35 #include "asterisk/linkedlists.h"
36 #include "asterisk/presencestate.h"
37 #include "asterisk/pbx.h"
38 #include "asterisk/app.h"
39 #include "asterisk/event.h"
40
41 /*! \brief Device state strings for printing */
42 static const struct {
43         const char *string;
44         enum ast_presence_state state;
45
46 } state2string[] = {
47         { "not_set", AST_PRESENCE_NOT_SET},
48         { "unavailable", AST_PRESENCE_UNAVAILABLE },
49         { "available", AST_PRESENCE_AVAILABLE},
50         { "away", AST_PRESENCE_AWAY},
51         { "xa", AST_PRESENCE_XA},
52         { "chat", AST_PRESENCE_CHAT},
53         { "dnd", AST_PRESENCE_DND},
54 };
55
56 STASIS_MESSAGE_TYPE_DEFN(ast_presence_state_message_type);
57 struct stasis_topic *presence_state_topic_all;
58 struct stasis_caching_topic *presence_state_topic_cached;
59
60 /*! \brief  A presence state provider */
61 struct presence_state_provider {
62         char label[40];
63         ast_presence_state_prov_cb_type callback;
64         AST_RWLIST_ENTRY(presence_state_provider) list;
65 };
66
67 /*! \brief A list of providers */
68 static AST_RWLIST_HEAD_STATIC(presence_state_providers, presence_state_provider);
69
70 const char *ast_presence_state2str(enum ast_presence_state state)
71 {
72         int i;
73         for (i = 0; i < ARRAY_LEN(state2string); i++) {
74                 if (state == state2string[i].state) {
75                         return state2string[i].string;
76                 }
77         }
78         return "";
79 }
80
81 enum ast_presence_state ast_presence_state_val(const char *val)
82 {
83         int i;
84         for (i = 0; i < ARRAY_LEN(state2string); i++) {
85                 if (!strcasecmp(val, state2string[i].string)) {
86                         return state2string[i].state;
87                 }
88         }
89         return AST_PRESENCE_INVALID;
90 }
91
92 static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
93 {
94         enum ast_presence_state res = AST_PRESENCE_INVALID;
95         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
96         struct ast_presence_state_message *presence_state;
97
98         msg = stasis_cache_get(ast_presence_state_topic_cached(), ast_presence_state_message_type(), presence_provider);
99
100         if (!msg) {
101                 return res;
102         }
103
104         presence_state = stasis_message_data(msg);
105         res = presence_state->state;
106
107         *subtype = !ast_strlen_zero(presence_state->subtype) ? ast_strdup(presence_state->subtype) : NULL;
108         *message = !ast_strlen_zero(presence_state->message) ? ast_strdup(presence_state->message) : NULL;
109
110         return res;
111 }
112
113 static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
114 {
115         struct presence_state_provider *provider;
116         char *address;
117         char *label = ast_strdupa(presence_provider);
118         int res = AST_PRESENCE_INVALID;
119
120         if (check_cache) {
121                 res = presence_state_cached(presence_provider, subtype, message);
122                 if (res != AST_PRESENCE_INVALID) {
123                         return res;
124                 }
125         }
126
127         if ((address = strchr(label, ':'))) {
128                 *address = '\0';
129                 address++;
130         } else {
131                 ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", presence_provider);
132                 return res;
133         }
134
135         AST_RWLIST_RDLOCK(&presence_state_providers);
136         AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
137                 ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
138
139                 if (!strcasecmp(provider->label, label)) {
140                         res = provider->callback(address, subtype, message);
141                         break;
142                 }
143         }
144         AST_RWLIST_UNLOCK(&presence_state_providers);
145
146         if (!provider) {
147                 ast_log(LOG_WARNING, "No provider found for label %s\n", label);
148         }
149
150         return res;
151 }
152
153 enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
154 {
155         return ast_presence_state_helper(presence_provider, subtype, message, 1);
156 }
157
158 enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
159 {
160         return ast_presence_state_helper(presence_provider, subtype, message, 0);
161 }
162
163 int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
164 {
165         struct presence_state_provider *provider;
166
167         if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
168                 return -1;
169         }
170
171         provider->callback = callback;
172         ast_copy_string(provider->label, label, sizeof(provider->label));
173
174         AST_RWLIST_WRLOCK(&presence_state_providers);
175         AST_RWLIST_INSERT_HEAD(&presence_state_providers, provider, list);
176         AST_RWLIST_UNLOCK(&presence_state_providers);
177
178         return 0;
179 }
180 int ast_presence_state_prov_del(const char *label)
181 {
182         struct presence_state_provider *provider;
183         int res = -1;
184
185         AST_RWLIST_WRLOCK(&presence_state_providers);
186         AST_RWLIST_TRAVERSE_SAFE_BEGIN(&presence_state_providers, provider, list) {
187                 if (!strcasecmp(provider->label, label)) {
188                         AST_RWLIST_REMOVE_CURRENT(list);
189                         ast_free(provider);
190                         res = 0;
191                         break;
192                 }
193         }
194         AST_RWLIST_TRAVERSE_SAFE_END;
195         AST_RWLIST_UNLOCK(&presence_state_providers);
196
197         return res;
198 }
199
200 static void presence_state_dtor(void *obj)
201 {
202         struct ast_presence_state_message *presence_state = obj;
203         ast_string_field_free_memory(presence_state);
204 }
205
206 static struct ast_presence_state_message *presence_state_alloc(const char *provider,
207                 enum ast_presence_state state,
208                 const char *subtype,
209                 const char *message)
210 {
211         RAII_VAR(struct ast_presence_state_message *, presence_state, ao2_alloc(sizeof(*presence_state), presence_state_dtor), ao2_cleanup);
212
213         if (!presence_state || ast_string_field_init(presence_state, 256)) {
214                 return NULL;
215         }
216
217         presence_state->state = state;
218         ast_string_field_set(presence_state, provider, provider);
219         ast_string_field_set(presence_state, subtype, S_OR(subtype, ""));
220         ast_string_field_set(presence_state, message, S_OR(message, ""));
221
222         ao2_ref(presence_state, +1);
223         return presence_state;
224 }
225
226 static void presence_state_event(const char *provider,
227                 enum ast_presence_state state,
228                 const char *subtype,
229                 const char *message)
230 {
231         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
232         RAII_VAR(struct ast_presence_state_message *, presence_state, presence_state_alloc(provider, state, subtype, message), ao2_cleanup);
233
234         if (!presence_state) {
235                 return;
236         }
237
238         msg = stasis_message_create(ast_presence_state_message_type(), presence_state);
239         if (!msg) {
240                 return;
241         }
242
243         stasis_publish(ast_presence_state_topic_all(), msg);
244 }
245
246 static void do_presence_state_change(const char *provider)
247 {
248         char *subtype = NULL;
249         char *message = NULL;
250         enum ast_presence_state state;
251
252         state = ast_presence_state_helper(provider, &subtype, &message, 0);
253
254         if (state < 0) {
255                 return;
256         }
257
258         presence_state_event(provider, state, subtype, message);
259         ast_free(subtype);
260         ast_free(message);
261 }
262
263 int ast_presence_state_changed_literal(enum ast_presence_state state,
264                 const char *subtype,
265                 const char *message,
266                 const char *presence_provider)
267 {
268         if (state == AST_PRESENCE_NOT_SET) {
269                 do_presence_state_change(presence_provider);
270         } else {
271                 presence_state_event(presence_provider, state, subtype, message);
272         }
273
274         return 0;
275 }
276
277 int ast_presence_state_changed(enum ast_presence_state state,
278                 const char *subtype,
279                 const char *message,
280                 const char *fmt, ...)
281 {
282         char buf[AST_MAX_EXTENSION];
283         va_list ap;
284
285         va_start(ap, fmt);
286         vsnprintf(buf, sizeof(buf), fmt, ap);
287         va_end(ap);
288
289         return ast_presence_state_changed_literal(state, subtype, message, buf);
290 }
291
292 struct stasis_topic *ast_presence_state_topic_all(void)
293 {
294         return presence_state_topic_all;
295 }
296
297 struct stasis_caching_topic *ast_presence_state_topic_cached(void)
298 {
299         return presence_state_topic_cached;
300 }
301
302 static const char *presence_state_get_id(struct stasis_message *msg)
303 {
304         struct ast_presence_state_message *presence_state = stasis_message_data(msg);
305
306         if (stasis_message_type(msg) != ast_presence_state_message_type()) {
307                 return NULL;
308         }
309
310         return presence_state->provider;
311 }
312
313 static void presence_state_engine_cleanup(void)
314 {
315         ao2_cleanup(presence_state_topic_all);
316         presence_state_topic_all = NULL;
317         ao2_cleanup(presence_state_topic_cached);
318         presence_state_topic_cached = NULL;
319         STASIS_MESSAGE_TYPE_CLEANUP(ast_presence_state_message_type);
320 }
321
322 int ast_presence_state_engine_init(void)
323 {
324         if (STASIS_MESSAGE_TYPE_INIT(ast_presence_state_message_type) != 0) {
325                 return -1;
326         }
327
328         presence_state_topic_all = stasis_topic_create("ast_presence_state_topic_all");
329         if (!presence_state_topic_all) {
330                 return -1;
331         }
332
333         presence_state_topic_cached = stasis_caching_topic_create(presence_state_topic_all, presence_state_get_id);
334         if (!presence_state_topic_cached) {
335                 return -1;
336         }
337         ast_register_atexit(presence_state_engine_cleanup);
338
339         return 0;
340 }
341