Coverage Report

Created: 2026-10-28 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/time_rec.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2001-2003 FhG Fokus
3
 * Copyright (C) 2020 OpenSIPS Solutions
4
 *
5
 * This file is part of opensips, a free SIP server.
6
 *
7
 * opensips is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version
11
 *
12
 * opensips is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
#include <stdio.h>
23
#include <string.h>
24
#include <time.h>
25
26
#include "mem/mem.h"
27
#include "mem/shm_mem.h"
28
#include "lib/osips_malloc.h"
29
#include "time_rec.h"
30
#include "ut.h"
31
32
/*
33
 * USE_YWEEK_U  -- Sunday system - see strftime %U
34
 * USE_YWEEK_V  -- ISO 8601 - see strftime %V
35
 * USE_YWEEK_W  -- Monday system - see strftime %W
36
 */
37
38
#ifndef USE_YWEEK_U
39
#ifndef USE_YWEEK_V
40
#ifndef USE_YWEEK_W
41
  #define USE_YWEEK_W   /* Monday system */
42
#endif
43
#endif
44
#endif
45
46
0
#define WDAY_SU 0
47
0
#define WDAY_MO 1
48
0
#define WDAY_TU 2
49
0
#define WDAY_WE 3
50
0
#define WDAY_TH 4
51
0
#define WDAY_FR 5
52
0
#define WDAY_SA 6
53
#define WDAY_NU 7
54
55
0
#define SEC_DAILY        (60 * 60 * 24)
56
0
#define SEC_WEEKLY       (7 * SEC_DAILY)
57
0
#define SEC_MONTHLY_MAX  (31 * SEC_DAILY)
58
0
#define SEC_YEARLY_MAX   (366 * SEC_DAILY)
59
60
#ifdef USE_YWEEK_U
61
#define SUN_WEEK(t) (int)(((t)->tm_yday + 7 - \
62
        ((t)->tm_wday)) / 7)
63
#else
64
0
#define MON_WEEK(t) (int)(((t)->tm_yday + 7 - \
65
0
        ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
66
#endif
67
68
0
#define ac_get_wday_yr(t) (int)((t)->tm_yday/7)
69
0
#define ac_get_wday_mr(t) (int)(((t)->tm_mday-1)/7)
70
71
0
#define is_leap_year(yyyy) ((((yyyy)%400))?(((yyyy)%100)?(((yyyy)%4)?0:1):0):1)
72
73
#define REC_ERR    -1
74
0
#define REC_MATCH   0
75
0
#define REC_NOMATCH 1
76
77
0
#define _IS_SET(x) ((x) != TR_NOVAL)
78
0
#define _D(c) ((c) -'0')
79
80
0
#define TR_SEPARATOR '|'
81
82
#define load_TR_value( _p,_s, _tr, _func, _err, _done) \
83
0
  do{ \
84
0
    int _rc = 0; \
85
0
    _s = strchr(_p, (int)TR_SEPARATOR); \
86
0
    if (_s) \
87
0
      *_s = 0; \
88
0
    /* LM_DBG("----parsing tr param <%s>\n",_p); \ */\
89
0
    if(_s != _p) {\
90
0
      _rc = _func( _tr, _p); \
91
0
      if (_rc < 0) {\
92
0
        LM_DBG("func error\n"); \
93
0
        if (_s) *_s = TR_SEPARATOR; \
94
0
        goto _err; \
95
0
      } \
96
0
    } \
97
0
    if (_s) { \
98
0
      *_s = TR_SEPARATOR; \
99
0
      if (_rc == 0) \
100
0
        _p = _s+1; /* rc > 1 means: "input not consumed" */ \
101
0
      if ( *(_p)==0 ) \
102
0
        goto _done; \
103
0
    } else if (_rc == 0) { /* if all "input is consumed" */ \
104
0
      goto _done; \
105
0
    }\
106
0
  } while(0)
107
108
#if 0 /* debugging mode */
109
#define LM_DEV LM_ERR
110
#else
111
#define LM_DEV(...)
112
#endif
113
114
typedef struct _ac_maxval
115
{
116
  int yweek;
117
  int yday;  /* current year's max days (365-366) */
118
  int ywday;
119
  int mweek; /* current month's max number of weeks (4-5) */
120
  int mday;  /* current month's max days (28-31) */
121
  int mwday; /* current month's max occurrences of current day (4-5) */
122
} ac_maxval_t, *ac_maxval_p;
123
124
typedef struct _ac_tm
125
{
126
  time_t time;
127
  struct tm t;
128
  int mweek;
129
  int yweek;
130
  int ywday;
131
  int wom; /* current day's week of the month (0-4) */
132
  char flags;
133
} ac_tm_t, *ac_tm_p;
134
135
0
#define TR_OP_NUL 0
136
0
#define TR_OP_AND 1
137
0
#define TR_OP_OR  2
138
139
int ac_tm_reset(ac_tm_p);
140
141
int ac_get_mweek(struct tm*);
142
int ac_get_yweek(struct tm*);
143
int ac_get_wkst();
144
145
int ac_print(ac_tm_p);
146
147
time_t ic_parse_datetime(char*,struct tm*);
148
time_t ic_parse_duration(char*);
149
150
tr_byxxx_p ic_parse_byday(char*, char);
151
tr_byxxx_p ic_parse_byxxx(char*, char);
152
int ic_parse_wkst(char*);
153
154
155
static inline int strz2int(char *_bp)
156
0
{
157
0
  int _v;
158
0
  char *_p;
159
160
0
  _v = 0;
161
0
  _p = _bp;
162
0
  while(*_p && *_p>='0' && *_p<='9')
163
0
  {
164
0
    _v += *_p - '0';
165
0
    _p++;
166
0
  }
167
0
  return _v;
168
0
}
169
170
171
/**
172
 * Check if @x falls within the recurring [@bgn, @end) time interval,
173
 * according to @freq.
174
 *
175
 * @x: value to check
176
 * @bgn: interval start
177
 * @end: interval end
178
 * @dur: duration of the interval (effectively: @end - @bgn)
179
 * @freq: FREQ_WEEKLY / FREQ_MONTHLY / FREQ_YEARLY
180
 *
181
 * Return: REC_MATCH or REC_NOMATCH
182
 */
183
int check_recur_itv(struct tm *x, struct tm *bgn, struct tm *end,
184
                    time_t dur, int freq);
185
186
187
static inline void ac_tm_fill(ac_tm_p _atp, struct tm* _tm)
188
0
{
189
0
  _atp->t = *_tm;
190
191
#if 0
192
  _atp->mweek = ac_get_mweek(_tm);
193
#endif
194
0
  _atp->yweek = ac_get_yweek(_tm);
195
0
  _atp->ywday = ac_get_wday_yr(_tm);
196
0
  _atp->wom = ac_get_wday_mr(_tm);
197
0
}
198
199
200
0
#define TZ_INTACT ((char *)-1)
201
static char *old_tz = TZ_INTACT;
202
203
void tz_set(const str *tz)
204
0
{
205
0
#define TZBUF_SZ 50
206
0
  char tzbuf[TZBUF_SZ];
207
208
0
  if (tz->len >= TZBUF_SZ)
209
0
    return;
210
211
0
  memcpy(tzbuf, tz->s, tz->len);
212
0
  tzbuf[tz->len] = '\0';
213
214
0
  _tz_set(tzbuf);
215
0
#undef TZBUF_SZ
216
0
}
217
218
219
void _tz_set(const char *tz)
220
0
{
221
0
  LM_DBG("setting timezone to: '%s'\n", tz);
222
223
0
  old_tz = getenv("TZ");
224
225
0
  setenv("TZ", tz, 1);
226
0
  tzset();
227
0
}
228
229
230
void tz_reset(void)
231
0
{
232
0
  if (old_tz == TZ_INTACT)
233
0
    return;
234
235
0
  if (!old_tz) {
236
0
    LM_DBG("resetting timezone to system default\n");
237
0
    unsetenv("TZ");
238
0
  } else {
239
0
    LM_DBG("resetting timezone to '%s'\n", old_tz);
240
0
    setenv("TZ", old_tz, 1);
241
0
  }
242
243
0
  tzset();
244
0
  old_tz = TZ_INTACT;
245
0
}
246
247
248
time_t tz_adjust_ts(time_t unix_time, const str *tz)
249
0
{
250
0
  struct tm local_tm;
251
0
  time_t adj_ts;
252
253
0
  tz_set(_str("UTC"));
254
0
  localtime_r(&unix_time, &local_tm );
255
0
  tz_reset();
256
257
0
  if (tz)
258
0
    tz_set(tz);
259
260
0
  adj_ts = mktime(&local_tm);
261
0
  tz_reset();
262
263
0
  if (local_tm.tm_isdst > 0)
264
0
    adj_ts -= 3600;
265
266
0
  LM_DBG("UNIX ts: %ld, local-adjusted ts: %ld (tz: '%.*s', DST: %s)\n",
267
0
         (long int)unix_time, (long int)adj_ts, tz ? tz->len : 4,
268
0
         tz ? tz->s : "null", local_tm.tm_isdst == 1 ? "on" :
269
0
         local_tm.tm_isdst == 0 ? "off" : "unavail");
270
271
0
  return adj_ts;
272
0
}
273
274
275
static inline void ac_tm_set_time(ac_tm_p _atp, time_t _t)
276
0
{
277
0
  struct tm ltime;
278
279
0
  memset(_atp, 0, sizeof *_atp);
280
0
  _atp->time = _t;
281
282
0
  localtime_r(&_t, &ltime);
283
0
  ac_tm_fill(_atp, &ltime);
284
0
}
285
286
int ac_get_mweek(struct tm* _tm)
287
0
{
288
0
  if(!_tm)
289
0
    return -1;
290
#ifdef USE_YWEEK_U
291
  return ((_tm->tm_mday-1)/7 + (7-_tm->tm_wday+(_tm->tm_mday-1)%7)/7);
292
#else
293
0
  return ((_tm->tm_mday-1)/7 + (7-(6+_tm->tm_wday)%7+(_tm->tm_mday-1)%7)/7);
294
0
#endif
295
0
}
296
297
298
int ac_get_yweek(struct tm* _tm)
299
0
{
300
0
  int week = -1;
301
#ifdef USE_YWEEK_V
302
  int days;
303
#endif
304
305
0
  if(!_tm)
306
0
    return -1;
307
308
#ifdef USE_YWEEK_U
309
  week = SUN_WEEK(_tm);
310
#else
311
0
  week = MON_WEEK(_tm);
312
0
#endif
313
314
#ifdef USE_YWEEK_V
315
  days = ((_tm->tm_yday + 7 - (_tm->tm_wday ? _tm->tm_wday-1 : 6)) % 7);
316
317
  if(days >= 4)
318
    week++;
319
  else
320
    if(week == 0)
321
      week = 53;
322
#endif
323
0
  return week;
324
0
}
325
326
int ac_get_wkst(void)
327
0
{
328
#ifdef USE_YWEEK_U
329
  return 0;
330
#else
331
0
  return 1;
332
0
#endif
333
0
}
334
335
int ac_tm_reset(ac_tm_p _atp)
336
0
{
337
0
  if(!_atp)
338
0
    return -1;
339
0
  memset(_atp, 0, sizeof(ac_tm_t));
340
0
  return 0;
341
0
}
342
343
static ac_maxval_p ac_get_maxval(ac_tm_p _atp)
344
0
{
345
0
  static ac_maxval_t _amp;
346
0
  struct tm _tm;
347
0
  int _v;
348
349
  /* the number of the days in the year */
350
0
  _amp.yday = 365 + is_leap_year(_atp->t.tm_year+1900);
351
352
  /* the number of the days in the month */
353
0
  switch(_atp->t.tm_mon)
354
0
  {
355
0
    case 1:
356
0
      if(_amp.yday == 366)
357
0
        _amp.mday = 29;
358
0
      else
359
0
        _amp.mday = 28;
360
0
    break;
361
0
    case 3: case 5: case 8: case 10:
362
0
      _amp.mday = 30;
363
0
    break;
364
0
    default:
365
0
      _amp.mday = 31;
366
0
  }
367
368
  /* maximum occurrences of a week day in the year */
369
0
  memset(&_tm, 0, sizeof(struct tm));
370
0
  _tm.tm_year = _atp->t.tm_year;
371
0
  _tm.tm_mon = 11;
372
0
  _tm.tm_mday = 31;
373
0
  mktime(&_tm);
374
0
  _v = 0;
375
0
  if(_atp->t.tm_wday > _tm.tm_wday)
376
0
    _v = _atp->t.tm_wday - _tm.tm_wday + 1;
377
0
  else
378
0
    _v = _tm.tm_wday - _atp->t.tm_wday;
379
0
  _amp.ywday = (int)((_tm.tm_yday-_v)/7) + 1;
380
381
  /* maximum number of weeks in the year */
382
0
  _amp.yweek = ac_get_yweek(&_tm) + 1;
383
384
  /* maximum number of the week day in the month */
385
0
  _amp.mwday=(int)((_amp.mday-1-(_amp.mday-_atp->t.tm_mday)%7)/7)+1;
386
387
#if 0
388
  /* maximum number of weeks in the month */
389
  _v = (_atp->t.tm_wday + (_amp.mday - _atp->t.tm_mday)%7)%7;
390
#ifdef USE_YWEEK_U
391
  _amp.mweek = (int)((_amp.mday-1)/7+(7-_v+(_amp.mday-1)%7)/7)+1;
392
#else
393
  _amp.mweek = (int)((_amp.mday-1)/7+(7-(6+_v)%7+(_amp.mday-1)%7)/7)+1;
394
#endif
395
#endif
396
397
0
  return &_amp;
398
0
}
399
400
401
int ac_print(ac_tm_p _atp)
402
0
{
403
0
  static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
404
0
  if(!_atp)
405
0
  {
406
0
    printf("\n(null)\n");
407
0
    return -1;
408
0
  }
409
410
0
  printf("\nSys time: %d\nTime: %02d:%02d:%02d\n", (int)_atp->time,
411
0
        _atp->t.tm_hour, _atp->t.tm_min, _atp->t.tm_sec);
412
0
  printf("Date: %s, %04d-%02d-%02d\n", _wdays[_atp->t.tm_wday],
413
0
        _atp->t.tm_year+1900, _atp->t.tm_mon+1, _atp->t.tm_mday);
414
0
  printf("Year day: %d\nYear week-day: %d\nYear week: %d\n", _atp->t.tm_yday,
415
0
      _atp->ywday, _atp->yweek);
416
0
  printf("Month week: %d\nMonth week-day: %d\n", _atp->mweek, _atp->wom);
417
0
  return 0;
418
0
}
419
420
421
422
423
424
425
tr_byxxx_p tr_byxxx_new(char alloc)
426
0
{
427
0
  tr_byxxx_p _bxp = NULL;
428
0
  if (alloc & PKG_ALLOC)
429
0
    _bxp = (tr_byxxx_p)pkg_malloc(sizeof(tr_byxxx_t));
430
0
  else
431
0
    _bxp = (tr_byxxx_p)shm_malloc(sizeof(tr_byxxx_t));
432
0
  if(!_bxp)
433
0
    return NULL;
434
0
  memset(_bxp, 0, sizeof(tr_byxxx_t));
435
0
  _bxp->flags = alloc;
436
0
  return _bxp;
437
0
}
438
439
int tr_byxxx_init(tr_byxxx_p _bxp, int _nr)
440
0
{
441
0
  _bxp->nr = _nr;
442
0
  if (_bxp->flags & PKG_ALLOC) {
443
0
    _bxp->xxx = (int*)pkg_malloc(_nr*sizeof(int));
444
0
    _bxp->req = (int*)pkg_malloc(_nr*sizeof(int));
445
0
  } else {
446
0
    _bxp->xxx = (int*)shm_malloc(_nr*sizeof(int));
447
0
    _bxp->req = (int*)shm_malloc(_nr*sizeof(int));
448
0
  }
449
450
0
  if (!_bxp->xxx || !_bxp->req)
451
0
    goto oom;
452
453
0
  memset(_bxp->xxx, 0, _nr*sizeof(int));
454
0
  memset(_bxp->req, 0, _nr*sizeof(int));
455
456
0
  return 0;
457
0
oom:
458
0
  LM_ERR("oom\n");
459
0
  if (_bxp->flags & PKG_ALLOC) {
460
0
    pkg_free(_bxp->xxx);
461
0
    pkg_free(_bxp->req);
462
0
  } else {
463
0
    shm_free(_bxp->xxx);
464
0
    shm_free(_bxp->req);
465
0
  }
466
0
  _bxp->xxx = NULL;
467
0
  _bxp->req = NULL;
468
0
  return -1;
469
0
}
470
471
472
int tr_byxxx_free(tr_byxxx_p _bxp)
473
0
{
474
0
  if (!_bxp)
475
0
    return -1;
476
477
0
  if (_bxp->flags & PKG_ALLOC) {
478
0
    pkg_free(_bxp->xxx);
479
0
    pkg_free(_bxp->req);
480
0
    pkg_free(_bxp);
481
0
  } else {
482
0
    shm_free(_bxp->xxx);
483
0
    shm_free(_bxp->req);
484
0
    shm_free(_bxp);
485
0
  }
486
487
0
  return 0;
488
0
}
489
490
void tmrec_init(tmrec_p t)
491
0
{
492
0
  memset(t, 0, sizeof *t);
493
494
  /* these values may be legitimately set to 0 (i.e. UNIX start time) */
495
0
  t->dtstart  = TR_NOVAL;
496
0
  t->dtend    = TR_NOVAL;
497
0
  t->duration = TR_NOVAL;
498
499
0
  t->freq     = TR_NOVAL;
500
0
  t->until    = TR_NOVAL;
501
0
  t->interval = TR_NOVAL;
502
0
}
503
504
static inline void tmrec_expr_init(tmrec_expr_t *e)
505
0
{
506
0
  memset(e, 0, sizeof *e);
507
0
  INIT_LIST_HEAD(&e->operands);
508
0
  tmrec_init(&e->tr);
509
0
}
510
511
static void tmrec_free_members(tmrec_p, char);
512
513
tmrec_p tmrec_new(char alloc)
514
0
{
515
0
  tmrec_p _trp;
516
0
  if (alloc & PKG_ALLOC)
517
0
    _trp = (tmrec_p)pkg_malloc(sizeof(tmrec_t));
518
0
  else
519
0
    _trp = (tmrec_p)shm_malloc(sizeof(tmrec_t));
520
0
  if(!_trp)
521
0
    return NULL;
522
523
0
  tmrec_init(_trp);
524
0
  _trp->flags = alloc;
525
0
  return _trp;
526
0
}
527
528
void tmrec_free(tmrec *tr)
529
0
{
530
0
  tmrec_p _trp = (tmrec_p)tr;
531
532
0
  if(!_trp)
533
0
    return;
534
535
0
  tmrec_free_members(_trp, _trp->flags);
536
537
0
  if (_trp->flags & PKG_ALLOC) {
538
0
    pkg_free(_trp);
539
0
  } else {
540
0
    shm_free(_trp);
541
0
  }
542
0
}
543
544
static void tmrec_free_members(tmrec_p _trp, char alloc)
545
0
{
546
0
  if (!_trp)
547
0
    return;
548
549
0
  tr_byxxx_free(_trp->byday);
550
0
  tr_byxxx_free(_trp->bymday);
551
0
  tr_byxxx_free(_trp->byyday);
552
0
  tr_byxxx_free(_trp->bymonth);
553
0
  tr_byxxx_free(_trp->byweekno);
554
555
0
  if (alloc & PKG_ALLOC)
556
0
    pkg_free(_trp->tz);
557
0
  else
558
0
    shm_free(_trp->tz);
559
560
0
  _trp->byday = NULL;
561
0
  _trp->bymday = NULL;
562
0
  _trp->byyday = NULL;
563
0
  _trp->bymonth = NULL;
564
0
  _trp->byweekno = NULL;
565
0
  _trp->tz = NULL;
566
0
}
567
568
int tr_parse_tz(tmrec_p _trp, char *_in)
569
0
{
570
0
  if (!_trp || !_in)
571
0
    return -1;
572
573
0
  if (*_in < 'A' || *_in > 'Z')
574
0
    return 1;
575
576
0
  if (_trp->flags & PKG_ALLOC)
577
0
    _trp->tz = pkg_strdup(_in);
578
0
  else
579
0
    _trp->tz = shm_strdup(_in);
580
581
0
  if (!_trp->tz) {
582
0
    LM_ERR("oom\n");
583
0
    return -1;
584
0
  }
585
586
0
  return 0;
587
0
}
588
589
int tr_parse_dtstart(tmrec_p _trp, char *_in)
590
0
{
591
0
  if (!_in)
592
0
    return -1;
593
0
  _trp->dtstart = ic_parse_datetime(_in, &(_trp->ts));
594
0
  return (_trp->dtstart == TR_NOVAL) ? -1 : 0;
595
0
}
596
597
int tr_parse_dtend(tmrec_p _trp, char *_in)
598
0
{
599
0
  struct tm _tm;
600
0
  if (!_in)
601
0
    return -1;
602
0
  _trp->dtend = ic_parse_datetime(_in,&_tm);
603
0
  return (_trp->dtend == TR_NOVAL) ? -1 : 0;
604
0
}
605
606
int tr_parse_duration(tmrec_p _trp, char *_in)
607
0
{
608
0
  if (!_in)
609
0
    return -1;
610
0
  _trp->duration = ic_parse_duration(_in);
611
0
  return (_trp->duration == TR_NOVAL) ? -1 : 0;
612
0
}
613
614
int tr_parse_until(tmrec_p _trp, char *_in)
615
0
{
616
0
  struct tm _tm;
617
0
  if (!_in)
618
0
    return -1;
619
0
  _trp->until = ic_parse_datetime(_in, &_tm);
620
0
  return (_trp->until == TR_NOVAL) ? -1 : 0;
621
0
}
622
623
int tr_parse_freq(tmrec_p _trp, char *_in)
624
0
{
625
0
  if (!_in)
626
0
    return -1;
627
628
0
  if(strlen(_in)<5)
629
0
  {
630
0
    _trp->freq = FREQ_NOFREQ;
631
0
    return 0;
632
0
  }
633
0
  if(!strcasecmp(_in, "daily"))
634
0
  {
635
0
    _trp->freq = FREQ_DAILY;
636
0
    return 0;
637
0
  }
638
0
  if(!strcasecmp(_in, "weekly"))
639
0
  {
640
0
    _trp->freq = FREQ_WEEKLY;
641
0
    return 0;
642
0
  }
643
0
  if(!strcasecmp(_in, "monthly"))
644
0
  {
645
0
    _trp->freq = FREQ_MONTHLY;
646
0
    return 0;
647
0
  }
648
0
  if(!strcasecmp(_in, "yearly"))
649
0
  {
650
0
    _trp->freq = FREQ_YEARLY;
651
0
    return 0;
652
0
  }
653
654
0
  _trp->freq = FREQ_NOFREQ;
655
0
  return 0;
656
0
}
657
658
int tr_parse_interval(tmrec_p _trp, char *_in)
659
0
{
660
0
  if (!_in)
661
0
    return -1;
662
0
  _trp->interval = strz2int(_in);
663
0
  return 0;
664
0
}
665
666
int tr_parse_byday(tmrec_p _trp, char *_in)
667
0
{
668
0
  tr_byxxx_p byday;
669
670
0
  if (!_in)
671
0
    return -1;
672
673
0
  byday = ic_parse_byday(_in, _trp->flags);
674
0
  if (!byday)
675
0
    return -1;
676
0
  tr_byxxx_free(_trp->byday);
677
0
  _trp->byday = byday;
678
0
  _trp->flags |= TR_BYXXX;
679
0
  return 0;
680
0
}
681
682
int tr_parse_bymday(tmrec_p _trp, char *_in)
683
0
{
684
0
  tr_byxxx_p bymday;
685
686
0
  if (!_in)
687
0
    return -1;
688
689
0
  bymday = ic_parse_byxxx(_in, _trp->flags);
690
0
  if (!bymday)
691
0
    return -1;
692
0
  tr_byxxx_free(_trp->bymday);
693
0
  _trp->bymday = bymday;
694
0
  _trp->flags |= TR_BYXXX;
695
0
  return 0;
696
0
}
697
698
int tr_parse_byyday(tmrec_p _trp, char *_in)
699
0
{
700
0
  tr_byxxx_p byyday;
701
702
0
  if (!_in)
703
0
    return -1;
704
705
0
  byyday = ic_parse_byxxx(_in, _trp->flags);
706
0
  if (!byyday)
707
0
    return -1;
708
0
  tr_byxxx_free(_trp->byyday);
709
0
  _trp->byyday = byyday;
710
0
  _trp->flags |= TR_BYXXX;
711
0
  return 0;
712
0
}
713
714
int tr_parse_bymonth(tmrec_p _trp, char *_in)
715
0
{
716
0
  tr_byxxx_p bymonth;
717
718
0
  if (!_in)
719
0
    return -1;
720
721
0
  bymonth = ic_parse_byxxx(_in, _trp->flags);
722
0
  if (!bymonth)
723
0
    return -1;
724
0
  tr_byxxx_free(_trp->bymonth);
725
0
  _trp->bymonth = bymonth;
726
0
  _trp->flags |= TR_BYXXX;
727
0
  return 0;
728
0
}
729
730
int tr_parse_byweekno(tmrec_p _trp, char *_in)
731
0
{
732
0
  tr_byxxx_p byweekno;
733
734
0
  if (!_in)
735
0
    return -1;
736
737
0
  byweekno = ic_parse_byxxx(_in, _trp->flags);
738
0
  if (!byweekno)
739
0
    return -1;
740
0
  tr_byxxx_free(_trp->byweekno);
741
0
  _trp->byweekno = byweekno;
742
0
  _trp->flags |= TR_BYXXX;
743
0
  return 0;
744
0
}
745
746
int tr_parse_wkst(tmrec_p _trp, char *_in)
747
0
{
748
0
  if (!_in)
749
0
    return -1;
750
751
0
  _trp->wkst = ic_parse_wkst(_in);
752
0
  return 0;
753
0
}
754
755
int tmrec_print(const tmrec *tr)
756
0
{
757
0
  static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
758
0
  tmrec_p _trp = (tmrec_p)tr;
759
0
  int i;
760
761
0
  if(!_trp)
762
0
  {
763
0
    printf("\n(null)\n");
764
0
    return -1;
765
0
  }
766
0
  printf("Recurrence definition\n-- start time ---\n");
767
0
  printf("Sys time: %d\n", (int)_trp->dtstart);
768
0
  printf("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour,
769
0
        _trp->ts.tm_min, _trp->ts.tm_sec);
770
0
  printf("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday],
771
0
        _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday);
772
0
  printf("---\n");
773
0
  printf("End time: %d\n", (int)_trp->dtend);
774
0
  printf("Duration: %d\n", (int)_trp->duration);
775
0
  printf("Until: %d\n", (int)_trp->until);
776
0
  printf("Freq: %d\n", (int)_trp->freq);
777
0
  printf("Interval: %d\n", (int)_trp->interval);
778
0
  if(_trp->byday)
779
0
  {
780
0
    printf("Byday: ");
781
0
    for(i=0; i<_trp->byday->nr; i++)
782
0
      printf(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]);
783
0
    printf("\n");
784
0
  }
785
0
  if(_trp->bymday)
786
0
  {
787
0
    printf("Bymday: %d:", _trp->bymday->nr);
788
0
    for(i=0; i<_trp->bymday->nr; i++)
789
0
      printf(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]);
790
0
    printf("\n");
791
0
  }
792
0
  if(_trp->byyday)
793
0
  {
794
0
    printf("Byyday:");
795
0
    for(i=0; i<_trp->byyday->nr; i++)
796
0
      printf(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]);
797
0
    printf("\n");
798
0
  }
799
0
  if(_trp->bymonth)
800
0
  {
801
0
    printf("Bymonth: %d:", _trp->bymonth->nr);
802
0
    for(i=0; i< _trp->bymonth->nr; i++)
803
0
      printf(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]);
804
0
    printf("\n");
805
0
  }
806
0
  if(_trp->byweekno)
807
0
  {
808
0
    printf("Byweekno: ");
809
0
    for(i=0; i<_trp->byweekno->nr; i++)
810
0
      printf(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]);
811
0
    printf("\n");
812
0
  }
813
0
  printf("Weekstart: %d\n", _trp->wkst);
814
0
  return 0;
815
0
}
816
817
time_t ic_parse_datetime(char *_in, struct tm *_tm)
818
0
{
819
0
  struct tm t;
820
821
0
  if (!_in || strlen(_in)!=15)
822
0
    return TR_NOVAL;
823
824
0
  t.tm_year = _D(_in[0])*1000 + _D(_in[1])*100
825
0
      + _D(_in[2])*10 + _D(_in[3]);
826
0
  if (t.tm_year < 1970) {
827
0
    LM_ERR("invalid year in Date-Time: '%s'\n", _in);
828
0
    return TR_NOVAL;
829
0
  }
830
831
0
  t.tm_year -= 1900; /* per man ctime(3) */
832
0
  t.tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1;
833
0
  t.tm_mday = _D(_in[6])*10 + _D(_in[7]);
834
0
  if (t.tm_mon == -1 || t.tm_mday == 0) {
835
0
    LM_ERR("month or month day cannot be zero in Date-Time: '%s'\n", _in);
836
0
    return TR_NOVAL;
837
0
  }
838
839
0
  t.tm_hour = _D(_in[9])*10 + _D(_in[10]);
840
0
  t.tm_min = _D(_in[11])*10 + _D(_in[12]);
841
0
  t.tm_sec = _D(_in[13])*10 + _D(_in[14]);
842
0
  t.tm_isdst = -1 /*daylight*/;
843
844
0
  *_tm = t;
845
0
  return mktime(_tm);
846
0
}
847
848
time_t ic_parse_duration(char *_in)
849
0
{
850
0
  time_t _t, _ft;
851
0
  char *_p;
852
0
  int _fl;
853
854
0
  if(!_in || strlen(_in)<2)
855
0
    return TR_NOVAL;
856
857
0
  if(*_in == 'P' || *_in=='p')
858
0
  {
859
0
    _p = _in+1;
860
0
    _fl = 1;
861
0
  } else {
862
0
    _p = _in;
863
0
    _fl = 0;
864
0
  }
865
866
0
  _t = _ft = 0;
867
868
0
  while(*_p)
869
0
  {
870
0
    switch(*_p)
871
0
    {
872
0
      case '0': case '1': case '2':
873
0
      case '3': case '4': case '5':
874
0
      case '6': case '7': case '8':
875
0
      case '9':
876
0
        _t = _t*10 + *_p - '0';
877
0
      break;
878
879
0
      case 'w':
880
0
      case 'W':
881
0
        if(!_fl)
882
0
        {
883
0
          LM_ERR("week duration not allowed"
884
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
885
0
          return 0;
886
0
        }
887
0
        _ft += _t*7*24*3600;
888
0
        _t = 0;
889
0
      break;
890
0
      case 'd':
891
0
      case 'D':
892
0
        if(!_fl)
893
0
        {
894
0
          LM_ERR("day duration not allowed"
895
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
896
0
          return 0;
897
0
        }
898
0
        _ft += _t*24*3600;
899
0
        _t = 0;
900
0
      break;
901
0
      case 'h':
902
0
      case 'H':
903
0
        if(_fl)
904
0
        {
905
0
          LM_ERR("hour duration not allowed"
906
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
907
0
          return 0;
908
0
        }
909
0
        _ft += _t*3600;
910
0
        _t = 0;
911
0
      break;
912
0
      case 'm':
913
0
      case 'M':
914
0
        if(_fl)
915
0
        {
916
0
          LM_ERR("minute duration not allowed"
917
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
918
0
          return 0;
919
0
        }
920
0
        _ft += _t*60;
921
0
        _t = 0;
922
0
      break;
923
0
      case 's':
924
0
      case 'S':
925
0
        if(_fl)
926
0
        {
927
0
          LM_ERR("second duration not allowed"
928
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
929
0
          return 0;
930
0
        }
931
0
        _ft += _t;
932
0
        _t = 0;
933
0
      break;
934
0
      case 't':
935
0
      case 'T':
936
0
        if(!_fl)
937
0
        {
938
0
          LM_ERR("'T' not allowed"
939
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
940
0
          return 0;
941
0
        }
942
0
        _fl = 0;
943
0
      break;
944
0
      default:
945
0
        LM_ERR("bad character here (%d) [%s]\n",
946
0
          (int)(_p-_in), _in);
947
0
        return 0;
948
0
    }
949
0
    _p++;
950
0
  }
951
952
0
  return _ft;
953
0
}
954
955
tr_byxxx_p ic_parse_byday(char *_in, char type)
956
0
{
957
0
  tr_byxxx_p _bxp = NULL;
958
0
  int _nr, _s, _v;
959
0
  char *_p;
960
961
0
  if(!_in)
962
0
    return NULL;
963
0
  _bxp = tr_byxxx_new(type);
964
0
  if(!_bxp)
965
0
    return NULL;
966
0
  _p = _in;
967
0
  _nr = 1;
968
0
  while(*_p)
969
0
  {
970
0
    if(*_p == ',')
971
0
      _nr++;
972
0
    _p++;
973
0
  }
974
0
  if(tr_byxxx_init(_bxp, _nr) < 0)
975
0
  {
976
0
    tr_byxxx_free(_bxp);
977
0
    return NULL;
978
0
  }
979
0
  _p = _in;
980
0
  _nr = _v = 0;
981
0
  _s = 1;
982
0
  while(*_p && _nr < _bxp->nr)
983
0
  {
984
0
    switch(*_p)
985
0
    {
986
0
      case '0': case '1': case '2':
987
0
      case '3': case '4': case '5':
988
0
      case '6': case '7': case '8':
989
0
      case '9':
990
0
        _v = _v*10 + *_p - '0';
991
0
      break;
992
993
0
      case 's':
994
0
      case 'S':
995
0
        _p++;
996
0
        switch(*_p)
997
0
        {
998
0
          case 'a':
999
0
          case 'A':
1000
0
            _bxp->xxx[_nr] = WDAY_SA;
1001
0
          break;
1002
0
          case 'u':
1003
0
          case 'U':
1004
0
            _bxp->xxx[_nr] = WDAY_SU;
1005
0
          break;
1006
0
          default:
1007
0
            goto error;
1008
0
        }
1009
1010
0
        _bxp->req[_nr] = _s * _v;
1011
0
        if (_bxp->req[_nr] > 0)
1012
0
          _bxp->req[_nr]--;
1013
0
        _s = 1;
1014
0
        _v = 0;
1015
0
        break;
1016
0
      case 'm':
1017
0
      case 'M':
1018
0
        _p++;
1019
0
        if(*_p!='o' && *_p!='O')
1020
0
          goto error;
1021
0
        _bxp->xxx[_nr] = WDAY_MO;
1022
1023
0
        _bxp->req[_nr] = _s * _v;
1024
0
        if (_bxp->req[_nr] > 0)
1025
0
          _bxp->req[_nr]--;
1026
0
        _s = 1;
1027
0
        _v = 0;
1028
0
        break;
1029
0
      case 't':
1030
0
      case 'T':
1031
0
        _p++;
1032
0
        switch(*_p)
1033
0
        {
1034
0
          case 'h':
1035
0
          case 'H':
1036
0
            _bxp->xxx[_nr] = WDAY_TH;
1037
0
          break;
1038
0
          case 'u':
1039
0
          case 'U':
1040
0
            _bxp->xxx[_nr] = WDAY_TU;
1041
0
          break;
1042
0
          default:
1043
0
            goto error;
1044
0
        }
1045
1046
0
        _bxp->req[_nr] = _s * _v;
1047
0
        if (_bxp->req[_nr] > 0)
1048
0
          _bxp->req[_nr]--;
1049
0
        _s = 1;
1050
0
        _v = 0;
1051
0
        break;
1052
0
      case 'w':
1053
0
      case 'W':
1054
0
        _p++;
1055
0
        if(*_p!='e' && *_p!='E')
1056
0
          goto error;
1057
0
        _bxp->xxx[_nr] = WDAY_WE;
1058
0
        _s = 1;
1059
0
        _v = 0;
1060
1061
0
        _bxp->req[_nr] = _s * _v;
1062
0
        if (_bxp->req[_nr] > 0)
1063
0
          _bxp->req[_nr]--;
1064
0
        break;
1065
0
      case 'f':
1066
0
      case 'F':
1067
0
        _p++;
1068
0
        if(*_p!='r' && *_p!='R')
1069
0
          goto error;
1070
0
        _bxp->xxx[_nr] = WDAY_FR;
1071
1072
0
        _bxp->req[_nr] = _s * _v;
1073
0
        if (_bxp->req[_nr] > 0)
1074
0
          _bxp->req[_nr]--;
1075
0
        _s = 1;
1076
0
        _v = 0;
1077
0
        break;
1078
0
      case '-':
1079
0
        _s = -1;
1080
0
      break;
1081
0
      case '+':
1082
0
      case ' ':
1083
0
      case '\t':
1084
0
      break;
1085
0
      case ',':
1086
0
        _nr++;
1087
0
      break;
1088
0
      default:
1089
0
        goto error;
1090
0
    }
1091
0
    _p++;
1092
0
  }
1093
1094
0
  return _bxp;
1095
1096
0
error:
1097
0
  tr_byxxx_free(_bxp);
1098
0
  return NULL;
1099
0
}
1100
1101
tr_byxxx_p ic_parse_byxxx(char *_in, char type)
1102
0
{
1103
0
  tr_byxxx_p _bxp = NULL;
1104
0
  int _nr, _s, _v;
1105
0
  char *_p;
1106
1107
0
  if(!_in)
1108
0
    return NULL;
1109
0
  _bxp = tr_byxxx_new(type);
1110
0
  if(!_bxp)
1111
0
    return NULL;
1112
0
  _p = _in;
1113
0
  _nr = 1;
1114
0
  while(*_p)
1115
0
  {
1116
0
    if(*_p == ',')
1117
0
      _nr++;
1118
0
    _p++;
1119
0
  }
1120
0
  if(tr_byxxx_init(_bxp, _nr) < 0)
1121
0
  {
1122
0
    tr_byxxx_free(_bxp);
1123
0
    return NULL;
1124
0
  }
1125
1126
0
  _nr = _v = 0;
1127
0
  _s = 1;
1128
1129
0
  for (_p = _in; *_p; _p++) {
1130
0
    switch (*_p) {
1131
0
    case '0': case '1': case '2':
1132
0
    case '3': case '4': case '5':
1133
0
    case '6': case '7': case '8':
1134
0
    case '9':
1135
0
      _v = _v*10 + *_p - '0';
1136
0
      break;
1137
1138
0
    case '-':
1139
0
      _s = -1;
1140
0
      break;
1141
0
    case '+':
1142
0
    case ' ':
1143
0
    case '\t':
1144
0
      break;
1145
1146
0
    case ',':
1147
0
      _bxp->xxx[_nr] = _v;
1148
0
      _bxp->req[_nr] = _s;
1149
0
      _s = 1;
1150
0
      _v = 0;
1151
0
      _nr++;
1152
0
      break;
1153
1154
0
    default:
1155
0
      goto error;
1156
0
    }
1157
0
  }
1158
1159
  /* store the last item of the list */
1160
0
  _bxp->xxx[_nr] = _v;
1161
0
  _bxp->req[_nr] = _s;
1162
1163
0
  return _bxp;
1164
1165
0
error:
1166
0
  tr_byxxx_free(_bxp);
1167
0
  return NULL;
1168
0
}
1169
1170
int ic_parse_wkst(char *_in)
1171
0
{
1172
0
  if(!_in || strlen(_in)!=2)
1173
0
    goto error;
1174
1175
0
  switch(_in[0])
1176
0
  {
1177
0
    case 's':
1178
0
    case 'S':
1179
0
      switch(_in[1])
1180
0
      {
1181
0
        case 'a':
1182
0
        case 'A':
1183
0
          return WDAY_SA;
1184
0
        case 'u':
1185
0
        case 'U':
1186
0
          return WDAY_SU;
1187
0
        default:
1188
0
          goto error;
1189
0
      }
1190
0
    case 'm':
1191
0
    case 'M':
1192
0
      if(_in[1]!='o' && _in[1]!='O')
1193
0
        goto error;
1194
0
      return WDAY_MO;
1195
0
    case 't':
1196
0
    case 'T':
1197
0
      switch(_in[1])
1198
0
      {
1199
0
        case 'h':
1200
0
        case 'H':
1201
0
          return WDAY_TH;
1202
0
        case 'u':
1203
0
        case 'U':
1204
0
          return WDAY_TU;
1205
0
        default:
1206
0
          goto error;
1207
0
      }
1208
0
    case 'w':
1209
0
    case 'W':
1210
0
      if(_in[1]!='e' && _in[1]!='E')
1211
0
        goto error;
1212
0
      return WDAY_WE;
1213
0
    case 'f':
1214
0
    case 'F':
1215
0
      if(_in[1]!='r' && _in[1]!='R')
1216
0
        goto error;
1217
0
      return WDAY_FR;
1218
0
    break;
1219
0
    default:
1220
0
      goto error;
1221
0
  }
1222
1223
0
error:
1224
#ifdef USE_YWEEK_U
1225
  return WDAY_SU;
1226
#else
1227
0
  return WDAY_MO;
1228
0
#endif
1229
0
}
1230
1231
1232
1233
/*** local headers ***/
1234
int check_min_unit(tmrec_p _trp, ac_tm_p _atp);
1235
int check_freq_interval(tmrec_p _trp, ac_tm_p _atp);
1236
int check_byxxx(tmrec_p, ac_tm_p);
1237
1238
/**
1239
 *
1240
 * return 0/REC_MATCH - the time falls in
1241
 *       -1/REC_ERR - error
1242
 *        1/REC_NOMATCH - the time falls out
1243
 */
1244
int check_tmrec(const tmrec_p _trp, ac_tm_p _atp)
1245
0
{
1246
  /* it is before the start date or outside a non-recurring interval? */
1247
0
  if (_atp->time < _trp->dtstart ||
1248
0
          (!_IS_SET(_trp->freq) && _atp->time >= _trp->dtend))
1249
0
    return REC_NOMATCH;
1250
1251
0
  LM_DEV("1) %ld + %ld = %ld\n", _trp->dtstart, _trp->duration, _trp->dtend);
1252
1253
0
  if (!_IS_SET(_trp->freq) && _atp->time < _trp->dtend)
1254
0
    return REC_MATCH;
1255
1256
0
  LM_DEV("2) check freq\n");
1257
1258
  /* check if the instance of recurrence matches the 'interval' */
1259
0
  if (check_freq_interval(_trp, _atp) != REC_MATCH)
1260
0
    return REC_NOMATCH;
1261
1262
0
  LM_DEV("3) check min unit\n");
1263
1264
0
  if (check_min_unit(_trp, _atp) != REC_MATCH)
1265
0
    return REC_NOMATCH;
1266
1267
0
  LM_DEV("4) check byxxx\n");
1268
1269
0
  if (check_byxxx(_trp, _atp) != REC_MATCH)
1270
0
    return REC_NOMATCH;
1271
1272
0
  LM_DEV("5) check until\n");
1273
1274
0
  return (!_IS_SET(_trp->until) || _atp->time <= _trp->until) ?
1275
0
              REC_MATCH : REC_NOMATCH;
1276
0
}
1277
1278
1279
int check_freq_interval(tmrec_p _trp, ac_tm_p _atp)
1280
0
{
1281
0
  int _t0, _t1;
1282
0
  struct tm _tm;
1283
1284
0
  if(!_IS_SET(_trp->freq))
1285
0
    return REC_NOMATCH;
1286
1287
0
  LM_DEV("have freq\n");
1288
1289
0
  if(!_IS_SET(_trp->interval) || _trp->interval==1)
1290
0
    return REC_MATCH;
1291
1292
0
  LM_DEV("have interval (%d)\n", _trp->interval);
1293
1294
0
  switch(_trp->freq)
1295
0
  {
1296
0
    case FREQ_DAILY:
1297
0
    case FREQ_WEEKLY:
1298
0
      memset(&_tm, 0, sizeof(struct tm));
1299
0
      _tm.tm_year = _trp->ts.tm_year;
1300
0
      _tm.tm_mon = _trp->ts.tm_mon;
1301
0
      _tm.tm_mday = _trp->ts.tm_mday;
1302
0
      _t0 = (int)mktime(&_tm);
1303
0
      memset(&_tm, 0, sizeof(struct tm));
1304
0
      _tm.tm_year = _atp->t.tm_year;
1305
0
      _tm.tm_mon = _atp->t.tm_mon;
1306
0
      _tm.tm_mday = _atp->t.tm_mday;
1307
0
      _t1 = (int)mktime(&_tm);
1308
0
      if(_trp->freq == FREQ_DAILY)
1309
0
        return (((_t1-_t0)/(24*3600))%_trp->interval==0)?
1310
0
          REC_MATCH:REC_NOMATCH;
1311
#ifdef USE_YWEEK_U
1312
      _t0 -= _trp->ts.tm_wday*24*3600;
1313
      _t1 -= _atp->t.tm_wday*24*3600;
1314
#else
1315
0
      _t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600;
1316
0
      _t1 -= ((_atp->t.tm_wday+6)%7)*24*3600;
1317
0
#endif
1318
0
      return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)?
1319
0
          REC_MATCH:REC_NOMATCH;
1320
0
    case FREQ_MONTHLY:
1321
0
      _t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12
1322
0
          + _atp->t.tm_mon-_trp->ts.tm_mon;
1323
0
      return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH;
1324
0
    case FREQ_YEARLY:
1325
0
      return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)?
1326
0
          REC_MATCH:REC_NOMATCH;
1327
0
  }
1328
1329
0
  return REC_NOMATCH;
1330
0
}
1331
1332
static inline int get_min_interval(tmrec_p _trp)
1333
0
{
1334
0
  if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday)
1335
0
    return FREQ_DAILY;
1336
0
  if(_trp->freq == FREQ_WEEKLY || _trp->byweekno)
1337
0
    return FREQ_WEEKLY;
1338
0
  if(_trp->freq == FREQ_MONTHLY || _trp->bymonth)
1339
0
    return FREQ_MONTHLY;
1340
0
  if(_trp->freq == FREQ_YEARLY)
1341
0
    return FREQ_YEARLY;
1342
1343
0
  return FREQ_NOFREQ;
1344
0
}
1345
1346
1347
int check_recur_itv(struct tm *x, struct tm *bgn, struct tm *end,
1348
                    time_t dur, int freq)
1349
0
{
1350
0
  int d1, d2, dx;
1351
0
  long diff;
1352
1353
0
  switch (freq) {
1354
0
  case FREQ_YEARLY:
1355
0
    LM_DEV("YEARLY\n");
1356
0
    d1 = bgn->tm_yday;
1357
0
    d2 = end->tm_yday;
1358
0
    dx = x->tm_yday;
1359
0
    break;
1360
1361
0
  case FREQ_MONTHLY:
1362
0
    LM_DEV("MONTHLY\n");
1363
0
    d1 = bgn->tm_mday;
1364
0
    d2 = end->tm_mday;
1365
0
    dx = x->tm_mday;
1366
0
    break;
1367
1368
0
  case FREQ_WEEKLY:
1369
0
    LM_DEV("WEEKLY\n");
1370
0
    d1 = bgn->tm_wday;
1371
0
    d2 = end->tm_wday;
1372
0
    dx = x->tm_wday;
1373
0
    break;
1374
1375
0
  case FREQ_DAILY:
1376
0
  default:
1377
0
    LM_DEV("DAILY\n");
1378
0
    if (bgn->tm_mday == end->tm_mday) {
1379
0
      LM_DEV("DAILY-1\n");
1380
0
      diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1381
0
          (bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec);
1382
0
      if (diff < 0)
1383
0
        return REC_NOMATCH;
1384
1385
0
      diff = end->tm_hour*3600 + end->tm_min*60 + end->tm_sec -
1386
0
          (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1387
0
      if (diff <= 0)
1388
0
        return REC_NOMATCH;
1389
1390
0
      LM_DEV("MATCH\n");
1391
0
      return REC_MATCH;
1392
1393
0
    } else {
1394
0
      LM_DEV("DAILY-2\n");
1395
0
      diff = bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec -
1396
0
          (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1397
0
      if (diff <= 0)
1398
0
        return REC_MATCH;
1399
1400
0
      diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1401
0
          (end->tm_hour*3600 + end->tm_min*60 + end->tm_sec);
1402
0
      if (diff < 0)
1403
0
        return REC_MATCH;
1404
1405
0
      LM_DEV("NOMATCH\n");
1406
0
      return REC_NOMATCH;
1407
0
    }
1408
0
  }
1409
1410
0
  LM_DEV("check intervals\n");
1411
1412
  /* continuous interval (e.g. "M [ T W T F ] S S") */
1413
0
  if (d1 < d2) {
1414
0
    LM_DEV("CI-1\n");
1415
0
    if (dx < d1 || dx > d2)
1416
0
      return REC_NOMATCH;
1417
1418
0
    if (dx > d1 && dx < d2)
1419
0
      return REC_MATCH;
1420
1421
  /* overlapping interval (e.g. "1 2 ... 20 ] 21 ... 29 [ 30 31") */
1422
0
  } else if (d2 < d1) {
1423
0
    LM_DEV("CI-2\n");
1424
0
    if (dx > d2 && dx < d1)
1425
0
      return REC_NOMATCH;
1426
1427
0
    if (dx < d2 || dx > d1)
1428
0
      return REC_MATCH;
1429
1430
0
  } else if (dx != d1) {
1431
0
    LM_DEV("CI-3\n");
1432
0
    if (dur <= SEC_DAILY)
1433
0
      return REC_NOMATCH;
1434
0
    else
1435
0
      return REC_MATCH;
1436
1437
0
  } else {
1438
0
    LM_DEV("CI-4\n");
1439
0
    diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1440
0
        (bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec);
1441
0
    if (diff < 0)
1442
0
      return REC_NOMATCH;
1443
1444
0
    diff = end->tm_hour*3600 + end->tm_min*60 + end->tm_sec -
1445
0
        (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1446
0
    if (diff <= 0)
1447
0
      return REC_NOMATCH;
1448
1449
0
    return REC_MATCH;
1450
0
  }
1451
1452
0
  if (dx == d1)
1453
0
    diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1454
0
      (bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec);
1455
0
  else
1456
0
    diff = end->tm_hour*3600 + end->tm_min*60 + end->tm_sec -
1457
0
      (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1458
1459
0
  return diff > 0 ? REC_MATCH : REC_NOMATCH;
1460
0
}
1461
1462
1463
int check_min_unit(tmrec_p _trp, ac_tm_p _atp)
1464
0
{
1465
0
  int min_itv;
1466
0
  struct tm end;
1467
1468
0
  min_itv = get_min_interval(_trp);
1469
0
  LM_DEV("min_itv: %d\n", min_itv);
1470
1471
0
  switch (min_itv) {
1472
0
  case FREQ_DAILY:
1473
0
    if (_trp->duration >= SEC_DAILY)
1474
0
      return REC_MATCH;
1475
0
    break;
1476
1477
0
  case FREQ_WEEKLY:
1478
0
    if (_trp->duration >= SEC_WEEKLY)
1479
0
      return REC_MATCH;
1480
0
    break;
1481
1482
0
  case FREQ_MONTHLY:
1483
0
    if (_trp->duration >= SEC_MONTHLY_MAX)
1484
0
      return REC_MATCH;
1485
0
    break;
1486
1487
0
  case FREQ_YEARLY:
1488
0
    if (_trp->duration >= SEC_YEARLY_MAX)
1489
0
      return REC_MATCH;
1490
0
    break;
1491
1492
0
  default:
1493
0
    return REC_NOMATCH;
1494
0
  }
1495
1496
0
  LM_DEV("check recur...\n");
1497
1498
0
  localtime_r(&_trp->dtend, &end);
1499
0
  return check_recur_itv(&_atp->t, &_trp->ts, &end, _trp->duration, min_itv);
1500
0
}
1501
1502
int check_byxxx(tmrec_p _trp, ac_tm_p _atp)
1503
0
{
1504
0
  int i;
1505
0
  ac_maxval_p _amp;
1506
1507
0
  if (!(_trp->flags & TR_BYXXX))
1508
0
    return REC_MATCH;
1509
1510
0
  _amp = ac_get_maxval(_atp);
1511
1512
0
  if(_trp->bymonth)
1513
0
  {
1514
0
    for(i=0; i<_trp->bymonth->nr; i++)
1515
0
    {
1516
0
      if(_atp->t.tm_mon ==
1517
0
          ((_trp->bymonth->xxx[i] - 1)*_trp->bymonth->req[i]+12)%12)
1518
0
        break;
1519
0
    }
1520
0
    if(i>=_trp->bymonth->nr)
1521
0
      return REC_NOMATCH;
1522
0
  }
1523
0
  if(_trp->freq==FREQ_YEARLY && _trp->byweekno)
1524
0
  {
1525
0
    for(i=0; i<_trp->byweekno->nr; i++)
1526
0
    {
1527
0
      if(_atp->yweek == ((_trp->byweekno->xxx[i] - 1) *_trp->byweekno->req[i]+
1528
0
              _amp->yweek)%_amp->yweek)
1529
0
        break;
1530
0
    }
1531
0
    if(i>=_trp->byweekno->nr)
1532
0
      return REC_NOMATCH;
1533
0
  }
1534
0
  if(_trp->byyday)
1535
0
  {
1536
0
    for(i=0; i<_trp->byyday->nr; i++)
1537
0
    {
1538
0
      if(_atp->t.tm_yday == ((_trp->byyday->xxx[i] - 1)*_trp->byyday->req[i]+
1539
0
            _amp->yday)%_amp->yday)
1540
0
        break;
1541
0
    }
1542
0
    if(i>=_trp->byyday->nr)
1543
0
      return REC_NOMATCH;
1544
0
  }
1545
0
  if(_trp->bymday)
1546
0
  {
1547
0
    for(i=0; i<_trp->bymday->nr; i++)
1548
0
    {
1549
#ifdef EXTRA_DEBUG
1550
      LM_DBG("%d == %d\n", _atp->t.tm_mday,
1551
        (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
1552
        _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0));
1553
#endif
1554
0
      if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
1555
0
            _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0))
1556
0
        break;
1557
0
    }
1558
0
    if(i>=_trp->bymday->nr)
1559
0
      return REC_NOMATCH;
1560
0
  }
1561
0
  if(_trp->byday)
1562
0
  {
1563
0
    for(i=0; i<_trp->byday->nr; i++)
1564
0
    {
1565
0
      if(_trp->freq==FREQ_YEARLY)
1566
0
      {
1567
#ifdef EXTRA_DEBUG
1568
        LM_DBG("%d==%d && %d==%d\n", _atp->t.tm_wday,
1569
          _trp->byday->xxx[i], _atp->ywday,
1570
          (_trp->byday->req[i]+_amp->ywday)%_amp->ywday);
1571
#endif
1572
0
        if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
1573
0
            _atp->ywday == (_trp->byday->req[i]+_amp->ywday)%
1574
0
            _amp->ywday)
1575
0
          break;
1576
0
      }
1577
0
      else
1578
0
      {
1579
0
        if(_trp->freq==FREQ_MONTHLY)
1580
0
        {
1581
#ifdef EXTRA_DEBUG
1582
          LM_DBG("%d==%d && %d==%d [%d]\n", _atp->t.tm_wday,
1583
            _trp->byday->xxx[i], _atp->wom,
1584
            (_trp->byday->req[i]+_amp->mwday)%_amp->mwday,
1585
            _amp->mwday);
1586
#endif
1587
0
          if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
1588
0
              _atp->wom==(_trp->byday->req[i]+
1589
0
              _amp->mwday)%_amp->mwday)
1590
0
            break;
1591
0
        }
1592
0
        else
1593
0
        {
1594
0
          if(_atp->t.tm_wday == _trp->byday->xxx[i])
1595
0
            break;
1596
0
        }
1597
0
      }
1598
0
    }
1599
0
    if(i>=_trp->byday->nr)
1600
0
      return REC_NOMATCH;
1601
0
  }
1602
1603
0
  return REC_MATCH;
1604
0
}
1605
1606
1607
static inline int _tmrec_parse(const char *tr, tmrec_t *time_rec)
1608
0
{
1609
0
  char *p, *s;
1610
0
  osips_free_t free_f = (time_rec->flags & PKG_ALLOC ?
1611
0
                             osips_pkg_free : osips_shm_free);
1612
1613
  /* empty definition? */
1614
0
  if (!tr || *tr == '\0')
1615
0
    return 0;
1616
1617
0
  p = (char *)tr;
1618
1619
0
  load_TR_value(p, s, time_rec, tr_parse_tz, parse_error, done);
1620
1621
  /* Important: make sure to set the tz NOW, before more parsing... */
1622
0
  if (time_rec->tz)
1623
0
    _tz_set(time_rec->tz);
1624
1625
0
  load_TR_value(p, s, time_rec, tr_parse_dtstart, parse_error, done);
1626
0
  load_TR_value(p, s, time_rec, tr_parse_dtend, parse_error, done);
1627
0
  load_TR_value(p, s, time_rec, tr_parse_duration, parse_error, done);
1628
0
  load_TR_value(p, s, time_rec, tr_parse_freq, parse_error, done);
1629
0
  load_TR_value(p, s, time_rec, tr_parse_until, parse_error, done);
1630
0
  load_TR_value(p, s, time_rec, tr_parse_interval, parse_error, done);
1631
0
  load_TR_value(p, s, time_rec, tr_parse_byday, parse_error, done);
1632
0
  load_TR_value(p, s, time_rec, tr_parse_bymday, parse_error, done);
1633
0
  load_TR_value(p, s, time_rec, tr_parse_byyday, parse_error, done);
1634
0
  load_TR_value(p, s, time_rec, tr_parse_byweekno, parse_error, done);
1635
0
  load_TR_value(p, s, time_rec, tr_parse_bymonth, parse_error, done);
1636
1637
0
done:
1638
0
  if (time_rec->tz)
1639
0
    tz_reset();
1640
1641
0
  if (!_IS_SET(time_rec->dtstart))
1642
0
    time_rec->dtstart = 0; /* invalid; auto-fix to 19700101T000000 */
1643
1644
0
  if (!_IS_SET(time_rec->duration)) {
1645
0
    if (!_IS_SET(time_rec->dtend)) {
1646
      /* invalid; since we don't support mementos, we need a duration */
1647
0
      switch (get_min_interval(time_rec)) {
1648
0
      case FREQ_DAILY:
1649
0
        time_rec->dtend = time_rec->dtstart + SEC_DAILY -
1650
0
            (time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1651
0
             + time_rec->ts.tm_sec); /* until the end of the day */
1652
0
        break;
1653
0
      case FREQ_WEEKLY:
1654
0
        time_rec->dtend = time_rec->dtstart + SEC_WEEKLY -
1655
0
            (time_rec->ts.tm_wday * SEC_DAILY
1656
0
             + time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1657
0
             + time_rec->ts.tm_sec); /* until the end of the week */
1658
0
        break;
1659
0
      case FREQ_MONTHLY:
1660
0
        time_rec->dtend = time_rec->dtstart + SEC_MONTHLY_MAX -
1661
0
            (time_rec->ts.tm_mday * SEC_DAILY
1662
0
             + time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1663
0
             + time_rec->ts.tm_sec); /* until the end of the month */
1664
0
        break;
1665
0
      case FREQ_YEARLY:
1666
0
        time_rec->dtend = time_rec->dtstart + SEC_YEARLY_MAX -
1667
0
            (time_rec->ts.tm_yday * SEC_DAILY
1668
0
             + time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1669
0
             + time_rec->ts.tm_sec); /* until the end of the year */
1670
0
        break;
1671
0
      default:
1672
0
        time_rec->dtend = 4294967295UL; /* auto-fix to +136 years */
1673
0
      }
1674
0
    }
1675
1676
0
    time_rec->duration = time_rec->dtend - time_rec->dtstart;
1677
0
  } else {
1678
0
    time_rec->dtend = time_rec->dtstart + time_rec->duration;
1679
0
  }
1680
1681
0
  if (!_IS_SET(time_rec->freq) &&
1682
0
          (_IS_SET(time_rec->until) || _IS_SET(time_rec->interval)
1683
0
           || time_rec->flags & TR_BYXXX)) {
1684
0
    LM_ERR("missing FREQ component in time rec: '%s'\n", tr);
1685
0
    return -1;
1686
0
  }
1687
1688
0
  return 0;
1689
1690
0
parse_error:
1691
0
  LM_ERR("parse error in <%s> around position %i\n",
1692
0
         tr, (int)(long)(p-tr));
1693
0
  if (time_rec->tz) {
1694
0
    free_f(time_rec->tz);
1695
0
    tz_reset();
1696
0
    time_rec->tz = NULL;
1697
0
  }
1698
1699
0
  return -1;
1700
0
}
1701
1702
1703
tmrec *tmrec_parse(const char *tr, char alloc)
1704
0
{
1705
0
  tmrec_p time_rec;
1706
1707
0
  time_rec = tmrec_new(alloc);
1708
0
  if (!time_rec) {
1709
0
    LM_ERR("oom\n");
1710
0
    return NULL;
1711
0
  }
1712
1713
0
  if (_tmrec_parse(tr, time_rec) < 0) {
1714
0
    tmrec_free(time_rec);
1715
0
    return NULL;
1716
0
  }
1717
1718
0
  return (tmrec *)time_rec;
1719
0
}
1720
1721
1722
int _tmrec_check(const tmrec *_tr, time_t time)
1723
0
{
1724
0
  tmrec_t *tr = (tmrec_p)_tr;
1725
0
  ac_tm_t att;
1726
0
  int rc;
1727
1728
  /* shortcut: if there is no dstart, timerec is valid */
1729
0
  if (!_IS_SET(tr->dtstart))
1730
0
    return 1;
1731
1732
0
  if (tr->tz)
1733
0
    _tz_set(tr->tz);
1734
1735
  /* set current time */
1736
0
  ac_tm_set_time(&att, time);
1737
1738
  /* does the recv_time match the specified interval?  */
1739
0
  rc = check_tmrec(tr, &att);
1740
1741
0
  if (tr->tz)
1742
0
    tz_reset();
1743
1744
0
  return rc == 0;
1745
0
}
1746
1747
1748
int _tmrec_check_str(const char *tr, time_t check_time)
1749
0
{
1750
0
  tmrec_t time_rec;
1751
0
  int rc;
1752
1753
0
  LM_DBG("checking: '%s'\n", tr);
1754
0
  tmrec_init(&time_rec);
1755
0
  time_rec.flags = PKG_ALLOC;
1756
1757
0
  if (_tmrec_parse(tr, &time_rec) < 0) {
1758
0
    LM_ERR("failed to parse time rec\n");
1759
0
    tmrec_free_members(&time_rec, time_rec.flags);
1760
0
    return -2;
1761
0
  }
1762
1763
0
  rc = _tmrec_check(&time_rec, check_time) ? 1 : -1;
1764
1765
0
  tmrec_free_members(&time_rec, time_rec.flags);
1766
1767
0
  return rc;
1768
0
}
1769
1770
1771
int _tmrec_expr_check_str(const char *trx, time_t check_time)
1772
0
{
1773
0
  char *p, *q, bkp, tmp = 77, need_close, invert_next = 0, op = 0;
1774
0
  str aux;
1775
0
  enum {
1776
0
    NEED_OPERAND,
1777
0
    NEED_OPERATOR,
1778
0
  } state = NEED_OPERAND;
1779
1780
0
  int rc = 0, _rc;
1781
0
  char is_valid;
1782
1783
0
  LM_DBG("checking: %s\n", trx);
1784
1785
  /* NULL input -> nothing to match against -> no match! */
1786
0
  if (!trx)
1787
0
    return -1;
1788
1789
0
  for (p = (char *)trx; *p != '\0'; p++) {
1790
0
    if (is_ws(*p))
1791
0
      continue;
1792
1793
0
    switch (state) {
1794
0
    case NEED_OPERAND:
1795
0
      switch (*p) {
1796
0
      case ')':
1797
0
      case '&':
1798
0
      case '/':
1799
0
        LM_ERR("failed to parse time rec (unexpected '%c')\n", *p);
1800
0
        goto parse_err;
1801
1802
0
      case '!':
1803
0
        invert_next = !invert_next;
1804
0
        continue;
1805
1806
0
      case '(':
1807
0
        for (need_close = 1, q = p + 1; *q != '\0'; q++) {
1808
0
          switch (*q) {
1809
0
          case '(':
1810
0
            need_close++;
1811
0
            break;
1812
1813
0
          case ')':
1814
0
            need_close--;
1815
0
            break;
1816
1817
0
          default:
1818
0
            continue;
1819
0
          }
1820
1821
0
          if (!need_close)
1822
0
            break;
1823
0
        }
1824
1825
0
        if (need_close) {
1826
0
          LM_ERR("failed to parse time rec (bad parentheses)\n");
1827
0
          goto parse_err;
1828
0
        }
1829
1830
0
        aux.s = p + 1;
1831
0
        aux.len = q - aux.s;
1832
0
        trim(&aux);
1833
1834
0
        bkp = aux.s[aux.len];
1835
0
        aux.s[aux.len] = '\0';
1836
0
        _rc = _tmrec_expr_check_str(aux.s, check_time) + 1;
1837
0
        aux.s[aux.len] = bkp;
1838
1839
0
        if (_rc < 0)
1840
0
          goto parse_err;
1841
1842
0
        p = q;
1843
1844
0
        if (invert_next) {
1845
0
          invert_next = 0;
1846
0
          _rc = (_rc + 2) % 4;
1847
0
        }
1848
1849
0
        if (op == 1)
1850
0
          rc &= _rc;
1851
0
        else if (op == 2)
1852
0
          rc |= _rc;
1853
0
        else
1854
0
          rc = tmp = _rc;
1855
1856
0
        state = NEED_OPERATOR;
1857
0
        break;
1858
1859
0
      default:
1860
0
        for (is_valid = 0, q = p + 1; *q != '\0'; q++) {
1861
0
          if (*q == '!' || *q == '(' || *q == ')') {
1862
0
            LM_ERR("failed to parse multi time rec at '%c' "
1863
0
                   "(unexpected character)\n", *q);
1864
0
            goto parse_err;
1865
0
          } else if (*q == TR_SEPARATOR) {
1866
0
            is_valid = 1;
1867
0
          }
1868
1869
0
          if (is_ws(*q)) {
1870
0
            state = NEED_OPERATOR;
1871
0
            break;
1872
0
          } else if (*q == '&' || (*q == '/' && is_valid)) {
1873
0
            break;
1874
0
          }
1875
0
        }
1876
1877
0
        aux.s = p;
1878
0
        aux.len = q - aux.s;
1879
0
        trim(&aux);
1880
1881
0
        bkp = aux.s[aux.len];
1882
0
        aux.s[aux.len] = '\0';
1883
0
        _rc = _tmrec_check_str(aux.s, check_time) + 1;
1884
0
        aux.s[aux.len] = bkp;
1885
1886
0
        if (_rc < 0) {
1887
0
          LM_ERR("failed to parse single time rec: '%.*s'\n",
1888
0
                 aux.len, aux.s);
1889
0
          return _rc - 1;
1890
0
        }
1891
1892
0
        if (invert_next) {
1893
0
          invert_next = 0;
1894
0
          _rc = (_rc + 2) % 4;
1895
0
        }
1896
1897
0
        if (*q == '&') {
1898
0
          if (op == 2) {
1899
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
1900
0
                   "type is allowed within an expression)\n");
1901
0
            goto parse_err;
1902
0
          }
1903
1904
0
          if (op == 0)
1905
0
            rc = _rc;
1906
0
          op = 1;
1907
0
        } else if (*q == '/') {
1908
0
          if (op == 1) {
1909
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
1910
0
                   "type is allowed within an expression)\n");
1911
0
            goto parse_err;
1912
0
          }
1913
1914
0
          op = 2;
1915
0
        }
1916
1917
0
        if (op == 1)
1918
0
          rc &= _rc;
1919
0
        else if (op == 2)
1920
0
          rc |= _rc;
1921
0
        else
1922
0
          rc = tmp = _rc;
1923
1924
0
        if (*q == '\0')
1925
0
          return rc - 1;
1926
1927
0
        p = q;
1928
0
      }
1929
0
      break;
1930
1931
0
    case NEED_OPERATOR:
1932
0
      switch (*p) {
1933
0
        case '&':
1934
0
          if (op == 2) {
1935
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
1936
0
                   "type is allowed within an expression)\n");
1937
0
            goto parse_err;
1938
0
          } else if (op == 0) {
1939
0
            rc = tmp;
1940
0
          }
1941
1942
0
          op = 1;
1943
0
          state = NEED_OPERAND;
1944
0
          break;
1945
1946
0
        case '/':
1947
0
          if (op == 1) {
1948
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
1949
0
                   "type is allowed within an expression)\n");
1950
0
            goto parse_err;
1951
0
          }
1952
1953
0
          op = 2;
1954
0
          state = NEED_OPERAND;
1955
0
          break;
1956
1957
0
        default:
1958
0
          LM_ERR("failed to parse the rec string (bad char: '%c', "
1959
0
                 "expected operator)\n", *p);
1960
0
          goto parse_err;
1961
0
      }
1962
0
    }
1963
0
  }
1964
1965
0
  if (state == NEED_OPERAND && op != 0) {
1966
0
    LM_ERR("failed to parse the rec string (missing operand)\n");
1967
0
    LM_ERR("input given: '%s'\n", trx);
1968
0
    return -2;
1969
0
  }
1970
1971
0
  if (invert_next)
1972
0
    return (rc + 2) % 4 - 1;
1973
0
  else
1974
0
    return rc - 1;
1975
1976
0
  return rc;
1977
1978
0
parse_err:
1979
0
  LM_ERR("input given: '%s'\n", trx);
1980
0
  return -2;
1981
0
}
1982
1983
1984
tmrec_expr *tmrec_expr_parse(const char *trx, char alloc_type)
1985
0
{
1986
0
  enum {
1987
0
    NEED_OPERAND,
1988
0
    NEED_OPERATOR,
1989
0
  } state = NEED_OPERAND;
1990
1991
0
  tmrec_expr_t *exp, *e;
1992
0
  osips_malloc_t malloc_f;
1993
0
  char *p, *q, bkp, need_close, invert_next = 0, is_valid;
1994
0
  str aux;
1995
0
  int rc;
1996
1997
0
  if (!trx)
1998
0
    return NULL;
1999
2000
0
  LM_DBG("checking: %s\n", trx);
2001
2002
0
  malloc_f = (alloc_type & PKG_ALLOC ?
2003
0
                  osips_pkg_malloc : osips_shm_malloc);
2004
0
  exp = malloc_f(sizeof *exp);
2005
0
  if (!exp) {
2006
0
    LM_ERR("oom\n");
2007
0
    return NULL;
2008
0
  }
2009
2010
0
  tmrec_expr_init(exp);
2011
0
  exp->flags = alloc_type;
2012
2013
0
  for (p = (char *)trx; *p != '\0'; p++) {
2014
0
    if (is_ws(*p))
2015
0
      continue;
2016
2017
0
    switch (state) {
2018
0
    case NEED_OPERAND:
2019
0
      switch (*p) {
2020
0
      case ')':
2021
0
      case '&':
2022
0
      case '/':
2023
0
        LM_ERR("failed to parse time rec (unexpected '%c')\n", *p);
2024
0
        goto parse_err;
2025
2026
0
      case '!':
2027
0
        invert_next = !invert_next;
2028
0
        continue;
2029
2030
0
      case '(':
2031
0
        for (need_close = 1, q = p + 1; *q != '\0'; q++) {
2032
0
          switch (*q) {
2033
0
          case '(':
2034
0
            need_close++;
2035
0
            break;
2036
2037
0
          case ')':
2038
0
            need_close--;
2039
0
            break;
2040
2041
0
          default:
2042
0
            continue;
2043
0
          }
2044
2045
0
          if (!need_close)
2046
0
            break;
2047
0
        }
2048
2049
0
        if (need_close) {
2050
0
          LM_ERR("failed to parse time rec (bad parentheses)\n");
2051
0
          goto parse_err;
2052
0
        }
2053
2054
0
        aux.s = p + 1;
2055
0
        aux.len = q - aux.s;
2056
0
        trim(&aux);
2057
2058
0
        bkp = aux.s[aux.len];
2059
0
        aux.s[aux.len] = '\0';
2060
0
        e = tmrec_expr_parse(aux.s, alloc_type);
2061
0
        aux.s[aux.len] = bkp;
2062
2063
0
        if (!e)
2064
0
          goto parse_err;
2065
2066
0
        if (invert_next) {
2067
0
          invert_next = 0;
2068
0
          e->inverted = 1;
2069
0
        }
2070
2071
0
        list_add_tail(&e->list, &exp->operands);
2072
0
        p = q;
2073
0
        state = NEED_OPERATOR;
2074
0
        break;
2075
2076
0
      default:
2077
0
        for (is_valid = 0, q = p + 1; *q != '\0'; q++) {
2078
0
          if (*q == '!' || *q == '(' || *q == ')') {
2079
0
            LM_ERR("failed to parse multi time rec at '%c' "
2080
0
                   "(unexpected character)\n", *q);
2081
0
            goto parse_err;
2082
0
          } else if (*q == TR_SEPARATOR) {
2083
0
            is_valid = 1;
2084
0
          }
2085
2086
0
          if (is_ws(*q)) {
2087
0
            state = NEED_OPERATOR;
2088
0
            break;
2089
0
          } else if (*q == '&' || (*q == '/' && is_valid)) {
2090
0
            break;
2091
0
          }
2092
0
        }
2093
2094
0
        e = malloc_f(sizeof *e);
2095
0
        if (!e) {
2096
0
          LM_ERR("oom\n");
2097
0
          goto parse_err;
2098
0
        }
2099
2100
0
        tmrec_expr_init(e);
2101
0
        e->flags = e->tr.flags = alloc_type;
2102
0
        e->is_leaf = 1;
2103
2104
0
        list_add_tail(&e->list, &exp->operands);
2105
2106
0
        aux.s = p;
2107
0
        aux.len = q - aux.s;
2108
0
        trim(&aux);
2109
2110
0
        bkp = aux.s[aux.len];
2111
0
        aux.s[aux.len] = '\0';
2112
0
        rc = _tmrec_parse(aux.s, &e->tr);
2113
0
        aux.s[aux.len] = bkp;
2114
2115
0
        if (rc < 0)
2116
0
          goto parse_err;
2117
2118
0
        if (invert_next) {
2119
0
          invert_next = 0;
2120
0
          e->inverted = 1;
2121
0
        }
2122
2123
0
        if (*q == '&') {
2124
0
          if (exp->op == TR_OP_OR) {
2125
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
2126
0
                   "type is allowed within an expression)\n");
2127
0
            goto parse_err;
2128
0
          }
2129
2130
0
          exp->op = TR_OP_AND;
2131
0
        } else if (*q == '/') {
2132
0
          if (exp->op == TR_OP_AND) {
2133
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
2134
0
                   "type is allowed within an expression)\n");
2135
0
            goto parse_err;
2136
0
          }
2137
2138
0
          exp->op = TR_OP_OR;
2139
0
        }
2140
2141
0
        if (*q == '\0')
2142
0
          return exp;
2143
2144
0
        p = q;
2145
0
      }
2146
0
      break;
2147
2148
0
    case NEED_OPERATOR:
2149
0
      switch (*p) {
2150
0
        case '&':
2151
0
          if (exp->op == TR_OP_OR) {
2152
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
2153
0
                   "type is allowed within an expression)\n");
2154
0
            goto parse_err;
2155
0
          }
2156
2157
0
          exp->op = TR_OP_AND;
2158
0
          state = NEED_OPERAND;
2159
0
          break;
2160
2161
0
        case '/':
2162
0
          if (exp->op == TR_OP_AND) {
2163
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
2164
0
                   "type is allowed within an expression)\n");
2165
0
            goto parse_err;
2166
0
          }
2167
2168
0
          exp->op = TR_OP_OR;
2169
0
          state = NEED_OPERAND;
2170
0
          break;
2171
2172
0
        default:
2173
0
          LM_ERR("failed to parse the rec string (bad char: '%c', "
2174
0
                 "expected operator)\n", *p);
2175
0
          goto parse_err;
2176
0
      }
2177
0
    }
2178
0
  }
2179
2180
0
  if (state == NEED_OPERAND && exp->op != TR_OP_NUL) {
2181
0
    LM_ERR("failed to parse the rec string (missing operand)\n");
2182
0
    goto parse_err;
2183
0
  }
2184
2185
0
  if (invert_next)
2186
0
    exp->inverted = 1;
2187
2188
0
  return exp;
2189
2190
0
parse_err:
2191
0
  tmrec_expr_free(exp);
2192
0
  LM_ERR("parsing failed, input given: '%s'\n", trx);
2193
0
  return NULL;
2194
0
}
2195
2196
2197
int _tmrec_expr_check(const tmrec_expr *_trx, time_t check_time)
2198
0
{
2199
0
  struct list_head *el;
2200
0
  const tmrec_expr_t *exp, *trx = (const tmrec_expr_t *)_trx;
2201
0
  int rc = 0;
2202
2203
0
  if (!trx)
2204
0
    return -1;
2205
2206
0
  if (trx->is_leaf) {
2207
0
    rc = _tmrec_check(&trx->tr, check_time) ? 2 : 0;
2208
0
    goto out;
2209
0
  }
2210
2211
0
  if (list_empty(&trx->operands))
2212
0
    goto out;
2213
2214
0
  rc = (trx->op == TR_OP_AND ? 2 : 0);
2215
2216
0
  list_for_each (el, &trx->operands) {
2217
0
    exp = list_entry(el, tmrec_expr_t, list);
2218
2219
0
    if (trx->op == TR_OP_AND)
2220
0
      rc = rc & (_tmrec_expr_check(exp, check_time) + 1);
2221
0
    else
2222
0
      rc = rc | (_tmrec_expr_check(exp, check_time) + 1);
2223
0
  }
2224
2225
0
out:
2226
0
  if (trx->inverted) {
2227
0
    LM_DEV("result: %d\n", (rc + 2) % 4 - 1);
2228
0
    return (rc + 2) % 4 - 1;
2229
0
  }
2230
2231
0
  LM_DEV("result: %d\n", rc - 1);
2232
0
  return rc - 1;
2233
0
}
2234
2235
2236
void tmrec_expr_free(tmrec_expr *_trx)
2237
0
{
2238
0
  struct list_head *el, *next;
2239
0
  tmrec_expr_t *exp, *trx = (tmrec_expr_t *)_trx;
2240
0
  osips_free_t free_f;
2241
2242
0
  if (!trx)
2243
0
    return;
2244
2245
0
  free_f = (trx->flags & PKG_ALLOC ?
2246
0
                osips_pkg_free : osips_shm_free);
2247
2248
0
  list_for_each_safe (el, next, &trx->operands) {
2249
0
    exp = list_entry(el, tmrec_expr_t, list);
2250
0
    tmrec_expr_free(exp);
2251
0
  }
2252
2253
0
  tmrec_free_members(&trx->tr, trx->flags);
2254
0
  free_f(trx);
2255
0
}
2256
2257
2258
int _tz_offset(const char *tz, time_t t)
2259
0
{
2260
0
  struct tm lt = {0};
2261
2262
0
  _tz_set(tz);
2263
0
  localtime_r(&t, &lt);
2264
0
  tz_reset();
2265
2266
0
  return lt.tm_gmtoff;
2267
0
}