Skip to content
Snippets Groups Projects
extconf.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		(head)->last->field.next = (list)->first;		\
    		(head)->last = (list)->last;				\
          }									\
    } while (0)
    
    #define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST
    
    /*!
      \brief Removes and returns the head entry from a list.
      \param head This is a pointer to the list head structure
      \param field This is the name of the field (declared using AST_LIST_ENTRY())
      used to link entries of this list together.
    
      Removes the head entry from the list, and returns a pointer to it.
      This macro is safe to call on an empty list.
     */
    #define AST_LIST_REMOVE_HEAD(head, field) ({				\
    		typeof((head)->first) cur = (head)->first;		\
    		if (cur) {						\
    			(head)->first = cur->field.next;		\
    			cur->field.next = NULL;				\
    			if ((head)->last == cur)			\
    				(head)->last = NULL;			\
    		}							\
    		cur;							\
    	})
    
    #define AST_RWLIST_REMOVE_HEAD AST_LIST_REMOVE_HEAD
    
    /*!
      \brief Removes a specific entry from a list.
      \param head This is a pointer to the list head structure
      \param elm This is a pointer to the entry to be removed.
      \param field This is the name of the field (declared using AST_LIST_ENTRY())
      used to link entries of this list together.
      \warning The removed entry is \b not freed nor modified in any way.
     */
    #define AST_LIST_REMOVE(head, elm, field) do {			        \
    	if ((head)->first == (elm)) {					\
    		(head)->first = (elm)->field.next;			\
    		if ((head)->last == (elm))			\
    			(head)->last = NULL;			\
    	} else {								\
    		typeof(elm) curelm = (head)->first;			\
    		while (curelm && (curelm->field.next != (elm)))			\
    			curelm = curelm->field.next;			\
    		if (curelm) { \
    			curelm->field.next = (elm)->field.next;			\
    			if ((head)->last == (elm))				\
    				(head)->last = curelm;				\
    		} \
    	}								\
            (elm)->field.next = NULL;                                       \
    } while (0)
    
    #define AST_RWLIST_REMOVE AST_LIST_REMOVE
    
    /* chanvars.h */
    
    struct ast_var_t {
    	AST_LIST_ENTRY(ast_var_t) entries;
    	char *value;
    	char name[0];
    };
    
    AST_LIST_HEAD_NOLOCK(varshead, ast_var_t);
    
    AST_RWLOCK_DEFINE_STATIC(globalslock);
    static struct varshead globals = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
    
    
    /* IN CONFLICT: struct ast_var_t *ast_var_assign(const char *name, const char *value); */
    
    static struct ast_var_t *ast_var_assign(const char *name, const char *value);
    
    static void ast_var_delete(struct ast_var_t *var);
    
    /*from channel.h */
    #define AST_MAX_EXTENSION  80      /*!< Max length of an extension */
    
    
    /* from pbx.h */
    #define PRIORITY_HINT	-1	/*!< Special Priority for a hint */
    
    enum ast_extension_states {
    	AST_EXTENSION_REMOVED = -2,	/*!< Extension removed */
    	AST_EXTENSION_DEACTIVATED = -1,	/*!< Extension hint removed */
    	AST_EXTENSION_NOT_INUSE = 0,	/*!< No device INUSE or BUSY  */
    	AST_EXTENSION_INUSE = 1 << 0,	/*!< One or more devices INUSE */
    	AST_EXTENSION_BUSY = 1 << 1,	/*!< All devices BUSY */
    	AST_EXTENSION_UNAVAILABLE = 1 << 2, /*!< All devices UNAVAILABLE/UNREGISTERED */
    	AST_EXTENSION_RINGING = 1 << 3,	/*!< All devices RINGING */
    	AST_EXTENSION_ONHOLD = 1 << 4,	/*!< All devices ONHOLD */
    };
    
    struct ast_custom_function {
    	const char *name;		/*!< Name */
    	const char *synopsis;		/*!< Short description for "show functions" */
    	const char *desc;		/*!< Help text that explains it all */
    	const char *syntax;		/*!< Syntax description */
    	int (*read)(struct ast_channel *, const char *, char *, char *, size_t);	/*!< Read function, if read is supported */
    	int (*write)(struct ast_channel *, const char *, char *, const char *);		/*!< Write function, if write is supported */
    	AST_RWLIST_ENTRY(ast_custom_function) acflist;
    };
    
    typedef int (ast_switch_f)(struct ast_channel *chan, const char *context,
    	const char *exten, int priority, const char *callerid, const char *data);
    
    struct ast_switch {
    	AST_LIST_ENTRY(ast_switch) list;
    	const char *name;			/*!< Name of the switch */
    	const char *description;		/*!< Description of the switch */
    
    	ast_switch_f *exists;
    	ast_switch_f *canmatch;
    	ast_switch_f *exec;
    	ast_switch_f *matchmore;
    };
    
    
    
    static char *config_filename = "extensions.conf";
    static char *global_registrar = "conf2ael";
    
    static char userscontext[AST_MAX_EXTENSION] = "default";
    static int static_config = 0;
    static int write_protect_config = 1;
    static int autofallthrough_config = 0;
    static int clearglobalvars_config = 0;
    static void pbx_substitute_variables_helper(struct ast_channel *c,const char *cp1,char *cp2,int count);
    
    
    /* stolen from callerid.c */
    
    /*! \brief Clean up phone string
     * remove '(', ' ', ')', non-trailing '.', and '-' not in square brackets.
     * Basically, remove anything that could be invalid in a pattern.
     */
    static void ast_shrink_phone_number(char *n)
    {
    	int x, y=0;
    	int bracketed = 0;
    
    	for (x=0; n[x]; x++) {
    		switch(n[x]) {
    		case '[':
    			bracketed++;
    			n[y++] = n[x];
    			break;
    		case ']':
    			bracketed--;
    			n[y++] = n[x];
    			break;
    		case '-':
    			if (bracketed)
    				n[y++] = n[x];
    			break;
    		case '.':
    			if (!n[x+1])
    				n[y++] = n[x];
    			break;
    		default:
    			if (!strchr("()", n[x]))
    				n[y++] = n[x];
    		}
    	}
    	n[y] = '\0';
    }
    
    
    /* stolen from chanvars.c */
    
    static const char *ast_var_name(const struct ast_var_t *var)
    {
    	const char *name;
    
    	if (var == NULL || (name = var->name) == NULL)
    		return NULL;
    	/* Return the name without the initial underscores */
    	if (name[0] == '_') {
    		name++;
    		if (name[0] == '_')
    			name++;
    	}
    	return name;
    }
    
    /* experiment 1: see if it's easier just to use existing config code
    
     *               to read in the extensions.conf file. In this scenario,
    
                     I have to rip/copy code from other modules, because they
                     are staticly declared as-is. A solution would be to move
                     the ripped code to another location and make them available
                     to other modules and standalones */
    
    /* Our own version of ast_log, since the expr parser uses it. -- stolen from utils/check_expr.c */
    
    static void ast_log(int level, const char *file, int line, const char *function, const char *fmt, ...)
    {
    	va_list vars;
    	va_start(vars,fmt);
    
    	printf("LOG: lev:%d file:%s  line:%d func: %s  ",
    		   level, file, line, function);
    	vprintf(fmt, vars);
    	fflush(stdout);
    	va_end(vars);
    }
    
    
    void __attribute__((format(printf, 1, 2))) ast_verbose(const char *fmt, ...)
    
    	printf("VERBOSE: ");
    	vprintf(fmt, vars);
    	fflush(stdout);
    	va_end(vars);
    }
    
    /* stolen from main/utils.c */
    static char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
    {
     	char *dataPut = start;
    	int inEscape = 0;
    	int inQuotes = 0;
    
    	for (; *start; start++) {
    		if (inEscape) {
    			*dataPut++ = *start;       /* Always goes verbatim */
    			inEscape = 0;
    		} else {
    			if (*start == '\\') {
    				inEscape = 1;      /* Do not copy \ into the data */
    			} else if (*start == '\'') {
    				inQuotes = 1 - inQuotes;   /* Do not copy ' into the data */
    			} else {
    				/* Replace , with |, unless in quotes */
    				*dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
    			}
    		}
    	}
    	if (start != dataPut)
    		*dataPut = 0;
    	return dataPut;
    }
    
    static int ast_true(const char *s)
    {
    	if (ast_strlen_zero(s))
    		return 0;
    
    	/* Determine if this is a true value */
    	if (!strcasecmp(s, "yes") ||
    	    !strcasecmp(s, "true") ||
    	    !strcasecmp(s, "y") ||
    	    !strcasecmp(s, "t") ||
    	    !strcasecmp(s, "1") ||
    	    !strcasecmp(s, "on"))
    		return -1;
    
    	return 0;
    }
    
    
    #define ONE_MILLION	1000000
    /*
     * put timeval in a valid range. usec is 0..999999
     * negative values are not allowed and truncated.
     */
    static struct timeval tvfix(struct timeval a)
    {
    	if (a.tv_usec >= ONE_MILLION) {
    		ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
    			(long)a.tv_sec, (long int) a.tv_usec);
    		a.tv_sec += a.tv_usec / ONE_MILLION;
    		a.tv_usec %= ONE_MILLION;
    	} else if (a.tv_usec < 0) {
    		ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
    			(long)a.tv_sec, (long int) a.tv_usec);
    		a.tv_usec = 0;
    	}
    	return a;
    }
    
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b);
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec += b.tv_sec;
    	a.tv_usec += b.tv_usec;
    	if (a.tv_usec >= ONE_MILLION) {
    		a.tv_sec++;
    		a.tv_usec -= ONE_MILLION;
    	}
    	return a;
    }
    
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b);
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec -= b.tv_sec;
    	a.tv_usec -= b.tv_usec;
    	if (a.tv_usec < 0) {
    		a.tv_sec-- ;
    		a.tv_usec += ONE_MILLION;
    	}
    	return a;
    }
    #undef ONE_MILLION
    
    
    void ast_mark_lock_failed(void *lock_addr);
    void ast_mark_lock_failed(void *lock_addr)
    {
    	/* Pretend to do something. */
    }
    
    
    /* stolen from pbx.c */
    #define VAR_BUF_SIZE 4096
    
    #define	VAR_NORMAL		1
    #define	VAR_SOFTTRAN	2
    #define	VAR_HARDTRAN	3
    
    #define BACKGROUND_SKIP		(1 << 0)
    #define BACKGROUND_NOANSWER	(1 << 1)
    #define BACKGROUND_MATCHEXTEN	(1 << 2)
    #define BACKGROUND_PLAYBACK	(1 << 3)
    
    /*!
       \brief ast_exten: An extension
    	The dialplan is saved as a linked list with each context
    	having it's own linked list of extensions - one item per
    	priority.
    */
    struct ast_exten {
    	char *exten;			/*!< Extension name */
    	int matchcid;			/*!< Match caller id ? */
    	const char *cidmatch;		/*!< Caller id to match for this extension */
    	int priority;			/*!< Priority */
    	const char *label;		/*!< Label */
    	struct ast_context *parent;	/*!< The context this extension belongs to  */
    	const char *app; 		/*!< Application to execute */
    	struct ast_app *cached_app;     /*!< Cached location of application */
    	void *data;			/*!< Data to use (arguments) */
    	void (*datad)(void *);		/*!< Data destructor */
    	struct ast_exten *peer;		/*!< Next higher priority with our extension */
    	const char *registrar;		/*!< Registrar */
    	struct ast_exten *next;		/*!< Extension with a greater ID */
    	char stuff[0];
    };
    /* from pbx.h */
    typedef int (*ast_state_cb_type)(char *context, char* id, enum ast_extension_states state, void *data);
    struct ast_timing {
    	int hastime;				/*!< If time construct exists */
    	unsigned int monthmask;			/*!< Mask for month */
    	unsigned int daymask;			/*!< Mask for date */
    	unsigned int dowmask;			/*!< Mask for day of week (mon-sun) */
    
    	unsigned int minmask[48];		/*!< Mask for minute */
    
    	char *timezone;                 /*!< NULL, or zoneinfo style timezone */
    
    };
    /* end of pbx.h */
    /*! \brief ast_include: include= support in extensions.conf */
    struct ast_include {
    	const char *name;
    	const char *rname;			/*!< Context to include */
    	const char *registrar;			/*!< Registrar */
    	int hastime;				/*!< If time construct exists */
    	struct ast_timing timing;               /*!< time construct */
    	struct ast_include *next;		/*!< Link them together */
    	char stuff[0];
    };
    
    /*! \brief ast_sw: Switch statement in extensions.conf */
    struct ast_sw {
    	char *name;
    	const char *registrar;			/*!< Registrar */
    	char *data;				/*!< Data load */
    	int eval;
    	AST_LIST_ENTRY(ast_sw) list;
    	char *tmpdata;
    	char stuff[0];
    };
    
    /*! \brief ast_ignorepat: Ignore patterns in dial plan */
    struct ast_ignorepat {
    	const char *registrar;
    	struct ast_ignorepat *next;
    
    	char pattern[0];
    
    };
    
    /*! \brief ast_context: An extension context */
    struct ast_context {
    	ast_rwlock_t lock; 			/*!< A lock to prevent multiple threads from clobbering the context */
    	struct ast_exten *root;			/*!< The root of the list of extensions */
    	struct ast_context *next;		/*!< Link them together */
    	struct ast_include *includes;		/*!< Include other contexts */
    	struct ast_ignorepat *ignorepats;	/*!< Patterns for which to continue playing dialtone */
    	const char *registrar;			/*!< Registrar */
    	AST_LIST_HEAD_NOLOCK(, ast_sw) alts;	/*!< Alternative switches */
    	ast_mutex_t macrolock;			/*!< A lock to implement "exclusive" macros - held whilst a call is executing in the macro */
    	char name[0];				/*!< Name of the context */
    };
    
    
    /*! \brief ast_app: A registered application */
    struct ast_app {
    	int (*execute)(struct ast_channel *chan, void *data);
    	const char *synopsis;			/*!< Synopsis text for 'show applications' */
    	const char *description;		/*!< Description (help text) for 'show application &lt;name&gt;' */
    	AST_RWLIST_ENTRY(ast_app) list;		/*!< Next app in list */
    	void *module;			/*!< Module this app belongs to */
    	char name[0];				/*!< Name of the application */
    };
    
    
    /*! \brief ast_state_cb: An extension state notify register item */
    struct ast_state_cb {
    	int id;
    	void *data;
    	ast_state_cb_type callback;
    	struct ast_state_cb *next;
    };
    
    /*! \brief Structure for dial plan hints
    
      \note Hints are pointers from an extension in the dialplan to one or
    
      more devices (tech/name)
    
    	- See \ref AstExtState
    */
    struct ast_hint {
    	struct ast_exten *exten;	/*!< Extension */
    	int laststate; 			/*!< Last known state */
    	struct ast_state_cb *callbacks;	/*!< Callback list for this extension */
    	AST_RWLIST_ENTRY(ast_hint) list;/*!< Pointer to next hint in list */
    };
    
    struct store_hint {
    	char *context;
    	char *exten;
    	struct ast_state_cb *callbacks;
    	int laststate;
    	AST_LIST_ENTRY(store_hint) list;
    	char data[1];
    };
    
    AST_LIST_HEAD(store_hints, store_hint);
    
    #define STATUS_NO_CONTEXT	1
    #define STATUS_NO_EXTENSION	2
    #define STATUS_NO_PRIORITY	3
    #define STATUS_NO_LABEL		4
    #define STATUS_SUCCESS		5
    
    static struct ast_var_t *ast_var_assign(const char *name, const char *value)
    
    	struct ast_var_t *var;
    	int name_len = strlen(name) + 1;
    	int value_len = strlen(value) + 1;
    
    	if (!(var = ast_calloc(sizeof(*var) + name_len + value_len, sizeof(char)))) {
    		return NULL;
    	}
    
    	ast_copy_string(var->name, name, name_len);
    	var->value = var->name + name_len;
    	ast_copy_string(var->value, value, value_len);
    
    static void ast_var_delete(struct ast_var_t *var)
    {
    
    }
    
    
    /* chopped this one off at the knees! */
    static int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
    {
    
    	/* ast_log(LOG_ERROR, "Function %s not registered\n", function); we are not interested in the details here */
    
    	return -1;
    }
    
    static unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arraylen)
    {
    	int argc;
    	char *scan;
    	int paren = 0, quote = 0;
    
    	if (!buf || !array || !arraylen)
    		return 0;
    
    	memset(array, 0, arraylen * sizeof(*array));
    
    	scan = buf;
    
    	for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
    		array[argc] = scan;
    		for (; *scan; scan++) {
    			if (*scan == '(')
    				paren++;
    			else if (*scan == ')') {
    				if (paren)
    					paren--;
    			} else if (*scan == '"' && delim != '"') {
    				quote = quote ? 0 : 1;
    				/* Remove quote character from argument */
    				memmove(scan, scan + 1, strlen(scan));
    				scan--;
    			} else if (*scan == '\\') {
    				/* Literal character, don't parse */
    				memmove(scan, scan + 1, strlen(scan));
    			} else if ((*scan == delim) && !paren && !quote) {
    				*scan++ = '\0';
    				break;
    			}
    		}
    	}
    
    	if (*scan)
    		array[argc++] = scan;
    
    	return argc;
    }
    
    static void pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
    {
    	struct ast_var_t *newvariable;
    	struct varshead *headp;
    	const char *nametail = name;
    
    	/* XXX may need locking on the channel ? */
    	if (name[strlen(name)-1] == ')') {
    		char *function = ast_strdupa(name);
    
    		ast_func_write(chan, function, value);
    		return;
    	}
    
    	headp = &globals;
    
    	/* For comparison purposes, we have to strip leading underscores */
    	if (*nametail == '_') {
    		nametail++;
    		if (*nametail == '_')
    			nametail++;
    	}
    
    	AST_LIST_TRAVERSE (headp, newvariable, entries) {
    		if (strcasecmp(ast_var_name(newvariable), nametail) == 0) {
    			/* there is already such a variable, delete it */
    			AST_LIST_REMOVE(headp, newvariable, entries);
    			ast_var_delete(newvariable);
    			break;
    		}
    	}
    
    
    	if (value && (newvariable = ast_var_assign(name, value))) {
    
    		if ((option_verbose > 1) && (headp == &globals))
    			ast_verbose(VERBOSE_PREFIX_2 "Setting global variable '%s' to '%s'\n", name, value);
    		AST_LIST_INSERT_HEAD(headp, newvariable, entries);
    	}
    
    }
    
    
    static int pbx_builtin_setvar(struct ast_channel *chan, const void *data)
    
    {
    	char *name, *value, *mydata;
    	int argc;
    	char *argv[24];		/* this will only support a maximum of 24 variables being set in a single operation */
    	int global = 0;
    	int x;
    
    	if (ast_strlen_zero(data)) {
    		ast_log(LOG_WARNING, "Set requires at least one variable name/value pair.\n");
    		return 0;
    	}
    
    	mydata = ast_strdupa(data);
    	argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0]));
    
    	/* check for a trailing flags argument */
    	if ((argc > 1) && !strchr(argv[argc-1], '=')) {
    		argc--;
    		if (strchr(argv[argc], 'g'))
    			global = 1;
    	}
    
    	for (x = 0; x < argc; x++) {
    		name = argv[x];
    		if ((value = strchr(name, '='))) {
    			*value++ = '\0';
    			pbx_builtin_setvar_helper((global) ? NULL : chan, name, value);
    		} else
    			ast_log(LOG_WARNING, "Ignoring entry '%s' with no = (and not last 'options' entry)\n", name);
    	}
    
    	return(0);
    }
    
    
    int localized_pbx_builtin_setvar(struct ast_channel *chan, const void *data);
    
    int localized_pbx_builtin_setvar(struct ast_channel *chan, const void *data)
    
    {
    	return pbx_builtin_setvar(chan, data);
    }
    
    
    /*! \brief Helper for get_range.
     * return the index of the matching entry, starting from 1.
     * If names is not supplied, try numeric values.
     */
    static int lookup_name(const char *s, char *const names[], int max)
    {
    	int i;
    
    
    	if (names && *s > '9') {
    
    			if (!strcasecmp(s, names[i])) {
    				return i;
    			}
    
    
    	/* Allow months and weekdays to be specified as numbers, as well */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (sscanf(s, "%2d", &i) == 1 && i >= 1 && i <= max) {
    
    		/* What the array offset would have been: "1" would be at offset 0 */
    		return i - 1;
    	}
    	return -1; /* error return */
    
    }
    
    /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
     * names, if supplied, is an array of names that should be mapped to numbers.
     */
    static unsigned get_range(char *src, int max, char *const names[], const char *msg)
    {
    
    	int start, end; /* start and ending position */
    
    
    	/* Check for whole range */
    	if (ast_strlen_zero(src) || !strcmp(src, "*")) {
    
    		return (1 << max) - 1;
    	}
    
    	while ((part = strsep(&src, "&"))) {
    
    		char *endpart = strchr(part, '-');
    		if (endpart) {
    			*endpart++ = '\0';
    		}
    
    		if ((start = lookup_name(part, names, max)) < 0) {
    			ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
    			continue;
    
    		if (endpart) { /* find end of range */
    			if ((end = lookup_name(endpart, names, max)) < 0) {
    				ast_log(LOG_WARNING, "Invalid end %s '%s', skipping element\n", msg, endpart);
    				continue;
    
    			end = start;
    		}
    		/* Fill the mask. Remember that ranges are cyclic */
    		mask |= (1 << end);   /* initialize with last element */
    		while (start != end) {
    			if (start >= max) {
    				start = 0;
    			}
    			mask |= (1 << start);
    			start++;
    
    		}
    	}
    	return mask;
    }
    
    /*! \brief store a bitmask of valid times, one bit each 2 minute */
    static void get_timerange(struct ast_timing *i, char *times)
    {
    
    	int st_h, st_m;
    	int endh, endm;
    	int minute_start, minute_end;
    
    
    	/* start disabling all times, fill the fields with 0's, as they may contain garbage */
    	memset(i->minmask, 0, sizeof(i->minmask));
    
    
    	/* 1-minute per bit */
    
    	/* Star is all times */
    	if (ast_strlen_zero(times) || !strcmp(times, "*")) {
    
    		/* 48, because each hour takes 2 integers; 30 bits each */
    		for (x = 0; x < 48; x++) {
    
    			i->minmask[x] = 0x3fffffff; /* 30 bits */
    
    	while ((part = strsep(&times, "&"))) {
    		if (!(endpart = strchr(part, '-'))) {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
    
    				ast_log(LOG_WARNING, "%s isn't a valid time.\n", part);
    				continue;
    			}
    			i->minmask[st_h * 2 + (st_m >= 30 ? 1 : 0)] |= (1 << (st_m % 30));
    			continue;
    
    		*endpart++ = '\0';
    		/* why skip non digits? Mostly to skip spaces */
    		while (*endpart && !isdigit(*endpart)) {
    			endpart++;
    		}
    		if (!*endpart) {
    			ast_log(LOG_WARNING, "Invalid time range starting with '%s-'.\n", part);
    			continue;
    		}
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
    
    			ast_log(LOG_WARNING, "'%s' isn't a valid start time.\n", part);
    			continue;
    		}
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		if (sscanf(endpart, "%2d:%2d", &endh, &endm) != 2 || endh < 0 || endh > 23 || endm < 0 || endm > 59) {
    
    			ast_log(LOG_WARNING, "'%s' isn't a valid end time.\n", endpart);
    			continue;
    		}
    		minute_start = st_h * 60 + st_m;
    		minute_end = endh * 60 + endm;
    		/* Go through the time and enable each appropriate bit */
    		for (x = minute_start; x != minute_end; x = (x + 1) % (24 * 60)) {
    			i->minmask[x / 30] |= (1 << (x % 30));
    		}
    		/* Do the last one */
    		i->minmask[x / 30] |= (1 << (x % 30));
    
    	}
    	/* All done */
    	return;
    }
    
    static void null_datad(void *foo)
    {
    }
    
    /*! \brief Find realtime engine for realtime family */
    
    static struct ast_config_engine *find_engine(const char *family, char *database, int dbsiz, char *table, int tabsiz)
    
    {
    	struct ast_config_engine *eng, *ret = NULL;
    	struct ast_config_map *map;
    
    
    	for (map = config_maps; map; map = map->next) {
    		if (!strcasecmp(family, map->name)) {
    			if (database)
    				ast_copy_string(database, map->database, dbsiz);
    			if (table)
    				ast_copy_string(table, map->table ? map->table : family, tabsiz);
    			break;
    		}
    	}
    
    	/* Check if the required driver (engine) exist */
    	if (map) {
    		for (eng = config_engine_list; !ret && eng; eng = eng->next) {
    			if (!strcasecmp(eng->name, map->driver))
    				ret = eng;
    		}
    	}
    
    	/* if we found a mapping, but the engine is not available, then issue a warning */
    	if (map && !ret)
    		ast_log(LOG_WARNING, "Realtime mapping for '%s' found to engine '%s', but the engine is not available\n", map->name, map->driver);
    
    	return ret;
    }
    
    struct ast_category *ast_config_get_current_category(const struct ast_config *cfg);
    
    struct ast_category *ast_config_get_current_category(const struct ast_config *cfg)
    {
    	return cfg->current;
    }
    
    
    static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno);
    
    static struct ast_category *ast_category_new(const char *name, const char *in_file, int lineno)
    
    {
    	struct ast_category *category;
    
    	if ((category = ast_calloc(1, sizeof(*category))))
    		ast_copy_string(category->name, name, sizeof(category->name));
    
    	category->file = strdup(in_file);
    	category->lineno = lineno; /* if you don't know the lineno, set it to 999999 or something real big */
     	return category;
    
    }
    
    struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name);
    
    struct ast_category *localized_category_get(const struct ast_config *config, const char *category_name)
    {
    	return category_get(config, category_name, 0);
    }
    
    static void move_variables(struct ast_category *old, struct ast_category *new)
    {
    	struct ast_variable *var = old->root;
    	old->root = NULL;
    #if 1
    	/* we can just move the entire list in a single op */
    	ast_variable_append(new, var);
    #else
    	while (var) {
    		struct ast_variable *next = var->next;
    		var->next = NULL;
    		ast_variable_append(new, var);
    		var = next;
    	}
    #endif
    }
    
    static void inherit_category(struct ast_category *new, const struct ast_category *base)
    {
    	struct ast_variable *var;
    
    	for (var = base->root; var; var = var->next)
    		ast_variable_append(new, variable_clone(var));
    }
    
    static void ast_category_append(struct ast_config *config, struct ast_category *category);
    
    static void ast_category_append(struct ast_config *config, struct ast_category *category)
    {
    	if (config->last)
    		config->last->next = category;
    	else
    		config->root = category;
    	config->last = category;
    	config->current = category;
    }
    
    static void ast_category_destroy(struct ast_category *cat);
    
    static void ast_category_destroy(struct ast_category *cat)
    {
    	ast_variables_destroy(cat->root);
    
    	free(cat);
    }
    
    static struct ast_config_engine text_file_engine = {
    	.name = "text",
    	.load_func = config_text_file_load,
    };
    
    
    
    static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file);
    
    static struct ast_config *ast_config_internal_load(const char *filename, struct ast_config *cfg, int withcomments, const char *suggested_incl_file)
    
    {
    	char db[256];
    	char table[256];
    	struct ast_config_engine *loader = &text_file_engine;
    
    	struct ast_config *result;
    
    
    	if (cfg->include_level == cfg->max_include_level) {
    		ast_log(LOG_WARNING, "Maximum Include level (%d) exceeded\n", cfg->max_include_level);
    		return NULL;
    	}
    
    	cfg->include_level++;
    	/*  silence is golden!
    		ast_log(LOG_WARNING, "internal loading file %s level=%d\n", filename, cfg->include_level);
    	*/
    
    	if (strcmp(filename, extconfig_conf) && strcmp(filename, "asterisk.conf") && config_engine_list) {
    		struct ast_config_engine *eng;
    
    		eng = find_engine(filename, db, sizeof(db), table, sizeof(table));
    
    
    		if (eng && eng->load_func) {
    			loader = eng;
    		} else {
    			eng = find_engine("global", db, sizeof(db), table, sizeof(table));
    			if (eng && eng->load_func)
    				loader = eng;
    		}
    	}
    
    
    	result = loader->load_func(db, table, filename, cfg, withcomments, suggested_incl_file);
    
    	/* silence is golden
    
    	   ast_log(LOG_WARNING, "finished internal loading file %s level=%d\n", filename, cfg->include_level);
    	*/
    
    	if (result)
    		result->include_level--;
    
    	return result;
    }
    
    
    
    static int process_text_line(struct ast_config *cfg, struct ast_category **cat, char *buf, int lineno, const char *configfile, int withcomments, const char *suggested_include_file)
    
    {
    	char *c;
    	char *cur = buf;
    	struct ast_variable *v;
    
    Corey Farrell's avatar
    Corey Farrell committed
    	char exec_file[512];
    
    	int object, do_exec, do_include;
    
    	/* Actually parse the entry */
    	if (cur[0] == '[') {
    		struct ast_category *newcat = NULL;
    		char *catname;
    
    		/* A category header */
    		c = strchr(cur, ']');
    		if (!c) {
    			ast_log(LOG_WARNING, "parse error: no closing ']', line %d of %s\n", lineno, configfile);
    			return -1;
    		}
    		*c++ = '\0';
    		cur++;
     		if (*c++ != '(')
     			c = NULL;
    		catname = cur;
    
    		if (!(*cat = newcat = ast_category_new(catname, ast_strlen_zero(suggested_include_file)?configfile:suggested_include_file, lineno))) {
    
    		/* add comments */
    		if (withcomments && comment_buffer && comment_buffer[0] ) {
    			newcat->precomments = ALLOC_COMMENT(comment_buffer);
    		}
    		if (withcomments && lline_buffer && lline_buffer[0] ) {
    			newcat->sameline = ALLOC_COMMENT(lline_buffer);
    		}
    		if( withcomments )
    			CB_RESET();
    
     		/* If there are options or categories to inherit from, process them now */
     		if (c) {
     			if (!(cur = strchr(c, ')'))) {
     				ast_log(LOG_WARNING, "parse error: no closing ')', line %d of %s\n", lineno, configfile);
     				return -1;
     			}
     			*cur = '\0';
     			while ((cur = strsep(&c, ","))) {
    				if (!strcasecmp(cur, "!")) {
    					(*cat)->ignored = 1;
    				} else if (!strcasecmp(cur, "+")) {
    					*cat = category_get(cfg, catname, 1);
    					if (!*cat) {
    						ast_config_destroy(cfg);
    						if (newcat)
    							ast_category_destroy(newcat);
    						ast_log(LOG_WARNING, "Category addition requested, but category '%s' does not exist, line %d of %s\n", catname, lineno, configfile);
    						return -1;
    					}
    					if (newcat) {
    						move_variables(newcat, *cat);
    						ast_category_destroy(newcat);
    						newcat = NULL;
    					}
    				} else {
    					struct ast_category *base;
    
    					base = category_get(cfg, cur, 1);
    					if (!base) {
    						ast_log(LOG_WARNING, "Inheritance requested, but category '%s' does not exist, line %d of %s\n", cur, lineno, configfile);
    						return -1;
    					}
    					inherit_category(*cat, base);
    				}
     			}
     		}
    		if (newcat)
    			ast_category_append(cfg, *cat);
    	} else if (cur[0] == '#') {
    		/* A directive */
    		cur++;
    		c = cur;
    		while(*c && (*c > 32)) c++;
    		if (*c) {
    			*c = '\0';
    			/* Find real argument */
    			c = ast_skip_blanks(c + 1);
    			if (!*c)
    				c = NULL;
    
    			c = NULL;
    		do_include = !strcasecmp(cur, "include");
    		if(!do_include)
    			do_exec = !strcasecmp(cur, "exec");