Asterisk - The Open Source Telephony Project GIT-master-d856a3e
pbx_timing.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2016, CFWare, LLC
5 *
6 * Corey Farrell <git@cfware.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief PBX timing routines.
22 *
23 * \author Corey Farrell <git@cfware.com>
24 */
25
26/*** MODULEINFO
27 <support_level>core</support_level>
28 ***/
29
30#include "asterisk.h"
31
32#include "asterisk/localtime.h"
33#include "asterisk/logger.h"
34#include "asterisk/pbx.h"
35#include "asterisk/strings.h"
36#include "asterisk/utils.h"
37
38/*! \brief Helper for get_range.
39 * return the index of the matching entry, starting from 1.
40 * If names is not supplied, try numeric values.
41 */
42static int lookup_name(const char *s, const char * const names[], int max)
43{
44 int i;
45
46 if (names && *s > '9') {
47 for (i = 0; names[i]; i++) {
48 if (!strcasecmp(s, names[i])) {
49 return i;
50 }
51 }
52 }
53
54 /* Allow months and weekdays to be specified as numbers, as well */
55 if (sscanf(s, "%2d", &i) == 1 && i >= 1 && i <= max) {
56 /* What the array offset would have been: "1" would be at offset 0 */
57 return i - 1;
58 }
59 return -1; /* error return */
60}
61
62/*! \brief helper function to return a range up to max (7, 12, 31 respectively).
63 * names, if supplied, is an array of names that should be mapped to numbers.
64 */
65static unsigned get_range(char *src, int max, const char * const names[], const char *msg)
66{
67 int start, end; /* start and ending position */
68 unsigned int mask = 0;
69 char *part;
70
71 /* Check for whole range */
72 if (ast_strlen_zero(src) || !strcmp(src, "*")) {
73 return (1 << max) - 1;
74 }
75
76 while ((part = strsep(&src, "&"))) {
77 /* Get start and ending position */
78 char *endpart = strchr(part, '-');
79 if (endpart) {
80 *endpart++ = '\0';
81 }
82 /* Find the start */
83 if ((start = lookup_name(part, names, max)) < 0) {
84 ast_log(LOG_WARNING, "Invalid %s '%s', skipping element\n", msg, part);
85 continue;
86 }
87 if (endpart) { /* find end of range */
88 if ((end = lookup_name(endpart, names, max)) < 0) {
89 ast_log(LOG_WARNING, "Invalid end %s '%s', skipping element\n", msg, endpart);
90 continue;
91 }
92 } else {
93 end = start;
94 }
95 /* Fill the mask. Remember that ranges are cyclic */
96 mask |= (1 << end); /* initialize with last element */
97 while (start != end) {
98 mask |= (1 << start);
99 if (++start >= max) {
100 start = 0;
101 }
102 }
103 }
104 return mask;
105}
106
107/*! \brief store a bitmask of valid times, one bit each 1 minute */
108static void get_timerange(struct ast_timing *i, char *times)
109{
110 char *endpart, *part;
111 int x;
112 int st_h, st_m;
113 int endh, endm;
114 int minute_start, minute_end;
115
116 /* start disabling all times, fill the fields with 0's, as they may contain garbage */
117 memset(i->minmask, 0, sizeof(i->minmask));
118
119 /* 1-minute per bit */
120 /* Star is all times */
121 if (ast_strlen_zero(times) || !strcmp(times, "*")) {
122 /* 48, because each hour takes 2 integers; 30 bits each */
123 for (x = 0; x < 48; x++) {
124 i->minmask[x] = 0x3fffffff; /* 30 bits */
125 }
126 return;
127 }
128 /* Otherwise expect a range */
129 while ((part = strsep(&times, "&"))) {
130 if (!(endpart = strchr(part, '-'))) {
131 if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
132 ast_log(LOG_WARNING, "%s isn't a valid time.\n", part);
133 continue;
134 }
135 i->minmask[st_h * 2 + (st_m >= 30 ? 1 : 0)] |= (1 << (st_m % 30));
136 continue;
137 }
138 *endpart++ = '\0';
139 /* why skip non digits? Mostly to skip spaces */
140 while (*endpart && !isdigit(*endpart)) {
141 endpart++;
142 }
143 if (!*endpart) {
144 ast_log(LOG_WARNING, "Invalid time range starting with '%s-'.\n", part);
145 continue;
146 }
147 if (sscanf(part, "%2d:%2d", &st_h, &st_m) != 2 || st_h < 0 || st_h > 23 || st_m < 0 || st_m > 59) {
148 ast_log(LOG_WARNING, "'%s' isn't a valid start time.\n", part);
149 continue;
150 }
151 if (sscanf(endpart, "%2d:%2d", &endh, &endm) != 2 || endh < 0 || endh > 23 || endm < 0 || endm > 59) {
152 ast_log(LOG_WARNING, "'%s' isn't a valid end time.\n", endpart);
153 continue;
154 }
155 minute_start = st_h * 60 + st_m;
156 minute_end = endh * 60 + endm;
157 /* Go through the time and enable each appropriate bit */
158 for (x = minute_start; x != minute_end; x = (x + 1) % (24 * 60)) {
159 i->minmask[x / 30] |= (1 << (x % 30));
160 }
161 /* Do the last one */
162 i->minmask[x / 30] |= (1 << (x % 30));
163 }
164 /* All done */
165 return;
166}
167
168static const char * const days[] =
169{
170 "sun",
171 "mon",
172 "tue",
173 "wed",
174 "thu",
175 "fri",
176 "sat",
177 NULL,
178};
179
180static const char * const months[] =
181{
182 "jan",
183 "feb",
184 "mar",
185 "apr",
186 "may",
187 "jun",
188 "jul",
189 "aug",
190 "sep",
191 "oct",
192 "nov",
193 "dec",
194 NULL,
195};
196
197int ast_build_timing(struct ast_timing *i, const char *info_in)
198{
199 char *info;
200 int j, num_fields, last_sep = -1;
201
202 i->timezone = NULL;
203
204 /* Check for empty just in case */
205 if (ast_strlen_zero(info_in)) {
206 return 0;
207 }
208
209 /* make a copy just in case we were passed a static string */
210 info = ast_strdupa(info_in);
211
212 /* count the number of fields in the timespec */
213 for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
214 if (info[j] == '|' || info[j] == ',') {
215 last_sep = j;
216 num_fields++;
217 }
218 }
219
220 /* save the timezone, if it is specified */
221 if (num_fields == 5) {
222 i->timezone = ast_strdup(info + last_sep + 1);
223 }
224
225 /* Assume everything except time */
226 i->monthmask = 0xfff; /* 12 bits */
227 i->daymask = 0x7fffffffU; /* 31 bits */
228 i->dowmask = 0x7f; /* 7 bits */
229 /* on each call, use strsep() to move info to the next argument */
230 get_timerange(i, strsep(&info, "|,"));
231 if (info)
232 i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
233 if (info)
234 i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
235 if (info)
236 i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
237 return 1;
238}
239
240int ast_check_timing(const struct ast_timing *i)
241{
242 return ast_check_timing2(i, ast_tvnow());
243}
244
245int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
246{
247 struct ast_tm tm;
248
249 ast_localtime(&tv, &tm, i->timezone);
250
251 /* If it's not the right month, return */
252 if (!(i->monthmask & (1 << tm.tm_mon)))
253 return 0;
254
255 /* If it's not that time of the month.... */
256 /* Warning, tm_mday has range 1..31! */
257 if (!(i->daymask & (1 << (tm.tm_mday-1))))
258 return 0;
259
260 /* If it's not the right day of the week */
261 if (!(i->dowmask & (1 << tm.tm_wday)))
262 return 0;
263
264 /* Sanity check the hour just to be safe */
265 if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
266 ast_log(LOG_WARNING, "Insane time...\n");
267 return 0;
268 }
269
270 /* Now the tough part, we calculate if it fits
271 in the right time based on min/hour */
272 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))))
273 return 0;
274
275 /* If we got this far, then we're good */
276 return 1;
277}
278
280{
281 if (i->timezone) {
282 ast_free(i->timezone);
283 i->timezone = NULL;
284 }
285 return 0;
286}
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
char * end
Definition: eagi_proxy.c:73
#define max(a, b)
Definition: f2c.h:198
char * strsep(char **str, const char *delims)
Support for logging to various files, console and syslog Configuration in file logger....
#define LOG_WARNING
Custom localtime functions for multiple timezones.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
def info(msg)
Core PBX routines and definitions.
int ast_build_timing(struct ast_timing *i, const char *info_in)
Construct a timing bitmap, for use in time-based conditionals.
Definition: pbx_timing.c:197
int ast_check_timing(const struct ast_timing *i)
Evaluate a pre-constructed bitmap as to whether the current time falls within the range specified.
Definition: pbx_timing.c:240
int ast_destroy_timing(struct ast_timing *i)
Deallocates memory structures associated with a timing bitmap.
Definition: pbx_timing.c:279
int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
Evaluate a pre-constructed bitmap as to whether a particular time falls within the range specified.
Definition: pbx_timing.c:245
static int lookup_name(const char *s, const char *const names[], int max)
Helper for get_range. return the index of the matching entry, starting from 1. If names is not suppli...
Definition: pbx_timing.c:42
static void get_timerange(struct ast_timing *i, char *times)
store a bitmask of valid times, one bit each 1 minute
Definition: pbx_timing.c:108
static const char *const months[]
Definition: pbx_timing.c:180
static const char *const days[]
Definition: pbx_timing.c:168
static unsigned get_range(char *src, int max, const char *const names[], const char *msg)
helper function to return a range up to max (7, 12, 31 respectively). names, if supplied,...
Definition: pbx_timing.c:65
#define NULL
Definition: resample.c:96
String manipulation functions.
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
unsigned int monthmask
Definition: pbx.h:173
unsigned int daymask
Definition: pbx.h:174
unsigned int minmask[48]
Definition: pbx.h:176
char * timezone
Definition: pbx.h:177
unsigned int dowmask
Definition: pbx.h:175
int tm_mday
Definition: localtime.h:39
int tm_wday
Definition: localtime.h:42
int tm_hour
Definition: localtime.h:38
int tm_min
Definition: localtime.h:37
int tm_mon
Definition: localtime.h:40
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
Utility functions.