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