diff --git a/modules/cfgutils/cfgutils.c b/modules/cfgutils/cfgutils.c index 6fbf27a862..4d90bab2db 100644 --- a/modules/cfgutils/cfgutils.c +++ b/modules/cfgutils/cfgutils.c @@ -108,7 +108,8 @@ static int pv_get_random_val(struct sip_msg *msg, pv_param_t *param, static int ts_usec_delta(struct sip_msg *msg, int *t1s, int *t1u, int *t2s, int *t2u, pv_spec_t *_res); -int check_time_rec(struct sip_msg *msg, str *time_str, unsigned int *ptime); +int check_time_rec(struct sip_msg *msg, str *time_str, str *tz, + unsigned int *ptime); #ifdef HAVE_TIMER_FD static int async_sleep(struct sip_msg* msg, @@ -191,6 +192,7 @@ static cmd_export_t cmds[]={ STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, {"check_time_rec", (cmd_function)check_time_rec, { {CMD_PARAM_STR, fixup_str, fixup_free_str}, + {CMD_PARAM_STR|CMD_PARAM_OPT, 0, 0}, {CMD_PARAM_INT|CMD_PARAM_OPT, 0, 0},{0,0,0}}, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE| STARTUP_ROUTE|TIMER_ROUTE|EVENT_ROUTE}, @@ -844,11 +846,13 @@ static int ts_usec_delta(struct sip_msg *msg, int *t1s, 1 - match -1 - otherwise */ -int check_time_rec(struct sip_msg *msg, str *time_str, unsigned int *ptime) +int check_time_rec(struct sip_msg *msg, str *time_str, str *tz, + unsigned int *ptime) { tmrec_p time_rec = 0; char *p, *s; ac_tm_t att; + time_t check_time; p = time_str->s; @@ -860,6 +864,18 @@ int check_time_rec(struct sip_msg *msg, str *time_str, unsigned int *ptime) goto error; } + if (!ptime) + check_time = time(NULL); + else + check_time = *ptime; + + if (tz) { + check_time = tz_adjust_ts(check_time, tz); + tz_set(tz); + } else { + check_time = tz_adjust_ts(check_time, NULL); + } + load_TR_value( p, s, time_rec, tr_parse_dtstart, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_dtend, parse_error, done); load_TR_value( p, s, time_rec, tr_parse_duration, parse_error, done); @@ -884,7 +900,7 @@ int check_time_rec(struct sip_msg *msg, str *time_str, unsigned int *ptime) memset( &att, 0, sizeof(att)); /* set current time */ - if ( ac_tm_set_time( &att, ptime?(time_t)*ptime:time(0) ) ) + if (ac_tm_set_time(&att, check_time)) goto error; /* does the recv_time match the specified interval? */ @@ -894,6 +910,7 @@ int check_time_rec(struct sip_msg *msg, str *time_str, unsigned int *ptime) success: tmrec_free(time_rec); + tz_reset(); return 1; parse_error: @@ -902,5 +919,7 @@ int check_time_rec(struct sip_msg *msg, str *time_str, unsigned int *ptime) error: if (time_rec) tmrec_free( time_rec ); + + tz_reset(); return -1; } diff --git a/modules/cfgutils/doc/cfgutils_admin.xml b/modules/cfgutils/doc/cfgutils_admin.xml index eef0ff330a..ac9ed40765 100644 --- a/modules/cfgutils/doc/cfgutils_admin.xml +++ b/modules/cfgutils/doc/cfgutils_admin.xml @@ -474,14 +474,14 @@ ts_usec_delta($var(t1s), 300, 10, $var(t2ms), $var(result));
- <function moreinfo="none">check_time_rec(time_string[,timestamp])</function> + <function moreinfo="none">check_time_rec(time_string, [timezone], [timestamp])</function> The function returns a positive value if the specified time recurrence string matches the current time, or a negative value otherwise. - For checking something else than the current time, the second parameter will + For checking something else than the current time, the third parameter will contain the UNIX timestamp of the time to check. @@ -504,20 +504,31 @@ ts_usec_delta($var(t1s), 300, 10, $var(t2ms), $var(result)); string ends in multiple null fields, they can all be ommited. + + timezone (string, optional) - The + timezone that the time recurrence stamps are specified in. By + default, the system time zone is used. + + + + timestamp (string, optional) - A + specific UNIX time to check. The function simply expects the + actual UNIX time here, there is no need to perform any timezone + adjustments. + + <function>check_time_rec</function> usage ... # Only passing if still in 2012 -if (check_time_rec("20120101T000000|20121231T235959")) { - xlog("Current time matches the given interval\n"); -} +if (check_time_rec("20120101T000000|20130101T000000", "Europe/Bucharest")) + xlog("Current system time matches the given Romanian time interval\n"); ... # Only passing if less than 30 days have passed from "dtstart" -if (check_time_rec("20121101T000000||p30d")) { +if (check_time_rec("20121101T000000||p30d")) xlog("Current time matches the given interval\n"); -} ... diff --git a/modules/cfgutils/test/opensips.cfg b/modules/cfgutils/test/opensips.cfg new file mode 100644 index 0000000000..4e0cc193c1 --- /dev/null +++ b/modules/cfgutils/test/opensips.cfg @@ -0,0 +1,15 @@ +log_level = 2 +log_stderror = yes + +udp_workers = 1 + +listen = udp:*:5060 + +####### Modules Section ######## + +mpath = "modules/" + +loadmodule "mi_fifo.so" +loadmodule "proto_udp.so" + +loadmodule "cfgutils.so" diff --git a/modules/cfgutils/test/test.c b/modules/cfgutils/test/test.c new file mode 100644 index 0000000000..8f1ea81445 --- /dev/null +++ b/modules/cfgutils/test/test.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2020 OpenSIPS Solutions + * + * This file is part of opensips, a free SIP server. + * + * opensips is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * opensips is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "../../../parser/msg_parser.h" +#include "../../../str.h" +#include "../../../ut.h" +#include "../../../time_rec.h" + +extern int check_time_rec(struct sip_msg *_, str *time_str, str *tz, + unsigned int *ptime); + +void mod_tests(void) +{ + str rec = STR_NULL; + str utc = str_init("UTC"), ro = str_init("Europe/Bucharest"), + au = str_init("Pacific/Auckland"); + unsigned int now = 1591357895 /* 2020-06-05, 11:51:35 UTC */; + + /* no timezone, DTSTART is inclusive, local time */ + shm_str_sync(&rec, _str("20200605T115135|20200605T230000")); + ok(check_time_rec(NULL, &rec, NULL, &now) == 1, "tmrec-1"); + shm_str_sync(&rec, _str("20200605T115136|20200605T230000")); + ok(check_time_rec(NULL, &rec, NULL, &now) == -1, "tmrec-2"); + + /* no timezone, DTEND is non-inclusive, local time */ + shm_str_sync(&rec, _str("20200101T000000|20200605T115135")); + ok(check_time_rec(NULL, &rec, NULL, &now) == -1, "tmrec-3"); + shm_str_sync(&rec, _str("20200101T000000|20200605T115136")); + ok(check_time_rec(NULL, &rec, NULL, &now) == 1, "tmrec-4"); + + + /* DTSTART is inclusive, UTC */ + shm_str_sync(&rec, _str("20200605T115135|20200605T230000")); + ok(check_time_rec(NULL, &rec, &utc, &now) == 1, "tmrec-5"); + shm_str_sync(&rec, _str("20200605T115136|20200605T230000")); + ok(check_time_rec(NULL, &rec, &utc, &now) == -1, "tmrec-6"); + + /* DTEND is non-inclusive, UTC */ + shm_str_sync(&rec, _str("20200101T000000|20200605T115135")); + ok(check_time_rec(NULL, &rec, &utc, &now) == -1, "tmrec-7"); + shm_str_sync(&rec, _str("20200101T000000|20200605T115136")); + ok(check_time_rec(NULL, &rec, &utc, &now) == 1, "tmrec-8"); + + + /* DTSTART is inclusive, RO timezone */ + shm_str_sync(&rec, _str("20200605T115135|20200605T230000")); + ok(check_time_rec(NULL, &rec, &ro, &now) == 1, "tmrec-9"); + shm_str_sync(&rec, _str("20200605T115136|20200605T230000")); + ok(check_time_rec(NULL, &rec, &ro, &now) == -1, "tmrec-10"); + + /* DTEND is non-inclusive, RO timezone */ + shm_str_sync(&rec, _str("20200101T000000|20200605T115135")); + ok(check_time_rec(NULL, &rec, &ro, &now) == -1, "tmrec-11"); + shm_str_sync(&rec, _str("20200101T000000|20200605T115136")); + ok(check_time_rec(NULL, &rec, &ro, &now) == 1, "tmrec-12"); + + + /* DTSTART is inclusive, AU timezone */ + shm_str_sync(&rec, _str("20200605T115135|20200605T230000")); + ok(check_time_rec(NULL, &rec, &au, &now) == 1, "tmrec-13"); + shm_str_sync(&rec, _str("20200605T115136|20200605T230000")); + ok(check_time_rec(NULL, &rec, &au, &now) == -1, "tmrec-14"); + + /* DTEND is non-inclusive, AU timezone */ + shm_str_sync(&rec, _str("20200101T000000|20200605T115135")); + ok(check_time_rec(NULL, &rec, &au, &now) == -1, "tmrec-15"); + shm_str_sync(&rec, _str("20200101T000000|20200605T115136")); + ok(check_time_rec(NULL, &rec, &au, &now) == 1, "tmrec-16"); +} diff --git a/time_rec.c b/time_rec.c index 59ae47de1e..4d08e72af3 100644 --- a/time_rec.c +++ b/time_rec.c @@ -29,7 +29,7 @@ #define REC_MATCH 0 #define REC_NOMATCH 1 -#define _IS_SET(x) (((x)>0)?1:0) +#define _IS_SET(x) ((x) > 0) #define _D(c) ((c) -'0') @@ -71,13 +71,82 @@ int ac_tm_fill(ac_tm_p _atp, struct tm* _tm) return 0; } + +#define TZ_INTACT ((char *)-1) +static char *old_tz = TZ_INTACT; + +void tz_set(const str *tz) +{ +#define TZBUF_SZ 50 + char tzbuf[TZBUF_SZ]; + + if (tz->len >= TZBUF_SZ) + return; + + LM_DBG("setting timezone to: '%.*s'\n", tz->len, tz->s); + + memcpy(tzbuf, tz->s, tz->len); + tzbuf[tz->len] = '\0'; + + old_tz = getenv("TZ"); + + setenv("TZ", tzbuf, 1); + tzset(); +#undef TZBUF_SZ +} + + +void tz_reset(void) +{ + if (old_tz == TZ_INTACT) + return; + + if (!old_tz) { + LM_DBG("resetting timezone to system default\n"); + unsetenv("TZ"); + } else { + LM_DBG("resetting timezone to '%s'\n", old_tz); + setenv("TZ", old_tz, 1); + } + + tzset(); + old_tz = TZ_INTACT; +} + + +time_t tz_adjust_ts(time_t unix_time, const str *tz) +{ + struct tm *local_tm; + time_t adj_ts; + + tz_set(_str("UTC")); + local_tm = localtime(&unix_time); + tz_reset(); + + if (tz) + tz_set(tz); + + adj_ts = mktime(local_tm); + tz_reset(); + + if (local_tm->tm_isdst > 0) + adj_ts -= 3600; + + LM_DBG("UNIX ts: %ld, local-adjusted ts: %ld (%.*s, DST: %s)\n", unix_time, + adj_ts, tz->len, tz->s, local_tm->tm_isdst == 1 ? "on" : + local_tm->tm_isdst == 0 ? "off":"unavail"); + return adj_ts; +} + + + int ac_tm_set_time(ac_tm_p _atp, time_t _t) { struct tm ltime; - if(!_atp) - return -1; - memset( _atp, 0, sizeof(ac_tm_t)); + + memset(_atp, 0, sizeof *_atp); _atp->time = _t; + localtime_r(&_t, <ime); return ac_tm_fill(_atp, <ime); } @@ -925,7 +994,7 @@ int check_tmrec(tmrec_p _trp, ac_tm_p _atp, tr_res_p _tsw) if(!_IS_SET(_trp->duration)) _trp->duration = _trp->dtend - _trp->dtstart; - if(_atp->time <= _trp->dtstart+_trp->duration) + if(_atp->time < _trp->dtstart+_trp->duration) { if(_tsw) { diff --git a/time_rec.h b/time_rec.h index f9f5f5b390..022bbfb0c1 100644 --- a/time_rec.h +++ b/time_rec.h @@ -188,6 +188,15 @@ int ic_parse_wkst(char*); int check_tmrec(tmrec_p, ac_tm_p, tr_res_p); +void tz_set(const str *tz); +void tz_reset(void); -#endif +/* + * obtain an equivalent to the @unix_time UNIX timestamp + * that matches the @tz timezone, including the current DST status + * + * Note: If @tz == NULL, @unix_time will be ajusted to local time + */ +time_t tz_adjust_ts(time_t unix_time, const str *tz); +#endif