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>
34 #include <sys/signal.h>
35 #include <netinet/in.h>
39 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41 #include "asterisk/lock.h"
42 #include "asterisk/file.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/options.h"
47 #include "asterisk/causes.h"
48 #include "asterisk/module.h"
49 #include "asterisk/translate.h"
50 #include "asterisk/app.h"
51 #include "asterisk/say.h"
52 #include "asterisk/features.h"
53 #include "asterisk/musiconhold.h"
54 #include "asterisk/config.h"
55 #include "asterisk/cli.h"
56 #include "asterisk/manager.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/adsi.h"
59 #include "asterisk/monitor.h"
61 #ifdef __AST_DEBUG_MALLOC
62 static void FREE(void *ptr)
70 #define DEFAULT_PARK_TIME 45000
71 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
72 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
74 #define AST_MAX_WATCHERS 256
76 static char *parkedcall = "ParkedCall";
78 static int parkingtime = DEFAULT_PARK_TIME; /*!< No more than 45 seconds parked before you do something with them */
79 static char parking_con[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */
80 static char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */
81 static char parking_ext[AST_MAX_EXTENSION]; /*!< Extension you type to park the call */
82 static char pickup_ext[AST_MAX_EXTENSION]; /*!< Call pickup extension */
83 static int parking_start; /*!< First available extension for parking */
84 static int parking_stop; /*!< Last available extension for parking */
86 static char courtesytone[256]; /*!< Courtesy tone */
87 static int parkedplay = 0; /*!< Who to play the courtesy tone to */
88 static char xfersound[256]; /*!< Call transfer sound */
89 static char xferfailsound[256]; /*!< Call transfer failure sound */
91 static int parking_offset;
92 static int parkfindnext;
96 static int transferdigittimeout;
97 static int featuredigittimeout;
99 static char *registrar = "res_features"; /*!< Registrar for operations */
101 /* module and CLI command definitions */
102 static char *synopsis = "Answer a parked call";
104 static char *descrip = "ParkedCall(exten):"
105 "Used to connect to a parked call. This application is always\n"
106 "registered internally and does not need to be explicitly added\n"
107 "into the dialplan, although you should include the 'parkedcalls'\n"
110 static char *parkcall = "Park";
112 static char *synopsis2 = "Park yourself";
114 static char *descrip2 = "Park():"
115 "Used to park yourself (typically in combination with a supervised\n"
116 "transfer to know the parking space). 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 struct ast_app *monitor_app = NULL;
122 static int monitor_ok = 1;
125 struct ast_channel *chan;
126 struct timeval start;
128 /* Where to go if our parking time expires */
129 char context[AST_MAX_CONTEXT];
130 char exten[AST_MAX_EXTENSION];
135 unsigned char moh_trys;
136 struct parkeduser *next;
139 static struct parkeduser *parkinglot;
141 AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */
143 static pthread_t parking_thread;
145 char *ast_parking_ext(void)
150 char *ast_pickup_ext(void)
155 struct ast_bridge_thread_obj
157 struct ast_bridge_config bconfig;
158 struct ast_channel *chan;
159 struct ast_channel *peer;
162 /*! \brief store context, priority and extension */
163 static void set_c_e_p(struct ast_channel *chan, const char *context, const char *ext, int pri)
165 ast_copy_string(chan->context, context, sizeof(chan->context));
166 ast_copy_string(chan->exten, ext, sizeof(chan->exten));
167 chan->priority = pri;
170 static void check_goto_on_transfer(struct ast_channel *chan)
172 struct ast_channel *xferchan;
173 const char *val = pbx_builtin_getvar_helper(chan, "GOTO_ON_BLINDXFR");
174 char *x, *goto_on_transfer;
177 if (!ast_strlen_zero(val) && (goto_on_transfer = ast_strdupa(val)) && (xferchan = ast_channel_alloc(0))) {
178 for (x = goto_on_transfer; x && *x; x++)
181 ast_string_field_set(xferchan, name, chan->name);
182 /* Make formats okay */
183 xferchan->readformat = chan->readformat;
184 xferchan->writeformat = chan->writeformat;
185 ast_channel_masquerade(xferchan, chan);
186 ast_parseable_goto(xferchan, goto_on_transfer);
187 xferchan->_state = AST_STATE_UP;
188 ast_clear_flag(xferchan, AST_FLAGS_ALL);
189 xferchan->_softhangup = 0;
190 if ((f = ast_read(xferchan))) {
193 ast_pbx_start(xferchan);
195 ast_hangup(xferchan);
200 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);
203 static void *ast_bridge_call_thread(void *data)
205 struct ast_bridge_thread_obj *tobj = data;
207 tobj->chan->appl = "Transferred Call";
208 tobj->chan->data = tobj->peer->name;
209 tobj->peer->appl = "Transferred Call";
210 tobj->peer->data = tobj->chan->name;
211 if (tobj->chan->cdr) {
212 ast_cdr_reset(tobj->chan->cdr, NULL);
213 ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
215 if (tobj->peer->cdr) {
216 ast_cdr_reset(tobj->peer->cdr, NULL);
217 ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
220 ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
221 ast_hangup(tobj->chan);
222 ast_hangup(tobj->peer);
223 bzero(tobj, sizeof(*tobj)); /*! \todo XXX for safety */
228 static void ast_bridge_call_thread_launch(void *data)
232 struct sched_param sched;
234 pthread_attr_init(&attr);
235 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
236 ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
237 pthread_attr_destroy(&attr);
238 memset(&sched, 0, sizeof(sched));
239 pthread_setschedparam(thread, SCHED_RR, &sched);
242 static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
245 int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
247 char *message[5] = {NULL, NULL, NULL, NULL, NULL};
249 snprintf(tmp, sizeof(tmp), "Parked on %d", parkingnum);
251 res = adsi_load_session(chan, NULL, 0, 1);
254 return adsi_print(chan, message, justify, 1);
257 /*! \brief Park a call
258 We put the user in the parking list, then wake up the parking thread to be sure it looks
259 after these channels too */
260 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
262 struct parkeduser *pu, *cur;
263 int i,x,parking_range;
264 char exten[AST_MAX_EXTENSION];
265 struct ast_context *con;
267 if (!(pu = ast_calloc(1, sizeof(*pu)))) {
270 ast_mutex_lock(&parking_lock);
271 parking_range = parking_stop - parking_start+1;
272 for (i = 0; i < parking_range; i++) {
273 x = (i + parking_offset) % parking_range + parking_start;
276 if (cur->parkingnum == x)
284 if (!(i < parking_range)) {
285 ast_log(LOG_WARNING, "No more parking spaces\n");
287 ast_mutex_unlock(&parking_lock);
291 parking_offset = x - parking_start + 1;
292 chan->appl = "Parked Call";
296 /* Start music on hold */
298 ast_indicate(pu->chan, AST_CONTROL_HOLD);
299 ast_moh_start(pu->chan, NULL);
301 pu->start = ast_tvnow();
303 pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
307 ast_copy_string(pu->peername, peer->name, sizeof(pu->peername));
309 /* Remember what had been dialed, so that if the parking
310 expires, we try to come back to the same place */
311 ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
312 ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
313 pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
314 pu->next = parkinglot;
316 /* If parking a channel directly, don't quiet yet get parking running on it */
319 ast_mutex_unlock(&parking_lock);
320 /* Wake up the (presumably select()ing) thread */
321 pthread_kill(parking_thread, SIGURG);
322 if (option_verbose > 1)
323 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
325 manager_event(EVENT_FLAG_CALL, "ParkedCall",
331 "CallerIDName: %s\r\n",
332 pu->parkingnum, pu->chan->name, peer ? peer->name : "",
333 (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
334 S_OR(pu->chan->cid.cid_num, "<unknown>"),
335 S_OR(pu->chan->cid.cid_name, "<unknown>")
339 if (adsipark && adsi_available(peer))
340 adsi_announce_park(peer, pu->parkingnum);
341 if (adsipark && adsi_available(peer))
342 adsi_unload_session(peer);
344 con = ast_context_find(parking_con);
346 con = ast_context_create(NULL, parking_con, registrar);
348 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
351 snprintf(exten, sizeof(exten), "%d", x);
352 ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), FREE, registrar);
355 ast_say_digits(peer, pu->parkingnum, "", peer->language);
356 if (pu->notquiteyet) {
357 /* Wake up parking thread if we're really done */
358 ast_moh_start(pu->chan, NULL);
360 pthread_kill(parking_thread, SIGURG);
365 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
367 struct ast_channel *chan;
370 /* Make a new, fake channel that we'll use to masquerade in the real one */
371 if ((chan = ast_channel_alloc(0))) {
372 /* Let us keep track of the channel name */
373 ast_string_field_build(chan, name, "Parked/%s",rchan->name);
375 /* Make formats okay */
376 chan->readformat = rchan->readformat;
377 chan->writeformat = rchan->writeformat;
378 ast_channel_masquerade(chan, rchan);
380 /* Setup the extensions and such */
381 set_c_e_p(chan, rchan->context, rchan->exten, rchan->priority);
383 /* Make the masq execute */
387 ast_park_call(chan, peer, timeout, extout);
389 ast_log(LOG_WARNING, "Unable to create parked channel\n");
396 #define FEATURE_RETURN_HANGUP -1
397 #define FEATURE_RETURN_SUCCESSBREAK 0
398 #define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
399 #define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
400 #define FEATURE_RETURN_PASSDIGITS 21
401 #define FEATURE_RETURN_STOREDIGITS 22
402 #define FEATURE_RETURN_SUCCESS 23
404 #define FEATURE_SENSE_CHAN (1 << 0)
405 #define FEATURE_SENSE_PEER (1 << 1)
408 * if the file name is non-empty, try to play it.
409 * Return 0 if success, -1 if error, digit if interrupted by a digit.
410 * If digits == "" then we can simply check for non-zero.
413 *! \todo XXX there are probably many replicas of this function in the source tree,
414 * that should be merged.
416 static int stream_and_wait(struct ast_channel *chan, const char *file, const char *language, const char *digits)
419 if (!ast_strlen_zero(file)) {
420 res = ast_streamfile(chan, file, language);
422 res = ast_waitstream(chan, digits);
428 * set caller and callee according to the direction
430 static void set_peers(struct ast_channel **caller, struct ast_channel **callee,
431 struct ast_channel *peer, struct ast_channel *chan, int sense)
433 if (sense == FEATURE_SENSE_PEER) {
442 static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
444 char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL;
447 struct ast_channel *caller_chan, *callee_chan;
450 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
454 if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) {
456 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
460 set_peers(&caller_chan, &callee_chan, peer, chan, sense);
462 if (!ast_strlen_zero(courtesytone)) {
463 if (ast_autoservice_start(callee_chan))
465 if (stream_and_wait(caller_chan, courtesytone, caller_chan->language, "")) {
466 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
467 ast_autoservice_stop(callee_chan);
470 if (ast_autoservice_stop(callee_chan))
474 if (callee_chan->monitor) {
475 if (option_verbose > 3)
476 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
477 ast_monitor_stop(callee_chan, 1);
478 return FEATURE_RETURN_SUCCESS;
481 if (caller_chan && callee_chan) {
482 const char *touch_format = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR_FORMAT");
483 const char *touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
486 touch_format = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR_FORMAT");
489 touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
492 len = strlen(touch_monitor) + 50;
494 touch_filename = alloca(len);
495 snprintf(touch_filename, len, "auto-%ld-%s", (long)time(NULL), touch_monitor);
496 snprintf(args, len, "%s|%s|m", (touch_format) ? touch_format : "wav", touch_filename);
498 caller_chan_id = ast_strdupa(S_OR(caller_chan->cid.cid_num, caller_chan->name));
499 callee_chan_id = ast_strdupa(S_OR(callee_chan->cid.cid_num, callee_chan->name));
500 len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
502 touch_filename = alloca(len);
503 snprintf(touch_filename, len, "auto-%ld-%s-%s", (long)time(NULL), caller_chan_id, callee_chan_id);
504 snprintf(args, len, "%s|%s|m", S_OR(touch_format, "wav"), touch_filename);
507 for( x = 0; x < strlen(args); x++) {
512 if (option_verbose > 3)
513 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
515 pbx_exec(callee_chan, monitor_app, args);
516 pbx_builtin_setvar_helper(callee_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
517 pbx_builtin_setvar_helper(caller_chan, "TOUCH_MONITOR_OUTPUT", touch_filename);
519 return FEATURE_RETURN_SUCCESS;
522 ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");
526 static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
528 if (option_verbose > 3)
529 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
530 return FEATURE_RETURN_HANGUP;
533 static int finishup(struct ast_channel *chan)
538 res = ast_autoservice_stop(chan);
539 ast_indicate(chan, AST_CONTROL_UNHOLD);
543 static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
545 const char *s = pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT");
546 if (ast_strlen_zero(s))
547 s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
548 if (ast_strlen_zero(s)) /* Use the non-macro context to transfer the call XXX ? */
549 s = transferer->macrocontext;
550 if (ast_strlen_zero(s))
551 s = transferer->context;
555 static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
557 struct ast_channel *transferer;
558 struct ast_channel *transferee;
559 const char *transferer_real_context;
563 set_peers(&transferer, &transferee, peer, chan, sense);
564 transferer_real_context = real_ctx(transferer, transferee);
565 /* Start autoservice on chan while we talk to the originator */
566 ast_indicate(transferee, AST_CONTROL_HOLD);
567 ast_autoservice_start(transferee);
568 ast_moh_start(transferee, NULL);
570 memset(xferto, 0, sizeof(xferto));
573 res = stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
575 finishup(transferee);
576 return -1; /* error ? */
578 if (res > 0) /* If they've typed a digit already, handle it */
579 xferto[0] = (char) res;
581 ast_stopstream(transferer);
582 res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
583 if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */
584 finishup(transferee);
587 if (!strcmp(xferto, ast_parking_ext())) {
588 res = finishup(transferee);
591 else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */
592 /* We return non-zero, but tell the PBX not to hang the channel when
593 the thread dies -- We have to be careful now though. We are responsible for
594 hanging up the channel, else it will never be hung up! */
596 return (transferer == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER;
598 ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
600 /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */
601 } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
602 pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
603 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
604 res=finishup(transferee);
605 if (!transferee->pbx) {
606 /* Doh! Use our handy async_goto functions */
607 if (option_verbose > 2)
608 ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
609 ,transferee->name, xferto, transferer_real_context);
610 if (ast_async_goto(transferee, transferer_real_context, xferto, 1))
611 ast_log(LOG_WARNING, "Async goto failed :-(\n");
614 /* Set the channel's new extension, since it exists, using transferer context */
615 set_c_e_p(transferee, transferer_real_context, xferto, 0);
617 check_goto_on_transfer(transferer);
620 if (option_verbose > 2)
621 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context);
623 if (stream_and_wait(transferer, xferfailsound, transferee->language, AST_DIGIT_ANY) < 0 ) {
624 finishup(transferee);
627 ast_stopstream(transferer);
628 res = finishup(transferee);
630 if (option_verbose > 1)
631 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
634 return FEATURE_RETURN_SUCCESS;
637 static int check_compat(struct ast_channel *c, struct ast_channel *newchan)
639 if (ast_channel_make_compatible(c, newchan) < 0) {
640 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n",
641 c->name, newchan->name);
648 static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
650 struct ast_channel *transferer;
651 struct ast_channel *transferee;
652 const char *transferer_real_context;
656 struct ast_channel *newchan;
657 struct ast_channel *xferchan;
658 struct ast_bridge_thread_obj *tobj;
659 struct ast_bridge_config bconfig;
664 ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense);
665 set_peers(&transferer, &transferee, peer, chan, sense);
666 transferer_real_context = real_ctx(transferer, transferee);
667 /* Start autoservice on chan while we talk to the originator */
668 ast_indicate(transferee, AST_CONTROL_HOLD);
669 ast_autoservice_start(transferee);
670 ast_moh_start(transferee, NULL);
671 memset(xferto, 0, sizeof(xferto));
673 res = stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY);
675 finishup(transferee);
678 if (res > 0) /* If they've typed a digit already, handle it */
679 xferto[0] = (char) res;
681 /* this is specific of atxfer */
682 res = ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout);
683 if (res < 0) { /* hangup, would be 0 for invalid and 1 for valid */
684 finishup(transferee);
688 ast_log(LOG_WARNING, "Did not read data.\n");
689 finishup(transferee);
690 if (stream_and_wait(transferer, "beeperr", transferer->language, ""))
692 return FEATURE_RETURN_SUCCESS;
695 /* valid extension, res == 1 */
696 if (!ast_exists_extension(transferer, transferer_real_context, xferto, 1, transferer->cid.cid_num)) {
697 ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
698 finishup(transferee);
699 if (stream_and_wait(transferer, "beeperr", transferer->language, ""))
701 return FEATURE_RETURN_SUCCESS;
705 snprintf(xferto + l, sizeof(xferto) - l, "@%s/n", transferer_real_context); /* append context */
706 newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats),
707 xferto, 15000, &outstate, transferer->cid.cid_num, transferer->cid.cid_name);
708 ast_indicate(transferer, -1);
710 finishup(transferee);
711 /* any reason besides user requested cancel and busy triggers the failed sound */
712 if (outstate != AST_CONTROL_UNHOLD && outstate != AST_CONTROL_BUSY &&
713 stream_and_wait(transferer, xferfailsound, transferer->language, ""))
715 return FEATURE_RETURN_SUCCESS;
718 if (check_compat(transferer, newchan))
720 memset(&bconfig,0,sizeof(struct ast_bridge_config));
721 ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
722 ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
723 res = ast_bridge_call(transferer, newchan, &bconfig);
724 if (newchan->_softhangup || newchan->_state != AST_STATE_UP || !transferer->_softhangup) {
726 if (stream_and_wait(transferer, xfersound, transferer->language, ""))
727 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
728 finishup(transferee);
729 transferer->_softhangup = 0;
730 return FEATURE_RETURN_SUCCESS;
733 if (check_compat(transferee, newchan))
736 ast_moh_stop(transferee);
738 if ((ast_autoservice_stop(transferee) < 0)
739 || (ast_waitfordigit(transferee, 100) < 0)
740 || (ast_waitfordigit(newchan, 100) < 0)
741 || ast_check_hangup(transferee)
742 || ast_check_hangup(newchan)) {
747 xferchan = ast_channel_alloc(0);
752 ast_string_field_build(xferchan, name, "Transfered/%s", transferee->name);
753 /* Make formats okay */
754 xferchan->readformat = transferee->readformat;
755 xferchan->writeformat = transferee->writeformat;
756 ast_channel_masquerade(xferchan, transferee);
757 ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
758 xferchan->_state = AST_STATE_UP;
759 ast_clear_flag(xferchan, AST_FLAGS_ALL);
760 xferchan->_softhangup = 0;
762 if ((f = ast_read(xferchan)))
765 newchan->_state = AST_STATE_UP;
766 ast_clear_flag(newchan, AST_FLAGS_ALL);
767 newchan->_softhangup = 0;
769 tobj = ast_calloc(1, sizeof(struct ast_bridge_thread_obj));
771 ast_hangup(xferchan);
775 tobj->chan = xferchan;
776 tobj->peer = newchan;
777 tobj->bconfig = *config;
779 if (stream_and_wait(newchan, xfersound, newchan->language, ""))
780 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
781 ast_bridge_call_thread_launch(tobj);
782 return -1; /* XXX meaning the channel is bridged ? */
786 /* add atxfer and automon as undefined so you can only use em if you configure them */
787 #define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
789 struct ast_call_feature builtin_features[] =
791 { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF },
792 { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF },
793 { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF },
794 { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
798 static AST_LIST_HEAD(feature_list,ast_call_feature) feature_list;
800 /*! \brief register new feature into feature_list*/
801 void ast_register_feature(struct ast_call_feature *feature)
804 ast_log(LOG_NOTICE,"You didn't pass a feature!\n");
808 AST_LIST_LOCK(&feature_list);
809 AST_LIST_INSERT_HEAD(&feature_list,feature,feature_entry);
810 AST_LIST_UNLOCK(&feature_list);
812 if (option_verbose >= 2)
813 ast_verbose(VERBOSE_PREFIX_2 "Registered Feature '%s'\n",feature->sname);
816 /*! \brief unregister feature from feature_list */
817 void ast_unregister_feature(struct ast_call_feature *feature)
822 AST_LIST_LOCK(&feature_list);
823 AST_LIST_REMOVE(&feature_list,feature,feature_entry);
824 AST_LIST_UNLOCK(&feature_list);
828 static void ast_unregister_features(void)
830 struct ast_call_feature *feature;
832 AST_LIST_LOCK(&feature_list);
833 while ((feature = AST_LIST_REMOVE_HEAD(&feature_list,feature_entry)))
835 AST_LIST_UNLOCK(&feature_list);
838 /*! \brief find a feature by name */
839 static struct ast_call_feature *find_feature(char *name)
841 struct ast_call_feature *tmp;
843 AST_LIST_LOCK(&feature_list);
844 AST_LIST_TRAVERSE(&feature_list, tmp, feature_entry) {
845 if (!strcasecmp(tmp->sname, name))
848 AST_LIST_UNLOCK(&feature_list);
853 /*! \brief exec an app by feature */
854 static int feature_exec_app(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
857 struct ast_call_feature *feature;
860 AST_LIST_LOCK(&feature_list);
861 AST_LIST_TRAVERSE(&feature_list,feature,feature_entry) {
862 if (!strcasecmp(feature->exten,code))
865 AST_LIST_UNLOCK(&feature_list);
867 if (!feature) { /* shouldn't ever happen! */
868 ast_log(LOG_NOTICE, "Found feature before, but at execing we've lost it??\n");
872 app = pbx_findapp(feature->app);
874 struct ast_channel *work = ast_test_flag(feature,AST_FEATURE_FLAG_CALLEE) ? peer : chan;
875 res = pbx_exec(work, app, feature->app_args);
879 ast_log(LOG_WARNING, "Could not find application (%s)\n", feature->app);
883 return FEATURE_RETURN_SUCCESS; /*! \todo XXX should probably return res */
886 static void unmap_features(void)
889 for (x = 0; x < FEATURES_COUNT; x++)
890 strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
893 static int remap_feature(const char *name, const char *value)
897 for (x = 0; x < FEATURES_COUNT; x++) {
898 if (!strcasecmp(name, builtin_features[x].sname)) {
899 ast_copy_string(builtin_features[x].exten, value, sizeof(builtin_features[x].exten));
900 if (option_verbose > 1)
901 ast_verbose(VERBOSE_PREFIX_2 "Remapping feature %s (%s) to sequence '%s'\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
903 } else if (!strcmp(value, builtin_features[x].exten))
904 ast_log(LOG_WARNING, "Sequence '%s' already mapped to function %s (%s) while assigning to %s\n", value, builtin_features[x].fname, builtin_features[x].sname, name);
909 static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
912 struct ast_flags features;
913 int res = FEATURE_RETURN_PASSDIGITS;
914 struct ast_call_feature *feature;
915 const char *dynamic_features=pbx_builtin_getvar_helper(chan,"DYNAMIC_FEATURES");
917 if (sense == FEATURE_SENSE_CHAN)
918 ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
920 ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
921 if (option_debug > 2)
922 ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features.flags);
924 for (x=0; x < FEATURES_COUNT; x++) {
925 if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
926 !ast_strlen_zero(builtin_features[x].exten)) {
927 /* Feature is up for consideration */
928 if (!strcmp(builtin_features[x].exten, code)) {
929 res = builtin_features[x].operation(chan, peer, config, code, sense);
931 } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
932 if (res == FEATURE_RETURN_PASSDIGITS)
933 res = FEATURE_RETURN_STOREDIGITS;
939 if (!ast_strlen_zero(dynamic_features)) {
940 char *tmp = ast_strdupa(dynamic_features);
946 while ((tok = strsep(&tmp, "#")) != NULL) {
947 feature = find_feature(tok);
950 /* Feature is up for consideration */
951 if (!strcmp(feature->exten, code)) {
952 if (option_verbose > 2)
953 ast_verbose(VERBOSE_PREFIX_3 " Feature Found: %s exten: %s\n",feature->sname, tok);
954 res = feature->operation(chan, peer, config, code, sense);
956 } else if (!strncmp(feature->exten, code, strlen(code))) {
957 res = FEATURE_RETURN_STOREDIGITS;
966 static void set_config_flags(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config)
970 ast_clear_flag(config, AST_FLAGS_ALL);
971 for (x = 0; x < FEATURES_COUNT; x++) {
972 if (ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF)) {
973 if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask))
974 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
976 if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask))
977 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
981 if (chan && peer && !(ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_0) && ast_test_flag(config, AST_BRIDGE_DTMF_CHANNEL_1))) {
982 const char *dynamic_features = pbx_builtin_getvar_helper(chan, "DYNAMIC_FEATURES");
984 if (dynamic_features) {
985 char *tmp = ast_strdupa(dynamic_features);
987 struct ast_call_feature *feature;
989 if (!tmp) /* no memory */
992 /* while we have a feature */
993 while (NULL != (tok = strsep(&tmp, "#"))) {
994 if ((feature = find_feature(tok)) && ast_test_flag(feature, AST_FEATURE_FLAG_NEEDSDTMF)) {
995 if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLER))
996 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
997 if (ast_test_flag(feature, AST_FEATURE_FLAG_CALLEE))
998 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
1005 /*! \todo XXX Check - this is very similar to the code in channel.c */
1006 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)
1011 struct ast_channel *chan;
1012 struct ast_channel *monitor_chans[2];
1013 struct ast_channel *active_channel;
1014 int res = 0, ready = 0;
1016 if ((chan = ast_request(type, format, data, &cause))) {
1017 ast_set_callerid(chan, cid_num, cid_name, cid_num);
1018 ast_channel_inherit_variables(caller, chan);
1019 if (!ast_call(chan, data, timeout)) {
1020 struct timeval started;
1022 char *disconnect_code = NULL, *dialed_code = NULL;
1024 ast_indicate(caller, AST_CONTROL_RINGING);
1025 /* support dialing of the featuremap disconnect code while performing an attended tranfer */
1026 for (x=0; x < FEATURES_COUNT; x++) {
1027 if (strcasecmp(builtin_features[x].sname, "disconnect"))
1030 disconnect_code = builtin_features[x].exten;
1031 len = strlen(disconnect_code) + 1;
1032 dialed_code = alloca(len);
1033 memset(dialed_code, 0, len);
1037 started = ast_tvnow();
1039 while (!ast_check_hangup(caller) && timeout && (chan->_state != AST_STATE_UP)) {
1040 struct ast_frame *f = NULL;
1042 monitor_chans[0] = caller;
1043 monitor_chans[1] = chan;
1044 active_channel = ast_waitfor_n(monitor_chans, 2, &to);
1046 /* see if the timeout has been violated */
1047 if(ast_tvdiff_ms(ast_tvnow(), started) > timeout) {
1048 state = AST_CONTROL_UNHOLD;
1049 ast_log(LOG_NOTICE, "We exceeded our AT-timeout\n");
1050 break; /*doh! timeout*/
1053 if (!active_channel)
1056 if (chan && (chan == active_channel)){
1058 if (f == NULL) { /*doh! where'd he go?*/
1059 state = AST_CONTROL_HANGUP;
1064 if (f->frametype == AST_FRAME_CONTROL || f->frametype == AST_FRAME_DTMF || f->frametype == AST_FRAME_TEXT) {
1065 if (f->subclass == AST_CONTROL_RINGING) {
1066 state = f->subclass;
1067 if (option_verbose > 2)
1068 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", chan->name);
1069 ast_indicate(caller, AST_CONTROL_RINGING);
1070 } else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
1071 state = f->subclass;
1072 if (option_verbose > 2)
1073 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", chan->name);
1074 ast_indicate(caller, AST_CONTROL_BUSY);
1078 } else if (f->subclass == AST_CONTROL_ANSWER) {
1079 /* This is what we are hoping for */
1080 state = f->subclass;
1086 ast_log(LOG_NOTICE, "Don't know what to do about control frame: %d\n", f->subclass);
1088 /* else who cares */
1091 } else if (caller && (active_channel == caller)) {
1092 f = ast_read(caller);
1093 if (f == NULL) { /*doh! where'd he go?*/
1094 if (caller->_softhangup && !chan->_softhangup) {
1095 /* make this a blind transfer */
1099 state = AST_CONTROL_HANGUP;
1104 if (f->frametype == AST_FRAME_DTMF) {
1105 dialed_code[x++] = f->subclass;
1106 dialed_code[x] = '\0';
1107 if (strlen(dialed_code) == len) {
1109 } else if (x && strncmp(dialed_code, disconnect_code, x)) {
1111 dialed_code[x] = '\0';
1113 if (*dialed_code && !strcmp(dialed_code, disconnect_code)) {
1114 /* Caller Canceled the call */
1115 state = AST_CONTROL_UNHOLD;
1126 ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
1128 ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
1130 case AST_CAUSE_BUSY:
1131 state = AST_CONTROL_BUSY;
1133 case AST_CAUSE_CONGESTION:
1134 state = AST_CONTROL_CONGESTION;
1139 ast_indicate(caller, -1);
1140 if (chan && ready) {
1141 if (chan->_state == AST_STATE_UP)
1142 state = AST_CONTROL_ANSWER;
1155 if (chan && res <= 0) {
1156 if (chan->cdr || (chan->cdr = ast_cdr_alloc())) {
1158 ast_cdr_init(chan->cdr, chan);
1159 snprintf(tmp, 256, "%s/%s", type, (char *)data);
1160 ast_cdr_setapp(chan->cdr,"Dial",tmp);
1161 ast_cdr_update(chan);
1162 ast_cdr_start(chan->cdr);
1163 ast_cdr_end(chan->cdr);
1164 /* If the cause wasn't handled properly */
1165 if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
1166 ast_cdr_failed(chan->cdr);
1168 ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
1175 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
1177 /* Copy voice back and forth between the two channels. Give the peer
1178 the ability to transfer calls with '#<extension' syntax. */
1179 struct ast_frame *f;
1180 struct ast_channel *who;
1181 char chan_featurecode[FEATURE_MAX_LEN + 1]="";
1182 char peer_featurecode[FEATURE_MAX_LEN + 1]="";
1187 struct ast_option_header *aoh;
1188 struct timeval start = { 0 , 0 };
1189 struct ast_bridge_config backup_config;
1191 memset(&backup_config, 0, sizeof(backup_config));
1193 config->start_time = ast_tvnow();
1196 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
1197 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
1199 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
1202 const char *monitor_exec;
1203 struct ast_channel *src = NULL;
1205 if (!(monitor_app = pbx_findapp("Monitor")))
1208 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR")))
1210 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
1212 if (monitor_app && src) {
1213 char *tmp = ast_strdupa(monitor_exec);
1215 pbx_exec(src, monitor_app, tmp);
1217 ast_log(LOG_ERROR, "Monitor failed: out of memory\n");
1222 set_config_flags(chan, peer, config);
1223 config->firstpass = 1;
1225 /* Answer if need be */
1226 if (ast_answer(chan))
1228 peer->appl = "Bridged Call";
1229 peer->data = chan->name;
1231 /* copy the userfield from the B-leg to A-leg if applicable */
1232 if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
1234 if (!ast_strlen_zero(chan->cdr->userfield)) {
1235 snprintf(tmp, sizeof(tmp), "%s;%s", chan->cdr->userfield, peer->cdr->userfield);
1236 ast_cdr_appenduserfield(chan, tmp);
1238 ast_cdr_setuserfield(chan, peer->cdr->userfield);
1239 /* free the peer's cdr without ast_cdr_free complaining */
1244 struct ast_channel *other; /* used later */
1245 if (config->feature_timer)
1246 start = ast_tvnow();
1248 res = ast_channel_bridge(chan, peer, config, &f, &who);
1250 if (config->feature_timer) {
1251 /* Update time limit for next pass */
1252 diff = ast_tvdiff_ms(ast_tvnow(), start);
1253 config->feature_timer -= diff;
1255 /* Running on backup config, meaning a feature might be being
1256 activated, but that's no excuse to keep things going
1258 if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
1260 ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
1261 config->feature_timer = 0;
1267 } else if (config->feature_timer <= 0) {
1268 /* Not *really* out of time, just out of time for
1269 digits to come in for features. */
1271 ast_log(LOG_DEBUG, "Timed out for feature!\n");
1272 if (!ast_strlen_zero(peer_featurecode)) {
1273 ast_dtmf_stream(chan, peer, peer_featurecode, 0);
1274 memset(peer_featurecode, 0, sizeof(peer_featurecode));
1276 if (!ast_strlen_zero(chan_featurecode)) {
1277 ast_dtmf_stream(peer, chan, chan_featurecode, 0);
1278 memset(chan_featurecode, 0, sizeof(chan_featurecode));
1282 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
1284 /* Restore original (possibly time modified) bridge config */
1285 memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
1286 memset(&backup_config, 0, sizeof(backup_config));
1288 hadfeatures = hasfeatures;
1289 /* Continue as we were */
1293 if (config->feature_timer <=0) {
1294 /* We ran out of time */
1295 config->feature_timer = 0;
1305 ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
1309 if (!f || (f->frametype == AST_FRAME_CONTROL &&
1310 (f->subclass == AST_CONTROL_HANGUP || f->subclass == AST_CONTROL_BUSY ||
1311 f->subclass == AST_CONTROL_CONGESTION ) ) ) {
1315 /* many things should be sent to the 'other' channel */
1316 other = (who == chan) ? peer : chan;
1317 if (f->frametype == AST_FRAME_CONTROL) {
1318 if (f->subclass == AST_CONTROL_RINGING)
1319 ast_indicate(other, AST_CONTROL_RINGING);
1320 else if (f->subclass == -1)
1321 ast_indicate(other, -1);
1322 else if (f->subclass == AST_CONTROL_FLASH)
1323 ast_indicate(other, AST_CONTROL_FLASH);
1324 else if (f->subclass == AST_CONTROL_OPTION) {
1326 /* Forward option Requests */
1327 if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST)
1328 ast_channel_setoption(other, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
1331 /* check for '*', if we find it it's time to disconnect */
1332 if (f->frametype == AST_FRAME_DTMF) {
1336 hadfeatures = hasfeatures;
1337 /* This cannot overrun because the longest feature is one shorter than our buffer */
1339 sense = FEATURE_SENSE_CHAN;
1340 featurecode = chan_featurecode;
1342 sense = FEATURE_SENSE_PEER;
1343 featurecode = peer_featurecode;
1345 /*! append the event to featurecode. we rely on the string being zero-filled, and
1346 * not overflowing it.
1347 * \todo XXX how do we guarantee the latter ?
1349 featurecode[strlen(featurecode)] = f->subclass;
1350 config->feature_timer = backup_config.feature_timer;
1351 res = ast_feature_interpret(chan, peer, config, featurecode, sense);
1353 case FEATURE_RETURN_PASSDIGITS:
1354 ast_dtmf_stream(other, who, featurecode, 0);
1356 case FEATURE_RETURN_SUCCESS:
1357 memset(featurecode, 0, sizeof(chan_featurecode));
1360 if (res >= FEATURE_RETURN_PASSDIGITS) {
1366 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
1367 if (hadfeatures && !hasfeatures) {
1368 /* Restore backup */
1369 memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
1370 memset(&backup_config, 0, sizeof(struct ast_bridge_config));
1371 } else if (hasfeatures) {
1373 /* Backup configuration */
1374 memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
1375 /* Setup temporary config options */
1376 config->play_warning = 0;
1377 ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
1378 ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
1379 config->warning_freq = 0;
1380 config->warning_sound = NULL;
1381 config->end_sound = NULL;
1382 config->start_sound = NULL;
1383 config->firstpass = 0;
1385 config->feature_timer = featuredigittimeout;
1387 ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->feature_timer);
1396 static void post_manager_event(const char *s, int num, struct ast_channel *chan)
1398 manager_event(EVENT_FLAG_CALL, s,
1402 "CallerIDName: %s\r\n\r\n",
1404 S_OR(chan->cid.cid_num, "<unknown>"),
1405 S_OR(chan->cid.cid_name, "<unknown>")
1409 /*! \brief Take care of parked calls and unpark them if needed */
1410 static void *do_parking_thread(void *ignore)
1412 fd_set rfds, efds; /* results from previous select, to be preserved across loops. */
1417 struct parkeduser *pu, *pl, *pt = NULL;
1418 int ms = -1; /* select timeout, uninitialized */
1419 int max = -1; /* max fd, none there yet */
1420 fd_set nrfds, nefds; /* args for the next select */
1424 ast_mutex_lock(&parking_lock);
1427 /* navigate the list with prev-cur pointers to support removals */
1429 struct ast_channel *chan = pu->chan; /* shorthand */
1430 int tms; /* timeout for this item */
1431 int x; /* fd index in channel */
1432 struct ast_context *con;
1434 if (pu->notquiteyet) { /* Pretend this one isn't here yet */
1439 tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
1440 if (tms > pu->parkingtime) {
1441 /* Stop music on hold */
1443 ast_indicate(chan, AST_CONTROL_UNHOLD);
1444 /* Get chan, exten from derived kludge */
1445 if (pu->peername[0]) {
1446 char *peername = ast_strdupa(pu->peername);
1447 char *cp = strrchr(peername, '-');
1450 con = ast_context_find(parking_con_dial);
1452 con = ast_context_create(NULL, parking_con_dial, registrar);
1454 ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
1458 char returnexten[AST_MAX_EXTENSION];
1459 snprintf(returnexten, sizeof(returnexten), "%s||t", peername);
1460 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), FREE, registrar);
1462 set_c_e_p(chan, parking_con_dial, peername, 1);
1464 /* They've been waiting too long, send them back to where they came. Theoretically they
1465 should have their original extensions and such, but we copy to be on the safe side */
1466 set_c_e_p(chan, pu->context, pu->exten, pu->priority);
1469 post_manager_event("ParkedCallTimeOut", pu->parkingnum, chan);
1471 if (option_verbose > 1)
1472 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);
1473 /* Start up the PBX, or hang them up */
1474 if (ast_pbx_start(chan)) {
1475 ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
1478 /* And take them out of the parking lot */
1480 pl->next = pu->next;
1482 parkinglot = pu->next;
1485 con = ast_context_find(parking_con);
1487 char exten[AST_MAX_EXTENSION];
1488 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1489 if (ast_context_remove_extension2(con, exten, 1, NULL))
1490 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1492 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1494 } else { /* still within parking time, process descriptors */
1495 for (x = 0; x < AST_MAX_FDS; x++) {
1496 struct ast_frame *f;
1498 if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
1499 continue; /* nothing on this descriptor */
1501 if (FD_ISSET(chan->fds[x], &efds))
1502 ast_set_flag(chan, AST_FLAG_EXCEPTION);
1504 ast_clear_flag(chan, AST_FLAG_EXCEPTION);
1506 /* See if they need servicing */
1508 if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) {
1511 post_manager_event("ParkedCallGiveUp", pu->parkingnum, chan);
1513 /* There's a problem, hang them up*/
1514 if (option_verbose > 1)
1515 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name);
1517 /* And take them out of the parking lot */
1519 pl->next = pu->next;
1521 parkinglot = pu->next;
1524 con = ast_context_find(parking_con);
1526 char exten[AST_MAX_EXTENSION];
1527 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1528 if (ast_context_remove_extension2(con, exten, 1, NULL))
1529 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1531 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1535 /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
1537 if (pu->moh_trys < 3 && !chan->generatordata) {
1539 ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting.\n");
1540 ast_moh_start(chan, NULL);
1543 goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */
1547 if (x >= AST_MAX_FDS) {
1548 std: for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
1549 if (chan->fds[x] > -1) {
1550 FD_SET(chan->fds[x], &nrfds);
1551 FD_SET(chan->fds[x], &nefds);
1552 if (chan->fds[x] > max)
1556 /* Keep track of our shortest wait */
1557 if (tms < ms || ms < 0)
1564 ast_mutex_unlock(&parking_lock);
1568 struct timeval tv = ast_samp2tv(ms, 1000);
1569 /* Wait for something to happen */
1570 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
1572 pthread_testcancel();
1574 return NULL; /* Never reached */
1577 static int park_call_exec(struct ast_channel *chan, void *data)
1579 /* Data is unused at the moment but could contain a parking
1580 lot context eventually */
1582 struct localuser *u;
1584 /* Setup the exten/priority to be s/1 since we don't know
1585 where this call should return */
1586 strcpy(chan->exten, "s");
1588 if (chan->_state != AST_STATE_UP)
1589 res = ast_answer(chan);
1591 res = ast_safe_sleep(chan, 1000);
1593 res = ast_park_call(chan, chan, 0, NULL);
1594 LOCAL_USER_REMOVE(u);
1596 res = AST_PBX_KEEPALIVE;
1600 static int park_exec(struct ast_channel *chan, void *data)
1603 struct localuser *u;
1604 struct ast_channel *peer=NULL;
1605 struct parkeduser *pu, *pl=NULL;
1606 struct ast_context *con;
1608 struct ast_bridge_config config;
1611 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
1615 park = atoi((char *)data);
1616 ast_mutex_lock(&parking_lock);
1619 if (pu->parkingnum == park) {
1621 pl->next = pu->next;
1623 parkinglot = pu->next;
1629 ast_mutex_unlock(&parking_lock);
1632 con = ast_context_find(parking_con);
1634 char exten[AST_MAX_EXTENSION];
1635 snprintf(exten, sizeof(exten), "%d", pu->parkingnum);
1636 if (ast_context_remove_extension2(con, exten, 1, NULL))
1637 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1639 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1641 manager_event(EVENT_FLAG_CALL, "UnParkedCall",
1646 "CallerIDName: %s\r\n",
1647 pu->parkingnum, pu->chan->name, chan->name,
1648 S_OR(pu->chan->cid.cid_num, "<unknown>"),
1649 S_OR(pu->chan->cid.cid_name, "<unknown>")
1654 /* JK02: it helps to answer the channel if not already up */
1655 if (chan->_state != AST_STATE_UP)
1659 /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
1661 if (!ast_strlen_zero(courtesytone)) {
1664 ast_indicate(peer, AST_CONTROL_UNHOLD);
1665 if (parkedplay == 0) {
1666 error = stream_and_wait(chan, courtesytone, chan->language, "");
1667 } else if (parkedplay == 1) {
1668 error = stream_and_wait(peer, courtesytone, chan->language, "");
1669 } else if (parkedplay == 2) {
1670 if (!ast_streamfile(chan, courtesytone, chan->language) &&
1671 !ast_streamfile(peer, courtesytone, chan->language)) {
1672 /*! \todo XXX we would like to wait on both! */
1673 res = ast_waitstream(chan, "");
1675 res = ast_waitstream(peer, "");
1681 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
1687 res = ast_channel_make_compatible(chan, peer);
1689 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
1693 /* This runs sorta backwards, since we give the incoming channel control, as if it
1694 were the person called. */
1695 if (option_verbose > 2)
1696 ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
1698 memset(&config, 0, sizeof(struct ast_bridge_config));
1699 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1700 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
1701 config.timelimit = 0;
1702 config.play_warning = 0;
1703 config.warning_freq = 0;
1704 config.warning_sound=NULL;
1705 res = ast_bridge_call(chan, peer, &config);
1707 /* Simulate the PBX hanging up */
1708 if (res != AST_PBX_NO_HANGUP_PEER)
1712 /*! \todo XXX Play a message XXX */
1713 if (stream_and_wait(chan, "pbx-invalidpark", chan->language, ""))
1714 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
1715 if (option_verbose > 2)
1716 ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
1719 LOCAL_USER_REMOVE(u);
1723 static int handle_showfeatures(int fd, int argc, char *argv[])
1727 struct ast_call_feature *feature;
1728 char format[] = "%-25s %-7s %-7s\n";
1730 ast_cli(fd, format, "Builtin Feature", "Default", "Current");
1731 ast_cli(fd, format, "---------------", "-------", "-------");
1733 ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
1735 fcount = sizeof(builtin_features) / sizeof(builtin_features[0]);
1737 for (i = 0; i < fcount; i++)
1739 ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
1742 ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
1743 ast_cli(fd, format, "---------------", "-------", "-------");
1744 if (AST_LIST_EMPTY(&feature_list)) {
1745 ast_cli(fd, "(none)\n");
1748 AST_LIST_LOCK(&feature_list);
1749 AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) {
1750 ast_cli(fd, format, feature->sname, "no def", feature->exten);
1752 AST_LIST_UNLOCK(&feature_list);
1754 ast_cli(fd, "\nCall parking\n");
1755 ast_cli(fd, "------------\n");
1756 ast_cli(fd,"%-20s: %s\n", "Parking extension", parking_ext);
1757 ast_cli(fd,"%-20s: %s\n", "Parking context", parking_con);
1758 ast_cli(fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
1761 return RESULT_SUCCESS;
1764 static char showfeatures_help[] =
1765 "Usage: show features\n"
1766 " Lists currently configured features.\n";
1768 static struct ast_cli_entry showfeatures =
1769 { { "show", "features", NULL }, handle_showfeatures, "Lists configured features", showfeatures_help };
1771 static int handle_parkedcalls(int fd, int argc, char *argv[])
1773 struct parkeduser *cur;
1776 ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
1777 , "Context", "Extension", "Pri", "Timeout");
1779 ast_mutex_lock(&parking_lock);
1781 for (cur = parkinglot; cur; cur = cur->next) {
1782 ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n"
1783 ,cur->parkingnum, cur->chan->name, cur->context, cur->exten
1784 ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
1788 ast_mutex_unlock(&parking_lock);
1789 ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : "");
1792 return RESULT_SUCCESS;
1795 static char showparked_help[] =
1796 "Usage: show parkedcalls\n"
1797 " Lists currently parked calls.\n";
1799 static struct ast_cli_entry showparked =
1800 { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
1802 /*! \brief Dump lot status */
1803 static int manager_parking_status( struct mansession *s, struct message *m )
1805 struct parkeduser *cur;
1806 char *id = astman_get_header(m,"ActionID");
1807 char idText[256] = "";
1809 if (!ast_strlen_zero(id))
1810 snprintf(idText,256,"ActionID: %s\r\n",id);
1812 astman_send_ack(s, m, "Parked calls will follow");
1814 ast_mutex_lock(&parking_lock);
1816 for (cur=parkinglot; cur; cur = cur->next) {
1817 astman_append(s, "Event: ParkedCall\r\n"
1823 "CallerIDName: %s\r\n"
1826 cur->parkingnum, cur->chan->name, cur->peername,
1827 (long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL),
1828 S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */
1829 S_OR(cur->chan->cid.cid_name, ""),
1834 "Event: ParkedCallsComplete\r\n"
1838 ast_mutex_unlock(&parking_lock);
1840 return RESULT_SUCCESS;
1843 static char mandescr_park[] =
1844 "Description: Park a channel.\n"
1845 "Variables: (Names marked with * are required)\n"
1846 " *Channel: Channel name to park\n"
1847 " *Channel2: Channel to announce park info to (and return to if timeout)\n"
1848 " Timeout: Number of milliseconds to wait before callback.\n";
1850 static int manager_park(struct mansession *s, struct message *m)
1852 char *channel = astman_get_header(m, "Channel");
1853 char *channel2 = astman_get_header(m, "Channel2");
1854 char *timeout = astman_get_header(m, "Timeout");
1859 struct ast_channel *ch1, *ch2;
1861 if (ast_strlen_zero(channel)) {
1862 astman_send_error(s, m, "Channel not specified");
1866 if (ast_strlen_zero(channel2)) {
1867 astman_send_error(s, m, "Channel2 not specified");
1871 ch1 = ast_get_channel_by_name_locked(channel);
1873 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
1874 astman_send_error(s, m, buf);
1878 ch2 = ast_get_channel_by_name_locked(channel2);
1880 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
1881 astman_send_error(s, m, buf);
1882 ast_mutex_unlock(&ch1->lock);
1886 if (!ast_strlen_zero(timeout)) {
1887 sscanf(timeout, "%d", &to);
1890 res = ast_masq_park_call(ch1, ch2, to, &parkExt);
1892 ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
1893 astman_send_ack(s, m, "Park successful");
1895 astman_send_error(s, m, "Park failure");
1898 ast_mutex_unlock(&ch1->lock);
1899 ast_mutex_unlock(&ch2->lock);
1905 int ast_pickup_call(struct ast_channel *chan)
1907 struct ast_channel *cur = NULL;
1910 while ( (cur = ast_channel_walk_locked(cur)) != NULL) {
1913 (chan->pickupgroup & cur->callgroup) &&
1914 ((cur->_state == AST_STATE_RINGING) ||
1915 (cur->_state == AST_STATE_RING))) {
1918 ast_mutex_unlock(&cur->lock);
1922 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
1923 res = ast_answer(chan);
1925 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
1926 res = ast_queue_control(chan, AST_CONTROL_ANSWER);
1928 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
1929 res = ast_channel_masquerade(cur, chan);
1931 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
1932 ast_mutex_unlock(&cur->lock);
1935 ast_log(LOG_DEBUG, "No call pickup possible...\n");
1940 static int load_config(void)
1942 int start = 0, end = 0;
1943 struct ast_context *con = NULL;
1944 struct ast_config *cfg = NULL;
1945 struct ast_variable *var = NULL;
1946 char old_parking_ext[AST_MAX_EXTENSION];
1947 char old_parking_con[AST_MAX_EXTENSION] = "";
1949 if (!ast_strlen_zero(parking_con)) {
1950 strcpy(old_parking_ext, parking_ext);
1951 strcpy(old_parking_con, parking_con);
1954 /* Reset to defaults */
1955 strcpy(parking_con, "parkedcalls");
1956 strcpy(parking_con_dial, "park-dial");
1957 strcpy(parking_ext, "700");
1958 strcpy(pickup_ext, "*8");
1959 courtesytone[0] = '\0';
1960 strcpy(xfersound, "beep");
1961 strcpy(xferfailsound, "pbx-invalid");
1962 parking_start = 701;
1967 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1968 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
1970 cfg = ast_config_load("features.conf");
1972 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
1973 if (!strcasecmp(var->name, "parkext")) {
1974 ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
1975 } else if (!strcasecmp(var->name, "context")) {
1976 ast_copy_string(parking_con, var->value, sizeof(parking_con));
1977 } else if (!strcasecmp(var->name, "parkingtime")) {
1978 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
1979 ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
1980 parkingtime = DEFAULT_PARK_TIME;
1982 parkingtime = parkingtime * 1000;
1983 } else if (!strcasecmp(var->name, "parkpos")) {
1984 if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
1985 ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of parking.conf\n", var->lineno);
1987 parking_start = start;
1990 } else if (!strcasecmp(var->name, "findslot")) {
1991 parkfindnext = (!strcasecmp(var->value, "next"));
1992 } else if (!strcasecmp(var->name, "adsipark")) {
1993 adsipark = ast_true(var->value);
1994 } else if (!strcasecmp(var->name, "transferdigittimeout")) {
1995 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
1996 ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
1997 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1999 transferdigittimeout = transferdigittimeout * 1000;
2000 } else if (!strcasecmp(var->name, "featuredigittimeout")) {
2001 if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
2002 ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
2003 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
2005 } else if (!strcasecmp(var->name, "courtesytone")) {
2006 ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
2007 } else if (!strcasecmp(var->name, "parkedplay")) {
2008 if (!strcasecmp(var->value, "both"))
2010 else if (!strcasecmp(var->value, "parked"))
2014 } else if (!strcasecmp(var->name, "xfersound")) {
2015 ast_copy_string(xfersound, var->value, sizeof(xfersound));
2016 } else if (!strcasecmp(var->name, "xferfailsound")) {
2017 ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
2018 } else if (!strcasecmp(var->name, "pickupexten")) {
2019 ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
2024 for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
2025 if (remap_feature(var->name, var->value))
2026 ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
2029 /* Map a key combination to an application*/
2030 ast_unregister_features();
2031 for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
2032 char *tmp_val = ast_strdup(var->value);
2033 char *exten, *party=NULL, *app=NULL, *app_args=NULL;
2036 /*! \todo XXX No memory. We should probably break, but at least we do not
2037 * insist on this entry or we could be stuck in an
2043 /* strsep() sets the argument to NULL if match not found, and it
2044 * is safe to use it with a NULL argument, so we don't check
2047 exten = strsep(&tmp_val,",");
2048 party = strsep(&tmp_val,",");
2049 app = strsep(&tmp_val,",");
2050 app_args = strsep(&tmp_val,",");
2052 /*! \todo XXX var_name or app_args ? */
2053 if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(party) || ast_strlen_zero(var->name)) {
2054 ast_log(LOG_NOTICE, "Please check the feature Mapping Syntax, either extension, name, or app aren't provided %s %s %s %s\n",app,exten,party,var->name);
2060 struct ast_call_feature *feature;
2063 if (!(feature = find_feature(var->name))) {
2066 if (!(feature = ast_calloc(1, sizeof(*feature)))) {
2072 ast_copy_string(feature->sname,var->name,FEATURE_SNAME_LEN);
2073 ast_copy_string(feature->app,app,FEATURE_APP_LEN);
2074 ast_copy_string(feature->exten, exten,FEATURE_EXTEN_LEN);
2078 ast_copy_string(feature->app_args,app_args,FEATURE_APP_ARGS_LEN);
2080 ast_copy_string(feature->exten, exten,sizeof(feature->exten));
2081 feature->operation=feature_exec_app;
2082 ast_set_flag(feature,AST_FEATURE_FLAG_NEEDSDTMF);
2084 if (!strcasecmp(party,"caller"))
2085 ast_set_flag(feature,AST_FEATURE_FLAG_CALLER);
2086 else if (!strcasecmp(party, "callee"))
2087 ast_set_flag(feature,AST_FEATURE_FLAG_CALLEE);
2089 ast_log(LOG_NOTICE, "Invalid party specification for feature '%s', must be caller, or callee\n", var->name);
2093 ast_register_feature(feature);
2094 /* XXX do we need to free it if mallocd ? */
2096 if (option_verbose >=1)
2097 ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s' with code '%s'\n", var->name, app, exten);
2101 ast_config_destroy(cfg);
2103 /* Remove the old parking extension */
2104 if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
2105 ast_context_remove_extension2(con, old_parking_ext, 1, registrar);
2107 ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
2110 if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
2111 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
2114 return ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), FREE, registrar);
2117 static int reload(void *mod)
2119 return load_config();
2122 static int load_module(void *mod)
2127 AST_LIST_HEAD_INIT(&feature_list);
2128 memset(parking_ext, 0, sizeof(parking_ext));
2129 memset(parking_con, 0, sizeof(parking_con));
2131 if ((res = load_config()))
2133 ast_cli_register(&showparked);
2134 ast_cli_register(&showfeatures);
2135 ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
2136 res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
2138 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
2140 ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" );
2141 ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
2142 "Park a channel", mandescr_park);
2148 static int unload_module(void *mod)
2150 STANDARD_HANGUP_LOCALUSERS;
2152 ast_manager_unregister("ParkedCalls");
2153 ast_manager_unregister("Park");
2154 ast_cli_unregister(&showfeatures);
2155 ast_cli_unregister(&showparked);
2156 ast_unregister_application(parkcall);
2157 return ast_unregister_application(parkedcall);
2160 static const char *description(void)
2162 return "Call Features Resource";
2165 static const char *key(void)
2167 return ASTERISK_GPL_KEY;
2170 STD_MOD(MOD_0 | NO_UNLOAD, reload, NULL, NULL);