diff options
-rw-r--r-- | include/asterisk/features.h | 1 | ||||
-rw-r--r-- | main/features.c | 124 |
2 files changed, 76 insertions, 49 deletions
diff --git a/include/asterisk/features.h b/include/asterisk/features.h index 4d581885d..c4abbae31 100644 --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -67,6 +67,7 @@ struct ast_call_feature { #define AST_FEATURE_RETURN_STOREDIGITS 22 #define AST_FEATURE_RETURN_SUCCESS 23 #define AST_FEATURE_RETURN_KEEPTRYING 24 +#define AST_FEATURE_RETURN_PARKFAILED 25 /*! * \brief Park a call and read back parked location diff --git a/main/features.c b/main/features.c index 66f4b57d2..7f8b78ca0 100644 --- a/main/features.c +++ b/main/features.c @@ -486,15 +486,15 @@ struct ast_park_call_args { const char *return_ext; int return_pri; uint32_t flags; + /*! Parked user that has already obtained a parking space */ + struct parkeduser *pu; }; -/* Park a call */ -static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer, - struct ast_park_call_args *args) +static struct parkeduser *park_space_reserve(struct ast_channel *chan, + struct ast_channel *peer, struct ast_park_call_args *args) { struct parkeduser *pu; - int i, x = -1, parking_range, parkingnum_copy; - struct ast_context *con; + int i, parking_space = -1, parking_range; const char *parkinglotname = NULL; const char *parkingexten; struct ast_parkinglot *parkinglot = NULL; @@ -517,7 +517,7 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer /* Allocate memory for parking data */ if (!(pu = ast_calloc(1, sizeof(*pu)))) { parkinglot_unref(parkinglot); - return -1; + return NULL; } /* Lock parking list */ @@ -531,21 +531,21 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer * 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->parkings); parkinglot_unref(parkinglot); 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, parkinglot->parking_con, pu->parkingexten, 1, NULL)) { AST_LIST_UNLOCK(&parkinglot->parkings); parkinglot_unref(parkinglot); ast_free(pu); ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parkinglot->parking_con); - return 1; /* Continue execution if possible */ + return NULL; } } else { int start; @@ -573,7 +573,7 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer } if (!cur || i == start - 1) { - x = i; + parking_space = i; break; } } @@ -583,13 +583,36 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer ast_free(pu); AST_LIST_UNLOCK(&parkinglot->parkings); parkinglot_unref(parkinglot); - return -1; + return NULL; } /* Set pointer for next parking */ if (parkinglot->parkfindnext) - parkinglot->parking_offset = x - parkinglot->parking_start + 1; - snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); + parkinglot->parking_offset = parking_space - parkinglot->parking_start + 1; + snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space); } + + pu->notquiteyet = 1; + pu->parkingnum = parking_space; + pu->parkinglot = parkinglot; + AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list); + parkinglot_unref(parkinglot); + + return pu; +} + +/* Park a call */ +static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer, struct ast_park_call_args *args) +{ + struct ast_context *con; + int parkingnum_copy; + struct parkeduser *pu = args->pu; + + if (pu == NULL) + pu = park_space_reserve(chan, peer, args); + 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; @@ -602,18 +625,16 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer ast_indicate(pu->chan, AST_CONTROL_RINGING); } else { ast_indicate_data(pu->chan, AST_CONTROL_HOLD, - S_OR(parkinglot->mohclass, NULL), - !ast_strlen_zero(parkinglot->mohclass) ? strlen(parkinglot->mohclass) + 1 : 0); + S_OR(pu->parkinglot->mohclass, NULL), + !ast_strlen_zero(pu->parkinglot->mohclass) ? strlen(pu->parkinglot->mohclass) + 1 : 0); } } pu->start = ast_tvnow(); - pu->parkingnum = x; - pu->parkinglot = parkinglot; - pu->parkingtime = (args->timeout > 0) ? args->timeout : parkinglot->parkingtime; + pu->parkingtime = (args->timeout > 0) ? args->timeout : pu->parkinglot->parkingtime; parkingnum_copy = pu->parkingnum; if (args->extout) - *(args->extout) = x; + *(args->extout) = pu->parkingnum; if (peer) { /* This is so ugly that it hurts, but implementing get_base_channel() on local channels @@ -651,15 +672,14 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer pu->priority = pu->priority ? pu->priority : (chan->macropriority ? chan->macropriority : chan->priority); - AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list); - - /* If parking a channel directly, don't quiet yet get parking running on it */ - if (peer == chan) - pu->notquiteyet = 1; + /* 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 (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parkinglot->name, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); + ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); manager_event(EVENT_FLAG_CALL, "ParkedCall", "Exten: %s\r\n" @@ -682,15 +702,15 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer ast_adsi_unload_session(peer); } - con = ast_context_find_or_create(NULL, NULL, parkinglot->parking_con, registrar); + con = ast_context_find_or_create(NULL, NULL, pu->parkinglot->parking_con, registrar); if (!con) /* Still no context? Bad */ - ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parkinglot->parking_con); + ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", pu->parkinglot->parking_con); if (con) { if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, ast_strdup(pu->parkingexten), ast_free_ptr, registrar)) - notify_metermaids(pu->parkingexten, parkinglot->parking_con, AST_DEVICE_INUSE); + notify_metermaids(pu->parkingexten, pu->parkinglot->parking_con, AST_DEVICE_INUSE); } - AST_LIST_UNLOCK(&parkinglot->parkings); + AST_LIST_UNLOCK(&pu->parkinglot->parkings); /* Only say number if it's a number and the channel hasn't been masqueraded away */ if (peer && !ast_test_flag(args, AST_PARK_OPT_SILENCE) && (ast_strlen_zero(args->orig_chan_name) || !strcasecmp(peer->name, args->orig_chan_name))) { @@ -703,8 +723,8 @@ static int ast_park_call_full(struct ast_channel *chan, struct ast_channel *peer if (peer == chan) { /* pu->notquiteyet = 1 */ /* Wake up parking thread if we're really done */ ast_indicate_data(pu->chan, AST_CONTROL_HOLD, - S_OR(parkinglot->mohclass, NULL), - !ast_strlen_zero(parkinglot->mohclass) ? strlen(parkinglot->mohclass) + 1 : 0); + S_OR(pu->parkinglot->mohclass, NULL), + !ast_strlen_zero(pu->parkinglot->mohclass) ? strlen(pu->parkinglot->mohclass) + 1 : 0); pu->notquiteyet = 0; pthread_kill(parking_thread, SIGURG); } @@ -726,10 +746,20 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i { struct ast_channel *chan; struct ast_frame *f; - char *orig_chan_name = NULL; int park_status; struct ast_park_call_args park_args = {0,}; + if (!args) { + args = &park_args; + args->timeout = timeout; + args->extout = extout; + } + + if ((args->pu = park_space_reserve(rchan, peer, args)) == NULL) { + ast_stream_and_wait(peer, "beeperr", ""); + return AST_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"); @@ -748,20 +778,13 @@ static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, i if ((f = ast_read(chan))) ast_frfree(f); - if (!play_announcement) { - orig_chan_name = ast_strdupa(chan->name); - } - if (peer == rchan) { peer = chan; } - if (!args) { - args = &park_args; - args->timeout = timeout, - args->extout = extout, - args->orig_chan_name = orig_chan_name; - } + if (!play_announcement && args == &park_args) { + args->orig_chan_name = ast_strdupa(chan->name); + } park_status = ast_park_call_full(chan, peer, args); if (park_status == 1) { @@ -843,8 +866,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); - res = 0; /* PBX should hangup zombie channel */ + res = masq_park_call_announce(parkee, parker, 0, NULL); + /* PBX should hangup zombie channel if a masquerade actually occurred (res=0) */ } return res; @@ -1151,7 +1174,7 @@ static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *p struct ast_channel *transferee; const char *transferer_real_context; char xferto[256]; - int res; + int res, parkstatus = 0; set_peers(&transferer, &transferee, peer, chan, sense); transferer_real_context = real_ctx(transferer, transferee); @@ -1180,14 +1203,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)) { /* success */ + else if (!(parkstatus = masq_park_call_announce(transferee, transferer, 0, 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)) { @@ -1230,7 +1253,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 != AST_FEATURE_RETURN_PARKFAILED && ast_stream_and_wait(transferer, xferfailsound, AST_DIGIT_ANY) < 0) { finishup(transferee); return -1; } @@ -1904,6 +1927,7 @@ 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)) { + ast_debug(3, "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; @@ -2910,6 +2934,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); AST_LIST_REMOVE_CURRENT(list); + free(pu); } else { /* still within parking time, process descriptors */ for (x = 0; x < AST_MAX_FDS; x++) { struct ast_frame *f; @@ -2944,6 +2969,7 @@ int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, } else ast_log(LOG_WARNING, "Whoa, no parking context for parking lot %s?\n", curlot->name); AST_LIST_REMOVE_CURRENT(list); + free(pu); break; } else { /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ |