diff --git a/main/features.c b/main/features.c index 9e208e55ab..94161626e0 100644 --- a/main/features.c +++ b/main/features.c @@ -66,6 +66,14 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #define AST_MAX_WATCHERS 256 #define MAX_DIAL_FEATURE_OPTIONS 30 +#define FEATURE_RETURN_HANGUP -1 +#define FEATURE_RETURN_SUCCESSBREAK 0 +#define FEATURE_RETURN_PASSDIGITS 21 +#define FEATURE_RETURN_STOREDIGITS 22 +#define FEATURE_RETURN_SUCCESS 23 +#define FEATURE_RETURN_KEEPTRYING 24 +#define FEATURE_RETURN_PARKFAILED 25 + enum { AST_FEATURE_FLAG_NEEDSDTMF = (1 << 0), AST_FEATURE_FLAG_ONPEER = (1 << 1), @@ -409,17 +417,15 @@ static enum ast_device_state metermaidstate(const char *data) return AST_DEVICE_INUSE; } -/* Park a call */ -static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, const char *orig_chan_name) +static struct parkeduser *park_space_reserve(struct ast_channel *chan) { struct parkeduser *pu, *cur; - int i, x = -1, parking_range; - struct ast_context *con; + int i, parking_space = -1, parking_range; const char *parkingexten; /* Allocate memory for parking data */ if (!(pu = ast_calloc(1, sizeof(*pu)))) - return -1; + return NULL; /* Lock parking lot */ AST_LIST_LOCK(&parkinglot); @@ -432,27 +438,27 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in * limitation here. If extout was not numeric, we could permit * arbitrary non-numeric extensions. */ - if (sscanf(parkingexten, "%d", &x) != 1 || x < 0) { + if (sscanf(parkingexten, "%d", &parking_space) != 1 || parking_space < 0) { AST_LIST_UNLOCK(&parkinglot); ast_free(pu); ast_log(LOG_WARNING, "PARKINGEXTEN does not indicate a valid parking slot: '%s'.\n", parkingexten); - return 1; /* Continue execution if possible */ + return NULL; } - snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); + snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space); if (ast_exists_extension(NULL, parking_con, pu->parkingexten, 1, NULL)) { AST_LIST_UNLOCK(&parkinglot); ast_free(pu); ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con); - return 1; /* Continue execution if possible */ + return NULL; } } else { /* Select parking space within range */ parking_range = parking_stop - parking_start+1; for (i = 0; i < parking_range; i++) { - x = (i + parking_offset) % parking_range + parking_start; + parking_space = (i + parking_offset) % parking_range + parking_start; AST_LIST_TRAVERSE(&parkinglot, cur, list) { - if (cur->parkingnum == x) + if (cur->parkingnum == parking_space) break; } if (!cur) @@ -463,14 +469,35 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in ast_log(LOG_WARNING, "No more parking spaces\n"); ast_free(pu); AST_LIST_UNLOCK(&parkinglot); - return -1; + return NULL; } /* Set pointer for next parking */ if (parkfindnext) - parking_offset = x - parking_start + 1; - snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); + parking_offset = parking_space - parking_start + 1; + snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space); } - + + pu->notquiteyet = 1; + pu->parkingnum = parking_space; + AST_LIST_INSERT_TAIL(&parkinglot, pu, list); + AST_LIST_UNLOCK(&parkinglot); + + return pu; +} + +/* Park a call */ +static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, const char *orig_chan_name, struct parkeduser *pu) +{ + struct ast_context *con; + + /* Get a valid space if not already done */ + if (pu == NULL) + pu = park_space_reserve(chan); + if (pu == NULL) + return 1; /* Continue execution if possible */ + + snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", pu->parkingnum); + chan->appl = "Parked Call"; chan->data = NULL; @@ -484,10 +511,9 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in } pu->start = ast_tvnow(); - pu->parkingnum = x; pu->parkingtime = (timeout > 0) ? timeout : parkingtime; if (extout) - *extout = x; + *extout = pu->parkingnum; if (peer) { /* This is so ugly that it hurts, but implementing get_base_channel() on local channels @@ -519,12 +545,12 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context)); ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten)); pu->priority = chan->macropriority ? chan->macropriority : chan->priority; - AST_LIST_INSERT_TAIL(&parkinglot, pu, list); - /* If parking a channel directly, don't quiet yet get parking running on it */ - if (peer == chan) - pu->notquiteyet = 1; - AST_LIST_UNLOCK(&parkinglot); + /* If parking a channel directly, don't quiet yet get parking running on it. + * All parking lot entries are put into the parking lot with notquiteyet on. */ + if (peer != chan) + pu->notquiteyet = 0; + /* Wake up the (presumably select()ing) thread */ pthread_kill(parking_thread, SIGURG); ast_verb(2, "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parking_con, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); @@ -577,15 +603,21 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in /*! \brief Park a call */ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout) { - return park_call_full(chan, peer, timeout, extout, NULL); + return park_call_full(chan, peer, timeout, extout, NULL, NULL); } static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout, int play_announcement, const char *orig_chan_name) { struct ast_channel *chan; struct ast_frame *f; + struct parkeduser *pu; int park_status; + if ((pu = park_space_reserve(rchan)) == NULL) { + ast_stream_and_wait(peer, "beeperr", ""); + return FEATURE_RETURN_PARKFAILED; + } + /* Make a new, fake channel that we'll use to masquerade in the real one */ if (!(chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, rchan->accountcode, rchan->exten, rchan->context, rchan->amaflags, "Parked/%s",rchan->name))) { ast_log(LOG_WARNING, "Unable to create parked channel\n"); @@ -612,7 +644,7 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i orig_chan_name = ast_strdupa(chan->name); } - park_status = park_call_full(chan, peer, timeout, extout, orig_chan_name); + park_status = park_call_full(chan, peer, timeout, extout, orig_chan_name, pu); if (park_status == 1) { /* would be nice to play "invalid parking extension" */ ast_hangup(chan); @@ -632,15 +664,6 @@ static int masq_park_call_announce(struct ast_channel *rchan, struct ast_channel return masq_park_call(rchan, peer, timeout, extout, 1, orig_chan_name); } -#define FEATURE_RETURN_HANGUP -1 -#define FEATURE_RETURN_SUCCESSBREAK 0 -#define FEATURE_RETURN_PBX_KEEPALIVE AST_PBX_KEEPALIVE -#define FEATURE_RETURN_NO_HANGUP_PEER AST_PBX_NO_HANGUP_PEER -#define FEATURE_RETURN_PASSDIGITS 21 -#define FEATURE_RETURN_STOREDIGITS 22 -#define FEATURE_RETURN_SUCCESS 23 -#define FEATURE_RETURN_KEEPTRYING 24 - #define FEATURE_SENSE_CHAN (1 << 0) #define FEATURE_SENSE_PEER (1 << 1) @@ -695,8 +718,8 @@ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, res = ast_safe_sleep(chan, 1000); if (!res) { /* one direction used to call park_call.... */ - masq_park_call_announce(parkee, parker, 0, NULL, NULL); - res = 0; /* PBX should hangup zombie channel */ + res = masq_park_call_announce(parkee, parker, 0, NULL, NULL); + /* PBX should hangup zombie channel if a masquerade actually occurred (res=0) */ } return res; } @@ -968,6 +991,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p const char *transferer_real_context; char xferto[256]; int res; + int parkstatus = 0; set_peers(&transferer, &transferee, peer, chan, sense); transferer_real_context = real_ctx(transferer, transferee); @@ -996,14 +1020,14 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p res = finishup(transferee); if (res) res = -1; - else if (!masq_park_call_announce(transferee, transferer, 0, NULL, NULL)) { /* success */ + else if (!(parkstatus = masq_park_call_announce(transferee, transferer, 0, NULL, NULL))) { /* success */ /* We return non-zero, but tell the PBX not to hang the channel when the thread dies -- We have to be careful now though. We are responsible for hanging up the channel, else it will never be hung up! */ return 0; } else { - ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name); + ast_log(LOG_WARNING, "Unable to park call %s, parkstatus = %d\n", transferee->name, parkstatus); } /*! \todo XXX Maybe we should have another message here instead of invalid extension XXX */ } else if (ast_exists_extension(transferee, transferer_real_context, xferto, 1, transferer->cid.cid_num)) { @@ -1046,7 +1070,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p } else { ast_verb(3, "Unable to find extension '%s' in context '%s'\n", xferto, transferer_real_context); } - if (ast_stream_and_wait(transferer, xferfailsound, AST_DIGIT_ANY) < 0) { + if (parkstatus != FEATURE_RETURN_PARKFAILED && ast_stream_and_wait(transferer, xferfailsound, AST_DIGIT_ANY) < 0) { finishup(transferee); return -1; } @@ -1704,6 +1728,9 @@ static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *p !ast_strlen_zero(builtin_features[x].exten)) { /* Feature is up for consideration */ if (!strcmp(builtin_features[x].exten, code)) { + if (option_debug > 2) { + ast_log(LOG_DEBUG, "Feature detected: fname=%s sname=%s exten=%s\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten); + } res = builtin_features[x].operation(chan, peer, config, code, sense, NULL); feature_detected = 1; break;