123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2016, CFWare, LLC
- *
- * Corey Farrell <git@cfware.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*! \file
- *
- * \brief PBX timing routines.
- *
- * \author Corey Farrell <git@cfware.com>
- */
- /*** MODULEINFO
- <support_level>core</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include "asterisk/localtime.h"
- #include "asterisk/logger.h"
- #include "asterisk/pbx.h"
- #include "asterisk/strings.h"
- #include "asterisk/utils.h"
- /*! \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, const char * const names[], int max)
- {
- int i;
- if (names && *s > '9') {
- for (i = 0; names[i]; i++) {
- if (!strcasecmp(s, names[i])) {
- return i;
- }
- }
- }
- /* Allow months and weekdays to be specified as numbers, as well */
- 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, const char * const names[], const char *msg)
- {
- int start, end; /* start and ending position */
- unsigned int mask = 0;
- char *part;
- /* Check for whole range */
- if (ast_strlen_zero(src) || !strcmp(src, "*")) {
- return (1 << max) - 1;
- }
- while ((part = strsep(&src, "&"))) {
- /* Get start and ending position */
- char *endpart = strchr(part, '-');
- if (endpart) {
- *endpart++ = '\0';
- }
- /* Find the start */
- 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;
- }
- } else {
- end = start;
- }
- /* Fill the mask. Remember that ranges are cyclic */
- mask |= (1 << end); /* initialize with last element */
- while (start != end) {
- mask |= (1 << start);
- if (++start >= max) {
- start = 0;
- }
- }
- }
- return mask;
- }
- /*! \brief store a bitmask of valid times, one bit each 1 minute */
- static void get_timerange(struct ast_timing *i, char *times)
- {
- char *endpart, *part;
- int x;
- 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 */
- }
- return;
- }
- /* Otherwise expect a range */
- while ((part = strsep(×, "&"))) {
- if (!(endpart = strchr(part, '-'))) {
- 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;
- }
- 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;
- }
- 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 const char * const days[] =
- {
- "sun",
- "mon",
- "tue",
- "wed",
- "thu",
- "fri",
- "sat",
- NULL,
- };
- static const char * const months[] =
- {
- "jan",
- "feb",
- "mar",
- "apr",
- "may",
- "jun",
- "jul",
- "aug",
- "sep",
- "oct",
- "nov",
- "dec",
- NULL,
- };
- /*! /brief Build timing
- *
- * /param i info
- * /param 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] == ',') {
- 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");
- return 1;
- }
- int ast_check_timing(const struct ast_timing *i)
- {
- return ast_check_timing2(i, ast_tvnow());
- }
- int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
- {
- struct ast_tm tm;
- ast_localtime(&tv, &tm, i->timezone);
- /* If it's not the right month, return */
- if (!(i->monthmask & (1 << tm.tm_mon)))
- return 0;
- /* If it's not that time of the month.... */
- /* Warning, tm_mday has range 1..31! */
- if (!(i->daymask & (1 << (tm.tm_mday-1))))
- return 0;
- /* If it's not the right day of the week */
- if (!(i->dowmask & (1 << tm.tm_wday)))
- return 0;
- /* Sanity check the hour just to be safe */
- if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
- ast_log(LOG_WARNING, "Insane time...\n");
- return 0;
- }
- /* Now the tough part, we calculate if it fits
- in the right time based on min/hour */
- if (!(i->minmask[tm.tm_hour * 2 + (tm.tm_min >= 30 ? 1 : 0)] & (1 << (tm.tm_min >= 30 ? tm.tm_min - 30 : tm.tm_min))))
- return 0;
- /* If we got this far, then we're good */
- return 1;
- }
- int ast_destroy_timing(struct ast_timing *i)
- {
- if (i->timezone) {
- ast_free(i->timezone);
- i->timezone = NULL;
- }
- return 0;
- }
|