ipa_timerules.c revision b9f94bc83f139df3e143cc020b98e6e652887049
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen/*
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen SSSD
2fbc2a7c65d30e46803195ebb4547176b85c22c7Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen IPA Provider Time Rules Parsing
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen Authors:
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen Jakub Hrozek <jhrozek@redhat.com>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen Copyright (C) Red Hat, Inc 2009
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen This program is free software; you can redistribute it and/or modify
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen it under the terms of the GNU General Public License as published by
3b94ff5951db4d4eddb7a80ed4e3f61207202635Timo Sirainen the Free Software Foundation; either version 3 of the License, or
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen (at your option) any later version.
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen This program is distributed in the hope that it will be useful,
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen but WITHOUT ANY WARRANTY; without even the implied warranty of
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0373a24e97602b4c394e93a70b75f45e5869fa51Timo Sirainen GNU General Public License for more details.
22535a9e685e29214082878e37a267157044618eTimo Sirainen
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen You should have received a copy of the GNU General Public License
22535a9e685e29214082878e37a267157044618eTimo Sirainen along with this program. If not, see <http://www.gnu.org/licenses/>.
22535a9e685e29214082878e37a267157044618eTimo Sirainen*/
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen#define _XOPEN_SOURCE /* strptime() needs this */
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen#include <pcre.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <talloc.h>
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen#include <stdio.h>
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen#include <string.h>
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen#include <stdlib.h>
9f431ccfb6932746db56245c8a3d3415717ef545Timo Sirainen#include <errno.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <time.h>
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen#include <stdbool.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include <limits.h>
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "providers/ipa/ipa_timerules.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#include "util/util.h"
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#define JMP_NEOK(variable) do { \
63c6eefc07085070733e702208101662bc5ccb45Timo Sirainen if (variable != EOK) goto done; \
63c6eefc07085070733e702208101662bc5ccb45Timo Sirainen} while (0)
63c6eefc07085070733e702208101662bc5ccb45Timo Sirainen
d7f46a941a12f70e49857598b4e3463dadfc12d9Timo Sirainen#define JMP_NEOK_LABEL(variable, label) do { \
63c6eefc07085070733e702208101662bc5ccb45Timo Sirainen if (variable != EOK) goto label; \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen} while (0)
63c6eefc07085070733e702208101662bc5ccb45Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#define CHECK_PTR(ptr) do { \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (ptr == NULL) { \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen return ENOMEM; \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen } \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen} while (0)
965ed6ea3fc8f7637bd0d159d2fdb283a191ce34Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#define CHECK_PTR_JMP(ptr) do { \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (ptr == NULL) { \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen ret = ENOMEM; \
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen goto done; \
66d2db642fe24d555d113ba463e446b038d476efTimo Sirainen } \
1f1e81aab38d833d1c9cdc244c91fd762e0080d4Timo Sirainen} while (0)
08aea01ef9a9d20703e0fcf8618e6195c0037a44Timo Sirainen
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen#define BUFFER_OR_JUMP(ctx, ptr, count) do { \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen ptr = talloc_array(ctx, unsigned char, count); \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen if (ptr == NULL) { \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen return ENOMEM; \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen } \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen memset(ptr, 0, sizeof(unsigned char)*count); \
25757faf029c369a8318349dafe952e2358df1d8Timo Sirainen} while (0)
#define TEST_BIT_RANGE(bitfield, index, resptr) do { \
if (bitfield) { \
if (test_bit(&bitfield, index) == 0) { \
*resptr = false; \
return EOK; \
} \
} \
} while (0)
#define TEST_BIT_RANGE_PTR(bitfield, index, resptr) do { \
if (bitfield) { \
if (test_bit(bitfield, index) == 0) { \
*resptr = false; \
return EOK; \
} \
} \
} while (0)
/* number of match offsets when matching pcre regexes */
#define OVEC_SIZE 30
/* regular expressions describing syntax of our HBAC grammar */
#define RGX_WEEKLY "day (?P<day_of_week>(0|1|2|3|4|5|6|7|Mon|Tue|Wed|Thu|Fri|Sat|Sun|,|-)+)"
#define RGX_MDAY "(?P<mperspec_day>day) (?P<interval_day>[0-9,-]+) "
#define RGX_MWEEK "(?P<mperspec_week>week) (?P<interval_week>[0-9,-]+) "RGX_WEEKLY
#define RGX_MONTHLY RGX_MDAY"|"RGX_MWEEK
#define RGX_YDAY "(?P<yperspec_day>day) (?P<day_of_year>[0-9,-]+) "
#define RGX_YWEEK "(?P<yperspec_week>week) (?P<week_of_year>[0-9,-]+) "RGX_WEEKLY
#define RGX_YMONTH "(?P<yperspec_month>month) (?P<month_number>[0-9,-]+) (?P<m_period>.*?)$"
#define RGX_YEARLY RGX_YMONTH"|"RGX_YWEEK"|"RGX_YDAY
#define RGX_TIMESPEC "(?P<timeFrom>[0-9]{4}) ~ (?P<timeTo>[0-9]{4})"
#define RGX_GENERALIZED "(?P<year>[0-9]{4})(?P<month>[0-9]{2})(?P<day>[0-9]{2})(?P<hour>[0-9]{2})?(?P<minute>[0-9]{2})?(?P<second>[0-9]{2})?"
#define RGX_PERIODIC "^periodic (?P<perspec>daily|weekly|monthly|yearly) (?P<period>.*?)"RGX_TIMESPEC"$"
#define RGX_ABSOLUTE "^absolute (?P<from>\\S+) ~ (?P<to>\\S+)$"
/* limits on various parameters */
#define DAY_OF_WEEK_MAX 7
#define DAY_OF_MONTH_MAX 31
#define WEEK_OF_MONTH_MAX 5
#define WEEK_OF_YEAR_MAX 54
#define DAY_OF_YEAR_MAX 366
#define MONTH_MAX 12
#define HOUR_MAX 23
#define MINUTE_MAX 59
/* limits on sizes of buffers for bit arrays */
#define DAY_OF_MONTH_BUFSIZE 8
#define DAY_OF_YEAR_BUFSIZE 44
#define WEEK_OF_YEAR_BUFSIZE 13
#define MONTH_BUFSIZE 2
#define HOUR_BUFSIZE 4
#define MINUTE_BUFSIZE 8
/* Lookup tables for translating names of days and months */
static const char *names_day_of_week[] =
{ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", NULL };
static const char *names_months[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Nov", "Dec", NULL };
/*
* Timelib knows two types of ranges - periodic and absolute
*/
enum rangetypes {
TYPE_ABSOLUTE,
TYPE_PERIODIC
};
struct absolute_range {
time_t time_from;
time_t time_to;
};
struct periodic_range {
unsigned char day_of_week;
unsigned char *day_of_month;
unsigned char *day_of_year;
unsigned char week_of_month;
unsigned char *week_of_year;
unsigned char *month;
unsigned char *hour;
unsigned char *minute;
};
/*
* Context of one time rule being analyzed
*/
struct range_ctx {
/* main context with precompiled patterns */
struct time_rules_ctx *trctx;
/* enum rangetypes */
enum rangetypes type;
struct absolute_range *abs;
struct periodic_range *per;
};
/*
* The context of one regular expression
*/
struct parse_ctx {
/* the regular expression used for one parsing */
pcre *re;
/* number of matches */
int matches;
/* vector of matches */
int *ovec;
};
/* indexes to the array of precompiled regexes */
enum timelib_rgx {
LP_RGX_GENERALIZED,
LP_RGX_MDAY,
LP_RGX_MWEEK,
LP_RGX_YEARLY,
LP_RGX_WEEKLY,
LP_RGX_ABSOLUTE,
LP_RGX_PERIODIC,
LP_RGX_MAX,
};
/* matches the indexes */
static const char *lookup_table[] = {
RGX_GENERALIZED,
RGX_MDAY,
RGX_MWEEK,
RGX_YEARLY,
RGX_WEEKLY,
RGX_ABSOLUTE,
RGX_PERIODIC,
NULL,
};
/*
* Main struct passed outside
* holds precompiled regular expressions
*/
struct time_rules_ctx {
pcre *re[LP_RGX_MAX];
};
/*******************************************************************
* helper function - bit arrays *
*******************************************************************/
/* set a single bit in a bitmap */
static void set_bit(unsigned char *bitmap, unsigned int bit)
{
bitmap[bit/CHAR_BIT] |= 1 << (bit%CHAR_BIT);
}
/*
* This function is based on bit_nset macro written originally by Paul Vixie,
* copyrighted by The Regents of the University of California, as found
* in tarball of fcron, file bitstring.h
*/
static void set_bit_range(unsigned char *bitmap, unsigned int start,
unsigned int stop)
{
int startbyte = start/CHAR_BIT;
int stopbyte = stop/CHAR_BIT;
if (startbyte == stopbyte) {
bitmap[startbyte] |= ((0xff << (start & 0x7)) &
(0xff >> (CHAR_BIT- 1 - (stop & 0x7))));
} else {
bitmap[startbyte] |= 0xff << (start & 0x7);
while (++startbyte < stopbyte) {
bitmap[startbyte] |= 0xff;
}
bitmap[stopbyte] |= 0xff >> (CHAR_BIT- 1 - (stop & 0x7));
}
}
static int test_bit(unsigned char *bitmap, unsigned int bit)
{
return (int)(bitmap[bit/CHAR_BIT] >> (bit%CHAR_BIT)) & 1;
}
/*******************************************************************
* parsing intervals *
*******************************************************************/
/*
* Some ranges allow symbolic names, like Mon..Sun for names of day.
* This routine takes a list of symbolic names as NAME_ARRAY and the
* one we're looking for as KEY and returns its index or -1 when not
* found. The last member of NAME_ARRAY must be NULL.
*/
static int name_index(const char **name_array, const char *key, int min)
{
int index = 0;
const char *one;
if (name_array == NULL) {
return -1;
}
while ((one = name_array[index]) != NULL) {
if (strcmp(key,one) == 0) {
return index+min;
}
index++;
}
return -1;
}
/*
* Sets appropriate bits given by an interval in STR (in form of 1,5-7,10) to
* a bitfield given in OUT. Does no boundary checking. STR can also contain
* symbolic names, these would be given in TRANSLATE.
*/
static int interval2bitfield(TALLOC_CTX *mem_ctx,
unsigned char *out,
const char *str,
int min, int max,
const char **translate)
{
char *copy;
char *next, *token;
int tokval, tokmax;
char *end_ptr;
int ret;
char *dash;
DEBUG(9, ("Converting '%s' to interval\n", str));
copy = talloc_strdup(mem_ctx, str);
CHECK_PTR(copy);
next = copy;
while (next) {
token = next;
next = strchr(next, ',');
if (next) {
*next = '\0';
next++;
}
errno = 0;
tokval = strtol(token, &end_ptr, 10);
if (*end_ptr == '\0' && errno == 0) {
if (tokval <= max && tokval >= 0) {
set_bit(out, tokval);
continue;
} else {
ret = ERANGE;
goto done;
}
} else if ((dash = strchr(token, '-')) != NULL){
*dash = '\0';
++dash;
errno = 0;
tokval = strtol(token, &end_ptr, 10);
if (*end_ptr != '\0' || errno != 0) {
tokval = name_index(translate, token, min);
if (tokval == -1) {
ret = ERANGE;
goto done;
}
}
errno = 0;
tokmax = strtol(dash, &end_ptr, 10);
if (*end_ptr != '\0' || errno != 0) {
tokmax = name_index(translate, dash, min);
if (tokmax == -1) {
ret = ERANGE;
goto done;
}
}
if (tokval <= max && tokmax <= max &&
tokval >= min && tokmax >= min) {
if (tokmax > tokval) {
DEBUG(7, ("Setting interval %d-%d\n", tokval, tokmax));
DEBUG(9, ("interval: %p\n", out));
set_bit_range(out, tokval, tokmax);
} else {
/* Interval wraps around - i.e. from 18.00 to 06.00 */
DEBUG(7, ("Setting inverted interval %d-%d\n", tokval, tokmax));
DEBUG(9, ("interval: %p\n", out));
set_bit_range(out, min, tokmax);
set_bit_range(out, tokval, max);
}
continue;
} else {
/* tokval or tokmax are not between <min, max> */
ret = ERANGE;
goto done;
}
} else if ((tokval = name_index(translate, token, min)) != -1) {
/* Try to translate one token by name */
if (tokval <= max) {
set_bit(out, tokval);
continue;
} else {
ret = ERANGE;
goto done;
}
} else {
ret = EINVAL;
goto done;
}
}
ret = EOK;
done:
talloc_free(copy);
return ret;
}
/*******************************************************************
* wrappers around regexp handling *
*******************************************************************/
/*
* Copies a named substring SUBSTR_NAME from string STR using the parsing
* information from PCTX. The context PCTX is also used as a talloc context.
*
* The resulting string is stored in OUT.
* Return value is EOK on no error or ENOENT on error capturing the substring
*/
static int copy_substring(struct parse_ctx *pctx,
const char *str,
const char *substr_name,
char **out)
{
const char *result = NULL;
int ret;
char *o = NULL;
result = NULL;
ret = pcre_get_named_substring(pctx->re, str, pctx->ovec,
pctx->matches, substr_name, &result);
if (ret < 0 || result == NULL) {
DEBUG(5, ("named substring '%s' does not exist in '%s'\n",
substr_name, str));
return ENOENT;
}
o = talloc_strdup(pctx, result);
pcre_free_substring(result);
if (o == NULL) {
return ENOMEM;
}
DEBUG(9, ("Copied substring named '%s' value '%s'\n", substr_name, o));
*out = o;
return EOK;
}
/*
* Copies a named substring SUBSTR_NAME from string STR using the parsing
* information from PCTX and converts it to an integer.
* The context PCTX is also used as a talloc context.
*
* The resulting string is stored in OUT.
* Return value is EOK on no error or ENOENT on error capturing the substring
*/
static int substring_strtol(struct parse_ctx *pctx,
const char *str,
const char *substr_name,
int *out)
{
char *substr = NULL;
int ret;
int val;
char *err_ptr;
ret = copy_substring(pctx, str, substr_name, &substr);
if (ret != EOK) {
DEBUG(5, ("substring '%s' does not exist\n", substr_name));
return ret;
}
errno = 0;
val = strtol(substr, &err_ptr, 10);
if (substr == '\0' || *err_ptr != '\0' || errno != 0) {
DEBUG(5, ("substring '%s' does not contain an integerexist\n",
substr));
talloc_free(substr);
return EINVAL;
}
*out = val;
talloc_free(substr);
return EOK;
}
/*
* Compiles a regular expression REGEXP and tries to match it against the
* string STR. Fills in structure _PCTX with info about matching.
*
* Returns EOK on no error, EFAULT on bad regexp, EINVAL when it cannot
* match the regexp.
*/
static int matches_regexp(TALLOC_CTX *ctx,
struct time_rules_ctx *trctx,
const char *str,
enum timelib_rgx regex,
struct parse_ctx **_pctx)
{
int ret;
struct parse_ctx *pctx = NULL;
pctx = talloc_zero(ctx, struct parse_ctx);
CHECK_PTR(pctx);
pctx->ovec = talloc_array(pctx, int, OVEC_SIZE);
CHECK_PTR_JMP(pctx->ovec);
pctx->re = trctx->re[regex];
ret = pcre_exec(pctx->re, NULL, str, strlen(str), 0, PCRE_NOTEMPTY, pctx->ovec, OVEC_SIZE);
if (ret <= 0) {
DEBUG(8, ("string '%s' did *NOT* match regexp '%s'\n", str, lookup_table[regex]));
ret = EINVAL;
goto done;
}
DEBUG(8, ("string '%s' matched regexp '%s'\n", str, lookup_table[regex]));
pctx->matches = ret;
*_pctx = pctx;
return EOK;
done:
talloc_free(pctx);
return ret;
}
/*******************************************************************
* date/time helper functions *
*******************************************************************/
/*
* Returns week number as an integer
* This may seem ugly, but I think it's actually less error prone
* than writing my own routine
*/
static int weeknum(const struct tm *t)
{
char buf[3];
if (!strftime(buf, 3, "%U", t)) {
return -1;
}
/* %U returns 0-53, we want 1-54 */
return atoi(buf)+1;
}
/*
* Return the week of the month
* Range is 1 to 5
*/
static int get_week_of_month(const struct tm *t)
{
int fs; /* first sunday */
fs = (t->tm_mday % 7) - t->tm_wday;
if (fs <= 0) {
fs += 7;
}
return (t->tm_mday <= fs) ? 1 : (2 + (t->tm_mday - fs - 1) / 7);
}
/*
* Normalize differencies between our HBAC definition and semantics of
* struct tm
*/
static void abs2tm(struct tm *t)
{
/* tm defines tm_year as num of yrs since 1900, we have absolute number */
t->tm_year %= 1900;
/* struct tm defines tm_mon as number of month since January */
t->tm_mon--;
}
/*
* Normalize differencies between our HBAC definition and semantics of
* struct tm
*/
static void tm2abs(struct tm *t)
{
/* tm defines tm_year as num of yrs since 1900, we have absolute number */
t->tm_year += 1900;
/* struct tm defines tm_mon as number of month since January */
t->tm_mon++;
}
/*******************************************************************
* parsing of HBAC rules themselves *
*******************************************************************/
/*
* Parses generalized time string given in STR and fills the
* information into OUT.
*/
static int parse_generalized_time(struct parse_ctx *pctx,
struct time_rules_ctx *trctx,
const char *str,
time_t *out)
{
int ret;
struct parse_ctx *gctx = NULL;
struct tm tm;
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1;
ret = matches_regexp(pctx, trctx, str, LP_RGX_GENERALIZED, &gctx);
JMP_NEOK(ret);
/* compulsory */
ret = substring_strtol(gctx, str, "year", &tm.tm_year);
JMP_NEOK(ret);
ret = substring_strtol(gctx, str, "month", &tm.tm_mon);
JMP_NEOK(ret);
ret = substring_strtol(gctx, str, "day", &tm.tm_mday);
JMP_NEOK(ret);
/* optional */
ret = substring_strtol(gctx, str, "hour", &tm.tm_hour);
JMP_NEOK_LABEL(ret, enoent);
ret = substring_strtol(gctx, str, "minute", &tm.tm_min);
JMP_NEOK_LABEL(ret, enoent);
ret = substring_strtol(gctx, str, "second", &tm.tm_sec);
JMP_NEOK_LABEL(ret, enoent);
enoent:
if (ret == ENOENT) {
ret = EOK;
}
abs2tm(&tm);
*out = mktime(&tm);
DEBUG(3, ("converted to time: '%s'\n", ctime(out)));
if (*out == -1) {
ret = EINVAL;
}
done:
talloc_free(gctx);
return ret;
}
/*
* Parses absolute timerange string given in STR and fills the
* information into ABS.
*/
static int parse_absolute(struct absolute_range *absr,
struct time_rules_ctx *trctx,
struct parse_ctx *pctx,
const char *str)
{
char *from = NULL, *to = NULL;
int ret;
ret = copy_substring(pctx, str, "from", &from);
if (ret != EOK) {
DEBUG(1, ("Missing required part 'from' in absolute timespec\n"));
ret = EINVAL;
goto done;
}
ret = copy_substring(pctx, str, "to", &to);
if (ret != EOK) {
DEBUG(1, ("Missing required part 'to' in absolute timespec\n"));
ret = EINVAL;
goto done;
}
ret = parse_generalized_time(pctx, trctx, from, &absr->time_from);
if (ret != EOK) {
DEBUG(1, ("Cannot parse generalized time - first part\n"));
goto done;
}
ret = parse_generalized_time(pctx, trctx, to, &absr->time_to);
if (ret != EOK) {
DEBUG(1, ("Cannot parse generalized time - second part\n"));
goto done;
}
if (difftime(absr->time_to, absr->time_from) < 0) {
DEBUG(1, ("Not a valid interval\n"));
ret = EINVAL;
}
ret = EOK;
done:
talloc_free(from);
talloc_free(to);
return ret;
}
static int parse_hhmm(const char *str, int *hour, int *min)
{
struct tm t;
char *err;
err = strptime(str, "%H%M", &t);
if (*err != '\0') {
return EINVAL;
}
*hour = t.tm_hour;
*min = t.tm_min;
return EOK;
}
/*
* Parses monthly periodic timerange given in STR.
* Fills the information into PER.
*/
static int parse_periodic_monthly(TALLOC_CTX *ctx,
struct time_rules_ctx *trctx,
struct periodic_range *per,
const char *str)
{
int ret;
struct parse_ctx *mpctx = NULL;
char *match = NULL;
char *mperspec = NULL;
/* This code would be much less ugly if RHEL5 PCRE knew about PCRE_DUPNAMES */
ret = matches_regexp(ctx, trctx, str, LP_RGX_MDAY, &mpctx);
if (ret == EOK) {
ret = copy_substring(mpctx, str, "mperspec_day", &mperspec);
JMP_NEOK(ret);
ret = copy_substring(mpctx, str, "interval_day", &match);
JMP_NEOK(ret);
BUFFER_OR_JUMP(ctx, per->day_of_month, DAY_OF_MONTH_BUFSIZE);
ret = interval2bitfield(mpctx, per->day_of_month, match,
1, DAY_OF_MONTH_MAX, NULL);
JMP_NEOK(ret);
} else {
ret = matches_regexp(ctx, trctx, str, LP_RGX_MWEEK, &mpctx);
JMP_NEOK(ret);
ret = copy_substring(mpctx, str, "mperspec_week", &mperspec);
JMP_NEOK(ret);
ret = copy_substring(mpctx, str, "interval_week", &match);
JMP_NEOK(ret);
ret = interval2bitfield(mpctx, &per->week_of_month, match,
1, WEEK_OF_MONTH_MAX, NULL);
JMP_NEOK(ret);
ret = copy_substring(mpctx, str, "day_of_week", &match);
JMP_NEOK(ret);
ret = interval2bitfield(mpctx, &per->day_of_week, match,
1, DAY_OF_WEEK_MAX, names_day_of_week);
JMP_NEOK(ret);
}
done:
talloc_free(mpctx);
return ret;
}
/*
* Parses yearly periodic timerange given in STR.
* Fills the information into PER.
*/
static int parse_periodic_yearly(TALLOC_CTX *ctx,
struct time_rules_ctx *trctx,
struct periodic_range *per,
const char *str)
{
int ret;
struct parse_ctx *ypctx = NULL;
char *match = NULL;
char *yperspec = NULL;
ret = matches_regexp(ctx, trctx, str, LP_RGX_YEARLY, &ypctx);
JMP_NEOK(ret);
ret = copy_substring(ypctx, str, "yperspec_day", &yperspec);
if (ret == EOK) {
ret = copy_substring(ypctx, str, "day_of_year", &match);
JMP_NEOK(ret);
BUFFER_OR_JUMP(ypctx, per->day_of_year, DAY_OF_YEAR_BUFSIZE);
ret = interval2bitfield(ypctx, per->day_of_year, match,
1, DAY_OF_YEAR_MAX, NULL);
JMP_NEOK(ret);
}
if (ret != ENOENT) goto done;
ret = copy_substring(ypctx, str, "yperspec_week", &yperspec);
if (ret == EOK) {
ret = copy_substring(ypctx, str, "week_of_year", &match);
JMP_NEOK(ret);
BUFFER_OR_JUMP(ypctx, per->week_of_year, WEEK_OF_YEAR_BUFSIZE);
ret = interval2bitfield(ypctx, per->week_of_year, match,
1, WEEK_OF_YEAR_MAX, NULL);
JMP_NEOK(ret);
talloc_free(match);
ret = copy_substring(ypctx, str, "day_of_week", &match);
JMP_NEOK(ret);
ret = interval2bitfield(ypctx, &per->day_of_week, match,
1, DAY_OF_WEEK_MAX, names_day_of_week);
JMP_NEOK(ret);
}
if (ret != ENOENT) goto done;
ret = copy_substring(ypctx, str, "yperspec_month", &yperspec);
JMP_NEOK(ret);
talloc_free(match);
ret = copy_substring(ypctx, str, "month_number", &match);
JMP_NEOK(ret);
BUFFER_OR_JUMP(ypctx, per->month, MONTH_BUFSIZE);
ret = interval2bitfield(ypctx, per->month, match,
1, MONTH_MAX, names_months);
JMP_NEOK(ret);
talloc_free(match);
ret = copy_substring(ypctx, str, "m_period", &match);
JMP_NEOK(ret);
DEBUG(7, ("Monthly year period - calling parse_periodic_monthly()\n"));
ret = parse_periodic_monthly(ypctx, trctx, per, match);
JMP_NEOK(ret);
done:
talloc_free(ypctx);
return ret;
}
/*
* Parses weekly periodic timerange given in STR.
* Fills the information into PER.
*/
static int parse_periodic_weekly(TALLOC_CTX *ctx,
struct time_rules_ctx *trctx,
struct periodic_range *per,
const char *str)
{
int ret;
struct parse_ctx *wpctx = NULL;
char *dow = NULL;
ret = matches_regexp(ctx, trctx, str, LP_RGX_WEEKLY, &wpctx);
JMP_NEOK(ret);
ret = copy_substring(wpctx, str, "day_of_week", &dow);
JMP_NEOK(ret);
DEBUG(8, ("day_of_week = '%s'\n", dow));
ret = interval2bitfield(wpctx, &per->day_of_week, dow,
1, DAY_OF_WEEK_MAX, names_day_of_week);
done:
talloc_free(wpctx);
return ret;
}
static int parse_periodic_time(struct periodic_range *per,
struct parse_ctx *pctx,
const char *str)
{
char *substr = NULL;
int ret;
int hour_from;
int hour_to;
int min_from;
int min_to;
/* parse out the time */
ret = copy_substring(pctx, str, "timeFrom", &substr);
JMP_NEOK(ret);
parse_hhmm(substr, &hour_from, &min_from);
DEBUG(7, ("Parsed timeFrom: %d:%d\n", hour_from, min_from));
JMP_NEOK(ret);
talloc_free(substr);
ret = copy_substring(pctx, str, "timeTo", &substr);
JMP_NEOK(ret);
parse_hhmm(substr, &hour_to, &min_to);
DEBUG(7, ("Parsed timeTo: %d:%d\n", hour_to, min_to));
JMP_NEOK(ret);
/* set the interval */
if (hour_from > hour_to ) {
set_bit_range(per->hour, 0, hour_to);
set_bit_range(per->hour, hour_from, HOUR_MAX);
} else {
set_bit_range(per->hour, hour_from, hour_to);
}
if (min_from > min_to) {
set_bit_range(per->minute, 0, min_to);
set_bit_range(per->minute, min_from, MINUTE_MAX);
} else {
set_bit_range(per->minute, min_from, min_to);
}
ret = EOK;
done:
talloc_free(substr);
return ret;
}
/*
* Parses periodic timerange given in STR.
* Fills the information into PER.
*/
static int parse_periodic(struct periodic_range *per,
struct time_rules_ctx *trctx,
struct parse_ctx *pctx,
const char *str)
{
char *substr = NULL;
char *period = NULL;
int ret;
/* These are mandatory */
BUFFER_OR_JUMP(per, per->hour, HOUR_BUFSIZE);
BUFFER_OR_JUMP(per, per->minute, MINUTE_BUFSIZE);
ret = copy_substring(pctx, str, "perspec", &substr);
JMP_NEOK(ret);
ret = copy_substring(pctx, str, "period", &period);
JMP_NEOK(ret);
if (strcmp(substr, "yearly") == 0) {
DEBUG(5, ("periodic yearly\n"));
ret = parse_periodic_yearly(pctx, trctx, per, period);
JMP_NEOK(ret);
} else if (strcmp(substr, "monthly") == 0) {
DEBUG(5, ("periodic monthly\n"));
ret = parse_periodic_monthly(pctx, trctx, per, period);
JMP_NEOK(ret);
} else if (strcmp(substr, "weekly") == 0) {
DEBUG(5, ("periodic weekly\n"));
ret = parse_periodic_weekly(pctx, trctx, per, period);
JMP_NEOK(ret);
} else if (strcmp(substr, "daily") == 0) {
DEBUG(5, ("periodic daily\n"));
} else {
DEBUG(1, ("Cannot determine periodic rule type"
"(perspec = '%s', period = '%s')\n", substr, period));
ret = EINVAL;
goto done;
}
talloc_free(period);
ret = parse_periodic_time(per, pctx, str);
JMP_NEOK(ret);
ret = EOK;
done:
talloc_free(substr);
return ret;
}
/*
* Parses time specification given in string RULE into range_ctx
* context CTX.
*/
static int parse_timespec(struct range_ctx *ctx, const char *rule)
{
int ret;
struct parse_ctx *pctx = NULL;
if (matches_regexp(ctx, ctx->trctx, rule, LP_RGX_ABSOLUTE, &pctx) == EOK) {
DEBUG(5, ("Matched absolute range\n"));
ctx->type = TYPE_ABSOLUTE;
ctx->abs = talloc_zero(ctx, struct absolute_range);
CHECK_PTR_JMP(ctx->abs);
ret = parse_absolute(ctx->abs, ctx->trctx, pctx, rule);
JMP_NEOK(ret);
} else if (matches_regexp(ctx, ctx->trctx, rule, LP_RGX_PERIODIC, &pctx) == EOK) {
DEBUG(5, ("Matched periodic range\n"));
ctx->type = TYPE_PERIODIC;
ctx->per = talloc_zero(ctx, struct periodic_range);
CHECK_PTR_JMP(ctx->per);
ret = parse_periodic(ctx->per, ctx->trctx, pctx, rule);
JMP_NEOK(ret);
} else {
DEBUG(1, ("Cannot determine rule type\n"));
ret = EINVAL;
goto done;
}
ret = EOK;
done:
talloc_free(pctx);
return ret;
}
/*******************************************************************
* validation of rules against time_t *
*******************************************************************/
static int absolute_timerange_valid(struct absolute_range *absr,
const time_t now,
bool *result)
{
if (difftime(absr->time_from, now) > 0) {
DEBUG(3, ("Absolute timerange invalid (before interval)\n"));
*result = false;
return EOK;
}
if (difftime(absr->time_to, now) < 0) {
DEBUG(3, ("Absolute timerange invalid (after interval)\n"));
*result = false;
return EOK;
}
DEBUG(3, ("Absolute timerange valid\n"));
*result = true;
return EOK;
}
static int periodic_timerange_valid(struct periodic_range *per,
const time_t now,
bool *result)
{
struct tm tm_now;
int wnum;
int wom;
memset(&tm_now, 0, sizeof(struct tm));
if (localtime_r(&now, &tm_now) == NULL) {
DEBUG(0, ("Cannot convert time_t to struct tm\n"));
return EFAULT;
}
DEBUG(9, ("Got struct tm value %s", asctime(&tm_now)));
tm2abs(&tm_now);
wnum = weeknum(&tm_now);
if (wnum == -1) {
DEBUG(7, ("Cannot get week number"));
return EINVAL;
}
DEBUG(9, ("Week number is %d\n", wnum));
wom = get_week_of_month(&tm_now);
if (wnum == -1) {
DEBUG(7, ("Cannot get week of number"));
return EINVAL;
}
DEBUG(9, ("Week of month number is %d\n", wom));
/* The validation itself */
TEST_BIT_RANGE(per->day_of_week, tm_now.tm_wday, result);
DEBUG(9, ("day of week OK\n"));
TEST_BIT_RANGE_PTR(per->day_of_month, tm_now.tm_mday, result);
DEBUG(9, ("day of month OK\n"));
TEST_BIT_RANGE(per->week_of_month, wom, result);
DEBUG(9, ("week of month OK\n"));
TEST_BIT_RANGE_PTR(per->week_of_year, wnum, result);
DEBUG(9, ("week of year OK\n"));
TEST_BIT_RANGE_PTR(per->month, tm_now.tm_mon, result);
DEBUG(9, ("month OK\n"));
TEST_BIT_RANGE_PTR(per->day_of_year, tm_now.tm_yday, result);
DEBUG(9, ("day of year OK\n"));
TEST_BIT_RANGE_PTR(per->hour, tm_now.tm_hour, result);
DEBUG(9, ("hour OK\n"));
TEST_BIT_RANGE_PTR(per->minute, tm_now.tm_min, result);
DEBUG(9, ("minute OK\n"));
DEBUG(3, ("Periodic timerange valid\n"));
*result = true;
return EOK;
}
/*
* Returns EOK if the timerange in range_ctx context is valid compared against a
* given time_t value in NOW, returns ERANGE if the time value is outside the
* specified range.
*/
static int timerange_valid(struct range_ctx *ctx,
const time_t now,
bool *result)
{
int ret;
switch(ctx->type) {
case TYPE_ABSOLUTE:
DEBUG(7, ("Checking absolute range\n"));
ret = absolute_timerange_valid(ctx->abs, now, result);
break;
case TYPE_PERIODIC:
DEBUG(7, ("Checking periodic range\n"));
ret = periodic_timerange_valid(ctx->per, now, result);
break;
default:
DEBUG(1, ("Unknown range type (%d)\n", ctx->type));
ret = EINVAL;
break;
}
return ret;
}
/*******************************************************************
* public interface *
*******************************************************************/
/*
* This is actually the meat of the library. The function takes a string
* representation of a time rule in STR and time to check against (usually that
* would be current time) in NOW.
*
* It returns EOK if the rule is valid in the current time, ERANGE if not and
* EINVAL if the rule cannot be parsed
*/
int check_time_rule(TALLOC_CTX *mem_ctx,
struct time_rules_ctx *trctx,
const char *str,
const time_t now,
bool *result)
{
int ret;
struct range_ctx *ctx;
ctx = talloc_zero(mem_ctx, struct range_ctx);
CHECK_PTR_JMP(ctx);
ctx->trctx = trctx;
DEBUG(9, ("Got time_t value %s", ctime(&now)));
ret = parse_timespec(ctx, str);
if (ret != EOK) {
DEBUG(1, ("Cannot parse the time specification (%d)\n", ret));
goto done;
}
ret = timerange_valid(ctx, now, result);
if (ret != EOK) {
DEBUG(1, ("Cannot check the time range (%d)\n", ret));
goto done;
}
ret = EOK;
done:
talloc_free(ctx);
return EOK;
}
/*
* Frees the resources taken by the precompiled rules
*/
static int time_rules_parser_destructor(struct time_rules_ctx *ctx)
{
int i;
for (i = 0; i< LP_RGX_MAX; ++i) {
pcre_free(ctx->re[i]);
ctx->re[i] = NULL;
}
return 0;
}
/*
* Initializes the parser by precompiling the regular expressions
* for later use
*/
int init_time_rules_parser(TALLOC_CTX *mem_ctx,
struct time_rules_ctx **_out)
{
const char *errstr;
int errval;
int errpos;
int ret;
int i;
struct time_rules_ctx *ctx = NULL;
ctx = talloc_zero(mem_ctx, struct time_rules_ctx);
CHECK_PTR(ctx);
talloc_set_destructor(ctx, time_rules_parser_destructor);
/* Precompile regular expressions */
for (i = LP_RGX_GENERALIZED; i< LP_RGX_MAX; ++i) {
ctx->re[i] = pcre_compile2(lookup_table[i],
0,
&errval,
&errstr,
&errpos,
NULL);
if (ctx->re[i] == NULL) {
DEBUG(0, ("Invalid Regular Expression pattern '%s' at position %d"
" (Error: %d [%s])\n", lookup_table[i],
errpos, errval, errstr));
ret = EFAULT;
goto done;
}
}
*_out = ctx;
return EOK;
done:
talloc_free(ctx);
return ret;
}