ARI: Add ability to raise arbitrary User Events
[asterisk/asterisk.git] / main / parking.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@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 Parking Core
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/_private.h"
31 #include "asterisk/astobj2.h"
32 #include "asterisk/pbx.h"
33 #include "asterisk/bridge.h"
34 #include "asterisk/parking.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/_private.h"
37 #include "asterisk/module.h"
38
39 /*! \brief Message type for parked calls */
40 STASIS_MESSAGE_TYPE_DEFN(ast_parked_call_type);
41
42 /*! \brief Topic for parking lots */
43 static struct stasis_topic *parking_topic;
44
45 /*! \brief The container for the parking provider */
46 static AO2_GLOBAL_OBJ_STATIC(parking_provider);
47
48 static void parking_stasis_cleanup(void)
49 {
50         STASIS_MESSAGE_TYPE_CLEANUP(ast_parked_call_type);
51         ao2_cleanup(parking_topic);
52         parking_topic = NULL;
53 }
54
55 int ast_parking_stasis_init(void)
56 {
57         if (STASIS_MESSAGE_TYPE_INIT(ast_parked_call_type)) {
58                 return -1;
59         }
60
61         parking_topic = stasis_topic_create("ast_parking");
62         if (!parking_topic) {
63                 return -1;
64         }
65         ast_register_cleanup(parking_stasis_cleanup);
66         return 0;
67 }
68
69 struct stasis_topic *ast_parking_topic(void)
70 {
71         return parking_topic;
72 }
73
74 /*! \brief Destructor for parked_call_payload objects */
75 static void parked_call_payload_destructor(void *obj)
76 {
77         struct ast_parked_call_payload *park_obj = obj;
78
79         ao2_cleanup(park_obj->parkee);
80         ao2_cleanup(park_obj->retriever);
81         ast_string_field_free_memory(park_obj);
82 }
83
84 struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_call_event_type event_type,
85                 struct ast_channel_snapshot *parkee_snapshot, const char *parker_dial_string,
86                 struct ast_channel_snapshot *retriever_snapshot, const char *parkinglot,
87                 unsigned int parkingspace, unsigned long int timeout,
88                 unsigned long int duration)
89 {
90         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
91
92         payload = ao2_alloc(sizeof(*payload), parked_call_payload_destructor);
93         if (!payload) {
94                 return NULL;
95         }
96
97         if (ast_string_field_init(payload, 32)) {
98                 return NULL;
99         }
100
101         payload->event_type = event_type;
102
103         ao2_ref(parkee_snapshot, +1);
104         payload->parkee = parkee_snapshot;
105
106         if (retriever_snapshot) {
107                 ao2_ref(retriever_snapshot, +1);
108                 payload->retriever = retriever_snapshot;
109         }
110
111         if (parkinglot) {
112                 ast_string_field_set(payload, parkinglot, parkinglot);
113         }
114
115         if (parker_dial_string) {
116                 ast_string_field_set(payload, parker_dial_string, parker_dial_string);
117         }
118
119         payload->parkingspace = parkingspace;
120         payload->timeout = timeout;
121         payload->duration = duration;
122
123         /* Bump the ref count by one since RAII_VAR is going to eat one when we leave. */
124         ao2_ref(payload, +1);
125         return payload;
126 }
127
128 int ast_parking_park_bridge_channel(struct ast_bridge_channel *parkee, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
129 {
130         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
131                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
132
133         if (!table || !table->parking_park_bridge_channel) {
134                 return -1;
135         }
136
137         if (table->module_info) {
138                 SCOPED_MODULE_USE(table->module_info->self);
139                 return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data);
140         }
141
142         return table->parking_park_bridge_channel(parkee, parkee_uuid, parker_uuid, app_data);
143 }
144
145 int ast_parking_blind_transfer_park(struct ast_bridge_channel *parker,
146         const char *context, const char *exten, transfer_channel_cb parked_channel_cb,
147         struct transfer_channel_data *parked_channel_data)
148 {
149         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
150                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
151
152         if (!table || !table->parking_blind_transfer_park) {
153                 return -1;
154         }
155
156         if (table->module_info) {
157                 SCOPED_MODULE_USE(table->module_info->self);
158                 return table->parking_blind_transfer_park(parker, context, exten, parked_channel_cb, parked_channel_data);
159         }
160
161         return table->parking_blind_transfer_park(parker, context, exten, parked_channel_cb, parked_channel_data);
162 }
163
164 int ast_parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length)
165 {
166         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
167                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
168
169         if (!table || !table->parking_park_call) {
170                 return -1;
171         }
172
173         if (table->module_info) {
174                 SCOPED_MODULE_USE(table->module_info->self);
175                 return table->parking_park_call(parker, exten, length);
176         }
177
178         return table->parking_park_call(parker, exten, length);
179 }
180
181 int ast_parking_is_exten_park(const char *context, const char *exten)
182 {
183         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
184                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
185
186         if (!table || !table->parking_is_exten_park) {
187                 return -1;
188         }
189
190         if (table->module_info) {
191                 SCOPED_MODULE_USE(table->module_info->self);
192                 return table->parking_is_exten_park(context, exten);
193         }
194
195         return table->parking_is_exten_park(context, exten);
196 }
197
198 int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_table *fn_table)
199 {
200         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, wrapper,
201                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
202
203         if (fn_table->module_version != PARKING_MODULE_VERSION) {
204                 ast_log(AST_LOG_WARNING, "Parking module provided incorrect parking module "
205                         "version: %u (expected: %d)\n", fn_table->module_version, PARKING_MODULE_VERSION);
206                 return -1;
207         }
208
209         if (wrapper) {
210                 ast_log(AST_LOG_WARNING, "Parking provider already registered by %s!\n",
211                         wrapper->module_name);
212                 return -1;
213         }
214
215         wrapper = ao2_alloc(sizeof(*wrapper), NULL);
216         if (!wrapper) {
217                 return -1;
218         }
219         *wrapper = *fn_table;
220
221         ao2_global_obj_replace_unref(parking_provider, wrapper);
222         return 0;
223 }
224
225 int ast_parking_unregister_bridge_features(const char *module_name)
226 {
227         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, wrapper,
228                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
229
230         if (!wrapper) {
231                 return -1;
232         }
233
234         if (strcmp(wrapper->module_name, module_name)) {
235                 ast_log(AST_LOG_WARNING, "%s has not registered the parking provider\n", module_name);
236                 return -1;
237         }
238
239         ao2_global_obj_release(parking_provider);
240         return 0;
241 }
242
243 int ast_parking_provider_registered(void)
244 {
245         RAII_VAR(struct ast_parking_bridge_feature_fn_table *, table,
246                 ao2_global_obj_ref(parking_provider), ao2_cleanup);
247
248         return !!table;
249 }