Skip to content

Commit

Permalink
check_time_rec(): Add timezone support
Browse files Browse the repository at this point in the history
  • Loading branch information
liviuchircu committed Jun 9, 2020
1 parent b0a63d9 commit 63eda69
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 16 deletions.
25 changes: 22 additions & 3 deletions modules/cfgutils/cfgutils.c
Expand Up @@ -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,
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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? */
Expand All @@ -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:
Expand All @@ -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;
}
25 changes: 18 additions & 7 deletions modules/cfgutils/doc/cfgutils_admin.xml
Expand Up @@ -474,14 +474,14 @@ ts_usec_delta($var(t1s), 300, 10, $var(t2ms), $var(result));
</section>
<section id="func_check_time_rec" xreflabel="check_time_rec()">
<title>
<function moreinfo="none">check_time_rec(time_string[,timestamp])</function>
<function moreinfo="none">check_time_rec(time_string, [timezone], [timestamp])</function>
</title>
<para>
The function returns a positive value if the specified time recurrence string
matches the current time, or a negative value otherwise.
</para>
<para>
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.
</para>
<para>
Expand All @@ -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.
</para>
</listitem>
<listitem>
<para><emphasis>timezone (string, optional)</emphasis> - The
timezone that the time recurrence stamps are specified in. By
default, the system time zone is used.
</para>
</listitem>
<listitem>
<para><emphasis>timestamp (string, optional)</emphasis> - A
specific UNIX time to check. The function simply expects the
actual UNIX time here, there is no need to perform any timezone
adjustments.
</para>
</listitem>
</itemizedlist>
<example>
<title><function>check_time_rec</function> usage</title>
<programlisting format="linespecific">
...
# 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");
}
...
</programlisting>
</example>
Expand Down
15 changes: 15 additions & 0 deletions 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"
88 changes: 88 additions & 0 deletions 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 <tap.h>

#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");
}
79 changes: 74 additions & 5 deletions time_rec.c
Expand Up @@ -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')


Expand Down Expand Up @@ -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, &ltime);
return ac_tm_fill(_atp, &ltime);
}
Expand Down Expand Up @@ -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)
{
Expand Down
11 changes: 10 additions & 1 deletion time_rec.h
Expand Up @@ -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

0 comments on commit 63eda69

Please sign in to comment.