2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Routines implementing call features as call pickup, parking and transfer
23 * \author Mark Spencer <markster@digium.com>
27 <depend>chan_local</depend>
32 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
42 #include <sys/signal.h>
43 #include <netinet/in.h>
45 #include "asterisk/lock.h"
46 #include "asterisk/file.h"
47 #include "asterisk/logger.h"
48 #include "asterisk/channel.h"
49 #include "asterisk/pbx.h"
50 #include "asterisk/options.h"
51 #include "asterisk/causes.h"
52 #include "asterisk/module.h"
53 #include "asterisk/translate.h"
54 #include "asterisk/app.h"
55 #include "asterisk/say.h"
56 #include "asterisk/features.h"
57 #include "asterisk/musiconhold.h"
58 #include "asterisk/config.h"
59 #include "asterisk/cli.h"
60 #include "asterisk/manager.h"
61 #include "asterisk/utils.h"
62 #include "asterisk/adsi.h"
63 #include "asterisk/devicestate.h"
64 #include "asterisk/monitor.h"
65 #include "asterisk/global_datastores.h"
67 #define DEFAULT_PARK_TIME 45000
68 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
69 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
70 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
72 #define AST_MAX_WATCHERS 256
75 AST_FEATURE_FLAG_NEEDSDTMF
= (1 << 0),
76 AST_FEATURE_FLAG_ONPEER
= (1 << 1),
77 AST_FEATURE_FLAG_ONSELF
= (1 << 2),
78 AST_FEATURE_FLAG_BYCALLEE
= (1 << 3),
79 AST_FEATURE_FLAG_BYCALLER
= (1 << 4),
80 AST_FEATURE_FLAG_BYBOTH
= (3 << 3),
83 static char *parkedcall
= "ParkedCall";
85 static int parkaddhints
= 0; /*!< Add parking hints automatically */
86 static int parkingtime
= DEFAULT_PARK_TIME
; /*!< No more than 45 seconds parked before you do something with them */
87 static char parking_con
[AST_MAX_EXTENSION
]; /*!< Context for which parking is made accessible */
88 static char parking_con_dial
[AST_MAX_EXTENSION
]; /*!< Context for dialback for parking (KLUDGE) */
89 static char parking_ext
[AST_MAX_EXTENSION
]; /*!< Extension you type to park the call */
90 static char pickup_ext
[AST_MAX_EXTENSION
]; /*!< Call pickup extension */
91 static char parkmohclass
[MAX_MUSICCLASS
]; /*!< Music class used for parking */
92 static int parking_start
; /*!< First available extension for parking */
93 static int parking_stop
; /*!< Last available extension for parking */
95 static char courtesytone
[256]; /*!< Courtesy tone */
96 static int parkedplay
= 0; /*!< Who to play the courtesy tone to */
97 static char xfersound
[256]; /*!< Call transfer sound */
98 static char xferfailsound
[256]; /*!< Call transfer failure sound */
100 static int parking_offset
;
101 static int parkfindnext
;
105 static int transferdigittimeout
;
106 static int featuredigittimeout
;
108 static int atxfernoanswertimeout
;
110 static char *registrar
= "res_features"; /*!< Registrar for operations */
112 /* module and CLI command definitions */
113 static char *synopsis
= "Answer a parked call";
115 static char *descrip
= "ParkedCall(exten):"
116 "Used to connect to a parked call. This application is always\n"
117 "registered internally and does not need to be explicitly added\n"
118 "into the dialplan, although you should include the 'parkedcalls'\n"
121 static char *parkcall
= "Park";
123 static char *synopsis2
= "Park yourself";
125 static char *descrip2
= "Park():"
126 "Used to park yourself (typically in combination with a supervised\n"
127 "transfer to know the parking space). This application is always\n"
128 "registered internally and does not need to be explicitly added\n"
129 "into the dialplan, although you should include the 'parkedcalls'\n"
130 "context (or the context specified in features.conf).\n\n"
131 "If you set the PARKINGEXTEN variable to an extension in your\n"
132 "parking context, park() will park the call on that extension, unless\n"
133 "it already exists. In that case, execution will continue at next\n"
136 static struct ast_app
*monitor_app
= NULL
;
137 static int monitor_ok
= 1;
140 struct ast_channel
*chan
; /*!< Parking channel */
141 struct timeval start
; /*!< Time the parking started */
142 int parkingnum
; /*!< Parking lot */
143 char parkingexten
[AST_MAX_EXTENSION
]; /*!< If set beforehand, parking extension used for this call */
144 char context
[AST_MAX_CONTEXT
]; /*!< Where to go if our parking time expires */
145 char exten
[AST_MAX_EXTENSION
];
147 int parkingtime
; /*!< Maximum length in parking lot before return */
150 unsigned char moh_trys
;
151 struct parkeduser
*next
;
154 static struct parkeduser
*parkinglot
;
156 AST_MUTEX_DEFINE_STATIC(parking_lock
); /*!< protects all static variables above */
158 static pthread_t parking_thread
;
160 char *ast_parking_ext(void)
165 char *ast_pickup_ext(void)
170 struct ast_bridge_thread_obj
172 struct ast_bridge_config bconfig
;
173 struct ast_channel
*chan
;
174 struct ast_channel
*peer
;
179 /*! \brief store context, priority and extension */
180 static void set_c_e_p(struct ast_channel
*chan
, const char *context
, const char *ext
, int pri
)
182 ast_copy_string(chan
->context
, context
, sizeof(chan
->context
));
183 ast_copy_string(chan
->exten
, ext
, sizeof(chan
->exten
));
184 chan
->priority
= pri
;
187 static void check_goto_on_transfer(struct ast_channel
*chan
)
189 struct ast_channel
*xferchan
;
190 const char *val
= pbx_builtin_getvar_helper(chan
, "GOTO_ON_BLINDXFR");
191 char *x
, *goto_on_transfer
;
194 if (ast_strlen_zero(val
))
197 goto_on_transfer
= ast_strdupa(val
);
199 if (!(xferchan
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, chan
->name
)))
202 for (x
= goto_on_transfer
; x
&& *x
; x
++) {
206 /* Make formats okay */
207 xferchan
->readformat
= chan
->readformat
;
208 xferchan
->writeformat
= chan
->writeformat
;
209 ast_channel_masquerade(xferchan
, chan
);
210 ast_parseable_goto(xferchan
, goto_on_transfer
);
211 xferchan
->_state
= AST_STATE_UP
;
212 ast_clear_flag(xferchan
, AST_FLAGS_ALL
);
213 xferchan
->_softhangup
= 0;
214 if ((f
= ast_read(xferchan
))) {
217 ast_pbx_start(xferchan
);
219 ast_hangup(xferchan
);
223 static struct ast_channel
*ast_feature_request_and_dial(struct ast_channel
*caller
, const char *type
, int format
, void *data
, int timeout
, int *outstate
, const char *cid_num
, const char *cid_name
, const char *language
);
226 static void *ast_bridge_call_thread(void *data
)
228 struct ast_bridge_thread_obj
*tobj
= data
;
230 tobj
->chan
->appl
= "Transferred Call";
231 tobj
->chan
->data
= tobj
->peer
->name
;
232 tobj
->peer
->appl
= "Transferred Call";
233 tobj
->peer
->data
= tobj
->chan
->name
;
235 ast_bridge_call(tobj
->peer
, tobj
->chan
, &tobj
->bconfig
);
236 ast_hangup(tobj
->chan
);
237 ast_hangup(tobj
->peer
);
238 bzero(tobj
, sizeof(*tobj
)); /*! \todo XXX for safety */
243 static void ast_bridge_call_thread_launch(void *data
)
247 struct sched_param sched
;
249 pthread_attr_init(&attr
);
250 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
251 ast_pthread_create(&thread
, &attr
,ast_bridge_call_thread
, data
);
252 pthread_attr_destroy(&attr
);
253 memset(&sched
, 0, sizeof(sched
));
254 pthread_setschedparam(thread
, SCHED_RR
, &sched
);
257 static int adsi_announce_park(struct ast_channel
*chan
, char *parkingexten
)
260 int justify
[5] = {ADSI_JUST_CENT
, ADSI_JUST_CENT
, ADSI_JUST_CENT
, ADSI_JUST_CENT
};
262 char *message
[5] = {NULL
, NULL
, NULL
, NULL
, NULL
};
264 snprintf(tmp
, sizeof(tmp
), "Parked on %s", parkingexten
);
266 res
= ast_adsi_load_session(chan
, NULL
, 0, 1);
269 return ast_adsi_print(chan
, message
, justify
, 1);
272 /*! \brief Notify metermaids that we've changed an extension */
273 static void notify_metermaids(char *exten
, char *context
)
275 if (option_debug
> 3)
276 ast_log(LOG_DEBUG
, "Notification of state change to metermaids %s@%s\n", exten
, context
);
278 /* Send notification to devicestate subsystem */
279 ast_device_state_changed("park:%s@%s", exten
, context
);
283 /*! \brief metermaids callback from devicestate.c */
284 static int metermaidstate(const char *data
)
286 int res
= AST_DEVICE_INVALID
;
287 char *context
= ast_strdupa(data
);
290 exten
= strsep(&context
, "@");
294 if (option_debug
> 3)
295 ast_log(LOG_DEBUG
, "Checking state of exten %s in context %s\n", exten
, context
);
297 res
= ast_exists_extension(NULL
, context
, exten
, 1, NULL
);
300 return AST_DEVICE_NOT_INUSE
;
302 return AST_DEVICE_INUSE
;
305 static int park_call_full(struct ast_channel
*chan
, struct ast_channel
*peer
, int timeout
, int *extout
, char *orig_chan_name
)
307 struct parkeduser
*pu
, *cur
;
308 int i
, x
= -1, parking_range
;
309 struct ast_context
*con
;
310 const char *parkingexten
;
312 /* Allocate memory for parking data */
313 if (!(pu
= ast_calloc(1, sizeof(*pu
))))
316 /* Lock parking lot */
317 ast_mutex_lock(&parking_lock
);
318 /* Check for channel variable PARKINGEXTEN */
319 parkingexten
= pbx_builtin_getvar_helper(chan
, "PARKINGEXTEN");
320 if (!ast_strlen_zero(parkingexten
)) {
321 /*!\note The API forces us to specify a numeric parking slot, even
322 * though the architecture would tend to support non-numeric extensions
323 * (as are possible with SIP, for example). Hence, we enforce that
324 * limitation here. If extout was not numeric, we could permit
325 * arbitrary non-numeric extensions.
327 if (sscanf(parkingexten
, "%d", &x
) != 1 || x
< 0) {
328 ast_log(LOG_WARNING
, "PARKINGEXTEN does not indicate a valid parking slot: '%s'.\n", parkingexten
);
329 ast_mutex_unlock(&parking_lock
);
331 return 1; /* Continue execution if possible */
333 snprintf(pu
->parkingexten
, sizeof(pu
->parkingexten
), "%d", x
);
335 if (ast_exists_extension(NULL
, parking_con
, pu
->parkingexten
, 1, NULL
)) {
336 ast_mutex_unlock(&parking_lock
);
338 ast_log(LOG_WARNING
, "Requested parking extension already exists: %s@%s\n", parkingexten
, parking_con
);
339 return 1; /* Continue execution if possible */
342 /* Select parking space within range */
343 parking_range
= parking_stop
- parking_start
+1;
344 for (i
= 0; i
< parking_range
; i
++) {
345 x
= (i
+ parking_offset
) % parking_range
+ parking_start
;
348 if (cur
->parkingnum
== x
)
356 if (!(i
< parking_range
)) {
357 ast_log(LOG_WARNING
, "No more parking spaces\n");
359 ast_mutex_unlock(&parking_lock
);
362 /* Set pointer for next parking */
364 parking_offset
= x
- parking_start
+ 1;
365 snprintf(pu
->parkingexten
, sizeof(pu
->parkingexten
), "%d", x
);
368 chan
->appl
= "Parked Call";
373 /* Put the parked channel on hold if we have two different channels */
375 ast_indicate_data(pu
->chan
, AST_CONTROL_HOLD
,
376 S_OR(parkmohclass
, NULL
),
377 !ast_strlen_zero(parkmohclass
) ? strlen(parkmohclass
) + 1 : 0);
380 pu
->start
= ast_tvnow();
382 pu
->parkingtime
= (timeout
> 0) ? timeout
: parkingtime
;
387 /* This is so ugly that it hurts, but implementing get_base_channel() on local channels
388 could have ugly side effects. We could have transferer<->local,1<->local,2<->parking
389 and we need the callback name to be that of transferer. Since local,1/2 have the same
390 name we can be tricky and just grab the bridged channel from the other side of the local
392 if (!strcasecmp(peer
->tech
->type
, "Local")) {
393 struct ast_channel
*tmpchan
, *base_peer
;
394 char other_side
[AST_CHANNEL_NAME
];
396 ast_copy_string(other_side
, peer
->name
, sizeof(other_side
));
397 if ((c
= strrchr(other_side
, ','))) {
400 if ((tmpchan
= ast_get_channel_by_name_locked(other_side
))) {
401 if ((base_peer
= ast_bridged_channel(tmpchan
))) {
402 ast_copy_string(pu
->peername
, base_peer
->name
, sizeof(pu
->peername
));
404 ast_channel_unlock(tmpchan
);
407 ast_copy_string(pu
->peername
, peer
->name
, sizeof(pu
->peername
));
411 /* Remember what had been dialed, so that if the parking
412 expires, we try to come back to the same place */
413 ast_copy_string(pu
->context
, S_OR(chan
->macrocontext
, chan
->context
), sizeof(pu
->context
));
414 ast_copy_string(pu
->exten
, S_OR(chan
->macroexten
, chan
->exten
), sizeof(pu
->exten
));
415 pu
->priority
= chan
->macropriority
? chan
->macropriority
: chan
->priority
;
416 pu
->next
= parkinglot
;
419 /* If parking a channel directly, don't quiet yet get parking running on it */
422 ast_mutex_unlock(&parking_lock
);
423 /* Wake up the (presumably select()ing) thread */
424 pthread_kill(parking_thread
, SIGURG
);
425 if (option_verbose
> 1)
426 ast_verbose(VERBOSE_PREFIX_2
"Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu
->chan
->name
, pu
->parkingnum
, parking_con
, pu
->context
, pu
->exten
, pu
->priority
, (pu
->parkingtime
/1000));
428 manager_event(EVENT_FLAG_CALL
, "ParkedCall",
434 "CallerIDName: %s\r\n",
435 pu
->parkingexten
, pu
->chan
->name
, peer
? peer
->name
: "",
436 (long)pu
->start
.tv_sec
+ (long)(pu
->parkingtime
/1000) - (long)time(NULL
),
437 S_OR(pu
->chan
->cid
.cid_num
, "<unknown>"),
438 S_OR(pu
->chan
->cid
.cid_name
, "<unknown>")
441 if (peer
&& adsipark
&& ast_adsi_available(peer
)) {
442 adsi_announce_park(peer
, pu
->parkingexten
); /* Only supports parking numbers */
443 ast_adsi_unload_session(peer
);
446 con
= ast_context_find(parking_con
);
448 con
= ast_context_create(NULL
, parking_con
, registrar
);
449 if (!con
) /* Still no context? Bad */
450 ast_log(LOG_ERROR
, "Parking context '%s' does not exist and unable to create\n", parking_con
);
451 /* Tell the peer channel the number of the parking space */
452 if (peer
&& (ast_strlen_zero(orig_chan_name
) || !strcasecmp(peer
->name
, orig_chan_name
))) { /* Only say number if it's a number and the channel hasn't been masqueraded away */
453 /* Make sure we don't start saying digits to the channel being parked */
454 ast_set_flag(peer
, AST_FLAG_MASQ_NOSTREAM
);
455 ast_say_digits(peer
, pu
->parkingnum
, "", peer
->language
);
456 ast_clear_flag(peer
, AST_FLAG_MASQ_NOSTREAM
);
459 if (!ast_add_extension2(con
, 1, pu
->parkingexten
, 1, NULL
, NULL
, parkedcall
, strdup(pu
->parkingexten
), ast_free
, registrar
))
460 notify_metermaids(pu
->parkingexten
, parking_con
);
462 if (pu
->notquiteyet
) {
463 /* Wake up parking thread if we're really done */
464 ast_indicate_data(pu
->chan
, AST_CONTROL_HOLD
,
465 S_OR(parkmohclass
, NULL
),
466 !ast_strlen_zero(parkmohclass
) ? strlen(parkmohclass
) + 1 : 0);
468 pthread_kill(parking_thread
, SIGURG
);
473 /*! \brief Park a call
474 \note We put the user in the parking list, then wake up the parking thread to be sure it looks
475 after these channels too */
476 int ast_park_call(struct ast_channel
*chan
, struct ast_channel
*peer
, int timeout
, int *extout
)
478 return park_call_full(chan
, peer
, timeout
, extout
, NULL
);
481 int ast_masq_park_call(struct ast_channel
*rchan
, struct ast_channel
*peer
, int timeout
, int *extout
)
483 struct ast_channel
*chan
;
485 char *orig_chan_name
= NULL
;
488 /* Make a new, fake channel that we'll use to masquerade in the real one */
489 if (!(chan
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, rchan
->accountcode
, rchan
->exten
, rchan
->context
, rchan
->amaflags
, "Parked/%s",rchan
->name
))) {
490 ast_log(LOG_WARNING
, "Unable to create parked channel\n");
494 /* Make formats okay */
495 chan
->readformat
= rchan
->readformat
;
496 chan
->writeformat
= rchan
->writeformat
;
497 ast_channel_masquerade(chan
, rchan
);
499 /* Setup the extensions and such */
500 set_c_e_p(chan
, rchan
->context
, rchan
->exten
, rchan
->priority
);
502 /* Make the masq execute */
507 orig_chan_name
= ast_strdupa(chan
->name
);
509 park_status
= park_call_full(chan
, peer
, timeout
, extout
, orig_chan_name
);
510 if (park_status
== 1) {
511 /* would be nice to play: "invalid parking extension" */
520 #define FEATURE_RETURN_HANGUP -1
521 #define FEATURE_RETURN_SUCCESSBREAK 0
522 #define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
523 #define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
524 #define FEATURE_RETURN_PASSDIGITS 21
525 #define FEATURE_RETURN_STOREDIGITS 22
526 #define FEATURE_RETURN_SUCCESS 23
527 #define FEATURE_RETURN_KEEPTRYING 24
529 #define FEATURE_SENSE_CHAN (1 << 0)
530 #define FEATURE_SENSE_PEER (1 << 1)
533 * set caller and callee according to the direction
535 static void set_peers(struct ast_channel
**caller
, struct ast_channel
**callee
,
536 struct ast_channel
*peer
, struct ast_channel
*chan
, int sense
)
538 if (sense
== FEATURE_SENSE_PEER
) {
547 /*! \brief support routing for one touch call parking */
548 static int builtin_parkcall(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
, void *data
)
550 struct ast_channel
*parker
;
551 struct ast_channel
*parkee
;
553 struct ast_module_user
*u
;
555 u
= ast_module_user_add(chan
);
557 set_peers(&parker
, &parkee
, peer
, chan
, sense
);
558 /* Setup the exten/priority to be s/1 since we don't know
559 where this call should return */
560 strcpy(chan
->exten
, "s");
562 if (chan
->_state
!= AST_STATE_UP
)
563 res
= ast_answer(chan
);
565 res
= ast_safe_sleep(chan
, 1000);
567 res
= ast_park_call(parkee
, parker
, 0, NULL
);
569 ast_module_user_remove(u
);
572 if (sense
== FEATURE_SENSE_CHAN
)
573 res
= AST_PBX_NO_HANGUP_PEER
;
575 res
= AST_PBX_KEEPALIVE
;
581 static int builtin_automonitor(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
, void *data
)
583 char *caller_chan_id
= NULL
, *callee_chan_id
= NULL
, *args
= NULL
, *touch_filename
= NULL
;
586 struct ast_channel
*caller_chan
, *callee_chan
;
589 ast_log(LOG_ERROR
,"Cannot record the call. The monitor application is disabled.\n");
593 if (!monitor_app
&& !(monitor_app
= pbx_findapp("Monitor"))) {
595 ast_log(LOG_ERROR
,"Cannot record the call. The monitor application is disabled.\n");
599 set_peers(&caller_chan
, &callee_chan
, peer
, chan
, sense
);
601 if (!ast_strlen_zero(courtesytone
)) {
602 if (ast_autoservice_start(callee_chan
))
604 if (ast_stream_and_wait(caller_chan
, courtesytone
, caller_chan
->language
, "")) {
605 ast_log(LOG_WARNING
, "Failed to play courtesy tone!\n");
606 ast_autoservice_stop(callee_chan
);
609 if (ast_autoservice_stop(callee_chan
))
613 if (callee_chan
->monitor
) {
614 if (option_verbose
> 3)
615 ast_verbose(VERBOSE_PREFIX_3
"User hit '%s' to stop recording call.\n", code
);
616 ast_monitor_stop(callee_chan
, 1);
617 return FEATURE_RETURN_SUCCESS
;
620 if (caller_chan
&& callee_chan
) {
621 const char *touch_format
= pbx_builtin_getvar_helper(caller_chan
, "TOUCH_MONITOR_FORMAT");
622 const char *touch_monitor
= pbx_builtin_getvar_helper(caller_chan
, "TOUCH_MONITOR");
625 touch_format
= pbx_builtin_getvar_helper(callee_chan
, "TOUCH_MONITOR_FORMAT");
628 touch_monitor
= pbx_builtin_getvar_helper(callee_chan
, "TOUCH_MONITOR");
631 len
= strlen(touch_monitor
) + 50;
633 touch_filename
= alloca(len
);
634 snprintf(touch_filename
, len
, "auto-%ld-%s", (long)time(NULL
), touch_monitor
);
635 snprintf(args
, len
, "%s|%s|m", (touch_format
) ? touch_format
: "wav", touch_filename
);
637 caller_chan_id
= ast_strdupa(S_OR(caller_chan
->cid
.cid_num
, caller_chan
->name
));
638 callee_chan_id
= ast_strdupa(S_OR(callee_chan
->cid
.cid_num
, callee_chan
->name
));
639 len
= strlen(caller_chan_id
) + strlen(callee_chan_id
) + 50;
641 touch_filename
= alloca(len
);
642 snprintf(touch_filename
, len
, "auto-%ld-%s-%s", (long)time(NULL
), caller_chan_id
, callee_chan_id
);
643 snprintf(args
, len
, "%s|%s|m", S_OR(touch_format
, "wav"), touch_filename
);
646 for( x
= 0; x
< strlen(args
); x
++) {
651 if (option_verbose
> 3)
652 ast_verbose(VERBOSE_PREFIX_3
"User hit '%s' to record call. filename: %s\n", code
, args
);
654 pbx_exec(callee_chan
, monitor_app
, args
);
655 pbx_builtin_setvar_helper(callee_chan
, "TOUCH_MONITOR_OUTPUT", touch_filename
);
656 pbx_builtin_setvar_helper(caller_chan
, "TOUCH_MONITOR_OUTPUT", touch_filename
);
658 return FEATURE_RETURN_SUCCESS
;
661 ast_log(LOG_NOTICE
,"Cannot record the call. One or both channels have gone away.\n");
665 static int builtin_disconnect(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
, void *data
)
667 if (option_verbose
> 3)
668 ast_verbose(VERBOSE_PREFIX_3
"User hit '%s' to disconnect call.\n", code
);
669 return FEATURE_RETURN_HANGUP
;
672 static int finishup(struct ast_channel
*chan
)
674 ast_indicate(chan
, AST_CONTROL_UNHOLD
);
676 return ast_autoservice_stop(chan
);
679 /*! \brief Find the context for the transfer */
680 static const char *real_ctx(struct ast_channel
*transferer
, struct ast_channel
*transferee
)
682 const char *s
= pbx_builtin_getvar_helper(transferer
, "TRANSFER_CONTEXT");
683 if (ast_strlen_zero(s
))
684 s
= pbx_builtin_getvar_helper(transferee
, "TRANSFER_CONTEXT");
685 if (ast_strlen_zero(s
)) /* Use the non-macro context to transfer the call XXX ? */
686 s
= transferer
->macrocontext
;
687 if (ast_strlen_zero(s
))
688 s
= transferer
->context
;
692 static int builtin_blindtransfer(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
, void *data
)
694 struct ast_channel
*transferer
;
695 struct ast_channel
*transferee
;
696 const char *transferer_real_context
;
700 set_peers(&transferer
, &transferee
, peer
, chan
, sense
);
701 transferer_real_context
= real_ctx(transferer
, transferee
);
702 /* Start autoservice on chan while we talk to the originator */
703 ast_autoservice_start(transferee
);
704 ast_indicate(transferee
, AST_CONTROL_HOLD
);
706 memset(xferto
, 0, sizeof(xferto
));
709 res
= ast_stream_and_wait(transferer
, "pbx-transfer", transferer
->language
, AST_DIGIT_ANY
);
711 finishup(transferee
);
712 return -1; /* error ? */
714 if (res
> 0) /* If they've typed a digit already, handle it */
715 xferto
[0] = (char) res
;
717 ast_stopstream(transferer
);
718 res
= ast_app_dtget(transferer
, transferer_real_context
, xferto
, sizeof(xferto
), 100, transferdigittimeout
);
719 if (res
< 0) { /* hangup, would be 0 for invalid and 1 for valid */
720 finishup(transferee
);
723 if (!strcmp(xferto
, ast_parking_ext())) {
724 res
= finishup(transferee
);
727 else if (!ast_park_call(transferee
, transferer
, 0, NULL
)) { /* success */
728 /* We return non-zero, but tell the PBX not to hang the channel when
729 the thread dies -- We have to be careful now though. We are responsible for
730 hanging up the channel, else it will never be hung up! */
732 return (transferer
== peer
) ? AST_PBX_KEEPALIVE
: AST_PBX_NO_HANGUP_PEER
;
734 ast_log(LOG_WARNING
, "Unable to park call %s\n", transferee
->name
);
736 /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
737 } else if (ast_exists_extension(transferee
, transferer_real_context
, xferto
, 1, transferer
->cid
.cid_num
)) {
738 pbx_builtin_setvar_helper(peer
, "BLINDTRANSFER", transferee
->name
);
739 pbx_builtin_setvar_helper(chan
, "BLINDTRANSFER", peer
->name
);
740 res
=finishup(transferee
);
741 if (!transferer
->cdr
) {
742 transferer
->cdr
=ast_cdr_alloc();
744 ast_cdr_init(transferer
->cdr
, transferer
); /* initilize our channel's cdr */
745 ast_cdr_start(transferer
->cdr
);
748 if (transferer
->cdr
) {
749 ast_cdr_setdestchan(transferer
->cdr
, transferee
->name
);
750 ast_cdr_setapp(transferer
->cdr
, "BLINDTRANSFER","");
752 if (!transferee
->pbx
) {
753 /* Doh! Use our handy async_goto functions */
754 if (option_verbose
> 2)
755 ast_verbose(VERBOSE_PREFIX_3
"Transferring %s to '%s' (context %s) priority 1\n"
756 ,transferee
->name
, xferto
, transferer_real_context
);
757 if (ast_async_goto(transferee
, transferer_real_context
, xferto
, 1))
758 ast_log(LOG_WARNING
, "Async goto failed :-(\n");
761 /* Set the channel's new extension, since it exists, using transferer context */
762 set_c_e_p(transferee
, transferer_real_context
, xferto
, 0);
764 check_goto_on_transfer(transferer
);
767 if (option_verbose
> 2)
768 ast_verbose(VERBOSE_PREFIX_3
"Unable to find extension '%s' in context '%s'\n", xferto
, transferer_real_context
);
770 if (ast_stream_and_wait(transferer
, xferfailsound
, transferer
->language
, AST_DIGIT_ANY
) < 0 ) {
771 finishup(transferee
);
774 ast_stopstream(transferer
);
775 res
= finishup(transferee
);
777 if (option_verbose
> 1)
778 ast_verbose(VERBOSE_PREFIX_2
"Hungup during autoservice stop on '%s'\n", transferee
->name
);
781 return FEATURE_RETURN_SUCCESS
;
784 static int check_compat(struct ast_channel
*c
, struct ast_channel
*newchan
)
786 if (ast_channel_make_compatible(c
, newchan
) < 0) {
787 ast_log(LOG_WARNING
, "Had to drop call because I couldn't make %s compatible with %s\n",
788 c
->name
, newchan
->name
);
795 static int builtin_atxfer(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
, void *data
)
797 struct ast_channel
*transferer
;
798 struct ast_channel
*transferee
;
799 const char *transferer_real_context
;
800 char xferto
[256] = "";
803 struct ast_channel
*newchan
;
804 struct ast_channel
*xferchan
;
805 struct ast_bridge_thread_obj
*tobj
;
806 struct ast_bridge_config bconfig
;
809 struct ast_datastore
*features_datastore
;
810 struct ast_dial_features
*dialfeatures
= NULL
;
813 ast_log(LOG_DEBUG
, "Executing Attended Transfer %s, %s (sense=%d) \n", chan
->name
, peer
->name
, sense
);
814 set_peers(&transferer
, &transferee
, peer
, chan
, sense
);
815 transferer_real_context
= real_ctx(transferer
, transferee
);
816 /* Start autoservice on chan while we talk to the originator */
817 ast_autoservice_start(transferee
);
818 ast_indicate(transferee
, AST_CONTROL_HOLD
);
821 res
= ast_stream_and_wait(transferer
, "pbx-transfer", transferer
->language
, AST_DIGIT_ANY
);
823 finishup(transferee
);
826 if (res
> 0) /* If they've typed a digit already, handle it */
827 xferto
[0] = (char) res
;
829 /* this is specific of atxfer */
830 res
= ast_app_dtget(transferer
, transferer_real_context
, xferto
, sizeof(xferto
), 100, transferdigittimeout
);
831 if (res
< 0) { /* hangup, would be 0 for invalid and 1 for valid */
832 finishup(transferee
);
836 ast_log(LOG_WARNING
, "Did not read data.\n");
837 finishup(transferee
);
838 if (ast_stream_and_wait(transferer
, "beeperr", transferer
->language
, ""))
840 return FEATURE_RETURN_SUCCESS
;
843 /* valid extension, res == 1 */
844 if (!ast_exists_extension(transferer
, transferer_real_context
, xferto
, 1, transferer
->cid
.cid_num
)) {
845 ast_log(LOG_WARNING
, "Extension %s does not exist in context %s\n",xferto
,transferer_real_context
);
846 finishup(transferee
);
847 if (ast_stream_and_wait(transferer
, "beeperr", transferer
->language
, ""))
849 return FEATURE_RETURN_SUCCESS
;
853 snprintf(xferto
+ l
, sizeof(xferto
) - l
, "@%s", transferer_real_context
); /* append context */
854 newchan
= ast_feature_request_and_dial(transferer
, "Local", ast_best_codec(transferer
->nativeformats
),
855 xferto
, atxfernoanswertimeout
, &outstate
, transferer
->cid
.cid_num
, transferer
->cid
.cid_name
, transferer
->language
);
857 /* If we are the callee and we are being transferred, after the masquerade
858 * caller features will really be the original callee features */
859 ast_channel_lock(transferee
);
860 if ((features_datastore
= ast_channel_datastore_find(transferee
, &dial_features_info
, NULL
))) {
861 dialfeatures
= features_datastore
->data
;
863 ast_channel_unlock(transferee
);
865 if (dialfeatures
&& !dialfeatures
->is_caller
) {
866 ast_copy_flags(&(config
->features_caller
), &(dialfeatures
->features_callee
), AST_FLAGS_ALL
);
869 ast_indicate(transferer
, -1);
871 finishup(transferee
);
872 /* any reason besides user requested cancel and busy triggers the failed sound */
873 if (outstate
!= AST_CONTROL_UNHOLD
&& outstate
!= AST_CONTROL_BUSY
&&
874 ast_stream_and_wait(transferer
, xferfailsound
, transferer
->language
, ""))
876 return FEATURE_RETURN_SUCCESS
;
879 if (check_compat(transferer
, newchan
)) {
880 /* we do mean transferee here, NOT transferer */
881 finishup(transferee
);
884 memset(&bconfig
,0,sizeof(struct ast_bridge_config
));
885 ast_set_flag(&(bconfig
.features_caller
), AST_FEATURE_DISCONNECT
);
886 ast_set_flag(&(bconfig
.features_callee
), AST_FEATURE_DISCONNECT
);
887 res
= ast_bridge_call(transferer
, newchan
, &bconfig
);
888 if (newchan
->_softhangup
|| !transferer
->_softhangup
) {
890 if (ast_stream_and_wait(transferer
, xfersound
, transferer
->language
, ""))
891 ast_log(LOG_WARNING
, "Failed to play transfer sound!\n");
892 finishup(transferee
);
893 transferer
->_softhangup
= 0;
894 return FEATURE_RETURN_SUCCESS
;
897 if (check_compat(transferee
, newchan
)) {
898 finishup(transferee
);
902 ast_indicate(transferee
, AST_CONTROL_UNHOLD
);
904 if ((ast_autoservice_stop(transferee
) < 0)
905 || (ast_waitfordigit(transferee
, 100) < 0)
906 || (ast_waitfordigit(newchan
, 100) < 0)
907 || ast_check_hangup(transferee
)
908 || ast_check_hangup(newchan
)) {
913 xferchan
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, "Transfered/%s", transferee
->name
);
918 /* Make formats okay */
919 xferchan
->visible_indication
= transferer
->visible_indication
;
920 xferchan
->readformat
= transferee
->readformat
;
921 xferchan
->writeformat
= transferee
->writeformat
;
922 ast_channel_masquerade(xferchan
, transferee
);
923 ast_explicit_goto(xferchan
, transferee
->context
, transferee
->exten
, transferee
->priority
);
924 xferchan
->_state
= AST_STATE_UP
;
925 ast_clear_flag(xferchan
, AST_FLAGS_ALL
);
926 xferchan
->_softhangup
= 0;
928 if ((f
= ast_read(xferchan
)))
931 newchan
->_state
= AST_STATE_UP
;
932 ast_clear_flag(newchan
, AST_FLAGS_ALL
);
933 newchan
->_softhangup
= 0;
935 tobj
= ast_calloc(1, sizeof(struct ast_bridge_thread_obj
));
937 ast_hangup(xferchan
);
942 /* For the case where the transfer target is being connected with the original
943 caller store the target's original features, and apply to the bridge */
944 ast_channel_lock(newchan
);
945 if ((features_datastore
= ast_channel_datastore_find(newchan
, &dial_features_info
, NULL
))) {
946 dialfeatures
= features_datastore
->data
;
948 ast_channel_unlock(newchan
);
951 ast_copy_flags(&(config
->features_callee
), &(dialfeatures
->features_callee
), AST_FLAGS_ALL
);
954 tobj
->chan
= newchan
;
955 tobj
->peer
= xferchan
;
956 tobj
->bconfig
= *config
;
958 if (ast_stream_and_wait(newchan
, xfersound
, newchan
->language
, ""))
959 ast_log(LOG_WARNING
, "Failed to play transfer sound!\n");
960 ast_bridge_call_thread_launch(tobj
);
961 return -1; /* XXX meaning the channel is bridged ? */
965 /* add atxfer and automon as undefined so you can only use em if you configure them */
966 #define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
968 AST_RWLOCK_DEFINE_STATIC(features_lock
);
970 static struct ast_call_feature builtin_features
[] =
972 { AST_FEATURE_REDIRECT
, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer
, AST_FEATURE_FLAG_NEEDSDTMF
, "" },
973 { AST_FEATURE_REDIRECT
, "Attended Transfer", "atxfer", "", "", builtin_atxfer
, AST_FEATURE_FLAG_NEEDSDTMF
, "" },
974 { AST_FEATURE_AUTOMON
, "One Touch Monitor", "automon", "", "", builtin_automonitor
, AST_FEATURE_FLAG_NEEDSDTMF
, "" },
975 { AST_FEATURE_DISCONNECT
, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect
, AST_FEATURE_FLAG_NEEDSDTMF
, "" },
976 { AST_FEATURE_PARKCALL
, "Park Call", "parkcall", "", "", builtin_parkcall
, AST_FEATURE_FLAG_NEEDSDTMF
, "" },
980 static AST_LIST_HEAD_STATIC(feature_list
,ast_call_feature
);
982 /*! \brief register new feature into feature_list*/
983 void ast_register_feature(struct ast_call_feature
*feature
)
986 ast_log(LOG_NOTICE
,"You didn't pass a feature!\n");
990 AST_LIST_LOCK(&feature_list
);
991 AST_LIST_INSERT_HEAD(&feature_list
,feature
,feature_entry
);
992 AST_LIST_UNLOCK(&feature_list
);
994 if (option_verbose
>= 2)
995 ast_verbose(VERBOSE_PREFIX_2
"Registered Feature '%s'\n",feature
->sname
);
998 /*! \brief unregister feature from feature_list */
999 void ast_unregister_feature(struct ast_call_feature
*feature
)
1004 AST_LIST_LOCK(&feature_list
);
1005 AST_LIST_REMOVE(&feature_list
,feature
,feature_entry
);
1006 AST_LIST_UNLOCK(&feature_list
);
1010 /*! \brief Remove all features in the list */
1011 static void ast_unregister_features(void)
1013 struct ast_call_feature
*feature
;
1015 AST_LIST_LOCK(&feature_list
);
1016 while ((feature
= AST_LIST_REMOVE_HEAD(&feature_list
,feature_entry
)))
1018 AST_LIST_UNLOCK(&feature_list
);
1021 /*! \brief find a feature by name */
1022 static struct ast_call_feature
*find_dynamic_feature(const char *name
)
1024 struct ast_call_feature
*tmp
;
1026 AST_LIST_TRAVERSE(&feature_list
, tmp
, feature_entry
) {
1027 if (!strcasecmp(tmp
->sname
, name
))
1034 /*! \brief exec an app by feature */
1035 static int feature_exec_app(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
, void *data
)
1037 struct ast_app
*app
;
1038 struct ast_call_feature
*feature
= data
;
1039 struct ast_channel
*work
, *idle
;
1042 if (!feature
) { /* shouldn't ever happen! */
1043 ast_log(LOG_NOTICE
, "Found feature before, but at execing we've lost it??\n");
1047 if (sense
== FEATURE_SENSE_CHAN
) {
1048 if (!ast_test_flag(feature
, AST_FEATURE_FLAG_BYCALLER
))
1049 return FEATURE_RETURN_KEEPTRYING
;
1050 if (ast_test_flag(feature
, AST_FEATURE_FLAG_ONSELF
)) {
1058 if (!ast_test_flag(feature
, AST_FEATURE_FLAG_BYCALLEE
))
1059 return FEATURE_RETURN_KEEPTRYING
;
1060 if (ast_test_flag(feature
, AST_FEATURE_FLAG_ONSELF
)) {
1069 if (!(app
= pbx_findapp(feature
->app
))) {
1070 ast_log(LOG_WARNING
, "Could not find application (%s)\n", feature
->app
);
1074 ast_autoservice_start(idle
);
1076 if (!ast_strlen_zero(feature
->moh_class
))
1077 ast_moh_start(idle
, feature
->moh_class
, NULL
);
1079 res
= pbx_exec(work
, app
, feature
->app_args
);
1081 if (!ast_strlen_zero(feature
->moh_class
))
1084 ast_autoservice_stop(idle
);
1086 if (res
== AST_PBX_KEEPALIVE
)
1087 return FEATURE_RETURN_PBX_KEEPALIVE
;
1088 else if (res
== AST_PBX_NO_HANGUP_PEER
)
1089 return FEATURE_RETURN_NO_HANGUP_PEER
;
1091 return FEATURE_RETURN_SUCCESSBREAK
;
1093 return FEATURE_RETURN_SUCCESS
; /*! \todo XXX should probably return res */
1096 static void unmap_features(void)
1100 ast_rwlock_wrlock(&features_lock
);
1101 for (x
= 0; x
< FEATURES_COUNT
; x
++)
1102 strcpy(builtin_features
[x
].exten
, builtin_features
[x
].default_exten
);
1103 ast_rwlock_unlock(&features_lock
);
1106 static int remap_feature(const char *name
, const char *value
)
1110 ast_rwlock_wrlock(&features_lock
);
1111 for (x
= 0; x
< FEATURES_COUNT
; x
++) {
1112 if (strcasecmp(builtin_features
[x
].sname
, name
))
1115 ast_copy_string(builtin_features
[x
].exten
, value
, sizeof(builtin_features
[x
].exten
));
1119 ast_rwlock_unlock(&features_lock
);
1124 static int ast_feature_interpret(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
, char *code
, int sense
)
1127 struct ast_flags features
;
1128 int res
= FEATURE_RETURN_PASSDIGITS
;
1129 struct ast_call_feature
*feature
;
1130 const char *dynamic_features
;
1133 if (sense
== FEATURE_SENSE_CHAN
) {
1134 ast_copy_flags(&features
, &(config
->features_caller
), AST_FLAGS_ALL
);
1135 dynamic_features
= pbx_builtin_getvar_helper(chan
, "DYNAMIC_FEATURES");
1137 ast_copy_flags(&features
, &(config
->features_callee
), AST_FLAGS_ALL
);
1138 dynamic_features
= pbx_builtin_getvar_helper(peer
, "DYNAMIC_FEATURES");
1140 if (option_debug
> 2)
1141 ast_log(LOG_DEBUG
, "Feature interpret: chan=%s, peer=%s, code=%s, sense=%d, features=%d dynamic=%s\n", chan
->name
, peer
->name
, code
, sense
, features
.flags
, dynamic_features
);
1143 ast_rwlock_rdlock(&features_lock
);
1144 for (x
= 0; x
< FEATURES_COUNT
; x
++) {
1145 if ((ast_test_flag(&features
, builtin_features
[x
].feature_mask
)) &&
1146 !ast_strlen_zero(builtin_features
[x
].exten
)) {
1147 /* Feature is up for consideration */
1148 if (!strcmp(builtin_features
[x
].exten
, code
)) {
1149 res
= builtin_features
[x
].operation(chan
, peer
, config
, code
, sense
, NULL
);
1151 } else if (!strncmp(builtin_features
[x
].exten
, code
, strlen(code
))) {
1152 if (res
== FEATURE_RETURN_PASSDIGITS
)
1153 res
= FEATURE_RETURN_STOREDIGITS
;
1157 ast_rwlock_unlock(&features_lock
);
1159 if (ast_strlen_zero(dynamic_features
))
1162 tmp
= ast_strdupa(dynamic_features
);
1164 while ((tok
= strsep(&tmp
, "#"))) {
1165 AST_LIST_LOCK(&feature_list
);
1166 if (!(feature
= find_dynamic_feature(tok
))) {
1167 AST_LIST_UNLOCK(&feature_list
);
1171 /* Feature is up for consideration */
1172 if (!strcmp(feature
->exten
, code
)) {
1173 if (option_verbose
> 2)
1174 ast_verbose(VERBOSE_PREFIX_3
" Feature Found: %s exten: %s\n",feature
->sname
, tok
);
1175 res
= feature
->operation(chan
, peer
, config
, code
, sense
, feature
);
1176 if (res
!= FEATURE_RETURN_KEEPTRYING
) {
1177 AST_LIST_UNLOCK(&feature_list
);
1180 res
= FEATURE_RETURN_PASSDIGITS
;
1181 } else if (!strncmp(feature
->exten
, code
, strlen(code
)))
1182 res
= FEATURE_RETURN_STOREDIGITS
;
1184 AST_LIST_UNLOCK(&feature_list
);
1190 static void set_config_flags(struct ast_channel
*chan
, struct ast_channel
*peer
, struct ast_bridge_config
*config
)
1194 ast_clear_flag(config
, AST_FLAGS_ALL
);
1196 ast_rwlock_rdlock(&features_lock
);
1197 for (x
= 0; x
< FEATURES_COUNT
; x
++) {
1198 if (!ast_test_flag(builtin_features
+ x
, AST_FEATURE_FLAG_NEEDSDTMF
))
1201 if (ast_test_flag(&(config
->features_caller
), builtin_features
[x
].feature_mask
))
1202 ast_set_flag(config
, AST_BRIDGE_DTMF_CHANNEL_0
);
1204 if (ast_test_flag(&(config
->features_callee
), builtin_features
[x
].feature_mask
))
1205 ast_set_flag(config
, AST_BRIDGE_DTMF_CHANNEL_1
);
1207 ast_rwlock_unlock(&features_lock
);
1209 if (chan
&& peer
&& !(ast_test_flag(config
, AST_BRIDGE_DTMF_CHANNEL_0
) && ast_test_flag(config
, AST_BRIDGE_DTMF_CHANNEL_1
))) {
1210 const char *dynamic_features
= pbx_builtin_getvar_helper(chan
, "DYNAMIC_FEATURES");
1212 if (dynamic_features
) {
1213 char *tmp
= ast_strdupa(dynamic_features
);
1215 struct ast_call_feature
*feature
;
1217 /* while we have a feature */
1218 while ((tok
= strsep(&tmp
, "#"))) {
1219 AST_LIST_LOCK(&feature_list
);
1220 if ((feature
= find_dynamic_feature(tok
)) && ast_test_flag(feature
, AST_FEATURE_FLAG_NEEDSDTMF
)) {
1221 if (ast_test_flag(feature
, AST_FEATURE_FLAG_BYCALLER
))
1222 ast_set_flag(config
, AST_BRIDGE_DTMF_CHANNEL_0
);
1223 if (ast_test_flag(feature
, AST_FEATURE_FLAG_BYCALLEE
))
1224 ast_set_flag(config
, AST_BRIDGE_DTMF_CHANNEL_1
);
1226 AST_LIST_UNLOCK(&feature_list
);
1232 /*! \todo XXX Check - this is very similar to the code in channel.c */
1233 static struct ast_channel
*ast_feature_request_and_dial(struct ast_channel
*caller
, const char *type
, int format
, void *data
, int timeout
, int *outstate
, const char *cid_num
, const char *cid_name
, const char *language
)
1238 struct ast_channel
*chan
;
1239 struct ast_channel
*monitor_chans
[2];
1240 struct ast_channel
*active_channel
;
1241 int res
= 0, ready
= 0;
1243 if ((chan
= ast_request(type
, format
, data
, &cause
))) {
1244 ast_set_callerid(chan
, cid_num
, cid_name
, cid_num
);
1245 ast_string_field_set(chan
, language
, language
);
1246 ast_channel_inherit_variables(caller
, chan
);
1247 pbx_builtin_setvar_helper(chan
, "TRANSFERERNAME", caller
->name
);
1249 if (!ast_call(chan
, data
, timeout
)) {
1250 struct timeval started
;
1252 char *disconnect_code
= NULL
, *dialed_code
= NULL
;
1254 ast_indicate(caller
, AST_CONTROL_RINGING
);
1255 /* support dialing of the featuremap disconnect code while performing an attended tranfer */
1256 ast_rwlock_rdlock(&features_lock
);
1257 for (x
= 0; x
< FEATURES_COUNT
; x
++) {
1258 if (strcasecmp(builtin_features
[x
].sname
, "disconnect"))
1261 disconnect_code
= builtin_features
[x
].exten
;
1262 len
= strlen(disconnect_code
) + 1;
1263 dialed_code
= alloca(len
);
1264 memset(dialed_code
, 0, len
);
1267 ast_rwlock_unlock(&features_lock
);
1269 started
= ast_tvnow();
1271 while (!ast_check_hangup(caller
) && timeout
&& (chan
->_state
!= AST_STATE_UP
)) {
1272 struct ast_frame
*f
= NULL
;
1274 monitor_chans
[0] = caller
;
1275 monitor_chans
[1] = chan
;
1276 active_channel
= ast_waitfor_n(monitor_chans
, 2, &to
);
1278 /* see if the timeout has been violated */
1279 if(ast_tvdiff_ms(ast_tvnow(), started
) > timeout
) {
1280 state
= AST_CONTROL_UNHOLD
;
1281 ast_log(LOG_NOTICE
, "We exceeded our AT-timeout\n");
1282 break; /*doh! timeout*/
1285 if (!active_channel
)
1288 if (chan
&& (chan
== active_channel
)){
1290 if (f
== NULL
) { /*doh! where'd he go?*/
1291 state
= AST_CONTROL_HANGUP
;
1296 if (f
->frametype
== AST_FRAME_CONTROL
|| f
->frametype
== AST_FRAME_DTMF
|| f
->frametype
== AST_FRAME_TEXT
) {
1297 if (f
->subclass
== AST_CONTROL_RINGING
) {
1298 state
= f
->subclass
;
1299 if (option_verbose
> 2)
1300 ast_verbose( VERBOSE_PREFIX_3
"%s is ringing\n", chan
->name
);
1301 ast_indicate(caller
, AST_CONTROL_RINGING
);
1302 } else if ((f
->subclass
== AST_CONTROL_BUSY
) || (f
->subclass
== AST_CONTROL_CONGESTION
)) {
1303 state
= f
->subclass
;
1304 if (option_verbose
> 2)
1305 ast_verbose( VERBOSE_PREFIX_3
"%s is busy\n", chan
->name
);
1306 ast_indicate(caller
, AST_CONTROL_BUSY
);
1310 } else if (f
->subclass
== AST_CONTROL_ANSWER
) {
1311 /* This is what we are hoping for */
1312 state
= f
->subclass
;
1317 } else if (f
->subclass
!= -1) {
1318 ast_log(LOG_NOTICE
, "Don't know what to do about control frame: %d\n", f
->subclass
);
1320 /* else who cares */
1323 } else if (caller
&& (active_channel
== caller
)) {
1324 f
= ast_read(caller
);
1325 if (f
== NULL
) { /*doh! where'd he go?*/
1326 if (caller
->_softhangup
&& !chan
->_softhangup
) {
1327 /* make this a blind transfer */
1331 state
= AST_CONTROL_HANGUP
;
1336 if (f
->frametype
== AST_FRAME_DTMF
) {
1337 dialed_code
[x
++] = f
->subclass
;
1338 dialed_code
[x
] = '\0';
1339 if (strlen(dialed_code
) == len
) {
1341 } else if (x
&& strncmp(dialed_code
, disconnect_code
, x
)) {
1343 dialed_code
[x
] = '\0';
1345 if (*dialed_code
&& !strcmp(dialed_code
, disconnect_code
)) {
1346 /* Caller Canceled the call */
1347 state
= AST_CONTROL_UNHOLD
;
1358 ast_log(LOG_NOTICE
, "Unable to call channel %s/%s\n", type
, (char *)data
);
1360 ast_log(LOG_NOTICE
, "Unable to request channel %s/%s\n", type
, (char *)data
);
1362 case AST_CAUSE_BUSY
:
1363 state
= AST_CONTROL_BUSY
;
1365 case AST_CAUSE_CONGESTION
:
1366 state
= AST_CONTROL_CONGESTION
;
1371 ast_indicate(caller
, -1);
1372 if (chan
&& ready
) {
1373 if (chan
->_state
== AST_STATE_UP
)
1374 state
= AST_CONTROL_ANSWER
;
1390 int ast_bridge_call(struct ast_channel
*chan
,struct ast_channel
*peer
,struct ast_bridge_config
*config
)
1392 /* Copy voice back and forth between the two channels. Give the peer
1393 the ability to transfer calls with '#<extension' syntax. */
1394 struct ast_frame
*f
;
1395 struct ast_channel
*who
;
1396 char chan_featurecode
[FEATURE_MAX_LEN
+ 1]="";
1397 char peer_featurecode
[FEATURE_MAX_LEN
+ 1]="";
1398 char orig_channame
[AST_MAX_EXTENSION
];
1399 char orig_peername
[AST_MAX_EXTENSION
];
1405 struct ast_option_header
*aoh
;
1406 struct ast_bridge_config backup_config
;
1407 struct ast_cdr
*bridge_cdr
= NULL
;
1409 memset(&backup_config
, 0, sizeof(backup_config
));
1411 config
->start_time
= ast_tvnow();
1414 pbx_builtin_setvar_helper(chan
, "BRIDGEPEER", peer
->name
);
1415 pbx_builtin_setvar_helper(peer
, "BRIDGEPEER", chan
->name
);
1417 pbx_builtin_setvar_helper(chan
, "BLINDTRANSFER", NULL
);
1420 const char *monitor_exec
;
1421 struct ast_channel
*src
= NULL
;
1423 if (!(monitor_app
= pbx_findapp("Monitor")))
1426 if ((monitor_exec
= pbx_builtin_getvar_helper(chan
, "AUTO_MONITOR")))
1428 else if ((monitor_exec
= pbx_builtin_getvar_helper(peer
, "AUTO_MONITOR")))
1430 if (monitor_app
&& src
) {
1431 char *tmp
= ast_strdupa(monitor_exec
);
1432 pbx_exec(src
, monitor_app
, tmp
);
1436 set_config_flags(chan
, peer
, config
);
1437 config
->firstpass
= 1;
1439 /* Answer if need be */
1440 if (ast_answer(chan
))
1443 ast_copy_string(orig_channame
,chan
->name
,sizeof(orig_channame
));
1444 ast_copy_string(orig_peername
,peer
->name
,sizeof(orig_peername
));
1446 if (!chan
->cdr
|| (chan
->cdr
&& !ast_test_flag(chan
->cdr
, AST_CDR_FLAG_POST_DISABLED
))) {
1449 ast_set_flag(chan
->cdr
, AST_CDR_FLAG_MAIN
);
1450 ast_cdr_update(chan
);
1451 bridge_cdr
= ast_cdr_dup(chan
->cdr
);
1452 ast_copy_string(bridge_cdr
->lastapp
, chan
->appl
, sizeof(bridge_cdr
->lastapp
));
1453 ast_copy_string(bridge_cdr
->lastdata
, chan
->data
, sizeof(bridge_cdr
->lastdata
));
1455 /* better yet, in a xfer situation, find out why the chan cdr got zapped (pun unintentional) */
1456 bridge_cdr
= ast_cdr_alloc(); /* this should be really, really rare/impossible? */
1457 ast_copy_string(bridge_cdr
->channel
, chan
->name
, sizeof(bridge_cdr
->channel
));
1458 ast_copy_string(bridge_cdr
->dstchannel
, peer
->name
, sizeof(bridge_cdr
->dstchannel
));
1459 ast_copy_string(bridge_cdr
->uniqueid
, chan
->uniqueid
, sizeof(bridge_cdr
->uniqueid
));
1460 ast_copy_string(bridge_cdr
->lastapp
, chan
->appl
, sizeof(bridge_cdr
->lastapp
));
1461 ast_copy_string(bridge_cdr
->lastdata
, chan
->data
, sizeof(bridge_cdr
->lastdata
));
1462 ast_cdr_setcid(bridge_cdr
, chan
);
1463 bridge_cdr
->disposition
= (chan
->_state
== AST_STATE_UP
) ? AST_CDR_ANSWERED
: AST_CDR_NULL
;
1464 bridge_cdr
->amaflags
= chan
->amaflags
? chan
->amaflags
: ast_default_amaflags
;
1465 ast_copy_string(bridge_cdr
->accountcode
, chan
->accountcode
, sizeof(bridge_cdr
->accountcode
));
1466 /* Destination information */
1467 ast_copy_string(bridge_cdr
->dst
, chan
->exten
, sizeof(bridge_cdr
->dst
));
1468 ast_copy_string(bridge_cdr
->dcontext
, chan
->context
, sizeof(bridge_cdr
->dcontext
));
1470 bridge_cdr
->start
= peer
->cdr
->start
;
1471 ast_copy_string(bridge_cdr
->userfield
, peer
->cdr
->userfield
, sizeof(bridge_cdr
->userfield
));
1473 ast_cdr_start(bridge_cdr
);
1476 ast_cdr_answer(bridge_cdr
);
1477 ast_cdr_answer(chan
->cdr
); /* for the sake of cli status checks */
1478 ast_set_flag(chan
->cdr
, AST_CDR_FLAG_BRIDGED
);
1480 ast_set_flag(peer
->cdr
, AST_CDR_FLAG_BRIDGED
);
1484 struct ast_channel
*other
; /* used later */
1486 res
= ast_channel_bridge(chan
, peer
, config
, &f
, &who
);
1488 if (config
->feature_timer
) {
1489 /* Update time limit for next pass */
1490 diff
= ast_tvdiff_ms(ast_tvnow(), config
->start_time
);
1491 config
->feature_timer
-= diff
;
1493 /* Running on backup config, meaning a feature might be being
1494 activated, but that's no excuse to keep things going
1496 if (backup_config
.feature_timer
&& ((backup_config
.feature_timer
-= diff
) <= 0)) {
1498 ast_log(LOG_DEBUG
, "Timed out, realtime this time!\n");
1499 config
->feature_timer
= 0;
1505 } else if (config
->feature_timer
<= 0) {
1506 /* Not *really* out of time, just out of time for
1507 digits to come in for features. */
1509 ast_log(LOG_DEBUG
, "Timed out for feature!\n");
1510 if (!ast_strlen_zero(peer_featurecode
)) {
1511 ast_dtmf_stream(chan
, peer
, peer_featurecode
, 0);
1512 memset(peer_featurecode
, 0, sizeof(peer_featurecode
));
1514 if (!ast_strlen_zero(chan_featurecode
)) {
1515 ast_dtmf_stream(peer
, chan
, chan_featurecode
, 0);
1516 memset(chan_featurecode
, 0, sizeof(chan_featurecode
));
1520 hasfeatures
= !ast_strlen_zero(chan_featurecode
) || !ast_strlen_zero(peer_featurecode
);
1522 /* Restore original (possibly time modified) bridge config */
1523 memcpy(config
, &backup_config
, sizeof(struct ast_bridge_config
));
1524 memset(&backup_config
, 0, sizeof(backup_config
));
1526 hadfeatures
= hasfeatures
;
1527 /* Continue as we were */
1530 /* The bridge returned without a frame and there is a feature in progress.
1531 * However, we don't think the feature has quite yet timed out, so just
1532 * go back into the bridge. */
1536 if (config
->feature_timer
<=0) {
1537 /* We ran out of time */
1538 config
->feature_timer
= 0;
1548 if (!ast_test_flag(chan
, AST_FLAG_ZOMBIE
) && !ast_test_flag(peer
, AST_FLAG_ZOMBIE
) && !ast_check_hangup(chan
) && !ast_check_hangup(peer
))
1549 ast_log(LOG_WARNING
, "Bridge failed on channels %s and %s\n", chan
->name
, peer
->name
);
1553 if (!f
|| (f
->frametype
== AST_FRAME_CONTROL
&&
1554 (f
->subclass
== AST_CONTROL_HANGUP
|| f
->subclass
== AST_CONTROL_BUSY
||
1555 f
->subclass
== AST_CONTROL_CONGESTION
) ) ) {
1559 /* many things should be sent to the 'other' channel */
1560 other
= (who
== chan
) ? peer
: chan
;
1561 if (f
->frametype
== AST_FRAME_CONTROL
) {
1562 switch (f
->subclass
) {
1563 case AST_CONTROL_RINGING
:
1564 case AST_CONTROL_FLASH
:
1566 ast_indicate(other
, f
->subclass
);
1568 case AST_CONTROL_HOLD
:
1569 case AST_CONTROL_UNHOLD
:
1570 ast_indicate_data(other
, f
->subclass
, f
->data
, f
->datalen
);
1572 case AST_CONTROL_OPTION
:
1574 /* Forward option Requests */
1575 if (aoh
&& aoh
->flag
== AST_OPTION_FLAG_REQUEST
) {
1576 ast_channel_setoption(other
, ntohs(aoh
->option
), aoh
->data
,
1577 f
->datalen
- sizeof(struct ast_option_header
), 0);
1581 } else if (f
->frametype
== AST_FRAME_DTMF_BEGIN
) {
1583 } else if (f
->frametype
== AST_FRAME_DTMF
) {
1587 hadfeatures
= hasfeatures
;
1588 /* This cannot overrun because the longest feature is one shorter than our buffer */
1590 sense
= FEATURE_SENSE_CHAN
;
1591 featurecode
= chan_featurecode
;
1593 sense
= FEATURE_SENSE_PEER
;
1594 featurecode
= peer_featurecode
;
1596 /*! append the event to featurecode. we rely on the string being zero-filled, and
1597 * not overflowing it.
1598 * \todo XXX how do we guarantee the latter ?
1600 featurecode
[strlen(featurecode
)] = f
->subclass
;
1601 /* Get rid of the frame before we start doing "stuff" with the channels */
1604 config
->feature_timer
= backup_config
.feature_timer
;
1605 res
= ast_feature_interpret(chan
, peer
, config
, featurecode
, sense
);
1607 case FEATURE_RETURN_PASSDIGITS
:
1608 ast_dtmf_stream(other
, who
, featurecode
, 0);
1610 case FEATURE_RETURN_SUCCESS
:
1611 memset(featurecode
, 0, sizeof(chan_featurecode
));
1614 if (res
>= FEATURE_RETURN_PASSDIGITS
) {
1618 hasfeatures
= !ast_strlen_zero(chan_featurecode
) || !ast_strlen_zero(peer_featurecode
);
1619 if (hadfeatures
&& !hasfeatures
) {
1620 /* Restore backup */
1621 memcpy(config
, &backup_config
, sizeof(struct ast_bridge_config
));
1622 memset(&backup_config
, 0, sizeof(struct ast_bridge_config
));
1623 } else if (hasfeatures
) {
1625 /* Backup configuration */
1626 memcpy(&backup_config
, config
, sizeof(struct ast_bridge_config
));
1627 /* Setup temporary config options */
1628 config
->play_warning
= 0;
1629 ast_clear_flag(&(config
->features_caller
), AST_FEATURE_PLAY_WARNING
);
1630 ast_clear_flag(&(config
->features_callee
), AST_FEATURE_PLAY_WARNING
);
1631 config
->warning_freq
= 0;
1632 config
->warning_sound
= NULL
;
1633 config
->end_sound
= NULL
;
1634 config
->start_sound
= NULL
;
1635 config
->firstpass
= 0;
1637 config
->start_time
= ast_tvnow();
1638 config
->feature_timer
= featuredigittimeout
;
1640 ast_log(LOG_DEBUG
, "Set time limit to %ld\n", config
->feature_timer
);
1647 /* obey the NoCDR() wishes. */
1648 if (!chan
->cdr
|| (chan
->cdr
&& !ast_test_flag(chan
->cdr
, AST_CDR_FLAG_POST_DISABLED
))) {
1650 ast_cdr_end(bridge_cdr
);
1652 ast_cdr_detach(bridge_cdr
);
1654 /* just in case, these channels get bridged again before hangup */
1656 ast_cdr_specialized_reset(chan
->cdr
,0);
1658 ast_cdr_specialized_reset(peer
->cdr
,0);
1664 static void post_manager_event(const char *s
, char *parkingexten
, struct ast_channel
*chan
)
1666 manager_event(EVENT_FLAG_CALL
, s
,
1670 "CallerIDName: %s\r\n\r\n",
1673 S_OR(chan
->cid
.cid_num
, "<unknown>"),
1674 S_OR(chan
->cid
.cid_name
, "<unknown>")
1678 /*! \brief Take care of parked calls and unpark them if needed */
1679 static void *do_parking_thread(void *ignore
)
1681 fd_set rfds
, efds
; /* results from previous select, to be preserved across loops. */
1686 struct parkeduser
*pu
, *pl
, *pt
= NULL
;
1687 int ms
= -1; /* select timeout, uninitialized */
1688 int max
= -1; /* max fd, none there yet */
1689 fd_set nrfds
, nefds
; /* args for the next select */
1693 ast_mutex_lock(&parking_lock
);
1696 /* navigate the list with prev-cur pointers to support removals */
1698 struct ast_channel
*chan
= pu
->chan
; /* shorthand */
1699 int tms
; /* timeout for this item */
1700 int x
; /* fd index in channel */
1701 struct ast_context
*con
;
1703 if (pu
->notquiteyet
) { /* Pretend this one isn't here yet */
1708 tms
= ast_tvdiff_ms(ast_tvnow(), pu
->start
);
1709 if (tms
> pu
->parkingtime
) {
1710 ast_indicate(chan
, AST_CONTROL_UNHOLD
);
1711 /* Get chan, exten from derived kludge */
1712 if (pu
->peername
[0]) {
1713 char *peername
= ast_strdupa(pu
->peername
);
1714 char *cp
= strrchr(peername
, '-');
1717 con
= ast_context_find(parking_con_dial
);
1719 con
= ast_context_create(NULL
, parking_con_dial
, registrar
);
1721 ast_log(LOG_ERROR
, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial
);
1724 char returnexten
[AST_MAX_EXTENSION
];
1725 struct ast_datastore
*features_datastore
;
1726 struct ast_dial_features
*dialfeatures
= NULL
;
1728 ast_channel_lock(chan
);
1730 if ((features_datastore
= ast_channel_datastore_find(chan
, &dial_features_info
, NULL
)))
1731 dialfeatures
= features_datastore
->data
;
1733 ast_channel_unlock(chan
);
1736 snprintf(returnexten
, sizeof(returnexten
), "%s|30|%s", peername
, dialfeatures
->options
);
1737 else /* Existing default */
1738 snprintf(returnexten
, sizeof(returnexten
), "%s|30|t", peername
);
1740 ast_add_extension2(con
, 1, peername
, 1, NULL
, NULL
, "Dial", strdup(returnexten
), ast_free
, registrar
);
1742 set_c_e_p(chan
, parking_con_dial
, peername
, 1);
1744 /* They've been waiting too long, send them back to where they came. Theoretically they
1745 should have their original extensions and such, but we copy to be on the safe side */
1746 set_c_e_p(chan
, pu
->context
, pu
->exten
, pu
->priority
);
1749 post_manager_event("ParkedCallTimeOut", pu
->parkingexten
, chan
);
1751 if (option_verbose
> 1)
1752 ast_verbose(VERBOSE_PREFIX_2
"Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan
->name
, pu
->parkingnum
, chan
->context
, chan
->exten
, chan
->priority
);
1753 /* Start up the PBX, or hang them up */
1754 if (ast_pbx_start(chan
)) {
1755 ast_log(LOG_WARNING
, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan
->name
);
1758 /* And take them out of the parking lot */
1760 pl
->next
= pu
->next
;
1762 parkinglot
= pu
->next
;
1765 con
= ast_context_find(parking_con
);
1767 if (ast_context_remove_extension2(con
, pt
->parkingexten
, 1, NULL
))
1768 ast_log(LOG_WARNING
, "Whoa, failed to remove the extension!\n");
1770 notify_metermaids(pt
->parkingexten
, parking_con
);
1772 ast_log(LOG_WARNING
, "Whoa, no parking context?\n");
1774 } else { /* still within parking time, process descriptors */
1775 for (x
= 0; x
< AST_MAX_FDS
; x
++) {
1776 struct ast_frame
*f
;
1778 if (chan
->fds
[x
] == -1 || (!FD_ISSET(chan
->fds
[x
], &rfds
) && !FD_ISSET(chan
->fds
[x
], &efds
)))
1779 continue; /* nothing on this descriptor */
1781 if (FD_ISSET(chan
->fds
[x
], &efds
))
1782 ast_set_flag(chan
, AST_FLAG_EXCEPTION
);
1784 ast_clear_flag(chan
, AST_FLAG_EXCEPTION
);
1787 /* See if they need servicing */
1789 if (!f
|| (f
->frametype
== AST_FRAME_CONTROL
&& f
->subclass
== AST_CONTROL_HANGUP
)) {
1792 post_manager_event("ParkedCallGiveUp", pu
->parkingexten
, chan
);
1794 /* There's a problem, hang them up*/
1795 if (option_verbose
> 1)
1796 ast_verbose(VERBOSE_PREFIX_2
"%s got tired of being parked\n", chan
->name
);
1798 /* And take them out of the parking lot */
1800 pl
->next
= pu
->next
;
1802 parkinglot
= pu
->next
;
1805 con
= ast_context_find(parking_con
);
1807 if (ast_context_remove_extension2(con
, pt
->parkingexten
, 1, NULL
))
1808 ast_log(LOG_WARNING
, "Whoa, failed to remove the extension!\n");
1810 notify_metermaids(pt
->parkingexten
, parking_con
);
1812 ast_log(LOG_WARNING
, "Whoa, no parking context?\n");
1816 /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
1818 if (pu
->moh_trys
< 3 && !chan
->generatordata
) {
1820 ast_log(LOG_DEBUG
, "MOH on parked call stopped by outside source. Restarting.\n");
1821 ast_indicate_data(pu
->chan
, AST_CONTROL_HOLD
,
1822 S_OR(parkmohclass
, NULL
),
1823 !ast_strlen_zero(parkmohclass
) ? strlen(parkmohclass
) + 1 : 0);
1826 goto std
; /*! \todo XXX Ick: jumping into an else statement??? XXX */
1830 if (x
>= AST_MAX_FDS
) {
1831 std
: for (x
=0; x
<AST_MAX_FDS
; x
++) { /* mark fds for next round */
1832 if (chan
->fds
[x
] > -1) {
1833 FD_SET(chan
->fds
[x
], &nrfds
);
1834 FD_SET(chan
->fds
[x
], &nefds
);
1835 if (chan
->fds
[x
] > max
)
1839 /* Keep track of our shortest wait */
1840 if (tms
< ms
|| ms
< 0)
1847 ast_mutex_unlock(&parking_lock
);
1851 struct timeval tv
= ast_samp2tv(ms
, 1000);
1852 /* Wait for something to happen */
1853 ast_select(max
+ 1, &rfds
, NULL
, &efds
, (ms
> -1) ? &tv
: NULL
);
1855 pthread_testcancel();
1857 return NULL
; /* Never reached */
1860 /*! \brief Park a call */
1861 static int park_call_exec(struct ast_channel
*chan
, void *data
)
1863 /* Cache the original channel name in case we get masqueraded in the middle
1864 * of a park--it is still theoretically possible for a transfer to happen before
1865 * we get here, but it is _really_ unlikely */
1866 char *orig_chan_name
= ast_strdupa(chan
->name
);
1867 char orig_exten
[AST_MAX_EXTENSION
];
1868 int orig_priority
= chan
->priority
;
1870 /* Data is unused at the moment but could contain a parking
1871 lot context eventually */
1873 struct ast_module_user
*u
;
1875 u
= ast_module_user_add(chan
);
1877 ast_copy_string(orig_exten
, chan
->exten
, sizeof(orig_exten
));
1879 /* Setup the exten/priority to be s/1 since we don't know
1880 where this call should return */
1881 strcpy(chan
->exten
, "s");
1883 /* Answer if call is not up */
1884 if (chan
->_state
!= AST_STATE_UP
)
1885 res
= ast_answer(chan
);
1886 /* Sleep to allow VoIP streams to settle down */
1888 res
= ast_safe_sleep(chan
, 1000);
1891 res
= park_call_full(chan
, chan
, 0, NULL
, orig_chan_name
);
1892 /* Continue on in the dialplan */
1894 ast_copy_string(chan
->exten
, orig_exten
, sizeof(chan
->exten
));
1895 chan
->priority
= orig_priority
;
1898 res
= AST_PBX_KEEPALIVE
;
1901 ast_module_user_remove(u
);
1906 /*! \brief Pickup parked call */
1907 static int park_exec(struct ast_channel
*chan
, void *data
)
1910 struct ast_module_user
*u
;
1911 struct ast_channel
*peer
=NULL
;
1912 struct parkeduser
*pu
, *pl
=NULL
;
1913 struct ast_context
*con
;
1916 struct ast_bridge_config config
;
1919 ast_log(LOG_WARNING
, "Parkedcall requires an argument (extension number)\n");
1923 u
= ast_module_user_add(chan
);
1925 park
= atoi((char *)data
);
1926 ast_mutex_lock(&parking_lock
);
1929 if (pu
->parkingnum
== park
) {
1931 pl
->next
= pu
->next
;
1933 parkinglot
= pu
->next
;
1939 ast_mutex_unlock(&parking_lock
);
1942 con
= ast_context_find(parking_con
);
1944 if (ast_context_remove_extension2(con
, pu
->parkingexten
, 1, NULL
))
1945 ast_log(LOG_WARNING
, "Whoa, failed to remove the extension!\n");
1947 notify_metermaids(pu
->parkingexten
, parking_con
);
1949 ast_log(LOG_WARNING
, "Whoa, no parking context?\n");
1951 manager_event(EVENT_FLAG_CALL
, "UnParkedCall",
1956 "CallerIDName: %s\r\n",
1957 pu
->parkingexten
, pu
->chan
->name
, chan
->name
,
1958 S_OR(pu
->chan
->cid
.cid_num
, "<unknown>"),
1959 S_OR(pu
->chan
->cid
.cid_name
, "<unknown>")
1964 /* JK02: it helps to answer the channel if not already up */
1965 if (chan
->_state
!= AST_STATE_UP
)
1969 /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
1971 if (!ast_strlen_zero(courtesytone
)) {
1973 ast_indicate(peer
, AST_CONTROL_UNHOLD
);
1974 if (parkedplay
== 0) {
1975 error
= ast_stream_and_wait(chan
, courtesytone
, chan
->language
, "");
1976 } else if (parkedplay
== 1) {
1977 error
= ast_stream_and_wait(peer
, courtesytone
, chan
->language
, "");
1978 } else if (parkedplay
== 2) {
1979 if (!ast_streamfile(chan
, courtesytone
, chan
->language
) &&
1980 !ast_streamfile(peer
, courtesytone
, chan
->language
)) {
1981 /*! \todo XXX we would like to wait on both! */
1982 res
= ast_waitstream(chan
, "");
1984 res
= ast_waitstream(peer
, "");
1990 ast_log(LOG_WARNING
, "Failed to play courtesy tone!\n");
1992 ast_module_user_remove(u
);
1996 ast_indicate(peer
, AST_CONTROL_UNHOLD
);
1998 res
= ast_channel_make_compatible(chan
, peer
);
2000 ast_log(LOG_WARNING
, "Could not make channels %s and %s compatible for bridge\n", chan
->name
, peer
->name
);
2002 ast_module_user_remove(u
);
2005 /* This runs sorta backwards, since we give the incoming channel control, as if it
2006 were the person called. */
2007 if (option_verbose
> 2)
2008 ast_verbose(VERBOSE_PREFIX_3
"Channel %s connected to parked call %d\n", chan
->name
, park
);
2010 pbx_builtin_setvar_helper(chan
, "PARKEDCHANNEL", peer
->name
);
2011 ast_cdr_setdestchan(chan
->cdr
, peer
->name
);
2012 memset(&config
, 0, sizeof(struct ast_bridge_config
));
2013 ast_set_flag(&(config
.features_callee
), AST_FEATURE_REDIRECT
);
2014 ast_set_flag(&(config
.features_caller
), AST_FEATURE_REDIRECT
);
2015 res
= ast_bridge_call(chan
, peer
, &config
);
2017 pbx_builtin_setvar_helper(chan
, "PARKEDCHANNEL", peer
->name
);
2018 ast_cdr_setdestchan(chan
->cdr
, peer
->name
);
2020 /* Simulate the PBX hanging up */
2021 if (res
!= AST_PBX_NO_HANGUP_PEER
)
2023 ast_module_user_remove(u
);
2026 /*! \todo XXX Play a message XXX */
2027 if (ast_stream_and_wait(chan
, "pbx-invalidpark", chan
->language
, ""))
2028 ast_log(LOG_WARNING
, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan
->name
);
2029 if (option_verbose
> 2)
2030 ast_verbose(VERBOSE_PREFIX_3
"Channel %s tried to talk to nonexistent parked call %d\n", chan
->name
, park
);
2034 ast_module_user_remove(u
);
2039 static int handle_showfeatures(int fd
, int argc
, char *argv
[])
2042 struct ast_call_feature
*feature
;
2043 char format
[] = "%-25s %-7s %-7s\n";
2045 ast_cli(fd
, format
, "Builtin Feature", "Default", "Current");
2046 ast_cli(fd
, format
, "---------------", "-------", "-------");
2048 ast_cli(fd
, format
, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
2050 ast_rwlock_rdlock(&features_lock
);
2051 for (i
= 0; i
< FEATURES_COUNT
; i
++)
2052 ast_cli(fd
, format
, builtin_features
[i
].fname
, builtin_features
[i
].default_exten
, builtin_features
[i
].exten
);
2053 ast_rwlock_unlock(&features_lock
);
2056 ast_cli(fd
, format
, "Dynamic Feature", "Default", "Current");
2057 ast_cli(fd
, format
, "---------------", "-------", "-------");
2058 if (AST_LIST_EMPTY(&feature_list
))
2059 ast_cli(fd
, "(none)\n");
2061 AST_LIST_LOCK(&feature_list
);
2062 AST_LIST_TRAVERSE(&feature_list
, feature
, feature_entry
)
2063 ast_cli(fd
, format
, feature
->sname
, "no def", feature
->exten
);
2064 AST_LIST_UNLOCK(&feature_list
);
2066 ast_cli(fd
, "\nCall parking\n");
2067 ast_cli(fd
, "------------\n");
2068 ast_cli(fd
,"%-20s: %s\n", "Parking extension", parking_ext
);
2069 ast_cli(fd
,"%-20s: %s\n", "Parking context", parking_con
);
2070 ast_cli(fd
,"%-20s: %d-%d\n", "Parked call extensions", parking_start
, parking_stop
);
2073 return RESULT_SUCCESS
;
2076 static char showfeatures_help
[] =
2077 "Usage: feature list\n"
2078 " Lists currently configured features.\n";
2080 static int handle_parkedcalls(int fd
, int argc
, char *argv
[])
2082 struct parkeduser
*cur
;
2085 ast_cli(fd
, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
2086 , "Context", "Extension", "Pri", "Timeout");
2088 ast_mutex_lock(&parking_lock
);
2090 for (cur
= parkinglot
; cur
; cur
= cur
->next
) {
2091 ast_cli(fd
, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
2092 ,cur
->parkingexten
, cur
->chan
->name
, cur
->context
, cur
->exten
2093 ,cur
->priority
, cur
->start
.tv_sec
+ (cur
->parkingtime
/1000) - time(NULL
));
2097 ast_mutex_unlock(&parking_lock
);
2098 ast_cli(fd
, "%d parked call%s.\n", numparked
, (numparked
!= 1) ? "s" : "");
2101 return RESULT_SUCCESS
;
2104 static char showparked_help
[] =
2105 "Usage: show parkedcalls\n"
2106 " Lists currently parked calls.\n";
2108 static struct ast_cli_entry cli_show_features_deprecated
= {
2109 { "show", "features", NULL
},
2110 handle_showfeatures
, NULL
,
2113 static struct ast_cli_entry cli_features
[] = {
2114 { { "feature", "show", NULL
},
2115 handle_showfeatures
, "Lists configured features",
2116 showfeatures_help
, NULL
, &cli_show_features_deprecated
},
2118 { { "show", "parkedcalls", NULL
},
2119 handle_parkedcalls
, "Lists parked calls",
2123 /*! \brief Dump lot status */
2124 static int manager_parking_status( struct mansession
*s
, const struct message
*m
)
2126 struct parkeduser
*cur
;
2127 const char *id
= astman_get_header(m
, "ActionID");
2128 char idText
[256] = "";
2130 if (!ast_strlen_zero(id
))
2131 snprintf(idText
, sizeof(idText
), "ActionID: %s\r\n", id
);
2133 astman_send_ack(s
, m
, "Parked calls will follow");
2135 ast_mutex_lock(&parking_lock
);
2137 for (cur
= parkinglot
; cur
; cur
= cur
->next
) {
2138 astman_append(s
, "Event: ParkedCall\r\n"
2144 "CallerIDName: %s\r\n"
2147 cur
->parkingnum
, cur
->chan
->name
, cur
->peername
,
2148 (long) cur
->start
.tv_sec
+ (long) (cur
->parkingtime
/ 1000) - (long) time(NULL
),
2149 S_OR(cur
->chan
->cid
.cid_num
, ""), /* XXX in other places it is <unknown> */
2150 S_OR(cur
->chan
->cid
.cid_name
, ""),
2155 "Event: ParkedCallsComplete\r\n"
2159 ast_mutex_unlock(&parking_lock
);
2161 return RESULT_SUCCESS
;
2164 static char mandescr_park
[] =
2165 "Description: Park a channel.\n"
2166 "Variables: (Names marked with * are required)\n"
2167 " *Channel: Channel name to park\n"
2168 " *Channel2: Channel to announce park info to (and return to if timeout)\n"
2169 " Timeout: Number of milliseconds to wait before callback.\n";
2171 static int manager_park(struct mansession
*s
, const struct message
*m
)
2173 const char *channel
= astman_get_header(m
, "Channel");
2174 const char *channel2
= astman_get_header(m
, "Channel2");
2175 const char *timeout
= astman_get_header(m
, "Timeout");
2180 struct ast_channel
*ch1
, *ch2
;
2182 if (ast_strlen_zero(channel
)) {
2183 astman_send_error(s
, m
, "Channel not specified");
2187 if (ast_strlen_zero(channel2
)) {
2188 astman_send_error(s
, m
, "Channel2 not specified");
2192 ch1
= ast_get_channel_by_name_locked(channel
);
2194 snprintf(buf
, sizeof(buf
), "Channel does not exist: %s", channel
);
2195 astman_send_error(s
, m
, buf
);
2199 ch2
= ast_get_channel_by_name_locked(channel2
);
2201 snprintf(buf
, sizeof(buf
), "Channel does not exist: %s", channel2
);
2202 astman_send_error(s
, m
, buf
);
2203 ast_channel_unlock(ch1
);
2207 if (!ast_strlen_zero(timeout
)) {
2208 sscanf(timeout
, "%d", &to
);
2211 res
= ast_masq_park_call(ch1
, ch2
, to
, &parkExt
);
2213 ast_softhangup(ch2
, AST_SOFTHANGUP_EXPLICIT
);
2214 astman_send_ack(s
, m
, "Park successful");
2216 astman_send_error(s
, m
, "Park failure");
2219 ast_channel_unlock(ch1
);
2220 ast_channel_unlock(ch2
);
2226 int ast_pickup_call(struct ast_channel
*chan
)
2228 struct ast_channel
*cur
= NULL
;
2231 while ( (cur
= ast_channel_walk_locked(cur
)) != NULL
) {
2234 (chan
->pickupgroup
& cur
->callgroup
) &&
2235 ((cur
->_state
== AST_STATE_RINGING
) ||
2236 (cur
->_state
== AST_STATE_RING
))) {
2239 ast_channel_unlock(cur
);
2243 ast_log(LOG_DEBUG
, "Call pickup on chan '%s' by '%s'\n",cur
->name
, chan
->name
);
2244 res
= ast_answer(chan
);
2246 ast_log(LOG_WARNING
, "Unable to answer '%s'\n", chan
->name
);
2247 res
= ast_queue_control(chan
, AST_CONTROL_ANSWER
);
2249 ast_log(LOG_WARNING
, "Unable to queue answer on '%s'\n", chan
->name
);
2250 res
= ast_channel_masquerade(cur
, chan
);
2252 ast_log(LOG_WARNING
, "Unable to masquerade '%s' into '%s'\n", chan
->name
, cur
->name
); /* Done */
2253 ast_channel_unlock(cur
);
2256 ast_log(LOG_DEBUG
, "No call pickup possible...\n");
2261 /*! \brief Add parking hints for all defined parking lots */
2262 static void park_add_hints(char *context
, int start
, int stop
)
2265 char device
[AST_MAX_EXTENSION
];
2268 for (numext
= start
; numext
<= stop
; numext
++) {
2269 snprintf(exten
, sizeof(exten
), "%d", numext
);
2270 snprintf(device
, sizeof(device
), "park:%s@%s", exten
, context
);
2271 ast_add_extension(context
, 1, exten
, PRIORITY_HINT
, NULL
, NULL
, device
, NULL
, NULL
, registrar
);
2276 static int load_config(void)
2278 int start
= 0, end
= 0;
2280 struct ast_context
*con
= NULL
;
2281 struct ast_config
*cfg
= NULL
;
2282 struct ast_variable
*var
= NULL
;
2283 char old_parking_ext
[AST_MAX_EXTENSION
];
2284 char old_parking_con
[AST_MAX_EXTENSION
] = "";
2286 if (!ast_strlen_zero(parking_con
)) {
2287 strcpy(old_parking_ext
, parking_ext
);
2288 strcpy(old_parking_con
, parking_con
);
2291 /* Reset to defaults */
2292 strcpy(parking_con
, "parkedcalls");
2293 strcpy(parking_con_dial
, "park-dial");
2294 strcpy(parking_ext
, "700");
2295 strcpy(pickup_ext
, "*8");
2296 strcpy(parkmohclass
, "default");
2297 courtesytone
[0] = '\0';
2298 strcpy(xfersound
, "beep");
2299 strcpy(xferfailsound
, "pbx-invalid");
2300 parking_start
= 701;
2306 transferdigittimeout
= DEFAULT_TRANSFER_DIGIT_TIMEOUT
;
2307 featuredigittimeout
= DEFAULT_FEATURE_DIGIT_TIMEOUT
;
2308 atxfernoanswertimeout
= DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER
;
2310 cfg
= ast_config_load("features.conf");
2312 ast_log(LOG_WARNING
,"Could not load features.conf\n");
2313 return AST_MODULE_LOAD_DECLINE
;
2315 for (var
= ast_variable_browse(cfg
, "general"); var
; var
= var
->next
) {
2316 if (!strcasecmp(var
->name
, "parkext")) {
2317 ast_copy_string(parking_ext
, var
->value
, sizeof(parking_ext
));
2318 } else if (!strcasecmp(var
->name
, "context")) {
2319 ast_copy_string(parking_con
, var
->value
, sizeof(parking_con
));
2320 } else if (!strcasecmp(var
->name
, "parkingtime")) {
2321 if ((sscanf(var
->value
, "%d", &parkingtime
) != 1) || (parkingtime
< 1)) {
2322 ast_log(LOG_WARNING
, "%s is not a valid parkingtime\n", var
->value
);
2323 parkingtime
= DEFAULT_PARK_TIME
;
2325 parkingtime
= parkingtime
* 1000;
2326 } else if (!strcasecmp(var
->name
, "parkpos")) {
2327 if (sscanf(var
->value
, "%d-%d", &start
, &end
) != 2) {
2328 ast_log(LOG_WARNING
, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var
->lineno
);
2330 parking_start
= start
;
2333 } else if (!strcasecmp(var
->name
, "findslot")) {
2334 parkfindnext
= (!strcasecmp(var
->value
, "next"));
2335 } else if (!strcasecmp(var
->name
, "parkinghints")) {
2336 parkaddhints
= ast_true(var
->value
);
2337 } else if (!strcasecmp(var
->name
, "adsipark")) {
2338 adsipark
= ast_true(var
->value
);
2339 } else if (!strcasecmp(var
->name
, "transferdigittimeout")) {
2340 if ((sscanf(var
->value
, "%d", &transferdigittimeout
) != 1) || (transferdigittimeout
< 1)) {
2341 ast_log(LOG_WARNING
, "%s is not a valid transferdigittimeout\n", var
->value
);
2342 transferdigittimeout
= DEFAULT_TRANSFER_DIGIT_TIMEOUT
;
2344 transferdigittimeout
= transferdigittimeout
* 1000;
2345 } else if (!strcasecmp(var
->name
, "featuredigittimeout")) {
2346 if ((sscanf(var
->value
, "%d", &featuredigittimeout
) != 1) || (featuredigittimeout
< 1)) {
2347 ast_log(LOG_WARNING
, "%s is not a valid featuredigittimeout\n", var
->value
);
2348 featuredigittimeout
= DEFAULT_FEATURE_DIGIT_TIMEOUT
;
2350 } else if (!strcasecmp(var
->name
, "atxfernoanswertimeout")) {
2351 if ((sscanf(var
->value
, "%d", &atxfernoanswertimeout
) != 1) || (atxfernoanswertimeout
< 1)) {
2352 ast_log(LOG_WARNING
, "%s is not a valid atxfernoanswertimeout\n", var
->value
);
2353 atxfernoanswertimeout
= DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER
;
2355 atxfernoanswertimeout
= atxfernoanswertimeout
* 1000;
2356 } else if (!strcasecmp(var
->name
, "courtesytone")) {
2357 ast_copy_string(courtesytone
, var
->value
, sizeof(courtesytone
));
2358 } else if (!strcasecmp(var
->name
, "parkedplay")) {
2359 if (!strcasecmp(var
->value
, "both"))
2361 else if (!strcasecmp(var
->value
, "parked"))
2365 } else if (!strcasecmp(var
->name
, "xfersound")) {
2366 ast_copy_string(xfersound
, var
->value
, sizeof(xfersound
));
2367 } else if (!strcasecmp(var
->name
, "xferfailsound")) {
2368 ast_copy_string(xferfailsound
, var
->value
, sizeof(xferfailsound
));
2369 } else if (!strcasecmp(var
->name
, "pickupexten")) {
2370 ast_copy_string(pickup_ext
, var
->value
, sizeof(pickup_ext
));
2371 } else if (!strcasecmp(var
->name
, "parkedmusicclass")) {
2372 ast_copy_string(parkmohclass
, var
->value
, sizeof(parkmohclass
));
2377 for (var
= ast_variable_browse(cfg
, "featuremap"); var
; var
= var
->next
) {
2378 if (remap_feature(var
->name
, var
->value
))
2379 ast_log(LOG_NOTICE
, "Unknown feature '%s'\n", var
->name
);
2382 /* Map a key combination to an application*/
2383 ast_unregister_features();
2384 for (var
= ast_variable_browse(cfg
, "applicationmap"); var
; var
= var
->next
) {
2385 char *tmp_val
= ast_strdupa(var
->value
);
2386 char *exten
, *activateon
, *activatedby
, *app
, *app_args
, *moh_class
;
2387 struct ast_call_feature
*feature
;
2389 /* strsep() sets the argument to NULL if match not found, and it
2390 * is safe to use it with a NULL argument, so we don't check
2393 exten
= strsep(&tmp_val
,",");
2394 activatedby
= strsep(&tmp_val
,",");
2395 app
= strsep(&tmp_val
,",");
2396 app_args
= strsep(&tmp_val
,",");
2397 moh_class
= strsep(&tmp_val
,",");
2399 activateon
= strsep(&activatedby
, "/");
2401 /*! \todo XXX var_name or app_args ? */
2402 if (ast_strlen_zero(app
) || ast_strlen_zero(exten
) || ast_strlen_zero(activateon
) || ast_strlen_zero(var
->name
)) {
2403 ast_log(LOG_NOTICE
, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",
2404 app
, exten
, activateon
, var
->name
);
2408 AST_LIST_LOCK(&feature_list
);
2409 if ((feature
= find_dynamic_feature(var
->name
))) {
2410 AST_LIST_UNLOCK(&feature_list
);
2411 ast_log(LOG_WARNING
, "Dynamic Feature '%s' specified more than once!\n", var
->name
);
2414 AST_LIST_UNLOCK(&feature_list
);
2416 if (!(feature
= ast_calloc(1, sizeof(*feature
))))
2419 ast_copy_string(feature
->sname
, var
->name
, FEATURE_SNAME_LEN
);
2420 ast_copy_string(feature
->app
, app
, FEATURE_APP_LEN
);
2421 ast_copy_string(feature
->exten
, exten
, FEATURE_EXTEN_LEN
);
2424 ast_copy_string(feature
->app_args
, app_args
, FEATURE_APP_ARGS_LEN
);
2427 ast_copy_string(feature
->moh_class
, moh_class
, FEATURE_MOH_LEN
);
2429 ast_copy_string(feature
->exten
, exten
, sizeof(feature
->exten
));
2430 feature
->operation
= feature_exec_app
;
2431 ast_set_flag(feature
, AST_FEATURE_FLAG_NEEDSDTMF
);
2433 /* Allow caller and calle to be specified for backwards compatability */
2434 if (!strcasecmp(activateon
, "self") || !strcasecmp(activateon
, "caller"))
2435 ast_set_flag(feature
, AST_FEATURE_FLAG_ONSELF
);
2436 else if (!strcasecmp(activateon
, "peer") || !strcasecmp(activateon
, "callee"))
2437 ast_set_flag(feature
, AST_FEATURE_FLAG_ONPEER
);
2439 ast_log(LOG_NOTICE
, "Invalid 'ActivateOn' specification for feature '%s',"
2440 " must be 'self', or 'peer'\n", var
->name
);
2444 if (ast_strlen_zero(activatedby
))
2445 ast_set_flag(feature
, AST_FEATURE_FLAG_BYBOTH
);
2446 else if (!strcasecmp(activatedby
, "caller"))
2447 ast_set_flag(feature
, AST_FEATURE_FLAG_BYCALLER
);
2448 else if (!strcasecmp(activatedby
, "callee"))
2449 ast_set_flag(feature
, AST_FEATURE_FLAG_BYCALLEE
);
2450 else if (!strcasecmp(activatedby
, "both"))
2451 ast_set_flag(feature
, AST_FEATURE_FLAG_BYBOTH
);
2453 ast_log(LOG_NOTICE
, "Invalid 'ActivatedBy' specification for feature '%s',"
2454 " must be 'caller', or 'callee', or 'both'\n", var
->name
);
2458 ast_register_feature(feature
);
2460 if (option_verbose
>= 1)
2461 ast_verbose(VERBOSE_PREFIX_2
"Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var
->name
, app
, app_args
, exten
);
2463 ast_config_destroy(cfg
);
2465 /* Remove the old parking extension */
2466 if (!ast_strlen_zero(old_parking_con
) && (con
= ast_context_find(old_parking_con
))) {
2467 if(ast_context_remove_extension2(con
, old_parking_ext
, 1, registrar
))
2468 notify_metermaids(old_parking_ext
, old_parking_con
);
2470 ast_log(LOG_DEBUG
, "Removed old parking extension %s@%s\n", old_parking_ext
, old_parking_con
);
2473 if (!(con
= ast_context_find(parking_con
)) && !(con
= ast_context_create(NULL
, parking_con
, registrar
))) {
2474 ast_log(LOG_ERROR
, "Parking context '%s' does not exist and unable to create\n", parking_con
);
2477 res
= ast_add_extension2(con
, 1, ast_parking_ext(), 1, NULL
, NULL
, parkcall
, NULL
, NULL
, registrar
);
2479 park_add_hints(parking_con
, parking_start
, parking_stop
);
2481 notify_metermaids(ast_parking_ext(), parking_con
);
2486 static int reload(void)
2488 return load_config();
2491 static int load_module(void)
2495 memset(parking_ext
, 0, sizeof(parking_ext
));
2496 memset(parking_con
, 0, sizeof(parking_con
));
2498 if ((res
= load_config()))
2500 ast_cli_register_multiple(cli_features
, sizeof(cli_features
) / sizeof(struct ast_cli_entry
));
2501 ast_pthread_create(&parking_thread
, NULL
, do_parking_thread
, NULL
);
2502 res
= ast_register_application(parkedcall
, park_exec
, synopsis
, descrip
);
2504 res
= ast_register_application(parkcall
, park_call_exec
, synopsis2
, descrip2
);
2506 ast_manager_register("ParkedCalls", 0, manager_parking_status
, "List parked calls" );
2507 ast_manager_register2("Park", EVENT_FLAG_CALL
, manager_park
,
2508 "Park a channel", mandescr_park
);
2511 res
|= ast_devstate_prov_add("Park", metermaidstate
);
2517 static int unload_module(void)
2519 ast_module_user_hangup_all();
2521 ast_manager_unregister("ParkedCalls");
2522 ast_manager_unregister("Park");
2523 ast_cli_unregister_multiple(cli_features
, sizeof(cli_features
) / sizeof(struct ast_cli_entry
));
2524 ast_unregister_application(parkcall
);
2525 ast_devstate_prov_del("Park");
2526 return ast_unregister_application(parkedcall
);
2529 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_GLOBAL_SYMBOLS
, "Call Features Resource",
2530 .load
= load_module
,
2531 .unload
= unload_module
,