[svn-commits] tilghman: trunk r164976 - in /trunk: ./ configs/ doc/ funcs/ include/asterisk...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Dec 16 16:57:18 CST 2008


Author: tilghman
Date: Tue Dec 16 16:57:17 2008
New Revision: 164976

URL: http://svn.digium.com/view/asterisk?view=rev&rev=164976
Log:
Add timezone to the possible fields in a timespec.
(closes issue #14028)
 Reported by: mostyn
 Patches: 
       timezone-v2.patch uploaded by mostyn (license 398)
       (with additional code guideline fixes and a memory leak fix by me - license 14)

Added:
    trunk/doc/api-1.6.2-changes.txt   (with props)
Modified:
    trunk/CHANGES
    trunk/configs/extensions.conf.sample
    trunk/funcs/func_logic.c
    trunk/include/asterisk/pbx.h
    trunk/main/pbx.c
    trunk/utils/extconf.c

Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=164976&r1=164975&r2=164976
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Tue Dec 16 16:57:17 2008
@@ -90,8 +90,16 @@
    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
 --------------------------

Modified: trunk/configs/extensions.conf.sample
URL: http://svn.digium.com/view/asterisk/trunk/configs/extensions.conf.sample?view=diff&rev=164976&r1=164975&r2=164976
==============================================================================
--- trunk/configs/extensions.conf.sample (original)
+++ trunk/configs/extensions.conf.sample Tue Dec 16 16:57:17 2008
@@ -185,7 +185,7 @@
 ;
 ; 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.

Added: trunk/doc/api-1.6.2-changes.txt
URL: http://svn.digium.com/view/asterisk/trunk/doc/api-1.6.2-changes.txt?view=auto&rev=164976
==============================================================================
--- trunk/doc/api-1.6.2-changes.txt (added)
+++ trunk/doc/api-1.6.2-changes.txt Tue Dec 16 16:57:17 2008
@@ -1,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.
+

Propchange: trunk/doc/api-1.6.2-changes.txt
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/doc/api-1.6.2-changes.txt
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/doc/api-1.6.2-changes.txt
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: trunk/funcs/func_logic.c
URL: http://svn.digium.com/view/asterisk/trunk/funcs/func_logic.c?view=diff&rev=164976&r1=164975&r2=164976
==============================================================================
--- trunk/funcs/func_logic.c (original)
+++ trunk/funcs/func_logic.c Tue Dec 16 16:57:17 2008
@@ -148,6 +148,7 @@
 
 	if (!ast_build_timing(&timing, expr)) {
 		ast_log(LOG_WARNING, "Invalid Time Spec.\n");
+		ast_destroy_timing(&timing);
 		return -1;
 	}
 
@@ -157,6 +158,7 @@
 		iffalse = ast_strip_quoted(iffalse, "\"", "\"");
 
 	ast_copy_string(buf, ast_check_timing(&timing) ? iftrue : iffalse, len);
+	ast_destroy_timing(&timing);
 
 	return 0;
 }

Modified: trunk/include/asterisk/pbx.h
URL: http://svn.digium.com/view/asterisk/trunk/include/asterisk/pbx.h?view=diff&rev=164976&r1=164975&r2=164976
==============================================================================
--- trunk/include/asterisk/pbx.h (original)
+++ trunk/include/asterisk/pbx.h Tue Dec 16 16:57:17 2008
@@ -119,10 +119,27 @@
 	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) */

Modified: trunk/main/pbx.c
URL: http://svn.digium.com/view/asterisk/trunk/main/pbx.c?view=diff&rev=164976&r1=164975&r2=164976
==============================================================================
--- trunk/main/pbx.c (original)
+++ trunk/main/pbx.c Tue Dec 16 16:57:17 2008
@@ -212,6 +212,7 @@
 				<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 @@
 				<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 @@
 			else
 				con->includes = i->next;
 			/* free include and return */
+			ast_destroy_timing(&(i->timing));
 			ast_free(i);
 			ret = 0;
 			break;
@@ -6793,15 +6796,32 @@
 
 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 @@
 	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 @@
 	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 @@
 	/* ... 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 @@
 	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 @@
 		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 @@
 	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 @@
 
 	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, '('))) {

Modified: trunk/utils/extconf.c
URL: http://svn.digium.com/view/asterisk/trunk/utils/extconf.c?view=diff&rev=164976&r1=164975&r2=164976
==============================================================================
--- trunk/utils/extconf.c (original)
+++ trunk/utils/extconf.c Tue Dec 16 16:57:17 2008
@@ -3061,20 +3061,24 @@
  * 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;
-		}
-	} else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
-		return i;
-	}
-	return 0; /* error return */
+			if (!strcasecmp(s, names[i])) {
+				return i;
+			}
+		}
+	}
+
+	/* 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 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;
-		}
-		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 ((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;
 			}
-			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 @@
 /*! \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));
-		}
-	}
-#endif
+	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));
+	}
 	/* All done */
 	return;
 }
@@ -4313,29 +4290,46 @@
 	NULL,
 };
 
-static int ast_build_timing(struct ast_timing *i, const char *info_in)
-{
-	char info_save[256];
-	char *info;
+int ast_build_timing(struct ast_timing *i, const char *info_in)
+{
+	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 @@
  * Wrapper around _extension_match_core() to do performance measurement
  * using the profiling code.
  */
-static int ast_check_timing(const struct ast_timing *i)
-{
-	struct tm tm;
-	time_t t = time(NULL);
-
-	localtime_r(&t,&tm);
+int ast_check_timing(const struct ast_timing *i)
+{
+	struct ast_tm tm;
+	struct timeval now = ast_tvnow();
+
+	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 @@
 
 	/* 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 @@
 	/* 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;




More information about the svn-commits mailing list