2 * Asterisk -- A telephony toolkit for Linux.
4 * Routines implementing call parking
6 * Copyright (C) 1999 - 2005, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/options.h>
20 #include <asterisk/module.h>
21 #include <asterisk/translate.h>
22 #include <asterisk/app.h>
23 #include <asterisk/say.h>
24 #include <asterisk/features.h>
25 #include <asterisk/musiconhold.h>
26 #include <asterisk/config.h>
27 #include <asterisk/cli.h>
28 #include <asterisk/manager.h>
29 #include <asterisk/utils.h>
30 #include <asterisk/adsi.h>
39 #include <sys/signal.h>
40 #include <netinet/in.h>
42 #define DEFAULT_PARK_TIME 45000
43 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
44 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
46 static char *parkedcall = "ParkedCall";
48 /* No more than 45 seconds parked before you do something with them */
49 static int parkingtime = DEFAULT_PARK_TIME;
51 /* Context for which parking is made accessible */
52 static char parking_con[AST_MAX_EXTENSION] = "parkedcalls";
54 /* Context for dialback for parking (KLUDGE) */
55 static char parking_con_dial[AST_MAX_EXTENSION] = "park-dial";
57 /* Extension you type to park the call */
58 static char parking_ext[AST_MAX_EXTENSION] = "700";
60 static char pickup_ext[AST_MAX_EXTENSION] = "*8";
63 static char courtesytone[256] = "";
64 static char xfersound[256] = "beep";
65 static char xferfailsound[256] = "pbx-invalid";
67 /* First available extension for parking */
68 static int parking_start = 701;
70 /* Last available extension for parking */
71 static int parking_stop = 750;
73 static int adsipark = 0;
75 static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
76 static int featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
78 /* Default courtesy tone played when party joins conference */
80 /* Registrar for operations */
81 static char *registrar = "res_features";
83 static char *synopsis = "Answer a parked call";
85 static char *descrip = "ParkedCall(exten):"
86 "Used to connect to a parked call. This application is always\n"
87 "registered internally and does not need to be explicitly added\n"
88 "into the dialplan, although you should include the 'parkedcalls'\n"
91 static char *parkcall = "Park";
93 static char *synopsis2 = "Park yourself";
95 static char *descrip2 = "Park(exten):"
96 "Used to park yourself (typically in combination with a supervised\n"
97 "transfer to know the parking space). This application is always\n"
98 "registered internally and does not need to be explicitly added\n"
99 "into the dialplan, although you should include the 'parkedcalls'\n"
102 static struct ast_app *monitor_app=NULL;
103 static int monitor_ok=1;
106 struct ast_channel *chan;
107 struct timeval start;
109 /* Where to go if our parking time expires */
110 char context[AST_MAX_EXTENSION];
111 char exten[AST_MAX_EXTENSION];
116 unsigned char moh_trys;
117 struct parkeduser *next;
120 static struct parkeduser *parkinglot;
122 AST_MUTEX_DEFINE_STATIC(parking_lock);
124 static pthread_t parking_thread;
130 char *ast_parking_ext(void)
135 char *ast_pickup_ext(void)
140 struct ast_bridge_thread_obj
142 struct ast_bridge_config bconfig;
143 struct ast_channel *chan;
144 struct ast_channel *peer;
147 static void *ast_bridge_call_thread(void *data)
149 struct ast_bridge_thread_obj *tobj = data;
150 tobj->chan->appl = "Transferred Call";
151 tobj->chan->data = tobj->peer->name;
152 tobj->peer->appl = "Transferred Call";
153 tobj->peer->data = tobj->chan->name;
154 if (tobj->chan->cdr) {
155 ast_cdr_reset(tobj->chan->cdr,0);
156 ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
158 if (tobj->peer->cdr) {
159 ast_cdr_reset(tobj->peer->cdr,0);
160 ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
164 ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
165 ast_hangup(tobj->chan);
166 ast_hangup(tobj->peer);
167 tobj->chan = tobj->peer = NULL;
173 static void ast_bridge_call_thread_launch(void *data)
179 result = pthread_attr_init(&attr);
180 pthread_attr_setschedpolicy(&attr, SCHED_RR);
181 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
182 result = ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
183 result = pthread_attr_destroy(&attr);
188 static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
191 int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
193 char *message[5] = {NULL, NULL, NULL, NULL, NULL};
195 snprintf(tmp, sizeof(tmp), "Parked on %d", parkingnum);
197 res = adsi_load_session(chan, NULL, 0, 1);
201 return adsi_print(chan, message, justify, 1);
204 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
206 /* We put the user in the parking list, then wake up the parking thread to be sure it looks
207 after these channels too */
208 struct parkeduser *pu, *cur;
210 char exten[AST_MAX_EXTENSION];
211 struct ast_context *con;
212 pu = malloc(sizeof(struct parkeduser));
214 memset(pu,0,sizeof(struct parkeduser));
215 ast_mutex_lock(&parking_lock);
216 for (x=parking_start;x<=parking_stop;x++) {
219 if (cur->parkingnum == x)
226 if (x <= parking_stop) {
227 chan->appl = "Parked Call";
231 /* Start music on hold */
233 ast_indicate(pu->chan, AST_CONTROL_HOLD);
234 ast_moh_start(pu->chan, NULL);
236 gettimeofday(&pu->start, NULL);
239 pu->parkingtime = timeout;
241 pu->parkingtime = parkingtime;
245 strncpy(pu->peername,peer->name,sizeof(pu->peername) - 1);
247 /* Remember what had been dialed, so that if the parking
248 expires, we try to come back to the same place */
249 if (!ast_strlen_zero(chan->macrocontext))
250 strncpy(pu->context, chan->macrocontext, sizeof(pu->context)-1);
252 strncpy(pu->context, chan->context, sizeof(pu->context)-1);
253 if (!ast_strlen_zero(chan->macroexten))
254 strncpy(pu->exten, chan->macroexten, sizeof(pu->exten)-1);
256 strncpy(pu->exten, chan->exten, sizeof(pu->exten)-1);
257 if (chan->macropriority)
258 pu->priority = chan->macropriority;
260 pu->priority = chan->priority;
261 pu->next = parkinglot;
263 /* If parking a channel directly, don't quiet yet get parking running on it */
266 ast_mutex_unlock(&parking_lock);
267 /* Wake up the (presumably select()ing) thread */
268 pthread_kill(parking_thread, SIGURG);
269 if (option_verbose > 1)
270 ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d. Will timeout back to %s,%s,%d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
272 manager_event(EVENT_FLAG_CALL, "ParkedCall",
278 "CallerIDName: %s\r\n\r\n"
279 ,pu->parkingnum, pu->chan->name, peer->name
280 ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL)
281 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
282 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
286 if (adsipark && adsi_available(peer)) {
287 adsi_announce_park(peer, pu->parkingnum);
289 if (adsipark && adsi_available(peer)) {
290 adsi_unload_session(peer);
293 con = ast_context_find(parking_con);
295 con = ast_context_create(NULL,parking_con, registrar);
297 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
301 snprintf(exten, sizeof(exten), "%d", x);
302 ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), free, registrar);
304 if (peer) ast_say_digits(peer, pu->parkingnum, "", peer->language);
305 if (pu->notquiteyet) {
306 /* Wake up parking thread if we're really done */
307 ast_moh_start(pu->chan, NULL);
309 pthread_kill(parking_thread, SIGURG);
313 ast_log(LOG_WARNING, "No more parking spaces\n");
315 ast_mutex_unlock(&parking_lock);
319 ast_log(LOG_WARNING, "Out of memory\n");
325 int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
327 struct ast_channel *chan;
329 /* Make a new, fake channel that we'll use to masquerade in the real one */
330 chan = ast_channel_alloc(0);
332 /* Let us keep track of the channel name */
333 snprintf(chan->name, sizeof (chan->name), "Parked/%s",rchan->name);
334 /* Make formats okay */
335 chan->readformat = rchan->readformat;
336 chan->writeformat = rchan->writeformat;
337 ast_channel_masquerade(chan, rchan);
338 /* Setup the extensions and such */
339 strncpy(chan->context, rchan->context, sizeof(chan->context) - 1);
340 strncpy(chan->exten, rchan->exten, sizeof(chan->exten) - 1);
341 chan->priority = rchan->priority;
342 /* Make the masq execute */
346 ast_park_call(chan, peer, timeout, extout);
348 ast_log(LOG_WARNING, "Unable to create parked channel\n");
355 #define FEATURE_RETURN_HANGUP -1
356 #define FEATURE_RETURN_SUCCESSBREAK 0
357 #define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE
358 #define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER
359 #define FEATURE_RETURN_PASSDIGITS 21
360 #define FEATURE_RETURN_STOREDIGITS 22
361 #define FEATURE_RETURN_SUCCESS 23
363 #define FEATURE_SENSE_CHAN (1 << 0)
364 #define FEATURE_SENSE_PEER (1 << 1)
365 #define FEATURE_MAX_LEN 11
367 static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
369 char *touch_monitor = NULL, *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL;
372 struct ast_channel *caller_chan = NULL, *callee_chan = NULL;
384 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
389 if (!(monitor_app = pbx_findapp("Monitor"))) {
391 ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n");
395 if (!ast_strlen_zero(courtesytone)) {
396 if (ast_autoservice_start(callee_chan))
398 if (!ast_streamfile(caller_chan, courtesytone, caller_chan->language)) {
399 if (ast_waitstream(caller_chan, "") < 0) {
400 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
401 ast_autoservice_stop(callee_chan);
405 if (ast_autoservice_stop(callee_chan))
409 if (callee_chan->monitor) {
410 if (option_verbose > 3)
411 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to stop recording call.\n", code);
412 ast_monitor_stop(callee_chan, 1);
413 return FEATURE_RETURN_SUCCESS;
416 if (caller_chan && callee_chan) {
417 touch_monitor = pbx_builtin_getvar_helper(caller_chan, "TOUCH_MONITOR");
419 touch_monitor = pbx_builtin_getvar_helper(callee_chan, "TOUCH_MONITOR");
422 len = strlen(touch_monitor) + 50;
424 snprintf(args, len, "WAV|auto-%ld-%s|m", time(NULL), touch_monitor);
426 caller_chan_id = ast_strdupa(caller_chan->cid.cid_num ? caller_chan->cid.cid_num : caller_chan->name);
427 callee_chan_id = ast_strdupa(callee_chan->cid.cid_num ? callee_chan->cid.cid_num : callee_chan->name);
428 len = strlen(caller_chan_id) + strlen(callee_chan_id) + 50;
430 snprintf(args, len, "WAV|auto-%ld-%s-%s|m", time(NULL), caller_chan_id, callee_chan_id);
433 for( x = 0; x < strlen(args); x++)
437 if (option_verbose > 3)
438 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call. filename: %s\n", code, args);
440 pbx_exec(callee_chan, monitor_app, args, 1);
442 return FEATURE_RETURN_SUCCESS;
445 ast_log(LOG_NOTICE,"Cannot record the call. One or both channels have gone away.\n");
449 static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
451 if (option_verbose > 3)
452 ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
453 return FEATURE_RETURN_HANGUP;
456 static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
458 struct ast_channel *transferer;
459 struct ast_channel *transferee;
460 char *transferer_real_context;
464 if (sense == FEATURE_SENSE_PEER) {
471 if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
472 !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
473 /* Use the non-macro context to transfer the call */
474 if (!ast_strlen_zero(transferer->macrocontext))
475 transferer_real_context = transferer->macrocontext;
477 transferer_real_context = transferer->context;
479 /* Start autoservice on chan while we talk
481 ast_indicate(transferee, AST_CONTROL_HOLD);
482 ast_autoservice_start(transferee);
483 ast_moh_start(transferee, NULL);
485 memset(newext, 0, sizeof(newext));
488 if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
489 ast_moh_stop(transferee);
490 ast_autoservice_stop(transferee);
491 ast_indicate(transferee, AST_CONTROL_UNHOLD);
494 if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
495 ast_moh_stop(transferee);
496 ast_autoservice_stop(transferee);
497 ast_indicate(transferee, AST_CONTROL_UNHOLD);
499 } else if (res > 0) {
500 /* If they've typed a digit already, handle it */
501 newext[0] = (char) res;
504 ast_stopstream(transferer);
505 res = ast_app_dtget(transferer, transferer_real_context, newext, sizeof(newext), 100, transferdigittimeout);
507 ast_moh_stop(transferee);
508 ast_autoservice_stop(transferee);
509 ast_indicate(transferee, AST_CONTROL_UNHOLD);
512 if (!strcmp(newext, ast_parking_ext())) {
513 ast_moh_stop(transferee);
515 res = ast_autoservice_stop(transferee);
516 ast_indicate(transferee, AST_CONTROL_UNHOLD);
519 else if (!ast_park_call(transferee, transferer, 0, NULL)) {
520 /* We return non-zero, but tell the PBX not to hang the channel when
521 the thread dies -- We have to be careful now though. We are responsible for
522 hanging up the channel, else it will never be hung up! */
524 if (transferer==peer)
525 res=AST_PBX_KEEPALIVE;
527 res=AST_PBX_NO_HANGUP_PEER;
530 ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
532 /* XXX Maybe we should have another message here instead of invalid extension XXX */
533 } else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
534 pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
535 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
536 ast_moh_stop(transferee);
537 res=ast_autoservice_stop(transferee);
538 ast_indicate(transferee, AST_CONTROL_UNHOLD);
539 if (!transferee->pbx) {
540 /* Doh! Use our handy async_goto functions */
541 if (option_verbose > 2)
542 ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
543 ,transferee->name, newext, transferer_real_context);
544 if (ast_async_goto(transferee, transferer_real_context, newext, 1))
545 ast_log(LOG_WARNING, "Async goto failed :-(\n");
548 /* Set the channel's new extension, since it exists, using transferer context */
549 strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
550 strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
551 transferee->priority = 0;
555 if (option_verbose > 2)
556 ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
558 if (!ast_strlen_zero(xferfailsound))
559 res = ast_streamfile(transferer, xferfailsound, transferee->language);
563 ast_moh_stop(transferee);
564 ast_autoservice_stop(transferee);
565 ast_indicate(transferee, AST_CONTROL_UNHOLD);
568 res = ast_waitstream(transferer, AST_DIGIT_ANY);
569 ast_stopstream(transferer);
570 ast_moh_stop(transferee);
571 res = ast_autoservice_stop(transferee);
572 ast_indicate(transferee, AST_CONTROL_UNHOLD);
574 if (option_verbose > 1)
575 ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
578 return FEATURE_RETURN_SUCCESS;
581 static int builtin_atxfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
583 struct ast_channel *transferer;
584 struct ast_channel *transferee;
585 struct ast_channel *newchan, *xferchan=NULL;
587 struct ast_bridge_config bconfig;
588 char *transferer_real_context;
589 char xferto[256],dialstr[265];
593 struct ast_frame *f = NULL;
594 struct ast_bridge_thread_obj *tobj;
596 ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) XXX\n", chan->name, peer->name, sense);
597 if (sense == FEATURE_SENSE_PEER) {
604 if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
605 !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
606 /* Use the non-macro context to transfer the call */
607 if (!ast_strlen_zero(transferer->macrocontext))
608 transferer_real_context = transferer->macrocontext;
610 transferer_real_context = transferer->context;
612 /* Start autoservice on chan while we talk
614 ast_indicate(transferee, AST_CONTROL_HOLD);
615 ast_autoservice_start(transferee);
616 ast_moh_start(transferee, NULL);
617 memset(xferto, 0, sizeof(xferto));
619 if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
620 ast_moh_stop(transferee);
621 ast_autoservice_stop(transferee);
622 ast_indicate(transferee, AST_CONTROL_UNHOLD);
625 if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
626 ast_moh_stop(transferee);
627 ast_autoservice_stop(transferee);
628 ast_indicate(transferee, AST_CONTROL_UNHOLD);
631 /* If they've typed a digit already, handle it */
632 xferto[0] = (char) res;
634 if ((ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout))) {
635 cid_num = transferer->cid.cid_num;
636 cid_name = transferer->cid.cid_name;
637 if (ast_exists_extension(transferer, transferer_real_context,xferto, 1, cid_num)) {
638 snprintf(dialstr, sizeof(dialstr), "%s@%s/n", xferto, transferer_real_context);
639 if ((newchan = ast_request_and_dial("Local", ast_best_codec(transferer->nativeformats), dialstr,30000, &outstate, cid_num, cid_name))) {
640 res = ast_channel_make_compatible(transferer, newchan);
642 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferer->name, newchan->name);
646 memset(&bconfig,0,sizeof(struct ast_bridge_config));
647 ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
648 ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
649 res = ast_bridge_call(transferer,newchan,&bconfig);
650 if (newchan->_softhangup || newchan->_state != AST_STATE_UP) {
656 if (!ast_strlen_zero(xfersound) && !ast_streamfile(transferer, xfersound, transferer->language)) {
657 if (ast_waitstream(transferer, "") < 0) {
658 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
661 ast_moh_stop(transferee);
662 ast_autoservice_stop(transferee);
663 ast_indicate(transferee, AST_CONTROL_UNHOLD);
664 transferer->_softhangup = 0;
665 return FEATURE_RETURN_SUCCESS;
668 res = ast_channel_make_compatible(transferee, newchan);
670 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferee->name, newchan->name);
676 ast_moh_stop(transferee);
678 if ((ast_autoservice_stop(transferee) < 0)
679 ||(ast_waitfordigit(transferee,100) < 0)
680 || (ast_waitfordigit(newchan,100) < 0)
681 || ast_check_hangup(transferee)
682 || ast_check_hangup(newchan)) {
688 if ((xferchan = ast_channel_alloc(0))) {
689 snprintf(xferchan->name, sizeof (xferchan->name), "Transfered/%s",transferee->name);
690 /* Make formats okay */
691 xferchan->readformat = transferee->readformat;
692 xferchan->writeformat = transferee->writeformat;
693 ast_channel_masquerade(xferchan, transferee);
694 ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
695 xferchan->_state = AST_STATE_UP;
696 ast_clear_flag(xferchan, AST_FLAGS_ALL);
697 xferchan->_softhangup = 0;
699 if ((f = ast_read(xferchan))) {
709 newchan->_state = AST_STATE_UP;
710 ast_clear_flag(newchan, AST_FLAGS_ALL);
711 newchan->_softhangup = 0;
713 tobj = malloc(sizeof(struct ast_bridge_thread_obj));
715 memset(tobj,0,sizeof(struct ast_bridge_thread_obj));
716 tobj->chan = xferchan;
717 tobj->peer = newchan;
718 tobj->bconfig = *config;
720 if (!ast_strlen_zero(xfersound) && !ast_streamfile(newchan, xfersound, newchan->language)) {
721 if (ast_waitstream(newchan, "") < 0) {
722 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
725 ast_bridge_call_thread_launch(tobj);
727 ast_log(LOG_WARNING, "Out of memory!\n");
728 ast_hangup(xferchan);
734 ast_log(LOG_WARNING, "Unable to create channel Local/%s do you have chan_local?\n",dialstr);
735 ast_moh_stop(transferee);
736 ast_autoservice_stop(transferee);
737 ast_indicate(transferee, AST_CONTROL_UNHOLD);
738 if (!ast_strlen_zero(xferfailsound)) {
739 res = ast_streamfile(transferer, xferfailsound, transferer->language);
740 if (!res && (ast_waitstream(transferer, "") < 0)) {
747 ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
748 ast_moh_stop(transferee);
749 ast_autoservice_stop(transferee);
750 ast_indicate(transferee, AST_CONTROL_UNHOLD);
751 res = ast_streamfile(transferer, "beeperr", transferer->language);
752 if (!res && (ast_waitstream(transferer, "") < 0)) {
757 ast_log(LOG_WARNING, "Did not read data.\n");
758 res = ast_streamfile(transferer, "beeperr", transferer->language);
759 if (ast_waitstream(transferer, "") < 0) {
763 ast_moh_stop(transferee);
764 ast_autoservice_stop(transferee);
765 ast_indicate(transferee, AST_CONTROL_UNHOLD);
767 return FEATURE_RETURN_SUCCESS;
770 struct ast_call_feature {
774 char exten[FEATURE_MAX_LEN];
775 char default_exten[FEATURE_MAX_LEN];
776 int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense);
780 /* add atxfer and automon as undefined so you can only use em if you configure them */
781 #define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
782 struct ast_call_feature builtin_features[] =
784 { AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF },
785 { AST_FEATURE_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF },
786 { AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF },
787 { AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
790 static void unmap_features(void)
793 for (x=0;x<FEATURES_COUNT;x++)
794 strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
797 static int remap_feature(const char *name, const char *value)
801 for (x=0;x<FEATURES_COUNT;x++) {
802 if (!strcasecmp(name, builtin_features[x].sname)) {
803 strncpy(builtin_features[x].exten, value, sizeof(builtin_features[x].exten) - 1);
804 if (option_verbose > 1)
805 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);
807 } else if (!strcmp(value, builtin_features[x].exten))
808 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);
813 static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
816 struct ast_flags features;
817 int res = FEATURE_RETURN_PASSDIGITS;
819 if (sense == FEATURE_SENSE_CHAN)
820 ast_copy_flags(&features, &(config->features_caller), AST_FLAGS_ALL);
822 ast_copy_flags(&features, &(config->features_callee), AST_FLAGS_ALL);
823 ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features.flags);
824 for (x=0;x<FEATURES_COUNT;x++) {
825 if ((ast_test_flag(&features, builtin_features[x].feature_mask)) &&
826 !ast_strlen_zero(builtin_features[x].exten)) {
827 /* Feature is up for consideration */
828 if (!strcmp(builtin_features[x].exten, code)) {
829 res = builtin_features[x].operation(chan, peer, config, code, sense);
831 } else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
832 if (res == FEATURE_RETURN_PASSDIGITS)
833 res = FEATURE_RETURN_STOREDIGITS;
840 static void set_config_flags(struct ast_bridge_config *config)
843 ast_clear_flag(config, AST_FLAGS_ALL);
844 for (x=0;x<FEATURES_COUNT;x++) {
845 if (ast_test_flag(&(config->features_caller), builtin_features[x].feature_mask)) {
846 if (ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
847 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
849 if (ast_test_flag(&(config->features_callee), builtin_features[x].feature_mask)) {
850 if (ast_test_flag(builtin_features + x, AST_FEATURE_FLAG_NEEDSDTMF))
851 ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
856 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
858 /* Copy voice back and forth between the two channels. Give the peer
859 the ability to transfer calls with '#<extension' syntax. */
861 struct ast_channel *who;
862 char chan_featurecode[FEATURE_MAX_LEN + 1]="";
863 char peer_featurecode[FEATURE_MAX_LEN + 1]="";
868 struct ast_option_header *aoh;
869 struct timeval start, end;
870 struct ast_bridge_config backup_config;
871 int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
874 memset(&backup_config, 0, sizeof(backup_config));
877 pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
878 pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
880 pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL);
884 if (!(monitor_app = pbx_findapp("Monitor")))
887 if ((monitor_exec = pbx_builtin_getvar_helper(chan, "AUTO_MONITOR")))
888 pbx_exec(chan, monitor_app, monitor_exec, 1);
889 else if ((monitor_exec = pbx_builtin_getvar_helper(peer, "AUTO_MONITOR")))
890 pbx_exec(peer, monitor_app, monitor_exec, 1);
893 allowdisconnect_in = ast_test_flag(&(config->features_callee), AST_FEATURE_DISCONNECT);
894 allowdisconnect_out = ast_test_flag(&(config->features_caller), AST_FEATURE_DISCONNECT);
895 allowredirect_in = ast_test_flag(&(config->features_callee), AST_FEATURE_REDIRECT);
896 allowredirect_out = ast_test_flag(&(config->features_caller), AST_FEATURE_REDIRECT);
897 set_config_flags(config);
898 config->firstpass = 1;
900 /* Answer if need be */
901 if (ast_answer(chan))
903 peer->appl = "Bridged Call";
904 peer->data = chan->name;
905 /* copy the userfield from the B-leg to A-leg if applicable */
906 if (chan->cdr && peer->cdr && !ast_strlen_zero(peer->cdr->userfield)) {
908 if (!ast_strlen_zero(chan->cdr->userfield)) {
909 snprintf(tmp, sizeof(tmp), "%s;%s",chan->cdr->userfield, peer->cdr->userfield);
910 ast_cdr_appenduserfield(chan, tmp);
912 ast_cdr_setuserfield(chan, peer->cdr->userfield);
913 /* free the peer's cdr without ast_cdr_free complaining */
918 if (config->timelimit)
919 gettimeofday(&start, NULL);
920 res = ast_channel_bridge(chan,peer,config,&f, &who);
921 if (config->timelimit) {
922 /* Update time limit for next pass */
923 gettimeofday(&end, NULL);
924 diff = (end.tv_sec - start.tv_sec) * 1000;
925 diff += (end.tv_usec - start.tv_usec) / 1000;
926 config->timelimit -= diff;
928 /* Running on backup config, meaning a feature might be being
929 activated, but that's no excuse to keep things going
931 if (backup_config.timelimit && ((backup_config.timelimit -= diff) <= 0)) {
932 ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
933 config->timelimit = 0;
939 } else if (config->timelimit <= 0) {
940 /* Not *really* out of time, just out of time for
941 digits to come in for features. */
942 ast_log(LOG_DEBUG, "Timed out for feature!\n");
943 if (!ast_strlen_zero(peer_featurecode)) {
944 ast_dtmf_stream(chan, peer, peer_featurecode, 0);
945 memset(peer_featurecode, 0, sizeof(peer_featurecode));
947 if (!ast_strlen_zero(chan_featurecode)) {
948 ast_dtmf_stream(peer, chan, chan_featurecode, 0);
949 memset(chan_featurecode, 0, sizeof(chan_featurecode));
953 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
955 /* Restore original (possibly time modified) bridge config */
956 memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
957 memset(&backup_config, 0, sizeof(backup_config));
959 hadfeatures = hasfeatures;
960 /* Continue as we were */
964 if (config->timelimit <=0) {
965 /* We ran out of time */
966 config->timelimit = 0;
976 ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
980 if (!f || ((f->frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) ||
981 (f->subclass == AST_CONTROL_CONGESTION)))) {
985 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) {
987 ast_indicate(peer, AST_CONTROL_RINGING);
989 ast_indicate(chan, AST_CONTROL_RINGING);
991 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == -1)) {
993 ast_indicate(peer, -1);
995 ast_indicate(chan, -1);
997 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_FLASH)) {
999 ast_indicate(peer, AST_CONTROL_FLASH);
1001 ast_indicate(chan, AST_CONTROL_FLASH);
1003 if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_OPTION)) {
1005 /* Forward option Requests */
1006 if (aoh && (aoh->flag == AST_OPTION_FLAG_REQUEST)) {
1008 ast_channel_setoption(peer, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
1010 ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
1013 /* check for '*', if we find it it's time to disconnect */
1014 if (f && (f->frametype == AST_FRAME_DTMF)) {
1017 struct ast_channel *other;
1018 hadfeatures = hasfeatures;
1019 /* This cannot overrun because the longest feature is one shorter than our buffer */
1022 sense = FEATURE_SENSE_CHAN;
1023 featurecode = chan_featurecode;
1026 sense = FEATURE_SENSE_PEER;
1027 featurecode = peer_featurecode;
1029 featurecode[strlen(featurecode)] = f->subclass;
1030 config->timelimit = backup_config.timelimit;
1031 res = ast_feature_interpret(chan, peer, config, featurecode, sense);
1033 case FEATURE_RETURN_PASSDIGITS:
1034 ast_dtmf_stream(other, who, featurecode, 0);
1036 case FEATURE_RETURN_SUCCESS:
1037 memset(featurecode, 0, sizeof(chan_featurecode));
1040 if (res >= FEATURE_RETURN_PASSDIGITS) {
1046 hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
1047 if (hadfeatures && !hasfeatures) {
1048 /* Restore backup */
1049 memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
1050 memset(&backup_config, 0, sizeof(struct ast_bridge_config));
1051 } else if (hasfeatures) {
1053 /* Backup configuration */
1054 memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
1055 /* Setup temporary config options */
1056 config->play_warning = 0;
1057 ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
1058 ast_clear_flag(&(config->features_callee),AST_FEATURE_PLAY_WARNING);
1059 config->warning_freq = 0;
1060 config->warning_sound = NULL;
1061 config->end_sound = NULL;
1062 config->start_sound = NULL;
1063 config->firstpass = 0;
1065 config->timelimit = featuredigittimeout;
1066 ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->timelimit);
1075 static void *do_parking_thread(void *ignore)
1078 struct parkeduser *pu, *pl, *pt = NULL;
1080 struct ast_frame *f;
1081 char exten[AST_MAX_EXTENSION];
1083 char returnexten[AST_MAX_EXTENSION];
1084 struct ast_context *con;
1087 fd_set nrfds, nefds;
1094 ast_mutex_lock(&parking_lock);
1100 if (pu->notquiteyet) {
1101 /* Pretend this one isn't here yet */
1106 gettimeofday(&tv, NULL);
1107 tms = (tv.tv_sec - pu->start.tv_sec) * 1000 + (tv.tv_usec - pu->start.tv_usec) / 1000;
1108 if (tms > pu->parkingtime) {
1109 /* Stop music on hold */
1110 ast_moh_stop(pu->chan);
1111 ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
1112 /* Get chan, exten from derived kludge */
1113 if (pu->peername[0]) {
1114 peername = ast_strdupa(pu->peername);
1115 cp = strrchr(peername, '-');
1118 con = ast_context_find(parking_con_dial);
1120 con = ast_context_create(NULL, parking_con_dial, registrar);
1122 ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
1126 snprintf(returnexten, sizeof(returnexten), "%s||t", peername);
1127 ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), free, registrar);
1129 strncpy(pu->chan->exten, peername, sizeof(pu->chan->exten) - 1);
1130 strncpy(pu->chan->context, parking_con_dial, sizeof(pu->chan->context) - 1);
1131 pu->chan->priority = 1;
1134 /* They've been waiting too long, send them back to where they came. Theoretically they
1135 should have their original extensions and such, but we copy to be on the safe side */
1136 strncpy(pu->chan->exten, pu->exten, sizeof(pu->chan->exten)-1);
1137 strncpy(pu->chan->context, pu->context, sizeof(pu->chan->context)-1);
1138 pu->chan->priority = pu->priority;
1141 manager_event(EVENT_FLAG_CALL, "ParkedCallTimeOut",
1145 "CallerIDName: %s\r\n\r\n"
1146 ,pu->parkingnum, pu->chan->name
1147 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
1148 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
1151 if (option_verbose > 1)
1152 ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d. Returning to %s,%s,%d\n", pu->chan->name, pu->parkingnum, pu->chan->context, pu->chan->exten, pu->chan->priority);
1153 /* Start up the PBX, or hang them up */
1154 if (ast_pbx_start(pu->chan)) {
1155 ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
1156 ast_hangup(pu->chan);
1158 /* And take them out of the parking lot */
1160 pl->next = pu->next;
1162 parkinglot = pu->next;
1165 con = ast_context_find(parking_con);
1167 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1168 if (ast_context_remove_extension2(con, exten, 1, NULL))
1169 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1171 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1174 for (x=0; x<AST_MAX_FDS; x++) {
1175 if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
1176 if (FD_ISSET(pu->chan->fds[x], &efds))
1177 ast_set_flag(pu->chan, AST_FLAG_EXCEPTION);
1179 ast_clear_flag(pu->chan, AST_FLAG_EXCEPTION);
1181 /* See if they need servicing */
1182 f = ast_read(pu->chan);
1183 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
1185 manager_event(EVENT_FLAG_CALL, "ParkedCallGiveUp",
1189 "CallerIDName: %s\r\n\r\n"
1190 ,pu->parkingnum, pu->chan->name
1191 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
1192 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
1195 /* There's a problem, hang them up*/
1196 if (option_verbose > 1)
1197 ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name);
1198 ast_hangup(pu->chan);
1199 /* And take them out of the parking lot */
1201 pl->next = pu->next;
1203 parkinglot = pu->next;
1206 con = ast_context_find(parking_con);
1208 snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
1209 if (ast_context_remove_extension2(con, exten, 1, NULL))
1210 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1212 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1216 /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
1218 if (pu->moh_trys < 3 && !pu->chan->generatordata) {
1219 ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting.\n");
1220 ast_moh_start(pu->chan, NULL);
1223 goto std; /* XXX Ick: jumping into an else statement??? XXX */
1227 if (x >= AST_MAX_FDS) {
1228 std: for (x=0; x<AST_MAX_FDS; x++) {
1229 /* Keep this one for next one */
1230 if (pu->chan->fds[x] > -1) {
1231 FD_SET(pu->chan->fds[x], &nrfds);
1232 FD_SET(pu->chan->fds[x], &nefds);
1233 if (pu->chan->fds[x] > max)
1234 max = pu->chan->fds[x];
1237 /* Keep track of our longest wait */
1238 if ((tms < ms) || (ms < 0))
1245 ast_mutex_unlock(&parking_lock);
1248 tv.tv_sec = ms / 1000;
1249 tv.tv_usec = (ms % 1000) * 1000;
1250 /* Wait for something to happen */
1251 ast_select(max + 1, &rfds, NULL, &efds, (ms > -1) ? &tv : NULL);
1252 pthread_testcancel();
1254 return NULL; /* Never reached */
1257 static int park_call_exec(struct ast_channel *chan, void *data)
1259 /* Data is unused at the moment but could contain a parking
1260 lot context eventually */
1262 struct localuser *u;
1264 /* Setup the exten/priority to be s/1 since we don't know
1265 where this call should return */
1266 strcpy(chan->exten, "s");
1268 if (chan->_state != AST_STATE_UP)
1269 res = ast_answer(chan);
1271 res = ast_safe_sleep(chan, 1000);
1273 res = ast_park_call(chan, chan, 0, NULL);
1274 LOCAL_USER_REMOVE(u);
1276 res = AST_PBX_KEEPALIVE;
1280 static int park_exec(struct ast_channel *chan, void *data)
1283 struct localuser *u;
1284 struct ast_channel *peer=NULL;
1285 struct parkeduser *pu, *pl=NULL;
1286 char exten[AST_MAX_EXTENSION];
1287 struct ast_context *con;
1290 struct ast_bridge_config config;
1293 ast_log(LOG_WARNING, "Park requires an argument (extension number)\n");
1297 park = atoi((char *)data);
1298 ast_mutex_lock(&parking_lock);
1301 if (pu->parkingnum == park) {
1303 pl->next = pu->next;
1305 parkinglot = pu->next;
1311 ast_mutex_unlock(&parking_lock);
1314 con = ast_context_find(parking_con);
1316 snprintf(exten, sizeof(exten), "%d", pu->parkingnum);
1317 if (ast_context_remove_extension2(con, exten, 1, NULL))
1318 ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
1320 ast_log(LOG_WARNING, "Whoa, no parking context?\n");
1322 manager_event(EVENT_FLAG_CALL, "UnParkedCall",
1327 "CallerIDName: %s\r\n\r\n"
1328 ,pu->parkingnum, pu->chan->name, chan->name
1329 ,(pu->chan->cid.cid_num ? pu->chan->cid.cid_num : "<unknown>")
1330 ,(pu->chan->cid.cid_name ? pu->chan->cid.cid_name : "<unknown>")
1335 /* JK02: it helps to answer the channel if not already up */
1336 if (chan->_state != AST_STATE_UP) {
1341 /* Play a courtesy beep in the calling channel to prefix the bridge connecting */
1342 if (!ast_strlen_zero(courtesytone)) {
1343 if (!ast_streamfile(chan, courtesytone, chan->language)) {
1344 if (ast_waitstream(chan, "") < 0) {
1345 ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
1353 ast_indicate(peer, AST_CONTROL_UNHOLD);
1354 res = ast_channel_make_compatible(chan, peer);
1356 ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name);
1360 /* This runs sorta backwards, since we give the incoming channel control, as if it
1361 were the person called. */
1362 if (option_verbose > 2)
1363 ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
1365 memset(&config,0,sizeof(struct ast_bridge_config));
1366 ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
1367 ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
1368 config.timelimit = 0;
1369 config.play_warning = 0;
1370 config.warning_freq = 0;
1371 config.warning_sound=NULL;
1372 res = ast_bridge_call(chan,peer,&config);
1374 /* Simulate the PBX hanging up */
1375 if (res != AST_PBX_NO_HANGUP_PEER)
1379 /* XXX Play a message XXX */
1380 dres = ast_streamfile(chan, "pbx-invalidpark", chan->language);
1382 dres = ast_waitstream(chan, "");
1384 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", "pbx-invalidpark", chan->name);
1387 if (option_verbose > 2)
1388 ast_verbose(VERBOSE_PREFIX_3 "Channel %s tried to talk to nonexistent parked call %d\n", chan->name, park);
1391 LOCAL_USER_REMOVE(u);
1395 static int handle_showfeatures(int fd, int argc, char *argv[])
1399 char format[] = "%-25s %-7s %-7s\n";
1401 ast_cli(fd, format, "Feature", "Default", "Current");
1402 ast_cli(fd, format, "-------", "-------", "-------");
1404 ast_cli(fd, format, "Pickup", "*8", ast_pickup_ext()); /* default hardcoded above, so we'll hardcode it here */
1406 fcount = sizeof(builtin_features) / sizeof(builtin_features[0]);
1408 for (i = 0; i < fcount; i++)
1410 ast_cli(fd, format, builtin_features[i].fname, builtin_features[i].default_exten, builtin_features[i].exten);
1413 return RESULT_SUCCESS;
1416 static char showfeatures_help[] =
1417 "Usage: show features\n"
1418 " Lists currently configured features.\n";
1420 static struct ast_cli_entry showfeatures =
1421 { { "show", "features", NULL }, handle_showfeatures, "Lists configured features", showfeatures_help };
1423 static int handle_parkedcalls(int fd, int argc, char *argv[])
1425 struct parkeduser *cur;
1427 ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
1428 , "Context", "Extension", "Pri", "Timeout");
1430 ast_mutex_lock(&parking_lock);
1434 ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n"
1435 ,cur->parkingnum, cur->chan->name, cur->context, cur->exten
1436 ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
1441 ast_mutex_unlock(&parking_lock);
1443 return RESULT_SUCCESS;
1446 static char showparked_help[] =
1447 "Usage: show parkedcalls\n"
1448 " Lists currently parked calls.\n";
1450 static struct ast_cli_entry showparked =
1451 { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
1452 /* Dump lot status */
1453 static int manager_parking_status( struct mansession *s, struct message *m )
1455 struct parkeduser *cur;
1456 char *id = astman_get_header(m,"ActionID");
1457 char idText[256] = "";
1459 if (id && !ast_strlen_zero(id))
1460 snprintf(idText,256,"ActionID: %s\r\n",id);
1462 astman_send_ack(s, m, "Parked calls will follow");
1464 ast_mutex_lock(&parking_lock);
1468 ast_mutex_lock(&s->lock);
1469 ast_cli(s->fd, "Event: ParkedCall\r\n"
1474 "CallerIDName: %s\r\n"
1477 ,cur->parkingnum, cur->chan->name
1478 ,(long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL)
1479 ,(cur->chan->cid.cid_num ? cur->chan->cid.cid_num : "")
1480 ,(cur->chan->cid.cid_name ? cur->chan->cid.cid_name : "")
1482 ast_mutex_unlock(&s->lock);
1488 "Event: ParkedCallsComplete\r\n"
1492 ast_mutex_unlock(&parking_lock);
1494 return RESULT_SUCCESS;
1498 static int load_config(void)
1500 int start = 0, end = 0;
1501 struct ast_context *con = NULL;
1502 struct ast_config *cfg = NULL;
1503 struct ast_variable *var = NULL;
1505 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1506 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
1508 cfg = ast_config_load("features.conf");
1510 cfg = ast_config_load("parking.conf");
1512 ast_log(LOG_NOTICE, "parking.conf is deprecated in favor of 'features.conf'. Please rename it.\n");
1515 var = ast_variable_browse(cfg, "general");
1517 if (!strcasecmp(var->name, "parkext")) {
1518 strncpy(parking_ext, var->value, sizeof(parking_ext) - 1);
1519 } else if (!strcasecmp(var->name, "context")) {
1520 strncpy(parking_con, var->value, sizeof(parking_con) - 1);
1521 } else if (!strcasecmp(var->name, "parkingtime")) {
1522 if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
1523 ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
1524 parkingtime = DEFAULT_PARK_TIME;
1526 parkingtime = parkingtime * 1000;
1527 } else if (!strcasecmp(var->name, "parkpos")) {
1528 if (sscanf(var->value, "%i-%i", &start, &end) != 2) {
1529 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);
1531 parking_start = start;
1534 } else if (!strcasecmp(var->name, "adsipark")) {
1535 adsipark = ast_true(var->value);
1536 } else if (!strcasecmp(var->name, "transferdigittimeout")) {
1537 if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) {
1538 ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value);
1539 transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
1541 transferdigittimeout = transferdigittimeout * 1000;
1542 } else if (!strcasecmp(var->name, "featuredigittimeout")) {
1543 if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (featuredigittimeout < 1)) {
1544 ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
1545 featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
1547 } else if (!strcasecmp(var->name, "courtesytone")) {
1548 strncpy(courtesytone, var->value, sizeof(courtesytone) - 1);
1549 } else if (!strcasecmp(var->name, "xfersound")) {
1550 strncpy(xfersound, var->value, sizeof(xfersound) - 1);
1551 } else if (!strcasecmp(var->name, "xferfailsound")) {
1552 strncpy(xferfailsound, var->value, sizeof(xferfailsound) - 1);
1553 } else if (!strcasecmp(var->name, "pickupexten")) {
1554 strncpy(pickup_ext, var->value, sizeof(pickup_ext) - 1);
1559 var = ast_variable_browse(cfg, "featuremap");
1561 if (remap_feature(var->name, var->value))
1562 ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
1565 ast_config_destroy(cfg);
1569 ast_context_remove_extension2(con, ast_parking_ext(), 1, registrar);
1571 if (!(con = ast_context_find(parking_con))) {
1572 if (!(con = ast_context_create(NULL, parking_con, registrar))) {
1573 ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
1577 return ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""),free, registrar);
1581 return load_config();
1584 int load_module(void)
1587 if ((res = load_config()))
1589 ast_cli_register(&showparked);
1590 ast_cli_register(&showfeatures);
1591 ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL);
1592 res = ast_register_application(parkedcall, park_exec, synopsis, descrip);
1594 res = ast_register_application(parkcall, park_call_exec, synopsis2, descrip2);
1596 ast_manager_register( "ParkedCalls", 0, manager_parking_status, "List parked calls" );
1601 int ast_pickup_call(struct ast_channel *chan)
1603 struct ast_channel *cur;
1605 cur = ast_channel_walk_locked(NULL);
1609 (chan->pickupgroup & cur->callgroup) &&
1610 ((cur->_state == AST_STATE_RINGING) ||
1611 (cur->_state == AST_STATE_RING))) {
1614 ast_mutex_unlock(&cur->lock);
1615 cur = ast_channel_walk_locked(cur);
1619 ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n",cur->name, chan->name);
1620 res = ast_answer(chan);
1622 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
1623 res = ast_queue_control(chan, AST_CONTROL_ANSWER);
1625 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
1626 res = ast_channel_masquerade(cur, chan);
1628 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, cur->name); /* Done */
1629 ast_mutex_unlock(&cur->lock);
1632 ast_log(LOG_DEBUG, "No call pickup possible...\n");
1637 int unload_module(void)
1639 STANDARD_HANGUP_LOCALUSERS;
1641 ast_manager_unregister( "ParkedCalls" );
1642 ast_cli_unregister(&showfeatures);
1643 ast_cli_unregister(&showparked);
1644 ast_unregister_application(parkcall);
1645 return ast_unregister_application(parkedcall);
1648 char *description(void)
1650 return "Call Parking Resource";
1655 /* Never allow parking to be unloaded because it will
1656 unresolve needed symbols in the dialer */
1659 STANDARD_USECOUNT(res);
1668 return ASTERISK_GPL_KEY;