Index: configs/features.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/features.conf.sample,v retrieving revision 1.12 diff -u -r1.12 features.conf.sample --- configs/features.conf.sample 3 Nov 2005 21:53:44 -0000 1.12 +++ configs/features.conf.sample 17 Nov 2005 13:39:39 -0000 @@ -8,6 +8,7 @@ context => parkedcalls ; Which context parked calls are in ;parkingtime => 45 ; Number of seconds a call can be parked for ; (default is 45 seconds) +;parkhints=yes ; Automatically add hints for all parking extensions (default no) ;transferdigittimeout => 3 ; Number of seconds to wait between digits when transfering a call ;courtesytone = beep ; Sound file to play to the parked caller ; when someone dials a parked call Index: res/res_features.c =================================================================== RCS file: /usr/cvsroot/asterisk/res/res_features.c,v retrieving revision 1.82 diff -u -r1.82 res_features.c --- res/res_features.c 10 Nov 2005 23:26:40 -0000 1.82 +++ res/res_features.c 17 Nov 2005 13:39:40 -0000 @@ -72,6 +72,18 @@ #define AST_MAX_WATCHERS 256 +/*! List of meter maids (parking lot watchers) */ +struct features_parkwatch { + void (*callback)(char *exten, char *context); /*! Callback for notification */ + int id; /*! Meter maid ID */ + char *context; /*! Watched context */ + struct features_parkwatch *next; /*! Next in this simple list */ +}; + +/*! Metermaid ID */ +struct features_parkwatch *metermaids; +int metermaidid = 0; + static char *parkedcall = "ParkedCall"; /* No more than 45 seconds parked before you do something with them */ @@ -88,6 +100,8 @@ static char pickup_ext[AST_MAX_EXTENSION]; +static int parkaddhints = 0; + /* Default sounds */ static char courtesytone[256]; static char xfersound[256]; @@ -272,6 +286,87 @@ return adsi_print(chan, message, justify, 1); } +/*! Add parking watcher (metermaid) to list. These will be notified when we create + or remove parking extensions in the dial plan + */ +int ast_park_metermaid_add(void (*maid)(char *exten, char *context), char *context) +{ + struct features_parkwatch *newmaid; + struct features_parkwatch *maids = metermaids; + + newmaid = malloc(sizeof(struct features_parkwatch)); + if (!newmaid) { + ast_log(LOG_ERROR, "Can't allocate parking watcher, out of memory.\n"); + return -1; + } + memset(newmaid, 0, sizeof(struct features_parkwatch)); + + /* Spool till end of list */ + while(maids && maids->next) { + maids = maids->next; + } + + newmaid->callback = maid; + if (context) + newmaid->context = strdup(context); + newmaid->id = metermaidid; + + /* Generate new ID */ + metermaidid++; + + /* Link the new object to the list */ + if (maids) + maids->next = newmaid; + else + metermaids = newmaid; + if (option_debug > 1) + ast_log(LOG_DEBUG, "Added metermaid # %d\n", metermaidid); + return metermaidid; +} + +/*! Remove parking watcher */ +int ast_park_metermaid_remove(int id) +{ + struct features_parkwatch *maids = metermaids; + struct features_parkwatch *prev = NULL; + struct features_parkwatch *kill = NULL; + + while (maids && !kill) { + if (maids->id == id) { + if (prev) { + prev->next = maids->next; + } else { + metermaids = maids->next; + } + kill = maids; + } + prev = maids; + maids = maids->next; + } + if (!kill) + return -1; /* Did not find id */ + if (kill->context) + free(kill->context); + free(kill); + return 0; +} + +/*! Notify metermaids that we've changed an extension */ +static void notify_metermaids(char *exten, char *context) +{ + struct features_parkwatch *maid = metermaids; + if (!maid) + return; + while (maid) { + if (!maid->context || !strcmp(context, maid->context)) + maid->callback(exten, context); + maid = maid->next; + } + if (option_debug > 3) + ast_log(LOG_DEBUG, "Notification of state change to metermaids %s@%s\n", exten, context); + return; +} + /*--- ast_park_call: Park a call */ /* We put the user in the parking list, then wake up the parking thread to be sure it looks after these channels too */ @@ -385,7 +480,9 @@ } if (con) { 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, exten, 1, NULL, NULL, parkedcall, strdup(exten), FREE, registrar) == 0) + notify_metermaids(exten, parking_con); /* Notify watchers */ } if (peer) ast_say_digits(peer, pu->parkingnum, "", peer->language); @@ -1565,6 +1662,8 @@ 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"); + else + notify_metermaids(exten, parking_con); /* Notify watchers */ } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); free(pt); @@ -1607,6 +1706,8 @@ 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"); + else + notify_metermaids(exten, parking_con); } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); free(pt); @@ -1714,6 +1815,8 @@ 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"); + else + notify_metermaids(exten, parking_con); } else ast_log(LOG_WARNING, "Whoa, no parking context?\n"); @@ -1950,9 +2053,22 @@ return res; } +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), "Local/%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; @@ -1975,6 +2091,7 @@ parking_start = 701; parking_stop = 750; parkfindnext = 0; + parkaddhints = 0; adsipark = 0; transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT; @@ -2008,6 +2125,8 @@ } } else if (!strcasecmp(var->name, "findslot")) { parkfindnext = (!strcasecmp(var->value, "next")); + } else if (!strcasecmp(var->name, "parkhints")) { + parkaddhints = ast_true(var->value); } else if (!strcasecmp(var->name, "adsipark")) { adsipark = ast_true(var->value); } else if (!strcasecmp(var->name, "transferdigittimeout")) { @@ -2116,7 +2235,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); ast_log(LOG_DEBUG, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con); } @@ -2126,7 +2246,12 @@ 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; } int reload(void) { Index: channels/chan_local.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_local.c,v retrieving revision 1.57 diff -u -r1.57 chan_local.c --- channels/chan_local.c 8 Nov 2005 20:38:10 -0000 1.57 +++ channels/chan_local.c 17 Nov 2005 13:39:40 -0000 @@ -57,17 +57,21 @@ #include "asterisk/app.h" #include "asterisk/musiconhold.h" #include "asterisk/manager.h" +#include "asterisk/devicestate.h" +#include "asterisk/features.h" static const char desc[] = "Local Proxy Channel"; static const char type[] = "Local"; static const char tdesc[] = "Local Proxy Channel Driver"; +static int watchid; + static int usecnt =0; AST_MUTEX_DEFINE_STATIC(usecnt_lock); #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0) -/* Protect the interface list (of sip_pvt's) */ +/* Protect the interface list (of local_pvt's) */ AST_MUTEX_DEFINE_STATIC(locallock); static struct ast_channel *local_request(const char *type, int format, void *data, int *cause); @@ -80,6 +84,7 @@ static int local_indicate(struct ast_channel *ast, int condition); static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen); +static int local_devicestate(void *data); /* PBX interface structure for channel registration */ static const struct ast_channel_tech local_tech = { @@ -97,6 +102,7 @@ .indicate = local_indicate, .fixup = local_fixup, .send_html = local_sendhtml, + .devicestate = local_devicestate, }; static struct local_pvt { @@ -114,6 +120,37 @@ struct local_pvt *next; /* Next entity */ } *locals = NULL; +/* Watcher callback for extension state changes in res_features */ +void local_watcher(char *exten, char *context) { + if (option_debug > 1) + ast_log(LOG_DEBUG, "Got notification of state change for %s@%s\n", exten, context); + ast_device_state_changed("Local/%s@%s", exten, context); + return; +} + +/*! Adds devicestate to extensions (for parking mostly) +*/ +static int local_devicestate(void *data) +{ + char *exten; + char *context; + + int res = AST_DEVICE_INVALID; + + exten = ast_strdupa(data); + if ((context = strchr(exten, '@'))) { + *context = '\0'; + context = context + 1; + } + if (option_debug > 2) + ast_log(LOG_DEBUG, "Checking if extension %s@%s exists (devicestate)\n", exten, context); + res = ast_exists_extension(NULL, context, exten, 1, NULL); + if (!res) + return AST_DEVICE_NOT_INUSE; + else + return AST_DEVICE_INUSE; +} + static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, struct ast_channel *us) { struct ast_channel *other; @@ -595,6 +632,10 @@ return -1; } ast_cli_register(&cli_show_locals); + + /* Register watcher for parking lots in res_features */ + ast_log(LOG_DEBUG, "Adding metermaid watcher...\n"); + watchid = ast_park_metermaid_add(&local_watcher, NULL); return 0; } @@ -611,6 +652,7 @@ /* First, take us out of the channel loop */ ast_cli_unregister(&cli_show_locals); + ast_park_metermaid_remove(watchid); ast_channel_unregister(&local_tech); if (!ast_mutex_lock(&locallock)) { /* Hangup all interfaces if they have an owner */ Index: include/asterisk/features.h =================================================================== RCS file: /usr/cvsroot/asterisk/include/asterisk/features.h,v retrieving revision 1.7 diff -u -r1.7 features.h --- include/asterisk/features.h 24 Oct 2005 20:12:06 -0000 1.7 +++ include/asterisk/features.h 17 Nov 2005 13:39:40 -0000 @@ -92,4 +92,17 @@ \param feature the ast_call_feature object which was registered before*/ extern void ast_unregister_feature(struct ast_call_feature *feature); + +/*! \brief Add parking watcher (metermaid) to list. These will be notified when we create + or remove parking extensions in the dial plan + + */ +int ast_park_metermaid_add(void (*maid)(char *exten, char *context), char *context); + +/*! Remove parking watcher + \param id Watcher ID returned by ast_park_metermaid_add() + \return -1 on error (ID not found), otherwise 0 +*/ +int ast_park_metermaid_remove(int id); + #endif /* _AST_FEATURES_H */