--- pbx_realtime.orig 2005-12-18 18:38:35.000000000 -0600 +++ pbx_realtime.c 2005-12-19 13:17:10.000000000 -0600 @@ -31,7 +31,7 @@ #include "asterisk.h" -ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7221 $") +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.15 $") #include "asterisk/file.h" #include "asterisk/logger.h" @@ -76,6 +76,20 @@ */ +struct rt_cache { + char context[AST_MAX_EXTENSION + 1]; + char extension[AST_MAX_EXTENSION + 20]; + char include[AST_MAX_EXTENSION + 1]; + struct rt_cache *next; +}; + +struct searched_context { + char context[AST_MAX_EXTENSION + 1]; + struct searched_context *next; +}; + +struct rt_cache *include_cache = NULL; +int rt_cache_count = 0; #define REALTIME_COMMON(mode) \ char *buf; \ @@ -106,15 +120,161 @@ } else \ res = -1; +static void realtime_cache_include(const char *exten, const char *context, const char *include) +{ + struct rt_cache *rtc = NULL; + struct rt_cache *last = NULL; + + rtc = malloc(sizeof(struct rt_cache)); + strncpy(rtc->extension, exten, sizeof(rtc->extension) -1 ); + strncpy(rtc->context, context, sizeof(rtc->context) -1 ); + strncpy(rtc->include, include, sizeof(rtc->include) -1 ); + + if (include_cache) { + /* THIS NEEDS MUTEX PROTECTION! */ + rtc->next = include_cache; + include_cache = rtc; + } else { + include_cache = rtc; + rtc->next = NULL; + } + + /* Maintain no more than 50 objects in the cache */ + if (rt_cache_count >= 50) { + /* Free the last item in the chain */ + while (rtc) { + last = rtc; + rtc = rtc->next; + } + free(last); + } else { + rt_cache_count++; + } + + ast_log(LOG_DEBUG, "Cached. Extension: %s, Search Context: %s -> Found Context: %s.\n", exten, context, include); + ast_log(LOG_DEBUG, "Realtime Cache Count: %d.\n", rt_cache_count); + return; +} + +static int realtime_check_cache(const char *exten, const char *context, char *include) +{ + struct rt_cache *rtc = NULL; + ast_log(LOG_DEBUG, "Checking Cache. Search Extension: %s, Search Context: %s\n", exten, context); + if (include_cache) { + rtc = include_cache; + while (rtc) { + + if ( (!strcasecmp(rtc->extension, exten)) && (!strcasecmp(rtc->context, context)) ) { + strncpy(include, rtc->include, sizeof(rtc->include) -1 ); + return 1; + } else { + rtc = rtc->next; + } + } + } + return 0; +} + +static void realtime_clear_cache(void) +{ + struct rt_cache *rtc; + struct rt_cache *next; + + if (include_cache) { + rtc = include_cache; + while (rtc) { + next = rtc->next; + free(rtc); + rtc = next; + } + } + rt_cache_count = 0; +} + +/* check to see if the context passed is either the root context or a context + which has already been searched. If not, add the name to the list so that + we don't re-search it in the future + */ +static int already_searched(char *context, struct searched_context *list) +{ + struct searched_context *last = NULL; + struct searched_context *nloop = NULL; + while (list) { + if (!strcasecmp(list->context, context)) + return 1; + last = list; + list = list->next; + } + + nloop = malloc(sizeof(struct searched_context)); + memset(nloop, 0, sizeof(struct searched_context)); + strncpy(nloop->context, context, sizeof(nloop->context)); + + last->next = nloop; + nloop->next = NULL; + return 0; +} + +/* delete the search cache */ +static void clear_searched(struct searched_context *nloop) +{ + struct searched_context *list = NULL; + struct searched_context *next = NULL; + list = nloop; + + while (list) { + next = list->next; + free(list); + list = next; + } +} + static struct ast_variable *realtime_switch_common(const char *table, const char *context, const char *exten, int priority, int mode) { struct ast_variable *var; + struct ast_variable *incvar; struct ast_config *cfg; char pri[20]; + char incpri[20]; char *ematch; + char *inccontext = ""; char rexten[AST_MAX_EXTENSION + 20]=""; int match; + int cached=0; + int incpriority = 1; + snprintf(pri, sizeof(pri), "%d", priority); + + static int depth; + static char root_context[AST_MAX_EXTENSION + 20]=""; + static struct searched_context* nloop; + + char include[AST_MAX_EXTENSION + 1]=""; + + /* If this is a non-recursive call to the function, check the cache */ + if (depth == 0) { + /* save the root context name */ + strncpy(root_context, context, sizeof(root_context)); + + /* check the cache - if we already have it this is easy... */ + if (realtime_check_cache(exten, root_context, include)) { + cached = 1; + context = (char *)include; + ast_log(LOG_DEBUG, "Extension found in cached context: %s\n", include); + } else { + /* init the loop prevention mechanism */ + nloop = malloc(sizeof(struct searched_context)); + memset(nloop, 0, sizeof(struct searched_context)); + strncpy(nloop->context, context, sizeof(nloop->context)); + nloop->next = NULL; + } + } + + /* No recursive calls beyond 10 levels */ + if (depth > 9) { + return NULL; + } + switch(mode) { case MODE_MATCHMORE: ematch = "exten LIKE"; @@ -156,6 +316,79 @@ ast_config_destroy(cfg); } } + + if (var) { + if (depth > 0) + realtime_cache_include(exten, root_context, context); + else { + if (nloop) + clear_searched(nloop); + nloop = NULL; + } + return var; + } else { + + /* If we don't have a match, check for realtime includeds. These are handled by + creating an extension which is named 'include'. If we find an include, we + call the search function again with the new context name. + */ + + /* Match only extact extension name */ + ematch = "exten"; + + /* Load the extension name 'include' */ + snprintf(rexten, sizeof(rexten), "%s", "include"); + + /* Set the priority number as a string */ + snprintf(incpri, sizeof(incpri), "%d", incpriority); + + /* Search for a match... */ + incvar = ast_load_realtime(table, ematch, rexten, "context", context, "priority", incpri, NULL); + while (incvar) { + + /* Now find the appdata value which contains the new context to search */ + while (incvar) { + /* Look for the context */ + if (!strcasecmp(incvar->name, "appdata")) { + inccontext = ast_strdupa(incvar->value); + break; + } + incvar = incvar->next; + } + + /* Loop protection! + Check to see if the context is either the root or one we have already + searched. If so, don't try it. + */ + if (!already_searched(inccontext, nloop)) { + + if(!ast_strlen_zero(inccontext)) + ast_log(LOG_DEBUG, "Searching realtime included context: %s\n", inccontext); + + /* Now look for matches in the included context... */ + depth++; + var = realtime_switch_common(table, inccontext, exten, priority, mode); + depth--; + + if (depth == 0) { + if (nloop) + clear_searched(nloop); + nloop = NULL; + } + + if(var) { + return var; + } + } + + /* Increment the priority and try the next match (if any) */ + incpriority++; + snprintf(incpri, sizeof(incpri), "%d", incpriority); + incvar = ast_load_realtime(table, ematch, rexten, "context", context, "priority", incpri, NULL); + + } + } + return var; } @@ -262,6 +495,7 @@ int unload_module(void) { + realtime_clear_cache(); ast_unregister_switch(&realtime_switch); return 0; }