Newer
Older
do_exec = 0;
if (do_exec && !ast_opt_exec_includes) {
ast_log(LOG_WARNING, "Cannot perform #exec unless execincludes option is enabled in asterisk.conf (options section)!\n");
do_exec = 0;
}
if (do_include || do_exec) {
if (c) {
Steve Murphy
committed
char *cur2;
/* Strip off leading and trailing "'s and <>'s */
while((*c == '<') || (*c == '>') || (*c == '\"')) c++;
/* Get rid of leading mess */
cur = c;
Steve Murphy
committed
cur2 = cur;
while (!ast_strlen_zero(cur)) {
c = cur + strlen(cur) - 1;
if ((*c == '>') || (*c == '<') || (*c == '\"'))
*c = '\0';
else
break;
}
/* #exec </path/to/executable>
We create a tmp file, then we #include it, then we delete it. */
snprintf(exec_file, sizeof(exec_file), "/var/tmp/exec.%d.%ld", (int)time(NULL), (long)pthread_self());
if (snprintf(cmd, sizeof(cmd), "%s > %s 2>&1", cur, exec_file) >= sizeof(cmd)) {
ast_log(LOG_ERROR, "Failed to construct command string to execute %s.\n", cur);
return -1;
}
ast_safe_system(cmd);
cur = exec_file;
} else
exec_file[0] = '\0';
/* A #include */
/* ast_log(LOG_WARNING, "Reading in included file %s withcomments=%d\n", cur, withcomments); */
Steve Murphy
committed
/* record this inclusion */
ast_include_new(cfg, configfile, cur, do_exec, cur2, lineno, real_inclusion_name, sizeof(real_inclusion_name));
Steve Murphy
committed
do_include = ast_config_internal_load(cur, cfg, withcomments, real_inclusion_name) ? 1 : 0;
if(!ast_strlen_zero(exec_file))
unlink(exec_file);
if(!do_include)
return 0;
/* ast_log(LOG_WARNING, "Done reading in included file %s withcomments=%d\n", cur, withcomments); */
} else {
ast_log(LOG_WARNING, "Directive '#%s' needs an argument (%s) at line %d of %s\n",
do_exec ? "exec" : "include",
do_exec ? "/path/to/executable" : "filename",
lineno,
configfile);
}
}
ast_log(LOG_WARNING, "Unknown directive '%s' at line %d of %s\n", cur, lineno, configfile);
} else {
/* Just a line (variable = value) */
if (!*cat) {
ast_log(LOG_WARNING,
"parse error: No category context for line %d of %s\n", lineno, configfile);
return -1;
}
c = strchr(cur, '=');
if (c) {
*c = 0;
c++;
/* Ignore > in => */
if (*c== '>') {
object = 1;
c++;
} else
object = 0;
Steve Murphy
committed
if ((v = ast_variable_new(ast_strip(cur), ast_strip(c), configfile))) {
v->lineno = lineno;
v->object = object;
/* Put and reset comments */
v->blanklines = 0;
ast_variable_append(*cat, v);
/* add comments */
if (withcomments && comment_buffer && comment_buffer[0] ) {
v->precomments = ALLOC_COMMENT(comment_buffer);
}
if (withcomments && lline_buffer && lline_buffer[0] ) {
v->sameline = ALLOC_COMMENT(lline_buffer);
}
if( withcomments )
CB_RESET();
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
} else {
return -1;
}
} else {
ast_log(LOG_WARNING, "EXTENSIONS.CONF: No '=' (equal sign) in line %d of %s\n", lineno, configfile);
}
}
return 0;
}
static int use_local_dir = 1;
void localized_use_local_dir(void);
void localized_use_conf_dir(void);
void localized_use_local_dir(void)
{
use_local_dir = 1;
}
void localized_use_conf_dir(void)
{
use_local_dir = 0;
}
Steve Murphy
committed
static struct ast_config *config_text_file_load(const char *database, const char *table, const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_include_file)
{
char fn[256];
char buf[8192];
char *new_buf, *comment_p, *process_buf;
FILE *f;
int lineno=0;
int comment = 0, nest[MAX_NESTED_COMMENTS];
struct ast_category *cat = NULL;
int count = 0;
struct stat statbuf;
cat = ast_config_get_current_category(cfg);
if (filename[0] == '/') {
ast_copy_string(fn, filename, sizeof(fn));
} else {
if (use_local_dir)
snprintf(fn, sizeof(fn), "./%s", filename);
else
snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
}
if (withcomments && cfg && cfg->include_level < 2 ) {
CB_INIT();
}
do {
if (stat(fn, &statbuf))
continue;
if (!S_ISREG(statbuf.st_mode)) {
ast_log(LOG_WARNING, "'%s' is not a regular file, ignoring\n", fn);
continue;
}
if (option_verbose > 1) {
ast_verbose(VERBOSE_PREFIX_2 "Parsing '%s': ", fn);
fflush(stdout);
}
if (!(f = fopen(fn, "r"))) {
if (option_debug)
ast_log(LOG_DEBUG, "No file to parse: %s\n", fn);
if (option_verbose > 1)
ast_verbose( "Not found (%s)\n", strerror(errno));
continue;
}
count++;
if (option_debug)
ast_log(LOG_DEBUG, "Parsing %s\n", fn);
if (option_verbose > 1)
ast_verbose("Found\n");
while(!feof(f)) {
lineno++;
if (fgets(buf, sizeof(buf), f)) {
CB_ADD(lline_buffer); /* add the current lline buffer to the comment buffer */
lline_buffer[0] = 0; /* erase the lline buffer */
}
new_buf = buf;
process_buf = NULL;
else
process_buf = buf;
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
while ((comment_p = strchr(new_buf, COMMENT_META))) {
if ((comment_p > new_buf) && (*(comment_p-1) == '\\')) {
/* Yuck, gotta memmove */
memmove(comment_p - 1, comment_p, strlen(comment_p) + 1);
new_buf = comment_p;
} else if(comment_p[1] == COMMENT_TAG && comment_p[2] == COMMENT_TAG && (comment_p[3] != '-')) {
/* Meta-Comment start detected ";--" */
if (comment < MAX_NESTED_COMMENTS) {
*comment_p = '\0';
new_buf = comment_p + 3;
comment++;
nest[comment-1] = lineno;
} else {
ast_log(LOG_ERROR, "Maximum nest limit of %d reached.\n", MAX_NESTED_COMMENTS);
}
} else if ((comment_p >= new_buf + 2) &&
(*(comment_p - 1) == COMMENT_TAG) &&
(*(comment_p - 2) == COMMENT_TAG)) {
/* Meta-Comment end detected */
comment--;
new_buf = comment_p + 1;
if (!comment) {
/* Back to non-comment now */
if (process_buf) {
/* Actually have to move what's left over the top, then continue */
char *oldptr;
oldptr = process_buf + strlen(process_buf);
if ( withcomments ) {
CB_ADD(";");
CB_ADD_LEN(oldptr+1,new_buf-oldptr-1);
}
memmove(oldptr, new_buf, strlen(new_buf) + 1);
new_buf = oldptr;
} else
process_buf = new_buf;
}
} else {
if (!comment) {
/* If ; is found, and we are not nested in a comment,
we immediately stop all comment processing */
if ( withcomments ) {
LLB_ADD(comment_p);
}
new_buf = comment_p;
} else
new_buf = comment_p + 1;
}
}
if( withcomments && comment && !process_buf )
{
CB_ADD(buf); /* the whole line is a comment, store it */
}
if (process_buf) {
char *stripped_process_buf = ast_strip(process_buf);
if (!ast_strlen_zero(stripped_process_buf)) {
if (process_text_line(cfg, &cat, stripped_process_buf, lineno, filename, withcomments, suggested_include_file)) {
cfg = NULL;
break;
}
}
}
}
}
} while(0);
if (comment) {
ast_log(LOG_WARNING,"Unterminated comment detected beginning on line %d\n", nest[comment]);
}
if (cfg && cfg->include_level == 1 && withcomments && comment_buffer) {
free(comment_buffer);
free(lline_buffer);
comment_buffer=0;
lline_buffer=0;
comment_buffer_size=0;
lline_buffer_size=0;
}
}
if (count == 0)
return NULL;
return cfg;
}
static struct ast_config *ast_config_new(void) ;
static struct ast_config *ast_config_new(void)
{
struct ast_config *config;
if ((config = ast_calloc(1, sizeof(*config))))
config->max_include_level = MAX_INCLUDE_LEVEL;
return config;
}
struct ast_config *localized_config_load(const char *filename);
struct ast_config *localized_config_load(const char *filename)
{
struct ast_config *cfg;
struct ast_config *result;
cfg = ast_config_new();
if (!cfg)
return NULL;
Steve Murphy
committed
result = ast_config_internal_load(filename, cfg, 0, "");
if (!result)
ast_config_destroy(cfg);
return result;
}
struct ast_config *localized_config_load_with_comments(const char *filename);
struct ast_config *localized_config_load_with_comments(const char *filename)
{
struct ast_config *cfg;
struct ast_config *result;
cfg = ast_config_new();
if (!cfg)
return NULL;
Steve Murphy
committed
result = ast_config_internal_load(filename, cfg, 1, "");
if (!result)
ast_config_destroy(cfg);
return result;
}
static struct ast_category *next_available_category(struct ast_category *cat)
{
for (; cat && cat->ignored; cat = cat->next);
return cat;
}
static char *ast_category_browse(struct ast_config *config, const char *prev)
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
struct ast_category *cat = NULL;
if (prev && config->last_browse && (config->last_browse->name == prev))
cat = config->last_browse->next;
else if (!prev && config->root)
cat = config->root;
else if (prev) {
for (cat = config->root; cat; cat = cat->next) {
if (cat->name == prev) {
cat = cat->next;
break;
}
}
if (!cat) {
for (cat = config->root; cat; cat = cat->next) {
if (!strcasecmp(cat->name, prev)) {
cat = cat->next;
break;
}
}
}
}
if (cat)
cat = next_available_category(cat);
config->last_browse = cat;
return (cat) ? cat->name : NULL;
}
void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat);
void ast_config_set_current_category(struct ast_config *cfg, const struct ast_category *cat)
{
/* cast below is just to silence compiler warning about dropping "const" */
cfg->current = (struct ast_category *) cat;
}
Steve Murphy
committed
/* NOTE: categories and variables each have a file and lineno attribute. On a save operation, these are used to determine
which file and line number to write out to. Thus, an entire hierarchy of config files (via #include statements) can be
recreated. BUT, care must be taken to make sure that every cat and var has the proper file name stored, or you may
be shocked and mystified as to why things are not showing up in the files!
Steve Murphy
committed
Also, All #include/#exec statements are recorded in the "includes" LL in the ast_config structure. The file name
and line number are stored for each include, plus the name of the file included, so that these statements may be
included in the output files on a file_save operation.
Steve Murphy
committed
The lineno's are really just for relative placement in the file. There is no attempt to make sure that blank lines
are included to keep the lineno's the same between input and output. The lineno fields are used mainly to determine
the position of the #include and #exec directives. So, blank lines tend to disappear from a read/rewrite operation,
and a header gets added.
Steve Murphy
committed
vars and category heads are output in the order they are stored in the config file. So, if the software
shuffles these at all, then the placement of #include directives might get a little mixed up, because the
file/lineno data probably won't get changed.
Steve Murphy
committed
*/
static void gen_header(FILE *f1, const char *configfile, const char *fn, const char *generator)
{
char date[256]="";
time_t t;
time(&t);
ast_copy_string(date, ctime(&t), sizeof(date));
Steve Murphy
committed
fprintf(f1, ";!\n");
fprintf(f1, ";! Automatically generated configuration file\n");
if (strcmp(configfile, fn))
fprintf(f1, ";! Filename: %s (%s)\n", configfile, fn);
else
fprintf(f1, ";! Filename: %s\n", configfile);
fprintf(f1, ";! Generator: %s\n", generator);
fprintf(f1, ";! Creation Date: %s", date);
fprintf(f1, ";!\n");
}
static void set_fn(char *fn, int fn_size, const char *file, const char *configfile)
{
if (!file || file[0] == 0) {
if (configfile[0] == '/')
ast_copy_string(fn, configfile, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, configfile);
} else if (file[0] == '/')
Steve Murphy
committed
ast_copy_string(fn, file, fn_size);
else
snprintf(fn, fn_size, "%s/%s", ast_config_AST_CONFIG_DIR, file);
}
int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator);
int localized_config_text_file_save(const char *configfile, const struct ast_config *cfg, const char *generator)
{
FILE *f;
char fn[256];
struct ast_variable *var;
struct ast_category *cat;
struct ast_comment *cmt;
Steve Murphy
committed
struct ast_config_include *incl;
int blanklines = 0;
Steve Murphy
committed
/* reset all the output flags, in case this isn't our first time saving this data */
Steve Murphy
committed
for (incl=cfg->includes; incl; incl = incl->next)
incl->output = 0;
Steve Murphy
committed
/* go thru all the inclusions and make sure all the files involved (configfile plus all its inclusions)
are all truncated to zero bytes and have that nice header*/
Steve Murphy
committed
for (incl=cfg->includes; incl; incl = incl->next)
{
if (!incl->exec) { /* leave the execs alone -- we'll write out the #exec directives, but won't zero out the include files or exec files*/
FILE *f1;
Steve Murphy
committed
set_fn(fn, sizeof(fn), incl->included_file, configfile); /* normally, fn is just set to incl->included_file, prepended with config dir if relative */
f1 = fopen(fn,"w");
if (f1) {
gen_header(f1, configfile, fn, generator);
fclose(f1); /* this should zero out the file */
} else {
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
}
}
}
Steve Murphy
committed
set_fn(fn, sizeof(fn), 0, configfile); /* just set fn to absolute ver of configfile */
if ((f = fopen(fn, "w+"))) {
#else
if ((f = fopen(fn, "w"))) {
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Saving '%s': ", fn);
Steve Murphy
committed
gen_header(f, configfile, fn, generator);
cat = cfg->root;
Steve Murphy
committed
fclose(f);
Steve Murphy
committed
/* from here out, we open each involved file and concat the stuff we need to add to the end and immediately close... */
/* since each var, cat, and associated comments can come from any file, we have to be
Steve Murphy
committed
mobile, and open each file, print, and close it on an entry-by-entry basis */
while(cat) {
Steve Murphy
committed
set_fn(fn, sizeof(fn), cat->file, configfile);
f = fopen(fn, "a");
if (!f)
{
Steve Murphy
committed
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
Steve Murphy
committed
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, cat->file) == 0){
if (cat->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
incl->output = 1;
}
}
}
Steve Murphy
committed
/* Dump section with any appropriate comment */
for (cmt = cat->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
if (!cat->precomments)
fprintf(f,"\n");
fprintf(f, "[%s]", cat->name);
Steve Murphy
committed
for(cmt = cat->sameline; cmt; cmt=cmt->next) {
fprintf(f,"%s", cmt->cmt);
}
if (!cat->sameline)
fprintf(f,"\n");
Steve Murphy
committed
fclose(f);
var = cat->root;
while(var) {
Steve Murphy
committed
set_fn(fn, sizeof(fn), var->file, configfile);
f = fopen(fn, "a");
if (!f)
{
Steve Murphy
committed
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
Steve Murphy
committed
/* dump any includes that happen before this category header */
for (incl=cfg->includes; incl; incl = incl->next) {
if (strcmp(incl->include_location_file, var->file) == 0){
if (var->lineno > incl->include_location_lineno && !incl->output) {
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
incl->output = 1;
}
}
}
Steve Murphy
committed
for (cmt = var->precomments; cmt; cmt=cmt->next) {
if (cmt->cmt[0] != ';' || cmt->cmt[1] != '!')
fprintf(f,"%s", cmt->cmt);
}
fprintf(f, "%s %s %s %s", var->name, (var->object ? "=>" : "="), var->value, var->sameline->cmt);
fprintf(f, "%s %s %s\n", var->name, (var->object ? "=>" : "="), var->value);
if (var->blanklines) {
blanklines = var->blanklines;
while (blanklines--)
fprintf(f, "\n");
}
Steve Murphy
committed
fclose(f);
var = var->next;
}
cat = cat->next;
}
if ((option_verbose > 1) && !option_debug)
ast_verbose("Saved\n");
} else {
if (option_debug)
ast_log(LOG_DEBUG, "Unable to open for writing: %s\n", fn);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Unable to write (%s)", strerror(errno));
return -1;
}
Steve Murphy
committed
/* Now, for files with trailing #include/#exec statements,
we have to make sure every entry is output */
Steve Murphy
committed
for (incl=cfg->includes; incl; incl = incl->next) {
if (!incl->output) {
/* open the respective file */
set_fn(fn, sizeof(fn), incl->include_location_file, configfile);
f = fopen(fn, "a");
if (!f)
{
ast_verbose(VERBOSE_PREFIX_2 "Unable to write %s (%s)", fn, strerror(errno));
return -1;
}
Steve Murphy
committed
/* output the respective include */
if (incl->exec)
fprintf(f,"#exec \"%s\"\n", incl->exec_file);
else
fprintf(f,"#include \"%s\"\n", incl->included_file);
fclose(f);
incl->output = 1;
}
}
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
return 0;
}
/* ================ the Line ========================================
above this line, you have what you need to load a config file,
and below it, you have what you need to process the extensions.conf
file into the context/exten/prio stuff. They are both in one file
to make things simpler */
static struct ast_context *local_contexts = NULL;
static struct ast_context *contexts = NULL;
struct ast_context;
struct ast_app;
#ifdef LOW_MEMORY
#define EXT_DATA_SIZE 256
#else
#define EXT_DATA_SIZE 8192
#endif
#ifdef NOT_ANYMORE
static AST_RWLIST_HEAD_STATIC(switches, ast_switch);
#endif
#define SWITCH_DATA_LENGTH 256
static const char *ast_get_extension_app(struct ast_exten *e)
{
return e ? e->app : NULL;
}
static const char *ast_get_extension_name(struct ast_exten *exten)
{
return exten ? exten->exten : NULL;
}
static AST_RWLIST_HEAD_STATIC(hints, ast_hint);
/*! \brief ast_change_hint: Change hint for an extension */
static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
{
struct ast_hint *hint;
int res = -1;
AST_RWLIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == oe) {
hint->exten = ne;
res = 0;
break;
}
}
return res;
}
/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
static int ast_add_hint(struct ast_exten *e)
{
struct ast_hint *hint;
if (!e)
return -1;
/* Search if hint exists, do nothing */
AST_RWLIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == e) {
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
return -1;
}
}
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
if (!(hint = ast_calloc(1, sizeof(*hint)))) {
return -1;
}
/* Initialize and insert new item at the top */
hint->exten = e;
AST_RWLIST_INSERT_HEAD(&hints, hint, list);
return 0;
}
/*! \brief add the extension in the priority chain.
* returns 0 on success, -1 on failure
*/
static int add_pri(struct ast_context *con, struct ast_exten *tmp,
struct ast_exten *el, struct ast_exten *e, int replace)
{
struct ast_exten *ep;
for (ep = NULL; e ; ep = e, e = e->peer) {
if (e->priority >= tmp->priority)
break;
}
if (!e) { /* go at the end, and ep is surely set because the list is not empty */
ep->peer = tmp;
return 0; /* success */
}
if (e->priority == tmp->priority) {
/* Can't have something exactly the same. Is this a
replacement? If so, replace, otherwise, bonk. */
if (!replace) {
ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
tmp->datad(tmp->data);
free(tmp);
return -1;
}
/* we are replacing e, so copy the link fields and then update
* whoever pointed to e to point to us
*/
tmp->next = e->next; /* not meaningful if we are not first in the peer list */
tmp->peer = e->peer; /* always meaningful */
if (ep) /* We're in the peer list, just insert ourselves */
ep->peer = tmp;
else if (el) /* We're the first extension. Take over e's functions */
el->next = tmp;
else /* We're the very first extension. */
con->root = tmp;
if (tmp->priority == PRIORITY_HINT)
ast_change_hint(e,tmp);
/* Destroy the old one */
e->datad(e->data);
free(e);
} else { /* Slip ourselves in just before e */
tmp->peer = e;
tmp->next = e->next; /* extension chain, or NULL if e is not the first extension */
if (ep) /* Easy enough, we're just in the peer list */
ep->peer = tmp;
else { /* we are the first in some peer list, so link in the ext list */
if (el)
el->next = tmp; /* in the middle... */
else
con->root = tmp; /* ... or at the head */
e->next = NULL; /* e is no more at the head, so e->next must be reset */
}
/* And immediately return success. */
if (tmp->priority == PRIORITY_HINT)
ast_add_hint(tmp);
}
return 0;
}
/*! \brief ast_remove_hint: Remove hint from extension */
static int ast_remove_hint(struct ast_exten *e)
{
/* Cleanup the Notifys if hint is removed */
struct ast_hint *hint;
struct ast_state_cb *cblist, *cbprev;
int res = -1;
if (!e)
return -1;
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
if (hint->exten == e) {
cbprev = NULL;
cblist = hint->callbacks;
while (cblist) {
/* Notify with -1 and remove all callbacks */
cbprev = cblist;
cblist = cblist->next;
free(cbprev);
}
hint->callbacks = NULL;
AST_RWLIST_REMOVE_CURRENT(&hints, list);
free(hint);
res = 0;
break;
}
}
AST_RWLIST_TRAVERSE_SAFE_END
return res;
}
static void destroy_exten(struct ast_exten *e)
{
if (e->priority == PRIORITY_HINT)
ast_remove_hint(e);
if (e->datad)
e->datad(e->data);
free(e);
}
char *days[] =
{
"sun",
"mon",
"tue",
"wed",
"thu",
"fri",
"sat",
NULL,
};
char *months[] =
{
"jan",
"feb",
"mar",
"apr",
"may",
"jun",
"jul",
"aug",
"sep",
"oct",
"nov",
"dec",
NULL,
};
int ast_build_timing(struct ast_timing *i, const char *info_in);
int ast_build_timing(struct ast_timing *i, const char *info_in)
{
char *info;
int j, num_fields, last_sep = -1;
i->timezone = NULL;
/* Check for empty just in case */
if (ast_strlen_zero(info_in)) {
return 0;
/* make a copy just in case we were passed a static string */
info = ast_strdupa(info_in);
/* count the number of fields in the timespec */
for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
if (info[j] == '|' || info[j] == ',') {
last_sep = j;
num_fields++;
}
}
/* save the timezone, if it is specified */
if (num_fields == 5) {
i->timezone = ast_strdup(info + last_sep + 1);
}
/* Assume everything except time */
i->monthmask = 0xfff; /* 12 bits */
i->daymask = 0x7fffffffU; /* 31 bits */
i->dowmask = 0x7f; /* 7 bits */
/* on each call, use strsep() to move info to the next argument */
get_timerange(i, strsep(&info, "|,"));
if (info)
i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
if (info)
i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
if (info)
i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
return 1;
}
/*!
* \brief helper functions to sort extensions and patterns in the desired way,
* so that more specific patterns appear first.
*
* ext_cmp1 compares individual characters (or sets of), returning
* an int where bits 0-7 are the ASCII code of the first char in the set,
* while bit 8-15 are the cardinality of the set minus 1.
* This way more specific patterns (smaller cardinality) appear first.
* Wildcards have a special value, so that we can directly compare them to
* sets by subtracting the two values. In particular:
* 0x000xx one character, xx
* 0x0yyxx yy character set starting with xx
* 0x10000 '.' (one or more of anything)
* 0x20000 '!' (zero or more of anything)
* 0x30000 NUL (end of string)
* 0x40000 error in set.
* The pointer to the string is advanced according to needs.
* NOTES:
* 1. the empty set is equivalent to NUL.
* 2. given that a full set has always 0 as the first element,
* we could encode the special cases as 0xffXX where XX
* is 1, 2, 3, 4 as used above.
*/
static int ext_cmp1(const char **p)
{
uint32_t chars[8];
int c, cmin = 0xff, count = 0;
const char *end;
/* load, sign extend and advance pointer until we find
* a valid character.
*/
while ( (c = *(*p)++) && (c == ' ' || c == '-') )
; /* ignore some characters */
/* always return unless we have a set of chars */
switch (c) {
default: /* ordinary character */
return 0x0000 | (c & 0xff);
case 'N': /* 2..9 */
return 0x0700 | '2' ;
case 'X': /* 0..9 */
return 0x0900 | '0';
case 'Z': /* 1..9 */
return 0x0800 | '1';
case '.': /* wildcard */
return 0x10000;
case '!': /* earlymatch */
return 0x20000; /* less specific than NULL */
case '\0': /* empty string */
*p = NULL;
return 0x30000;
case '[': /* pattern */
break;
}
/* locate end of set */
if (end == NULL) {
ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
return 0x40000; /* XXX make this entry go last... */
}
memset(chars, '\0', sizeof(chars)); /* clear all chars in the set */
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
for (; *p < end ; (*p)++) {
unsigned char c1, c2; /* first-last char in range */
c1 = (unsigned char)((*p)[0]);
if (*p + 2 < end && (*p)[1] == '-') { /* this is a range */
c2 = (unsigned char)((*p)[2]);
*p += 2; /* skip a total of 3 chars */
} else /* individual character */
c2 = c1;
if (c1 < cmin)
cmin = c1;
for (; c1 <= c2; c1++) {
uint32_t mask = 1 << (c1 % 32);
if ( (chars[ c1 / 32 ] & mask) == 0)
count += 0x100;
chars[ c1 / 32 ] |= mask;
}
}
(*p)++;
return count == 0 ? 0x30000 : (count | cmin);
}
/*!
* \brief the full routine to compare extensions in rules.
*/
static int ext_cmp(const char *a, const char *b)
{
/* make sure non-patterns come first.
* If a is not a pattern, it either comes first or
* we use strcmp to compare the strings.
*/
int ret = 0;
if (a[0] != '_')
return (b[0] == '_') ? -1 : strcmp(a, b);
/* Now we know a is a pattern; if b is not, a comes first */
if (b[0] != '_')
return 1;
#if 0 /* old mode for ext matching */
return strcmp(a, b);
#endif
/* ok we need full pattern sorting routine */
while (!ret && a && b)
ret = ext_cmp1(&a) - ext_cmp1(&b);
if (ret == 0)
return 0;
else
return (ret > 0) ? 1 : -1;
}
/*! \brief copy a string skipping whitespace */
static int ext_strncpy(char *dst, const char *src, int len)
{
int count=0;
while (*src && (count < len - 1)) {
switch(*src) {
case ' ':
/* otherwise exten => [a-b],1,... doesn't work */
/* case '-': */
/* Ignore */
break;
default:
*dst = *src;
dst++;
}
src++;
count++;
}
*dst = '\0';
return count;
}
/*
* Wrapper around _extension_match_core() to do performance measurement
* using the profiling code.
*/
int ast_check_timing(const struct ast_timing *i);