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