Fix DEBUG_THREADS when lock is acquired in __constructor__
[asterisk/asterisk.git] / main / manager_mwi.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Matt Jordan <mjordan@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 The Asterisk Management Interface - AMI (MWI event handling)
22  *
23  * \author Matt Jordan <mjordan@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/manager.h"
31 #include "asterisk/app.h"
32 #include "asterisk/channel.h"
33 #include "asterisk/stasis_message_router.h"
34 #include "asterisk/stasis.h"
35
36 struct stasis_message_router *mwi_state_router;
37
38 /*** DOCUMENTATION
39  ***/
40
41 /*! \brief The \ref stasis subscription returned by the forwarding of the MWI topic
42  * to the manager topic
43  */
44 static struct stasis_subscription *topic_forwarder;
45
46 /*! \brief Callback function used by \ref mwi_app_event_cb to weed out "Event" keys */
47 static int exclude_event_cb(const char *key)
48 {
49         if (!strcmp(key, "Event")) {
50                 return -1;
51         }
52         return 0;
53 }
54
55 /*! \brief Generic MWI event callback used for one-off events from voicemail modules */
56 static void mwi_app_event_cb(void *data, struct stasis_subscription *sub,
57                                     struct stasis_topic *topic,
58                                     struct stasis_message *message)
59 {
60         struct ast_mwi_blob *payload = stasis_message_data(message);
61         RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
62         RAII_VAR(struct ast_str *, event_buffer, NULL, ast_free);
63         struct ast_json *event_json = ast_json_object_get(payload->blob, "Event");
64
65         if (!event_json) {
66                 return;
67         }
68
69         if (payload->mwi_state && payload->mwi_state->snapshot) {
70                 channel_event_string = ast_manager_build_channel_state_string(payload->mwi_state->snapshot);
71         }
72
73         event_buffer = ast_manager_str_from_json_object(payload->blob, exclude_event_cb);
74         if (!event_buffer) {
75                 ast_log(AST_LOG_WARNING, "Failed to create payload for event %s\n", ast_json_string_get(event_json));
76                 return;
77         }
78
79         manager_event(EVENT_FLAG_CALL, ast_json_string_get(event_json),
80                         "Mailbox: %s\r\n"
81                         "%s"
82                         "%s",
83                         payload->mwi_state ? payload->mwi_state->uniqueid : "Unknown",
84                         ast_str_buffer(event_buffer),
85                         channel_event_string ? ast_str_buffer(channel_event_string) : "");
86 }
87
88 static void mwi_update_cb(void *data, struct stasis_subscription *sub,
89                                     struct stasis_topic *topic,
90                                     struct stasis_message *message)
91 {
92         struct ast_mwi_state *mwi_state;
93         RAII_VAR(struct ast_str *, channel_event_string, NULL, ast_free);
94
95         if (ast_mwi_state_type() != stasis_message_type(message)) {
96                 return;
97         }
98
99         mwi_state = stasis_message_data(message);
100         if (!mwi_state) {
101                 return;
102         }
103
104         if (mwi_state->snapshot) {
105                 channel_event_string = ast_manager_build_channel_state_string(mwi_state->snapshot);
106         }
107
108         /*** DOCUMENTATION
109                 <managerEventInstance>
110                         <synopsis>Raised when the state of messages in a voicemail mailbox
111                         has changed or when a channel has finished interacting with a
112                         mailbox.</synopsis>
113                         <syntax>
114                                 <channel_snapshot/>
115                                 <parameter name="Mailbox">
116                                         <para>The mailbox with the new message, specified as <literal>mailbox</literal>@<literal>context</literal></para>
117                                 </parameter>
118                                 <parameter name="Waiting">
119                                         <para>Whether or not the mailbox has messages waiting for it.</para>
120                                 </parameter>
121                                 <parameter name="New">
122                                         <para>The number of new messages.</para>
123                                 </parameter>
124                                 <parameter name="Old">
125                                         <para>The number of old messages.</para>
126                                 </parameter>
127                         </syntax>
128                         <description>
129                                 <note><para>The Channel related parameters are only present if a
130                                 channel was involved in the manipulation of a mailbox. If no
131                                 channel is involved, the parameters are not included with the
132                                 event.</para>
133                                 </note>
134                         </description>
135                 </managerEventInstance>
136         ***/
137         manager_event(EVENT_FLAG_CALL, "MessageWaiting",
138                         "%s"
139                         "Mailbox: %s\r\n"
140                         "Waiting: %d\r\n"
141                         "New: %d\r\n"
142                         "Old: %d\r\n",
143                         AS_OR(channel_event_string, ""),
144                         mwi_state->uniqueid,
145                         ast_app_has_voicemail(mwi_state->uniqueid, NULL),
146                         mwi_state->new_msgs,
147                         mwi_state->old_msgs);
148 }
149
150 static void manager_mwi_shutdown(void)
151 {
152         stasis_unsubscribe(topic_forwarder);
153         topic_forwarder = NULL;
154 }
155
156 int manager_mwi_init(void)
157 {
158         int ret = 0;
159         struct stasis_topic *manager_topic;
160         struct stasis_topic *mwi_topic;
161         struct stasis_message_router *message_router;
162
163         manager_topic = ast_manager_get_topic();
164         if (!manager_topic) {
165                 return -1;
166         }
167         message_router = ast_manager_get_message_router();
168         if (!message_router) {
169                 return -1;
170         }
171         mwi_topic = ast_mwi_topic_all();
172         if (!mwi_topic) {
173                 return -1;
174         }
175
176         topic_forwarder = stasis_forward_all(mwi_topic, manager_topic);
177         if (!topic_forwarder) {
178                 return -1;
179         }
180
181         ast_register_atexit(manager_mwi_shutdown);
182
183         ret |= stasis_message_router_add(message_router,
184                                          ast_mwi_state_type(),
185                                          mwi_update_cb,
186                                          NULL);
187
188         ret |= stasis_message_router_add(message_router,
189                                          ast_mwi_vm_app_type(),
190                                          mwi_app_event_cb,
191                                          NULL);
192
193         /* If somehow we failed to add any routes, just shut down the whole
194          * thing and fail it.
195          */
196         if (ret) {
197                 manager_mwi_shutdown();
198                 return -1;
199         }
200
201         return 0;
202 }