2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2013, Digium, Inc.
6 * Jonathan Rose <jrose@digium.com>
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.
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.
21 * \brief Call Parking Manager Actions and Events
23 * \author Jonathan Rose <jrose@digium.com>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include "res_parking.h"
31 #include "asterisk/config.h"
32 #include "asterisk/config_options.h"
33 #include "asterisk/event.h"
34 #include "asterisk/utils.h"
35 #include "asterisk/module.h"
36 #include "asterisk/cli.h"
37 #include "asterisk/astobj2.h"
38 #include "asterisk/features.h"
39 #include "asterisk/manager.h"
42 <manager name="Parkinglots" language="en_US">
44 Get a list of parking lots
47 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
50 <para>List all parking lots as a series of AMI events</para>
53 <manager name="ParkedCalls" language="en_US">
58 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
59 <parameter name="ParkingLot">
60 <para>If specified, only show parked calls from the parking lot with this name.</para>
64 <para>List parked calls.</para>
67 <managerEvent language="en_US" name="ParkedCall">
68 <managerEventInstance class="EVENT_FLAG_CALL">
69 <synopsis>Raised when a channel is parked.</synopsis>
71 <parameter name="ParkeeChannel">
73 <parameter name="ParkeeChannelState">
74 <para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
76 <parameter name="ParkeeChannelStateDesc">
80 <enum name="OffHook"/>
81 <enum name="Dialing"/>
83 <enum name="Ringing"/>
86 <enum name="Dialing Offhook"/>
87 <enum name="Pre-ring"/>
88 <enum name="Unknown"/>
91 <parameter name="ParkeeCallerIDNum">
93 <parameter name="ParkeeCallerIDName">
95 <parameter name="ParkeeConnectedLineNum">
97 <parameter name="ParkeeConnectedLineName">
99 <parameter name="ParkeeAccountCode">
101 <parameter name="ParkeeContext">
103 <parameter name="ParkeeExten">
105 <parameter name="ParkeePriority">
107 <parameter name="ParkeeUniqueid">
109 <parameter name="ParkerChannel">
111 <parameter name="ParkerChannelState">
112 <para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
114 <parameter name="ParkerChannelStateDesc">
118 <enum name="OffHook"/>
119 <enum name="Dialing"/>
121 <enum name="Ringing"/>
124 <enum name="Dialing Offhook"/>
125 <enum name="Pre-ring"/>
126 <enum name="Unknown"/>
129 <parameter name="ParkerCallerIDNum">
131 <parameter name="ParkerCallerIDName">
133 <parameter name="ParkerConnectedLineNum">
135 <parameter name="ParkerConnectedLineName">
137 <parameter name="ParkerAccountCode">
139 <parameter name="ParkerContext">
141 <parameter name="ParkerExten">
143 <parameter name="ParkerPriority">
145 <parameter name="ParkerUniqueid">
147 <parameter name="Parkinglot">
148 <para>Name of the parking lot that the parkee is parked in</para>
150 <parameter name="ParkingSpace">
151 <para>Parking Space that the parkee is parked in</para>
153 <parameter name="ParkingTimeout">
154 <para>Time remaining until the parkee is forcefully removed from parking in seconds</para>
156 <parameter name="ParkingDuration">
157 <para>Time the parkee has been in the parking bridge (in seconds)</para>
160 </managerEventInstance>
162 <managerEvent language="en_US" name="ParkedCallTimeOut">
163 <managerEventInstance class="EVENT_FLAG_CALL">
164 <synopsis>Raised when a channel leaves a parking lot due to reaching the time limit of being parked.</synopsis>
166 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
168 </managerEventInstance>
170 <managerEvent language="en_US" name="ParkedCallGiveUp">
171 <managerEventInstance class="EVENT_FLAG_CALL">
172 <synopsis>Raised when a channel leaves a parking lot because it hung up without being answered.</synopsis>
174 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
176 </managerEventInstance>
178 <managerEvent language="en_US" name="UnParkedCall">
179 <managerEventInstance class="EVENT_FLAG_CALL">
180 <synopsis>Raised when a channel leaves a parking lot because it was retrieved from the parking lot and reconnected.</synopsis>
182 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
183 <parameter name="RetrieverChannel">
185 <parameter name="RetrieverChannelState">
186 <para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
188 <parameter name="RetrieverChannelStateDesc">
192 <enum name="OffHook"/>
193 <enum name="Dialing"/>
195 <enum name="Ringing"/>
198 <enum name="Dialing Offhook"/>
199 <enum name="Pre-ring"/>
200 <enum name="Unknown"/>
203 <parameter name="RetrieverCallerIDNum">
205 <parameter name="RetrieverCallerIDName">
207 <parameter name="RetrieverConnectedLineNum">
209 <parameter name="RetrieverConnectedLineName">
211 <parameter name="RetrieverAccountCode">
213 <parameter name="RetrieverContext">
215 <parameter name="RetrieverExten">
217 <parameter name="RetrieverPriority">
219 <parameter name="RetrieverUniqueid">
222 </managerEventInstance>
226 /*! \brief subscription to the parking lot topic */
227 static struct stasis_subscription *parking_sub;
229 static struct ast_parked_call_payload *parked_call_payload_from_failure(struct ast_channel *chan)
231 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
232 RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
234 parkee_snapshot = ast_channel_snapshot_create(chan);
235 if (!parkee_snapshot) {
239 return ast_parked_call_payload_create(PARKED_CALL_FAILED, parkee_snapshot, NULL, NULL, NULL, 0, 0, 0);
242 static struct ast_parked_call_payload *parked_call_payload_from_parked_user(struct parked_user *pu, enum ast_parked_call_event_type event_type)
244 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
245 RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
248 struct timeval now = ast_tvnow();
249 const char *lot_name = pu->lot->name;
255 parkee_snapshot = ast_channel_snapshot_create(pu->chan);
257 if (!parkee_snapshot) {
261 timeout = pu->start.tv_sec + (long) pu->time_limit - now.tv_sec;
262 duration = now.tv_sec - pu->start.tv_sec;
264 return ast_parked_call_payload_create(event_type, parkee_snapshot, pu->parker, pu->retriever, lot_name, pu->parking_space, timeout, duration);
268 /*! \brief Builds a manager string based on the contents of a parked call payload */
269 static struct ast_str *manager_build_parked_call_string(const struct ast_parked_call_payload *payload)
271 struct ast_str *out = ast_str_create(1024);
272 RAII_VAR(struct ast_str *, parkee_string, NULL, ast_free);
273 RAII_VAR(struct ast_str *, parker_string, NULL, ast_free);
274 RAII_VAR(struct ast_str *, retriever_string, NULL, ast_free);
280 parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
282 if (payload->parker) {
283 parker_string = ast_manager_build_channel_state_string_prefix(payload->parker, "Parker");
286 if (payload->retriever) {
287 retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
291 "%s" /* parkee channel state */
292 "%s" /* parker channel state */
293 "%s" /* retriever channel state (when available) */
295 "ParkingSpace: %u\r\n"
296 "ParkingTimeout: %lu\r\n"
297 "ParkingDuration: %lu\r\n",
299 ast_str_buffer(parkee_string),
300 parker_string ? ast_str_buffer(parker_string) : "",
301 retriever_string ? ast_str_buffer(retriever_string) : "",
303 payload->parkingspace,
310 static int manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
312 RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
313 struct parked_user *curuser;
314 struct ao2_iterator iter_users;
317 curlot = parking_lot_find_by_name(lot_name);
320 astman_send_error(s, m, "Requested parking lot could not be found.");
321 return RESULT_SUCCESS;
324 astman_send_ack(s, m, "Parked calls will follow");
326 iter_users = ao2_iterator_init(curlot->parked_users, 0);
327 while ((curuser = ao2_iterator_next(&iter_users))) {
328 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
329 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
331 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
333 astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
334 return RESULT_FAILURE;
337 parked_call_string = manager_build_parked_call_string(payload);
338 if (!parked_call_string) {
339 astman_send_error(s, m, "Failed to retrieve parkingd ata about a parked user.");
340 return RESULT_FAILURE;
345 astman_append(s, "Event: ParkedCall\r\n"
346 "%s" /* The parked call string */
347 "%s" /* The action ID */
349 ast_str_buffer(parked_call_string),
352 ao2_ref(curuser, -1);
355 ao2_iterator_destroy(&iter_users);
358 "Event: ParkedCallsComplete\r\n"
364 return RESULT_SUCCESS;
367 static int manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
369 struct parked_user *curuser;
370 struct ao2_container *lot_container;
371 struct ao2_iterator iter_lots;
372 struct ao2_iterator iter_users;
373 struct parking_lot *curlot;
376 lot_container = get_parking_lot_container();
378 if (!lot_container) {
379 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
380 astman_send_error(s, m, "Could not create parking lot list");
381 return RESULT_SUCCESS;
384 iter_lots = ao2_iterator_init(lot_container, 0);
386 astman_send_ack(s, m, "Parked calls will follow");
388 while ((curlot = ao2_iterator_next(&iter_lots))) {
389 iter_users = ao2_iterator_init(curlot->parked_users, 0);
390 while ((curuser = ao2_iterator_next(&iter_users))) {
391 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
392 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
394 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
396 return RESULT_FAILURE;
399 parked_call_string = manager_build_parked_call_string(payload);
401 return RESULT_FAILURE;
406 astman_append(s, "Event: ParkedCall\r\n"
407 "%s" /* The parked call string */
408 "%s" /* The action ID */
410 ast_str_buffer(parked_call_string),
413 ao2_ref(curuser, -1);
415 ao2_iterator_destroy(&iter_users);
419 ao2_iterator_destroy(&iter_lots);
422 "Event: ParkedCallsComplete\r\n"
428 return RESULT_SUCCESS;
431 static int manager_parking_status(struct mansession *s, const struct message *m)
433 const char *id = astman_get_header(m, "ActionID");
434 const char *lot_name = astman_get_header(m, "ParkingLot");
435 char id_text[256] = "";
437 if (!ast_strlen_zero(id)) {
438 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
441 if (!ast_strlen_zero(lot_name)) {
442 return manager_parking_status_single_lot(s, m, id_text, lot_name);
445 return manager_parking_status_all_lots(s, m, id_text);
449 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
451 struct parking_lot *curlot = obj;
452 struct mansession *s = arg;
453 char *id_text = data;
455 astman_append(s, "Event: Parkinglot\r\n"
460 "%s" /* The Action ID */
463 curlot->cfg->parking_start,
464 curlot->cfg->parking_stop,
465 curlot->cfg->parkingtime,
471 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
473 const char *id = astman_get_header(m, "ActionID");
474 char id_text[256] = "";
475 struct ao2_container *lot_container;
477 if (!ast_strlen_zero(id)) {
478 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
481 lot_container = get_parking_lot_container();
483 if (!lot_container) {
484 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
485 astman_send_error(s, m, "Could not create parking lot list");
489 astman_send_ack(s, m, "Parking lots will follow");
491 ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA, manager_append_event_parking_lot_data_cb, s, id_text);
494 "Event: ParkinglotsComplete\r\n"
498 return RESULT_SUCCESS;
501 void publish_parked_call_failure(struct ast_channel *parkee)
503 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
504 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
506 payload = parked_call_payload_from_failure(parkee);
511 msg = stasis_message_create(ast_parked_call_type(), payload);
516 stasis_publish(ast_parking_topic(), msg);
519 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
521 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
522 RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
524 payload = parked_call_payload_from_parked_user(pu, event_type);
529 msg = stasis_message_create(ast_parked_call_type(), payload);
534 stasis_publish(ast_parking_topic(), msg);
537 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
539 char *event_type = "";
540 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
542 switch (parked_call->event_type) {
544 event_type = "ParkedCall";
546 case PARKED_CALL_TIMEOUT:
547 event_type = "ParkedCallTimeOut";
549 case PARKED_CALL_GIVEUP:
550 event_type = "ParkedCallGiveUp";
552 case PARKED_CALL_UNPARKED:
553 event_type = "UnParkedCall";
555 case PARKED_CALL_FAILED:
556 /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
560 parked_call_string = manager_build_parked_call_string(parked_call);
561 if (!parked_call_string) {
562 ast_log(LOG_ERROR, "Failed to issue an AMI event of '%s' in response to a stasis message.\n", event_type);
566 manager_event(EVENT_FLAG_CALL, event_type,
568 ast_str_buffer(parked_call_string)
572 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message)
574 if (stasis_message_type(message) == ast_parked_call_type()) {
575 struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
576 parked_call_message_response(parked_call_message);
580 static void parking_manager_enable_stasis(void)
583 parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
587 int load_parking_manager(void)
591 res = ast_manager_register_xml_core("Parkinglots", 0, manager_parking_lot_list);
592 res |= ast_manager_register_xml_core("ParkedCalls", 0, manager_parking_status);
593 /* TODO Add a 'Park' manager action */
594 parking_manager_enable_stasis();
598 static void parking_manager_disable_stasis(void)
600 parking_sub = stasis_unsubscribe(parking_sub);
603 void unload_parking_manager(void)
605 ast_manager_unregister("Parkinglots");
606 ast_manager_unregister("ParkedCalls");
607 parking_manager_disable_stasis();