Stasis: Allow message types to be blocked
[asterisk/asterisk.git] / res / parking / parking_manager.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 Call Parking Manager Actions and Events
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "res_parking.h"
31 #include "asterisk/config.h"
32 #include "asterisk/config_options.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/module.h"
35 #include "asterisk/cli.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/features.h"
38 #include "asterisk/manager.h"
39 #include "asterisk/bridge.h"
40 #include "asterisk/module.h"
41
42 /*** DOCUMENTATION
43         <manager name="Parkinglots" language="en_US">
44                 <synopsis>
45                         Get a list of parking lots
46                 </synopsis>
47                 <syntax>
48                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
49                 </syntax>
50                 <description>
51                         <para>List all parking lots as a series of AMI events</para>
52                 </description>
53         </manager>
54         <manager name="ParkedCalls" language="en_US">
55                 <synopsis>
56                         List parked calls.
57                 </synopsis>
58                 <syntax>
59                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
60                         <parameter name="ParkingLot">
61                                 <para>If specified, only show parked calls from the parking lot with this name.</para>
62                         </parameter>
63                 </syntax>
64                 <description>
65                         <para>List parked calls.</para>
66                 </description>
67         </manager>
68         <manager name="Park" language="en_US">
69                 <synopsis>
70                         Park a channel.
71                 </synopsis>
72                 <syntax>
73                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
74                         <parameter name="Channel" required="true">
75                                 <para>Channel name to park.</para>
76                         </parameter>
77                         <parameter name="TimeoutChannel" required="false">
78                                 <para>Channel name to use when constructing the dial string that will be dialed if the parked channel
79                                 times out. If <literal>TimeoutChannel</literal> is in a two party bridge with
80                                 <literal>Channel</literal>, then <literal>TimeoutChannel</literal> will receive an announcement and be
81                                 treated as having parked <literal>Channel</literal> in the same manner as the Park Call DTMF feature.
82                                 </para>
83                         </parameter>
84                         <parameter name="AnnounceChannel" required="false">
85                                 <para>If specified, then this channel will receive an announcement when <literal>Channel</literal>
86                                 is parked if <literal>AnnounceChannel</literal> is in a state where it can receive announcements
87                                 (AnnounceChannel must be bridged). <literal>AnnounceChannel</literal> has no bearing on the actual
88                                 state of the parked call.</para>
89                         </parameter>
90                         <parameter name="Timeout" required="false">
91                                 <para>Overrides the timeout of the parking lot for this park action. Specified in milliseconds, but will be converted to
92                                         seconds. Use a value of 0 to disable the timeout.
93                                 </para>
94                         </parameter>
95                         <parameter name="Parkinglot" required="false">
96                                 <para>The parking lot to use when parking the channel</para>
97                         </parameter>
98                 </syntax>
99                 <description>
100                         <para>Park an arbitrary channel with optional arguments for specifying the parking lot used, how long
101                                 the channel should remain parked, and what dial string to use as the parker if the call times out.
102                         </para>
103                 </description>
104         </manager>
105         <managerEvent language="en_US" name="ParkedCall">
106                 <managerEventInstance class="EVENT_FLAG_CALL">
107                         <synopsis>Raised when a channel is parked.</synopsis>
108                         <syntax>
109                                 <channel_snapshot prefix="Parkee"/>
110                                 <parameter name="ParkerDialString">
111                                         <para>Dial String that can be used to call back the parker on ParkingTimeout.</para>
112                                 </parameter>
113                                 <parameter name="Parkinglot">
114                                         <para>Name of the parking lot that the parkee is parked in</para>
115                                 </parameter>
116                                 <parameter name="ParkingSpace">
117                                         <para>Parking Space that the parkee is parked in</para>
118                                 </parameter>
119                                 <parameter name="ParkingTimeout">
120                                 <para>Time remaining until the parkee is forcefully removed from parking in seconds</para>
121                                 </parameter>
122                                 <parameter name="ParkingDuration">
123                                         <para>Time the parkee has been in the parking bridge (in seconds)</para>
124                                 </parameter>
125                         </syntax>
126                 </managerEventInstance>
127         </managerEvent>
128         <managerEvent language="en_US" name="ParkedCallTimeOut">
129                 <managerEventInstance class="EVENT_FLAG_CALL">
130                         <synopsis>Raised when a channel leaves a parking lot due to reaching the time limit of being parked.</synopsis>
131                         <syntax>
132                                 <channel_snapshot prefix="Parkee"/>
133                                 <channel_snapshot prefix="Parker"/>
134                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
135                         </syntax>
136                 </managerEventInstance>
137         </managerEvent>
138         <managerEvent language="en_US" name="ParkedCallGiveUp">
139                 <managerEventInstance class="EVENT_FLAG_CALL">
140                         <synopsis>Raised when a channel leaves a parking lot because it hung up without being answered.</synopsis>
141                         <syntax>
142                                 <channel_snapshot prefix="Parkee"/>
143                                 <channel_snapshot prefix="Parker"/>
144                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
145                         </syntax>
146                 </managerEventInstance>
147         </managerEvent>
148         <managerEvent language="en_US" name="UnParkedCall">
149                 <managerEventInstance class="EVENT_FLAG_CALL">
150                         <synopsis>Raised when a channel leaves a parking lot because it was retrieved from the parking lot and reconnected.</synopsis>
151                         <syntax>
152                                 <channel_snapshot prefix="Parkee"/>
153                                 <channel_snapshot prefix="Parker"/>
154                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
155                                 <channel_snapshot prefix="Retriever"/>
156                         </syntax>
157                 </managerEventInstance>
158         </managerEvent>
159  ***/
160
161 /*! \brief subscription to the parking lot topic */
162 static struct stasis_subscription *parking_sub;
163
164 static struct ast_parked_call_payload *parked_call_payload_from_failure(struct ast_channel *chan)
165 {
166         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
167
168         ast_channel_lock(chan);
169         parkee_snapshot = ast_channel_snapshot_create(chan);
170         ast_channel_unlock(chan);
171         if (!parkee_snapshot) {
172                 return NULL;
173         }
174
175         return ast_parked_call_payload_create(PARKED_CALL_FAILED, parkee_snapshot, NULL, NULL, NULL, 0, 0, 0);
176 }
177
178 static struct ast_parked_call_payload *parked_call_payload_from_parked_user(struct parked_user *pu, enum ast_parked_call_event_type event_type)
179 {
180         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
181         long int timeout;
182         long int duration;
183         struct timeval now = ast_tvnow();
184         const char *lot_name = pu->lot->name;
185
186         ast_channel_lock(pu->chan);
187         parkee_snapshot = ast_channel_snapshot_create(pu->chan);
188         ast_channel_unlock(pu->chan);
189         if (!parkee_snapshot) {
190                 return NULL;
191         }
192
193         timeout = pu->start.tv_sec + (long) pu->time_limit - now.tv_sec;
194         duration = now.tv_sec - pu->start.tv_sec;
195
196         return ast_parked_call_payload_create(event_type, parkee_snapshot, pu->parker_dial_string, pu->retriever, lot_name, pu->parking_space, timeout, duration);
197
198 }
199
200 /*! \brief Builds a manager string based on the contents of a parked call payload */
201 static struct ast_str *manager_build_parked_call_string(const struct ast_parked_call_payload *payload)
202 {
203         struct ast_str *out = ast_str_create(1024);
204         RAII_VAR(struct ast_str *, parkee_string, NULL, ast_free);
205         RAII_VAR(struct ast_str *, retriever_string, NULL, ast_free);
206
207         if (!out) {
208                 return NULL;
209         }
210
211         parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
212         if (!parkee_string) {
213                 return NULL;
214         }
215
216         if (payload->retriever) {
217                 retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
218         }
219
220         ast_str_set(&out, 0,
221                 "%s" /* parkee channel state */
222                 "%s" /* retriever channel state (when available) */
223                 "ParkerDialString: %s\r\n"
224                 "Parkinglot: %s\r\n"
225                 "ParkingSpace: %u\r\n"
226                 "ParkingTimeout: %lu\r\n"
227                 "ParkingDuration: %lu\r\n",
228
229                 ast_str_buffer(parkee_string),
230                 retriever_string ? ast_str_buffer(retriever_string) : "",
231                 payload->parker_dial_string,
232                 payload->parkinglot,
233                 payload->parkingspace,
234                 payload->timeout,
235                 payload->duration);
236
237         return out;
238 }
239
240 static void manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
241 {
242         RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
243         struct parked_user *curuser;
244         struct ao2_iterator iter_users;
245         int total = 0;
246
247         curlot = parking_lot_find_by_name(lot_name);
248         if (!curlot) {
249                 astman_send_error(s, m, "Requested parking lot could not be found.");
250                 return;
251         }
252
253         astman_send_ack(s, m, "Parked calls will follow");
254
255         iter_users = ao2_iterator_init(curlot->parked_users, 0);
256         while ((curuser = ao2_iterator_next(&iter_users))) {
257                 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
258                 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
259
260                 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
261                 if (!payload) {
262                         ao2_ref(curuser, -1);
263                         ao2_iterator_destroy(&iter_users);
264                         astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
265                         return;
266                 }
267
268                 parked_call_string = manager_build_parked_call_string(payload);
269                 if (!parked_call_string) {
270                         ao2_ref(curuser, -1);
271                         ao2_iterator_destroy(&iter_users);
272                         astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
273                         return;
274                 }
275
276                 total++;
277
278                 astman_append(s, "Event: ParkedCall\r\n"
279                         "%s" /* The parked call string */
280                         "%s" /* The action ID */
281                         "\r\n",
282                         ast_str_buffer(parked_call_string),
283                         id_text);
284
285                 ao2_ref(curuser, -1);
286         }
287         ao2_iterator_destroy(&iter_users);
288
289         astman_append(s,
290                 "Event: ParkedCallsComplete\r\n"
291                 "Total: %d\r\n"
292                 "%s"
293                 "\r\n",
294                 total, id_text);
295 }
296
297 static void manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
298 {
299         struct parked_user *curuser;
300         struct ao2_container *lot_container;
301         struct ao2_iterator iter_lots;
302         struct ao2_iterator iter_users;
303         struct parking_lot *curlot;
304         int total = 0;
305
306         lot_container = get_parking_lot_container();
307         if (!lot_container) {
308                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
309                 astman_send_error(s, m, "Could not create parking lot list");
310                 return;
311         }
312
313         astman_send_ack(s, m, "Parked calls will follow");
314
315         iter_lots = ao2_iterator_init(lot_container, 0);
316         while ((curlot = ao2_iterator_next(&iter_lots))) {
317                 iter_users = ao2_iterator_init(curlot->parked_users, 0);
318                 while ((curuser = ao2_iterator_next(&iter_users))) {
319                         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
320                         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
321
322                         payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
323                         if (!payload) {
324                                 ao2_ref(curuser, -1);
325                                 ao2_iterator_destroy(&iter_users);
326                                 ao2_ref(curlot, -1);
327                                 ao2_iterator_destroy(&iter_lots);
328                                 return;
329                         }
330
331                         parked_call_string = manager_build_parked_call_string(payload);
332                         if (!parked_call_string) {
333                                 ao2_ref(curuser, -1);
334                                 ao2_iterator_destroy(&iter_users);
335                                 ao2_ref(curlot, -1);
336                                 ao2_iterator_destroy(&iter_lots);
337                                 return;
338                         }
339
340                         total++;
341
342                         astman_append(s, "Event: ParkedCall\r\n"
343                                 "%s" /* The parked call string */
344                                 "%s" /* The action ID */
345                                 "\r\n",
346                                 ast_str_buffer(parked_call_string),
347                                 id_text);
348
349                         ao2_ref(curuser, -1);
350                 }
351                 ao2_iterator_destroy(&iter_users);
352                 ao2_ref(curlot, -1);
353         }
354         ao2_iterator_destroy(&iter_lots);
355
356         astman_append(s,
357                 "Event: ParkedCallsComplete\r\n"
358                 "Total: %d\r\n"
359                 "%s"
360                 "\r\n",
361                 total, id_text);
362 }
363
364 static int manager_parking_status(struct mansession *s, const struct message *m)
365 {
366         const char *id = astman_get_header(m, "ActionID");
367         const char *lot_name = astman_get_header(m, "ParkingLot");
368         char id_text[256] = "";
369
370         if (!ast_strlen_zero(id)) {
371                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
372         }
373
374         if (!ast_strlen_zero(lot_name)) {
375                 manager_parking_status_single_lot(s, m, id_text, lot_name);
376         } else {
377                 manager_parking_status_all_lots(s, m, id_text);
378         }
379
380         return 0;
381 }
382
383 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
384 {
385         struct parking_lot *curlot = obj;
386         struct mansession *s = arg;
387         char *id_text = data;
388
389         astman_append(s, "Event: Parkinglot\r\n"
390                 "Name: %s\r\n"
391                 "StartSpace: %d\r\n"
392                 "StopSpace: %d\r\n"
393                 "Timeout: %u\r\n"
394                 "%s" /* The Action ID */
395                 "\r\n",
396                 curlot->name,
397                 curlot->cfg->parking_start,
398                 curlot->cfg->parking_stop,
399                 curlot->cfg->parkingtime,
400                 id_text);
401
402         return 0;
403 }
404
405 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
406 {
407         const char *id = astman_get_header(m, "ActionID");
408         char id_text[256] = "";
409         struct ao2_container *lot_container;
410
411         if (!ast_strlen_zero(id)) {
412                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
413         }
414
415         lot_container = get_parking_lot_container();
416         if (!lot_container) {
417                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
418                 astman_send_error(s, m, "Could not create parking lot list");
419                 return 0;
420         }
421
422         astman_send_ack(s, m, "Parking lots will follow");
423
424         ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA, manager_append_event_parking_lot_data_cb, s, id_text);
425
426         astman_append(s,
427                 "Event: ParkinglotsComplete\r\n"
428                 "%s"
429                 "\r\n",id_text);
430
431         return 0;
432 }
433
434 static void manager_park_unbridged(struct mansession *s, const struct message *m,
435                 struct ast_channel *chan, const char *parkinglot, int timeout_override)
436 {
437         struct ast_bridge *parking_bridge = park_common_setup(chan,
438                 chan, parkinglot, NULL, 0, 0, timeout_override, 1);
439
440         if (!parking_bridge) {
441                 astman_send_error(s, m, "Park action failed\n");
442                 return;
443         }
444
445         if (ast_bridge_add_channel(parking_bridge, chan, NULL, 0, NULL)) {
446                 astman_send_error(s, m, "Park action failed\n");
447                 ao2_cleanup(parking_bridge);
448                 return;
449         }
450
451         astman_send_ack(s, m, "Park successful\n");
452         ao2_cleanup(parking_bridge);
453 }
454
455 static void manager_park_bridged(struct mansession *s, const struct message *m,
456                 struct ast_channel *chan, struct ast_channel *parker_chan,
457                 const char *parkinglot, int timeout_override)
458 {
459         struct ast_bridge_channel *bridge_channel;
460         char *app_data;
461
462         if (timeout_override != -1) {
463                 if (ast_asprintf(&app_data, "%s,t(%d)", parkinglot, timeout_override) == -1) {
464                         astman_send_error(s, m, "Park action failed\n");
465                         return;
466                 }
467         } else {
468                 if (ast_asprintf(&app_data, "%s", parkinglot) == -1) {
469                         astman_send_error(s, m, "Park action failed\n");
470                         return;
471                 }
472         }
473
474         ast_channel_lock(parker_chan);
475         bridge_channel = ast_channel_get_bridge_channel(parker_chan);
476         ast_channel_unlock(parker_chan);
477
478         if (!bridge_channel) {
479                 ast_free(app_data);
480                 astman_send_error(s, m, "Park action failed\n");
481                 return;
482         }
483
484         /* Subscribe to park messages for the channel being parked */
485         if (create_parked_subscription(parker_chan, ast_channel_uniqueid(chan), 1)) {
486                 ast_free(app_data);
487                 astman_send_error(s, m, "Park action failed\n");
488                 ao2_cleanup(bridge_channel);
489                 return;
490         }
491
492         ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(chan),
493                         ast_channel_uniqueid(parker_chan), app_data);
494
495         ast_free(app_data);
496
497         astman_send_ack(s, m, "Park successful\n");
498         ao2_cleanup(bridge_channel);
499 }
500
501 static int manager_park(struct mansession *s, const struct message *m)
502 {
503         const char *channel = astman_get_header(m, "Channel");
504         const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2"));
505         const char *announce_channel = astman_get_header(m, "AnnounceChannel");
506         const char *timeout = astman_get_header(m, "Timeout");
507         const char *parkinglot = astman_get_header(m, "Parkinglot");
508         char buf[BUFSIZ];
509         int timeout_override = -1;
510
511         RAII_VAR(struct ast_channel *, parker_chan, NULL, ao2_cleanup);
512         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
513
514         if (ast_strlen_zero(channel)) {
515                 astman_send_error(s, m, "Channel not specified");
516                 return 0;
517         }
518
519         if (!ast_strlen_zero(timeout)) {
520                 if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout < 0) {
521                         astman_send_error(s, m, "Invalid Timeout value.");
522                         return 0;
523                 }
524
525                 if (timeout_override > 0) {
526                         /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */
527                         timeout_override = MAX(1, timeout_override / 1000);
528                 }
529         }
530
531         if (!(chan = ast_channel_get_by_name(channel))) {
532                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
533                 astman_send_error(s, m, buf);
534                 return 0;
535         }
536
537         ast_channel_lock(chan);
538         if (!ast_strlen_zero(timeout_channel)) {
539                 ast_bridge_set_transfer_variables(chan, timeout_channel, 0);
540         }
541         ast_channel_unlock(chan);
542
543         parker_chan = ast_channel_bridge_peer(chan);
544         if (!parker_chan || strcmp(ast_channel_name(parker_chan), timeout_channel)) {
545                 if (!ast_strlen_zero(announce_channel)) {
546                         struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel);
547                         if (!announce_channel) {
548                                 astman_send_error(s, m, "AnnounceChannel does not exist");
549                                 return 0;
550                         }
551
552                         create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0);
553                         ast_channel_cleanup(announce_chan);
554                 }
555
556                 manager_park_unbridged(s, m, chan, parkinglot, timeout_override);
557                 return 0;
558         }
559
560         if (!ast_strlen_zero(announce_channel) && strcmp(announce_channel, timeout_channel)) {
561                 /* When using an announce_channel in bridge mode, only add the announce channel if it isn't
562                  * the same as the timeout channel (which will play announcements anyway) */
563                 struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel);
564                 if (!announce_channel) {
565                         astman_send_error(s, m, "AnnounceChannel does not exist");
566                         return 0;
567                 }
568
569                 create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0);
570                 ast_channel_cleanup(announce_chan);
571         }
572
573         manager_park_bridged(s, m, chan, parker_chan, parkinglot, timeout_override);
574         return 0;
575 }
576
577 void publish_parked_call_failure(struct ast_channel *parkee)
578 {
579         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
580         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
581
582         if (!ast_parked_call_type()) {
583                 return;
584         }
585
586         payload = parked_call_payload_from_failure(parkee);
587         if (!payload) {
588                 return;
589         }
590
591         msg = stasis_message_create(ast_parked_call_type(), payload);
592         if (!msg) {
593                 return;
594         }
595
596         stasis_publish(ast_parking_topic(), msg);
597 }
598
599 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
600 {
601         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
602         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
603
604         if (!ast_parked_call_type()) {
605                 return;
606         }
607
608         payload = parked_call_payload_from_parked_user(pu, event_type);
609         if (!payload) {
610                 return;
611         }
612
613         msg = stasis_message_create(ast_parked_call_type(), payload);
614         if (!msg) {
615                 return;
616         }
617
618         stasis_publish(ast_parking_topic(), msg);
619 }
620
621 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
622 {
623         char *event_type = "";
624         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
625
626         switch (parked_call->event_type) {
627         case PARKED_CALL:
628                 event_type = "ParkedCall";
629                 break;
630         case PARKED_CALL_TIMEOUT:
631                 event_type = "ParkedCallTimeOut";
632                 break;
633         case PARKED_CALL_GIVEUP:
634                 event_type = "ParkedCallGiveUp";
635                 break;
636         case PARKED_CALL_UNPARKED:
637                 event_type = "UnParkedCall";
638                 break;
639         case PARKED_CALL_SWAP:
640                 event_type = "ParkedCallSwap";
641                 break;
642         case PARKED_CALL_FAILED:
643                 /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
644                 return;
645         }
646
647         parked_call_string = manager_build_parked_call_string(parked_call);
648         if (!parked_call_string) {
649                 ast_log(LOG_ERROR, "Failed to issue an AMI event of '%s' in response to a stasis message.\n", event_type);
650                 return;
651         }
652
653         manager_event(EVENT_FLAG_CALL, event_type,
654                         "%s",
655                         ast_str_buffer(parked_call_string)
656                 );
657 }
658
659 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
660 {
661         if (stasis_message_type(message) == ast_parked_call_type()) {
662                 struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
663                 parked_call_message_response(parked_call_message);
664         }
665 }
666
667 static void parking_manager_enable_stasis(void)
668 {
669         if (!parking_sub) {
670                 parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
671         }
672 }
673
674 int load_parking_manager(void)
675 {
676         int res;
677         const struct ast_module_info *module = parking_get_module_info();
678
679         res = ast_manager_register2("Parkinglots", EVENT_FLAG_CALL, manager_parking_lot_list, module->self, NULL, NULL);
680         res |= ast_manager_register2("ParkedCalls", EVENT_FLAG_CALL, manager_parking_status, module->self, NULL, NULL);
681         res |= ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, module->self, NULL, NULL);
682         parking_manager_enable_stasis();
683         return res ? -1 : 0;
684 }
685
686 static void parking_manager_disable_stasis(void)
687 {
688         parking_sub = stasis_unsubscribe(parking_sub);
689 }
690
691 void unload_parking_manager(void)
692 {
693         ast_manager_unregister("Parkinglots");
694         ast_manager_unregister("ParkedCalls");
695         ast_manager_unregister("Park");
696         parking_manager_disable_stasis();
697 }