ao2_iterator: Mini-audit of the ao2_iterator loops in the new code files.
[asterisk/asterisk.git] / res / parking / parking_manager.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Call Parking Manager Actions and Events
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "res_parking.h"
31 #include "asterisk/config.h"
32 #include "asterisk/config_options.h"
33 #include "asterisk/utils.h"
34 #include "asterisk/module.h"
35 #include "asterisk/cli.h"
36 #include "asterisk/astobj2.h"
37 #include "asterisk/features.h"
38 #include "asterisk/manager.h"
39 #include "asterisk/bridge.h"
40
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         <manager name="Park" language="en_US">
68                 <synopsis>
69                         Park a channel.
70                 </synopsis>
71                 <syntax>
72                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
73                         <parameter name="Channel" required="true">
74                                 <para>Channel name to park.</para>
75                         </parameter>
76                         <parameter name="TimeoutChannel" required="false">
77                                 <para>Channel name to use when constructing the dial string that will be dialed if the parked channel times out.</para>
78                         </parameter>
79                         <parameter name="Timeout" required="false">
80                                 <para>Overrides the timeout of the parking lot for this park action. Specified in milliseconds, but will be converted to
81                                         seconds. Use a value of 0 to nullify the timeout.
82                                 </para>
83                         </parameter>
84                         <parameter name="Parkinglot" required="false">
85                                 <para>The parking lot to use when parking the channel</para>
86                         </parameter>
87                 </syntax>
88                 <description>
89                         <para>Park an arbitrary channel with optional arguments for specifying the parking lot used, how long
90                                 the channel should remain parked, and what dial string to use as the parker if the call times out.
91                         </para>
92                 </description>
93         </manager>
94         <managerEvent language="en_US" name="ParkedCall">
95                 <managerEventInstance class="EVENT_FLAG_CALL">
96                         <synopsis>Raised when a channel is parked.</synopsis>
97                         <syntax>
98                                 <channel_snapshot prefix="Parkee"/>
99                                 <parameter name="ParkerDialString">
100                                         <para>Dial String that can be used to call back the parker on ParkingTimeout.</para>
101                                 </parameter>
102                                 <parameter name="Parkinglot">
103                                         <para>Name of the parking lot that the parkee is parked in</para>
104                                 </parameter>
105                                 <parameter name="ParkingSpace">
106                                         <para>Parking Space that the parkee is parked in</para>
107                                 </parameter>
108                                 <parameter name="ParkingTimeout">
109                                 <para>Time remaining until the parkee is forcefully removed from parking in seconds</para>
110                                 </parameter>
111                                 <parameter name="ParkingDuration">
112                                         <para>Time the parkee has been in the parking bridge (in seconds)</para>
113                                 </parameter>
114                         </syntax>
115                 </managerEventInstance>
116         </managerEvent>
117         <managerEvent language="en_US" name="ParkedCallTimeOut">
118                 <managerEventInstance class="EVENT_FLAG_CALL">
119                         <synopsis>Raised when a channel leaves a parking lot due to reaching the time limit of being parked.</synopsis>
120                         <syntax>
121                                 <channel_snapshot prefix="Parkee"/>
122                                 <channel_snapshot prefix="Parker"/>
123                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
124                         </syntax>
125                 </managerEventInstance>
126         </managerEvent>
127         <managerEvent language="en_US" name="ParkedCallGiveUp">
128                 <managerEventInstance class="EVENT_FLAG_CALL">
129                         <synopsis>Raised when a channel leaves a parking lot because it hung up without being answered.</synopsis>
130                         <syntax>
131                                 <channel_snapshot prefix="Parkee"/>
132                                 <channel_snapshot prefix="Parker"/>
133                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
134                         </syntax>
135                 </managerEventInstance>
136         </managerEvent>
137         <managerEvent language="en_US" name="UnParkedCall">
138                 <managerEventInstance class="EVENT_FLAG_CALL">
139                         <synopsis>Raised when a channel leaves a parking lot because it was retrieved from the parking lot and reconnected.</synopsis>
140                         <syntax>
141                                 <channel_snapshot prefix="Parkee"/>
142                                 <channel_snapshot prefix="Parker"/>
143                                 <xi:include xpointer="xpointer(/docs/managerEvent[@name='ParkedCall']/managerEventInstance/syntax/parameter)" />
144                                 <channel_snapshot prefix="Retriever"/>
145                         </syntax>
146                 </managerEventInstance>
147         </managerEvent>
148  ***/
149
150 /*! \brief subscription to the parking lot topic */
151 static struct stasis_subscription *parking_sub;
152
153 static struct ast_parked_call_payload *parked_call_payload_from_failure(struct ast_channel *chan)
154 {
155         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
156         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
157
158         ast_channel_lock(chan);
159         parkee_snapshot = ast_channel_snapshot_create(chan);
160         ast_channel_unlock(chan);
161         if (!parkee_snapshot) {
162                 return NULL;
163         }
164
165         return ast_parked_call_payload_create(PARKED_CALL_FAILED, parkee_snapshot, NULL, NULL, NULL, 0, 0, 0);
166 }
167
168 static struct ast_parked_call_payload *parked_call_payload_from_parked_user(struct parked_user *pu, enum ast_parked_call_event_type event_type)
169 {
170         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
171         RAII_VAR(struct ast_channel_snapshot *, parkee_snapshot, NULL, ao2_cleanup);
172         long int timeout;
173         long int duration;
174         struct timeval now = ast_tvnow();
175         const char *lot_name = pu->lot->name;
176
177         ast_channel_lock(pu->chan);
178         parkee_snapshot = ast_channel_snapshot_create(pu->chan);
179         ast_channel_unlock(pu->chan);
180
181         if (!parkee_snapshot) {
182                 return NULL;
183         }
184
185         timeout = pu->start.tv_sec + (long) pu->time_limit - now.tv_sec;
186         duration = now.tv_sec - pu->start.tv_sec;
187
188         return ast_parked_call_payload_create(event_type, parkee_snapshot, pu->parker_dial_string, pu->retriever, lot_name, pu->parking_space, timeout, duration);
189
190 }
191
192 /*! \brief Builds a manager string based on the contents of a parked call payload */
193 static struct ast_str *manager_build_parked_call_string(const struct ast_parked_call_payload *payload)
194 {
195         struct ast_str *out = ast_str_create(1024);
196         RAII_VAR(struct ast_str *, parkee_string, NULL, ast_free);
197         RAII_VAR(struct ast_str *, retriever_string, NULL, ast_free);
198
199         if (!out) {
200                 return NULL;
201         }
202
203         parkee_string = ast_manager_build_channel_state_string_prefix(payload->parkee, "Parkee");
204         if (!parkee_string) {
205                 return NULL;
206         }
207
208         if (payload->retriever) {
209                 retriever_string = ast_manager_build_channel_state_string_prefix(payload->retriever, "Retriever");
210         }
211
212         ast_str_set(&out, 0,
213                 "%s" /* parkee channel state */
214                 "%s" /* retriever channel state (when available) */
215                 "ParkerDialString: %s\r\n"
216                 "Parkinglot: %s\r\n"
217                 "ParkingSpace: %u\r\n"
218                 "ParkingTimeout: %lu\r\n"
219                 "ParkingDuration: %lu\r\n",
220
221                 ast_str_buffer(parkee_string),
222                 retriever_string ? ast_str_buffer(retriever_string) : "",
223                 payload->parker_dial_string,
224                 payload->parkinglot,
225                 payload->parkingspace,
226                 payload->timeout,
227                 payload->duration);
228
229         return out;
230 }
231
232 static void manager_parking_status_single_lot(struct mansession *s, const struct message *m, const char *id_text, const char *lot_name)
233 {
234         RAII_VAR(struct parking_lot *, curlot, NULL, ao2_cleanup);
235         struct parked_user *curuser;
236         struct ao2_iterator iter_users;
237         int total = 0;
238
239         curlot = parking_lot_find_by_name(lot_name);
240         if (!curlot) {
241                 astman_send_error(s, m, "Requested parking lot could not be found.");
242                 return;
243         }
244
245         astman_send_ack(s, m, "Parked calls will follow");
246
247         iter_users = ao2_iterator_init(curlot->parked_users, 0);
248         while ((curuser = ao2_iterator_next(&iter_users))) {
249                 RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
250                 RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
251
252                 payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
253                 if (!payload) {
254                         ao2_ref(curuser, -1);
255                         ao2_iterator_destroy(&iter_users);
256                         astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
257                         return;
258                 }
259
260                 parked_call_string = manager_build_parked_call_string(payload);
261                 if (!parked_call_string) {
262                         ao2_ref(curuser, -1);
263                         ao2_iterator_destroy(&iter_users);
264                         astman_send_error(s, m, "Failed to retrieve parking data about a parked user.");
265                         return;
266                 }
267
268                 total++;
269
270                 astman_append(s, "Event: ParkedCall\r\n"
271                         "%s" /* The parked call string */
272                         "%s" /* The action ID */
273                         "\r\n",
274                         ast_str_buffer(parked_call_string),
275                         id_text);
276
277                 ao2_ref(curuser, -1);
278         }
279         ao2_iterator_destroy(&iter_users);
280
281         astman_append(s,
282                 "Event: ParkedCallsComplete\r\n"
283                 "Total: %d\r\n"
284                 "%s"
285                 "\r\n",
286                 total, id_text);
287 }
288
289 static void manager_parking_status_all_lots(struct mansession *s, const struct message *m, const char *id_text)
290 {
291         struct parked_user *curuser;
292         struct ao2_container *lot_container;
293         struct ao2_iterator iter_lots;
294         struct ao2_iterator iter_users;
295         struct parking_lot *curlot;
296         int total = 0;
297
298         lot_container = get_parking_lot_container();
299         if (!lot_container) {
300                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
301                 astman_send_error(s, m, "Could not create parking lot list");
302                 return;
303         }
304
305         astman_send_ack(s, m, "Parked calls will follow");
306
307         iter_lots = ao2_iterator_init(lot_container, 0);
308         while ((curlot = ao2_iterator_next(&iter_lots))) {
309                 iter_users = ao2_iterator_init(curlot->parked_users, 0);
310                 while ((curuser = ao2_iterator_next(&iter_users))) {
311                         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
312                         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
313
314                         payload = parked_call_payload_from_parked_user(curuser, PARKED_CALL);
315                         if (!payload) {
316                                 ao2_ref(curuser, -1);
317                                 ao2_iterator_destroy(&iter_users);
318                                 ao2_ref(curlot, -1);
319                                 ao2_iterator_destroy(&iter_lots);
320                                 return;
321                         }
322
323                         parked_call_string = manager_build_parked_call_string(payload);
324                         if (!parked_call_string) {
325                                 ao2_ref(curuser, -1);
326                                 ao2_iterator_destroy(&iter_users);
327                                 ao2_ref(curlot, -1);
328                                 ao2_iterator_destroy(&iter_lots);
329                                 return;
330                         }
331
332                         total++;
333
334                         astman_append(s, "Event: ParkedCall\r\n"
335                                 "%s" /* The parked call string */
336                                 "%s" /* The action ID */
337                                 "\r\n",
338                                 ast_str_buffer(parked_call_string),
339                                 id_text);
340
341                         ao2_ref(curuser, -1);
342                 }
343                 ao2_iterator_destroy(&iter_users);
344                 ao2_ref(curlot, -1);
345         }
346         ao2_iterator_destroy(&iter_lots);
347
348         astman_append(s,
349                 "Event: ParkedCallsComplete\r\n"
350                 "Total: %d\r\n"
351                 "%s"
352                 "\r\n",
353                 total, id_text);
354 }
355
356 static int manager_parking_status(struct mansession *s, const struct message *m)
357 {
358         const char *id = astman_get_header(m, "ActionID");
359         const char *lot_name = astman_get_header(m, "ParkingLot");
360         char id_text[256] = "";
361
362         if (!ast_strlen_zero(id)) {
363                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
364         }
365
366         if (!ast_strlen_zero(lot_name)) {
367                 manager_parking_status_single_lot(s, m, id_text, lot_name);
368         } else {
369                 manager_parking_status_all_lots(s, m, id_text);
370         }
371
372         return 0;
373 }
374
375 static int manager_append_event_parking_lot_data_cb(void *obj, void *arg, void *data, int flags)
376 {
377         struct parking_lot *curlot = obj;
378         struct mansession *s = arg;
379         char *id_text = data;
380
381         astman_append(s, "Event: Parkinglot\r\n"
382                 "Name: %s\r\n"
383                 "StartSpace: %d\r\n"
384                 "StopSpace: %d\r\n"
385                 "Timeout: %d\r\n"
386                 "%s" /* The Action ID */
387                 "\r\n",
388                 curlot->name,
389                 curlot->cfg->parking_start,
390                 curlot->cfg->parking_stop,
391                 curlot->cfg->parkingtime,
392                 id_text);
393
394         return 0;
395 }
396
397 static int manager_parking_lot_list(struct mansession *s, const struct message *m)
398 {
399         const char *id = astman_get_header(m, "ActionID");
400         char id_text[256] = "";
401         struct ao2_container *lot_container;
402
403         if (!ast_strlen_zero(id)) {
404                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
405         }
406
407         lot_container = get_parking_lot_container();
408         if (!lot_container) {
409                 ast_log(LOG_ERROR, "Failed to obtain parking lot list. Action canceled.\n");
410                 astman_send_error(s, m, "Could not create parking lot list");
411                 return 0;
412         }
413
414         astman_send_ack(s, m, "Parking lots will follow");
415
416         ao2_callback_data(lot_container, OBJ_MULTIPLE | OBJ_NODATA, manager_append_event_parking_lot_data_cb, s, id_text);
417
418         astman_append(s,
419                 "Event: ParkinglotsComplete\r\n"
420                 "%s"
421                 "\r\n",id_text);
422
423         return 0;
424 }
425
426 static int manager_park(struct mansession *s, const struct message *m)
427 {
428         const char *channel = astman_get_header(m, "Channel");
429         const char *timeout_channel = S_OR(astman_get_header(m, "TimeoutChannel"), astman_get_header(m, "Channel2"));
430         const char *timeout = astman_get_header(m, "Timeout");
431         const char *parkinglot = astman_get_header(m, "Parkinglot");
432         char buf[BUFSIZ];
433         int timeout_override = -1;
434
435         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
436         RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
437
438         if (ast_strlen_zero(channel)) {
439                 astman_send_error(s, m, "Channel not specified");
440                 return 0;
441         }
442
443         if (!ast_strlen_zero(timeout)) {
444                 if (sscanf(timeout, "%30d", &timeout_override) != 1 || timeout < 0) {
445                         astman_send_error(s, m, "Invalid Timeout value.");
446                         return 0;
447                 }
448
449                 if (timeout_override > 0) {
450                         /* If greater than zero, convert to seconds for internal use. Must be >= 1 second. */
451                         timeout_override = MAX(1, timeout_override / 1000);
452                 }
453         }
454
455         if (!(chan = ast_channel_get_by_name(channel))) {
456                 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
457                 astman_send_error(s, m, buf);
458                 return 0;
459         }
460
461         ast_channel_lock(chan);
462         if (!ast_strlen_zero(timeout_channel)) {
463                 ast_bridge_set_transfer_variables(chan, timeout_channel, 0);
464         }
465         ast_channel_unlock(chan);
466
467         if (!(parking_bridge = park_common_setup(chan, chan, parkinglot, NULL, 0, 0, timeout_override, 0))) {
468                 astman_send_error(s, m, "Park action failed\n");
469                 return 0;
470         }
471
472         if (ast_bridge_add_channel(parking_bridge, chan, NULL, 0, NULL)) {
473                 astman_send_error(s, m, "Park action failed\n");
474                 return 0;
475         }
476
477         astman_send_ack(s, m, "Park successful\n");
478         return 0;
479 }
480
481 void publish_parked_call_failure(struct ast_channel *parkee)
482 {
483         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
484         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
485
486         payload = parked_call_payload_from_failure(parkee);
487         if (!payload) {
488                 return;
489         }
490
491         msg = stasis_message_create(ast_parked_call_type(), payload);
492         if (!msg) {
493                 return;
494         }
495
496         stasis_publish(ast_parking_topic(), msg);
497 }
498
499 void publish_parked_call(struct parked_user *pu, enum ast_parked_call_event_type event_type)
500 {
501         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
502         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
503
504         payload = parked_call_payload_from_parked_user(pu, event_type);
505         if (!payload) {
506                 return;
507         }
508
509         msg = stasis_message_create(ast_parked_call_type(), payload);
510         if (!msg) {
511                 return;
512         }
513
514         stasis_publish(ast_parking_topic(), msg);
515 }
516
517 static void parked_call_message_response(struct ast_parked_call_payload *parked_call)
518 {
519         char *event_type = "";
520         RAII_VAR(struct ast_str *, parked_call_string, NULL, ast_free);
521
522         switch (parked_call->event_type) {
523         case PARKED_CALL:
524                 event_type = "ParkedCall";
525                 break;
526         case PARKED_CALL_TIMEOUT:
527                 event_type = "ParkedCallTimeOut";
528                 break;
529         case PARKED_CALL_GIVEUP:
530                 event_type = "ParkedCallGiveUp";
531                 break;
532         case PARKED_CALL_UNPARKED:
533                 event_type = "UnParkedCall";
534                 break;
535         case PARKED_CALL_SWAP:
536                 event_type = "ParkedCallSwap";
537                 break;
538         case PARKED_CALL_FAILED:
539                 /* PARKED_CALL_FAILED doesn't currently get a message and is used exclusively for bridging */
540                 return;
541         }
542
543         parked_call_string = manager_build_parked_call_string(parked_call);
544         if (!parked_call_string) {
545                 ast_log(LOG_ERROR, "Failed to issue an AMI event of '%s' in response to a stasis message.\n", event_type);
546                 return;
547         }
548
549         manager_event(EVENT_FLAG_CALL, event_type,
550                         "%s",
551                         ast_str_buffer(parked_call_string)
552                 );
553 }
554
555 static void parking_event_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message)
556 {
557         if (stasis_message_type(message) == ast_parked_call_type()) {
558                 struct ast_parked_call_payload *parked_call_message = stasis_message_data(message);
559                 parked_call_message_response(parked_call_message);
560         }
561 }
562
563 static void parking_manager_enable_stasis(void)
564 {
565         if (!parking_sub) {
566                 parking_sub = stasis_subscribe(ast_parking_topic(), parking_event_cb, NULL);
567         }
568 }
569
570 int load_parking_manager(void)
571 {
572         int res;
573
574         res = ast_manager_register_xml_core("Parkinglots", EVENT_FLAG_CALL, manager_parking_lot_list);
575         res |= ast_manager_register_xml_core("ParkedCalls", EVENT_FLAG_CALL, manager_parking_status);
576         res |= ast_manager_register_xml_core("Park", EVENT_FLAG_CALL, manager_park);
577         parking_manager_enable_stasis();
578         return res ? -1 : 0;
579 }
580
581 static void parking_manager_disable_stasis(void)
582 {
583         parking_sub = stasis_unsubscribe(parking_sub);
584 }
585
586 void unload_parking_manager(void)
587 {
588         ast_manager_unregister("Parkinglots");
589         ast_manager_unregister("ParkedCalls");
590         ast_manager_unregister("Park");
591         parking_manager_disable_stasis();
592 }