diff -Naur asterisk-1.4.20.1.old/configs/features.conf.sample asterisk-1.4.20.1.new/configs/features.conf.sample --- asterisk-1.4.20.1.old/configs/features.conf.sample 2008-02-06 16:19:41.000000000 +0100 +++ asterisk-1.4.20.1.new/configs/features.conf.sample 2008-05-28 13:32:21.000000000 +0200 @@ -3,24 +3,27 @@ ; [general] -parkext => 700 ; What extension to dial to park -parkpos => 701-720 ; What extensions to park calls on. These needs to be - ; numeric, as Asterisk starts from the start position +parkext => 700 ; What extension to dial to park (default parking lot) +parkpos => 701-720 ; What extensions to park calls on (default parking lot). + ; These needs to be numeric, as Asterisk starts from the start position ; and increments with one for the next parked call. -context => parkedcalls ; Which context parked calls are in +context => parkedcalls ; Which context parked calls are in (default parking lot) ;parkingtime => 45 ; Number of seconds a call can be parked for - ; (default is 45 seconds) + ; (default parking lot, default is 45 seconds) +;parkhints = yes ; Automatically add hints (default parking lot, default no) ;courtesytone = beep ; Sound file to play to the parked caller ; when someone dials a parked call ; or the Touch Monitor is activated/deactivated. -;parkedplay = caller ; Who to play the courtesy tone to when picking up a parked call + ; (default parking lot) +;parkedplay = caller ; Who to play the courtesy tone to when picking up a parked call (default parking lot). ; one of: parked, caller, both (default is caller) -;adsipark = yes ; if you want ADSI parking announcements -;findslot => next ; Continue to the 'next' free parking space. +;adsipark = yes ; if you want ADSI parking announcements (default parking lot) +;findslot => next ; Continue to the 'next' free parking space (default parking lot). ; Defaults to 'first' available ;parkedmusicclass=default ; This is the MOH class to use for the parked channel ; as long as the class is not set on the channel directly ; using Set(CHANNEL(musicclass)=whatever) in the dialplan + ; (default parking lot) ;transferdigittimeout => 3 ; Number of seconds to wait between digits when transferring a call ; (default is 3 seconds) @@ -96,3 +99,17 @@ ;unpauseMonitor => #3,self/callee,UnPauseMonitor ;Allow the callee to unpause monitoring ; ;on their channel ; + +;*** Define another parking lot +; +;[taigaparklot] +:parkext => 800 +;parkpos => 801-750 +;context => taigapark +;parkingtime => 45 +;parkhints = yes +;courtesytone = beep +;parkedplay = caller +;adsipark = yes +;findslot => next +;parkedmusicclass=default diff -Naur asterisk-1.4.20.1.old/res/res_features.c asterisk-1.4.20.1.new/res/res_features.c --- asterisk-1.4.20.1.old/res/res_features.c 2008-04-22 20:29:56.000000000 +0200 +++ asterisk-1.4.20.1.new/res/res_features.c 2008-05-28 13:54:13.000000000 +0200 @@ -29,7 +29,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 114542 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include #include @@ -62,11 +62,13 @@ #include "asterisk/adsi.h" #include "asterisk/devicestate.h" #include "asterisk/monitor.h" +#include "asterisk/astobj.h" #define DEFAULT_PARK_TIME 45000 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000 +#define DEFAULT_PARKINGLOT "default" /*!< Default parking lot */ #define AST_MAX_WATCHERS 256 @@ -81,25 +83,34 @@ static char *parkedcall = "ParkedCall"; -static int parkaddhints = 0; /*!< Add parking hints automatically */ -static int parkingtime = DEFAULT_PARK_TIME; /*!< No more than 45 seconds parked before you do something with them */ -static char parking_con[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */ static char parking_con_dial[AST_MAX_EXTENSION]; /*!< Context for dialback for parking (KLUDGE) */ -static char parking_ext[AST_MAX_EXTENSION]; /*!< Extension you type to park the call */ static char pickup_ext[AST_MAX_EXTENSION]; /*!< Call pickup extension */ -static char parkmohclass[MAX_MUSICCLASS]; /*!< Music class used for parking */ -static int parking_start; /*!< First available extension for parking */ -static int parking_stop; /*!< Last available extension for parking */ - -static char courtesytone[256]; /*!< Courtesy tone */ -static int parkedplay = 0; /*!< Who to play the courtesy tone to */ static char xfersound[256]; /*!< Call transfer sound */ static char xferfailsound[256]; /*!< Call transfer failure sound */ -static int parking_offset; -static int parkfindnext; +/*! \brief Structure for parking lots in a linked list. */ +struct ast_parkinglot { + ASTOBJ_COMPONENTS(struct ast_parkinglot); /*!< various object stuff, including ->name */ + char parking_con[AST_MAX_EXTENSION]; /*!< Context for which parking is made accessible */ + char parking_ext[AST_MAX_EXTENSION]; /*!< Extension you type to park the call */ + char parkmohclass[MAX_MUSICCLASS]; /*!< Music class used for parking */ + char courtesytone[256]; /*!< Courtesy tone */ + int parkedplay; /*!< Who to play the courtesy tone to */ + int parkaddhints; /*!< Add parking hints automatically */ + int adsipark; + int parking_start; /*!< First available extension for parking */ + int parking_stop; /*!< Last available extension for parking */ + int parking_offset; + int parkfindnext; + int parkingtime; /*!< Default parking time */ + struct parkeduser *occupiedlots; +}; + +struct ast_parkinglot_list { + ASTOBJ_CONTAINER_COMPONENTS(struct ast_parkinglot); +} parkinglots; -static int adsipark; +struct ast_parkinglot *default_parkinglot = NULL; static int transferdigittimeout; static int featuredigittimeout; @@ -135,6 +146,7 @@ static struct ast_app *monitor_app = NULL; static int monitor_ok = 1; +/*! \brief Structure to handle one parked user */ struct parkeduser { struct ast_channel *chan; /*!< Parking channel */ struct timeval start; /*!< Time the parking started */ @@ -148,17 +160,32 @@ char peername[1024]; unsigned char moh_trys; struct parkeduser *next; + struct ast_parkinglot *parkinglot; }; -static struct parkeduser *parkinglot; - -AST_MUTEX_DEFINE_STATIC(parking_lock); /*!< protects all static variables above */ - static pthread_t parking_thread; +/* Forward declarations */ +static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot); +static void parkinglot_unref(struct ast_parkinglot *parkinglot); +static void parkinglot_destroy(struct ast_parkinglot *ruin); +static struct ast_parkinglot *find_parkinglot(const char *parkinglotname); +static void park_add_hints(char *context, int start, int stop); +static int finishup(struct ast_channel *chan); +static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee); + char *ast_parking_ext(void) { - return parking_ext; + /* Each parking lot now uses its own 'parking extension', + so we can't return ONE value. + Function is used from channel drivers to check if + parking call can be handled directly. + As we return "" this check always fails and the action + is passed to ast_async_goto() which uses the dial plan. + Thats not the short way but it works. + Behaviour results in call of park_call_exec() to park the call + instead of ast_park_call(). */ + return ""; } char *ast_pickup_ext(void) @@ -309,36 +336,53 @@ return AST_DEVICE_INUSE; } -static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, char *orig_chan_name) +/* Park a call */ +static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, + char *orig_chan_name, const char *parkinglotname) { struct parkeduser *pu, *cur; int i, x = -1, parking_range; struct ast_context *con; const char *parkingexten; - + struct ast_parkinglot *parkinglot = 0; + + if (parkinglotname) + parkinglot = find_parkinglot(parkinglotname); + if (!parkinglot) { + parkinglot_addref(default_parkinglot); + parkinglot = default_parkinglot; + } + + ASTOBJ_WRLOCK(parkinglot); + + if (option_debug) + ast_log(LOG_DEBUG, "Parkinglot: %s\n", parkinglot->name); + /* Allocate memory for parking data */ - if (!(pu = ast_calloc(1, sizeof(*pu)))) + if (!(pu = ast_calloc(1, sizeof(*pu)))) { + ASTOBJ_UNLOCK(parkinglot); + parkinglot_unref(parkinglot); return -1; + } - /* Lock parking lot */ - ast_mutex_lock(&parking_lock); /* Check for channel variable PARKINGEXTEN */ parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN"); if (!ast_strlen_zero(parkingexten)) { - if (ast_exists_extension(NULL, parking_con, parkingexten, 1, NULL)) { - ast_mutex_unlock(&parking_lock); + if (ast_exists_extension(NULL, parkinglot->parking_con, parkingexten, 1, NULL)) { + ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parkinglot->parking_con); + ASTOBJ_UNLOCK(parkinglot); + parkinglot_unref(parkinglot); free(pu); - ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con); return 1; /* Continue execution if possible */ } ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten)); x = atoi(parkingexten); } else { /* Select parking space within range */ - parking_range = parking_stop - parking_start+1; + parking_range = parkinglot->parking_stop - parkinglot->parking_start + 1; for (i = 0; i < parking_range; i++) { - x = (i + parking_offset) % parking_range + parking_start; - cur = parkinglot; + x = (i + parkinglot->parking_offset) % parking_range + parkinglot->parking_start; + cur = parkinglot->occupiedlots; while(cur) { if (cur->parkingnum == x) break; @@ -349,14 +393,15 @@ } if (!(i < parking_range)) { - ast_log(LOG_WARNING, "No more parking spaces\n"); + ast_log(LOG_WARNING, "No more parking spaces in parking lot \"%s\"\n", parkinglot->name); free(pu); - ast_mutex_unlock(&parking_lock); + ASTOBJ_UNLOCK(parkinglot); + parkinglot_unref(parkinglot); return -1; } /* Set pointer for next parking */ - if (parkfindnext) - parking_offset = x - parking_start + 1; + if (parkinglot->parkfindnext) + parkinglot->parking_offset = x - parkinglot->parking_start + 1; } chan->appl = "Parked Call"; @@ -367,61 +412,72 @@ /* Put the parked channel on hold if we have two different channels */ if (chan != peer) { ast_indicate_data(pu->chan, AST_CONTROL_HOLD, - S_OR(parkmohclass, NULL), - !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); + S_OR(parkinglot->parkmohclass, NULL), + !ast_strlen_zero(parkinglot->parkmohclass) ? strlen(parkinglot->parkmohclass) + 1 : 0); } pu->start = ast_tvnow(); pu->parkingnum = x; - pu->parkingtime = (timeout > 0) ? timeout : parkingtime; + pu->parkinglot = parkinglot; + pu->parkingtime = (timeout > 0) ? timeout : parkinglot->parkingtime; if (extout) *extout = x; - if (peer) + if (peer) ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); + else { + /* try to get peer name, some channel driver set this value */ + const char *peername = pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"); + if (peername) { + if (option_debug) + ast_log(LOG_DEBUG, "Got name of peer: %s.\n", peername); + ast_copy_string(pu->peername, peername, sizeof(pu->peername)); + } + } /* Remember what had been dialed, so that if the parking expires, we try to come back to the same place */ 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; - pu->next = parkinglot; - parkinglot = pu; + pu->next = parkinglot->occupiedlots; + parkinglot->occupiedlots = pu; /* If parking a channel directly, don't quiet yet get parking running on it */ if (peer == chan) pu->notquiteyet = 1; - ast_mutex_unlock(&parking_lock); + ASTOBJ_UNLOCK(parkinglot); /* Wake up the (presumably select()ing) thread */ pthread_kill(parking_thread, SIGURG); if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_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)); + ast_verbose(VERBOSE_PREFIX_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)); if (pu->parkingnum != -1) snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); manager_event(EVENT_FLAG_CALL, "ParkedCall", "Exten: %s\r\n" "Channel: %s\r\n" + "Parkinglot: %s\r\n" "From: %s\r\n" "Timeout: %ld\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n", - pu->parkingexten, pu->chan->name, peer ? peer->name : "", + pu->parkingexten, pu->chan->name, pu->parkinglot->name, peer ? peer->name : "", (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL), S_OR(pu->chan->cid.cid_num, ""), S_OR(pu->chan->cid.cid_name, "") ); - if (peer && adsipark && ast_adsi_available(peer)) { + if (peer && parkinglot->adsipark && ast_adsi_available(peer)) { adsi_announce_park(peer, pu->parkingexten); /* Only supports parking numbers */ ast_adsi_unload_session(peer); } - con = ast_context_find(parking_con); + con = ast_context_find(parkinglot->parking_con); if (!con) - con = ast_context_create(NULL, parking_con, registrar); + con = ast_context_create(NULL, 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", parking_con); + ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parkinglot->parking_con); /* Tell the peer channel the number of the parking space */ if (peer && ((pu->parkingnum != -1 && ast_strlen_zero(orig_chan_name)) || !strcasecmp(peer->name, orig_chan_name))) { /* Only say number if it's a number and the channel hasn't been masqueraded away */ /* Make sure we don't start saying digits to the channel being parked */ @@ -430,17 +486,29 @@ ast_clear_flag(peer, AST_FLAG_MASQ_NOSTREAM); } if (con) { - if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), ast_free, registrar)) - notify_metermaids(pu->parkingexten, parking_con); + char extatlot[AST_MAX_EXTENSION*2]; + char *pextatlot; + if (!ast_strlen_zero(parkinglotname)) { + snprintf(extatlot, sizeof(extatlot), "%s@%s", pu->parkingexten, parkinglotname); + pextatlot = extatlot; + } + else + pextatlot = pu->parkingexten; + if (option_debug) + ast_log(LOG_DEBUG, "Add extension to dialplan: %s(%s).\n", parkedcall, pextatlot); + if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pextatlot), ast_free, registrar)) + notify_metermaids(pu->parkingexten, parkinglot->parking_con); } if (pu->notquiteyet) { /* Wake up parking thread if we're really done */ ast_indicate_data(pu->chan, AST_CONTROL_HOLD, - S_OR(parkmohclass, NULL), - !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); + S_OR(parkinglot->parkmohclass, NULL), + !ast_strlen_zero(parkinglot->parkmohclass) ? strlen(parkinglot->parkmohclass) + 1 : 0); pu->notquiteyet = 0; pthread_kill(parking_thread, SIGURG); } + parkinglot_unref(parkinglot); + return 0; } @@ -449,10 +517,14 @@ after these channels too */ 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); + if (option_debug) + ast_log(LOG_DEBUG, "Park '%s' without indication of parkinglot name, peer: %s.\n", + chan ? chan->name : "", peer ? peer->name : ""); + return park_call_full(chan, peer, timeout, extout, NULL, NULL); } -int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout) + +static int masq_park_call_full(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout, const char *parkinglotname) { struct ast_channel *chan; struct ast_frame *f; @@ -479,7 +551,16 @@ orig_chan_name = ast_strdupa(chan->name); - park_call_full(chan, peer, timeout, extout, orig_chan_name); + return park_call_full(chan, peer, timeout, extout, orig_chan_name, parkinglotname); +} + +int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout) +{ + if (option_debug) + ast_log(LOG_DEBUG, "Park '%s' without indication of parkinglot name, peer: %s.\n", + rchan ? rchan->name : "", peer ? peer->name : ""); + + masq_park_call_full(rchan, peer, timeout, extout, NULL); return 0; } @@ -512,6 +593,31 @@ } } +static struct ast_exten *find_matching_application(struct ast_context *c, const char *exten, const char *app) +{ + struct ast_exten *e; + struct ast_include *i; + struct ast_context *c2; + + for (e=ast_walk_context_extensions(c, NULL); e; e=ast_walk_context_extensions(c, e)) { + if (!(exten) || ast_extension_match(ast_get_extension_name(e), exten)) { + if (ast_extension_match(ast_get_extension_app(e), app)) + return e; + } + } + /* No match, run through includes */ + for (i=ast_walk_context_includes(c, NULL); i; i=ast_walk_context_includes(c, i)) { + for (c2=ast_walk_contexts(NULL); c2; c2=ast_walk_contexts(c2)) { + if (!strcmp(ast_get_context_name(c2), ast_get_include_name(i))) { + e = find_matching_application(c2, exten, app); + if (e) + return e; + } + } + } + return NULL; +} + /*! \brief support routing for one touch call parking */ static int builtin_parkcall(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) { @@ -519,6 +625,11 @@ struct ast_channel *parkee; int res = 0; struct ast_module_user *u; + const char *transferer_real_context; + struct ast_context *con; + static struct ast_exten *e; + char *parkinglotname; + struct ast_parkinglot *parkinglot = 0; u = ast_module_user_add(chan); @@ -531,27 +642,76 @@ res = ast_answer(chan); if (!res) res = ast_safe_sleep(chan, 1000); - if (!res) - res = ast_park_call(parkee, parker, 0, NULL); + if (!res) { + transferer_real_context = real_ctx(parker, parkee); - ast_module_user_remove(u); + /* Get parkinglot name from dialplan */ + con = ast_context_find(transferer_real_context); + e = find_matching_application(con, NULL, parkcall); + if (e) { + parkinglotname = ast_get_extension_app_data(e); + if (option_debug) + ast_log(LOG_DEBUG, "Park entry present in dialplan for context %s, parkinglot name: %s.\n", + transferer_real_context, parkinglotname ? parkinglotname : ""); + /* Get parkinglot */ + if (parkinglotname) { + parkinglot = find_parkinglot(parkinglotname); + if (parkinglot) + parkinglot_unref(parkinglot); + } + if (!parkinglot) { + parkinglot = default_parkinglot; + } + } else { + if (option_debug) + ast_log(LOG_DEBUG, "Park entry not present in dialplan for context \"%s\".\n", + transferer_real_context); + } - if (!res) { - if (sense == FEATURE_SENSE_CHAN) - res = AST_PBX_NO_HANGUP_PEER; - else - res = AST_PBX_KEEPALIVE; + if (parkinglot) { + if (option_debug) + ast_log(LOG_DEBUG, "Parking lot: %s, parking extension: %s.\n", + parkinglot->name, parkinglot->parking_ext); + res = finishup(parkee); + if (res) + res = -1; + else if (!park_call_full(parkee, parker, 0, NULL, NULL, parkinglotname)) { /* success */ + ast_module_user_remove(u); + /* 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 (parker == peer) ? AST_PBX_KEEPALIVE : AST_PBX_NO_HANGUP_PEER; + } else { + ast_log(LOG_WARNING, "Unable to park call %s\n", parkee->name); + } + } } - return res; + ast_module_user_remove(u); + + if (ast_stream_and_wait(parker, xferfailsound, parker->language, AST_DIGIT_ANY) < 0 ) { + finishup(parkee); + return -1; + } + ast_stopstream(parker); + res = finishup(parkee); + if (res) { + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", parkee->name); + return res; + } + return FEATURE_RETURN_SUCCESS; } + static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense, void *data) { char *caller_chan_id = NULL, *callee_chan_id = NULL, *args = NULL, *touch_filename = NULL; int x = 0; size_t len; struct ast_channel *caller_chan, *callee_chan; + char *courtesytone; if (!monitor_ok) { ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n"); @@ -566,6 +726,7 @@ set_peers(&caller_chan, &callee_chan, peer, chan, sense); + courtesytone = (default_parkinglot) ? default_parkinglot->courtesytone : ""; if (!ast_strlen_zero(courtesytone)) { if (ast_autoservice_start(callee_chan)) return -1; @@ -664,6 +825,10 @@ const char *transferer_real_context; char xferto[256]; int res; + struct ast_context *con; + static struct ast_exten *e; + char *parkinglotname; + struct ast_parkinglot *parkinglot = 0; set_peers(&transferer, &transferee, peer, chan, sense); transferer_real_context = real_ctx(transferer, transferee); @@ -688,11 +853,40 @@ finishup(transferee); return res; } - if (!strcmp(xferto, ast_parking_ext())) { + + if (option_debug) + ast_log(LOG_DEBUG, "Got number, xferto: %s.\n", xferto); + + /* Get parkinglot name from dialplan */ + con = ast_context_find(transferer_real_context); + e = find_matching_application(con, xferto, parkcall); + if (e) { + parkinglotname = ast_get_extension_app_data(e); + if (option_debug) + ast_log(LOG_DEBUG, "Park entry present in dialplan for context %s, parkinglot name: %s.\n", + transferer_real_context, parkinglotname ? parkinglotname : ""); + /* Get parkinglot */ + if (parkinglotname) { + parkinglot = find_parkinglot(parkinglotname); + if (parkinglot) + parkinglot_unref(parkinglot); + } + if (!parkinglot) + parkinglot = default_parkinglot; + } else { + if (option_debug) + ast_log(LOG_DEBUG, "Park entry not present in dialplan for context \"%s\" and extension \"%s\".\n", + transferer_real_context, xferto); + } + if (option_debug && parkinglot) + ast_log(LOG_DEBUG, "Parking lot: %s, parking extension: %s, xferto: %s.\n", + parkinglot->name, parkinglot->parking_ext, xferto); + + if (parkinglot && !strcmp(xferto, parkinglot->parking_ext)) { res = finishup(transferee); if (res) res = -1; - else if (!ast_park_call(transferee, transferer, 0, NULL)) { /* success */ + else if (!park_call_full(transferee, transferer, 0, NULL, NULL, parkinglotname)) { /* 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! */ @@ -1650,176 +1844,196 @@ return res; } -static void post_manager_event(const char *s, char *parkingexten, struct ast_channel *chan) +static void post_manager_event(const char *s, struct parkeduser *pu) { manager_event(EVENT_FLAG_CALL, s, "Exten: %s\r\n" "Channel: %s\r\n" + "Parkinglot: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n\r\n", - parkingexten, - chan->name, - S_OR(chan->cid.cid_num, ""), - S_OR(chan->cid.cid_name, "") + pu->parkingexten, + pu->chan->name, + pu->parkinglot->name, + S_OR(pu->chan->cid.cid_num, ""), + S_OR(pu->chan->cid.cid_name, "") ); } -/*! \brief Take care of parked calls and unpark them if needed */ -static void *do_parking_thread(void *ignore) +/*! \brief Run management on parkinglots, called once per parkinglot */ +static int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, fd_set *nrfds, fd_set *nefds, int *ms, int *max) { - fd_set rfds, efds; /* results from previous select, to be preserved across loops. */ - FD_ZERO(&rfds); - FD_ZERO(&efds); - - for (;;) { - struct parkeduser *pu, *pl, *pt = NULL; - int ms = -1; /* select timeout, uninitialized */ - int max = -1; /* max fd, none there yet */ - fd_set nrfds, nefds; /* args for the next select */ - FD_ZERO(&nrfds); - FD_ZERO(&nefds); + struct parkeduser *pu, *pl = NULL, *pt = NULL; + int res = 0; - ast_mutex_lock(&parking_lock); - pl = NULL; - pu = parkinglot; - /* navigate the list with prev-cur pointers to support removals */ - while (pu) { - struct ast_channel *chan = pu->chan; /* shorthand */ - int tms; /* timeout for this item */ - int x; /* fd index in channel */ - struct ast_context *con; + pu = curlot->occupiedlots; - if (pu->notquiteyet) { /* Pretend this one isn't here yet */ - pl = pu; - pu = pu->next; - continue; - } - tms = ast_tvdiff_ms(ast_tvnow(), pu->start); - if (tms > pu->parkingtime) { - ast_indicate(chan, AST_CONTROL_UNHOLD); - /* Get chan, exten from derived kludge */ - if (pu->peername[0]) { - char *peername = ast_strdupa(pu->peername); - char *cp = strrchr(peername, '-'); - if (cp) - *cp = 0; - con = ast_context_find(parking_con_dial); - if (!con) { - con = ast_context_create(NULL, parking_con_dial, registrar); - if (!con) - ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial); - } - if (con) { - char returnexten[AST_MAX_EXTENSION]; - snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername); - ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar); - } - set_c_e_p(chan, parking_con_dial, peername, 1); - } else { - /* They've been waiting too long, send them back to where they came. Theoretically they - should have their original extensions and such, but we copy to be on the safe side */ - set_c_e_p(chan, pu->context, pu->exten, pu->priority); + while (pu) { + struct ast_channel *chan = pu->chan; /* shorthand */ + int tms; /* timeout for this item */ + int x; /* fd index in channel */ + struct ast_context *con; + + if (pu->notquiteyet) { /* Pretend this one isn't here yet */ + pl = pu; + pu = pu->next; + continue; + } + tms = ast_tvdiff_ms(ast_tvnow(), pu->start); + if (tms > pu->parkingtime) { + /* Stop music on hold */ + ast_indicate(chan, AST_CONTROL_UNHOLD); + /* Get chan, exten from derived kludge */ + if (pu->peername[0]) { + char *peername = ast_strdupa(pu->peername); + char *cp = strrchr(peername, '-'); + if (cp) + *cp = 0; + con = ast_context_find(parking_con_dial); + if (!con) { + con = ast_context_create(NULL, parking_con_dial, registrar); + if (!con) + ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial); + } + if (con) { + char returnexten[AST_MAX_EXTENSION]; + snprintf(returnexten, sizeof(returnexten), "%s|30|t", peername); + ast_add_extension2(con, 1, peername, 1, NULL, NULL, "Dial", strdup(returnexten), ast_free, registrar); } + set_c_e_p(chan, parking_con_dial, peername, 1); + } else { + /* They've been waiting too long, send them back to where they came. Theoretically they + should have their original extensions and such, but we copy to be on the safe side */ + set_c_e_p(chan, pu->context, pu->exten, pu->priority); + } - post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan); + post_manager_event("ParkedCallTimeOut", pu); - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan->name, pu->parkingnum, chan->context, chan->exten, chan->priority); - /* Start up the PBX, or hang them up */ - if (ast_pbx_start(chan)) { - ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name); - ast_hangup(chan); - } - /* And take them out of the parking lot */ - if (pl) - pl->next = pu->next; + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->chan->context, pu->chan->exten, pu->chan->priority); + /* Start up the PBX, or hang them up */ + if (ast_pbx_start(chan)) { + ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name); + ast_hangup(chan); + } + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + curlot->occupiedlots = pu->next; + pt = pu; + pu = pu->next; + con = ast_context_find(pt->parkinglot->parking_con); + if (con) { + if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) + ast_log(LOG_WARNING, "Whoa, failed to remove the parking extension!\n"); else - parkinglot = pu->next; - pt = pu; - pu = pu->next; - con = ast_context_find(parking_con); - if (con) { - if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) - ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); - else - notify_metermaids(pt->parkingexten, parking_con); - } else - ast_log(LOG_WARNING, "Whoa, no parking context?\n"); - free(pt); - } else { /* still within parking time, process descriptors */ - for (x = 0; x < AST_MAX_FDS; x++) { - struct ast_frame *f; + notify_metermaids(pt->parkingexten, curlot->parking_con); + } else + ast_log(LOG_WARNING, "Whoa, no parking context?\n"); + free(pt); + } else { /* still within parking time, process descriptors */ + for (x = 0; x < AST_MAX_FDS; x++) { + struct ast_frame *f; - if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds))) - continue; /* nothing on this descriptor */ + if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], rfds) && !FD_ISSET(chan->fds[x], efds))) + continue; /* nothing on this descriptor */ - if (FD_ISSET(chan->fds[x], &efds)) - ast_set_flag(chan, AST_FLAG_EXCEPTION); - else - ast_clear_flag(chan, AST_FLAG_EXCEPTION); - chan->fdno = x; + if (FD_ISSET(chan->fds[x], efds)) + ast_set_flag(chan, AST_FLAG_EXCEPTION); + else + ast_clear_flag(chan, AST_FLAG_EXCEPTION); + chan->fdno = x; - /* See if they need servicing */ - f = ast_read(chan); - if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) { - if (f) - ast_frfree(f); - post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan); + /* See if they need servicing */ + f = ast_read(pu->chan); - /* There's a problem, hang them up*/ - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name); - ast_hangup(chan); - /* And take them out of the parking lot */ - if (pl) - pl->next = pu->next; - else - parkinglot = pu->next; - pt = pu; - pu = pu->next; - con = ast_context_find(parking_con); - if (con) { - if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) - ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); - else - notify_metermaids(pt->parkingexten, parking_con); - } else - ast_log(LOG_WARNING, "Whoa, no parking context?\n"); - free(pt); - break; - } else { - /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + /* Hangup? */ + if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) { + if (f) ast_frfree(f); - if (pu->moh_trys < 3 && !chan->generatordata) { - if (option_debug) - ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting.\n"); - ast_indicate_data(pu->chan, AST_CONTROL_HOLD, - S_OR(parkmohclass, NULL), - !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0); - pu->moh_trys++; - } - goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */ + post_manager_event("ParkedCallGiveUp", pu); + + /* There's a problem, hang them up*/ + if (option_verbose > 1) + ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", chan->name); + ast_hangup(chan); + /* And take them out of the parking lot */ + if (pl) + pl->next = pu->next; + else + curlot->occupiedlots = pu->next; + pt = pu; + pu = pu->next; + con = ast_context_find(curlot->parking_con); + if (con) { + if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) + ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); + else + notify_metermaids(pt->parkingexten, curlot->parking_con); + } else + ast_log(LOG_WARNING, "Whoa, no parking context for parking lot %s?\n", curlot->name); + free(pt); + break; + } else { + /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */ + ast_frfree(f); + if (pu->moh_trys < 3 && !chan->generatordata) { + if (option_debug) + ast_log(LOG_DEBUG, "MOH on parked call stopped by outside source. Restarting on channel %s.\n", chan->name); + ast_indicate_data(chan, AST_CONTROL_HOLD, + S_OR(curlot->parkmohclass, NULL), + !ast_strlen_zero(curlot->parkmohclass) ? strlen(curlot->parkmohclass) + 1 : 0); + pu->moh_trys++; } + goto std; /*! \todo XXX Ick: jumping into an else statement??? XXX */ + } - } /* end for */ - if (x >= AST_MAX_FDS) { -std: for (x=0; xfds[x] > -1) { - FD_SET(chan->fds[x], &nrfds); - FD_SET(chan->fds[x], &nefds); - if (chan->fds[x] > max) - max = chan->fds[x]; - } + } /* End for */ + if (x >= AST_MAX_FDS) { +std: for (x=0; xfds[x] > -1) { + FD_SET(chan->fds[x], nrfds); + FD_SET(chan->fds[x], nefds); + if (chan->fds[x] > *max) + *max = chan->fds[x]; } - /* Keep track of our shortest wait */ - if (tms < ms || ms < 0) - ms = tms; - pl = pu; - pu = pu->next; } + /* Keep track of our shortest wait */ + if (tms < *ms || *ms < 0) + *ms = tms; + pl = pu; + pu = pu->next; } - } /* end while */ - ast_mutex_unlock(&parking_lock); + } + } /* end while */ + + return res; +} + +/*! \brief Take care of parked calls and unpark them if needed */ +static void *do_parking_thread(void *ignore) +{ + fd_set rfds, efds; /* results from previous select, to be preserved across loops. */ + fd_set nrfds, nefds; /* args for the next select */ + FD_ZERO(&rfds); + FD_ZERO(&efds); + + for (;;) { + int res = 0; + int ms = -1; /* select timeout, uninitialized */ + int max = -1; /* max fd, none there yet */ + FD_ZERO(&nrfds); + FD_ZERO(&nefds); + + /* We need to do this for every parking lot */ + ASTOBJ_CONTAINER_TRAVERSE(&parkinglots, 1, do { + parkinglot_addref(iterator); + ASTOBJ_WRLOCK(iterator); + res = manage_parkinglot(iterator, &rfds, &efds, &nrfds, &nefds, &ms, &max); + ASTOBJ_UNLOCK(iterator); + parkinglot_unref(iterator); + } while (0) ); rfds = nrfds; efds = nefds; { @@ -1832,6 +2046,22 @@ return NULL; /* Never reached */ } +/* Find parkinglot by name */ +static struct ast_parkinglot *find_parkinglot(const char *parkinglotname) +{ + struct ast_parkinglot *parkinglot = NULL; + + if (ast_strlen_zero(parkinglotname)) + return NULL; + + parkinglot = ASTOBJ_CONTAINER_FIND(&parkinglots, parkinglotname); + + if (parkinglot && option_debug) + ast_log(LOG_DEBUG, "Found Parkinglot: %s\n", parkinglot->name); + + return parkinglot; +} + /*! \brief Park a call */ static int park_call_exec(struct ast_channel *chan, void *data) { @@ -1842,11 +2072,15 @@ char orig_exten[AST_MAX_EXTENSION]; int orig_priority = chan->priority; - /* Data is unused at the moment but could contain a parking - lot context eventually */ + /* Data contains the name of the parking lot */ + char *parkinglotname = data; int res = 0; struct ast_module_user *u; + if (option_debug) + ast_log(LOG_DEBUG, "Park '%s', parkinglot name: '%s'.\n", + orig_chan_name, parkinglotname ? parkinglotname : ""); + u = ast_module_user_add(chan); ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten)); @@ -1863,7 +2097,7 @@ res = ast_safe_sleep(chan, 1000); /* Park the call */ if (!res) { - res = park_call_full(chan, chan, 0, NULL, orig_chan_name); + res = park_call_full(chan, NULL, 0, NULL, orig_chan_name, parkinglotname); /* Continue on in the dialplan */ if (res == 1) { ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten)); @@ -1886,6 +2120,9 @@ struct ast_channel *peer=NULL; struct parkeduser *pu, *pl=NULL; struct ast_context *con; + struct ast_parkinglot *parkinglot = NULL; + char *exten; + char *parkinglotname; int park; struct ast_bridge_config config; @@ -1897,39 +2134,51 @@ u = ast_module_user_add(chan); - park = atoi((char *)data); - ast_mutex_lock(&parking_lock); - pu = parkinglot; + /* data should contain something like: @ */ + parkinglotname = ast_strdupa(data); + exten = strsep(&parkinglotname, "@"); + park = atoi((char *)exten); + + if (parkinglotname) + parkinglot = find_parkinglot(parkinglotname); + if (!parkinglot) { + parkinglot = default_parkinglot; + parkinglot_addref(parkinglot); + } + + ASTOBJ_WRLOCK(parkinglot); + pu = parkinglot->occupiedlots; while(pu) { if (pu->parkingnum == park) { if (pl) pl->next = pu->next; else - parkinglot = pu->next; + parkinglot->occupiedlots = pu->next; break; } pl = pu; pu = pu->next; } - ast_mutex_unlock(&parking_lock); if (pu) { peer = pu->chan; - con = ast_context_find(parking_con); + con = ast_context_find(pu->parkinglot->parking_con); if (con) { if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL)) ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); else - notify_metermaids(pu->parkingexten, parking_con); + notify_metermaids(pu->parkingexten, parkinglot->parking_con); } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); manager_event(EVENT_FLAG_CALL, "UnParkedCall", "Exten: %s\r\n" "Channel: %s\r\n" + "Parkinglot: %s\r\n" + "Context: %s\r\n" "From: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n", - pu->parkingexten, pu->chan->name, chan->name, + pu->parkingexten, pu->chan->name, pu->parkinglot->name, pu->parkinglot->parking_con, chan->name, S_OR(pu->chan->cid.cid_num, ""), S_OR(pu->chan->cid.cid_name, "") ); @@ -1942,17 +2191,17 @@ if (peer) { /* Play a courtesy to the source(s) configured to prefix the bridge connecting */ - - if (!ast_strlen_zero(courtesytone)) { + + if (!ast_strlen_zero(parkinglot->courtesytone)) { int error = 0; ast_indicate(peer, AST_CONTROL_UNHOLD); - if (parkedplay == 0) { - error = ast_stream_and_wait(chan, courtesytone, chan->language, ""); - } else if (parkedplay == 1) { - error = ast_stream_and_wait(peer, courtesytone, chan->language, ""); - } else if (parkedplay == 2) { - if (!ast_streamfile(chan, courtesytone, chan->language) && - !ast_streamfile(peer, courtesytone, chan->language)) { + if (parkinglot->parkedplay == 0) { + error = ast_stream_and_wait(chan, parkinglot->courtesytone, chan->language, ""); + } else if (parkinglot->parkedplay == 1) { + error = ast_stream_and_wait(peer, parkinglot->courtesytone, chan->language, ""); + } else if (parkinglot->parkedplay == 2) { + if (!ast_streamfile(chan, parkinglot->courtesytone, chan->language) && + !ast_streamfile(peer, parkinglot->courtesytone, chan->language)) { /*! \todo XXX we would like to wait on both! */ res = ast_waitstream(chan, ""); if (res >= 0) @@ -1963,6 +2212,8 @@ } if (error) { ast_log(LOG_WARNING, "Failed to play courtesy tone!\n"); + ASTOBJ_UNLOCK(parkinglot); + parkinglot_unref(parkinglot); ast_hangup(peer); ast_module_user_remove(u); return -1; @@ -1973,6 +2224,8 @@ res = ast_channel_make_compatible(chan, peer); if (res < 0) { ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", chan->name, peer->name); + ASTOBJ_UNLOCK(parkinglot); + parkinglot_unref(parkinglot); ast_hangup(peer); ast_module_user_remove(u); return -1; @@ -1980,13 +2233,17 @@ /* This runs sorta backwards, since we give the incoming channel control, as if it were the person called. */ if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park); + ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d in lot %s\n", chan->name, park, parkinglot->name); + + ASTOBJ_UNLOCK(parkinglot); + parkinglot_unref(parkinglot); pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); ast_cdr_setdestchan(chan->cdr, peer->name); memset(&config, 0, sizeof(struct ast_bridge_config)); ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); + res = ast_bridge_call(chan, peer, &config); pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name); @@ -2040,9 +2297,16 @@ } ast_cli(fd, "\nCall parking\n"); ast_cli(fd, "------------\n"); - ast_cli(fd,"%-20s: %s\n", "Parking extension", parking_ext); - ast_cli(fd,"%-20s: %s\n", "Parking context", parking_con); - ast_cli(fd,"%-20s: %d-%d\n", "Parked call extensions", parking_start, parking_stop); + + ASTOBJ_CONTAINER_TRAVERSE(&parkinglots, 1, do { + ASTOBJ_RDLOCK(iterator); + ast_cli(fd, "%-24s : %s\n", "Parking lot", iterator->name); + ast_cli(fd, " %-24s: %s\n", "Parking extension", iterator->parking_ext); + ast_cli(fd, " %-24s: %s\n", "Parking context", iterator->parking_con); + ast_cli(fd, " %-24s: %d-%d\n", "Parked call extensions", iterator->parking_start, iterator->parking_stop); + ASTOBJ_UNLOCK(iterator); + + } while (0) ); ast_cli(fd,"\n"); return RESULT_SUCCESS; @@ -2060,17 +2324,25 @@ ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel" , "Context", "Extension", "Pri", "Timeout"); - ast_mutex_lock(&parking_lock); - - for (cur = parkinglot; cur; cur = cur->next) { - ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n" - ,cur->parkingexten, cur->chan->name, cur->context, cur->exten - ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL)); - - numparked++; - } - ast_mutex_unlock(&parking_lock); - ast_cli(fd, "%d parked call%s.\n", numparked, (numparked != 1) ? "s" : ""); + ASTOBJ_CONTAINER_TRAVERSE(&parkinglots, 1, do { + int lotparked = 0; + ASTOBJ_RDLOCK(iterator); + cur = iterator->occupiedlots; + if (cur) + ast_cli(fd, "*** Parking lot: %s\n", iterator->name); + while(cur) { + ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n" + ,cur->parkingexten, cur->chan->name, cur->context, cur->exten + ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL)); + cur = cur->next; + lotparked++; + } + if (lotparked) + ast_cli(fd, " %d parked call%s in parking lot %s\n", lotparked, (lotparked != 1) ? "s" : "", iterator->name); + numparked += lotparked; + ASTOBJ_UNLOCK(iterator); + } while (0) ); + ast_cli(fd, "---\n%d parked call%s in total.\n", numparked, (numparked != 1) ? "s" : ""); return RESULT_SUCCESS; @@ -2080,6 +2352,43 @@ "Usage: show parkedcalls\n" " Lists currently parked calls.\n"; +static int handle_parkinglots(int fd, int argc, char *argv[]) +{ + int count = 0; + + ast_cli(fd, "Parking lots:\n"); + + ASTOBJ_CONTAINER_TRAVERSE(&parkinglots, 1, do { + ASTOBJ_RDLOCK(iterator); + ast_cli(fd, "Parking lot: \"%s\"\n", iterator->name); + ast_cli(fd, "----------------------------------\n"); + ast_cli(fd, " Extension: %s\n", iterator->parking_ext); + ast_cli(fd, " Context: %s\n", iterator->parking_con); + ast_cli(fd, " Park position: %d-%d\n", iterator->parking_start, iterator->parking_stop); + ast_cli(fd, " Findslot: %s\n", iterator->parkfindnext ? "next" : "-"); + ast_cli(fd, " Parking_offset: %d\n", iterator->parking_offset); + ast_cli(fd, " Occupiedlots: %s\n", iterator->occupiedlots ? "yes" : "no"); + ast_cli(fd, " Parkedmusicclass: %s\n", iterator->parkmohclass); + ast_cli(fd, " Parkedplay: %s\n", (iterator->parkedplay == 2) ? "both" : ((iterator->parkedplay == 1) ? "parked" : "caller")); + ast_cli(fd, " Courtesytone: %s\n", iterator->courtesytone); + ast_cli(fd, " Parkingtime (sec): %d\n\n", iterator->parkingtime/1000); +#if 0 + ast_cli(fd, " Refcount: %d\n\n", iterator->refcount); /* for debug purpose */ +#endif + count++; + ASTOBJ_UNLOCK(iterator); + } while (0) ); + if (count) + ast_cli(fd, " %d parking lot%s.\n", count, (count != 1) ? "s" : ""); + else + ast_cli(fd, " No parking lots configured.\n"); + return RESULT_SUCCESS; +} + +static char showparkinglots_help[] = +"Usage: show parkinglots\n" +" Lists all parking lots.\n"; + static struct ast_cli_entry cli_show_features_deprecated = { { "show", "features", NULL }, handle_showfeatures, NULL, @@ -2093,12 +2402,17 @@ { { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help }, + + { { "show", "parkinglots", NULL }, + handle_parkinglots, "Lists parkinglots", + showparkinglots_help }, }; /*! \brief Dump lot status */ static int manager_parking_status( struct mansession *s, const struct message *m) { struct parkeduser *cur; + struct ast_parkinglot *parkinglot = NULL; const char *id = astman_get_header(m, "ActionID"); char idText[256] = ""; @@ -2107,32 +2421,43 @@ astman_send_ack(s, m, "Parked calls will follow"); - ast_mutex_lock(&parking_lock); + ASTOBJ_CONTAINER_TRAVERSE(&parkinglots, 1, do { + ASTOBJ_RDLOCK(iterator); - for (cur = parkinglot; cur; cur = cur->next) { - astman_append(s, "Event: ParkedCall\r\n" - "Exten: %d\r\n" - "Channel: %s\r\n" - "From: %s\r\n" - "Timeout: %ld\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "%s" - "\r\n", - cur->parkingnum, cur->chan->name, cur->peername, - (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL), - S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is */ - S_OR(cur->chan->cid.cid_name, ""), - idText); - } + /* if Parkinglot is specified only show parked calls of this parking lot */ + if (!parkinglot || (!strcmp(parkinglot->name, iterator->name))) { + cur=iterator->occupiedlots; + while(cur) { + astman_append(s, "Event: ParkedCall\r\n" + "Exten: %d\r\n" + "Channel: %s\r\n" + "From: %s\r\n" + "Parkinglot: %s\r\n" + "Context: %s\r\n" + "Timeout: %ld\r\n" + "CallerID: %s\r\n" + "CallerIDName: %s\r\n" + "%s" + "\r\n", + cur->parkingnum, cur->chan->name, cur->peername, + iterator->name, iterator->parking_con, + (long)cur->start.tv_sec + (long)(cur->parkingtime/1000) - (long)time(NULL), + S_OR(cur->chan->cid.cid_num, ""), /* XXX in other places it is */ + S_OR(cur->chan->cid.cid_name, ""), + idText); + + cur = cur->next; + } + } + + ASTOBJ_UNLOCK(iterator); + } while (0) ); astman_append(s, "Event: ParkedCallsComplete\r\n" "%s" "\r\n",idText); - ast_mutex_unlock(&parking_lock); - return RESULT_SUCCESS; } @@ -2233,6 +2558,233 @@ return res; } +/*! \brief Unreference parkinglot object. If no more references, + then go ahead and delete it */ +static void parkinglot_unref(struct ast_parkinglot *parkinglot) +{ + if (option_debug > 2) + ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, parkinglot->refcount - 1); + ASTOBJ_UNREF(parkinglot, parkinglot_destroy); +} + +static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot) +{ + if (option_debug > 2) + ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, parkinglot->refcount + 1); + return ASTOBJ_REF(parkinglot); +} + +/*! \brief Allocate parking lot structure */ +static struct ast_parkinglot *create_parkinglot(char *name) +{ + struct ast_parkinglot *newlot = (struct ast_parkinglot *) NULL; + + if (!name) + return NULL; + + newlot = ast_calloc(1, sizeof(*newlot)); + if (!newlot) + return NULL; + + ASTOBJ_INIT(newlot); + ast_copy_string(newlot->name, name, sizeof(newlot->name)); + + return newlot; +} + +/*! \brief Destroy a parking lot */ +static void parkinglot_destroy(struct ast_parkinglot *ruin) +{ + struct ast_context *con; + struct parkeduser *pu, *pl; + + con = ast_context_find(ruin->parking_con); + if (con) { + if (ast_context_remove_extension2(con, ruin->parking_ext, 1, NULL)) + ast_log(LOG_WARNING, "Failed to remove extension %s in context %s.\n", ruin->parking_ext, ruin->parking_con); + + /* Cleanup parked users */ + pu = ruin->occupiedlots; + while (pu) { + pl = pu; + pu = pu->next; + if (pl->chan) { + if (ast_context_remove_extension2(con, pl->parkingexten, 1, NULL)) + ast_log(LOG_WARNING, "Failed to remove extension %s in context %s.\n", pl->parkingexten, ruin->parking_con); + ast_hangup(pl->chan); + } + free(pl); + } + + /* Delete context if there are no more entries */ + if (!ast_walk_context_extensions(con, NULL)) + ast_context_destroy(con, NULL); + } + + ASTOBJ_CONTAINER_UNLINK(&parkinglots, ruin); /* Remove from parkinglot list */ + + free(ruin); +} + +/*! \brief Build parkinglot from configuration and chain it in */ +static struct ast_parkinglot *build_parkinglot(char *parkinglotname, struct ast_variable *var, int reload) +{ + struct ast_parkinglot *parkinglot; + struct ast_context *con = NULL; + + struct ast_variable *confvar = var; + int error = 0; + int start = 0, end = 0; + int res = 0; + char old_parking_ext[AST_MAX_EXTENSION]; + char old_parking_con[AST_MAX_EXTENSION] = ""; + + if (reload) { + parkinglot = find_parkinglot(parkinglotname); + if (parkinglot) { + strcpy(old_parking_ext, parkinglot->parking_ext); + strcpy(old_parking_con, parkinglot->parking_con); + } + else { + parkinglot = create_parkinglot(parkinglotname); + if (!parkinglot) { + ast_log(LOG_ERROR, "Reload, configuration of parkinglot %s failed.\n", parkinglotname); + return NULL; + } + reload = 0; + } + } + else { + parkinglot = create_parkinglot(parkinglotname); + if (!parkinglot) { + ast_log(LOG_ERROR, "Configuration of parkinglot %s failed.\n", parkinglotname); + return NULL; + } + } + + /* reload = 1 - refcount should be 2 at this point * + * = 0 - refcount should be 1 at this point */ + + ASTOBJ_WRLOCK(parkinglot); + + if (option_debug) + ast_log(LOG_DEBUG, "Building parking lot %s.\n", parkinglotname); + + /* Initialize parkinglot with default values */ + parkinglot->parking_con[0] = '\0'; /* no default value for parking context, it must be set by configuration */ + strcpy(parkinglot->parking_ext, "700"); + strcpy(parkinglot->parkmohclass, "default"); + parkinglot->courtesytone[0] = '\0'; + parkinglot->parkedplay = 0; + parkinglot->parkaddhints = 0; + parkinglot->adsipark = 0; + parkinglot->parking_start = 701; + parkinglot->parking_stop = 750; + parkinglot->parking_offset = 0; + parkinglot->parkfindnext = 0; + parkinglot->parkingtime = DEFAULT_PARK_TIME; + if (!reload) + parkinglot->occupiedlots = NULL; + + /* Do some config stuff */ + while(confvar) { + if (!strcasecmp(confvar->name, "parkext")) { + ast_copy_string(parkinglot->parking_ext, confvar->value, sizeof(parkinglot->parking_ext)); + } else if (!strcasecmp(confvar->name, "context")) { + ast_copy_string(parkinglot->parking_con, confvar->value, sizeof(parkinglot->parking_con)); + } else if (!strcasecmp(confvar->name, "parkingtime")) { + if ((sscanf(confvar->value, "%d", &parkinglot->parkingtime) != 1) || (parkinglot->parkingtime < 1)) { + ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", confvar->value); + parkinglot->parkingtime = DEFAULT_PARK_TIME; + } else + parkinglot->parkingtime = parkinglot->parkingtime * 1000; + } else if (!strcasecmp(confvar->name, "parkpos")) { + if (sscanf(confvar->value, "%d-%d", &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", confvar->lineno); + error = 1; + } else { + parkinglot->parking_start = start; + parkinglot->parking_stop = end; + } + } else if (!strcasecmp(confvar->name, "findslot")) { + parkinglot->parkfindnext = (!strcasecmp(confvar->value, "next")); + } else if (!strcasecmp(confvar->name, "parkinghints")) { + parkinglot->parkaddhints = ast_true(confvar->value); + } else if (!strcasecmp(confvar->name, "adsipark")) { + parkinglot->adsipark = ast_true(confvar->value); + } else if (!strcasecmp(confvar->name, "courtesytone")) { + ast_copy_string(parkinglot->courtesytone, confvar->value, sizeof(parkinglot->courtesytone)); + } else if (!strcasecmp(confvar->name, "parkedplay")) { + if (!strcasecmp(confvar->value, "both")) + parkinglot->parkedplay = 2; + else if (!strcasecmp(confvar->value, "parked")) + parkinglot->parkedplay = 1; + else + parkinglot->parkedplay = 0; + } else if (!strcasecmp(confvar->name, "parkedmusicclass")) { + ast_copy_string(parkinglot->parkmohclass, confvar->value, sizeof(parkinglot->parkmohclass)); + } + + confvar = confvar->next; + } + + ASTOBJ_UNLOCK(parkinglot); + + /* Check for errors */ + if (ast_strlen_zero(parkinglot->parking_con)) { + ast_log(LOG_WARNING, "Parking lot %s lacks context\n", parkinglotname); + goto failed; + } + if (error) { + ast_log(LOG_WARNING, "Wrong parameter setting for parking lot %s\n", parkinglotname); + goto failed; + } + + /* Remove the old parking extension */ + if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { + if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar)) + notify_metermaids(old_parking_ext, old_parking_con); + /* Delete context if there are no more entries */ + if (!ast_walk_context_extensions(con, NULL)) + ast_context_destroy(con, NULL); + } + + if (option_debug) + ast_log(LOG_DEBUG, "Add to dialplan: %s@%s, parkinglot: %s.\n", + parkinglot->parking_ext, parkinglot->parking_con, parkinglot->name); + + /* Create context */ + if (!(con = ast_context_find(parkinglot->parking_con)) && !(con = ast_context_create(NULL, parkinglot->parking_con, registrar))) { + ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parkinglot->parking_con); + goto failed; + } + + /* Add entry to dialplan: Park(name>) */ + res = ast_add_extension2(con, 1, parkinglot->parking_ext, 1, NULL, NULL, parkcall, strdup(parkinglot->name), ast_free, registrar); + + if (parkinglot->parkaddhints) + park_add_hints(parkinglot->parking_con, parkinglot->parking_start, parkinglot->parking_stop); + + if (!res) { + notify_metermaids(parkinglot->parking_ext, parkinglot->parking_con); + if (reload) { + parkinglot_unref(parkinglot); + } else { + /* Move it into the list */ + ASTOBJ_CONTAINER_LINK(&parkinglots, parkinglot); + parkinglot_unref(parkinglot); + } + return parkinglot; + } + +failed: + ast_log(LOG_WARNING, "Parking %s not open for business. Configuration error.\n", parkinglot->name); + parkinglot_destroy(parkinglot); + + return NULL; +} + + /*! \brief Add parking hints for all defined parking lots */ static void park_add_hints(char *context, int start, int stop) { @@ -2248,69 +2800,162 @@ } -static int load_config(void) +static void unmark_valid_parkinglots(void) +{ + struct ast_parkinglot *parkinglot; + char *parkinglotname; + char *section; + struct ast_config *cfg = NULL; + + cfg = ast_config_load("features.conf"); + if (!cfg) { + ast_log(LOG_WARNING,"Could not load features.conf\n"); + return; + } + + /* Now, find parking lot definitions */ + section = NULL; + while ((section = ast_category_browse(cfg, section))) { + if (option_debug) + ast_log(LOG_DEBUG, "*** Found section %s\n", section); + + if ( !strncasecmp(section, "featuremap", strlen("featuremap")) || + !strncasecmp(section, "applicationmap", strlen("applicationmap")) ) + continue; + + if (option_debug) + ast_log(LOG_DEBUG, "Found configuration section %s, assume parking lot.\n", section); + + parkinglotname = !strncasecmp(section, "general", strlen("general")) ? "default" : section; + parkinglot = find_parkinglot(parkinglotname); + if (parkinglot) { + ASTOBJ_UNMARK(parkinglot); + parkinglot_unref(parkinglot); + } + } + + ast_config_destroy(cfg); + + return; +} + + +static int load_config(int reload) { int start = 0, end = 0; int res; struct ast_context *con = NULL; struct ast_config *cfg = NULL; struct ast_variable *var = NULL; + char *section; char old_parking_ext[AST_MAX_EXTENSION]; char old_parking_con[AST_MAX_EXTENSION] = ""; + int reloaddpl = reload; - if (!ast_strlen_zero(parking_con)) { - strcpy(old_parking_ext, parking_ext); - strcpy(old_parking_con, parking_con); - } + if (reloaddpl && default_parkinglot) { + strcpy(old_parking_ext, default_parkinglot->parking_ext); + strcpy(old_parking_con, default_parkinglot->parking_con); + } - /* Reset to defaults */ - strcpy(parking_con, "parkedcalls"); + /* Reset general parameters to defaults */ strcpy(parking_con_dial, "park-dial"); - strcpy(parking_ext, "700"); strcpy(pickup_ext, "*8"); - strcpy(parkmohclass, "default"); - courtesytone[0] = '\0'; strcpy(xfersound, "beep"); strcpy(xferfailsound, "pbx-invalid"); - parking_start = 701; - parking_stop = 750; - parkfindnext = 0; - adsipark = 0; - parkaddhints = 0; transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; + if (reloaddpl) { + default_parkinglot = find_parkinglot(DEFAULT_PARKINGLOT); + if (!default_parkinglot) { + default_parkinglot = create_parkinglot(DEFAULT_PARKINGLOT); + if (!default_parkinglot) { + ast_log(LOG_ERROR, "Reload, configuration of default parkinglot failed.\n"); + return -1; + } + reloaddpl = 0; + } + } + else { + default_parkinglot = create_parkinglot(DEFAULT_PARKINGLOT); + if (!default_parkinglot) { + ast_log(LOG_ERROR, "Configuration of default parkinglot failed.\n"); + return -1; + } + } + + /* reloaddpl = 1 - refcount should be 2 at this point * + * = 0 - refcount should be 1 at this point */ + cfg = ast_config_load("features.conf"); if (!cfg) { ast_log(LOG_WARNING,"Could not load features.conf\n"); + parkinglot_unref(default_parkinglot); return AST_MODULE_LOAD_DECLINE; } + + ASTOBJ_WRLOCK(default_parkinglot); + + if (option_debug) + ast_log(LOG_DEBUG, "Building default parking lot.\n"); + + /* Reset values for default parkinglot */ + strcpy(default_parkinglot->parking_con, "parkedcalls"); + strcpy(default_parkinglot->parking_ext, "700"); + strcpy(default_parkinglot->parkmohclass, "default"); + default_parkinglot->courtesytone[0] = '\0'; + default_parkinglot->parkedplay = 0; + default_parkinglot->parkaddhints = 0; + default_parkinglot->adsipark = 0; + default_parkinglot->parking_start = 701; + default_parkinglot->parking_stop = 750; + default_parkinglot->parking_offset = 0; + default_parkinglot->parkfindnext = 0; + default_parkinglot->parkingtime = DEFAULT_PARK_TIME; + if (!reloaddpl) + default_parkinglot->occupiedlots = NULL; + for (var = ast_variable_browse(cfg, "general"); var; var = var->next) { + + /* Set parameters for default parkinglot */ if (!strcasecmp(var->name, "parkext")) { - ast_copy_string(parking_ext, var->value, sizeof(parking_ext)); + ast_copy_string(default_parkinglot->parking_ext, var->value, sizeof(default_parkinglot->parking_ext)); } else if (!strcasecmp(var->name, "context")) { - ast_copy_string(parking_con, var->value, sizeof(parking_con)); + ast_copy_string(default_parkinglot->parking_con, var->value, sizeof(default_parkinglot->parking_con)); } else if (!strcasecmp(var->name, "parkingtime")) { - if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) { + if ((sscanf(var->value, "%d", &default_parkinglot->parkingtime) != 1) || (default_parkinglot->parkingtime < 1)) { ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value); - parkingtime = DEFAULT_PARK_TIME; + default_parkinglot->parkingtime = DEFAULT_PARK_TIME; } else - parkingtime = parkingtime * 1000; + default_parkinglot->parkingtime = default_parkinglot->parkingtime * 1000; } else if (!strcasecmp(var->name, "parkpos")) { if (sscanf(var->value, "%d-%d", &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; + default_parkinglot->parking_start = start; + default_parkinglot->parking_stop = end; } } else if (!strcasecmp(var->name, "findslot")) { - parkfindnext = (!strcasecmp(var->value, "next")); + default_parkinglot->parkfindnext = (!strcasecmp(var->value, "next")); } else if (!strcasecmp(var->name, "parkinghints")) { - parkaddhints = ast_true(var->value); + default_parkinglot->parkaddhints = ast_true(var->value); } else if (!strcasecmp(var->name, "adsipark")) { - adsipark = ast_true(var->value); + default_parkinglot->adsipark = ast_true(var->value); + } else if (!strcasecmp(var->name, "courtesytone")) { + ast_copy_string(default_parkinglot->courtesytone, var->value, sizeof(default_parkinglot->courtesytone)); + } else if (!strcasecmp(var->name, "parkedplay")) { + if (!strcasecmp(var->value, "both")) + default_parkinglot->parkedplay = 2; + else if (!strcasecmp(var->value, "parked")) + default_parkinglot->parkedplay = 1; + else + default_parkinglot->parkedplay = 0; + } else if (!strcasecmp(var->name, "parkedmusicclass")) { + ast_copy_string(default_parkinglot->parkmohclass, var->value, sizeof(default_parkinglot->parkmohclass)); + + /* Set general parameters */ } 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); @@ -2328,26 +2973,17 @@ atxfernoanswertimeout = DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER; } else atxfernoanswertimeout = atxfernoanswertimeout * 1000; - } else if (!strcasecmp(var->name, "courtesytone")) { - ast_copy_string(courtesytone, var->value, sizeof(courtesytone)); - } else if (!strcasecmp(var->name, "parkedplay")) { - if (!strcasecmp(var->value, "both")) - parkedplay = 2; - else if (!strcasecmp(var->value, "parked")) - parkedplay = 1; - else - parkedplay = 0; } else if (!strcasecmp(var->name, "xfersound")) { ast_copy_string(xfersound, var->value, sizeof(xfersound)); } else if (!strcasecmp(var->name, "xferfailsound")) { ast_copy_string(xferfailsound, var->value, sizeof(xferfailsound)); } else if (!strcasecmp(var->name, "pickupexten")) { ast_copy_string(pickup_ext, var->value, sizeof(pickup_ext)); - } else if (!strcasecmp(var->name, "parkedmusicclass")) { - ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass)); } } + ASTOBJ_UNLOCK(default_parkinglot); + unmap_features(); for (var = ast_variable_browse(cfg, "featuremap"); var; var = var->next) { if (remap_feature(var->name, var->value)) @@ -2435,42 +3071,88 @@ if (option_verbose >= 1) ast_verbose(VERBOSE_PREFIX_2 "Mapping Feature '%s' to app '%s(%s)' with code '%s'\n", var->name, app, app_args, exten); } + + /* Now, find parking lot definitions */ + section = NULL; + while ((section = ast_category_browse(cfg, section))) { + if (option_debug) + ast_log(LOG_DEBUG, "*** Found section %s\n", section); + + /* Assume additional parkinglot, if keyword is unequal to predefined keyword */ + if ( !strncasecmp(section, "general", strlen("general")) || + !strncasecmp(section, "featuremap", strlen("featuremap")) || + !strncasecmp(section, "applicationmap", strlen("applicationmap")) ) + continue; + + if (option_debug) + ast_log(LOG_DEBUG, "Found configuration section %s, assume parking lot.\n", section); + if(!build_parkinglot(section, ast_variable_browse(cfg, section), reload)) + ast_log(LOG_ERROR, "Could not build parking lot %s. Configuration error.\n", section); + else if (option_debug) + ast_log(LOG_DEBUG, "Configured parking context %s\n", section); + } + ast_config_destroy(cfg); /* Remove the old parking extension */ - if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { + if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { if(ast_context_remove_extension2(con, old_parking_ext, 1, registrar)) notify_metermaids(old_parking_ext, old_parking_con); - if (option_debug) - ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); + /* Delete context if there are no more entries */ + if (!ast_walk_context_extensions(con, NULL)) + ast_context_destroy(con, NULL); } - - if (!(con = ast_context_find(parking_con)) && !(con = ast_context_create(NULL, parking_con, registrar))) { - ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); + + /* Add default parking lot to dialplan */ + if (!(con = ast_context_find(default_parkinglot->parking_con)) && !(con = ast_context_create(NULL, default_parkinglot->parking_con, registrar))) { + ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", default_parkinglot->parking_con); + if (reloaddpl) + parkinglot_unref(default_parkinglot); return -1; } - res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar); - if (parkaddhints) - park_add_hints(parking_con, parking_start, parking_stop); - if (!res) - notify_metermaids(ast_parking_ext(), parking_con); - return res; + res = ast_add_extension2(con, 1, default_parkinglot->parking_ext, 1, NULL, NULL, parkcall, NULL, NULL, registrar); + if (default_parkinglot->parkaddhints) + park_add_hints(default_parkinglot->parking_con, default_parkinglot->parking_start, default_parkinglot->parking_stop); + if (!res) { + notify_metermaids(default_parkinglot->parking_ext, default_parkinglot->parking_con); + if (!reloaddpl) { + /* Move it into the list */ + ASTOBJ_CONTAINER_LINK(&parkinglots, default_parkinglot); + parkinglot_unref(default_parkinglot); + } + } else { + ast_log(LOG_WARNING, "Parking %s not open for business. Configuration error.\n", default_parkinglot->name); + } + if (reloaddpl) + parkinglot_unref(default_parkinglot); + return res; } static int reload(void) { - return load_config(); + int res; + + /* Release parking lot list */ + ASTOBJ_CONTAINER_MARKALL(&parkinglots); + + unmark_valid_parkinglots(); + + ASTOBJ_CONTAINER_PRUNE_MARKED(&parkinglots, parkinglot_destroy); + + /* Reload configuration */ + res = load_config(1); + + return res; } static int load_module(void) { int res; - - memset(parking_ext, 0, sizeof(parking_ext)); - memset(parking_con, 0, sizeof(parking_con)); - if ((res = load_config())) + ASTOBJ_CONTAINER_INIT(&parkinglots); + + if ((res = load_config(0))) return res; ast_cli_register_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); ast_pthread_create(&parking_thread, NULL, do_parking_thread, NULL); @@ -2491,6 +3173,8 @@ static int unload_module(void) { + int res; + ast_module_user_hangup_all(); ast_manager_unregister("ParkedCalls"); @@ -2498,7 +3182,19 @@ ast_cli_unregister_multiple(cli_features, sizeof(cli_features) / sizeof(struct ast_cli_entry)); ast_unregister_application(parkcall); ast_devstate_prov_del("Park"); - return ast_unregister_application(parkedcall); + res = ast_unregister_application(parkedcall); + + ASTOBJ_CONTAINER_DESTROYALL(&parkinglots,parkinglot_destroy); + ASTOBJ_CONTAINER_DESTROY(&parkinglots); + + default_parkinglot = NULL; + + /* Drop all parkinglot contexts from dialplan. + * Already should be done by parkinglot_destroy(), + for security reason do it again. */ + ast_context_destroy(NULL, registrar); + + return res; } AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Call Features Resource",