Index: apps/app_dial.c =================================================================== --- apps/app_dial.c (revision 172062) +++ apps/app_dial.c (working copy) @@ -806,35 +806,6 @@ return 0; } -static void set_dial_features(struct ast_flags *opts, struct ast_dial_features *features) -{ - struct ast_flags perm_opts = {.flags = 0}; - - ast_copy_flags(&perm_opts, opts, - OPT_CALLER_TRANSFER | OPT_CALLER_PARK | OPT_CALLER_MONITOR | OPT_CALLER_HANGUP | - OPT_CALLEE_TRANSFER | OPT_CALLEE_PARK | OPT_CALLEE_MONITOR | OPT_CALLEE_HANGUP); - - memset(features->options, 0, sizeof(features->options)); - - ast_app_options2str(dial_exec_options, &perm_opts, features->options, sizeof(features->options)); - if (ast_test_flag(&perm_opts, OPT_CALLEE_TRANSFER)) - ast_set_flag(&(features->features_callee), AST_FEATURE_REDIRECT); - if (ast_test_flag(&perm_opts, OPT_CALLER_TRANSFER)) - ast_set_flag(&(features->features_caller), AST_FEATURE_REDIRECT); - if (ast_test_flag(&perm_opts, OPT_CALLEE_HANGUP)) - ast_set_flag(&(features->features_callee), AST_FEATURE_DISCONNECT); - if (ast_test_flag(&perm_opts, OPT_CALLER_HANGUP)) - ast_set_flag(&(features->features_caller), AST_FEATURE_DISCONNECT); - if (ast_test_flag(&perm_opts, OPT_CALLEE_MONITOR)) - ast_set_flag(&(features->features_callee), AST_FEATURE_AUTOMON); - if (ast_test_flag(&perm_opts, OPT_CALLER_MONITOR)) - ast_set_flag(&(features->features_caller), AST_FEATURE_AUTOMON); - if (ast_test_flag(&perm_opts, OPT_CALLEE_PARK)) - ast_set_flag(&(features->features_callee), AST_FEATURE_PARKCALL); - if (ast_test_flag(&perm_opts, OPT_CALLER_PARK)) - ast_set_flag(&(features->features_caller), AST_FEATURE_PARKCALL); -} - static void end_bridge_callback (void *data) { char buf[80]; @@ -906,9 +877,6 @@ struct ast_flags opts = { 0, }; char *opt_args[OPT_ARG_ARRAY_SIZE]; struct ast_datastore *datastore = NULL; - struct ast_datastore *ds_caller_features = NULL; - struct ast_datastore *ds_callee_features = NULL; - struct ast_dial_features *caller_features; int fulldial = 0, num_dialed = 0; if (ast_strlen_zero(data)) { @@ -1172,25 +1140,6 @@ ast_copy_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING); - /* Create datastore for channel dial features for caller */ - if (!(ds_caller_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for dial features. Aborting!\n"); - goto out; - } - - if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) { - ast_log(LOG_WARNING, "Unable to allocate memory for feature flags. Aborting!\n"); - goto out; - } - - ast_channel_lock(chan); - caller_features->is_caller = 1; - set_dial_features(&opts, caller_features); - ds_caller_features->inheritance = -1; - ds_caller_features->data = caller_features; - ast_channel_datastore_add(chan, ds_caller_features); - ast_channel_unlock(chan); - /* loop through the list of dial destinations */ rest = args.peers; while ((cur = strsep(&rest, "&")) ) { @@ -1201,7 +1150,6 @@ char *tech = strsep(&number, "/"); /* find if we already dialed this interface */ struct ast_dialed_interface *di; - struct ast_dial_features *callee_features; AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; num_dialed++; if (!number) { @@ -1351,27 +1299,6 @@ else ast_copy_string(tmp->chan->exten, chan->exten, sizeof(tmp->chan->exten)); - /* Save callee features */ - if (!(ds_callee_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for dial features. Aborting!\n"); - ast_free(tmp); - goto out; - } - - if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) { - ast_log(LOG_WARNING, "Unable to allocate memory for feature flags. Aborting!\n"); - ast_free(tmp); - goto out; - } - - ast_channel_lock(tmp->chan); - callee_features->is_caller = 0; - set_dial_features(&opts, callee_features); - ds_callee_features->inheritance = -1; - ds_callee_features->data = callee_features; - ast_channel_datastore_add(tmp->chan, ds_callee_features); - ast_channel_unlock(tmp->chan); - /* Place the call, but don't wait on the answer */ res = ast_call(tmp->chan, numsubst, 0); Index: include/asterisk/global_datastores.h =================================================================== --- include/asterisk/global_datastores.h (revision 172062) +++ include/asterisk/global_datastores.h (working copy) @@ -26,8 +26,6 @@ #include "asterisk/channel.h" -#define MAX_DIAL_FEATURE_OPTIONS 30 - extern const struct ast_datastore_info dialed_interface_info; extern const struct ast_datastore_info dial_features_info; @@ -40,7 +38,6 @@ struct ast_dial_features { struct ast_flags features_caller; struct ast_flags features_callee; - char options[MAX_DIAL_FEATURE_OPTIONS]; int is_caller; }; Index: configs/features.conf.sample =================================================================== --- configs/features.conf.sample (revision 172062) +++ configs/features.conf.sample (working copy) @@ -17,6 +17,12 @@ ; one of: parked, caller, both (default is caller) ;parkedcalltransfers = caller ; Enables or disables DTMF based transfers when picking up a parked call. ; one of: callee, caller, both, no (default is both) +;parkedcallparking = caller ; Enables or disables DTMF based one-touch parking when picking up a parked call. + ; one of: callee, caller, both, no (default is no) +;parkedcallhangup = caller ; Enables or disables DTMF based hangups when picking up a parked call. + ; one of: callee, caller, both, no (default is no) +;parkedcallrecording = caller ; Enables or disables DTMF based one-touch recording when picking up a parked call. + ; one of: callee, caller, both, no (default is no) ;adsipark = yes ; if you want ADSI parking announcements ;findslot => next ; Continue to the 'next' free parking space. ; Defaults to 'first' available Index: res/res_features.c =================================================================== --- res/res_features.c (revision 172062) +++ res/res_features.c (working copy) @@ -70,6 +70,7 @@ #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 #define AST_MAX_WATCHERS 256 +#define MAX_DIAL_FEATURE_OPTIONS 30 enum { AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), @@ -93,6 +94,9 @@ static int parking_stop; /*!< Last available extension for parking */ static int parkedcalltransfers; /*!< Who can REDIRECT after picking up a parked a call */ +static int parkedcallparking; /*!< Who can PARKCALL after picking up a parked call */ +static int parkedcallhangup; /*!< Who can DISCONNECT after picking up a parked call */ +static int parkedcallrecording; /*!< Who can AUTOMON after picking up a parked call */ static char courtesytone[256]; /*!< Courtesy tone */ static int parkedplay = 0; /*!< Who to play the courtesy tone to */ @@ -222,8 +226,7 @@ static struct ast_channel *ast_feature_request_and_dial(struct ast_channel *caller, const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, const char *language); - -static void *ast_bridge_call_thread(void *data) +static void *ast_bridge_call_thread(void *data) { struct ast_bridge_thread_obj *tobj = data; @@ -594,7 +597,6 @@ ast_module_user_remove(u); return res; - } static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) @@ -725,7 +727,7 @@ ast_indicate(transferee, AST_CONTROL_HOLD); memset(xferto, 0, sizeof(xferto)); - + /* Transfer */ res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY); if (res < 0) { @@ -835,11 +837,11 @@ if (option_debug) ast_log(LOG_DEBUG, "Executing Attended Transfer %s, %s (sense=%d) \n", chan->name, peer->name, sense); set_peers(&transferer, &transferee, peer, chan, sense); - transferer_real_context = real_ctx(transferer, transferee); + transferer_real_context = real_ctx(transferer, transferee); /* Start autoservice on chan while we talk to the originator */ ast_autoservice_start(transferee); ast_indicate(transferee, AST_CONTROL_HOLD); - + /* Transfer */ res = ast_stream_and_wait(transferer, "pbx-transfer", transferer->language, AST_DIGIT_ANY); if (res < 0) { @@ -872,23 +874,15 @@ return FEATURE_RETURN_SUCCESS; } + if (!strcmp(xferto, ast_parking_ext())) { + finishup(transferee); + return builtin_parkcall(chan, peer, config, code, sense, data); + } + l = strlen(xferto); snprintf(xferto + l, sizeof(xferto) - l, "@%s", transferer_real_context); /* append context */ newchan = ast_feature_request_and_dial(transferer, "Local", ast_best_codec(transferer->nativeformats), xferto, atxfernoanswertimeout, &outstate, transferer->cid.cid_num, transferer->cid.cid_name, transferer->language); - - /* If we are the callee and we are being transferred, after the masquerade - * caller features will really be the original callee features */ - ast_channel_lock(transferee); - if ((features_datastore = ast_channel_datastore_find(transferee, &dial_features_info, NULL))) { - dialfeatures = features_datastore->data; - } - ast_channel_unlock(transferee); - - if (dialfeatures && !dialfeatures->is_caller) { - ast_copy_flags(&(config->features_caller), &(dialfeatures->features_callee), AST_FLAGS_ALL); - } - ast_indicate(transferer, -1); if (!newchan) { finishup(transferee); @@ -916,14 +910,14 @@ transferer->_softhangup = 0; return FEATURE_RETURN_SUCCESS; } - + if (check_compat(transferee, newchan)) { finishup(transferee); return -1; } ast_indicate(transferee, AST_CONTROL_UNHOLD); - + if ((ast_autoservice_stop(transferee) < 0) || (ast_waitfordigit(transferee, 100) < 0) || (ast_waitfordigit(newchan, 100) < 0) @@ -961,9 +955,7 @@ ast_hangup(newchan); return -1; } - - /* For the case where the transfer target is being connected with the original - caller store the target's original features, and apply to the bridge */ + ast_channel_lock(newchan); if ((features_datastore = ast_channel_datastore_find(newchan, &dial_features_info, NULL))) { dialfeatures = features_datastore->data; @@ -971,9 +963,21 @@ ast_channel_unlock(newchan); if (dialfeatures) { - ast_copy_flags(&(config->features_callee), &(dialfeatures->features_callee), AST_FLAGS_ALL); + /* newchan should always be the callee and shows up as callee in dialfeatures, but for some reason + I don't currently understand, the abilities of newchan seem to be stored on the caller side */ + ast_copy_flags(&(config->features_callee), &(dialfeatures->features_caller), AST_FLAGS_ALL); } + ast_channel_lock(xferchan); + if ((features_datastore = ast_channel_datastore_find(xferchan, &dial_features_info, NULL))) { + dialfeatures = features_datastore->data; + } + ast_channel_unlock(xferchan); + + if (dialfeatures) { + ast_copy_flags(&(config->features_caller), &(dialfeatures->features_caller), AST_FLAGS_ALL); + } + tobj->chan = newchan; tobj->peer = xferchan; tobj->bconfig = *config; @@ -1427,7 +1431,95 @@ return cdr_orig; /* everybody LOCKED or some other weirdness, like a NULL */ } +static void set_bridge_features_on_config(struct ast_bridge_config *config, const char *features) +{ + const char *feature; + if (ast_strlen_zero(features)) { + return; + } + + for (feature = features; *feature; feature++) { + switch (tolower(*feature)) { + case 't' : + ast_set_flag(&(config->features_caller), AST_FEATURE_REDIRECT); + break; + case 'k' : + ast_set_flag(&(config->features_caller), AST_FEATURE_PARKCALL); + break; + case 'h' : + ast_set_flag(&(config->features_caller), AST_FEATURE_DISCONNECT); + break; + case 'w' : + ast_set_flag(&(config->features_caller), AST_FEATURE_AUTOMON); + break; + default : + ast_log(LOG_WARNING, "Skipping unknown feature code '%c'\n", *feature); + } + } +} + +static void add_features_datastores(struct ast_channel *caller, struct ast_channel *callee, struct ast_bridge_config *config) +{ + struct ast_datastore *ds_callee_features = NULL, *ds_caller_features = NULL; + struct ast_dial_features *callee_features = NULL, *caller_features = NULL; + + if (!ast_channel_datastore_find(caller, &dial_features_info, NULL)) { + if (!(ds_caller_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) { + ast_log(LOG_WARNING, "Unable to create channel datastore for caller features. Aborting!\n"); + ast_channel_datastore_free(ds_callee_features); + goto ds_error; + } + if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) { + ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n"); + goto ds_error; + } + ds_caller_features->inheritance = DATASTORE_INHERIT_FOREVER; + caller_features->is_caller = 1; + ast_copy_flags(&(caller_features->features_callee), &(config->features_callee), AST_FLAGS_ALL); + ast_copy_flags(&(caller_features->features_caller), &(config->features_caller), AST_FLAGS_ALL); + ds_caller_features->data = caller_features; + ast_channel_lock(caller); + ast_channel_datastore_add(caller, ds_caller_features); + ast_channel_unlock(caller); + } + + if (!ast_channel_datastore_find(callee, &dial_features_info, NULL)) { + if (!(ds_callee_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) { + ast_log(LOG_WARNING, "Unable to create channel datastore for callee features. Aborting!\n"); + goto ds_error; + } + if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) { + ast_log(LOG_WARNING, "Unable to allocate memory for callee feature flags. Aborting!\n"); + goto ds_error; + } + ds_callee_features->inheritance = DATASTORE_INHERIT_FOREVER; + callee_features->is_caller = 0; + ast_copy_flags(&(callee_features->features_callee), &(config->features_caller), AST_FLAGS_ALL); + ast_copy_flags(&(callee_features->features_caller), &(config->features_callee), AST_FLAGS_ALL); + ds_callee_features->data = callee_features; + ast_channel_lock(callee); + ast_channel_datastore_add(callee, ds_callee_features); + ast_channel_unlock(callee); + } + + return; + +ds_error: + if (ds_callee_features) { + ast_channel_datastore_free(ds_callee_features); + } + if (ds_caller_features) { + ast_channel_datastore_free(ds_caller_features); + } + if (callee_features) { + ast_free(callee_features); + } + if (caller_features) { + ast_free(caller_features); + } +} + int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config) { /* Copy voice back and forth between the two channels. Give the peer @@ -1464,6 +1556,9 @@ pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL); } + set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES")); + add_features_datastores(chan, peer, config); + /* This is an interesting case. One example is if a ringing channel gets redirected to * an extension that picks up a parked call. This will make sure that the call taken * out of parking gets told that the channel it just got bridged to is still ringing. */ @@ -1487,7 +1582,7 @@ pbx_exec(src, monitor_app, tmp); } } - + set_config_flags(chan, peer, config); config->firstpass = 1; @@ -1551,12 +1646,12 @@ } } } - + for (;;) { struct ast_channel *other; /* used later */ - + res = ast_channel_bridge(chan, peer, config, &f, &who); - + if (config->feature_timer) { /* Update time limit for next pass */ diff = ast_tvdiff_ms(ast_tvnow(), config->start_time); @@ -1877,7 +1972,7 @@ return res; } - + static void post_manager_event(const char *s, char *parkingexten, struct ast_channel *chan) { manager_event(EVENT_FLAG_CALL, s, @@ -1892,6 +1987,50 @@ ); } +static char *callback_dialoptions(struct ast_flags *features_callee, struct ast_flags *features_caller, char *options, size_t len) +{ + int i = 0; + enum { + OPT_CALLEE_REDIRECT = 't', + OPT_CALLER_REDIRECT = 'T', + OPT_CALLEE_AUTOMON = 'w', + OPT_CALLER_AUTOMON = 'W', + OPT_CALLEE_DISCONNECT = 'h', + OPT_CALLER_DISCONNECT = 'H', + OPT_CALLEE_PARKCALL = 'k', + OPT_CALLER_PARKCALL = 'K', + }; + + memset(options, 0, len); + if (ast_test_flag(features_caller, AST_FEATURE_REDIRECT) && i < len) { + options[i++] = OPT_CALLER_REDIRECT; + } + if (ast_test_flag(features_caller, AST_FEATURE_AUTOMON) && i < len) { + options[i++] = OPT_CALLER_AUTOMON; + } + if (ast_test_flag(features_caller, AST_FEATURE_DISCONNECT) && i < len) { + options[i++] = OPT_CALLER_DISCONNECT; + } + if (ast_test_flag(features_caller, AST_FEATURE_PARKCALL) && i < len) { + options[i++] = OPT_CALLER_PARKCALL; + } + + if (ast_test_flag(features_callee, AST_FEATURE_REDIRECT) && i < len) { + options[i++] = OPT_CALLEE_REDIRECT; + } + if (ast_test_flag(features_callee, AST_FEATURE_AUTOMON) && i < len) { + options[i++] = OPT_CALLEE_AUTOMON; + } + if (ast_test_flag(features_callee, AST_FEATURE_DISCONNECT) && i < len) { + options[i++] = OPT_CALLEE_DISCONNECT; + } + if (ast_test_flag(features_callee, AST_FEATURE_PARKCALL) && i < len) { + options[i++] = OPT_CALLEE_PARKCALL; + } + + return options; +} + /*! \brief Take care of parked calls and unpark them if needed */ static void *do_parking_thread(void *ignore) { @@ -1953,10 +2092,13 @@ peername += 7; } - if (dialfeatures) - snprintf(returnexten, sizeof(returnexten), "%s|30|%s", peername, dialfeatures->options); - else /* Existing default */ + if (dialfeatures) { + char buf[MAX_DIAL_FEATURE_OPTIONS] = {0,}; + snprintf(returnexten, sizeof(returnexten), "%s|30|%s", peername, callback_dialoptions(&(dialfeatures->features_callee), &(dialfeatures->features_caller), buf, sizeof(buf))); + } else { /* Existing default */ + ast_log(LOG_WARNING, "Dialfeatures not found on %s, using default!\n", chan->name); snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername); + } ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar); } @@ -2142,7 +2284,7 @@ ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n"); return -1; } - + u = ast_module_user_add(chan); park = atoi((char *)data); @@ -2194,8 +2336,11 @@ ast_answer(chan); if (peer) { + struct ast_datastore *features_datastore; + struct ast_dial_features *dialfeatures = NULL; + /* Play a courtesy to the source(s) configured to prefix the bridge connecting */ - + if (!ast_strlen_zero(courtesytone)) { int error = 0; ast_indicate(peer, AST_CONTROL_UNHOLD); @@ -2221,7 +2366,7 @@ return -1; } } else - ast_indicate(peer, AST_CONTROL_UNHOLD); + ast_indicate(peer, AST_CONTROL_UNHOLD); res = ast_channel_make_compatible(chan, peer); if (res < 0) { @@ -2232,19 +2377,48 @@ } /* This runs sorta backwards, since we give the incoming channel control, as if it were the person called. */ - if (option_verbose > 2) + if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park); pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); ast_cdr_setdestchan(chan->cdr, peer->name); memset(&config, 0, sizeof(struct ast_bridge_config)); + /* Get datastore for peer and apply it's features to the callee side of the bridge config */ + ast_channel_lock(peer); + if ((features_datastore = ast_channel_datastore_find(peer, &dial_features_info, NULL))) { + dialfeatures = features_datastore->data; + } + ast_channel_unlock(peer); + + if (dialfeatures) { + ast_copy_flags(&(config.features_callee), dialfeatures->is_caller ? &(dialfeatures->features_caller) : &(dialfeatures->features_callee), AST_FLAGS_ALL); + } + if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) { ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); } if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH)) { ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); } + if ((parkedcallparking == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallparking == AST_FEATURE_FLAG_BYBOTH)) { + ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL); + } + if ((parkedcallparking == AST_FEATURE_FLAG_BYCALLER) || (parkedcallparking == AST_FEATURE_FLAG_BYBOTH)) { + ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL); + } + if ((parkedcallhangup == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) { + ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT); + } + if ((parkedcallhangup == AST_FEATURE_FLAG_BYCALLER) || (parkedcallhangup == AST_FEATURE_FLAG_BYBOTH)) { + ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT); + } + if ((parkedcallrecording == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) { + ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); + } + if ((parkedcallrecording == AST_FEATURE_FLAG_BYCALLER) || (parkedcallrecording == AST_FEATURE_FLAG_BYBOTH)) { + ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); + } res = ast_bridge_call(chan, peer, &config); pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); @@ -2536,6 +2710,9 @@ adsipark = 0; parkaddhints = 0; parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH; + parkedcallparking = 0; + parkedcallhangup = 0; + parkedcallrecording = 0; transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; @@ -2577,6 +2754,33 @@ parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE; else if (!strcasecmp(var->value, "both")) parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH; + } else if (!strcasecmp(var->name, "parkedcallparking")) { + if (!strcasecmp(var->value, "no")) + parkedcallparking = 0; + else if (!strcasecmp(var->value, "caller")) + parkedcallparking = AST_FEATURE_FLAG_BYCALLER; + else if (!strcasecmp(var->value, "callee")) + parkedcallparking = AST_FEATURE_FLAG_BYCALLEE; + else if (!strcasecmp(var->value, "both")) + parkedcallparking = AST_FEATURE_FLAG_BYBOTH; + } else if (!strcasecmp(var->name, "parkedcallhangup")) { + if (!strcasecmp(var->value, "no")) + parkedcallhangup = 0; + else if (!strcasecmp(var->value, "caller")) + parkedcallhangup = AST_FEATURE_FLAG_BYCALLER; + else if (!strcasecmp(var->value, "callee")) + parkedcallhangup = AST_FEATURE_FLAG_BYCALLEE; + else if (!strcasecmp(var->value, "both")) + parkedcallhangup = AST_FEATURE_FLAG_BYBOTH; + } else if (!strcasecmp(var->name, "parkedcallrecording")) { + if (!strcasecmp(var->value, "no")) + parkedcallrecording = 0; + else if (!strcasecmp(var->value, "caller")) + parkedcallrecording = AST_FEATURE_FLAG_BYCALLER; + else if (!strcasecmp(var->value, "callee")) + parkedcallrecording = AST_FEATURE_FLAG_BYCALLEE; + else if (!strcasecmp(var->value, "both")) + parkedcallrecording = AST_FEATURE_FLAG_BYBOTH; } else if (!strcasecmp(var->name, "adsipark")) { adsipark = ast_true(var->value); } else if (!strcasecmp(var->name, "transferdigittimeout")) {