Fix a variety of memory leaks
[asterisk/asterisk.git] / main / parking.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 Parking Core
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/_private.h"
31 #include "asterisk/astobj2.h"
32 #include "asterisk/pbx.h"
33 #include "asterisk/bridging.h"
34 #include "asterisk/parking.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/_private.h"
37
38 /*! \brief Message type for parked calls */
39 STASIS_MESSAGE_TYPE_DEFN(ast_parked_call_type);
40
41 /*! \brief Topic for parking lots */
42 static struct stasis_topic *parking_topic;
43
44 /*! \brief Function Callback for handling blind transfers to park applications */
45 static ast_park_blind_xfer_fn ast_park_blind_xfer_func = NULL;
46
47 /*! \brief Function Callback for handling a bridge channel trying to park itself */
48 static ast_bridge_channel_park_fn ast_bridge_channel_park_func = NULL;
49
50 static void parking_stasis_cleanup(void)
51 {
52         STASIS_MESSAGE_TYPE_CLEANUP(ast_parked_call_type);
53         ao2_cleanup(parking_topic);
54         parking_topic = NULL;
55 }
56
57 int ast_parking_stasis_init(void)
58 {
59         if (STASIS_MESSAGE_TYPE_INIT(ast_parked_call_type)) {
60                 return -1;
61         }
62
63         parking_topic = stasis_topic_create("ast_parking");
64         if (!parking_topic) {
65                 return -1;
66         }
67         ast_register_cleanup(parking_stasis_cleanup);
68         return 0;
69 }
70
71 struct stasis_topic *ast_parking_topic(void)
72 {
73         return parking_topic;
74 }
75
76 /*! \brief Destructor for parked_call_payload objects */
77 static void parked_call_payload_destructor(void *obj)
78 {
79         struct ast_parked_call_payload *park_obj = obj;
80
81         ao2_cleanup(park_obj->parkee);
82         ao2_cleanup(park_obj->parker);
83         ao2_cleanup(park_obj->retriever);
84         ast_string_field_free_memory(park_obj);
85 }
86
87 struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_call_event_type event_type,
88                 struct ast_channel_snapshot *parkee_snapshot, struct ast_channel_snapshot *parker_snapshot,
89                 struct ast_channel_snapshot *retriever_snapshot, const char *parkinglot,
90                 unsigned int parkingspace, unsigned long int timeout,
91                 unsigned long int duration)
92 {
93         RAII_VAR(struct ast_parked_call_payload *, payload, NULL, ao2_cleanup);
94
95         payload = ao2_alloc(sizeof(*payload), parked_call_payload_destructor);
96         if (!payload) {
97                 return NULL;
98         }
99
100         if (ast_string_field_init(payload, 32)) {
101                 return NULL;
102         }
103
104         payload->event_type = event_type;
105
106         ao2_ref(parkee_snapshot, +1);
107         payload->parkee = parkee_snapshot;
108
109         if (parker_snapshot) {
110                 ao2_ref(parker_snapshot, +1);
111                 payload->parker = parker_snapshot;
112         }
113
114         if (retriever_snapshot) {
115                 ao2_ref(retriever_snapshot, +1);
116                 payload->retriever = retriever_snapshot;
117         }
118
119         if (parkinglot) {
120                 ast_string_field_set(payload, parkinglot, parkinglot);
121         }
122
123         payload->parkingspace = parkingspace;
124         payload->timeout = timeout;
125         payload->duration = duration;
126
127         /* Bump the ref count by one since RAII_VAR is going to eat one when we leave. */
128         ao2_ref(payload, +1);
129         return payload;
130 }
131
132 void ast_install_park_blind_xfer_func(ast_park_blind_xfer_fn park_blind_xfer_func)
133 {
134         ast_park_blind_xfer_func = park_blind_xfer_func;
135 }
136
137 void ast_install_bridge_channel_park_func(ast_bridge_channel_park_fn bridge_channel_park_func)
138 {
139         ast_bridge_channel_park_func = bridge_channel_park_func;
140 }
141
142 void ast_uninstall_park_blind_xfer_func(void)
143 {
144         ast_park_blind_xfer_func = NULL;
145 }
146
147 void ast_uninstall_bridge_channel_park_func(void)
148 {
149         ast_bridge_channel_park_func = NULL;
150 }
151
152 int ast_park_blind_xfer(struct ast_bridge *bridge, struct ast_bridge_channel *parker,
153                 struct ast_exten *park_exten)
154 {
155         static int warned = 0;
156         if (ast_park_blind_xfer_func) {
157                 return ast_park_blind_xfer_func(bridge, parker, park_exten);
158         }
159
160         if (warned++ % 10 == 0) {
161                 ast_verb(3, "%s attempted to blind transfer to a parking extension, but no parking blind transfer function is loaded.\n",
162                         ast_channel_name(parker->chan));
163         }
164
165         return -1;
166 }
167
168 struct ast_exten *ast_get_parking_exten(const char *exten_str, struct ast_channel *chan, const char *context)
169 {
170         struct ast_exten *exten;
171         struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
172         const char *app_at_exten;
173
174         ast_debug(4, "Checking if %s@%s is a parking exten\n", exten_str, context);
175         exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL,
176                 E_MATCH);
177         if (!exten) {
178                 return NULL;
179         }
180
181         app_at_exten = ast_get_extension_app(exten);
182         if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) {
183                 return NULL;
184         }
185
186         return exten;
187 }
188
189 void ast_bridge_channel_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
190 {
191         /* Run installable function */
192         if (ast_bridge_channel_park_func) {
193                 return ast_bridge_channel_park_func(bridge_channel, parkee_uuid, parker_uuid, app_data);
194         }
195 }