core: utils/tmrec - use safer localtime_r() instead of localtime()
[sip-router] / src / core / utils / tmrec.c
1 /*
2  * Copyright (C) 2001-2003 FhG Fokus
3  *
4  * This file is part of Kamailio, a free SIP server.
5  *
6  * Kamailio is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version
10  *
11  * Kamailio is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 /**
23  * Time Recurence Library according to iCalendar (RFC 2445)
24  * - implemented at FhG Fokus, 2003 - external link:
25  *   https://github.com/miconda/tmrec
26  * \brief core/utils :: <description of this file>
27  * \ingroup core/utils
28  * Module: \ref core/utils
29  */
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <time.h>
34
35 #include "../../core/mem/mem.h"
36 #include "tmrec.h"
37
38
39 /**
40  * ===== imported from "utils.h"
41  */
42
43 static inline int tr_strz_to_int(char *_bp)
44 {
45         int _v;
46         char *_p;
47         if(!_bp)
48                 return 0;
49         _v = 0;
50         _p = _bp;
51         while(*_p && *_p>='0' && *_p<='9')
52         {
53                 _v += *_p - '0';
54                 _p++;
55         }
56         return _v;
57 }
58
59
60 static inline char* tr_trim(char* _s)
61 {
62         int len;
63         char* end;
64
65         /* Null pointer, there is nothing to do */
66         if (!_s) return _s;
67
68         /* Remove spaces and tabs from the beginning of string */
69         while ((*_s == ' ') || (*_s == '\t')) _s++;
70
71         len = strlen(_s);
72
73         end = _s + len - 1;
74
75         /* Remove trailing spaces and tabs */
76         while ((*end == ' ') || (*end == '\t')) end--;
77         if (end != (_s + len - 1)) {
78                 *(end+1) = '\0';
79         }
80
81         return _s;
82 }
83
84
85
86
87 /**
88  * ===== imported from "ac_tm.c"
89  */
90
91 /* #define USE_YWEEK_U          // Sunday system
92  * #define USE_YWEEK_V          // ISO 8601
93  */
94 #ifndef USE_YWEEK_U
95 #ifndef USE_YWEEK_V
96 #ifndef USE_YWEEK_W
97 #define USE_YWEEK_W             /* Monday system */.
98 #endif
99 #endif
100 #endif
101
102 #ifdef USE_YWEEK_U
103 #define SUN_WEEK(t)     (int)(((t)->tm_yday + 7 - \
104                                 ((t)->tm_wday)) / 7)
105 #else
106 #define MON_WEEK(t)     (int)(((t)->tm_yday + 7 - \
107                                 ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
108 #endif
109
110 #define ac_get_wday_yr(t) (int)((t)->tm_yday/7)
111 #define ac_get_wday_mr(t) (int)(((t)->tm_mday-1)/7)
112
113 ac_tm_t *ac_tm_new(void)
114 {
115         ac_tm_t *_atp = NULL;
116         _atp = (ac_tm_t*)pkg_malloc(sizeof(ac_tm_t));
117         if(!_atp)
118                 return NULL;
119         memset(_atp, 0, sizeof(ac_tm_t));
120
121         return _atp;
122 }
123
124 int ac_tm_fill(ac_tm_t *_atp, struct tm* _tm)
125 {
126         if(!_atp || !_tm)
127                 return -1;
128         _atp->t.tm_sec = _tm->tm_sec;       /* seconds */
129         _atp->t.tm_min = _tm->tm_min;       /* minutes */
130         _atp->t.tm_hour = _tm->tm_hour;     /* hours */
131         _atp->t.tm_mday = _tm->tm_mday;     /* day of the month */
132         _atp->t.tm_mon = _tm->tm_mon;       /* month */
133         _atp->t.tm_year = _tm->tm_year;     /* year */
134         _atp->t.tm_wday = _tm->tm_wday;     /* day of the week */
135         _atp->t.tm_yday = _tm->tm_yday;     /* day in the year */
136         _atp->t.tm_isdst = _tm->tm_isdst;   /* daylight saving time */
137
138         _atp->mweek = ac_get_mweek(_tm);
139         _atp->yweek = ac_get_yweek(_tm);
140         _atp->ywday = ac_get_wday_yr(_tm);
141         _atp->mwday = ac_get_wday_mr(_tm);
142         return 0;
143 }
144
145 int ac_tm_set_time(ac_tm_t *_atp, time_t _t)
146 {
147         struct tm tnow;
148         if(!_atp)
149                 return -1;
150         _atp->time = _t;
151         localtime_r(&_t, &tnow);
152         return ac_tm_fill(_atp, &tnow);
153 }
154
155 int ac_get_mweek(struct tm* _tm)
156 {
157         if(!_tm)
158                 return -1;
159 #ifdef USE_YWEEK_U
160         return ((_tm->tm_mday-1)/7 + (7-_tm->tm_wday+(_tm->tm_mday-1)%7)/7);
161 #else
162         return ((_tm->tm_mday-1)/7 + (7-(6+_tm->tm_wday)%7+(_tm->tm_mday-1)%7)/7);
163 #endif
164 }
165
166 int ac_get_yweek(struct tm* _tm)
167 {
168         int week = -1;
169 #ifdef USE_YWEEK_V
170         int days;
171 #endif
172
173         if(!_tm)
174                 return -1;
175
176 #ifdef USE_YWEEK_U
177         week = SUN_WEEK(_tm);
178 #else
179         week = MON_WEEK(_tm);
180 #endif
181
182 #ifdef USE_YWEEK_V
183         days = ((_tm->tm_yday + 7 - (_tm->tm_wday ? _tm->tm_wday-1 : 6)) % 7);
184
185         if(days >= 4)
186                 week++;
187         else
188                 if(week == 0)
189                         week = 53;
190 #endif
191         return week;
192 }
193
194 int ac_get_wkst(void)
195 {
196 #ifdef USE_YWEEK_U
197         return 0;
198 #else
199         return 1;
200 #endif
201 }
202
203 int ac_tm_reset(ac_tm_t *_atp)
204 {
205         if(!_atp)
206                 return -1;
207         memset(_atp, 0, sizeof(ac_tm_t));
208         return 0;
209 }
210
211 int ac_tm_free(ac_tm_t *_atp)
212 {
213         if(!_atp)
214                 return -1;
215         if(_atp->mv)
216                 pkg_free(_atp->mv);
217         pkg_free(_atp);
218         return 0;
219 }
220
221 int ac_tm_destroy(ac_tm_t *_atp)
222 {
223         if(!_atp)
224                 return -1;
225         if(_atp->mv)
226                 pkg_free(_atp->mv);
227         return 0;
228 }
229
230 ac_maxval_t *ac_get_maxval(ac_tm_t *_atp)
231 {
232         struct tm _tm;
233         int _v;
234         ac_maxval_t *_amp = NULL;
235
236         if(!_atp)
237                 return NULL;
238         _amp = (ac_maxval_t*)pkg_malloc(sizeof(ac_maxval_t));
239         if(!_amp)
240                 return NULL;
241
242         /* the number of the days in the year */
243         _amp->yday = 365 + tr_is_leap_year(_atp->t.tm_year+1900);
244
245         /* the number of the days in the month */
246         switch(_atp->t.tm_mon)
247         {
248                 case 1:
249                         if(_amp->yday == 366)
250                                 _amp->mday = 29;
251                         else
252                                 _amp->mday = 28;
253                 break;
254                 case 3: case 5: case 8: case 10:
255                         _amp->mday = 30;
256                 break;
257                 default:
258                         _amp->mday = 31;
259         }
260
261         /* maximum occurrences of a week day in the year */
262         memset(&_tm, 0, sizeof(struct tm));
263         _tm.tm_year = _atp->t.tm_year;
264         _tm.tm_mon = 11;
265         _tm.tm_mday = 31;
266         mktime(&_tm);
267         _v = 0;
268         if(_atp->t.tm_wday > _tm.tm_wday)
269                 _v = _atp->t.tm_wday - _tm.tm_wday + 1;
270         else
271                 _v = _tm.tm_wday - _atp->t.tm_wday;
272         _amp->ywday = (int)((_tm.tm_yday-_v)/7) + 1;
273
274         /* maximum number of weeks in the year */
275         _amp->yweek = ac_get_yweek(&_tm) + 1;
276
277         /* maximum number of the week day in the month */
278         _amp->mwday=(int)((_amp->mday-1-(_amp->mday-_atp->t.tm_mday)%7)/7)+1;
279
280         /* maximum number of weeks in the month */
281         _v = (_atp->t.tm_wday + (_amp->mday - _atp->t.tm_mday)%7)%7;
282 #ifdef USE_YWEEK_U
283         _amp->mweek = (int)((_amp->mday-1)/7+(7-_v+(_amp->mday-1)%7)/7)+1;
284 #else
285         _amp->mweek = (int)((_amp->mday-1)/7+(7-(6+_v)%7+(_amp->mday-1)%7)/7)+1;
286 #endif
287
288         _atp->mv = _amp;
289         return _amp;
290 }
291
292 int ac_print(ac_tm_t *_atp)
293 {
294         static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
295         if(!_atp)
296         {
297                 printf("\n(null)\n");
298                 return -1;
299         }
300
301         printf("\nSys time: %d\nTime: %02d:%02d:%02d\n", (int)_atp->time,
302                                 _atp->t.tm_hour, _atp->t.tm_min, _atp->t.tm_sec);
303         printf("Date: %s, %04d-%02d-%02d\n", _wdays[_atp->t.tm_wday],
304                                 _atp->t.tm_year+1900, _atp->t.tm_mon+1, _atp->t.tm_mday);
305         printf("Year day: %d\nYear week-day: %d\nYear week: %d\n", _atp->t.tm_yday,
306                         _atp->ywday, _atp->yweek);
307         printf("Month week: %d\nMonth week-day: %d\n", _atp->mweek, _atp->mwday);
308         if(_atp->mv)
309         {
310                 printf("Max ydays: %d\nMax yweeks: %d\nMax yweekday: %d\n",
311                                 _atp->mv->yday, _atp->mv->yweek, _atp->mv->ywday);;
312                 printf("Max mdays: %d\nMax mweeks: %d\nMax mweekday: %d\n",
313                                 _atp->mv->mday, _atp->mv->mweek, _atp->mv->mwday);;
314         }
315         return 0;
316 }
317
318
319
320
321
322 /************************ imported from "tmrec.c"  ***************************/
323
324 #define _D(c) ((c) -'0')
325
326 tr_byxxx_t *tr_byxxx_new(void)
327 {
328         tr_byxxx_t *_bxp = NULL;
329         _bxp = (tr_byxxx_t*)pkg_malloc(sizeof(tr_byxxx_t));
330         if(!_bxp)
331                 return NULL;
332         memset(_bxp, 0, sizeof(tr_byxxx_t));
333         return _bxp;
334 }
335
336 int tr_byxxx_init(tr_byxxx_t *_bxp, int _nr)
337 {
338         if(!_bxp)
339                 return -1;
340         _bxp->nr = _nr;
341         _bxp->xxx = (int*)pkg_malloc(_nr*sizeof(int));
342         if(!_bxp->xxx)
343                 return -1;
344         _bxp->req = (int*)pkg_malloc(_nr*sizeof(int));
345         if(!_bxp->req)
346         {
347                 pkg_free(_bxp->xxx);
348                 _bxp->xxx = NULL;
349                 return -1;
350         }
351
352         memset(_bxp->xxx, 0, _nr*sizeof(int));
353         memset(_bxp->req, 0, _nr*sizeof(int));
354
355         return 0;
356 }
357
358
359 int tr_byxxx_free(tr_byxxx_t *_bxp)
360 {
361         if(!_bxp)
362                 return -1;
363         if(_bxp->xxx)
364                 pkg_free(_bxp->xxx);
365         if(_bxp->req)
366                 pkg_free(_bxp->req);
367         pkg_free(_bxp);
368         return 0;
369 }
370
371 tmrec_t *tmrec_new(void)
372 {
373         tmrec_t *_trp = NULL;
374         _trp = (tmrec_t*)pkg_malloc(sizeof(tmrec_t));
375         if(!_trp)
376                 return NULL;
377         memset(_trp, 0, sizeof(tmrec_t));
378         localtime_r(&_trp->dtstart,&(_trp->ts));
379         return _trp;
380 }
381
382 int tmrec_free(tmrec_t *_trp)
383 {
384         if(!_trp)
385                 return -1;
386
387         tr_byxxx_free(_trp->byday);
388         tr_byxxx_free(_trp->bymday);
389         tr_byxxx_free(_trp->byyday);
390         tr_byxxx_free(_trp->bymonth);
391         tr_byxxx_free(_trp->byweekno);
392
393         pkg_free(_trp);
394         return 0;
395 }
396
397 int tmrec_destroy(tmrec_t *_trp)
398 {
399         if(!_trp)
400                 return -1;
401
402         tr_byxxx_free(_trp->byday);
403         tr_byxxx_free(_trp->bymday);
404         tr_byxxx_free(_trp->byyday);
405         tr_byxxx_free(_trp->bymonth);
406         tr_byxxx_free(_trp->byweekno);
407
408         return 0;
409 }
410
411 int tr_parse_dtstart(tmrec_t *_trp, char *_in)
412 {
413         if(!_trp || !_in)
414                 return -1;
415         _trp->dtstart = ic_parse_datetime(_in, &(_trp->ts));
416         return (_trp->dtstart==0)?-1:0;
417 }
418
419 int tr_parse_dtend(tmrec_t *_trp, char *_in)
420 {
421         struct tm _tm;
422         if(!_trp || !_in)
423                 return -1;
424         _trp->dtend = ic_parse_datetime(_in,&_tm);
425         return (_trp->dtend==0)?-1:0;
426 }
427
428 int tr_parse_duration(tmrec_t *_trp, char *_in)
429 {
430         if(!_trp || !_in)
431                 return -1;
432         _trp->duration = ic_parse_duration(_in);
433         return (_trp->duration==0)?-1:0;
434 }
435
436 int tr_parse_until(tmrec_t *_trp, char *_in)
437 {
438         struct tm _tm;
439         if(!_trp || !_in)
440                 return -1;
441         _trp->until = ic_parse_datetime(_in, &_tm);
442         return (_trp->until==0)?-1:0;
443 }
444
445 int tr_parse_freq(tmrec_t *_trp, char *_in)
446 {
447         if(!_trp || !_in)
448                 return -1;
449         if(!strcasecmp(_in, "daily"))
450         {
451                 _trp->freq = FREQ_DAILY;
452                 return 0;
453         }
454         if(!strcasecmp(_in, "weekly"))
455         {
456                 _trp->freq = FREQ_WEEKLY;
457                 return 0;
458         }
459         if(!strcasecmp(_in, "monthly"))
460         {
461                 _trp->freq = FREQ_MONTHLY;
462                 return 0;
463         }
464         if(!strcasecmp(_in, "yearly"))
465         {
466                 _trp->freq = FREQ_YEARLY;
467                 return 0;
468         }
469
470         _trp->freq = FREQ_NOFREQ;
471         return 0;
472 }
473
474 int tr_parse_interval(tmrec_t *_trp, char *_in)
475 {
476         if(!_trp || !_in)
477                 return -1;
478         _trp->interval = tr_strz_to_int(_in);
479         return 0;
480 }
481
482 int tr_parse_byday(tmrec_t *_trp, char *_in)
483 {
484         if(!_trp || !_in)
485                 return -1;
486         _trp->byday = ic_parse_byday(_in);
487         return 0;
488 }
489
490 int tr_parse_bymday(tmrec_t *_trp, char *_in)
491 {
492         if(!_trp || !_in)
493                 return -1;
494         _trp->bymday = ic_parse_byxxx(_in);
495         return 0;
496 }
497
498 int tr_parse_byyday(tmrec_t *_trp, char *_in)
499 {
500         if(!_trp || !_in)
501                 return -1;
502         _trp->byyday = ic_parse_byxxx(_in);
503         return 0;
504 }
505
506 int tr_parse_bymonth(tmrec_t *_trp, char *_in)
507 {
508         if(!_trp || !_in)
509                 return -1;
510         _trp->bymonth = ic_parse_byxxx(_in);
511         return 0;
512 }
513
514 int tr_parse_byweekno(tmrec_t *_trp, char *_in)
515 {
516         if(!_trp || !_in)
517                 return -1;
518         _trp->byweekno = ic_parse_byxxx(_in);
519         return 0;
520 }
521
522 int tr_parse_wkst(tmrec_t *_trp, char *_in)
523 {
524         if(!_trp || !_in)
525                 return -1;
526         _trp->wkst = ic_parse_wkst(_in);
527         return 0;
528 }
529
530 int tr_print(tmrec_t *_trp)
531 {
532         static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
533         int i;
534
535         if(!_trp)
536         {
537                 printf("\n(null)\n");
538                 return -1;
539         }
540         printf("Recurrence definition\n-- start time ---\n");
541         printf("Sys time: %d\n", (int)_trp->dtstart);
542         printf("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour,
543                                 _trp->ts.tm_min, _trp->ts.tm_sec);
544         printf("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday],
545                                 _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday);
546         printf("---\n");
547         printf("End time: %d\n", (int)_trp->dtend);
548         printf("Duration: %d\n", (int)_trp->duration);
549         printf("Until: %d\n", (int)_trp->until);
550         printf("Freq: %d\n", (int)_trp->freq);
551         printf("Interval: %d\n", (int)_trp->interval);
552         if(_trp->byday)
553         {
554                 printf("Byday: ");
555                 for(i=0; i<_trp->byday->nr; i++)
556                         printf(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]);
557                 printf("\n");
558         }
559         if(_trp->bymday)
560         {
561                 printf("Bymday: %d:", _trp->bymday->nr);
562                 for(i=0; i<_trp->bymday->nr; i++)
563                         printf(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]);
564                 printf("\n");
565         }
566         if(_trp->byyday)
567         {
568                 printf("Byyday:");
569                 for(i=0; i<_trp->byyday->nr; i++)
570                         printf(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]);
571                 printf("\n");
572         }
573         if(_trp->bymonth)
574         {
575                 printf("Bymonth: %d:", _trp->bymonth->nr);
576                 for(i=0; i< _trp->bymonth->nr; i++)
577                         printf(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]);
578                 printf("\n");
579         }
580         if(_trp->byweekno)
581         {
582                 printf("Byweekno: ");
583                 for(i=0; i<_trp->byweekno->nr; i++)
584                         printf(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]);
585                 printf("\n");
586         }
587         printf("Weekstart: %d\n", _trp->wkst);
588         return 0;
589 }
590
591 time_t ic_parse_datetime(char *_in, struct tm *_tm)
592 {
593         if(!_in || !_tm || strlen(_in)!=15)
594                 return 0;
595
596         memset(_tm, 0, sizeof(struct tm));
597         _tm->tm_year = _D(_in[0])*1000 + _D(_in[1])*100
598                         + _D(_in[2])*10 + _D(_in[3]) - 1900;
599         _tm->tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1;
600         _tm->tm_mday = _D(_in[6])*10 + _D(_in[7]);
601         _tm->tm_hour = _D(_in[9])*10 + _D(_in[10]);
602         _tm->tm_min = _D(_in[11])*10 + _D(_in[12]);
603         _tm->tm_sec = _D(_in[13])*10 + _D(_in[14]);
604         _tm->tm_isdst = -1 /*daylight*/;
605         return mktime(_tm);
606 }
607
608 time_t ic_parse_duration(char *_in)
609 {
610         time_t _t, _ft;
611         char *_p;
612         int _fl;
613
614         if(!_in || (*_in!='+' && *_in!='-' && *_in!='P' && *_in!='p'))
615                 return 0;
616
617         if(*_in == 'P' || *_in=='p')
618                 _p = _in+1;
619         else
620         {
621                 if(strlen(_in)<2 || (_in[1]!='P' && _in[1]!='p'))
622                         return 0;
623                 _p = _in+2;
624         }
625
626         _t = _ft = 0;
627         _fl = 1;
628
629         while(*_p)
630         {
631                 switch(*_p)
632                 {
633                         case '0': case '1': case '2':
634                         case '3': case '4': case '5':
635                         case '6': case '7': case '8':
636                         case '9':
637                                 _t = _t*10 + *_p - '0';
638                         break;
639
640                         case 'w':
641                         case 'W':
642                                 if(!_fl)
643                                         return 0;
644                                 _ft += _t*7*24*3600;
645                                 _t = 0;
646                         break;
647                         case 'd':
648                         case 'D':
649                                 if(!_fl)
650                                         return 0;
651                                 _ft += _t*24*3600;
652                                 _t = 0;
653                         break;
654                         case 'h':
655                         case 'H':
656                                 if(_fl)
657                                         return 0;
658                                 _ft += _t*3600;
659                                 _t = 0;
660                         break;
661                         case 'm':
662                         case 'M':
663                                 if(_fl)
664                                         return 0;
665                                 _ft += _t*60;
666                                 _t = 0;
667                         break;
668                         case 's':
669                         case 'S':
670                                 if(_fl)
671                                         return 0;
672                                 _ft += _t;
673                                 _t = 0;
674                         break;
675                         case 't':
676                         case 'T':
677                                 if(!_fl)
678                                         return 0;
679                                 _fl = 0;
680                         break;
681                         default:
682                                 return 0;
683                 }
684                 _p++;
685         }
686
687         return _ft;
688 }
689
690 tr_byxxx_t *ic_parse_byday(char *_in)
691 {
692         tr_byxxx_t *_bxp = NULL;
693         int _nr, _s, _v;
694         char *_p;
695
696         if(!_in)
697                 return NULL;
698         _bxp = tr_byxxx_new();
699         if(!_bxp)
700                 return NULL;
701         _p = _in;
702         _nr = 1;
703         while(*_p)
704         {
705                 if(*_p == ',')
706                         _nr++;
707                 _p++;
708         }
709         if(tr_byxxx_init(_bxp, _nr) < 0)
710         {
711                 tr_byxxx_free(_bxp);
712                 return NULL;
713         }
714         _p = _in;
715         _nr = _v = 0;
716         _s = 1;
717         while(*_p && _nr < _bxp->nr)
718         {
719                 switch(*_p)
720                 {
721                         case '0': case '1': case '2':
722                         case '3': case '4': case '5':
723                         case '6': case '7': case '8':
724                         case '9':
725                                 _v = _v*10 + *_p - '0';
726                         break;
727
728                         case 's':
729                         case 'S':
730                                 _p++;
731                                 switch(*_p)
732                                 {
733                                         case 'a':
734                                         case 'A':
735                                                 _bxp->xxx[_nr] = WDAY_SA;
736                                                 _bxp->req[_nr] = _s*_v;
737                                         break;
738                                         case 'u':
739                                         case 'U':
740                                                 _bxp->xxx[_nr] = WDAY_SU;
741                                                 _bxp->req[_nr] = _s*_v;
742                                         break;
743                                         default:
744                                                 goto error;
745                                 }
746                                 _s = 1;
747                                 _v = 0;
748                         break;
749                         case 'm':
750                         case 'M':
751                                 _p++;
752                                 if(*_p!='o' && *_p!='O')
753                                         goto error;
754                                 _bxp->xxx[_nr] = WDAY_MO;
755                                 _bxp->req[_nr] = _s*_v;
756                                 _s = 1;
757                                 _v = 0;
758                         break;
759                         case 't':
760                         case 'T':
761                                 _p++;
762                                 switch(*_p)
763                                 {
764                                         case 'h':
765                                         case 'H':
766                                                 _bxp->xxx[_nr] = WDAY_TH;
767                                                 _bxp->req[_nr] = _s*_v;
768                                         break;
769                                         case 'u':
770                                         case 'U':
771                                                 _bxp->xxx[_nr] = WDAY_TU;
772                                                 _bxp->req[_nr] = _s*_v;
773                                         break;
774                                         default:
775                                                 goto error;
776                                 }
777                                 _s = 1;
778                                 _v = 0;
779                         break;
780                         case 'w':
781                         case 'W':
782                                 _p++;
783                                 if(*_p!='e' && *_p!='E')
784                                         goto error;
785                                 _bxp->xxx[_nr] = WDAY_WE;
786                                 _bxp->req[_nr] = _s*_v;
787                                 _s = 1;
788                                 _v = 0;
789                         break;
790                         case 'f':
791                         case 'F':
792                                 _p++;
793                                 if(*_p!='r' && *_p!='R')
794                                         goto error;
795                                 _bxp->xxx[_nr] = WDAY_FR;
796                                 _bxp->req[_nr] = _s*_v;
797                                 _s = 1;
798                                 _v = 0;
799                         break;
800                         case '-':
801                                 _s = -1;
802                         break;
803                         case '+':
804                         case ' ':
805                         case '\t':
806                         break;
807                         case ',':
808                                 _nr++;
809                         break;
810                         default:
811                                 goto error;
812                 }
813                 _p++;
814         }
815
816         return _bxp;
817
818 error:
819         tr_byxxx_free(_bxp);
820         return NULL;
821 }
822
823 tr_byxxx_t *ic_parse_byxxx(char *_in)
824 {
825         tr_byxxx_t *_bxp = NULL;
826         int _nr, _s, _v;
827         char *_p;
828
829         if(!_in)
830                 return NULL;
831         _bxp = tr_byxxx_new();
832         if(!_bxp)
833                 return NULL;
834         _p = _in;
835         _nr = 1;
836         while(*_p)
837         {
838                 if(*_p == ',')
839                         _nr++;
840                 _p++;
841         }
842         if(tr_byxxx_init(_bxp, _nr) < 0)
843         {
844                 tr_byxxx_free(_bxp);
845                 return NULL;
846         }
847         _p = _in;
848         _nr = _v = 0;
849         _s = 1;
850         while(*_p && _nr < _bxp->nr)
851         {
852                 switch(*_p)
853                 {
854                         case '0': case '1': case '2':
855                         case '3': case '4': case '5':
856                         case '6': case '7': case '8':
857                         case '9':
858                                 _v = _v*10 + *_p - '0';
859                         break;
860
861                         case '-':
862                                 _s = -1;
863                         break;
864                         case '+':
865                         case ' ':
866                         case '\t':
867                         break;
868                         case ',':
869                                 _bxp->xxx[_nr] = _v;
870                                 _bxp->req[_nr] = _s;
871                                 _s = 1;
872                                 _v = 0;
873                                 _nr++;
874                         break;
875                         default:
876                                 goto error;
877                 }
878                 _p++;
879         }
880         if(_nr < _bxp->nr)
881         {
882                 _bxp->xxx[_nr] = _v;
883                 _bxp->req[_nr] = _s;
884         }
885         return _bxp;
886
887 error:
888         tr_byxxx_free(_bxp);
889         return NULL;
890 }
891
892 int ic_parse_wkst(char *_in)
893 {
894         if(!_in || strlen(_in)!=2)
895                 goto error;
896
897         switch(_in[0])
898         {
899                 case 's':
900                 case 'S':
901                         switch(_in[1])
902                         {
903                                 case 'a':
904                                 case 'A':
905                                         return WDAY_SA;
906                                 case 'u':
907                                 case 'U':
908                                         return WDAY_SU;
909                                 default:
910                                         goto error;
911                         }
912                 case 'm':
913                 case 'M':
914                         if(_in[1]!='o' && _in[1]!='O')
915                                 goto error;
916                         return WDAY_MO;
917                 case 't':
918                 case 'T':
919                         switch(_in[1])
920                         {
921                                 case 'h':
922                                 case 'H':
923                                         return WDAY_TH;
924                                 case 'u':
925                                 case 'U':
926                                         return WDAY_TU;
927                                 default:
928                                         goto error;
929                         }
930                 case 'w':
931                 case 'W':
932                         if(_in[1]!='e' && _in[1]!='E')
933                                 goto error;
934                         return WDAY_WE;
935                 case 'f':
936                 case 'F':
937                         if(_in[1]!='r' && _in[1]!='R')
938                                 goto error;
939                         return WDAY_FR;
940                 break;
941                 default:
942                         goto error;
943         }
944
945 error:
946 #ifdef USE_YWEEK_U
947         return WDAY_SU;
948 #else
949         return WDAY_MO;
950 #endif
951 }
952
953
954 /**
955  * =====imported from "checktr.c"
956  */
957
958 #define REC_ERR    -1
959 #define REC_MATCH   0
960 #define REC_NOMATCH 1
961
962 #define _IS_SET(x) (((x)>0)?1:0)
963
964 /*** local headers ***/
965 int get_min_interval(tmrec_t*);
966 int check_min_unit(tmrec_t*, ac_tm_t*, tr_res_t*);
967 int check_freq_interval(tmrec_t *_trp, ac_tm_t *_atp);
968 int check_byxxx(tmrec_t*, ac_tm_t*);
969
970 /**
971  *
972  * return 0/REC_MATCH - the time falls in
973  *       -1/REC_ERR - error
974  *        1/REC_NOMATCH - the time falls out
975  */
976 int tr_check_recurrence(tmrec_t *_trp, ac_tm_t *_atp, tr_res_t *_tsw)
977 {
978         if(!_trp || !_atp || (!_IS_SET(_trp->duration) && !_IS_SET(_trp->dtend)))
979                 return REC_ERR;
980
981         /* it is before start date */
982         if(_atp->time < _trp->dtstart)
983                 return REC_NOMATCH;
984
985         /* compute the duration of the recurrence interval */
986         if(!_IS_SET(_trp->duration))
987                 _trp->duration = _trp->dtend - _trp->dtstart;
988
989         if(_atp->time <= _trp->dtstart+_trp->duration)
990         {
991                 if(_tsw)
992                 {
993                         if(_tsw->flag & TSW_RSET)
994                         {
995                                 if(_tsw->rest>_trp->dtstart+_trp->duration-_atp->time)
996                                         _tsw->rest = _trp->dtstart+_trp->duration - _atp->time;
997                         }
998                         else
999                         {
1000                                 _tsw->flag |= TSW_RSET;
1001                                 _tsw->rest = _trp->dtstart+_trp->duration - _atp->time;
1002                         }
1003                 }
1004                 return REC_MATCH;
1005         }
1006
1007         /* after the bound of recurrence */
1008         if(_IS_SET(_trp->until) && _atp->time >= _trp->until + _trp->duration)
1009                 return REC_NOMATCH;
1010
1011         /* check if the instance of recurrence matches the 'interval' */
1012         if(check_freq_interval(_trp, _atp)!=REC_MATCH)
1013                 return REC_NOMATCH;
1014
1015         if(check_min_unit(_trp, _atp, _tsw)!=REC_MATCH)
1016                 return REC_NOMATCH;
1017
1018         if(check_byxxx(_trp, _atp)!=REC_MATCH)
1019                 return REC_NOMATCH;
1020
1021         return REC_MATCH;
1022 }
1023
1024
1025 int check_freq_interval(tmrec_t *_trp, ac_tm_t *_atp)
1026 {
1027         int _t0, _t1;
1028         struct tm _tm;
1029         if(!_trp || !_atp)
1030                 return REC_ERR;
1031
1032         if(!_IS_SET(_trp->freq))
1033                 return REC_NOMATCH;
1034
1035         if(!_IS_SET(_trp->interval) || _trp->interval==1)
1036                 return REC_MATCH;
1037
1038         switch(_trp->freq)
1039         {
1040                 case FREQ_DAILY:
1041                 case FREQ_WEEKLY:
1042                         memset(&_tm, 0, sizeof(struct tm));
1043                         _tm.tm_year = _trp->ts.tm_year;
1044                         _tm.tm_mon = _trp->ts.tm_mon;
1045                         _tm.tm_mday = _trp->ts.tm_mday;
1046                         _t0 = (int)mktime(&_tm);
1047                         memset(&_tm, 0, sizeof(struct tm));
1048                         _tm.tm_year = _atp->t.tm_year;
1049                         _tm.tm_mon = _atp->t.tm_mon;
1050                         _tm.tm_mday = _atp->t.tm_mday;
1051                         _t1 = (int)mktime(&_tm);
1052                         if(_trp->freq == FREQ_DAILY)
1053                                 return (((_t1-_t0)/(24*3600))%_trp->interval==0)?
1054                                         REC_MATCH:REC_NOMATCH;
1055 #ifdef USE_YWEEK_U
1056                         _t0 -= _trp->ts.tm_wday*24*3600;
1057                         _t1 -= _atp->t.tm_wday*24*3600;
1058 #else
1059                         _t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600;
1060                         _t1 -= ((_atp->t.tm_wday+6)%7)*24*3600;
1061 #endif
1062                         return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)?
1063                                         REC_MATCH:REC_NOMATCH;
1064                 case FREQ_MONTHLY:
1065                         _t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12
1066                                         + _atp->t.tm_mon-_trp->ts.tm_mon;
1067                         return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH;
1068                 case FREQ_YEARLY:
1069                         return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)?
1070                                         REC_MATCH:REC_NOMATCH;
1071         }
1072
1073         return REC_NOMATCH;
1074 }
1075
1076 int get_min_interval(tmrec_t *_trp)
1077 {
1078         if(!_trp)
1079                 return FREQ_NOFREQ;
1080
1081         if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday)
1082                 return FREQ_DAILY;
1083         if(_trp->freq == FREQ_WEEKLY || _trp->byweekno)
1084                 return FREQ_WEEKLY;
1085         if(_trp->freq == FREQ_MONTHLY || _trp->bymonth)
1086                 return FREQ_MONTHLY;
1087         if(_trp->freq == FREQ_YEARLY)
1088                 return FREQ_YEARLY;
1089
1090         return FREQ_NOFREQ;
1091 }
1092
1093 int check_min_unit(tmrec_t *_trp, ac_tm_t *_atp, tr_res_t *_tsw)
1094 {
1095         int _v0, _v1;
1096         if(!_trp || !_atp)
1097                 return REC_ERR;
1098         switch(get_min_interval(_trp))
1099         {
1100                 case FREQ_DAILY:
1101                 break;
1102                 case FREQ_WEEKLY:
1103                         if(_trp->ts.tm_wday != _atp->t.tm_wday)
1104                                 return REC_NOMATCH;
1105                 break;
1106                 case FREQ_MONTHLY:
1107                         if(_trp->ts.tm_mday != _atp->t.tm_mday)
1108                                 return REC_NOMATCH;
1109                 break;
1110                 case FREQ_YEARLY:
1111                         if(_trp->ts.tm_mon != _atp->t.tm_mon
1112                                         || _trp->ts.tm_mday != _atp->t.tm_mday)
1113                                 return REC_NOMATCH;
1114                 break;
1115                 default:
1116                         return REC_NOMATCH;
1117         }
1118         _v0 = _trp->ts.tm_hour*3600 + _trp->ts.tm_min*60 + _trp->ts.tm_sec;
1119         _v1 = _atp->t.tm_hour*3600 + _atp->t.tm_min*60 + _atp->t.tm_sec;
1120         if(_v1 >= _v0 && _v1 < _v0 + _trp->duration)
1121         {
1122                 if(_tsw)
1123                 {
1124                         if(_tsw->flag & TSW_RSET)
1125                         {
1126                                 if(_tsw->rest>_v0+_trp->duration-_v1)
1127                                         _tsw->rest = _v0 + _trp->duration - _v1;
1128                         }
1129                         else
1130                         {
1131                                 _tsw->flag |= TSW_RSET;
1132                                 _tsw->rest = _v0 + _trp->duration - _v1;
1133                         }
1134                 }
1135                 return REC_MATCH;
1136         }
1137
1138         return REC_NOMATCH;
1139 }
1140
1141 int check_byxxx(tmrec_t *_trp, ac_tm_t *_atp)
1142 {
1143         int i;
1144         ac_maxval_t *_amp = NULL;
1145         if(!_trp || !_atp)
1146                 return REC_ERR;
1147         if(!_trp->byday && !_trp->bymday && !_trp->byyday && !_trp->bymonth
1148                         && !_trp->byweekno)
1149                 return REC_MATCH;
1150
1151         _amp = ac_get_maxval(_atp);
1152         if(!_amp)
1153                 return REC_NOMATCH;
1154
1155         if(_trp->bymonth)
1156         {
1157                 for(i=0; i<_trp->bymonth->nr; i++)
1158                 {
1159                         if(_atp->t.tm_mon ==
1160                                         (_trp->bymonth->xxx[i]*_trp->bymonth->req[i]+12)%12)
1161                                 break;
1162                 }
1163                 if(i>=_trp->bymonth->nr)
1164                         return REC_NOMATCH;
1165         }
1166         if(_trp->freq==FREQ_YEARLY && _trp->byweekno)
1167         {
1168                 for(i=0; i<_trp->byweekno->nr; i++)
1169                 {
1170                         if(_atp->yweek == (_trp->byweekno->xxx[i]*_trp->byweekno->req[i]+
1171                                                         _amp->yweek)%_amp->yweek)
1172                                 break;
1173                 }
1174                 if(i>=_trp->byweekno->nr)
1175                         return REC_NOMATCH;
1176         }
1177         if(_trp->byyday)
1178         {
1179                 for(i=0; i<_trp->byyday->nr; i++)
1180                 {
1181                         if(_atp->t.tm_yday == (_trp->byyday->xxx[i]*_trp->byyday->req[i]+
1182                                                 _amp->yday)%_amp->yday)
1183                                 break;
1184                 }
1185                 if(i>=_trp->byyday->nr)
1186                         return REC_NOMATCH;
1187         }
1188         if(_trp->bymday)
1189         {
1190                 for(i=0; i<_trp->bymday->nr; i++)
1191                 {
1192 #ifdef EXTRA_DEBUG
1193                         DBG("Req:bymday: %d == %d\n", _atp->t.tm_mday,
1194                                 (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
1195                                 _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0));
1196 #endif
1197                         if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
1198                                                 _amp->mday)%_amp->mday + (_trp->bymday->req[i]<0)?1:0)
1199                                 break;
1200                 }
1201                 if(i>=_trp->bymday->nr)
1202                         return REC_NOMATCH;
1203         }
1204         if(_trp->byday)
1205         {
1206                 for(i=0; i<_trp->byday->nr; i++)
1207                 {
1208                         if(_trp->freq==FREQ_YEARLY)
1209                         {
1210 #ifdef EXTRA_DEBUG
1211                                 DBG("Req:byday:y: %d==%d && %d==%d\n", _atp->t.tm_wday,
1212                                         _trp->byday->xxx[i], _atp->ywday+1,
1213                                         (_trp->byday->req[i]+_amp->ywday)%_amp->ywday);
1214 #endif
1215                                 if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
1216                                                 _atp->ywday+1 == (_trp->byday->req[i]+_amp->ywday)%
1217                                                 _amp->ywday)
1218                                         break;
1219                         }
1220                         else
1221                         {
1222                                 if(_trp->freq==FREQ_MONTHLY)
1223                                 {
1224 #ifdef EXTRA_DEBUG
1225                                         DBG("Req:byday:m: %d==%d && %d==%d\n", _atp->t.tm_wday,
1226                                                 _trp->byday->xxx[i], _atp->mwday+1,
1227                                                 (_trp->byday->req[i]+_amp->mwday)%_amp->mwday);
1228 #endif
1229                                         if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
1230                                                         _atp->mwday+1==(_trp->byday->req[i]+
1231                                                         _amp->mwday)%_amp->mwday)
1232                                                 break;
1233                                 }
1234                                 else
1235                                 {
1236                                         if(_atp->t.tm_wday == _trp->byday->xxx[i])
1237                                                 break;
1238                                 }
1239                         }
1240                 }
1241                 if(i>=_trp->byday->nr)
1242                         return REC_NOMATCH;
1243         }
1244
1245         return REC_MATCH;
1246 }
1247
1248 int tr_parse_recurrence_string(tmrec_t *trp, char *rdef, char sep)
1249 {
1250         char *p;
1251         char *s;
1252         int type;
1253
1254         type = 0;
1255         p = rdef;
1256
1257         memset(trp, 0, sizeof(tmrec_t));
1258
1259         do{
1260                 s = strchr(p, (int)sep);
1261                 if (s!=NULL)
1262                         *s = '\0';
1263                 /* LM_DBG("----- parsing tr param <%s>\n", p); */
1264                 if(s != p) {
1265                         switch(type) {
1266                                 case 0:
1267                                         if(tr_parse_dtstart(trp, p)<0)
1268                                                 goto error;
1269                                         break;
1270                                 case 1:
1271                                         if(tr_parse_duration(trp, p)<0)
1272                                                 goto error;
1273                                         break;
1274                                 case 2:
1275                                         if(tr_parse_freq(trp, p)<0)
1276                                                 goto error;
1277                                         break;
1278                                 case 3:
1279                                         if(tr_parse_until(trp, p)<0)
1280                                                 goto error;
1281                                         break;
1282                                 case 4:
1283                                         if(tr_parse_interval(trp, p)<0)
1284                                                 goto error;
1285                                         break;
1286                                 case 5:
1287                                         if(tr_parse_byday(trp, p)<0)
1288                                                 goto error;
1289                                         break;
1290                                 case 6:
1291                                         if(tr_parse_bymday(trp, p)<0)
1292                                                 goto error;
1293                                         break;
1294                                 case 7:
1295                                         if(tr_parse_byyday(trp, p)<0)
1296                                                 goto error;
1297                                         break;
1298                                 case 8:
1299                                         if(tr_parse_byweekno(trp, p)<0)
1300                                                 goto error;
1301                                         break;
1302                                 case 9:
1303                                         if(tr_parse_bymonth(trp, p)<0)
1304                                                 goto error;
1305                                         break;
1306                         }
1307                 }
1308                 type++;
1309                 if (s!=NULL) {
1310                         *s = sep;
1311                         p = s + 1;
1312                         if(*p==0)
1313                                 goto done;
1314                 } else {
1315                         goto done;
1316                 }
1317         } while(1);
1318
1319 done:
1320         return 0;
1321
1322 error:
1323         LM_ERR("failed to parse time recurrence [%s]\n", rdef);
1324         if (s!=NULL)
1325                 *s = sep;
1326         return -1;
1327 }
1328