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 /*! \brief Take care of parked calls and unpark them if needed */
1397 static void *do_parking_thread(void *ignore)
1399 fd_set rfds, efds; /* results from previous select, to be preserved across loops. */
1404 struct parkeduser *pu, *pl, *pt = NULL;
1405 int ms = -1; /* select timeout, uninitialized */
1406 int max = -1; /* max fd, none there yet */
1407 fd_set nrfds, nefds; /* args for the next select */
1411 ast_mutex_lock(&parking_lock);
1414 /* navigate the list with prev-cur pointers to support removals */
1416 struct ast_channel *chan = pu->chan; /* shorthand */
1417 int tms; /* timeout for this item */
1418 int x; /* fd index in channel */
1419 struct ast_context *con;
1421 if (pu->notquiteyet) { /* Pretend this one isn't here yet */
1426 tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
1427 if (tms > pu->parkingtime) {
1428 /* Stop music on hold */
1430 ast_indicate(chan, AST_CONTROL_UNHOLD);
1431 /* Get chan, exten from derived kludge */
1432 if (pu->peername[0]) {
1433 char *peername = ast_strdupa(pu->peername);
1434 char *cp = strrchr(peername, '-');
1437 con = ast_context_find(parking_con_dial);
1439 con = ast_context_create(NULL, parking_con_dial, registrar);
1441 ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
1445 char returnexten[AST_MAX_EXTENSION];
1446 snprintf(returnexten, sizeof(returnexten), "%s||t", peername);
1447 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), FREE, registrar);
1449 set_c_e_p(chan, parking_con_dial, peername, 1);
1451 /* They've been waiting too long, send them back to where they came. Theoretically they
1452 should have their original extensions and such, but we copy to be on the safe side */
1453 set_c_e_p(chan, pu->context, pu->exten, pu->priority);
1456 manager_event(EVENT_FLAG_CALL, "ParkedCallTimeOut",
1460 "CallerIDName: %s\r\n",
1461 pu->parkingnum, chan->name,
1462 S_OR(chan->cid.cid_num, "<unknown>"),
1463 S_OR(chan->cid.cid_name, "<unknown>")
1466 if (option_verbose > 1)
1467 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);
1468 /* Start up the PBX, or hang them up */
1469 if (ast_pbx_start(chan)) {
1470 ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
1473 /* And take them out of the parking lot */
1475 pl->next = pu->next;
1477 parkinglot = pu->next;
1480 con = ast_context_find(parking_con);
1482 char exten[AST_MAX_EXTENSION];
1483 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1484 if (ast_context_remove_extension2(con, exten, 1, NULL))
1485 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1487 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1489 } else { /* still within parking time, process descriptors */
1490 for (x = 0; x < AST_MAX_FDS; x++) {
1491 struct ast_frame *f;
1493 if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
1494 continue; /* nothing on this descriptor */
1496 if (FD_ISSET(chan->fds[x], &efds))
1497 ast_set_flag(chan, AST_FLAG_EXCEPTION);
1499 ast_clear_flag(chan, AST_FLAG_EXCEPTION);
1501 /* See if they need servicing */
1503 if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) {
1506 manager_event(EVENT_FLAG_CALL, "ParkedCallGiveUp",
1510 "CallerIDName: %s\r\n",
1511 pu->parkingnum, chan->name,
1512 S_OR(chan->cid.cid_num, "<unknown>"),
1513 S_OR(chan->cid.cid_name, "<unknown>")
1516 /* There's a problem, hang them up*/
1517 if (option_verbose > 1)
1518 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name);
1520 /* And take them out of the parking lot */
1522 pl->next = pu->next;
1524 parkinglot = pu->next;
1527 con = ast_context_find(parking_con);
1529 char exten[AST_MAX_EXTENSION];
1530 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1531 if (ast_context_remove_extension2(con, exten, 1, NULL))
1532 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1534 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1538 /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
1540 if (pu->moh_trys < 3 && !chan->generatordata) {
1542 ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting.\n");
1543 ast_moh_start(chan, NULL);
1546 goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */
1550 if (x >= AST_MAX_FDS) {
1551 std: for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
1552 if (chan->fds[x] > -1) {
1553 FD_SET(chan->fds[x], &nrfds);
1554 FD_SET(chan->fds[x], &nefds);
1555 if (chan->fds[x] > max)
1559 /* Keep track of our shortest wait */
1560 if (tms < ms || ms < 0)
1567 ast_mutex_unlock(&parking_lock);
1571 struct timeval tv = ast_samp2tv(ms, 1000);
1572 /* Wait for something to happen */
1573 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
1575 pthread_testcancel();
1577 return NULL; /* Never reached */
1580 static int park_call_exec(struct ast_channel *chan, void *data)
1582 /* Data is unused at the moment but could contain a parking
1583 lot context eventually */
1585 struct localuser *u;
1587 /* Setup the exten/priority to be s/1 since we don't know
1588 where this call should return */
1589 strcpy(chan->exten, "s");
1591 if (chan->_state != AST_STATE_UP)
1592 res = ast_answer(chan);
1594 res = ast_safe_sleep(chan, 1000);
1596 res = ast_park_call(chan, chan, 0, NULL);
1597 LOCAL_USER_REMOVE(u);
1599 res = AST_PBX_KEEPALIVE;
1603 static int park_exec(struct ast_channel *chan, void *data)
1606 struct localuser *u;
1607 struct ast_channel *peer=NULL;
1608 struct parkeduser *pu, *pl=NULL;
1609 struct ast_context *con;
1611 struct ast_bridge_config config;
1614 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
1618 park = atoi((char *)data);
1619 ast_mutex_lock(&parking_lock);
1622 if (pu->parkingnum == park) {
1624 pl->next = pu->next;
1626 parkinglot = pu->next;
1632 ast_mutex_unlock(&parking_lock);
1635 con = ast_context_find(parking_con);
1637 char exten[AST_MAX_EXTENSION];
1638 snprintf(exten, sizeof(exten), "%d", pu->parkingnum);
1639 if (ast_context_remove_extension2(con, exten, 1, NULL))
1640 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1642 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1644 manager_event(EVENT_FLAG_CALL, "UnParkedCall",
1649 "CallerIDName: %s\r\n",
1650 pu->parkingnum, pu->chan->name, chan->name,
1651 S_OR(pu->chan->cid.cid_num, "<unknown>"),
1652 S_OR(pu->chan->cid.cid_name, "<unknown>")
1657 /* JK02: it helps to answer the channel if not already up */
1658 if (chan->_state != AST_STATE_UP)
1662 /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
1664 if (!ast_strlen_zero(courtesytone)) {
1667 ast_indicate(peer, AST_CONTROL_UNHOLD);
1668 if (parkedplay == 0) {
1669 error = stream_and_wait(chan, courtesytone, chan->language, "");
1670 } else if (parkedplay == 1) {
1671 error = stream_and_wait(peer, courtesytone, chan->language, "");
1672 } else if (parkedplay == 2) {
1673 if (!ast_streamfile(chan, courtesytone, chan->language) &&
1674 !ast_streamfile(peer, courtesytone, chan->language)) {
1675 /*! \todo XXX we would like to wait on both! */
1676 res = ast_waitstream(chan, "");
1678 res = ast_waitstream(peer, "");
1684 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
1690 res = ast_channel_make_compatible(chan, peer);
1692 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
1696 /* This runs sorta backwards, since we give the incoming channel control, as if it
1697 were the person called. */
1698 if (option_verbose > 2)
1699 ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
1701 memset(&config, 0, sizeof(struct ast_bridge_config));
1702 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1703 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
1704 config.timelimit = 0;
1705 config.play_warning = 0;
1706 config.warning_freq = 0;
1707 config.warning_sound=NULL;
1708 res = ast_bridge_call(chan, peer, &config);
1710 /* Simulate the PBX hanging up */
1711 if (res != AST_PBX_NO_HANGUP_PEER)
1715 /*! \todo XXX Play a message XXX */
1716 if (stream_and_wait(chan, "pbx-invalidpark", chan->language, ""))
1717 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
1718 if (option_verbose > 2)
1719 ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
1722 LOCAL_USER_REMOVE(u);
1726 static int handle_showfeatures(int fd, int argc, char *argv[])
1730 struct ast_call_feature *feature;
1731 char format[] = "%-25s %-7s %-7s\n";
1733 ast_cli(fd, format, "Builtin Feature", "Default", "Current");
1734 ast_cli(fd, format, "---------------", "-------", "-------");
1736 ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
1738 fcount = sizeof(builtin_features) / sizeof(builtin_features[0]);
1740 for (i = 0; i < fcount; i++)
1742 ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
1745 ast_cli(fd, format, "Dynamic Feature", "Default", "Current");
1746 ast_cli(fd, format, "---------------", "-------", "-------");
1747 if (AST_LIST_EMPTY(&feature_list)) {
1748 ast_cli(fd, "(none)\n");
1751 AST_LIST_LOCK(&feature_list);
1752 AST_LIST_TRAVERSE(&feature_list, feature, feature_entry) {
1753 ast_cli(fd, format, feature->sname, "no def", feature->exten);
1755 AST_LIST_UNLOCK(&feature_list);
1757 ast_cli(fd, "\nCall parking\n");
1758 ast_cli(fd, "------------\n");
1759 ast_cli(fd,"%-20s: %s\n", "Parking extension", parking_ext);
1760 ast_cli(fd,"%-20s: %s\n", "Parking context", parking_con);
1761 ast_cli(fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop);
1764 return RESULT_SUCCESS;
1767 static char showfeatures_help[] =
1768 "Usage: show features\n"
1769 " Lists currently configured features.\n";
1771 static struct ast_cli_entry showfeatures =
1772 { { "show", "features", NULL }, handle_showfeatures, "Lists configured features", showfeatures_help };
1774 static int handle_parkedcalls(int fd, int argc, char *argv[])
1776 struct parkeduser *cur;
1779 ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
1780 , "Context", "Extension", "Pri", "Timeout");
1782 ast_mutex_lock(&parking_lock);
1784 for (cur = parkinglot; cur; cur = cur->next) {
1785 ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n"
1786 ,cur->parkingnum, cur->chan->name, cur->context, cur->exten
1787 ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
1791 ast_mutex_unlock(&parking_lock);
1792 ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : "");
1795 return RESULT_SUCCESS;
1798 static char showparked_help[] =
1799 "Usage: show parkedcalls\n"
1800 " Lists currently parked calls.\n";
1802 static struct ast_cli_entry showparked =
1803 { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
1805 /*! \brief Dump lot status */
1806 static int manager_parking_status( struct mansession *s, struct message *m )
1808 struct parkeduser *cur;
1809 char *id = astman_get_header(m,"ActionID");
1810 char idText[256] = "";
1812 if (!ast_strlen_zero(id))
1813 snprintf(idText,256,"ActionID: %s\r\n",id);
1815 astman_send_ack(s, m, "Parked calls will follow");
1817 ast_mutex_lock(&parking_lock);
1819 for (cur=parkinglot; cur; cur = cur->next) {
1820 astman_append(s, "Event: ParkedCall\r\n"
1826 "CallerIDName: %s\r\n"
1829 cur->parkingnum, cur->chan->name, cur->peername,
1830 (long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL),
1831 S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is <unknown> */
1832 S_OR(cur->chan->cid.cid_name, ""),
1837 "Event: ParkedCallsComplete\r\n"
1841 ast_mutex_unlock(&parking_lock);
1843 return RESULT_SUCCESS;
1846 static char mandescr_park[] =
1847 "Description: Park a channel.\n"
1848 "Variables: (Names marked with * are required)\n"
1849 " *Channel: Channel name to park\n"
1850 " *Channel2: Channel to announce park info to (and return to if timeout)\n"
1851 " Timeout: Number of milliseconds to wait before callback.\n";
1853 static int manager_park(struct mansession *s, struct message *m)
1855 char *channel = astman_get_header(m, "Channel");
1856 char *channel2 = astman_get_header(m, "Channel2");
1857 char *timeout = astman_get_header(m, "Timeout");
1862 struct ast_channel *ch1, *ch2;
1864 if (ast_strlen_zero(channel)) {
1865 astman_send_error(s, m, "Channel not specified");
1869 if (ast_strlen_zero(channel2)) {
1870 astman_send_error(s, m, "Channel2 not specified");
1874 ch1 = ast_get_channel_by_name_locked(channel);
1876 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel);
1877 astman_send_error(s, m, buf);
1881 ch2 = ast_get_channel_by_name_locked(channel2);
1883 snprintf(buf, sizeof(buf), "Channel does not exist: %s", channel2);
1884 astman_send_error(s, m, buf);
1885 ast_mutex_unlock(&ch1->lock);
1889 if (!ast_strlen_zero(timeout)) {
1890 sscanf(timeout, "%d", &to);
1893 res = ast_masq_park_call(ch1, ch2, to, &parkExt);
1895 ast_softhangup(ch2, AST_SOFTHANGUP_EXPLICIT);
1896 astman_send_ack(s, m, "Park successful");
1898 astman_send_error(s, m, "Park failure");
1901 ast_mutex_unlock(&ch1->lock);
1902 ast_mutex_unlock(&ch2->lock);
1908 int ast_pickup_call(struct ast_channel *chan)
1910 struct ast_channel *cur = NULL;
1913 while ( (cur = ast_channel_walk_locked(cur)) != NULL) {
1916 (chan->pickupgroup & cur->callgroup) &&
1917 ((cur->_state == AST_STATE_RINGING) ||
1918 (cur->_state == AST_STATE_RING))) {
1921 ast_mutex_unlock(&cur->lock);
1925 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
1926 res = ast_answer(chan);
1928 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
1929 res = ast_queue_control(chan, AST_CONTROL_ANSWER);
1931 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
1932 res = ast_channel_masquerade(cur, chan);
1934 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
1935 ast_mutex_unlock(&cur->lock);
1938 ast_log(LOG_DEBUG, "No call pickup possible...\n");
1943 static int load_config(void)
1945 int start = 0, end = 0;
1946 struct ast_context *con = NULL;
1947 struct ast_config *cfg = NULL;
1948 struct ast_variable *var = NULL;
1949 char old_parking_ext[AST_MAX_EXTENSION];
1950 char old_parking_con[AST_MAX_EXTENSION] = "";
1952 if (!ast_strlen_zero(parking_con)) {
1953 strcpy(old_parking_ext, parking_ext);
1954 strcpy(old_parking_con, parking_con);
1957 /* Reset to defaults */
1958 strcpy(parking_con, "parkedcalls");
1959 strcpy(parking_con_dial, "park-dial");
1960 strcpy(parking_ext, "700");
1961 strcpy(pickup_ext, "*8");
1962 courtesytone[0] = '\0';
1963 strcpy(xfersound, "beep");
1964 strcpy(xferfailsound, "pbx-invalid");
1965 parking_start = 701;
1970 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1971 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
1973 cfg = ast_config_load("features.conf");
1975 for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
1976 if (!strcasecmp(var->name, "parkext")) {
1977 ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
1978 } else if (!strcasecmp(var->name, "context")) {
1979 ast_copy_string(parking_con, var->value, sizeof(parking_con));
1980 } else if (!strcasecmp(var->name, "parkingtime")) {
1981 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
1982 ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
1983 parkingtime = DEFAULT_PARK_TIME;
1985 parkingtime = parkingtime * 1000;
1986 } else if (!strcasecmp(var->name, "parkpos")) {
1987 if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
1988 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);
1990 parking_start = start;
1993 } else if (!strcasecmp(var->name, "findslot")) {
1994 parkfindnext = (!strcasecmp(var->value, "next"));
1995 } else if (!strcasecmp(var->name, "adsipark")) {
1996 adsipark = ast_true(var->value);
1997 } else if (!strcasecmp(var->name, "transferdigittimeout")) {
1998 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
1999 ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
2000 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
2002 transferdigittimeout = transferdigittimeout * 1000;
2003 } else if (!strcasecmp(var->name, "featuredigittimeout")) {
2004 if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
2005 ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
2006 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
2008 } else if (!strcasecmp(var->name, "courtesytone")) {
2009 ast_copy_string(courtesytone, var->value, sizeof(courtesytone));
2010 } else if (!strcasecmp(var->name, "parkedplay")) {
2011 if (!strcasecmp(var->value, "both"))
2013 else if (!strcasecmp(var->value, "parked"))
2017 } else if (!strcasecmp(var->name, "xfersound")) {
2018 ast_copy_string(xfersound, var->value, sizeof(xfersound));
2019 } else if (!strcasecmp(var->name, "xferfailsound")) {
2020 ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound));
2021 } else if (!strcasecmp(var->name, "pickupexten")) {
2022 ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext));
2027 for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) {
2028 if (remap_feature(var->name, var->value))
2029 ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
2032 /* Map a key combination to an application*/
2033 ast_unregister_features();
2034 for (var = ast_variable_browse(cfg, "applicationmap"); var; var = var->next) {
2035 char *tmp_val = ast_strdup(var->value);
2036 char *exten, *party=NULL, *app=NULL, *app_args=NULL;
2039 /*! \todo XXX No memory. We should probably break, but at least we do not
2040 * insist on this entry or we could be stuck in an
2046 /* strsep() sets the argument to NULL if match not found, and it
2047 * is safe to use it with a NULL argument, so we don't check
2050 exten = strsep(&tmp_val,",");
2051 party = strsep(&tmp_val,",");
2052 app = strsep(&tmp_val,",");
2053 app_args = strsep(&tmp_val,",");
2055 /*! \todo XXX var_name or app_args ? */
2056 if (ast_strlen_zero(app) || ast_strlen_zero(exten) || ast_strlen_zero(party) || ast_strlen_zero(var->name)) {
2057 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);
2063 struct ast_call_feature *feature;
2066 if (!(feature = find_feature(var->name))) {
2069 if (!(feature = ast_calloc(1, sizeof(*feature)))) {
2075 ast_copy_string(feature->sname,var->name,FEATURE_SNAME_LEN);
2076 ast_copy_string(feature->app,app,FEATURE_APP_LEN);
2077 ast_copy_string(feature->exten, exten,FEATURE_EXTEN_LEN);
2081 ast_copy_string(feature->app_args,app_args,FEATURE_APP_ARGS_LEN);
2083 ast_copy_string(feature->exten, exten,sizeof(feature->exten));
2084 feature->operation=feature_exec_app;
2085 ast_set_flag(feature,AST_FEATURE_FLAG_NEEDSDTMF);
2087 if (!strcasecmp(party,"caller"))
2088 ast_set_flag(feature,AST_FEATURE_FLAG_CALLER);
2089 else if (!strcasecmp(party, "callee"))
2090 ast_set_flag(feature,AST_FEATURE_FLAG_CALLEE);
2092 ast_log(LOG_NOTICE, "Invalid party specification for feature '%s', must be caller, or callee\n", var->name);
2096 ast_register_feature(feature);
2098 if (option_verbose >=1)
2099 ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s' with code '%s'\n", var->name, app, exten);
2103 ast_config_destroy(cfg);
2105 /* Remove the old parking extension */
2106 if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) {
2107 ast_context_remove_extension2(con, old_parking_ext, 1, registrar);
2109 ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
2112 if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) {
2113 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
2116 return ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), FREE, registrar);
2119 static int reload(void *mod)
2121 return load_config();
2124 static int load_module(void *mod)
2129 AST_LIST_HEAD_INIT(&feature_list);
2130 memset(parking_ext, 0, sizeof(parking_ext));
2131 memset(parking_con, 0, sizeof(parking_con));
2133 if ((res = load_config()))
2135 ast_cli_register(&showparked);
2136 ast_cli_register(&showfeatures);
2137 ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
2138 res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
2140 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
2142 ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls" );
2143 ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
2144 "Park a channel", mandescr_park);
2150 static int unload_module(void *mod)
2152 STANDARD_HANGUP_LOCALUSERS;
2154 ast_manager_unregister("ParkedCalls");
2155 ast_manager_unregister("Park");
2156 ast_cli_unregister(&showfeatures);
2157 ast_cli_unregister(&showparked);
2158 ast_unregister_application(parkcall);
2159 return ast_unregister_application(parkedcall);
2162 static const char *description(void)
2164 return "Call Features Resource";
2167 static const char *key(void)
2169 return ASTERISK_GPL_KEY;
2172 STD_MOD(MOD_0 | NO_UNLOAD, reload, NULL, NULL);