2
0
Эх сурвалжийг харах

lib/srutils: new internal library to collect common shared code for modules

- for now includes tmrec - time recurrence matching library
- used by tmrec module
- cpl-c and drrouting embed tmrec -- planned to migrate to use of the
  internal lib
Daniel-Constantin Mierla 13 жил өмнө
parent
commit
ba04bebd37

+ 9 - 0
lib/srutils/Makefile

@@ -0,0 +1,9 @@
+include ../../Makefile.defs
+auto_gen=
+NAME:=srutils
+MAJOR_VER=1
+MINOR_VER=0
+BUGFIX_VER=0
+LIBS=
+
+include ../../Makefile.libs

+ 1325 - 0
lib/srutils/tmrec.c

@@ -0,0 +1,1325 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/**
+ * Time Recurence Library according to iCalendar (RFC 2445)
+ * - implemented at FhG Fokus, 2003 - external link:
+ *   https://github.com/miconda/tmrec
+ * - this is a clone adapted for kamailio/ser
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "../../mem/mem.h"
+#include "tmrec.h"
+
+
+/**
+ * ===== imported from "utils.h"
+ */
+
+static inline int tr_strz_to_int(char *_bp)
+{
+	int _v;
+	char *_p;
+	if(!_bp)
+		return 0;
+	_v = 0;
+	_p = _bp;
+	while(*_p && *_p>='0' && *_p<='9')
+	{
+		_v += *_p - '0';
+		_p++;
+	}
+	return _v;
+}
+
+
+static inline char* tr_trim(char* _s)
+{
+	int len;
+	char* end;
+
+	     /* Null pointer, there is nothing to do */
+	if (!_s) return _s;
+
+	     /* Remove spaces and tabs from the beginning of string */
+	while ((*_s == ' ') || (*_s == '\t')) _s++;
+
+	len = strlen(_s);
+
+        end = _s + len - 1;
+
+	     /* Remove trailing spaces and tabs */
+	while ((*end == ' ') || (*end == '\t')) end--;
+	if (end != (_s + len - 1)) {
+		*(end+1) = '\0';
+	}
+
+	return _s;
+}
+
+
+
+
+/**
+ * ===== imported from "ac_tm.c"
+ */
+
+/* #define USE_YWEEK_U		// Sunday system
+ * #define USE_YWEEK_V		// ISO 8601
+ */
+#ifndef USE_YWEEK_U
+#ifndef USE_YWEEK_V
+#ifndef USE_YWEEK_W
+#define USE_YWEEK_W		/* Monday system */.
+#endif
+#endif
+#endif
+
+#ifdef USE_YWEEK_U
+#define SUN_WEEK(t)	(int)(((t)->tm_yday + 7 - \
+				((t)->tm_wday)) / 7)
+#else
+#define MON_WEEK(t)	(int)(((t)->tm_yday + 7 - \
+				((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
+#endif
+
+#define ac_get_wday_yr(t) (int)((t)->tm_yday/7)
+#define ac_get_wday_mr(t) (int)(((t)->tm_mday-1)/7)
+
+ac_tm_t *ac_tm_new(void)
+{
+	ac_tm_t *_atp = NULL;
+	_atp = (ac_tm_t*)pkg_malloc(sizeof(ac_tm_t));
+	if(!_atp)
+		return NULL;
+	memset(_atp, 0, sizeof(ac_tm_t));
+	
+	return _atp;
+}
+
+int ac_tm_fill(ac_tm_t *_atp, struct tm* _tm)
+{
+	if(!_atp || !_tm)
+		return -1;
+	_atp->t.tm_sec = _tm->tm_sec;       /* seconds */
+	_atp->t.tm_min = _tm->tm_min;       /* minutes */
+	_atp->t.tm_hour = _tm->tm_hour;     /* hours */
+	_atp->t.tm_mday = _tm->tm_mday;     /* day of the month */
+	_atp->t.tm_mon = _tm->tm_mon;       /* month */
+	_atp->t.tm_year = _tm->tm_year;     /* year */
+	_atp->t.tm_wday = _tm->tm_wday;     /* day of the week */
+	_atp->t.tm_yday = _tm->tm_yday;     /* day in the year */
+	_atp->t.tm_isdst = _tm->tm_isdst;   /* daylight saving time */
+	
+	_atp->mweek = ac_get_mweek(_tm);
+	_atp->yweek = ac_get_yweek(_tm);
+	_atp->ywday = ac_get_wday_yr(_tm);
+	_atp->mwday = ac_get_wday_mr(_tm);
+	return 0;
+}
+
+int ac_tm_set_time(ac_tm_t *_atp, time_t _t)
+{
+	if(!_atp)
+		return -1;
+	_atp->time = _t;
+	return ac_tm_fill(_atp, localtime(&_t));
+}
+
+int ac_get_mweek(struct tm* _tm)
+{
+	if(!_tm)
+		return -1;
+#ifdef USE_YWEEK_U
+	return ((_tm->tm_mday-1)/7 + (7-_tm->tm_wday+(_tm->tm_mday-1)%7)/7);
+#else
+	return ((_tm->tm_mday-1)/7 + (7-(6+_tm->tm_wday)%7+(_tm->tm_mday-1)%7)/7);
+#endif
+}
+
+int ac_get_yweek(struct tm* _tm)
+{
+	int week = -1;
+#ifdef USE_YWEEK_V
+	int days;
+#endif
+	
+	if(!_tm)
+		return -1;
+	
+#ifdef USE_YWEEK_U
+	week = SUN_WEEK(_tm);
+#else
+	week = MON_WEEK(_tm);
+#endif
+
+#ifdef USE_YWEEK_V
+	days = ((_tm->tm_yday + 7 - (_tm->tm_wday ? _tm->tm_wday-1 : 6)) % 7);
+
+	if(days >= 4) 
+		week++;
+	else 
+		if(week == 0) 
+			week = 53;
+#endif
+	return week;
+}
+
+int ac_get_wkst(void)
+{
+#ifdef USE_YWEEK_U
+	return 0;
+#else
+	return 1;
+#endif
+}
+
+int ac_tm_reset(ac_tm_t *_atp)
+{
+	if(!_atp)
+		return -1;
+	memset(_atp, 0, sizeof(ac_tm_t));
+	return 0;
+}
+
+int ac_tm_free(ac_tm_t *_atp)
+{
+	if(!_atp)
+		return -1;
+	if(_atp->mv)
+		pkg_free(_atp->mv);
+	pkg_free(_atp);
+	return 0;
+}
+
+int ac_tm_destroy(ac_tm_t *_atp)
+{
+	if(!_atp)
+		return -1;
+	if(_atp->mv)
+		pkg_free(_atp->mv);
+	return 0;
+}
+
+ac_maxval_t *ac_get_maxval(ac_tm_t *_atp)
+{
+	struct tm _tm;
+	int _v;
+	ac_maxval_t *_amp = NULL;
+
+	if(!_atp)
+		return NULL;
+	_amp = (ac_maxval_t*)pkg_malloc(sizeof(ac_maxval_t));
+	if(!_amp)
+		return NULL;
+	
+	/* the number of the days in the year */
+	_amp->yday = 365 + tr_is_leap_year(_atp->t.tm_year+1900);
+
+	/* the number of the days in the month */
+	switch(_atp->t.tm_mon)
+	{
+		case 1:
+			if(_amp->yday == 366)
+				_amp->mday = 29;
+			else
+				_amp->mday = 28;
+		break;
+		case 3: case 5: case 8: case 10:
+			_amp->mday = 30;
+		break;
+		default:
+			_amp->mday = 31;
+	}
+	
+	/* maximum occurrences of a week day in the year */
+	memset(&_tm, 0, sizeof(struct tm));
+	_tm.tm_year = _atp->t.tm_year;
+	_tm.tm_mon = 11;
+	_tm.tm_mday = 31;
+	mktime(&_tm);
+	_v = 0;
+	if(_atp->t.tm_wday > _tm.tm_wday)
+		_v = _atp->t.tm_wday - _tm.tm_wday + 1;
+	else
+		_v = _tm.tm_wday - _atp->t.tm_wday;
+	_amp->ywday = (int)((_tm.tm_yday-_v)/7) + 1;
+	
+	/* maximum number of weeks in the year */
+	_amp->yweek = ac_get_yweek(&_tm) + 1;
+	
+	/* maximum number of the week day in the month */
+	_amp->mwday=(int)((_amp->mday-1-(_amp->mday-_atp->t.tm_mday)%7)/7)+1;
+	
+	/* maximum number of weeks in the month */
+	_v = (_atp->t.tm_wday + (_amp->mday - _atp->t.tm_mday)%7)%7;
+#ifdef USE_YWEEK_U
+	_amp->mweek = (int)((_amp->mday-1)/7+(7-_v+(_amp->mday-1)%7)/7)+1;
+#else
+	_amp->mweek = (int)((_amp->mday-1)/7+(7-(6+_v)%7+(_amp->mday-1)%7)/7)+1;
+#endif
+
+	_atp->mv = _amp;
+	return _amp;
+}
+
+int ac_print(ac_tm_t *_atp)
+{
+	static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; 
+	if(!_atp)
+	{
+		printf("\n(null)\n");
+		return -1;
+	}
+	
+	printf("\nSys time: %d\nTime: %02d:%02d:%02d\n", (int)_atp->time,
+				_atp->t.tm_hour, _atp->t.tm_min, _atp->t.tm_sec);
+	printf("Date: %s, %04d-%02d-%02d\n", _wdays[_atp->t.tm_wday],
+				_atp->t.tm_year+1900, _atp->t.tm_mon+1, _atp->t.tm_mday);
+	printf("Year day: %d\nYear week-day: %d\nYear week: %d\n", _atp->t.tm_yday,
+			_atp->ywday, _atp->yweek);
+	printf("Month week: %d\nMonth week-day: %d\n", _atp->mweek, _atp->mwday);
+	if(_atp->mv)
+	{
+		printf("Max ydays: %d\nMax yweeks: %d\nMax yweekday: %d\n",
+				_atp->mv->yday, _atp->mv->yweek, _atp->mv->ywday);;
+		printf("Max mdays: %d\nMax mweeks: %d\nMax mweekday: %d\n",
+				_atp->mv->mday, _atp->mv->mweek, _atp->mv->mwday);;
+	}
+	return 0;
+}
+
+
+
+
+
+/************************ imported from "tmrec.c"  ***************************/
+
+#define _D(c) ((c) -'0')
+
+tr_byxxx_t *tr_byxxx_new(void)
+{
+	tr_byxxx_t *_bxp = NULL;
+	_bxp = (tr_byxxx_t*)pkg_malloc(sizeof(tr_byxxx_t));
+	if(!_bxp)
+		return NULL;
+	memset(_bxp, 0, sizeof(tr_byxxx_t));
+	return _bxp;
+}
+
+int tr_byxxx_init(tr_byxxx_t *_bxp, int _nr)
+{
+	if(!_bxp)
+		return -1;
+	_bxp->nr = _nr;
+	_bxp->xxx = (int*)pkg_malloc(_nr*sizeof(int));
+	if(!_bxp->xxx)
+		return -1;
+	_bxp->req = (int*)pkg_malloc(_nr*sizeof(int));
+	if(!_bxp->req)
+	{
+		pkg_free(_bxp->xxx);
+		return -1;
+	}
+	
+	memset(_bxp->xxx, 0, _nr*sizeof(int));
+	memset(_bxp->req, 0, _nr*sizeof(int));
+	
+	return 0;
+}
+
+
+int tr_byxxx_free(tr_byxxx_t *_bxp)
+{
+	if(!_bxp)
+		return -1;
+	if(_bxp->xxx)
+		pkg_free(_bxp->xxx);
+	if(_bxp->req)
+		pkg_free(_bxp->req);
+	pkg_free(_bxp);
+	return 0;
+}
+
+tmrec_t *tmrec_new(void)
+{
+	tmrec_t *_trp = NULL;
+	_trp = (tmrec_t*)pkg_malloc(sizeof(tmrec_t));
+	if(!_trp)
+		return NULL;
+	memset(_trp, 0, sizeof(tmrec_t));
+	localtime_r(&_trp->dtstart,&(_trp->ts));
+	return _trp;
+}
+
+int tmrec_free(tmrec_t *_trp)
+{
+	if(!_trp)
+		return -1;
+	
+	tr_byxxx_free(_trp->byday);
+	tr_byxxx_free(_trp->bymday);
+	tr_byxxx_free(_trp->byyday);
+	tr_byxxx_free(_trp->bymonth);
+	tr_byxxx_free(_trp->byweekno);
+
+	pkg_free(_trp);
+	return 0;
+}
+
+int tmrec_destroy(tmrec_t *_trp)
+{
+	if(!_trp)
+		return -1;
+	
+	tr_byxxx_free(_trp->byday);
+	tr_byxxx_free(_trp->bymday);
+	tr_byxxx_free(_trp->byyday);
+	tr_byxxx_free(_trp->bymonth);
+	tr_byxxx_free(_trp->byweekno);
+
+	return 0;
+}
+
+int tr_parse_dtstart(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->dtstart = ic_parse_datetime(_in, &(_trp->ts));
+	return (_trp->dtstart==0)?-1:0;
+}
+
+int tr_parse_dtend(tmrec_t *_trp, char *_in)
+{
+	struct tm _tm;
+	if(!_trp || !_in)
+		return -1;
+	_trp->dtend = ic_parse_datetime(_in,&_tm);
+	return (_trp->dtend==0)?-1:0;
+}
+
+int tr_parse_duration(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->duration = ic_parse_duration(_in);
+	return (_trp->duration==0)?-1:0;
+}
+
+int tr_parse_until(tmrec_t *_trp, char *_in)
+{
+	struct tm _tm;
+	if(!_trp || !_in)
+		return -1;
+	_trp->until = ic_parse_datetime(_in, &_tm);
+	return (_trp->until==0)?-1:0;
+}
+
+int tr_parse_freq(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	if(!strcasecmp(_in, "daily"))
+	{
+		_trp->freq = FREQ_DAILY;
+		return 0;
+	}
+	if(!strcasecmp(_in, "weekly"))
+	{
+		_trp->freq = FREQ_WEEKLY;
+		return 0;
+	}
+	if(!strcasecmp(_in, "monthly"))
+	{
+		_trp->freq = FREQ_MONTHLY;
+		return 0;
+	}
+	if(!strcasecmp(_in, "yearly"))
+	{
+		_trp->freq = FREQ_YEARLY;
+		return 0;
+	}
+
+	_trp->freq = FREQ_NOFREQ;
+	return 0;
+}
+
+int tr_parse_interval(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->interval = tr_strz_to_int(_in);
+	return 0;
+}
+
+int tr_parse_byday(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->byday = ic_parse_byday(_in); 
+	return 0;
+}
+
+int tr_parse_bymday(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->bymday = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_byyday(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->byyday = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_bymonth(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->bymonth = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_byweekno(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->byweekno = ic_parse_byxxx(_in); 
+	return 0;
+}
+
+int tr_parse_wkst(tmrec_t *_trp, char *_in)
+{
+	if(!_trp || !_in)
+		return -1;
+	_trp->wkst = ic_parse_wkst(_in);
+	return 0;
+}
+
+int tr_print(tmrec_t *_trp)
+{
+	static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"}; 
+	int i;
+	
+	if(!_trp)
+	{
+		printf("\n(null)\n");
+		return -1;
+	}
+	printf("Recurrence definition\n-- start time ---\n");
+	printf("Sys time: %d\n", (int)_trp->dtstart);
+	printf("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour, 
+				_trp->ts.tm_min, _trp->ts.tm_sec);
+	printf("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday],
+				_trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday);
+	printf("---\n");
+	printf("End time: %d\n", (int)_trp->dtend);
+	printf("Duration: %d\n", (int)_trp->duration);
+	printf("Until: %d\n", (int)_trp->until);
+	printf("Freq: %d\n", (int)_trp->freq);
+	printf("Interval: %d\n", (int)_trp->interval);
+	if(_trp->byday)
+	{
+		printf("Byday: ");
+		for(i=0; i<_trp->byday->nr; i++)
+			printf(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]);
+		printf("\n");
+	}
+	if(_trp->bymday)
+	{
+		printf("Bymday: %d:", _trp->bymday->nr);
+		for(i=0; i<_trp->bymday->nr; i++)
+			printf(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]);
+		printf("\n");
+	}
+	if(_trp->byyday)
+	{
+		printf("Byyday:");
+		for(i=0; i<_trp->byyday->nr; i++)
+			printf(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]);
+		printf("\n");
+	}
+	if(_trp->bymonth)
+	{
+		printf("Bymonth: %d:", _trp->bymonth->nr);
+		for(i=0; i< _trp->bymonth->nr; i++)
+			printf(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]);
+		printf("\n");
+	}
+	if(_trp->byweekno)
+	{
+		printf("Byweekno: ");
+		for(i=0; i<_trp->byweekno->nr; i++)
+			printf(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]);
+		printf("\n");
+	}
+	printf("Weekstart: %d\n", _trp->wkst);
+	return 0;
+}
+
+time_t ic_parse_datetime(char *_in, struct tm *_tm)
+{
+	if(!_in || !_tm || strlen(_in)!=15)
+		return 0;
+	
+	memset(_tm, 0, sizeof(struct tm));
+	_tm->tm_year = _D(_in[0])*1000 + _D(_in[1])*100 
+			+ _D(_in[2])*10 + _D(_in[3]) - 1900;
+	_tm->tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1;
+	_tm->tm_mday = _D(_in[6])*10 + _D(_in[7]);
+	_tm->tm_hour = _D(_in[9])*10 + _D(_in[10]);
+	_tm->tm_min = _D(_in[11])*10 + _D(_in[12]);
+	_tm->tm_sec = _D(_in[13])*10 + _D(_in[14]);
+	_tm->tm_isdst = -1 /*daylight*/;
+	return mktime(_tm);
+}
+
+time_t ic_parse_duration(char *_in)
+{
+	time_t _t, _ft;
+	char *_p;
+	int _fl;
+	
+	if(!_in || (*_in!='+' && *_in!='-' && *_in!='P' && *_in!='p'))
+		return 0;
+	
+	if(*_in == 'P' || *_in=='p')
+		_p = _in+1;
+	else
+	{
+		if(strlen(_in)<2 || (_in[1]!='P' && _in[1]!='p'))
+			return 0;
+		_p = _in+2;
+	}
+	
+	_t = _ft = 0;
+	_fl = 1;
+	
+	while(*_p)
+	{
+		switch(*_p)
+		{
+			case '0': case '1': case '2':
+			case '3': case '4': case '5':
+			case '6': case '7': case '8':
+			case '9':
+				_t = _t*10 + *_p - '0';
+			break;
+			
+			case 'w':
+			case 'W':
+				if(!_fl)
+					return 0;
+				_ft += _t*7*24*3600;
+				_t = 0;
+			break;
+			case 'd':
+			case 'D':
+				if(!_fl)
+					return 0;
+				_ft += _t*24*3600;
+				_t = 0;
+			break;
+			case 'h':
+			case 'H':
+				if(_fl)
+					return 0;
+				_ft += _t*3600;
+				_t = 0;
+			break;
+			case 'm':
+			case 'M':
+				if(_fl)
+					return 0;
+				_ft += _t*60;
+				_t = 0;
+			break;
+			case 's':
+			case 'S':
+				if(_fl)
+					return 0;
+				_ft += _t;
+				_t = 0;
+			break;
+			case 't':
+			case 'T':
+				if(!_fl)
+					return 0;
+				_fl = 0;
+			break;
+			default:
+				return 0;
+		}
+		_p++;
+	}
+
+	return _ft;
+}
+
+tr_byxxx_t *ic_parse_byday(char *_in)
+{
+	tr_byxxx_t *_bxp = NULL;
+	int _nr, _s, _v;
+	char *_p;
+
+	if(!_in)
+		return NULL;
+	_bxp = tr_byxxx_new();
+	if(!_bxp)
+		return NULL;
+	_p = _in;
+	_nr = 1;
+	while(*_p)
+	{
+		if(*_p == ',')
+			_nr++;
+		_p++;
+	}
+	if(tr_byxxx_init(_bxp, _nr) < 0)
+	{
+		tr_byxxx_free(_bxp);
+		return NULL;
+	}
+	_p = _in;
+	_nr = _v = 0;
+	_s = 1;
+	while(*_p && _nr < _bxp->nr)
+	{
+		switch(*_p)
+		{
+			case '0': case '1': case '2':
+			case '3': case '4': case '5':
+			case '6': case '7': case '8':
+			case '9':
+				_v = _v*10 + *_p - '0';
+			break;
+			
+			case 's':
+			case 'S':
+				_p++;
+				switch(*_p)
+				{
+					case 'a':
+					case 'A':
+						_bxp->xxx[_nr] = WDAY_SA;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					case 'u':
+					case 'U':
+						_bxp->xxx[_nr] = WDAY_SU;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					default:
+						goto error;
+				}
+				_s = 1;
+				_v = 0;
+			break;
+			case 'm':
+			case 'M':
+				_p++;
+				if(*_p!='o' && *_p!='O')
+					goto error;
+				_bxp->xxx[_nr] = WDAY_MO;
+				_bxp->req[_nr] = _s*_v;
+				_s = 1;
+				_v = 0;
+			break;
+			case 't':
+			case 'T':
+				_p++;
+				switch(*_p)
+				{
+					case 'h':
+					case 'H':
+						_bxp->xxx[_nr] = WDAY_TH;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					case 'u':
+					case 'U':
+						_bxp->xxx[_nr] = WDAY_TU;
+						_bxp->req[_nr] = _s*_v;
+					break;
+					default:
+						goto error;
+				}
+				_s = 1;
+				_v = 0;
+			break;
+			case 'w':
+			case 'W':
+				_p++;
+				if(*_p!='e' && *_p!='E')
+					goto error;
+				_bxp->xxx[_nr] = WDAY_WE;
+				_bxp->req[_nr] = _s*_v;
+				_s = 1;
+				_v = 0;
+			break;
+			case 'f':
+			case 'F':
+				_p++;
+				if(*_p!='r' && *_p!='R')
+					goto error;
+				_bxp->xxx[_nr] = WDAY_FR;
+				_bxp->req[_nr] = _s*_v;
+				_s = 1;
+				_v = 0;
+			break;
+			case '-':
+				_s = -1;
+			break;
+			case '+':
+			case ' ':
+			case '\t':
+			break;
+			case ',':
+				_nr++;
+			break;
+			default:
+				goto error;
+		}
+		_p++;
+	}
+
+	return _bxp;
+
+error:
+	tr_byxxx_free(_bxp);
+	return NULL;
+}
+
+tr_byxxx_t *ic_parse_byxxx(char *_in)
+{
+	tr_byxxx_t *_bxp = NULL;
+	int _nr, _s, _v;
+	char *_p;
+
+	if(!_in)
+		return NULL;
+	_bxp = tr_byxxx_new();
+	if(!_bxp)
+		return NULL;
+	_p = _in;
+	_nr = 1;
+	while(*_p)
+	{
+		if(*_p == ',')
+			_nr++;
+		_p++;
+	}
+	if(tr_byxxx_init(_bxp, _nr) < 0)
+	{
+		tr_byxxx_free(_bxp);
+		return NULL;
+	}
+	_p = _in;
+	_nr = _v = 0;
+	_s = 1;
+	while(*_p && _nr < _bxp->nr)
+	{
+		switch(*_p)
+		{
+			case '0': case '1': case '2':
+			case '3': case '4': case '5':
+			case '6': case '7': case '8':
+			case '9':
+				_v = _v*10 + *_p - '0';
+			break;
+			
+			case '-':
+				_s = -1;
+			break;
+			case '+':
+			case ' ':
+			case '\t':
+			break;
+			case ',':
+				_bxp->xxx[_nr] = _v;
+				_bxp->req[_nr] = _s;
+				_s = 1;
+				_v = 0;
+				_nr++;
+			break;
+			default:
+				goto error;
+		}
+		_p++;
+	}
+	if(_nr < _bxp->nr)
+	{
+		_bxp->xxx[_nr] = _v;
+		_bxp->req[_nr] = _s;
+	}
+	return _bxp;
+
+error:
+	tr_byxxx_free(_bxp);
+	return NULL;
+}
+
+int ic_parse_wkst(char *_in)
+{
+	if(!_in || strlen(_in)!=2)
+		goto error;
+	
+	switch(_in[0])
+	{
+		case 's':
+		case 'S':
+			switch(_in[1])
+			{
+				case 'a':
+				case 'A':
+					return WDAY_SA;
+				case 'u':
+				case 'U':
+					return WDAY_SU;
+				default:
+					goto error;
+			}
+		case 'm':
+		case 'M':
+			if(_in[1]!='o' && _in[1]!='O')
+				goto error;
+			return WDAY_MO;
+		case 't':
+		case 'T':
+			switch(_in[1])
+			{
+				case 'h':
+				case 'H':
+					return WDAY_TH;
+				case 'u':
+				case 'U':
+					return WDAY_TU;
+				default:
+					goto error;
+			}
+		case 'w':
+		case 'W':
+			if(_in[1]!='e' && _in[1]!='E')
+				goto error;
+			return WDAY_WE;
+		case 'f':
+		case 'F':
+			if(_in[1]!='r' && _in[1]!='R')
+				goto error;
+			return WDAY_FR;
+		break;
+		default:
+			goto error;
+	}
+	
+error:
+#ifdef USE_YWEEK_U
+	return WDAY_SU;
+#else
+	return WDAY_MO;
+#endif
+}
+
+
+/**
+ * =====imported from "checktr.c"
+ */
+
+#define REC_ERR    -1
+#define REC_MATCH   0
+#define REC_NOMATCH 1
+
+#define _IS_SET(x) (((x)>0)?1:0)
+
+/*** local headers ***/
+int get_min_interval(tmrec_t*);
+int check_min_unit(tmrec_t*, ac_tm_t*, tr_res_t*);
+int check_freq_interval(tmrec_t *_trp, ac_tm_t *_atp);
+int check_byxxx(tmrec_t*, ac_tm_t*);
+
+/**
+ *
+ * return 0/REC_MATCH - the time falls in
+ *       -1/REC_ERR - error
+ *        1/REC_NOMATCH - the time falls out
+ */
+int tr_check_recurrence(tmrec_t *_trp, ac_tm_t *_atp, tr_res_t *_tsw)
+{
+	if(!_trp || !_atp || (!_IS_SET(_trp->duration) && !_IS_SET(_trp->dtend)))
+		return REC_ERR;
+
+	/* it is before start date */
+	if(_atp->time < _trp->dtstart)
+		return REC_NOMATCH;
+	
+	/* compute the duration of the recurrence interval */
+	if(!_IS_SET(_trp->duration))
+		_trp->duration = _trp->dtend - _trp->dtstart;
+	
+	if(_atp->time <= _trp->dtstart+_trp->duration)
+	{
+		if(_tsw)
+		{
+			if(_tsw->flag & TSW_RSET)
+			{
+				if(_tsw->rest>_trp->dtstart+_trp->duration-_atp->time)
+					_tsw->rest = _trp->dtstart+_trp->duration - _atp->time;
+			}
+			else
+			{
+				_tsw->flag |= TSW_RSET;
+				_tsw->rest = _trp->dtstart+_trp->duration - _atp->time;
+			}
+		}
+		return REC_MATCH;
+	}
+	
+	/* after the bound of recurrence */
+	if(_IS_SET(_trp->until) && _atp->time >= _trp->until + _trp->duration)
+		return REC_NOMATCH;
+	
+	/* check if the instance of recurrence matches the 'interval' */
+	if(check_freq_interval(_trp, _atp)!=REC_MATCH)
+		return REC_NOMATCH;
+
+	if(check_min_unit(_trp, _atp, _tsw)!=REC_MATCH)
+		return REC_NOMATCH;
+
+	if(check_byxxx(_trp, _atp)!=REC_MATCH)
+		return REC_NOMATCH;
+
+	return REC_MATCH;
+}
+
+
+int check_freq_interval(tmrec_t *_trp, ac_tm_t *_atp)
+{
+	int _t0, _t1;
+	struct tm _tm;
+	if(!_trp || !_atp)
+		return REC_ERR;
+	
+	if(!_IS_SET(_trp->freq))
+		return REC_NOMATCH;
+	
+	if(!_IS_SET(_trp->interval) || _trp->interval==1)
+		return REC_MATCH;
+	
+	switch(_trp->freq)
+	{
+		case FREQ_DAILY:
+		case FREQ_WEEKLY:
+			memset(&_tm, 0, sizeof(struct tm));
+			_tm.tm_year = _trp->ts.tm_year;
+			_tm.tm_mon = _trp->ts.tm_mon;
+			_tm.tm_mday = _trp->ts.tm_mday;
+			_t0 = (int)mktime(&_tm);
+			memset(&_tm, 0, sizeof(struct tm));
+			_tm.tm_year = _atp->t.tm_year;
+			_tm.tm_mon = _atp->t.tm_mon;
+			_tm.tm_mday = _atp->t.tm_mday;
+			_t1 = (int)mktime(&_tm);
+			if(_trp->freq == FREQ_DAILY)
+				return (((_t1-_t0)/(24*3600))%_trp->interval==0)?
+					REC_MATCH:REC_NOMATCH;
+#ifdef USE_YWEEK_U
+			_t0 -= _trp->ts.tm_wday*24*3600;
+			_t1 -= _atp->t.tm_wday*24*3600;
+#else
+			_t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600;
+			_t1 -= ((_atp->t.tm_wday+6)%7)*24*3600;
+#endif
+			return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)?
+					REC_MATCH:REC_NOMATCH;
+		case FREQ_MONTHLY:
+			_t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12
+					+ _atp->t.tm_mon-_trp->ts.tm_mon;
+			return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH;
+		case FREQ_YEARLY:
+			return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)?
+					REC_MATCH:REC_NOMATCH;
+	}
+	
+	return REC_NOMATCH;
+}
+
+int get_min_interval(tmrec_t *_trp)
+{
+	if(!_trp)
+		return FREQ_NOFREQ;
+	
+	if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday)
+		return FREQ_DAILY;
+	if(_trp->freq == FREQ_WEEKLY || _trp->byweekno) 
+		return FREQ_WEEKLY;
+	if(_trp->freq == FREQ_MONTHLY || _trp->bymonth)
+		return FREQ_MONTHLY;
+	if(_trp->freq == FREQ_YEARLY)
+		return FREQ_YEARLY;
+	
+	return FREQ_NOFREQ;
+}
+
+int check_min_unit(tmrec_t *_trp, ac_tm_t *_atp, tr_res_t *_tsw)
+{
+	int _v0, _v1;
+	if(!_trp || !_atp)
+		return REC_ERR;
+	switch(get_min_interval(_trp))
+	{
+		case FREQ_DAILY:
+		break;
+		case FREQ_WEEKLY:
+			if(_trp->ts.tm_wday != _atp->t.tm_wday)
+				return REC_NOMATCH;
+		break;
+		case FREQ_MONTHLY:
+			if(_trp->ts.tm_mday != _atp->t.tm_mday)
+				return REC_NOMATCH;
+		break;
+		case FREQ_YEARLY:
+			if(_trp->ts.tm_mon != _atp->t.tm_mon 
+					|| _trp->ts.tm_mday != _atp->t.tm_mday)
+				return REC_NOMATCH;
+		break;
+		default:
+			return REC_NOMATCH;
+	}
+	_v0 = _trp->ts.tm_hour*3600 + _trp->ts.tm_min*60 + _trp->ts.tm_sec;
+	_v1 = _atp->t.tm_hour*3600 + _atp->t.tm_min*60 + _atp->t.tm_sec;
+	if(_v1 >= _v0 && _v1 < _v0 + _trp->duration)
+	{
+		if(_tsw)
+		{
+			if(_tsw->flag & TSW_RSET)
+			{
+				if(_tsw->rest>_v0+_trp->duration-_v1)
+					_tsw->rest = _v0 + _trp->duration - _v1;
+			}
+			else
+			{
+				_tsw->flag |= TSW_RSET;
+				_tsw->rest = _v0 + _trp->duration - _v1;
+			}
+		}
+		return REC_MATCH;
+	}
+	
+	return REC_NOMATCH;
+}
+
+int check_byxxx(tmrec_t *_trp, ac_tm_t *_atp)
+{
+	int i;
+	ac_maxval_t *_amp = NULL;
+	if(!_trp || !_atp)
+		return REC_ERR;
+	if(!_trp->byday && !_trp->bymday && !_trp->byyday && !_trp->bymonth 
+			&& !_trp->byweekno)
+		return REC_MATCH;
+	
+	_amp = ac_get_maxval(_atp);
+	if(!_amp)
+		return REC_NOMATCH;
+	
+	if(_trp->bymonth)
+	{
+		for(i=0; i<_trp->bymonth->nr; i++)
+		{
+			if(_atp->t.tm_mon == 
+					(_trp->bymonth->xxx[i]*_trp->bymonth->req[i]+12)%12)
+				break;
+		}
+		if(i>=_trp->bymonth->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->freq==FREQ_YEARLY && _trp->byweekno)
+	{
+		for(i=0; i<_trp->byweekno->nr; i++)
+		{
+			if(_atp->yweek == (_trp->byweekno->xxx[i]*_trp->byweekno->req[i]+
+							_amp->yweek)%_amp->yweek)
+				break;
+		}
+		if(i>=_trp->byweekno->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->byyday)
+	{
+		for(i=0; i<_trp->byyday->nr; i++)
+		{
+			if(_atp->t.tm_yday == (_trp->byyday->xxx[i]*_trp->byyday->req[i]+
+						_amp->yday)%_amp->yday)
+				break;
+		}
+		if(i>=_trp->byyday->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->bymday)
+	{
+		for(i=0; i<_trp->bymday->nr; i++)
+		{
+#ifdef EXTRA_DEBUG
+			DBG("Req:bymday: %d == %d\n", _atp->t.tm_mday,
+				(_trp->bymday->xxx[i]*_trp->bymday->req[i]+
+				_amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0));
+#endif
+			if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
+						_amp->mday)%_amp->mday + (_trp->bymday->req[i]<0)?1:0)
+				break;
+		}
+		if(i>=_trp->bymday->nr)
+			return REC_NOMATCH;
+	}
+	if(_trp->byday)
+	{
+		for(i=0; i<_trp->byday->nr; i++)
+		{
+			if(_trp->freq==FREQ_YEARLY)
+			{
+#ifdef EXTRA_DEBUG
+				DBG("Req:byday:y: %d==%d && %d==%d\n", _atp->t.tm_wday,
+					_trp->byday->xxx[i], _atp->ywday+1, 
+					(_trp->byday->req[i]+_amp->ywday)%_amp->ywday);
+#endif
+				if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
+						_atp->ywday+1 == (_trp->byday->req[i]+_amp->ywday)%
+						_amp->ywday)
+					break;
+			}
+			else
+			{
+				if(_trp->freq==FREQ_MONTHLY)
+				{
+#ifdef EXTRA_DEBUG
+					DBG("Req:byday:m: %d==%d && %d==%d\n", _atp->t.tm_wday,
+						_trp->byday->xxx[i], _atp->mwday+1, 
+						(_trp->byday->req[i]+_amp->mwday)%_amp->mwday);
+#endif
+					if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
+							_atp->mwday+1==(_trp->byday->req[i]+
+							_amp->mwday)%_amp->mwday)
+						break;
+				}
+				else
+				{
+					if(_atp->t.tm_wday == _trp->byday->xxx[i])
+						break;
+				}
+			}
+		}
+		if(i>=_trp->byday->nr)
+			return REC_NOMATCH;
+	}
+
+	return REC_MATCH;
+}
+
+int tr_parse_recurrence_string(tmrec_t *trp, char *rdef, char sep)
+{
+	char *p;
+	char *s;
+	int type;
+
+	type = 0;
+	p = rdef;
+
+	memset(trp, 0, sizeof(tmrec_t));
+
+	do{
+		s = strchr(p, (int)sep);
+		if (s!=NULL)
+			*s = '\0';
+		/* LM_DBG("----- parsing tr param <%s>\n", p); */
+		if(s != p) {
+			switch(type) {
+				case 0:
+					if(tr_parse_dtstart(trp, p)<0)
+						goto error;
+					break;
+				case 1:
+					if(tr_parse_duration(trp, p)<0)
+						goto error;
+					break;
+				case 2:
+					if(tr_parse_freq(trp, p)<0)
+						goto error;
+					break;
+				case 3:
+					if(tr_parse_until(trp, p)<0)
+						goto error;
+					break;
+				case 4:
+					if(tr_parse_interval(trp, p)<0)
+						goto error;
+					break;
+				case 5:
+					if(tr_parse_byday(trp, p)<0)
+						goto error;
+					break;
+				case 6:
+					if(tr_parse_bymday(trp, p)<0)
+						goto error;
+					break;
+				case 7:
+					if(tr_parse_byyday(trp, p)<0)
+						goto error;
+					break;
+				case 8:
+					if(tr_parse_byweekno(trp, p)<0)
+						goto error;
+					break;
+				case 9:
+					if(tr_parse_bymonth(trp, p)<0)
+						goto error;
+					break;
+			}
+		}
+		type++;
+		if (s!=NULL) {
+			*s = sep;
+			p = s + 1;
+			if(*p==0)
+				goto done;
+		} else {
+			goto done;
+		}
+	} while(1);
+
+done:
+	return 0;
+
+error:
+	LM_ERR("failed to parse time recurrence [%s]\n", rdef);
+	if (s!=NULL)
+		*s = sep;
+	return -1;
+}
+

+ 177 - 0
lib/srutils/tmrec.h

@@ -0,0 +1,177 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * Kamailio 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
+ *
+ * Kamailio 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef _SR_TMREC_H_
+#define _SR_TMREC_H_
+
+
+/**
+ * ===== imported from "ac_tm.h"
+ */
+
+#include <time.h>
+
+
+/* USE_YWEEK_U	-- Sunday system - see strftime %U
+ * USE_YWEEK_V	-- ISO 8601 - see strftime %V
+ * USE_YWEEK_W	-- Monday system - see strftime %W
+*/
+
+#ifndef USE_YWEEK_U
+# ifndef USE_YWEEK_V
+#  ifndef USE_YWEEK_W
+#   define USE_YWEEK_W
+#  endif
+# endif
+#endif
+
+#define tr_is_leap_year(yyyy) ((((yyyy)%400))?(((yyyy)%100)?(((yyyy)%4)?0:1):0):1)
+
+
+typedef struct _ac_maxval
+{
+	int yweek;
+	int yday;
+	int ywday;
+	int mweek;
+	int mday;
+	int mwday;
+} ac_maxval_t;
+
+typedef struct _ac_tm
+{
+	time_t time;
+	struct tm t;
+	int mweek;
+	int yweek;
+	int ywday;
+	int mwday;
+	ac_maxval_t *mv;
+} ac_tm_t;
+
+ac_tm_t *ac_tm_new(void);
+
+int ac_tm_set_time(ac_tm_t*, time_t);
+
+int ac_tm_reset(ac_tm_t*);
+int ac_tm_free(ac_tm_t*);
+int ac_tm_destroy(ac_tm_t*);
+
+int ac_get_mweek(struct tm*);
+int ac_get_yweek(struct tm*);
+ac_maxval_t *ac_get_maxval(ac_tm_t*);
+int ac_get_wkst(void);
+
+int ac_print(ac_tm_t*);
+
+
+
+
+/**
+ * ===== imported from "tmrec.h"
+ */
+
+
+#define FREQ_NOFREQ  0
+#define FREQ_YEARLY  1
+#define FREQ_MONTHLY 2
+#define FREQ_WEEKLY  3
+#define FREQ_DAILY   4
+
+#define WDAY_SU 0
+#define WDAY_MO 1
+#define WDAY_TU 2
+#define WDAY_WE 3
+#define WDAY_TH 4
+#define WDAY_FR 5
+#define WDAY_SA 6
+#define WDAY_NU 7
+
+#define TSW_TSET	1
+#define TSW_RSET	2
+
+typedef struct _tr_byxxx
+{
+	int nr;
+	int *xxx;
+	int *req;
+} tr_byxxx_t;
+
+typedef struct _tmrec
+{
+	time_t dtstart;
+	struct tm ts;
+	time_t dtend;
+	time_t duration;
+	time_t until;
+	int freq;
+	int interval;
+	tr_byxxx_t *byday;
+	tr_byxxx_t *bymday;
+	tr_byxxx_t *byyday;
+	tr_byxxx_t *bymonth;
+	tr_byxxx_t *byweekno;
+	int wkst;
+} tmrec_t;
+
+typedef struct _tr_res
+{
+	int flag;
+	time_t rest;
+} tr_res_t;
+
+tr_byxxx_t *tr_byxxx_new(void);
+int tr_byxxx_init(tr_byxxx_t*, int);
+int tr_byxxx_free(tr_byxxx_t*);
+
+tmrec_t *tmrec_new(void);
+int tmrec_free(tmrec_t*);
+int tmrec_destroy(tmrec_t*);
+
+int tr_parse_dtstart(tmrec_t*, char*);
+int tr_parse_dtend(tmrec_t*, char*);
+int tr_parse_duration(tmrec_t*, char*);
+int tr_parse_until(tmrec_t*, char*);
+int tr_parse_freq(tmrec_t*, char*);
+int tr_parse_interval(tmrec_t*, char*);
+int tr_parse_byday(tmrec_t*, char*);
+int tr_parse_bymday(tmrec_t*, char*);
+int tr_parse_byyday(tmrec_t*, char*);
+int tr_parse_bymonth(tmrec_t*, char*);
+int tr_parse_byweekno(tmrec_t*, char*);
+int tr_parse_wkst(tmrec_t*, char*);
+
+int tr_print(tmrec_t*);
+time_t ic_parse_datetime(char*,struct tm*);
+time_t ic_parse_duration(char*);
+
+tr_byxxx_t *ic_parse_byday(char*);
+tr_byxxx_t *ic_parse_byxxx(char*);
+int ic_parse_wkst(char*);
+
+int tr_check_recurrence(tmrec_t*, ac_tm_t*, tr_res_t*);
+int tr_parse_recurrence_string(tmrec_t *trp, char *rdef, char sep);
+
+
+#endif
+