Asterisk - The Open Source Telephony Project  21.4.1
parking_controller.c
Go to the documentation of this file.
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 Entry, Exit, and other assorted controls.
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  */
25 #include "asterisk.h"
26 
27 #include "asterisk/logger.h"
28 #include "res_parking.h"
29 #include "asterisk/astobj2.h"
30 #include "asterisk/utils.h"
31 #include "asterisk/manager.h"
32 #include "asterisk/test.h"
33 #include "asterisk/features.h"
34 #include "asterisk/bridge_basic.h"
35 
37 {
38  struct ast_bridge *lot_bridge;
39 
40  if (lot->parking_bridge) {
41  ao2_ref(lot->parking_bridge, +1);
42  return lot->parking_bridge;
43  }
44 
45  lot_bridge = bridge_parking_new(lot);
46  if (!lot_bridge) {
47  return NULL;
48  }
49 
50  /* The parking lot needs a reference to the bridge as well. */
51  lot->parking_bridge = lot_bridge;
52  ao2_ref(lot->parking_bridge, +1);
53 
54  return lot_bridge;
55 }
56 
57 int parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing)
58 {
59  if (ast_channel_add_bridge_role(chan, "holding_participant")) {
60  return -1;
61  }
62 
63  if (force_ringing) {
64  if (ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing")) {
65  return -1;
66  }
67  } else {
68  if (ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold")) {
69  return -1;
70  }
71  if (!ast_strlen_zero(lot->cfg->mohclass)) {
72  if (ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", lot->cfg->mohclass)) {
73  return -1;
74  }
75  }
76  }
77 
78  return 0;
79 }
80 
82  struct parked_user *user;
83 };
84 
86 {
87  if (pu->lot) {
88  ao2_unlink(pu->lot->parked_users, pu);
90  return 0;
91  }
92 
93  return -1;
94 }
95 
96 int parking_lot_get_space(struct parking_lot *lot, int target_override)
97 {
98  int original_target;
99  int current_target;
100  struct ao2_iterator i;
101  struct parked_user *user;
102  int wrap;
103 
104  if (lot->cfg->parkfindnext) {
105  /* Use next_space if the lot already has next_space set; otherwise use lot start. */
106  original_target = lot->next_space ? lot->next_space : lot->cfg->parking_start;
107  } else {
108  original_target = lot->cfg->parking_start;
109  }
110 
111  if (target_override >= lot->cfg->parking_start && target_override <= lot->cfg->parking_stop) {
112  original_target = target_override;
113  } else if (target_override > -1) {
114  ast_log(LOG_WARNING, "Preferred parking spot %d is out of bounds (%d-%d)\n", target_override, lot->cfg->parking_start, lot->cfg->parking_stop);
115  }
116 
117  current_target = original_target;
118 
119  wrap = lot->cfg->parking_start;
120 
121  i = ao2_iterator_init(lot->parked_users, 0);
122  while ((user = ao2_iterator_next(&i))) {
123  /* Increment the wrap on each pass until we find an empty space */
124  if (wrap == user->parking_space) {
125  wrap += 1;
126  }
127 
128  if (user->parking_space < current_target) {
129  /* It's lower than the anticipated target, so we haven't reached the target yet. */
130  ao2_ref(user, -1);
131  continue;
132  }
133 
134  if (user->parking_space > current_target) {
135  /* The current target is usable because all items below have been read and the next target is higher than the one we want. */
136  ao2_ref(user, -1);
137  break;
138  }
139 
140  /* We found one already parked here. */
141  current_target += 1;
142  ao2_ref(user, -1);
143  }
145 
146  if (current_target <= lot->cfg->parking_stop) {
147  return current_target;
148  }
149 
150  if (wrap <= lot->cfg->parking_stop) {
151  return wrap;
152  }
153 
154  return -1;
155 }
156 
157 static int retrieve_parked_user_targeted(void *obj, void *arg, int flags)
158 {
159  int *target = arg;
160  struct parked_user *user = obj;
161  if (user->parking_space == *target) {
162  return CMP_MATCH;
163  }
164 
165  return 0;
166 }
167 
169 {
170  struct parked_user *user;
171 
172  if (target < 0) {
173  user = ao2_callback(lot->parked_users, 0, NULL, NULL);
174  } else {
175  user = ao2_callback(lot->parked_users, 0, retrieve_parked_user_targeted, &target);
176  }
177 
178  if (!user) {
179  return NULL;
180  }
181 
182  return user;
183 }
184 
186 {
187  RAII_VAR(struct parked_user *, user, NULL, ao2_cleanup);
188 
189  if (target < 0) {
190  user = ao2_callback(lot->parked_users, 0, NULL, NULL);
191  } else {
192  user = ao2_callback(lot->parked_users, 0, retrieve_parked_user_targeted, &target);
193  }
194 
195  if (!user) {
196  return NULL;
197  }
198 
199  ao2_lock(user);
200  if (user->resolution != PARK_UNSET) {
201  /* Abandon. Something else has resolved the parked user before we got to it. */
202  ao2_unlock(user);
203  return NULL;
204  }
205 
206  ao2_unlink(lot->parked_users, user);
207  user->resolution = PARK_ANSWERED;
208  ao2_unlock(user);
209 
211 
212  /* Bump the ref count by 1 since the RAII_VAR will eat the reference otherwise */
213  ao2_ref(user, +1);
214  return user;
215 }
216 
217 void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode)
218 {
219  /* Enabling features here should be additive to features that are already on the channel. */
220  struct ast_flags feature_flags = { 0 };
221  struct ast_flags *existing_features;
222 
223  ast_channel_lock(chan);
224  existing_features = ast_bridge_features_ds_get(chan);
225  if (existing_features) {
226  feature_flags = *existing_features;
227  }
228 
229  if (lot->cfg->parkedcalltransfers & recipient_mode) {
230  ast_set_flag(&feature_flags, AST_FEATURE_REDIRECT);
231  }
232 
233  if (lot->cfg->parkedcallreparking & recipient_mode) {
234  ast_set_flag(&feature_flags, AST_FEATURE_PARKCALL);
235  }
236 
237  if (lot->cfg->parkedcallhangup & recipient_mode) {
238  ast_set_flag(&feature_flags, AST_FEATURE_DISCONNECT);
239  }
240 
241  if (lot->cfg->parkedcallrecording & recipient_mode) {
242  ast_set_flag(&feature_flags, AST_FEATURE_AUTOMIXMON);
243  }
244 
245  ast_bridge_features_ds_set(chan, &feature_flags);
246  ast_channel_unlock(chan);
247 
248  return;
249 }
250 
251 void flatten_dial_string(char *dialstring)
252 {
253  int i;
254 
255  for (i = 0; dialstring[i]; i++) {
256  if (dialstring[i] == '/') {
257  /* The underscore is the flattest character of all. */
258  dialstring[i] = '_';
259  }
260  }
261 }
262 
263 int comeback_goto(struct parked_user *pu, struct parking_lot *lot)
264 {
265  struct ast_channel *chan = pu->chan;
266  char *peername_flat = ast_strdupa(pu->parker_dial_string);
267 
268  /* Flatten the peername so that it can be used for performing the timeout PBX operations */
269  flatten_dial_string(peername_flat);
270 
271  if (lot->cfg->comebacktoorigin) {
272  if (ast_exists_extension(chan, PARK_DIAL_CONTEXT, peername_flat, 1, NULL)) {
273  ast_async_goto(chan, PARK_DIAL_CONTEXT, peername_flat, 1);
274  return 0;
275  } else {
276  ast_log(LOG_ERROR, "Can not start %s at %s,%s,1 because extension does not exist. Terminating call.\n",
277  ast_channel_name(chan), PARK_DIAL_CONTEXT, peername_flat);
278  return -1;
279  }
280  }
281 
282  if (ast_exists_extension(chan, lot->cfg->comebackcontext, peername_flat, 1, NULL)) {
283  ast_async_goto(chan, lot->cfg->comebackcontext, peername_flat, 1);
284  return 0;
285  }
286 
287  if (ast_exists_extension(chan, lot->cfg->comebackcontext, "s", 1, NULL)) {
288  ast_verb(2, "Could not start %s at %s,%s,1. Using 's@%s' instead.\n", ast_channel_name(chan),
289  lot->cfg->comebackcontext, peername_flat, lot->cfg->comebackcontext);
290  ast_async_goto(chan, lot->cfg->comebackcontext, "s", 1);
291  return 0;
292  }
293 
294  ast_verb(2, "Can not start %s at %s,%s,1 and exten 's@%s' does not exist. Using 's@default'\n",
295  ast_channel_name(chan),
296  lot->cfg->comebackcontext, peername_flat, lot->cfg->comebackcontext);
297  ast_async_goto(chan, "default", "s", 1);
298 
299  return 0;
300 }
Main Channel structure associated with a channel.
int unpark_parked_user(struct parked_user *pu)
Pull a parked user out of its parking lot. Use this when you don't want to use the parked user afterw...
Asterisk main include file. File version handling, generic pbx functions.
int parkedcallreparking
Definition: res_parking.h:77
int parking_lot_get_space(struct parking_lot *lot, int target_override)
Get an available parking space within a parking lot.
struct ast_bridge * parking_bridge
Definition: res_parking.h:94
struct ao2_container * parked_users
Definition: res_parking.h:95
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
Test Framework API.
void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode)
Apply features based on the parking lot feature options.
int ast_bridge_features_ds_set(struct ast_channel *chan, struct ast_flags *flags)
Set basic bridge DTMF feature flags datastore on the channel.
Definition: bridge_basic.c:258
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
const ast_string_field comebackcontext
Definition: res_parking.h:89
void flatten_dial_string(char *dialstring)
Flattens a dial string so that it can be written to/found from PBX extensions.
int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
Adds a bridge role to a channel.
Definition: bridge_roles.c:313
struct parking_lot * lot
Definition: res_parking.h:111
struct parked_user * parking_lot_retrieve_parked_user(struct parking_lot *lot, int target)
Determine if there is a parked user in a parking space and pull it from the parking lot if there is...
Utility functions.
struct parked_user * parking_lot_inspect_parked_user(struct parking_lot *lot, int target)
Determine if there is a parked user in a parking space and return it if there is. ...
int next_space
Definition: res_parking.h:93
int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
Set a role option on a channel.
Definition: bridge_roles.c:375
int parking_channel_set_roles(struct ast_channel *chan, struct parking_lot *lot, int force_ringing)
Set necessary bridge roles on a channel that is about to enter a parking lot.
int parking_lot_remove_if_unused(struct parking_lot *lot)
Remove a parking lot from the usable lists if it is no longer involved in any calls and no configurat...
Definition: res_parking.c:400
char * parker_dial_string
Definition: res_parking.h:109
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int parking_space
Definition: res_parking.h:107
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
enum park_call_resolution resolution
Definition: res_parking.h:112
unsigned int comebacktoorigin
Definition: res_parking.h:74
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
Structure that contains information about a bridge.
Definition: bridge.h:349
struct ast_flags * ast_bridge_features_ds_get(struct ast_channel *chan)
Get DTMF feature flags from the channel.
Definition: bridge_basic.c:268
int comeback_goto(struct parked_user *pu, struct parking_lot *lot)
Set a channel's position in the PBX after timeout using the parking lot settings. ...
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
struct ast_bridge * bridge_parking_new(struct parking_lot *bridge_lot)
Create a new parking bridge.
unsigned int parkfindnext
Definition: res_parking.h:71
Support for logging to various files, console and syslog Configuration in file logger.conf.
Basic bridge subclass API.
structure to hold users read from users.conf
Structure used to handle boolean flags.
Definition: utils.h:199
int parkedcalltransfers
Definition: res_parking.h:76
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
Set the channel to next execute the specified dialplan location.
Definition: pbx.c:6969
int parkedcallrecording
Definition: res_parking.h:79
const ast_string_field mohclass
Definition: res_parking.h:89
Call Parking Resource Internal API.
Call Parking and Pickup API Includes code and algorithms from the Zapata library. ...
struct ast_bridge * parking_lot_get_bridge(struct parking_lot *lot)
Get a reference to a parking lot's bridge. If it doesn't exist, create it and get a reference...
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
struct ast_channel * chan
Definition: res_parking.h:104
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct parking_lot_cfg * cfg
Definition: res_parking.h:96