AMI: Make AMI actions that generate event lists consistent.
[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                 ast_free(out);
214                 return NULL;
215         }
216
217         if (payload->retriever) {
218                 retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
219                 if (!retriever_string) {
220                         ast_free(out);
221                         return NULL;
222                 }
223         }
224
225         ast_str_set(&out, 0,
226                 "%s" /* parkee channel state */
227                 "%s" /* retriever channel state (when available) */
228                 "ParkerDialString: %s\r\n"
229                 "Parkinglot: %s\r\n"
230                 "ParkingSpace: %u\r\n"
231                 "ParkingTimeout: %lu\r\n"
232                 "ParkingDuration: %lu\r\n",
233
234                 ast_str_buffer(parkee_string),
235                 retriever_string ? ast_str_buffer(retriever_string) : "",
236                 payload->parker_dial_string,
237                 payload->parkinglot,
238                 payload->parkingspace,
239                 payload->timeout,
240                 payload->duration);
241
242         return out;
243 }
244
245 static void manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
246 {
247         RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
248         struct parked_user *curuser;
249         struct ao2_iterator iter_users;
250         int total = 0;
251
252         curlot = parking_lot_find_by_name(lot_name);
253         if (!curlot) {
254                 astman_send_error(s, m, "Requested parking lot could not be found.");
255                 return;
256         }
257
258         astman_send_listack(s, m, "Parked calls will follow", "start");
259
260         iter_users = ao2_iterator_init(curlot->parked_users, 0);
261         while ((curuser = ao2_iterator_next(&iter_users))) {
262                 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
263                 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
264
265                 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
266                 if (!payload) {
267                         ao2_ref(curuser, -1);
268                         break;
269                 }
270
271                 parked_call_string = manager_build_parked_call_string(payload);
272                 if (!parked_call_string) {
273                         ao2_ref(curuser, -1);
274                         break;
275                 }
276
277                 total++;
278
279                 astman_append(s, "Event: ParkedCall\r\n"
280                         "%s" /* The parked call string */
281                         "%s" /* The action ID */
282                         "\r\n",
283                         ast_str_buffer(parked_call_string),
284                         id_text);
285
286                 ao2_ref(curuser, -1);
287         }
288         ao2_iterator_destroy(&iter_users);
289
290         astman_send_list_complete_start(s, m, "ParkedCallsComplete", total);
291         astman_append(s, "Total: %d\r\n", total);
292         astman_send_list_complete_end(s);
293 }
294
295 static void manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
296 {
297         struct parked_user *curuser;
298         struct ao2_container *lot_container;
299         struct ao2_iterator iter_lots;
300         struct ao2_iterator iter_users;
301         struct parking_lot *curlot;
302         int total = 0;
303
304         lot_container = get_parking_lot_container();
305         if (!lot_container) {
306                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
307                 astman_send_error(s, m, "Could not create parking lot list");
308                 return;
309         }
310
311         astman_send_listack(s, m, "Parked calls will follow", "start");
312
313         iter_lots = ao2_iterator_init(lot_container, 0);
314         while ((curlot = ao2_iterator_next(&iter_lots))) {
315                 iter_users = ao2_iterator_init(curlot->parked_users, 0);
316                 while ((curuser = ao2_iterator_next(&iter_users))) {
317                         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
318                         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
319
320                         payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
321                         if (!payload) {
322                                 ao2_ref(curuser, -1);
323                                 ao2_iterator_destroy(&iter_users);
324                                 ao2_ref(curlot, -1);
325                                 goto abort_list;
326                         }
327
328                         parked_call_string = manager_build_parked_call_string(payload);
329                         if (!parked_call_string) {
330                                 ao2_ref(curuser, -1);
331                                 ao2_iterator_destroy(&iter_users);
332                                 ao2_ref(curlot, -1);
333                                 goto abort_list;
334                         }
335
336                         total++;
337
338                         astman_append(s, "Event: ParkedCall\r\n"
339                                 "%s" /* The parked call string */
340                                 "%s" /* The action ID */
341                                 "\r\n",
342                                 ast_str_buffer(parked_call_string),
343                                 id_text);
344
345                         ao2_ref(curuser, -1);
346                 }
347                 ao2_iterator_destroy(&iter_users);
348                 ao2_ref(curlot, -1);
349         }
350 abort_list:
351         ao2_iterator_destroy(&iter_lots);
352
353         astman_send_list_complete_start(s, m, "ParkedCallsComplete", total);
354         astman_append(s, "Total: %d\r\n", total);
355         astman_send_list_complete_end(s);
356 }
357
358 static int manager_parking_status(struct mansession *s, const struct message *m)
359 {
360         const char *id = astman_get_header(m, "ActionID");
361         const char *lot_name = astman_get_header(m, "ParkingLot");
362         char id_text[256];
363
364         id_text[0] = '\0';
365         if (!ast_strlen_zero(id)) {
366                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
367         }
368
369         if (!ast_strlen_zero(lot_name)) {
370                 manager_parking_status_single_lot(s, m, id_text, lot_name);
371         } else {
372                 manager_parking_status_all_lots(s, m, id_text);
373         }
374
375         return 0;
376 }
377
378 struct park_list_data {
379         const char *id_text;
380         int count;
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         struct park_list_data *list_data = data;
388
389         astman_append(s, "Event: Parkinglot\r\n"
390                 "%s" /* The Action ID */
391                 "Name: %s\r\n"
392                 "StartSpace: %d\r\n"
393                 "StopSpace: %d\r\n"
394                 "Timeout: %u\r\n"
395                 "\r\n",
396                 list_data->id_text,
397                 curlot->name,
398                 curlot->cfg->parking_start,
399                 curlot->cfg->parking_stop,
400                 curlot->cfg->parkingtime);
401         ++list_data->count;
402
403         return 0;
404 }
405
406 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
407 {
408         const char *id = astman_get_header(m, "ActionID");
409         struct ao2_container *lot_container;
410         char id_text[256];
411         struct park_list_data list_data;
412
413         id_text[0] = '\0';
414         if (!ast_strlen_zero(id)) {
415                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
416         }
417
418         lot_container = get_parking_lot_container();
419         if (!lot_container) {
420                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
421                 astman_send_error(s, m, "Could not create parking lot list");
422                 return 0;
423         }
424
425         astman_send_listack(s, m, "Parking lots will follow", "start");
426
427         list_data.id_text = id_text;
428         list_data.count = 0;
429         ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA,
430                 manager_append_event_parking_lot_data_cb, s, &list_data);
431
432         astman_send_list_complete_start(s, m, "ParkinglotsComplete", list_data.count);
433         astman_send_list_complete_end(s);
434
435         return 0;
436 }
437
438 static void manager_park_unbridged(struct mansession *s, const struct message *m,
439                 struct ast_channel *chan, const char *parkinglot, int timeout_override)
440 {
441         struct ast_bridge *parking_bridge = park_common_setup(chan,
442                 chan, parkinglot, NULL, 0, 0, timeout_override, 1);
443
444         if (!parking_bridge) {
445                 astman_send_error(s, m, "Park action failed\n");
446                 return;
447         }
448
449         if (ast_bridge_add_channel(parking_bridge, chan, NULL, 0, NULL)) {
450                 astman_send_error(s, m, "Park action failed\n");
451                 ao2_cleanup(parking_bridge);
452                 return;
453         }
454
455         astman_send_ack(s, m, "Park successful\n");
456         ao2_cleanup(parking_bridge);
457 }
458
459 static void manager_park_bridged(struct mansession *s, const struct message *m,
460                 struct ast_channel *chan, struct ast_channel *parker_chan,
461                 const char *parkinglot, int timeout_override)
462 {
463         struct ast_bridge_channel *bridge_channel;
464         char *app_data;
465
466         if (timeout_override != -1) {
467                 if (ast_asprintf(&app_data, "%s,t(%d)", parkinglot, timeout_override) == -1) {
468                         astman_send_error(s, m, "Park action failed\n");
469                         return;
470                 }
471         } else {
472                 if (ast_asprintf(&app_data, "%s", parkinglot) == -1) {
473                         astman_send_error(s, m, "Park action failed\n");
474                         return;
475                 }
476         }
477
478         ast_channel_lock(parker_chan);
479         bridge_channel = ast_channel_get_bridge_channel(parker_chan);
480         ast_channel_unlock(parker_chan);
481
482         if (!bridge_channel) {
483                 ast_free(app_data);
484                 astman_send_error(s, m, "Park action failed\n");
485                 return;
486         }
487
488         /* Subscribe to park messages for the channel being parked */
489         if (create_parked_subscription(parker_chan, ast_channel_uniqueid(chan), 1)) {
490                 ast_free(app_data);
491                 astman_send_error(s, m, "Park action failed\n");
492                 ao2_cleanup(bridge_channel);
493                 return;
494         }
495
496         ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(chan),
497                         ast_channel_uniqueid(parker_chan), app_data);
498
499         ast_free(app_data);
500
501         astman_send_ack(s, m, "Park successful\n");
502         ao2_cleanup(bridge_channel);
503 }
504
505 static int manager_park(struct mansession *s, const struct message *m)
506 {
507         const char *channel = astman_get_header(m, "Channel");
508         const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2"));
509         const char *announce_channel = astman_get_header(m, "AnnounceChannel");
510         const char *timeout = astman_get_header(m, "Timeout");
511         const char *parkinglot = astman_get_header(m, "Parkinglot");
512         char buf[BUFSIZ];
513         int timeout_override = -1;
514
515         RAII_VAR(struct ast_channel *, parker_chan, NULL, ao2_cleanup);
516         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
517
518         if (ast_strlen_zero(channel)) {
519                 astman_send_error(s, m, "Channel not specified");
520                 return 0;
521         }
522
523         if (!ast_strlen_zero(timeout)) {
524                 if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout < 0) {
525                         astman_send_error(s, m, "Invalid Timeout value.");
526                         return 0;
527                 }
528
529                 if (timeout_override > 0) {
530                         /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */
531                         timeout_override = MAX(1, timeout_override / 1000);
532                 }
533         }
534
535         if (!(chan = ast_channel_get_by_name(channel))) {
536                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
537                 astman_send_error(s, m, buf);
538                 return 0;
539         }
540
541         ast_channel_lock(chan);
542         if (!ast_strlen_zero(timeout_channel)) {
543                 ast_bridge_set_transfer_variables(chan, timeout_channel, 0);
544         }
545         ast_channel_unlock(chan);
546
547         parker_chan = ast_channel_bridge_peer(chan);
548         if (!parker_chan || strcmp(ast_channel_name(parker_chan), timeout_channel)) {
549                 if (!ast_strlen_zero(announce_channel)) {
550                         struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel);
551                         if (!announce_channel) {
552                                 astman_send_error(s, m, "AnnounceChannel does not exist");
553                                 return 0;
554                         }
555
556                         create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0);
557                         ast_channel_cleanup(announce_chan);
558                 }
559
560                 manager_park_unbridged(s, m, chan, parkinglot, timeout_override);
561                 return 0;
562         }
563
564         if (!ast_strlen_zero(announce_channel) && strcmp(announce_channel, timeout_channel)) {
565                 /* When using an announce_channel in bridge mode, only add the announce channel if it isn't
566                  * the same as the timeout channel (which will play announcements anyway) */
567                 struct ast_channel *announce_chan = ast_channel_get_by_name(announce_channel);
568                 if (!announce_channel) {
569                         astman_send_error(s, m, "AnnounceChannel does not exist");
570                         return 0;
571                 }
572
573                 create_parked_subscription(announce_chan, ast_channel_uniqueid(chan), 0);
574                 ast_channel_cleanup(announce_chan);
575         }
576
577         manager_park_bridged(s, m, chan, parker_chan, parkinglot, timeout_override);
578         return 0;
579 }
580
581 void publish_parked_call_failure(struct ast_channel *parkee)
582 {
583         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
584         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
585
586         if (!ast_parked_call_type()) {
587                 return;
588         }
589
590         payload = parked_call_payload_from_failure(parkee);
591         if (!payload) {
592                 return;
593         }
594
595         msg = stasis_message_create(ast_parked_call_type(), payload);
596         if (!msg) {
597                 return;
598         }
599
600         stasis_publish(ast_parking_topic(), msg);
601 }
602
603 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
604 {
605         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
606         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
607
608         if (!ast_parked_call_type()) {
609                 return;
610         }
611
612         payload = parked_call_payload_from_parked_user(pu, event_type);
613         if (!payload) {
614                 return;
615         }
616
617         msg = stasis_message_create(ast_parked_call_type(), payload);
618         if (!msg) {
619                 return;
620         }
621
622         stasis_publish(ast_parking_topic(), msg);
623 }
624
625 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
626 {
627         char *event_type = "";
628         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
629
630         switch (parked_call->event_type) {
631         case PARKED_CALL:
632                 event_type = "ParkedCall";
633                 break;
634         case PARKED_CALL_TIMEOUT:
635                 event_type = "ParkedCallTimeOut";
636                 break;
637         case PARKED_CALL_GIVEUP:
638                 event_type = "ParkedCallGiveUp";
639                 break;
640         case PARKED_CALL_UNPARKED:
641                 event_type = "UnParkedCall";
642                 break;
643         case PARKED_CALL_SWAP:
644                 event_type = "ParkedCallSwap";
645                 break;
646         case PARKED_CALL_FAILED:
647                 /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
648                 return;
649         }
650
651         parked_call_string = manager_build_parked_call_string(parked_call);
652         if (!parked_call_string) {
653                 ast_log(LOG_ERROR, "Failed to issue an AMI event of '%s' in response to a stasis message.\n", event_type);
654                 return;
655         }
656
657         manager_event(EVENT_FLAG_CALL, event_type,
658                         "%s",
659                         ast_str_buffer(parked_call_string)
660                 );
661 }
662
663 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
664 {
665         if (stasis_message_type(message) == ast_parked_call_type()) {
666                 struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
667                 parked_call_message_response(parked_call_message);
668         }
669 }
670
671 static void parking_manager_enable_stasis(void)
672 {
673         if (!parking_sub) {
674                 parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
675         }
676 }
677
678 int load_parking_manager(void)
679 {
680         int res;
681         const struct ast_module_info *module = parking_get_module_info();
682
683         res = ast_manager_register2("Parkinglots", EVENT_FLAG_CALL, manager_parking_lot_list, module->self, NULL, NULL);
684         res |= ast_manager_register2("ParkedCalls", EVENT_FLAG_CALL, manager_parking_status, module->self, NULL, NULL);
685         res |= ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, module->self, NULL, NULL);
686         parking_manager_enable_stasis();
687         return res ? -1 : 0;
688 }
689
690 static void parking_manager_disable_stasis(void)
691 {
692         parking_sub = stasis_unsubscribe(parking_sub);
693 }
694
695 void unload_parking_manager(void)
696 {
697         ast_manager_unregister("Parkinglots");
698         ast_manager_unregister("ParkedCalls");
699         ast_manager_unregister("Park");
700         parking_manager_disable_stasis();
701 }