697468eb39426bf6ba027d6ed0656886c9517e4c
[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/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"
40
41 /*** DOCUMENTATION
42         <manager name="Parkinglots" language="en_US">
43                 <synopsis>
44                         Get a list of parking lots
45                 </synopsis>
46                 <syntax>
47                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
48                 </syntax>
49                 <description>
50                         <para>List all parking lots as a series of AMI events</para>
51                 </description>
52         </manager>
53         <manager name="ParkedCalls" language="en_US">
54                 <synopsis>
55                         List parked calls.
56                 </synopsis>
57                 <syntax>
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>
61                         </parameter>
62                 </syntax>
63                 <description>
64                         <para>List parked calls.</para>
65                 </description>
66         </manager>
67         <managerEvent language="en_US" name="ParkedCall">
68                 <managerEventInstance class="EVENT_FLAG_CALL">
69                         <synopsis>Raised when a channel is parked.</synopsis>
70                         <syntax>
71                                 <parameter name="ChannelParkee">
72                                 </parameter>
73                                 <parameter name="ChannelStateParkee">
74                                         <para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
75                                 </parameter>
76                                 <parameter name="ChannelStateDescParkee">
77                                         <enumlist>
78                                                 <enum name="Down"/>
79                                                 <enum name="Rsrvd"/>
80                                                 <enum name="OffHook"/>
81                                                 <enum name="Dialing"/>
82                                                 <enum name="Ring"/>
83                                                 <enum name="Ringing"/>
84                                                 <enum name="Up"/>
85                                                 <enum name="Busy"/>
86                                                 <enum name="Dialing Offhook"/>
87                                                 <enum name="Pre-ring"/>
88                                                 <enum name="Unknown"/>
89                                         </enumlist>
90                                 </parameter>
91                                 <parameter name="CallerIDNumParkee">
92                                 </parameter>
93                                 <parameter name="CallerIDNameParkee">
94                                 </parameter>
95                                 <parameter name="ConnectedLineNumParkee">
96                                 </parameter>
97                                 <parameter name="ConnectedLineNameParkee">
98                                 </parameter>
99                                 <parameter name="AccountCodeParkee">
100                                 </parameter>
101                                 <parameter name="ContextParkee">
102                                 </parameter>
103                                 <parameter name="ExtenParkee">
104                                 </parameter>
105                                 <parameter name="PriorityParkee">
106                                 </parameter>
107                                 <parameter name="UniqueidParkee">
108                                 </parameter>
109                                 <parameter name="ChannelParker">
110                                 </parameter>
111                                 <parameter name="ChannelStateParker">
112                                 <para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
113                                 </parameter>
114                                 <parameter name="ChannelStateDescParker">
115                                         <enumlist>
116                                                 <enum name="Down"/>
117                                                 <enum name="Rsrvd"/>
118                                                 <enum name="OffHook"/>
119                                                 <enum name="Dialing"/>
120                                                 <enum name="Ring"/>
121                                                 <enum name="Ringing"/>
122                                                 <enum name="Up"/>
123                                                 <enum name="Busy"/>
124                                                 <enum name="Dialing Offhook"/>
125                                                 <enum name="Pre-ring"/>
126                                                 <enum name="Unknown"/>
127                                         </enumlist>
128                                 </parameter>
129                                 <parameter name="CallerIDNumParker">
130                                 </parameter>
131                                 <parameter name="CallerIDNameParker">
132                                 </parameter>
133                                 <parameter name="ConnectedLineNumParker">
134                                 </parameter>
135                                 <parameter name="ConnectedLineNameParker">
136                                 </parameter>
137                                 <parameter name="AccountCodeParker">
138                                 </parameter>
139                                 <parameter name="ContextParker">
140                                 </parameter>
141                                 <parameter name="ExtenParker">
142                                 </parameter>
143                                 <parameter name="PriorityParker">
144                                 </parameter>
145                                 <parameter name="UniqueidParker">
146                                 </parameter>
147                                 <parameter name="Parkinglot">
148                                         <para>Name of the parking lot that the parkee is parked in</para>
149                                 </parameter>
150                                 <parameter name="ParkingSpace">
151                                         <para>Parking Space that the parkee is parked in</para>
152                                 </parameter>
153                                 <parameter name="ParkingTimeout">
154                                 <para>Time remaining until the parkee is forcefully removed from parking in seconds</para>
155                                 </parameter>
156                                 <parameter name="ParkingDuration">
157                                         <para>Time the parkee has been in the parking bridge (in seconds)</para>
158                                 </parameter>
159                         </syntax>
160                 </managerEventInstance>
161         </managerEvent>
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>
165                         <syntax>
166                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
167                         </syntax>
168                 </managerEventInstance>
169         </managerEvent>
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>
173                         <syntax>
174                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
175                         </syntax>
176                 </managerEventInstance>
177         </managerEvent>
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>
181                         <syntax>
182                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
183                                 <parameter name="ChannelRetriever">
184                                 </parameter>
185                                 <parameter name="ChannelStateRetriever">
186                                         <para>A numeric code for the channel's current state, related to ChannelStateDesc</para>
187                                 </parameter>
188                                 <parameter name="ChannelStateDescRetriever">
189                                         <enumlist>
190                                                 <enum name="Down"/>
191                                                 <enum name="Rsrvd"/>
192                                                 <enum name="OffHook"/>
193                                                 <enum name="Dialing"/>
194                                                 <enum name="Ring"/>
195                                                 <enum name="Ringing"/>
196                                                 <enum name="Up"/>
197                                                 <enum name="Busy"/>
198                                                 <enum name="Dialing Offhook"/>
199                                                 <enum name="Pre-ring"/>
200                                                 <enum name="Unknown"/>
201                                         </enumlist>
202                                 </parameter>
203                                 <parameter name="CallerIDNumRetriever">
204                                 </parameter>
205                                 <parameter name="CallerIDNameRetriever">
206                                 </parameter>
207                                 <parameter name="ConnectedLineNumRetriever">
208                                 </parameter>
209                                 <parameter name="ConnectedLineNameRetriever">
210                                 </parameter>
211                                 <parameter name="AccountCodeRetriever">
212                                 </parameter>
213                                 <parameter name="ContextRetriever">
214                                 </parameter>
215                                 <parameter name="ExtenRetriever">
216                                 </parameter>
217                                 <parameter name="PriorityRetriever">
218                                 </parameter>
219                                 <parameter name="UniqueidRetriever">
220                                 </parameter>
221                         </syntax>
222                 </managerEventInstance>
223         </managerEvent>
224  ***/
225
226 /*! \brief subscription to the parking lot topic */
227 static struct stasis_subscription *parking_sub;
228
229 static struct ast_parked_call_payload *parked_call_payload_from_failure(struct ast_channel *chan)
230 {
231         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
232         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
233
234         parkee_snapshot = ast_channel_snapshot_create(chan);
235         if (!parkee_snapshot) {
236                 return NULL;
237         }
238
239         return ast_parked_call_payload_create(PARKED_CALL_FAILED, parkee_snapshot, NULL, NULL, NULL, 0, 0, 0);
240 }
241
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)
243 {
244         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
245         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
246         long int timeout;
247         long int duration;
248         struct timeval now = ast_tvnow();
249         const char *lot_name = pu->lot->name;
250
251         if (!pu->parker) {
252                 return NULL;
253         }
254
255         parkee_snapshot = ast_channel_snapshot_create(pu->chan);
256
257         if (!parkee_snapshot) {
258                 return NULL;
259         }
260
261         timeout = pu->start.tv_sec + (long) pu->time_limit - now.tv_sec;
262         duration = now.tv_sec - pu->start.tv_sec;
263
264         return ast_parked_call_payload_create(event_type, parkee_snapshot, pu->parker, pu->retriever, lot_name, pu->parking_space, timeout, duration);
265
266 }
267
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)
270 {
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);
275
276         if (!out) {
277                 return NULL;
278         }
279
280         parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
281
282         if (payload->parker) {
283                 parker_string = ast_manager_build_channel_state_string_prefix(payload->parker, "Parker");
284         }
285
286         if (payload->retriever) {
287                 retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
288         }
289
290         ast_str_set(&out, 0,
291                 "%s" /* parkee channel state */
292                 "%s" /* parker channel state */
293                 "%s" /* retriever channel state (when available) */
294                 "Parkinglot: %s\r\n"
295                 "ParkingSpace: %u\r\n"
296                 "ParkingTimeout: %lu\r\n"
297                 "ParkingDuration: %lu\r\n",
298
299                 ast_str_buffer(parkee_string),
300                 parker_string ? ast_str_buffer(parker_string) : "",
301                 retriever_string ? ast_str_buffer(retriever_string) : "",
302                 payload->parkinglot,
303                 payload->parkingspace,
304                 payload->timeout,
305                 payload->duration);
306
307         return out;
308 }
309
310 static int manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
311 {
312         RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
313         struct parked_user *curuser;
314         struct ao2_iterator iter_users;
315         int total = 0;
316
317         curlot = parking_lot_find_by_name(lot_name);
318
319         if (!curlot) {
320                 astman_send_error(s, m, "Requested parking lot could not be found.");
321                 return RESULT_SUCCESS;
322         }
323
324         astman_send_ack(s, m, "Parked calls will follow");
325
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);
330
331                 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
332                 if (!payload) {
333                         astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
334                         return RESULT_FAILURE;
335                 }
336
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;
341                 }
342
343                 total++;
344
345                 astman_append(s, "Event: ParkedCall\r\n"
346                         "%s" /* The parked call string */
347                         "%s" /* The action ID */
348                         "\r\n",
349                         ast_str_buffer(parked_call_string),
350                         id_text);
351
352                 ao2_ref(curuser, -1);
353         }
354
355         ao2_iterator_destroy(&iter_users);
356
357         astman_append(s,
358                 "Event: ParkedCallsComplete\r\n"
359                 "Total: %d\r\n"
360                 "%s"
361                 "\r\n",
362                 total, id_text);
363
364         return RESULT_SUCCESS;
365 }
366
367 static int manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
368 {
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;
374         int total = 0;
375
376         lot_container = get_parking_lot_container();
377
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;
382         }
383
384         iter_lots = ao2_iterator_init(lot_container, 0);
385
386         astman_send_ack(s, m, "Parked calls will follow");
387
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);
393
394                         payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
395                         if (!payload) {
396                                 return RESULT_FAILURE;
397                         }
398
399                         parked_call_string = manager_build_parked_call_string(payload);
400                         if (!payload) {
401                                 return RESULT_FAILURE;
402                         }
403
404                         total++;
405
406                         astman_append(s, "Event: ParkedCall\r\n"
407                                 "%s" /* The parked call string */
408                                 "%s" /* The action ID */
409                                 "\r\n",
410                                 ast_str_buffer(parked_call_string),
411                                 id_text);
412
413                         ao2_ref(curuser, -1);
414                 }
415                 ao2_iterator_destroy(&iter_users);
416                 ao2_ref(curlot, -1);
417         }
418
419         ao2_iterator_destroy(&iter_lots);
420
421         astman_append(s,
422                 "Event: ParkedCallsComplete\r\n"
423                 "Total: %d\r\n"
424                 "%s"
425                 "\r\n",
426                 total, id_text);
427
428         return RESULT_SUCCESS;
429 }
430
431 static int manager_parking_status(struct mansession *s, const struct message *m)
432 {
433         const char *id = astman_get_header(m, "ActionID");
434         const char *lot_name = astman_get_header(m, "ParkingLot");
435         char id_text[256] = "";
436
437         if (!ast_strlen_zero(id)) {
438                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
439         }
440
441         if (!ast_strlen_zero(lot_name)) {
442                 return manager_parking_status_single_lot(s, m, id_text, lot_name);
443         }
444
445         return manager_parking_status_all_lots(s, m, id_text);
446
447 }
448
449 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
450 {
451         struct parking_lot *curlot = obj;
452         struct mansession *s = arg;
453         char *id_text = data;
454
455         astman_append(s, "Event: Parkinglot\r\n"
456                 "Name: %s\r\n"
457                 "StartSpace: %d\r\n"
458                 "StopSpace: %d\r\n"
459                 "Timeout: %d\r\n"
460                 "%s" /* The Action ID */
461                 "\r\n",
462                 curlot->name,
463                 curlot->cfg->parking_start,
464                 curlot->cfg->parking_stop,
465                 curlot->cfg->parkingtime,
466                 id_text);
467
468         return 0;
469 }
470
471 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
472 {
473         const char *id = astman_get_header(m, "ActionID");
474         char id_text[256] = "";
475         struct ao2_container *lot_container;
476
477         if (!ast_strlen_zero(id)) {
478                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
479         }
480
481         lot_container = get_parking_lot_container();
482
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");
486                 return -1;
487         }
488
489         astman_send_ack(s, m, "Parking lots will follow");
490
491         ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA, manager_append_event_parking_lot_data_cb, s, id_text);
492
493         astman_append(s,
494                 "Event: ParkinglotsComplete\r\n"
495                 "%s"
496                 "\r\n",id_text);
497
498         return RESULT_SUCCESS;
499 }
500
501 void publish_parked_call_failure(struct ast_channel *parkee)
502 {
503         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
504         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
505
506         payload = parked_call_payload_from_failure(parkee);
507         if (!payload) {
508                 return;
509         }
510
511         msg = stasis_message_create(ast_parked_call_type(), payload);
512         if (!msg) {
513                 return;
514         }
515
516         stasis_publish(ast_parking_topic(), msg);
517 }
518
519 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
520 {
521         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
522         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
523
524         payload = parked_call_payload_from_parked_user(pu, event_type);
525         if (!payload) {
526                 return;
527         }
528
529         msg = stasis_message_create(ast_parked_call_type(), payload);
530         if (!msg) {
531                 return;
532         }
533
534         stasis_publish(ast_parking_topic(), msg);
535 }
536
537 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
538 {
539         char *event_type = "";
540         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
541
542         switch (parked_call->event_type) {
543         case PARKED_CALL:
544                 event_type = "ParkedCall";
545                 break;
546         case PARKED_CALL_TIMEOUT:
547                 event_type = "ParkedCallTimeOut";
548                 break;
549         case PARKED_CALL_GIVEUP:
550                 event_type = "ParkedCallGiveUp";
551                 break;
552         case PARKED_CALL_UNPARKED:
553                 event_type = "UnParkedCall";
554                 break;
555         case PARKED_CALL_FAILED:
556                 /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
557                 return;
558         }
559
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);
563                 return;
564         }
565
566         manager_event(EVENT_FLAG_CALL, event_type,
567                         "%s",
568                         ast_str_buffer(parked_call_string)
569                 );
570 }
571
572 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *message)
573 {
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);
577         }
578 }
579
580 static void parking_manager_enable_stasis(void)
581 {
582         ast_parking_stasis_init();
583         if (!parking_sub) {
584                 parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
585         }
586 }
587
588 int load_parking_manager(void)
589 {
590         int res;
591
592         res = ast_manager_register_xml_core("Parkinglots", 0, manager_parking_lot_list);
593         res |= ast_manager_register_xml_core("ParkedCalls", 0, manager_parking_status);
594         /* TODO Add a 'Park' manager action */
595         parking_manager_enable_stasis();
596         return res ? -1 : 0;
597 }
598
599 static void parking_manager_disable_stasis(void)
600 {
601         parking_sub = stasis_unsubscribe(parking_sub);
602         ast_parking_stasis_disable();
603 }
604
605 void unload_parking_manager(void)
606 {
607         ast_manager_unregister("Parkinglots");
608         ast_manager_unregister("ParkedCalls");
609         parking_manager_disable_stasis();
610 }