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/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;
}

@ -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

@ -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 */

@ -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)

@ -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 */

@ -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, "<unknown>"),
S_OR(pu->chan->cid.cid_name, "<unknown>")
);
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, "<unknown>"),
S_OR(chan->cid.cid_name, "<unknown>")
);
@ -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; x<AST_MAX_FDS; x++) { /* mark fds for next round */
return NULL; /* Never reached */
}
/*! \brief Park a call */
static int park_call_exec(struct ast_channel *chan, void *data)
{
/* 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 */
strcpy(chan->exten, "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, "<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);
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);
}

Loading…
Cancel
Save