Index: apps/app_dial.c =================================================================== RCS file: /usr/cvsroot/asterisk/apps/app_dial.c,v retrieving revision 1.91 diff -u -r1.91 app_dial.c --- apps/app_dial.c 28 Jul 2004 19:25:14 -0000 1.91 +++ apps/app_dial.c 18 Aug 2004 00:02:54 -0000 @@ -59,16 +59,18 @@ " This application returns -1 if the originating channel hangs up, or if the\n" "call is bridged and either of the parties in the bridge terminate the call.\n" "The option string may contain zero or more of the following characters:\n" -" 't' -- allow the called user transfer the calling user by hitting #.\n" -" 'T' -- allow the calling user to transfer the call by hitting #.\n" +" 't' -- allow the called user transfer the calling user by hitting the\n" +" key defined in features.conf, or #, if not defined.\n" +" 'T' -- allow the calling user to transfer the call by hitting the\n" +" key defined in features.conf, or #, if not defined.\n" " 'f' -- Forces callerid to be set as the extension of the line \n" " making/redirecting the outgoing call. For example, some PSTNs\n" " don't allow callerids from other extensions then the ones\n" " that are assigned to you.\n" " 'r' -- indicate ringing to the calling party, pass no audio until answered.\n" " 'm' -- provide hold music to the calling party until answered.\n" -" 'h' -- allow callee to hang up by hitting *.\n" -" 'H' -- allow caller to hang up by hitting *.\n" +" 'h' -- allow callee to hang up by hitting the key defined in features.conf.\n" +" 'H' -- allow caller to hang up by hitting the key defined in features.conf\n" " 'C' -- reset call detail record for this call.\n" " 'P[(x)]' -- privacy mode, using 'x' as database if provided.\n" " 'g' -- goes on in context if the destination channel hangs up\n" Index: configs/features.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/features.conf.sample,v retrieving revision 1.2 diff -u -r1.2 features.conf.sample --- configs/features.conf.sample 1 Aug 2004 01:38:15 -0000 1.2 +++ configs/features.conf.sample 18 Aug 2004 00:02:55 -0000 @@ -1,10 +1,29 @@ +[general] + + +[parking] ; ; Sample Parking configuration ; - -[general] parkext => 700 ; What ext. to dial to park parkpos => 701-720 ; What extensions to park calls on context => parkedcalls ; Which context parked calls are in ;parkingtime => 45 ; Number of seconds a call can be parked for (default is 45 seconds) + +[transfer] +; +; This allows you to change the keys you use to disconnect and transfer calls +; inline. (otherwise known as # transfer). The values are set at the default +; of # for transfer and * for hangup (show application dial for usage). +; +; Please note that this affects BOTH 't' and 'T' dial options. +; + +;hangup=* ; Sets hangup key sequence to '*' + ; +;transfer=## ; Sets transfer key sequence to '##' + ; +;digittimeout=3000 ; ONLY useful when we use more than one transfer digit + ; sets the timeout between keypresses - defaults to + ; 500ms (minimum is 500ms.) ;transferdigittimeout => 3 ; Number of seconds to wait between digits when transfering a call Index: res_features.c =================================================================== RCS file: /usr/cvsroot/asterisk/res/res_features.c,v retrieving revision 1.9 diff -u -r1.9 res_features.c --- res_features.c 8 Aug 2004 17:15:02 -0000 1.9 +++ res_features.c 22 Aug 2004 09:07:45 -0000 @@ -1,7 +1,8 @@ /* * Asterisk -- A telephony toolkit for Linux. * - * Routines implementing call parking + * Routines implementing call features such as parking, + * bridging, and transferring. * * Copyright (C) 1999, Mark Spencer * @@ -27,10 +28,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -39,6 +42,9 @@ #define DEFAULT_PARK_TIME 45000 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 +#define DEFAULT_TRANSFER_KEYPRESS_TIMEOUT 500 + +AST_MUTEX_DEFINE_STATIC(xfrlock); static char *parkedcall = "ParkedCall"; @@ -61,6 +67,9 @@ static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; +/* By Default, we are not allowing double digits for xfer and thus don't need timeout */ +static int astdigittimeout = DEFAULT_TRANSFER_KEYPRESS_TIMEOUT; + /* Registrar for operations */ static char *registrar = "res_features"; @@ -84,6 +93,10 @@ "into the dialplan, although you should include the 'parkedcalls'\n" "context.\n"; +/* handling our configurable hangup/transfer */ +static char asthangupkey = '*'; +static char astxferkey[3]; + struct parkeduser { struct ast_channel *chan; struct timeval start; @@ -140,7 +153,7 @@ } if (x <= parking_stop) { chan->appl = "Parked Call"; - chan->data = NULL; + chan->data = NULL; pu->chan = chan; /* Start music on hold */ @@ -180,15 +193,15 @@ ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d\n", pu->chan->name, pu->parkingnum); manager_event(EVENT_FLAG_CALL, "ParkedCall", - "Exten: %d\r\n" - "Channel: %s\r\n" - "From: %s\r\n" - "Timeout: %ld\r\n" - "CallerID: %s\r\n" - ,pu->parkingnum, pu->chan->name, peer->name - ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL) - ,(pu->chan->callerid ? pu->chan->callerid : "") - ); + "Exten: %d\r\n" + "Channel: %s\r\n" + "From: %s\r\n" + "Timeout: %ld\r\n" + "CallerID: %s\r\n" + ,pu->parkingnum, pu->chan->name, peer->name + ,(long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL) + ,(pu->chan->callerid ? pu->chan->callerid : "") + ); if (peer) { ast_say_digits(peer, pu->parkingnum, "", peer->language); @@ -259,10 +272,14 @@ the ability to transfer calls with '#frametype == AST_FRAME_CONTROL) && ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || - (f->subclass == AST_CONTROL_CONGESTION)))) { - res = -1; - break; + if (!f || ((f->frametype == AST_FRAME_CONTROL) && + ((f->subclass == AST_CONTROL_HANGUP) || (f->subclass == AST_CONTROL_BUSY) || + (f->subclass == AST_CONTROL_CONGESTION)))) { + res = -1; + break; } if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_RINGING)) { if (who == chan) @@ -346,7 +364,7 @@ /* check for '*', if we find it it's time to disconnect */ if (f && (f->frametype == AST_FRAME_DTMF) && (((who == chan) && allowdisconnect_out) || ((who == peer) && allowdisconnect_in)) && - (f->subclass == '*')) { + ((char)(f->subclass & 0xff) == asthangupkey)) { if (option_verbose > 3) ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); @@ -356,15 +374,55 @@ if ((f->frametype == AST_FRAME_DTMF) && ((allowredirect_in && who == peer) || (allowredirect_out && who == chan)) && - (f->subclass == '#')) { - if(allowredirect_in && who == peer) { - transferer = peer; - transferee = chan; - } - else { - transferer = chan; - transferee = peer; + ((char)(f->subclass & 0xff) == astxferkey[0])) { + if(allowredirect_in && who == peer) { + transferer = peer; + transferee = chan; + } else { + transferer = chan; + transferee = peer; + } + ast_log(LOG_DEBUG, "Keys are %c, %c.\n", astxferkey[0], astxferkey[1]); + if (strlen(astxferkey) > 1) { + int f_subclass_tmp = f->subclass; + dotransfer=0; + ms = astdigittimeout; + while(ms) { + ast_autoservice_start(transferee); + ms = ast_waitfor(transferer, ms); + if (ms > 0) { + ft = ast_read(transferer); + if (ft && (ft->frametype == AST_FRAME_DTMF)) { + if ((char)(ft->subclass & 0xff) == astxferkey[1]) { + dotransfer=1; + autoserv=1; + ast_frfree(ft); + break; + } else { + int ft_subclass_tmp = ft->subclass; + f->subclass = f_subclass_tmp; + ast_autoservice_stop(transferee); + ast_log(LOG_DEBUG, "Writing Frame 'f' which contains DTMF %c\n", f->subclass); + ft->subclass = ft_subclass_tmp; + ast_log(LOG_DEBUG, "Writing Frame 'ft' which contains DTMF %c\n", ft->subclass); + ast_write(transferee, ft); + dotransfer=0; + + break; + } + ast_frfree(ft); + } + + } else { + /* redo the frame, since it got lost in the move */ + ast_autoservice_stop(transferee); + f->subclass = f_subclass_tmp; + f->frametype = AST_FRAME_DTMF; + } } + } + + if (dotransfer) { if(!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) && !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) { /* Use the non-macro context to transfer the call */ @@ -375,7 +433,8 @@ } /* Start autoservice on chan while we talk to the originator */ - ast_autoservice_start(transferee); + if (!(autoserv == 1)) + ast_autoservice_start(transferee); ast_moh_start(transferee, NULL); memset(newext, 0, sizeof(newext)); @@ -408,7 +467,7 @@ break; *(ptr++) = res; if (!ast_matchmore_extension(transferer, transferer_real_context - , newext, 1, transferer->callerid)) { + , newext, 1, transferer->callerid)) { break; } } @@ -444,7 +503,7 @@ /* Doh! Use our handy async_goto funcitons */ if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n" - ,transferee->name, newext, transferer_real_context); + ,transferee->name, newext, transferer_real_context); if (ast_async_goto(transferee, transferer_real_context, newext, 1)) ast_log(LOG_WARNING, "Async goto fialed :(\n"); res = -1; @@ -474,21 +533,103 @@ if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name); } - } else { - if (f && (f->frametype == AST_FRAME_DTMF)) { - if (who == peer) - ast_write(chan, f); - else - ast_write(peer, f); - } + #if 1 ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass); #endif + if (f) + ast_frfree(f); + } + } + if (f && (f->frametype == AST_FRAME_DTMF)) { + + if (who == peer) + ast_write(chan, f); + else + ast_write(peer, f); + + } + }return res; +} + +static int parse_parking_opts(struct ast_variable *var, int complain) +{ + int start, end; + + while(var) { + if (!strcasecmp(var->name, "parkext")) { + strncpy(parking_ext, var->value, sizeof(parking_ext) - 1); + if (complain) + ast_log(LOG_NOTICE, "Parking configuration should be in [parking], not [general]. Please correct this.\n"); + } else if (!strcasecmp(var->name, "context")) { + strncpy(parking_con, var->value, sizeof(parking_con) - 1); + } else if (!strcasecmp(var->name, "parkingtime")) { + if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) { + ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value); + parkingtime = DEFAULT_PARK_TIME; + } else + parkingtime = parkingtime * 1000; + } else if (!strcasecmp(var->name, "parkpos")) { + if (sscanf(var->value, "%i-%i", &start, &end) != 2) { + ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno); + } else { + parking_start = start; + parking_stop = end; } - if (f) - ast_frfree(f); - } - return res; + } + var = var->next; + } return RESULT_SUCCESS; +} + +static int parse_transfer_opts(struct ast_variable *var) +{ + int timeout; + char hangupkey = '*'; + + while (var) { + if (!strcasecmp(var->name, "hangup")) { + strncpy(&hangupkey, var->value, 1); + if (strlen(var->value) > 0) { + if (isalpha(hangupkey)) { + ast_log(LOG_WARNING, "hangup=%c on line %i of features.conf is not valid.\n", hangupkey, var->lineno); + ast_log(LOG_NOTICE, "Setting default hangup sequence to '*'\n"); + asthangupkey = '*'; + } else + ast_log(LOG_DEBUG, "Found alternate hangup key: %c\n", hangupkey); + asthangupkey = hangupkey; + } else { + ast_log(LOG_DEBUG, "NO HANGUP KEY DEFINED - using '*'\n"); + } + } else if (!strcasecmp(var->name, "transfer")) { + if (strlen(var->value) > 0) { + strncpy(astxferkey, var->value, 2); + if (isalpha(astxferkey[0]) || isalpha(astxferkey[1])) { + ast_log(LOG_WARNING, "transfer=%c%c on line %i of features.conf is not valid.\n", astxferkey[0], astxferkey[1], var->lineno); + ast_log(LOG_NOTICE, "Setting default transfer sequence to '#'\n"); + astxferkey[0] = '#'; + astxferkey[1] = '\0'; + } else + ast_log(LOG_DEBUG, "Found alternate transfer key(s): %c%c\n", astxferkey[0], astxferkey[1]); + } else { + ast_log(LOG_DEBUG, "NO TRANSFER KEY DEFINED - using '#'\n"); + astxferkey[0] = '#'; + astxferkey[1] = '\0'; + } + } else if (!strcasecmp(var->name, "digittimeout")) { + if((sscanf(var->value, "%d", &timeout) != 1) || (timeout < 500)) { + ast_log(LOG_WARNING, "Transfer keypress timeout too short: %d. Setting to half a second.\n", timeout); + astdigittimeout = DEFAULT_TRANSFER_KEYPRESS_TIMEOUT; + } else + astdigittimeout = timeout; + } else if (!strcasecmp(var->name, "transferdigittimeout")) { + if((sscanf(var->value, "%d", &transferdigittimeout) != 1 || (transferdigittimeout < 1))) { + ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value); + transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; + } else + transferdigittimeout = transferdigittimeout * 1000; + } + var = var->next; + } return RESULT_SUCCESS; } static void *do_parking_thread(void *ignore) @@ -811,10 +952,8 @@ int load_module(void) { int res; - int start, end; struct ast_context *con; struct ast_config *cfg; - struct ast_variable *var; ast_cli_register(&showparked); @@ -825,34 +964,11 @@ ast_log(LOG_NOTICE, "parking.conf is deprecated in favor of 'features.conf'. Please rename it.\n"); } if (cfg) { - var = ast_variable_browse(cfg, "general"); - while(var) { - if (!strcasecmp(var->name, "parkext")) { - strncpy(parking_ext, var->value, sizeof(parking_ext) - 1); - } else if (!strcasecmp(var->name, "context")) { - strncpy(parking_con, var->value, sizeof(parking_con) - 1); - } else if (!strcasecmp(var->name, "parkingtime")) { - if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) { - ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value); - parkingtime = DEFAULT_PARK_TIME; - } else - parkingtime = parkingtime * 1000; - } else if (!strcasecmp(var->name, "parkpos")) { - if (sscanf(var->value, "%i-%i", &start, &end) != 2) { - 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); - } else { - parking_start = start; - parking_stop = end; - } - } else if(!strcasecmp(var->name, "transferdigittimeout")) { - if ((sscanf(var->value, "%d", &transferdigittimeout) != 1) || (transferdigittimeout < 1)) { - ast_log(LOG_WARNING, "%s is not a valid transferdigittimeout\n", var->value); - transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; - } else - transferdigittimeout = transferdigittimeout * 1000; - } - var = var->next; - } + ast_mutex_lock(&xfrlock); + parse_parking_opts(ast_variable_browse(cfg, "general"), 1); /* Parse parking options and complain */ + parse_parking_opts(ast_variable_browse(cfg, "parking"), 0); /* Everything is in place, just parse */ + parse_transfer_opts(ast_variable_browse(cfg, "transfer")); /* Parse transfer options */ + ast_mutex_unlock(&xfrlock); ast_destroy(cfg); } con = ast_context_find(parking_con); @@ -860,7 +976,7 @@ con = ast_context_create(NULL,parking_con, registrar); if (!con) { ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); - return -1; + return -1; } } ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, parkcall, strdup(""),free, registrar);