From f8311adcdafbe3e8692f99e2e064112a591972da Mon Sep 17 00:00:00 2001 From: Olle Johansson Date: Mon, 26 Jun 2006 16:43:21 +0000 Subject: [PATCH] METERMAIDS: ----------- - Adding devicestate providers, a new architecture to add non-channel related device state information, like parking lots, queues, meetmes, vending machines and Windows 98 reboots (lots of blinking on those lights) - Adding provider for parking lots, so you can subscribe to the status of a parking lot - Adding provider for meetme, so you can have a blinking lamp for a meetme ( Example: exten => edvina,hint,meetme:1234 ) - Adding support for directed parking - set the PARKINGEXTEN before you manually call Park() and you will be parked on that space. If it's occupied, dialplan execution will continue. This work was sponsored by Voop A/S - www.voop.com git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@36055 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- apps/app_meetme.c | 36 +++++- configs/extensions.conf.sample | 11 +- devicestate.c | 117 ++++++++++++++++-- include/asterisk/devicestate.h | 24 ++++ pbx.c | 2 +- res/res_features.c | 213 ++++++++++++++++++++++++--------- 6 files changed, 335 insertions(+), 68 deletions(-) diff --git a/apps/app_meetme.c b/apps/app_meetme.c index 16cf7f1a85..e162c56a22 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -58,6 +58,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" #include "asterisk/translate.h" #include "asterisk/ulaw.h" +#include "asterisk/devicestate.h" #include "enter.h" #include "leave.h" @@ -274,7 +275,7 @@ struct ast_conference { int markedusers; /*!< Number of marked users */ time_t start; /*!< Start time (s) */ int refcount; /*!< reference count of usage */ - enum recording_state recording:2; /*!< recording status */ + enum recording_state recording:2; /*!< recording status */ unsigned int isdynamic:1; /*!< Created on the fly? */ unsigned int locked:1; /*!< Is the conference locked? */ pthread_t recordthread; /*!< thread for recording */ @@ -967,6 +968,10 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c snprintf(members, sizeof(members), "%d", conf->users); ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL); + /* This device changed state now - if this is the first user */ + if (conf->users == 1) + ast_device_state_changed("meetme:%s", conf->confno); + ast_mutex_unlock(&conf->playlock); if (confflags & CONFFLAG_EXIT_CONTEXT) { @@ -1742,6 +1747,10 @@ bailoutandtrynormal: /* Return the number of seconds the user was in the conf */ snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime)); pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); + + /* This device changed state now */ + if (!conf->users) /* If there are no more members */ + ast_device_state_changed("meetme:%s", conf->confno); } free(user); AST_LIST_UNLOCK(&confs); @@ -2510,6 +2519,29 @@ static void *recordthread(void *args) pthread_exit(0); } +/*! \brief Callback for devicestate providers */ +static int meetmestate(const char *data) +{ + struct ast_conference *conf; + + /* Find conference */ + AST_LIST_LOCK(&confs); + AST_LIST_TRAVERSE(&confs, conf, list) { + if (!strcmp(data, conf->confno)) + break; + } + AST_LIST_UNLOCK(&confs); + if (!conf) + return AST_DEVICE_INVALID; + + + /* SKREP to fill */ + if (!conf->users) + return AST_DEVICE_NOT_INUSE; + + return AST_DEVICE_INUSE; +} + static void load_config(void) { struct ast_config *cfg; @@ -2547,6 +2579,7 @@ static int unload_module(void *mod) res |= ast_unregister_application(app2); res |= ast_unregister_application(app); + ast_devstate_prov_del("Meetme"); STANDARD_HANGUP_LOCALUSERS; return res; @@ -2565,6 +2598,7 @@ static int load_module(void *mod) res |= ast_register_application(app2, count_exec, synopsis2, descrip2); res |= ast_register_application(app, conf_exec, synopsis, descrip); + res |= ast_devstate_prov_add("Meetme", meetmestate); return res; } diff --git a/configs/extensions.conf.sample b/configs/extensions.conf.sample index f74f350a1a..e9dda61616 100644 --- a/configs/extensions.conf.sample +++ b/configs/extensions.conf.sample @@ -278,11 +278,14 @@ include => trunkld ; ignorepat => 9 include => default -include => parkedcalls include => trunklocal include => iaxtel700 include => trunktollfree include => iaxprovider + +;Include parkedcalls (or the context you define in features conf) +;to enable call parking. +include => parkedcalls ; ; You can use an alternative switch type as well, to resolve ; extensions that are not known here, for example with remote @@ -511,6 +514,12 @@ include => demo ;exten => mark,1,Goto(6275|1) ; alias mark to 6275 ;exten => 6536,1,Macro(stdexten,6236,${WIL}) ; Ditto for wil ;exten => wil,1,Goto(6236|1) + +;If you want to subscribe to the status of a parking space, this is +;how you do it. Subscribe to extension 6600 in sip, and you will see +;the status of the first parking lot with this extensions' help +;exten => 6600,hint,park:701@parkedcalls +;exten => 6600,1,noop ; ; Some other handy things are an extension for checking voicemail via ; voicemailmain diff --git a/devicestate.c b/devicestate.c index 4121cfbcbc..b0e5d5aec8 100644 --- a/devicestate.c +++ b/devicestate.c @@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/logger.h" #include "asterisk/devicestate.h" #include "asterisk/pbx.h" +#include "asterisk/app.h" #include "asterisk/options.h" /*! \brief Device state strings for printing */ @@ -53,6 +54,16 @@ static const char *devstatestring[] = { /* 6 AST_DEVICE_RINGING */ "Ringing" /*!< Ring, ring, ring */ }; +/*! \brief A device state provider (not a channel) */ +struct devstate_prov { + char label[40]; + ast_devstate_prov_cb_type callback; + AST_LIST_ENTRY(devstate_prov) list; +}; + +/*! \brief A list of providers */ +static AST_LIST_HEAD_STATIC(devstate_provs, devstate_prov); + /*! \brief A device state watcher (callback) */ struct devstate_cb { void *data; @@ -60,6 +71,7 @@ struct devstate_cb { AST_LIST_ENTRY(devstate_cb) list; }; +/*! \brief A device state watcher list */ static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb); struct state_change { @@ -67,11 +79,19 @@ struct state_change { char device[1]; }; +/*! \brief The state change queue. State changes are queued + for processing by a separate thread */ static AST_LIST_HEAD_STATIC(state_changes, state_change); +/*! \brief The device state change notification thread */ static pthread_t change_thread = AST_PTHREADT_NULL; + +/*! \brief Flag for the queue */ static ast_cond_t change_pending; +/* Forward declarations */ +static int getproviderstate(const char *provider, const char *address); + /*! \brief Find devicestate as text message for output */ const char *devstate2str(int devstate) { @@ -79,9 +99,9 @@ const char *devstate2str(int devstate) } /*! \brief Find out if device is active in a call or not -\note find channels with the device's name in it -This function is only used for channels that does not implement -devicestate natively + \note find channels with the device's name in it + This function is only used for channels that does not implement + devicestate natively */ int ast_parse_device_state(const char *device) { @@ -110,17 +130,34 @@ int ast_parse_device_state(const char *device) int ast_device_state(const char *device) { char *buf; - char *tech; char *number; const struct ast_channel_tech *chan_tech; int res = 0; + /*! \brief Channel driver that provides device state */ + char *tech; + /*! \brief Another provider of device state */ + char *provider = NULL; buf = ast_strdupa(device); tech = strsep(&buf, "/"); number = buf; - if (!number) - return AST_DEVICE_INVALID; - + if (!number) { + provider = strsep(&tech, ":"); + if (!provider) + return AST_DEVICE_INVALID; + /* We have a provider */ + number = tech; + tech = NULL; + } + + if (provider) { + if(option_debug > 2) + ast_log(LOG_DEBUG, "Checking if I can find provider for \"%s\" - number: %s\n", provider, number); + return getproviderstate(provider, number); + } + if (option_debug > 3) + ast_log(LOG_DEBUG, "No provider found, checking channel drivers for %s - %s\n", tech, number); + chan_tech = ast_get_channel_tech(tech); if (!chan_tech) return AST_DEVICE_INVALID; @@ -143,6 +180,63 @@ int ast_device_state(const char *device) } } +/*! \brief Add device state provider */ +int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback) +{ + struct devstate_prov *devprov; + + if (!callback || !(devprov = ast_calloc(1, sizeof(*devprov)))) + return -1; + + devprov->callback = callback; + ast_copy_string(devprov->label, label, sizeof(devprov->label)); + + AST_LIST_LOCK(&devstate_provs); + AST_LIST_INSERT_HEAD(&devstate_provs, devprov, list); + AST_LIST_UNLOCK(&devstate_provs); + + return 0; +} + +/*! \brief Remove device state provider */ +void ast_devstate_prov_del(const char *label) +{ + struct devstate_prov *devcb; + + AST_LIST_LOCK(&devstate_provs); + AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devcb, list) { + if (!strcasecmp(devcb->label, label)) { + AST_LIST_REMOVE_CURRENT(&devstate_provs, list); + free(devcb); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&devstate_provs); +} + +/*! \brief Get provider device state */ +static int getproviderstate(const char *provider, const char *address) +{ + struct devstate_prov *devprov; + int res = AST_DEVICE_INVALID; + + + AST_LIST_LOCK(&devstate_provs); + AST_LIST_TRAVERSE_SAFE_BEGIN(&devstate_provs, devprov, list) { + if(option_debug > 4) + ast_log(LOG_DEBUG, "Checking provider %s with %s\n", devprov->label, provider); + + if (!strcasecmp(devprov->label, provider)) { + res = devprov->callback(address); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&devstate_provs); + return res; +} + /*! \brief Add device state watcher */ int ast_devstate_add(ast_devstate_cb_type callback, void *data) { @@ -178,7 +272,9 @@ void ast_devstate_del(ast_devstate_cb_type callback, void *data) AST_LIST_UNLOCK(&devstate_cbs); } -/*! \brief Notify callback watchers of change, and notify PBX core for hint updates */ +/*! \brief Notify callback watchers of change, and notify PBX core for hint updates + Normally executed within a separate thread +*/ static void do_state_change(const char *device) { int state; @@ -201,10 +297,15 @@ static int __ast_device_state_changed_literal(char *buf) char *device, *tmp; struct state_change *change; + if (option_debug > 2) + ast_log(LOG_DEBUG, "Notification of state change to be queued on device/channel %s\n", buf); + device = buf; if ((tmp = strrchr(device, '-'))) *tmp = '\0'; + + if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) { /* we could not allocate a change struct, or */ /* there is no background thread, so process the change now */ diff --git a/include/asterisk/devicestate.h b/include/asterisk/devicestate.h index 3c7f5b40ee..d93d6d9967 100644 --- a/include/asterisk/devicestate.h +++ b/include/asterisk/devicestate.h @@ -44,8 +44,12 @@ extern "C" { /*! Device is ringing *and* in use */ #define AST_DEVICE_RINGINUSE 7 +/*! \brief Devicestate watcher call back */ typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data); +/*! \brief Devicestate provider call back */ +typedef int (*ast_devstate_prov_cb_type)(const char *data); + /*! \brief Convert device state to text string for output * \param devstate Current device state */ @@ -95,8 +99,28 @@ int ast_device_state_changed_literal(const char *device); * Return -1 on failure, ID on success */ int ast_devstate_add(ast_devstate_cb_type callback, void *data); + +/*! \brief Unregisters a device state change callback + * \param callback Callback + * \param data to pass to callback + * The callback is called if the state for extension is changed + * Return -1 on failure, ID on success + */ void ast_devstate_del(ast_devstate_cb_type callback, void *data); +/*! \brief Add device state provider + * \param label to use in hint, like label:object + * \param callback Callback + * Return -1 on failure, ID on success + */ +int ast_devstate_prov_add(const char *label, ast_devstate_prov_cb_type callback); + +/*! \brief Remove device state provider + * \param label to use in hint, like label:object + * Return -1 on failure, ID on success + */ +void ast_devstate_prov_del(const char *label); + int ast_device_state_engine_init(void); #if defined(__cplusplus) || defined(c_plusplus) diff --git a/pbx.c b/pbx.c index 7bbe3bcdd1..560a932be6 100644 --- a/pbx.c +++ b/pbx.c @@ -188,7 +188,7 @@ struct ast_state_cb { /*! \brief Structure for dial plan hints - Hints are pointers from an extension in the dialplan to one or + \note Hints are pointers from an extension in the dialplan to one or more devices (tech/name) */ struct ast_hint { struct ast_exten *exten; /*!< Extension */ diff --git a/res/res_features.c b/res/res_features.c index 43586ce82b..588229e161 100644 --- a/res/res_features.c +++ b/res/res_features.c @@ -56,6 +56,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/manager.h" #include "asterisk/utils.h" #include "asterisk/adsi.h" +#include "asterisk/devicestate.h" #include "asterisk/monitor.h" #ifdef __AST_DEBUG_MALLOC @@ -76,6 +77,7 @@ static void FREE(void *ptr) 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) */ @@ -119,7 +121,11 @@ static char *descrip2 = "Park():" "transfer to know the parking space). This application is always\n" "registered internally and does not need to be explicitly added\n" "into the dialplan, although you should include the 'parkedcalls'\n" -"context.\n"; +"context (or the context specified in features.conf).\n\n" +"If you set the PARKINGEXTEN variable to an extension in your\n" +"parking context, park() will park the call on that extension, unless\n" +"it already exists. In that case, execution will continue at next\n" +"priority.\n" ; static struct ast_app *monitor_app = NULL; static int monitor_ok = 1; @@ -128,6 +134,7 @@ struct parkeduser { struct ast_channel *chan; /*!< Parking channel */ struct timeval start; /*!< Time the parking started */ int parkingnum; /*!< Parking lot */ + char parkingexten[AST_MAX_EXTENSION]; /*!< If set beforehand, parking extension used for this call */ char context[AST_MAX_CONTEXT]; /*!< Where to go if our parking time expires */ char exten[AST_MAX_EXTENSION]; int priority; @@ -248,14 +255,14 @@ static void ast_bridge_call_thread_launch(void *data) pthread_setschedparam(thread, SCHED_RR, &sched); } -static int adsi_announce_park(struct ast_channel *chan, int parkingnum) +static int adsi_announce_park(struct ast_channel *chan, char *parkingexten) { int res; int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT}; char tmp[256]; char *message[5] = {NULL, NULL, NULL, NULL, NULL}; - snprintf(tmp, sizeof(tmp), "Parked on %d", parkingnum); + snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten); message[0] = tmp; res = adsi_load_session(chan, NULL, 0, 1); if (res == -1) @@ -263,46 +270,95 @@ static int adsi_announce_park(struct ast_channel *chan, int parkingnum) return adsi_print(chan, message, justify, 1); } +/*! \brief Notify metermaids that we've changed an extension */ +static void notify_metermaids(char *exten, char *context) +{ + if (option_debug > 3) + ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context); + + /* Send notification to devicestate subsystem */ + ast_device_state_changed("park:%s@%s", exten, context); + return; +} + +/*! \brief metermaids callback from devicestate.c */ +static int metermaidstate(const char *data) +{ + int res = AST_DEVICE_INVALID; + char *context = ast_strdupa(data); + char *exten; + + exten = strsep(&context, "@"); + if (!context) + return res; + + if (option_debug > 3) + ast_log(LOG_DEBUG, "Checking state of exten %s in context %s\n", exten, context); + + res = ast_exists_extension(NULL, context, exten, 1, NULL); + + if (!res) + return AST_DEVICE_NOT_INUSE; + else + return AST_DEVICE_INUSE; +} + /*! \brief Park a call \note We put the user in the parking list, then wake up the parking thread to be sure it looks after these channels too */ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout) { struct parkeduser *pu, *cur; - int i,x,parking_range; - char exten[AST_MAX_EXTENSION]; + int i, x = -1, parking_range; struct ast_context *con; + const char *parkingexten; + /* Allocate memory for parking data */ if (!(pu = ast_calloc(1, sizeof(*pu)))) return -1; + /* Lock parking lot */ ast_mutex_lock(&parking_lock); - parking_range = parking_stop - parking_start+1; - for (i = 0; i < parking_range; i++) { - x = (i + parking_offset) % parking_range + parking_start; - cur = parkinglot; - while(cur) { - if (cur->parkingnum == x) + /* 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_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con); + return 0; /* Continue execution if possible */ + } + ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten)); + } 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; + cur = parkinglot; + while(cur) { + if (cur->parkingnum == x) + break; + cur = cur->next; + } + if (!cur) break; - cur = cur->next; } - if (!cur) - break; - } - if (!(i < parking_range)) { - ast_log(LOG_WARNING, "No more parking spaces\n"); - free(pu); - ast_mutex_unlock(&parking_lock); - return -1; + if (!(i < parking_range)) { + ast_log(LOG_WARNING, "No more parking spaces\n"); + free(pu); + ast_mutex_unlock(&parking_lock); + return -1; + } + /* Set pointer for next parking */ + if (parkfindnext) + parking_offset = x - parking_start + 1; } - if (parkfindnext) - parking_offset = x - parking_start + 1; + chan->appl = "Parked Call"; chan->data = NULL; pu->chan = chan; - /* Start music on hold */ + + /* Start music on hold if we have two different channels */ if (chan != peer) { ast_indicate(pu->chan, AST_CONTROL_HOLD); /* Indicate to peer that we're on hold */ ast_moh_start(pu->chan, NULL); @@ -312,6 +368,7 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeou pu->parkingtime = (timeout > 0) ? timeout : parkingtime; if (extout) *extout = x; + if (peer) ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); @@ -322,6 +379,7 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeou pu->priority = chan->macropriority ? chan->macropriority : chan->priority; pu->next = parkinglot; parkinglot = pu; + /* If parking a channel directly, don't quiet yet get parking running on it */ if (peer == chan) pu->notquiteyet = 1; @@ -329,23 +387,25 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeou /* 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. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000)); + 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)); + if (pu->parkingnum != -1) + snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x); manager_event(EVENT_FLAG_CALL, "ParkedCall", - "Exten: %d\r\n" + "Exten: %s\r\n" "Channel: %s\r\n" "From: %s\r\n" "Timeout: %ld\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n", - pu->parkingnum, pu->chan->name, peer ? peer->name : "", + pu->parkingexten, pu->chan->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 && adsi_available(peer)) { - adsi_announce_park(peer, pu->parkingnum); + adsi_announce_park(peer, pu->parkingexten); /* Only supports parking numbers */ adsi_unload_session(peer); } @@ -355,11 +415,11 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeou if (!con) /* Still no context? Bad */ ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); else { /* Add extension to context */ - snprintf(exten, sizeof(exten), "%d", x); - ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), FREE, registrar); + if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), FREE, registrar)) + notify_metermaids(pu->parkingexten, parking_con); } /* Tell the peer channel the number of the parking space */ - if (peer) + if (peer && !pu->parkingnum == -1) /* Only say number if it's a number */ ast_say_digits(peer, pu->parkingnum, "", peer->language); if (pu->notquiteyet) { /* Wake up parking thread if we're really done */ @@ -412,7 +472,7 @@ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int #define FEATURE_SENSE_CHAN (1 << 0) #define FEATURE_SENSE_PEER (1 << 1) -/* +/*! \brief * set caller and callee according to the direction */ static void set_peers(struct ast_channel **caller, struct ast_channel **callee, @@ -427,6 +487,7 @@ static void set_peers(struct ast_channel **caller, struct ast_channel **callee, } } +/*! \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) { struct ast_channel *parker; @@ -471,7 +532,7 @@ static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *pee } if (!monitor_app && !(monitor_app = pbx_findapp("Monitor"))) { - monitor_ok=0; + monitor_ok = 0; ast_log(LOG_ERROR,"Cannot record the call. The monitor application is disabled.\n"); return -1; } @@ -559,6 +620,7 @@ static int finishup(struct ast_channel *chan) return res; } +/*! \brief Find the context for the transfer */ static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee) { const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"); @@ -845,6 +907,7 @@ void ast_unregister_feature(struct ast_call_feature *feature) free(feature); } +/*! \brief Remove all features in the list */ static void ast_unregister_features(void) { struct ast_call_feature *feature; @@ -1412,14 +1475,15 @@ int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast return res; } -static void post_manager_event(const char *s, int num, struct ast_channel *chan) +static void post_manager_event(const char *s, char *parkingexten, struct ast_channel *chan) { manager_event(EVENT_FLAG_CALL, s, - "Exten: %d\r\n" + "Exten: %s\r\n" "Channel: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n\r\n", - num, chan->name, + parkingexten, + chan->name, S_OR(chan->cid.cid_num, ""), S_OR(chan->cid.cid_name, "") ); @@ -1444,7 +1508,7 @@ static void *do_parking_thread(void *ignore) pl = NULL; pu = parkinglot; /* navigate the list with prev-cur pointers to support removals */ - while(pu) { + while (pu) { struct ast_channel *chan = pu->chan; /* shorthand */ int tms; /* timeout for this item */ int x; /* fd index in channel */ @@ -1469,9 +1533,8 @@ static void *do_parking_thread(void *ignore) con = ast_context_find(parking_con_dial); if (!con) { con = ast_context_create(NULL, parking_con_dial, registrar); - if (!con) { + 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]; @@ -1485,7 +1548,7 @@ static void *do_parking_thread(void *ignore) set_c_e_p(chan, pu->context, pu->exten, pu->priority); } - post_manager_event("ParkedCallTimeOut", pu->parkingnum, chan); + post_manager_event("ParkedCallTimeOut", pu->parkingexten, chan); 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); @@ -1503,10 +1566,10 @@ static void *do_parking_thread(void *ignore) pu = pu->next; con = ast_context_find(parking_con); if (con) { - char exten[AST_MAX_EXTENSION]; - snprintf(exten, sizeof(exten), "%d", pt->parkingnum); - if (ast_context_remove_extension2(con, exten, 1, NULL)) + if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL)) ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); + else + notify_metermaids(pu->parkingexten, parking_con); } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); free(pt); @@ -1522,12 +1585,13 @@ static void *do_parking_thread(void *ignore) 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->parkingnum, chan); + post_manager_event("ParkedCallGiveUp", pu->parkingexten, chan); /* There's a problem, hang them up*/ if (option_verbose > 1) @@ -1542,10 +1606,10 @@ static void *do_parking_thread(void *ignore) pu = pu->next; con = ast_context_find(parking_con); if (con) { - char exten[AST_MAX_EXTENSION]; - snprintf(exten, sizeof(exten), "%d", pt->parkingnum); - if (ast_context_remove_extension2(con, exten, 1, NULL)) + 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); @@ -1593,6 +1657,7 @@ std: for (x=0; xexten, "s"); chan->priority = 1; + /* Answer if call is not up */ if (chan->_state != AST_STATE_UP) res = ast_answer(chan); + /* Sleep to allow VoIP streams to settle down */ if (!res) res = ast_safe_sleep(chan, 1000); + /* Park the call */ if (!res) res = ast_park_call(chan, chan, 0, NULL); LOCAL_USER_REMOVE(u); @@ -1616,6 +1684,7 @@ static int park_call_exec(struct ast_channel *chan, void *data) return res; } +/*! \brief Pickup parked call */ static int park_exec(struct ast_channel *chan, void *data) { int res=0; @@ -1627,7 +1696,7 @@ static int park_exec(struct ast_channel *chan, void *data) struct ast_bridge_config config; if (!data) { - ast_log(LOG_WARNING, "Park requires an argument (extension number)\n"); + ast_log(LOG_WARNING, "Parkedcall requires an argument (extension number)\n"); return -1; } LOCAL_USER_ADD(u); @@ -1650,20 +1719,20 @@ static int park_exec(struct ast_channel *chan, void *data) peer = pu->chan; con = ast_context_find(parking_con); if (con) { - char exten[AST_MAX_EXTENSION]; - snprintf(exten, sizeof(exten), "%d", pu->parkingnum); - if (ast_context_remove_extension2(con, exten, 1, NULL)) + 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); } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); manager_event(EVENT_FLAG_CALL, "UnParkedCall", - "Exten: %d\r\n" + "Exten: %s\r\n" "Channel: %s\r\n" "From: %s\r\n" "CallerID: %s\r\n" "CallerIDName: %s\r\n", - pu->parkingnum, pu->chan->name, chan->name, + pu->parkingexten, pu->chan->name, chan->name, S_OR(pu->chan->cid.cid_num, ""), S_OR(pu->chan->cid.cid_name, "") ); @@ -1801,8 +1870,8 @@ static int handle_parkedcalls(int fd, int argc, char *argv[]) ast_mutex_lock(&parking_lock); for (cur = parkinglot; cur; cur = cur->next) { - ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n" - ,cur->parkingnum, cur->chan->name, cur->context, cur->exten + 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++; @@ -1829,7 +1898,7 @@ static int manager_parking_status( struct mansession *s, struct message *m ) char idText[256] = ""; if (!ast_strlen_zero(id)) - snprintf(idText,256,"ActionID: %s\r\n",id); + snprintf(idText, 256, "ActionID: %s\r\n", id); astman_send_ack(s, m, "Parked calls will follow"); @@ -1959,9 +2028,25 @@ int ast_pickup_call(struct ast_channel *chan) return res; } +/*! \brief Add parking hints for all defined parking lots */ +static void park_add_hints(char *context, int start, int stop) +{ + int numext; + char device[AST_MAX_EXTENSION]; + char exten[10]; + + for (numext = start; numext <= stop; numext++) { + snprintf(exten, sizeof(exten), "%d", numext); + snprintf(device, sizeof(device), "park:%s@%s", exten, context); + ast_add_extension(context, 1, exten, PRIORITY_HINT, NULL, NULL, device, NULL, NULL, registrar); + } +} + + static int load_config(void) { int start = 0, end = 0; + int res; struct ast_context *con = NULL; struct ast_config *cfg = NULL; struct ast_variable *var = NULL; @@ -1985,6 +2070,7 @@ static int load_config(void) parking_stop = 750; parkfindnext = 0; adsipark = 0; + parkaddhints = 0; transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; @@ -2012,6 +2098,8 @@ static int load_config(void) } } else if (!strcasecmp(var->name, "findslot")) { parkfindnext = (!strcasecmp(var->value, "next")); + } else if (!strcasecmp(var->name, "parkinghints")) { + parkaddhints = ast_true(var->value); } else if (!strcasecmp(var->name, "adsipark")) { adsipark = ast_true(var->value); } else if (!strcasecmp(var->name, "transferdigittimeout")) { @@ -2131,7 +2219,8 @@ static int load_config(void) /* Remove the old parking extension */ if (!ast_strlen_zero(old_parking_con) && (con = ast_context_find(old_parking_con))) { - ast_context_remove_extension2(con, old_parking_ext, 1, registrar); + 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); } @@ -2140,7 +2229,13 @@ static int load_config(void) ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con); return -1; } - return ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), FREE, registrar); + res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), FREE, registrar); + if (parkaddhints) + park_add_hints(parking_con, parking_start, parking_stop); + if (!res) + notify_metermaids(ast_parking_ext(), parking_con); + return res; + } static int reload(void *mod) @@ -2169,6 +2264,9 @@ static int load_module(void *mod) ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, "Park a channel", mandescr_park); } + + res |= ast_devstate_prov_add("Park", metermaidstate); + return res; } @@ -2182,6 +2280,7 @@ static int unload_module(void *mod) ast_cli_unregister(&showfeatures); ast_cli_unregister(&showparked); ast_unregister_application(parkcall); + ast_devstate_prov_del("Park"); return ast_unregister_application(parkedcall); }