Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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  */
42 static 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  */
65 static 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 */
108 static 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 
168 static const char * const days[] =
169 {
170  "sun",
171  "mon",
172  "tue",
173  "wed",
174  "thu",
175  "fri",
176  "sat",
177  NULL,
178 };
179 
180 static 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 
197 /*! /brief Build timing
198  *
199  * /param i info
200  * /param info_in
201  *
202  */
203 int ast_build_timing(struct ast_timing *i, const char *info_in)
204 {
205  char *info;
206  int j, num_fields, last_sep = -1;
207 
208  i->timezone = NULL;
209 
210  /* Check for empty just in case */
211  if (ast_strlen_zero(info_in)) {
212  return 0;
213  }
214 
215  /* make a copy just in case we were passed a static string */
216  info = ast_strdupa(info_in);
217 
218  /* count the number of fields in the timespec */
219  for (j = 0, num_fields = 1; info[j] != '\0'; j++) {
220  if (info[j] == ',') {
221  last_sep = j;
222  num_fields++;
223  }
224  }
225 
226  /* save the timezone, if it is specified */
227  if (num_fields == 5) {
228  i->timezone = ast_strdup(info + last_sep + 1);
229  }
230 
231  /* Assume everything except time */
232  i->monthmask = 0xfff; /* 12 bits */
233  i->daymask = 0x7fffffffU; /* 31 bits */
234  i->dowmask = 0x7f; /* 7 bits */
235  /* on each call, use strsep() to move info to the next argument */
236  get_timerange(i, strsep(&info, "|,"));
237  if (info)
238  i->dowmask = get_range(strsep(&info, "|,"), 7, days, "day of week");
239  if (info)
240  i->daymask = get_range(strsep(&info, "|,"), 31, NULL, "day");
241  if (info)
242  i->monthmask = get_range(strsep(&info, "|,"), 12, months, "month");
243  return 1;
244 }
245 
246 int ast_check_timing(const struct ast_timing *i)
247 {
248  return ast_check_timing2(i, ast_tvnow());
249 }
250 
251 int ast_check_timing2(const struct ast_timing *i, const struct timeval tv)
252 {
253  struct ast_tm tm;
254 
255  ast_localtime(&tv, &tm, i->timezone);
256 
257  /* If it's not the right month, return */
258  if (!(i->monthmask & (1 << tm.tm_mon)))
259  return 0;
260 
261  /* If it's not that time of the month.... */
262  /* Warning, tm_mday has range 1..31! */
263  if (!(i->daymask & (1 << (tm.tm_mday-1))))
264  return 0;
265 
266  /* If it's not the right day of the week */
267  if (!(i->dowmask & (1 << tm.tm_wday)))
268  return 0;
269 
270  /* Sanity check the hour just to be safe */
271  if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
272  ast_log(LOG_WARNING, "Insane time...\n");
273  return 0;
274  }
275 
276  /* Now the tough part, we calculate if it fits
277  in the right time based on min/hour */
278  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))))
279  return 0;
280 
281  /* If we got this far, then we're good */
282  return 1;
283 }
284 
286 {
287  if (i->timezone) {
288  ast_free(i->timezone);
289  i->timezone = NULL;
290  }
291  return 0;
292 }
unsigned int daymask
Definition: pbx.h:174
Asterisk main include file. File version handling, generic pbx functions.
String manipulation functions.
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:203
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:251
#define LOG_WARNING
Definition: logger.h:274
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
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
char * end
Definition: eagi_proxy.c:73
unsigned int minmask[48]
Definition: pbx.h:176
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
unsigned int monthmask
Definition: pbx.h:173
Custom localtime functions for multiple timezones.
#define ast_log
Definition: astobj2.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
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:246
int tm_mon
Definition: localtime.h:40
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
int tm_mday
Definition: localtime.h:39
Core PBX routines and definitions.
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, is an array of names that should be mapped to numbers.
Definition: pbx_timing.c:65
static const char *const months[]
Definition: pbx_timing.c:180
unsigned int dowmask
Definition: pbx.h:175
def info(msg)
int ast_destroy_timing(struct ast_timing *i)
Deallocates memory structures associated with a timing bitmap.
Definition: pbx_timing.c:285
int tm_wday
Definition: localtime.h:42
#define ast_free(a)
Definition: astmm.h:182
static const char *const days[]
Definition: pbx_timing.c:168
int tm_hour
Definition: localtime.h:38
Support for logging to various files, console and syslog Configuration in file logger.conf.
char * strsep(char **str, const char *delims)
char * timezone
Definition: pbx.h:177
int tm_min
Definition: localtime.h:37
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
#define max(a, b)
Definition: f2c.h:198