Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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  }
114 
115  current_target = original_target;
116 
117  wrap = lot->cfg->parking_start;
118 
119  i = ao2_iterator_init(lot->parked_users, 0);
120  while ((user = ao2_iterator_next(&i))) {
121  /* Increment the wrap on each pass until we find an empty space */
122  if (wrap == user->parking_space) {
123  wrap += 1;
124  }
125 
126  if (user->parking_space < current_target) {
127  /* It's lower than the anticipated target, so we haven't reached the target yet. */
128  ao2_ref(user, -1);
129  continue;
130  }
131 
132  if (user->parking_space > current_target) {
133  /* The current target is usable because all items below have been read and the next target is higher than the one we want. */
134  ao2_ref(user, -1);
135  break;
136  }
137 
138  /* We found one already parked here. */
139  current_target += 1;
140  ao2_ref(user, -1);
141  }
143 
144  if (current_target <= lot->cfg->parking_stop) {
145  return current_target;
146  }
147 
148  if (wrap <= lot->cfg->parking_stop) {
149  return wrap;
150  }
151 
152  return -1;
153 }
154 
155 static int retrieve_parked_user_targeted(void *obj, void *arg, int flags)
156 {
157  int *target = arg;
158  struct parked_user *user = obj;
159  if (user->parking_space == *target) {
160  return CMP_MATCH;
161  }
162 
163  return 0;
164 }
165 
167 {
168  struct parked_user *user;
169 
170  if (target < 0) {
171  user = ao2_callback(lot->parked_users, 0, NULL, NULL);
172  } else {
173  user = ao2_callback(lot->parked_users, 0, retrieve_parked_user_targeted, &target);
174  }
175 
176  if (!user) {
177  return NULL;
178  }
179 
180  return user;
181 }
182 
184 {
186 
187  if (target < 0) {
188  user = ao2_callback(lot->parked_users, 0, NULL, NULL);
189  } else {
191  }
192 
193  if (!user) {
194  return NULL;
195  }
196 
197  ao2_lock(user);
198  if (user->resolution != PARK_UNSET) {
199  /* Abandon. Something else has resolved the parked user before we got to it. */
200  ao2_unlock(user);
201  return NULL;
202  }
203 
205  user->resolution = PARK_ANSWERED;
206  ao2_unlock(user);
207 
209 
210  /* Bump the ref count by 1 since the RAII_VAR will eat the reference otherwise */
211  ao2_ref(user, +1);
212  return user;
213 }
214 
215 void parked_call_retrieve_enable_features(struct ast_channel *chan, struct parking_lot *lot, int recipient_mode)
216 {
217  /* Enabling features here should be additive to features that are already on the channel. */
218  struct ast_flags feature_flags = { 0 };
219  struct ast_flags *existing_features;
220 
221  ast_channel_lock(chan);
222  existing_features = ast_bridge_features_ds_get(chan);
223  if (existing_features) {
224  feature_flags = *existing_features;
225  }
226 
227  if (lot->cfg->parkedcalltransfers & recipient_mode) {
228  ast_set_flag(&feature_flags, AST_FEATURE_REDIRECT);
229  }
230 
231  if (lot->cfg->parkedcallreparking & recipient_mode) {
232  ast_set_flag(&feature_flags, AST_FEATURE_PARKCALL);
233  }
234 
235  if (lot->cfg->parkedcallhangup & recipient_mode) {
236  ast_set_flag(&feature_flags, AST_FEATURE_DISCONNECT);
237  }
238 
239  if (lot->cfg->parkedcallrecording & recipient_mode) {
240  ast_set_flag(&feature_flags, AST_FEATURE_AUTOMIXMON);
241  }
242 
243  ast_bridge_features_ds_set(chan, &feature_flags);
244  ast_channel_unlock(chan);
245 
246  return;
247 }
248 
249 void flatten_dial_string(char *dialstring)
250 {
251  int i;
252 
253  for (i = 0; dialstring[i]; i++) {
254  if (dialstring[i] == '/') {
255  /* The underscore is the flattest character of all. */
256  dialstring[i] = '_';
257  }
258  }
259 }
260 
261 int comeback_goto(struct parked_user *pu, struct parking_lot *lot)
262 {
263  struct ast_channel *chan = pu->chan;
264  char *peername_flat = ast_strdupa(pu->parker_dial_string);
265 
266  /* Flatten the peername so that it can be used for performing the timeout PBX operations */
267  flatten_dial_string(peername_flat);
268 
269  if (lot->cfg->comebacktoorigin) {
270  if (ast_exists_extension(chan, PARK_DIAL_CONTEXT, peername_flat, 1, NULL)) {
271  ast_async_goto(chan, PARK_DIAL_CONTEXT, peername_flat, 1);
272  return 0;
273  } else {
274  ast_log(LOG_ERROR, "Can not start %s at %s,%s,1 because extension does not exist. Terminating call.\n",
275  ast_channel_name(chan), PARK_DIAL_CONTEXT, peername_flat);
276  return -1;
277  }
278  }
279 
280  if (ast_exists_extension(chan, lot->cfg->comebackcontext, peername_flat, 1, NULL)) {
281  ast_async_goto(chan, lot->cfg->comebackcontext, peername_flat, 1);
282  return 0;
283  }
284 
285  if (ast_exists_extension(chan, lot->cfg->comebackcontext, "s", 1, NULL)) {
286  ast_verb(2, "Could not start %s at %s,%s,1. Using '[email protected]%s' instead.\n", ast_channel_name(chan),
287  lot->cfg->comebackcontext, peername_flat, lot->cfg->comebackcontext);
288  ast_async_goto(chan, lot->cfg->comebackcontext, "s", 1);
289  return 0;
290  }
291 
292  ast_verb(2, "Can not start %s at %s,%s,1 and exten '[email protected]%s' does not exist. Using '[email protected]'\n",
293  ast_channel_name(chan),
294  lot->cfg->comebackcontext, peername_flat, lot->cfg->comebackcontext);
295  ast_async_goto(chan, "default", "s", 1);
296 
297  return 0;
298 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
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&#39;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
#define ast_set_flag(p, flag)
Definition: utils.h:70
struct ao2_container * parked_users
Definition: res_parking.h:95
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
#define PARK_DIAL_CONTEXT
Definition: res_parking.h:37
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
#define ao2_unlock(a)
Definition: astobj2.h:730
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:317
#define NULL
Definition: resample.c:96
struct parking_lot * lot
Definition: res_parking.h:111
#define ast_verb(level,...)
Definition: logger.h:463
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.
#define ast_strlen_zero(foo)
Definition: strings.h:52
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:379
#define ast_log
Definition: astobj2.c:42
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.
#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:911
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
int parking_space
Definition: res_parking.h:107
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ao2_lock(a)
Definition: astobj2.h:718
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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:4179
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:357
static int retrieve_parked_user_targeted(void *obj, void *arg, int flags)
#define LOG_ERROR
Definition: logger.h:285
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&#39;s position in the PBX after timeout using the parking lot settings. ...
#define ao2_unlink(container, obj)
Definition: astobj2.h:1598
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
struct ast_bridge * bridge_parking_new(struct parking_lot *bridge_lot)
Create a new parking bridge.
#define ast_channel_unlock(chan)
Definition: channel.h:2946
unsigned int parkfindnext
Definition: res_parking.h:71
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
Support for logging to various files, console and syslog Configuration in file logger.conf.
struct parked_user * user
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
const char * ast_channel_name(const struct ast_channel *chan)
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:7011
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&#39;s bridge. If it doesn&#39;t exist, create it and get a reference...
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