summaryrefslogtreecommitdiff
path: root/main/pbx_timing.c
diff options
context:
space:
mode:
authorCorey Farrell <git@cfware.com>2016-01-04 19:00:23 -0500
committerCorey Farrell <git@cfware.com>2016-01-05 12:06:23 -0500
commitc608274a39f9916ed72d7b3f8f971ac7e8218f32 (patch)
treea4414b49bda8d04886b991fc0fd306845272cb43 /main/pbx_timing.c
parent7fdcfd77247b461bfe7dd38c3a56a12f648534de (diff)
main/pbx: Move timing routines to pbx_timing.c.
This is the fourth patch in a series meant to reduce the bulk of pbx.c. This moves pbx timing functions to their own source. Change-Id: I05c45186cb11edfc901e95f6be4e6a8abf129cd6
Diffstat (limited to 'main/pbx_timing.c')
-rw-r--r--main/pbx_timing.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/main/pbx_timing.c b/main/pbx_timing.c
new file mode 100644
index 000000000..53529bb83
--- /dev/null
+++ b/main/pbx_timing.c
@@ -0,0 +1,294 @@
+/*
+ * 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(&times, "&"))) {
+ 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;
+}