aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES8
-rw-r--r--configs/extensions.conf.sample2
-rw-r--r--doc/api-1.6.2-changes.txt6
-rw-r--r--funcs/func_logic.c2
-rw-r--r--include/asterisk/pbx.h17
-rw-r--r--main/pbx.c52
-rw-r--r--utils/extconf.c230
7 files changed, 189 insertions, 128 deletions
diff --git a/CHANGES b/CHANGES
index 4d55a2a49..c2fd95a33 100644
--- a/CHANGES
+++ b/CHANGES
@@ -90,8 +90,16 @@ Miscellaneous
which are interpreted as relative to the astvarlibdir setting in asterisk.conf.
* All deprecated CLI commands are removed from the sourcecode. They are now handled
by the new clialiases module. See cli_aliases.conf.sample file.
+ * Times within timespecs are now accurate down to the minute. This is a change
+ from historical Asterisk, which only provided timespecs rounded to the nearest
+ even (read: evenly divisible by 2) minute mark.
* The realtime switch now supports an option flag, 'p', which disables searches for
pattern matches.
+ * In addition to a time range and date range, timespecs now accept a 5th optional
+ argument, timezone. This allows you to perform time checks on alternate
+ timezones, especially if those daylight savings time ranges vary from your
+ machine's native timezone. See GotoIfTime, ExecIfTime, IFTIME(), and timed
+ includes.
Asterisk Manager Interface
--------------------------
diff --git a/configs/extensions.conf.sample b/configs/extensions.conf.sample
index 0b82f3b3a..167ae372c 100644
--- a/configs/extensions.conf.sample
+++ b/configs/extensions.conf.sample
@@ -185,7 +185,7 @@ TRUNKMSD=1 ; MSD digits to strip (usually 1 or 0)
;
; Timing list for includes is
;
-; <time range>,<days of week>,<days of month>,<months>
+; <time range>,<days of week>,<days of month>,<months>[,<timezone>]
;
; Note that ranges may be specified to wrap around the ends. Also, minutes are
; fine-grained only down to the closest even minute.
diff --git a/doc/api-1.6.2-changes.txt b/doc/api-1.6.2-changes.txt
new file mode 100644
index 000000000..34a3e5fc0
--- /dev/null
+++ b/doc/api-1.6.2-changes.txt
@@ -0,0 +1,6 @@
+PBX changes
+-----------
+ * If you use ast_build_timing() in your application, you should start calling
+ ast_destroy_timing() upon destruction of the structure, to avoid a memory
+ leak.
+
diff --git a/funcs/func_logic.c b/funcs/func_logic.c
index ef644f43f..e583d5439 100644
--- a/funcs/func_logic.c
+++ b/funcs/func_logic.c
@@ -148,6 +148,7 @@ static int iftime(struct ast_channel *chan, const char *cmd, char *data, char *b
if (!ast_build_timing(&timing, expr)) {
ast_log(LOG_WARNING, "Invalid Time Spec.\n");
+ ast_destroy_timing(&timing);
return -1;
}
@@ -157,6 +158,7 @@ static int iftime(struct ast_channel *chan, const char *cmd, char *data, char *b
iffalse = ast_strip_quoted(iffalse, "\"", "\"");
ast_copy_string(buf, ast_check_timing(&timing) ? iftrue : iffalse, len);
+ ast_destroy_timing(&timing);
return 0;
}
diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index 7847bc598..a2ab92592 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -119,11 +119,28 @@ struct ast_timing {
unsigned int daymask; /*!< Mask for date */
unsigned int dowmask; /*!< Mask for day of week (sun-sat) */
unsigned int minmask[48]; /*!< Mask for minute */
+ char *timezone; /*!< NULL, or zoneinfo style timezone */
};
+/*!\brief Construct a timing bitmap, for use in time-based conditionals.
+ * \param i Pointer to an ast_timing structure.
+ * \param info Standard string containing a timerange, weekday range, monthday range, and month range, as well as an optional timezone.
+ * \retval Returns 1 on success or 0 on failure.
+ */
int ast_build_timing(struct ast_timing *i, const char *info);
+
+/*!\brief Evaluate a pre-constructed bitmap as to whether the current time falls within the range specified.
+ * \param i Pointer to an ast_timing structure.
+ * \retval Returns 1, if the time matches or 0, if the current time falls outside of the specified range.
+ */
int ast_check_timing(const struct ast_timing *i);
+/*!\brief Deallocates memory structures associated with a timing bitmap.
+ * \param i Pointer to an ast_timing structure.
+ * \retval Returns 0 on success or a number suitable for passing into strerror, otherwise.
+ */
+int ast_destroy_timing(struct ast_timing *i);
+
struct ast_pbx {
int dtimeoutms; /*!< Timeout between digits (milliseconds) */
int rtimeoutms; /*!< Timeout for response (milliseconds) */
diff --git a/main/pbx.c b/main/pbx.c
index 840455c44..273418366 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -212,6 +212,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<argument name="weekdays" required="true" />
<argument name="mdays" required="true" />
<argument name="months" required="true" />
+ <argument name="timezone" required="false" />
</parameter>
<parameter name="appname" required="true" hasparams="optional">
<argument name="appargs" required="true" />
@@ -306,6 +307,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<argument name="weekdays" required="true" />
<argument name="mdays" required="true" />
<argument name="months" required="true" />
+ <argument name="timezone" required="false" />
</parameter>
<parameter name="destination" required="true" argsep=":">
<argument name="labeliftrue" />
@@ -4613,6 +4615,7 @@ int ast_context_remove_include2(struct ast_context *con, const char *include, co
else
con->includes = i->next;
/* free include and return */
+ ast_destroy_timing(&(i->timing));
ast_free(i);
ret = 0;
break;
@@ -6793,15 +6796,32 @@ static char *months[] =
int ast_build_timing(struct ast_timing *i, const char *info_in)
{
- char info_save[256];
- char *info;
+ char *info_save, *info;
+ int j, num_fields, last_sep = -1;
/* Check for empty just in case */
- if (ast_strlen_zero(info_in))
+ if (ast_strlen_zero(info_in)) {
return 0;
+ }
+
/* make a copy just in case we were passed a static string */
- ast_copy_string(info_save, info_in, sizeof(info_save));
- info = info_save;
+ info_save = 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);
+ } else {
+ i->timezone = NULL;
+ }
+
/* Assume everything except time */
i->monthmask = 0xfff; /* 12 bits */
i->daymask = 0x7fffffffU; /* 31 bits */
@@ -6822,7 +6842,7 @@ int ast_check_timing(const struct ast_timing *i)
struct ast_tm tm;
struct timeval now = ast_tvnow();
- ast_localtime(&now, &tm, NULL);
+ ast_localtime(&now, &tm, i->timezone);
/* If it's not the right month, return */
if (!(i->monthmask & (1 << tm.tm_mon)))
@@ -6852,6 +6872,14 @@ int ast_check_timing(const struct ast_timing *i)
return 1;
}
+int ast_destroy_timing(struct ast_timing *i)
+{
+ if (i->timezone) {
+ ast_free(i->timezone);
+ i->timezone = NULL;
+ }
+ return 0;
+}
/*
* errno values
* ENOMEM - out of memory
@@ -6896,6 +6924,7 @@ int ast_context_add_include2(struct ast_context *con, const char *value,
/* ... go to last include and check if context is already included too... */
for (i = con->includes; i; i = i->next) {
if (!strcasecmp(i->name, new_include->name)) {
+ ast_destroy_timing(&(new_include->timing));
ast_free(new_include);
ast_unlock_context(con);
errno = EEXIST;
@@ -8380,7 +8409,7 @@ static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
struct ast_timing timing;
if (ast_strlen_zero(data)) {
- ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?'labeliftrue':'labeliffalse'\n");
+ ast_log(LOG_WARNING, "GotoIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>[,<timezone>]?'labeliftrue':'labeliffalse'\n");
return -1;
}
@@ -8396,6 +8425,7 @@ static int pbx_builtin_gotoiftime(struct ast_channel *chan, void *data)
branch = branch1;
else
branch = branch2;
+ ast_destroy_timing(&timing);
if (ast_strlen_zero(branch)) {
ast_debug(1, "Not taking any branch\n");
@@ -8413,7 +8443,7 @@ static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
char *s, *appname;
struct ast_timing timing;
struct ast_app *app;
- static const char *usage = "ExecIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>?<appname>[(<appargs>)]";
+ static const char *usage = "ExecIfTime requires an argument:\n <time range>,<days of week>,<days of month>,<months>[,<timezone>]?<appname>[(<appargs>)]";
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "%s\n", usage);
@@ -8430,11 +8460,15 @@ static int pbx_builtin_execiftime(struct ast_channel *chan, void *data)
if (!ast_build_timing(&timing, s)) {
ast_log(LOG_WARNING, "Invalid Time Spec: %s\nCorrect usage: %s\n", s, usage);
+ ast_destroy_timing(&timing);
return -1;
}
- if (!ast_check_timing(&timing)) /* outside the valid time window, just return */
+ if (!ast_check_timing(&timing)) { /* outside the valid time window, just return */
+ ast_destroy_timing(&timing);
return 0;
+ }
+ ast_destroy_timing(&timing);
/* now split appname(appargs) */
if ((s = strchr(appname, '('))) {
diff --git a/utils/extconf.c b/utils/extconf.c
index 4a5c6c3e0..5d198f85b 100644
--- a/utils/extconf.c
+++ b/utils/extconf.c
@@ -3061,20 +3061,24 @@ int localized_pbx_builtin_setvar(struct ast_channel *chan, void *data)
* 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) {
+ if (names && *s > '9') {
for (i = 0; names[i]; i++) {
- if (!strcasecmp(s, names[i]))
- return i+1;
+ if (!strcasecmp(s, names[i])) {
+ return i;
+ }
}
- } else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
- return i;
}
- return 0; /* error return */
+
+ /* Allow months and weekdays to be specified as numbers, as well */
+ if (sscanf(s, "%d", &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).
@@ -3082,44 +3086,42 @@ static int lookup_name(const char *s, char *const names[], int max)
*/
static unsigned get_range(char *src, int max, char *const names[], const char *msg)
{
- int s, e; /* start and ending position */
+ int start, end; /* start and ending position */
unsigned int mask = 0;
+ char *part;
/* Check for whole range */
if (ast_strlen_zero(src) || !strcmp(src, "*")) {
- s = 0;
- e = max - 1;
- } else {
+ return (1 << max) - 1;
+ }
+
+ while ((part = strsep(&src, "&"))) {
/* Get start and ending position */
- char *c = strchr(src, '-');
- if (c)
- *c++ = '\0';
+ char *endpart = strchr(part, '-');
+ if (endpart) {
+ *endpart++ = '\0';
+ }
/* Find the start */
- s = lookup_name(src, names, max);
- if (!s) {
- ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
- return 0;
+ if ((start = lookup_name(part, names, max)) < 0) {
+ ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
+ continue;
}
- s--;
- if (c) { /* find end of range */
- e = lookup_name(c, names, max);
- if (!e) {
- ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
- return 0;
+ 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;
}
- e--;
- } else
- e = s;
- }
- /* Fill the mask. Remember that ranges are cyclic */
- mask = 1 << e; /* initialize with last element */
- while (s != e) {
- if (s >= max) {
- s = 0;
- mask |= (1 << s);
} else {
- mask |= (1 << s);
- s++;
+ 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;
@@ -3128,85 +3130,60 @@ static unsigned get_range(char *src, int max, char *const names[], const char *m
/*! \brief store a bitmask of valid times, one bit each 2 minute */
static void get_timerange(struct ast_timing *i, char *times)
{
- char *e;
+ char *endpart, *part;
int x;
- int s1, s2;
- int e1, e2;
- /* int cth, ctm; */
+ 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));
- /* 2-minutes per bit, since the mask has only 32 bits :( */
+ /* 1-minute per bit */
/* Star is all times */
if (ast_strlen_zero(times) || !strcmp(times, "*")) {
- for (x=0; x<24; x++)
+ /* 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 */
- e = strchr(times, '-');
- if (!e) {
- ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
- return;
- }
- *e++ = '\0';
- /* XXX why skip non digits ? */
- while (*e && !isdigit(*e))
- e++;
- if (!*e) {
- ast_log(LOG_WARNING, "Invalid time range. Assuming no restrictions based on time.\n");
- return;
- }
- if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
- ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", times);
- return;
- }
- if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
- ast_log(LOG_WARNING, "%s isn't a time. Assuming no restrictions based on time.\n", e);
- return;
- }
- /* XXX this needs to be optimized */
-#if 1
- s1 = s1 * 30 + s2/2;
- if ((s1 < 0) || (s1 >= 24*30)) {
- ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
- return;
- }
- e1 = e1 * 30 + e2/2;
- if ((e1 < 0) || (e1 >= 24*30)) {
- ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
- return;
- }
- /* Go through the time and enable each appropriate bit */
- for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
- i->minmask[x/30] |= (1 << (x % 30));
- }
- /* Do the last one */
- i->minmask[x/30] |= (1 << (x % 30));
-#else
- for (cth=0; cth<24; cth++) {
- /* Initialize masks to blank */
- i->minmask[cth] = 0;
- for (ctm=0; ctm<30; ctm++) {
- if (
- /* First hour with more than one hour */
- (((cth == s1) && (ctm >= s2)) &&
- ((cth < e1)))
- /* Only one hour */
- || (((cth == s1) && (ctm >= s2)) &&
- ((cth == e1) && (ctm <= e2)))
- /* In between first and last hours (more than 2 hours) */
- || ((cth > s1) &&
- (cth < e1))
- /* Last hour with more than one hour */
- || ((cth > s1) &&
- ((cth == e1) && (ctm <= e2)))
- )
- i->minmask[cth] |= (1 << (ctm / 2));
+ while ((part = strsep(&times, "&"))) {
+ if (!(endpart = strchr(part, '-'))) {
+ if (sscanf(part, "%d:%d", &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, "%d:%d", &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, "%d:%d", &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));
}
-#endif
/* All done */
return;
}
@@ -4313,29 +4290,46 @@ char *months[] =
NULL,
};
-static 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_save[256];
- char *info;
+ char *info_save, *info;
+ int j, num_fields, last_sep = -1;
/* Check for empty just in case */
- if (ast_strlen_zero(info_in))
+ if (ast_strlen_zero(info_in)) {
return 0;
+ }
+
/* make a copy just in case we were passed a static string */
- ast_copy_string(info_save, info_in, sizeof(info_save));
- info = info_save;
+ info_save = 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);
+ } else {
+ i->timezone = NULL;
+ }
+
/* 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, "|"));
+ get_timerange(i, strsep(&info, "|,"));
if (info)
- i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
+ i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
if (info)
- i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
+ i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
if (info)
- i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
+ i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
return 1;
}
@@ -4488,12 +4482,12 @@ static int ext_strncpy(char *dst, const char *src, int len)
* Wrapper around _extension_match_core() to do performance measurement
* using the profiling code.
*/
-static int ast_check_timing(const struct ast_timing *i)
+int ast_check_timing(const struct ast_timing *i)
{
- struct tm tm;
- time_t t = time(NULL);
+ struct ast_tm tm;
+ struct timeval now = ast_tvnow();
- localtime_r(&t,&tm);
+ ast_localtime(&now, &tm, i->timezone);
/* If it's not the right month, return */
if (!(i->monthmask & (1 << tm.tm_mon)))
@@ -4516,7 +4510,7 @@ static int ast_check_timing(const struct ast_timing *i)
/* Now the tough part, we calculate if it fits
in the right time based on min/hour */
- if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
+ 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 */
@@ -5005,7 +4999,7 @@ static int ast_context_add_include2(struct ast_context *con, const char *value,
/* Strip off timing info, and process if it is there */
if ( (c = strchr(p, '|')) ) {
*c++ = '\0';
- new_include->hastime = ast_build_timing(&(new_include->timing), c);
+ new_include->hastime = ast_build_timing(&(new_include->timing), c);
}
new_include->next = NULL;
new_include->registrar = registrar;