[asterisk-commits] trunk r36055 - in /trunk: ./ apps/ configs/ include/asterisk/ res/

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Mon Jun 26 09:43:22 MST 2006


Author: oej
Date: Mon Jun 26 11:43:21 2006
New Revision: 36055

URL: http://svn.digium.com/view/asterisk?rev=36055&view=rev
Log:
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

Modified:
    trunk/apps/app_meetme.c
    trunk/configs/extensions.conf.sample
    trunk/devicestate.c
    trunk/include/asterisk/devicestate.h
    trunk/pbx.c
    trunk/res/res_features.c

Modified: trunk/apps/app_meetme.c
URL: http://svn.digium.com/view/asterisk/trunk/apps/app_meetme.c?rev=36055&r1=36054&r2=36055&view=diff
==============================================================================
--- trunk/apps/app_meetme.c (original)
+++ trunk/apps/app_meetme.c Mon Jun 26 11:43:21 2006
@@ -58,6 +58,7 @@
 #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 @@
 	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 */
@@ -966,6 +967,10 @@
 	/* Update table */
 	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);
 
@@ -1742,6 +1747,10 @@
 		/* 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 @@
 	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 @@
 	res |= ast_unregister_application(app2);
 	res |= ast_unregister_application(app);
 
+	ast_devstate_prov_del("Meetme");
 	STANDARD_HANGUP_LOCALUSERS;
 
 	return res;
@@ -2565,6 +2598,7 @@
 	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;
 }
 

Modified: trunk/configs/extensions.conf.sample
URL: http://svn.digium.com/view/asterisk/trunk/configs/extensions.conf.sample?rev=36055&r1=36054&r2=36055&view=diff
==============================================================================
--- trunk/configs/extensions.conf.sample (original)
+++ trunk/configs/extensions.conf.sample Mon Jun 26 11:43:21 2006
@@ -278,11 +278,14 @@
 ;
 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 @@
 ;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 at parkedcalls
+;exten => 6600,1,noop
 ;
 ; Some other handy things are an extension for checking voicemail via
 ; voicemailmain

Modified: trunk/devicestate.c
URL: http://svn.digium.com/view/asterisk/trunk/devicestate.c?rev=36055&r1=36054&r2=36055&view=diff
==============================================================================
--- trunk/devicestate.c (original)
+++ trunk/devicestate.c Mon Jun 26 11:43:21 2006
@@ -40,6 +40,7 @@
 #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 @@
 	/* 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 @@
 	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,10 +79,18 @@
 	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 @@
 }
 
 /*! \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_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 @@
 	}
 }
 
+/*! \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 @@
 	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,9 +297,14 @@
 	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 */

Modified: trunk/include/asterisk/devicestate.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/devicestate.h?rev=36055&r1=36054&r2=36055&view=diff
==============================================================================
--- trunk/include/asterisk/devicestate.h (original)
+++ trunk/include/asterisk/devicestate.h Mon Jun 26 11:43:21 2006
@@ -44,7 +44,11 @@
 /*! 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,7 +99,27 @@
  * 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);
 

Modified: trunk/pbx.c
URL: http://svn.digium.com/view/asterisk/trunk/pbx.c?rev=36055&r1=36054&r2=36055&view=diff
==============================================================================
--- trunk/pbx.c (original)
+++ trunk/pbx.c Mon Jun 26 11:43:21 2006
@@ -188,7 +188,7 @@
 
 /*! \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 */

Modified: trunk/res/res_features.c
URL: http://svn.digium.com/view/asterisk/trunk/res/res_features.c?rev=36055&r1=36054&r2=36055&view=diff
==============================================================================
--- trunk/res/res_features.c (original)
+++ trunk/res/res_features.c Mon Jun 26 11:43:21 2006
@@ -56,6 +56,7 @@
 #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 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 @@
 "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 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 @@
 	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 @@
 	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 (parkfindnext) 
-		parking_offset = x - parking_start + 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;
+	}
+	
 	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 @@
 	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 @@
 	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 @@
 	/* 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 @@
 	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 @@
 #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 @@
 	}
 }
 
+/*! \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 @@
 	}
 
 	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 @@
         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 @@
 	free(feature);
 }
 
+/*! \brief Remove all features in the list */
 static void ast_unregister_features(void)
 {
 	struct ast_call_feature *feature;
@@ -1412,14 +1475,15 @@
 	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 @@
 		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 @@
 					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 @@
 					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 @@
 				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 @@
 					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 @@
 						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 @@
 	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 @@
 	   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 @@
 	return res;
 }
 
+/*! \brief Pickup parked call */
 static int park_exec(struct ast_channel *chan, void *data)
 {
 	int res=0;
@@ -1627,7 +1696,7 @@
 	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 @@
 		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 @@
 	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 @@
 	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 @@
 	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 @@
 	parking_stop = 750;
 	parkfindnext = 0;
 	adsipark = 0;
+	parkaddhints = 0;
 
 	transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
 	featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
@@ -2012,6 +2098,8 @@
 				}
 			} 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 @@
 
 	/* 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 @@
 		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 @@
 		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 @@
 	ast_cli_unregister(&showfeatures);
 	ast_cli_unregister(&showparked);
 	ast_unregister_application(parkcall);
+	ast_devstate_prov_del("Park");
 	return ast_unregister_application(parkedcall);
 }
 



More information about the asterisk-commits mailing list