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
1.4
Olle Johansson 19 years ago
parent e2b0c5b558
commit f8311adcda

@ -58,6 +58,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/translate.h" #include "asterisk/translate.h"
#include "asterisk/ulaw.h" #include "asterisk/ulaw.h"
#include "asterisk/devicestate.h"
#include "enter.h" #include "enter.h"
#include "leave.h" #include "leave.h"
@ -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); snprintf(members, sizeof(members), "%d", conf->users);
ast_update_realtime("meetme", "confno", conf->confno, "members", members , NULL); 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); ast_mutex_unlock(&conf->playlock);
if (confflags & CONFFLAG_EXIT_CONTEXT) { if (confflags & CONFFLAG_EXIT_CONTEXT) {
@ -1742,6 +1747,10 @@ bailoutandtrynormal:
/* Return the number of seconds the user was in the conf */ /* Return the number of seconds the user was in the conf */
snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime)); snprintf(meetmesecs, sizeof(meetmesecs), "%d", (int) (time(NULL) - user->jointime));
pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); 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); free(user);
AST_LIST_UNLOCK(&confs); AST_LIST_UNLOCK(&confs);
@ -2510,6 +2519,29 @@ static void *recordthread(void *args)
pthread_exit(0); 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) static void load_config(void)
{ {
struct ast_config *cfg; struct ast_config *cfg;
@ -2547,6 +2579,7 @@ static int unload_module(void *mod)
res |= ast_unregister_application(app2); res |= ast_unregister_application(app2);
res |= ast_unregister_application(app); res |= ast_unregister_application(app);
ast_devstate_prov_del("Meetme");
STANDARD_HANGUP_LOCALUSERS; STANDARD_HANGUP_LOCALUSERS;
return res; 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(app2, count_exec, synopsis2, descrip2);
res |= ast_register_application(app, conf_exec, synopsis, descrip); res |= ast_register_application(app, conf_exec, synopsis, descrip);
res |= ast_devstate_prov_add("Meetme", meetmestate);
return res; return res;
} }

@ -278,11 +278,14 @@ include => trunkld
; ;
ignorepat => 9 ignorepat => 9
include => default include => default
include => parkedcalls
include => trunklocal include => trunklocal
include => iaxtel700 include => iaxtel700
include => trunktollfree include => trunktollfree
include => iaxprovider 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 ; You can use an alternative switch type as well, to resolve
; extensions that are not known here, for example with remote ; 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 => mark,1,Goto(6275|1) ; alias mark to 6275
;exten => 6536,1,Macro(stdexten,6236,${WIL}) ; Ditto for wil ;exten => 6536,1,Macro(stdexten,6236,${WIL}) ; Ditto for wil
;exten => wil,1,Goto(6236|1) ;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 ; Some other handy things are an extension for checking voicemail via
; voicemailmain ; voicemailmain

@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/logger.h" #include "asterisk/logger.h"
#include "asterisk/devicestate.h" #include "asterisk/devicestate.h"
#include "asterisk/pbx.h" #include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/options.h" #include "asterisk/options.h"
/*! \brief Device state strings for printing */ /*! \brief Device state strings for printing */
@ -53,6 +54,16 @@ static const char *devstatestring[] = {
/* 6 AST_DEVICE_RINGING */ "Ringing" /*!< Ring, ring, ring */ /* 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) */ /*! \brief A device state watcher (callback) */
struct devstate_cb { struct devstate_cb {
void *data; void *data;
@ -60,6 +71,7 @@ struct devstate_cb {
AST_LIST_ENTRY(devstate_cb) list; AST_LIST_ENTRY(devstate_cb) list;
}; };
/*! \brief A device state watcher list */
static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb); static AST_LIST_HEAD_STATIC(devstate_cbs, devstate_cb);
struct state_change { struct state_change {
@ -67,11 +79,19 @@ struct state_change {
char device[1]; 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); static AST_LIST_HEAD_STATIC(state_changes, state_change);
/*! \brief The device state change notification thread */
static pthread_t change_thread = AST_PTHREADT_NULL; static pthread_t change_thread = AST_PTHREADT_NULL;
/*! \brief Flag for the queue */
static ast_cond_t change_pending; 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 */ /*! \brief Find devicestate as text message for output */
const char *devstate2str(int devstate) const char *devstate2str(int devstate)
{ {
@ -110,16 +130,33 @@ int ast_parse_device_state(const char *device)
int ast_device_state(const char *device) int ast_device_state(const char *device)
{ {
char *buf; char *buf;
char *tech;
char *number; char *number;
const struct ast_channel_tech *chan_tech; const struct ast_channel_tech *chan_tech;
int res = 0; 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); buf = ast_strdupa(device);
tech = strsep(&buf, "/"); tech = strsep(&buf, "/");
number = buf; number = buf;
if (!number) if (!number) {
provider = strsep(&tech, ":");
if (!provider)
return AST_DEVICE_INVALID; 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); chan_tech = ast_get_channel_tech(tech);
if (!chan_tech) if (!chan_tech)
@ -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 */ /*! \brief Add device state watcher */
int ast_devstate_add(ast_devstate_cb_type callback, void *data) 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); 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) static void do_state_change(const char *device)
{ {
int state; int state;
@ -201,10 +297,15 @@ static int __ast_device_state_changed_literal(char *buf)
char *device, *tmp; char *device, *tmp;
struct state_change *change; 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; device = buf;
if ((tmp = strrchr(device, '-'))) if ((tmp = strrchr(device, '-')))
*tmp = '\0'; *tmp = '\0';
if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) { if (change_thread == AST_PTHREADT_NULL || !(change = ast_calloc(1, sizeof(*change) + strlen(device)))) {
/* we could not allocate a change struct, or */ /* we could not allocate a change struct, or */
/* there is no background thread, so process the change now */ /* there is no background thread, so process the change now */

@ -44,8 +44,12 @@ extern "C" {
/*! Device is ringing *and* in use */ /*! Device is ringing *and* in use */
#define AST_DEVICE_RINGINUSE 7 #define AST_DEVICE_RINGINUSE 7
/*! \brief Devicestate watcher call back */
typedef int (*ast_devstate_cb_type)(const char *dev, int state, void *data); 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 /*! \brief Convert device state to text string for output
* \param devstate Current device state * \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 * Return -1 on failure, ID on success
*/ */
int ast_devstate_add(ast_devstate_cb_type callback, void *data); 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); 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); int ast_device_state_engine_init(void);
#if defined(__cplusplus) || defined(c_plusplus) #if defined(__cplusplus) || defined(c_plusplus)

@ -188,7 +188,7 @@ struct ast_state_cb {
/*! \brief Structure for dial plan hints /*! \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) */ more devices (tech/name) */
struct ast_hint { struct ast_hint {
struct ast_exten *exten; /*!< Extension */ struct ast_exten *exten; /*!< Extension */

@ -56,6 +56,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/manager.h" #include "asterisk/manager.h"
#include "asterisk/utils.h" #include "asterisk/utils.h"
#include "asterisk/adsi.h" #include "asterisk/adsi.h"
#include "asterisk/devicestate.h"
#include "asterisk/monitor.h" #include "asterisk/monitor.h"
#ifdef __AST_DEBUG_MALLOC #ifdef __AST_DEBUG_MALLOC
@ -76,6 +77,7 @@ static void FREE(void *ptr)
static char *parkedcall = "ParkedCall"; 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 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[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_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" "transfer to know the parking space). This application is always\n"
"registered internally and does not need to be explicitly added\n" "registered internally and does not need to be explicitly added\n"
"into the dialplan, although you should include the 'parkedcalls'\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 struct ast_app *monitor_app = NULL;
static int monitor_ok = 1; static int monitor_ok = 1;
@ -128,6 +134,7 @@ struct parkeduser {
struct ast_channel *chan; /*!< Parking channel */ struct ast_channel *chan; /*!< Parking channel */
struct timeval start; /*!< Time the parking started */ struct timeval start; /*!< Time the parking started */
int parkingnum; /*!< Parking lot */ 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 context[AST_MAX_CONTEXT]; /*!< Where to go if our parking time expires */
char exten[AST_MAX_EXTENSION]; char exten[AST_MAX_EXTENSION];
int priority; int priority;
@ -248,14 +255,14 @@ static void ast_bridge_call_thread_launch(void *data)
pthread_setschedparam(thread, SCHED_RR, &sched); 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 res;
int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT}; int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
char tmp[256]; char tmp[256];
char *message[5] = {NULL, NULL, NULL, NULL, NULL}; 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; message[0] = tmp;
res = adsi_load_session(chan, NULL, 0, 1); res = adsi_load_session(chan, NULL, 0, 1);
if (res == -1) if (res == -1)
@ -263,20 +270,65 @@ static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
return adsi_print(chan, message, justify, 1); 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 /*! \brief Park a call
\note We put the user in the parking list, then wake up the parking thread to be sure it looks \note We put the user in the parking list, then wake up the parking thread to be sure it looks
after these channels too */ after these channels too */
int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout) int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
{ {
struct parkeduser *pu, *cur; struct parkeduser *pu, *cur;
int i,x,parking_range; int i, x = -1, parking_range;
char exten[AST_MAX_EXTENSION];
struct ast_context *con; struct ast_context *con;
const char *parkingexten;
/* Allocate memory for parking data */
if (!(pu = ast_calloc(1, sizeof(*pu)))) if (!(pu = ast_calloc(1, sizeof(*pu))))
return -1; return -1;
/* Lock parking lot */
ast_mutex_lock(&parking_lock); 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_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; parking_range = parking_stop - parking_start+1;
for (i = 0; i < parking_range; i++) { for (i = 0; i < parking_range; i++) {
x = (i + parking_offset) % parking_range + parking_start; x = (i + parking_offset) % parking_range + parking_start;
@ -296,13 +348,17 @@ int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeou
ast_mutex_unlock(&parking_lock); ast_mutex_unlock(&parking_lock);
return -1; return -1;
} }
/* Set pointer for next parking */
if (parkfindnext) if (parkfindnext)
parking_offset = x - parking_start + 1; parking_offset = x - parking_start + 1;
}
chan->appl = "Parked Call"; chan->appl = "Parked Call";
chan->data = NULL; chan->data = NULL;
pu->chan = chan; pu->chan = chan;
/* Start music on hold */
/* Start music on hold if we have two different channels */
if (chan != peer) { if (chan != peer) {
ast_indicate(pu->chan, AST_CONTROL_HOLD); /* Indicate to peer that we're on hold */ ast_indicate(pu->chan, AST_CONTROL_HOLD); /* Indicate to peer that we're on hold */
ast_moh_start(pu->chan, NULL); 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; pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
if (extout) if (extout)
*extout = x; *extout = x;
if (peer) if (peer)
ast_copy_string(pu->peername, peer->name, sizeof(pu->peername)); 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->priority = chan->macropriority ? chan->macropriority : chan->priority;
pu->next = parkinglot; pu->next = parkinglot;
parkinglot = pu; parkinglot = pu;
/* If parking a channel directly, don't quiet yet get parking running on it */ /* If parking a channel directly, don't quiet yet get parking running on it */
if (peer == chan) if (peer == chan)
pu->notquiteyet = 1; 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 */ /* Wake up the (presumably select()ing) thread */
pthread_kill(parking_thread, SIGURG); pthread_kill(parking_thread, SIGURG);
if (option_verbose > 1) 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", manager_event(EVENT_FLAG_CALL, "ParkedCall",
"Exten: %d\r\n" "Exten: %s\r\n"
"Channel: %s\r\n" "Channel: %s\r\n"
"From: %s\r\n" "From: %s\r\n"
"Timeout: %ld\r\n" "Timeout: %ld\r\n"
"CallerID: %s\r\n" "CallerID: %s\r\n"
"CallerIDName: %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), (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
S_OR(pu->chan->cid.cid_num, "<unknown>"), S_OR(pu->chan->cid.cid_num, "<unknown>"),
S_OR(pu->chan->cid.cid_name, "<unknown>") S_OR(pu->chan->cid.cid_name, "<unknown>")
); );
if (peer && adsipark && adsi_available(peer)) { 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); 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 */ 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", parking_con);
else { /* Add extension to context */ else { /* Add extension to context */
snprintf(exten, sizeof(exten), "%d", x); if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, strdup(pu->parkingexten), FREE, registrar))
ast_add_extension2(con, 1, exten, 1, NULL, NULL, parkedcall, strdup(exten), FREE, registrar); notify_metermaids(pu->parkingexten, parking_con);
} }
/* Tell the peer channel the number of the parking space */ /* 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); ast_say_digits(peer, pu->parkingnum, "", peer->language);
if (pu->notquiteyet) { if (pu->notquiteyet) {
/* Wake up parking thread if we're really done */ /* 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_CHAN (1 << 0)
#define FEATURE_SENSE_PEER (1 << 1) #define FEATURE_SENSE_PEER (1 << 1)
/* /*! \brief
* set caller and callee according to the direction * set caller and callee according to the direction
*/ */
static void set_peers(struct ast_channel **caller, struct ast_channel **callee, 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) 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; struct ast_channel *parker;
@ -559,6 +620,7 @@ static int finishup(struct ast_channel *chan)
return res; return res;
} }
/*! \brief Find the context for the transfer */
static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee) static const char *real_ctx(struct ast_channel *transferer, struct ast_channel *transferee)
{ {
const char *s = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"); 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); free(feature);
} }
/*! \brief Remove all features in the list */
static void ast_unregister_features(void) static void ast_unregister_features(void)
{ {
struct ast_call_feature *feature; 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; 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, manager_event(EVENT_FLAG_CALL, s,
"Exten: %d\r\n" "Exten: %s\r\n"
"Channel: %s\r\n" "Channel: %s\r\n"
"CallerID: %s\r\n" "CallerID: %s\r\n"
"CallerIDName: %s\r\n\r\n", "CallerIDName: %s\r\n\r\n",
num, chan->name, parkingexten,
chan->name,
S_OR(chan->cid.cid_num, "<unknown>"), S_OR(chan->cid.cid_num, "<unknown>"),
S_OR(chan->cid.cid_name, "<unknown>") S_OR(chan->cid.cid_name, "<unknown>")
); );
@ -1469,10 +1533,9 @@ static void *do_parking_thread(void *ignore)
con = ast_context_find(parking_con_dial); con = ast_context_find(parking_con_dial);
if (!con) { if (!con) {
con = ast_context_create(NULL, parking_con_dial, registrar); 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); ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
} }
}
if (con) { if (con) {
char returnexten[AST_MAX_EXTENSION]; char returnexten[AST_MAX_EXTENSION];
snprintf(returnexten, sizeof(returnexten), "%s||t", peername); snprintf(returnexten, sizeof(returnexten), "%s||t", peername);
@ -1485,7 +1548,7 @@ static void *do_parking_thread(void *ignore)
set_c_e_p(chan, pu->context, pu->exten, pu->priority); 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) 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); 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; pu = pu->next;
con = ast_context_find(parking_con); con = ast_context_find(parking_con);
if (con) { if (con) {
char exten[AST_MAX_EXTENSION]; if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
if (ast_context_remove_extension2(con, exten, 1, NULL))
ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
else
notify_metermaids(pu->parkingexten, parking_con);
} else } else
ast_log(LOG_WARNING, "Whoa, no parking context?\n"); ast_log(LOG_WARNING, "Whoa, no parking context?\n");
free(pt); free(pt);
@ -1522,12 +1585,13 @@ static void *do_parking_thread(void *ignore)
else else
ast_clear_flag(chan, AST_FLAG_EXCEPTION); ast_clear_flag(chan, AST_FLAG_EXCEPTION);
chan->fdno = x; chan->fdno = x;
/* See if they need servicing */ /* See if they need servicing */
f = ast_read(chan); f = ast_read(chan);
if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) { if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP)) {
if (f) if (f)
ast_frfree(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*/ /* There's a problem, hang them up*/
if (option_verbose > 1) if (option_verbose > 1)
@ -1542,10 +1606,10 @@ static void *do_parking_thread(void *ignore)
pu = pu->next; pu = pu->next;
con = ast_context_find(parking_con); con = ast_context_find(parking_con);
if (con) { if (con) {
char exten[AST_MAX_EXTENSION]; if (ast_context_remove_extension2(con, pt->parkingexten, 1, NULL))
snprintf(exten, sizeof(exten), "%d", pt->parkingnum);
if (ast_context_remove_extension2(con, exten, 1, NULL))
ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
else
notify_metermaids(pt->parkingexten, parking_con);
} else } else
ast_log(LOG_WARNING, "Whoa, no parking context?\n"); ast_log(LOG_WARNING, "Whoa, no parking context?\n");
free(pt); free(pt);
@ -1593,6 +1657,7 @@ std: for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
return NULL; /* Never reached */ return NULL; /* Never reached */
} }
/*! \brief Park a call */
static int park_call_exec(struct ast_channel *chan, void *data) static int park_call_exec(struct ast_channel *chan, void *data)
{ {
/* Data is unused at the moment but could contain a parking /* Data is unused at the moment but could contain a parking
@ -1604,10 +1669,13 @@ static int park_call_exec(struct ast_channel *chan, void *data)
where this call should return */ where this call should return */
strcpy(chan->exten, "s"); strcpy(chan->exten, "s");
chan->priority = 1; chan->priority = 1;
/* Answer if call is not up */
if (chan->_state != AST_STATE_UP) if (chan->_state != AST_STATE_UP)
res = ast_answer(chan); res = ast_answer(chan);
/* Sleep to allow VoIP streams to settle down */
if (!res) if (!res)
res = ast_safe_sleep(chan, 1000); res = ast_safe_sleep(chan, 1000);
/* Park the call */
if (!res) if (!res)
res = ast_park_call(chan, chan, 0, NULL); res = ast_park_call(chan, chan, 0, NULL);
LOCAL_USER_REMOVE(u); LOCAL_USER_REMOVE(u);
@ -1616,6 +1684,7 @@ static int park_call_exec(struct ast_channel *chan, void *data)
return res; return res;
} }
/*! \brief Pickup parked call */
static int park_exec(struct ast_channel *chan, void *data) static int park_exec(struct ast_channel *chan, void *data)
{ {
int res=0; int res=0;
@ -1627,7 +1696,7 @@ static int park_exec(struct ast_channel *chan, void *data)
struct ast_bridge_config config; struct ast_bridge_config config;
if (!data) { 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; return -1;
} }
LOCAL_USER_ADD(u); LOCAL_USER_ADD(u);
@ -1650,20 +1719,20 @@ static int park_exec(struct ast_channel *chan, void *data)
peer = pu->chan; peer = pu->chan;
con = ast_context_find(parking_con); con = ast_context_find(parking_con);
if (con) { if (con) {
char exten[AST_MAX_EXTENSION]; if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
snprintf(exten, sizeof(exten), "%d", pu->parkingnum);
if (ast_context_remove_extension2(con, exten, 1, NULL))
ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n"); ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
else
notify_metermaids(pu->parkingexten, parking_con);
} else } else
ast_log(LOG_WARNING, "Whoa, no parking context?\n"); ast_log(LOG_WARNING, "Whoa, no parking context?\n");
manager_event(EVENT_FLAG_CALL, "UnParkedCall", manager_event(EVENT_FLAG_CALL, "UnParkedCall",
"Exten: %d\r\n" "Exten: %s\r\n"
"Channel: %s\r\n" "Channel: %s\r\n"
"From: %s\r\n" "From: %s\r\n"
"CallerID: %s\r\n" "CallerID: %s\r\n"
"CallerIDName: %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, "<unknown>"), S_OR(pu->chan->cid.cid_num, "<unknown>"),
S_OR(pu->chan->cid.cid_name, "<unknown>") S_OR(pu->chan->cid.cid_name, "<unknown>")
); );
@ -1801,8 +1870,8 @@ static int handle_parkedcalls(int fd, int argc, char *argv[])
ast_mutex_lock(&parking_lock); ast_mutex_lock(&parking_lock);
for (cur = parkinglot; cur; cur = cur->next) { for (cur = parkinglot; cur; cur = cur->next) {
ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6lds\n" ast_cli(fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
,cur->parkingnum, cur->chan->name, cur->context, cur->exten ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL)); ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
numparked++; numparked++;
@ -1959,9 +2028,25 @@ int ast_pickup_call(struct ast_channel *chan)
return res; 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) static int load_config(void)
{ {
int start = 0, end = 0; int start = 0, end = 0;
int res;
struct ast_context *con = NULL; struct ast_context *con = NULL;
struct ast_config *cfg = NULL; struct ast_config *cfg = NULL;
struct ast_variable *var = NULL; struct ast_variable *var = NULL;
@ -1985,6 +2070,7 @@ static int load_config(void)
parking_stop = 750; parking_stop = 750;
parkfindnext = 0; parkfindnext = 0;
adsipark = 0; adsipark = 0;
parkaddhints = 0;
transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT; featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
@ -2012,6 +2098,8 @@ static int load_config(void)
} }
} else if (!strcasecmp(var->name, "findslot")) { } else if (!strcasecmp(var->name, "findslot")) {
parkfindnext = (!strcasecmp(var->value, "next")); parkfindnext = (!strcasecmp(var->value, "next"));
} else if (!strcasecmp(var->name, "parkinghints")) {
parkaddhints = ast_true(var->value);
} else if (!strcasecmp(var->name, "adsipark")) { } else if (!strcasecmp(var->name, "adsipark")) {
adsipark = ast_true(var->value); adsipark = ast_true(var->value);
} else if (!strcasecmp(var->name, "transferdigittimeout")) { } else if (!strcasecmp(var->name, "transferdigittimeout")) {
@ -2131,7 +2219,8 @@ static int load_config(void)
/* Remove the old parking extension */ /* 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))) {
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) if (option_debug)
ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); 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); ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
return -1; 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) static int reload(void *mod)
@ -2169,6 +2264,9 @@ static int load_module(void *mod)
ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
"Park a channel", mandescr_park); "Park a channel", mandescr_park);
} }
res |= ast_devstate_prov_add("Park", metermaidstate);
return res; return res;
} }
@ -2182,6 +2280,7 @@ static int unload_module(void *mod)
ast_cli_unregister(&showfeatures); ast_cli_unregister(&showfeatures);
ast_cli_unregister(&showparked); ast_cli_unregister(&showparked);
ast_unregister_application(parkcall); ast_unregister_application(parkcall);
ast_devstate_prov_del("Park");
return ast_unregister_application(parkedcall); return ast_unregister_application(parkedcall);
} }

Loading…
Cancel
Save