| 
									
										
										
										
											2016-01-04 19:00:23 -05:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #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, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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++) { | 
					
						
							| 
									
										
										
										
											2021-08-02 19:33:07 +02:00
										 |  |  | 		if (info[j] == '|' || info[j] == ',') { | 
					
						
							| 
									
										
										
										
											2016-01-04 19:00:23 -05:00
										 |  |  | 			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; | 
					
						
							|  |  |  | } |