Coverage Report

Created: 2025-07-18 06:32

/src/opensips/time_rec.c
Line
Count
Source (jump to first uncovered line)
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
  return -1;
467
0
}
468
469
470
int tr_byxxx_free(tr_byxxx_p _bxp)
471
0
{
472
0
  if (!_bxp)
473
0
    return -1;
474
475
0
  if (_bxp->flags & PKG_ALLOC) {
476
0
    pkg_free(_bxp->xxx);
477
0
    pkg_free(_bxp->req);
478
0
    pkg_free(_bxp);
479
0
  } else {
480
0
    shm_free(_bxp->xxx);
481
0
    shm_free(_bxp->req);
482
0
    shm_free(_bxp);
483
0
  }
484
485
0
  return 0;
486
0
}
487
488
void tmrec_init(tmrec_p t)
489
0
{
490
0
  memset(t, 0, sizeof *t);
491
492
  /* these values may be legitimately set to 0 (i.e. UNIX start time) */
493
0
  t->dtstart  = TR_NOVAL;
494
0
  t->dtend    = TR_NOVAL;
495
0
  t->duration = TR_NOVAL;
496
497
0
  t->freq     = TR_NOVAL;
498
0
  t->until    = TR_NOVAL;
499
0
  t->interval = TR_NOVAL;
500
0
}
501
502
static inline void tmrec_expr_init(tmrec_expr_t *e)
503
0
{
504
0
  memset(e, 0, sizeof *e);
505
0
  INIT_LIST_HEAD(&e->operands);
506
0
  tmrec_init(&e->tr);
507
0
}
508
509
tmrec_p tmrec_new(char alloc)
510
0
{
511
0
  tmrec_p _trp;
512
0
  if (alloc & PKG_ALLOC)
513
0
    _trp = (tmrec_p)pkg_malloc(sizeof(tmrec_t));
514
0
  else
515
0
    _trp = (tmrec_p)shm_malloc(sizeof(tmrec_t));
516
0
  if(!_trp)
517
0
    return NULL;
518
519
0
  tmrec_init(_trp);
520
0
  _trp->flags = alloc;
521
0
  return _trp;
522
0
}
523
524
void tmrec_free(tmrec *tr)
525
0
{
526
0
  tmrec_p _trp = (tmrec_p)tr;
527
528
0
  if(!_trp)
529
0
    return;
530
531
0
  tr_byxxx_free(_trp->byday);
532
0
  tr_byxxx_free(_trp->bymday);
533
0
  tr_byxxx_free(_trp->byyday);
534
0
  tr_byxxx_free(_trp->bymonth);
535
0
  tr_byxxx_free(_trp->byweekno);
536
537
0
  if (_trp->flags & PKG_ALLOC) {
538
0
    pkg_free(_trp->tz);
539
0
    pkg_free(_trp);
540
0
  } else {
541
0
    shm_free(_trp->tz);
542
0
    shm_free(_trp);
543
0
  }
544
0
}
545
546
int tr_parse_tz(tmrec_p _trp, char *_in)
547
0
{
548
0
  if (!_trp || !_in)
549
0
    return -1;
550
551
0
  if (*_in < 'A' || *_in > 'Z')
552
0
    return 1;
553
554
0
  if (_trp->flags & PKG_ALLOC)
555
0
    _trp->tz = pkg_strdup(_in);
556
0
  else
557
0
    _trp->tz = shm_strdup(_in);
558
559
0
  if (!_trp->tz) {
560
0
    LM_ERR("oom\n");
561
0
    return -1;
562
0
  }
563
564
0
  return 0;
565
0
}
566
567
int tr_parse_dtstart(tmrec_p _trp, char *_in)
568
0
{
569
0
  if (!_in)
570
0
    return -1;
571
0
  _trp->dtstart = ic_parse_datetime(_in, &(_trp->ts));
572
0
  return (_trp->dtstart == TR_NOVAL) ? -1 : 0;
573
0
}
574
575
int tr_parse_dtend(tmrec_p _trp, char *_in)
576
0
{
577
0
  struct tm _tm;
578
0
  if (!_in)
579
0
    return -1;
580
0
  _trp->dtend = ic_parse_datetime(_in,&_tm);
581
0
  return (_trp->dtend == TR_NOVAL) ? -1 : 0;
582
0
}
583
584
int tr_parse_duration(tmrec_p _trp, char *_in)
585
0
{
586
0
  if (!_in)
587
0
    return -1;
588
0
  _trp->duration = ic_parse_duration(_in);
589
0
  return (_trp->duration == TR_NOVAL) ? -1 : 0;
590
0
}
591
592
int tr_parse_until(tmrec_p _trp, char *_in)
593
0
{
594
0
  struct tm _tm;
595
0
  if (!_in)
596
0
    return -1;
597
0
  _trp->until = ic_parse_datetime(_in, &_tm);
598
0
  return (_trp->until == TR_NOVAL) ? -1 : 0;
599
0
}
600
601
int tr_parse_freq(tmrec_p _trp, char *_in)
602
0
{
603
0
  if (!_in)
604
0
    return -1;
605
606
0
  if(strlen(_in)<5)
607
0
  {
608
0
    _trp->freq = FREQ_NOFREQ;
609
0
    return 0;
610
0
  }
611
0
  if(!strcasecmp(_in, "daily"))
612
0
  {
613
0
    _trp->freq = FREQ_DAILY;
614
0
    return 0;
615
0
  }
616
0
  if(!strcasecmp(_in, "weekly"))
617
0
  {
618
0
    _trp->freq = FREQ_WEEKLY;
619
0
    return 0;
620
0
  }
621
0
  if(!strcasecmp(_in, "monthly"))
622
0
  {
623
0
    _trp->freq = FREQ_MONTHLY;
624
0
    return 0;
625
0
  }
626
0
  if(!strcasecmp(_in, "yearly"))
627
0
  {
628
0
    _trp->freq = FREQ_YEARLY;
629
0
    return 0;
630
0
  }
631
632
0
  _trp->freq = FREQ_NOFREQ;
633
0
  return 0;
634
0
}
635
636
int tr_parse_interval(tmrec_p _trp, char *_in)
637
0
{
638
0
  if (!_in)
639
0
    return -1;
640
0
  _trp->interval = strz2int(_in);
641
0
  return 0;
642
0
}
643
644
int tr_parse_byday(tmrec_p _trp, char *_in)
645
0
{
646
0
  if (!_in)
647
0
    return -1;
648
649
0
  _trp->byday = ic_parse_byday(_in, _trp->flags);
650
0
  _trp->flags |= TR_BYXXX;
651
0
  return 0;
652
0
}
653
654
int tr_parse_bymday(tmrec_p _trp, char *_in)
655
0
{
656
0
  if (!_in)
657
0
    return -1;
658
659
0
  _trp->bymday = ic_parse_byxxx(_in, _trp->flags);
660
0
  _trp->flags |= TR_BYXXX;
661
0
  return 0;
662
0
}
663
664
int tr_parse_byyday(tmrec_p _trp, char *_in)
665
0
{
666
0
  if (!_in)
667
0
    return -1;
668
669
0
  _trp->byyday = ic_parse_byxxx(_in, _trp->flags);
670
0
  _trp->flags |= TR_BYXXX;
671
0
  return 0;
672
0
}
673
674
int tr_parse_bymonth(tmrec_p _trp, char *_in)
675
0
{
676
0
  if (!_in)
677
0
    return -1;
678
679
0
  _trp->bymonth = ic_parse_byxxx(_in, _trp->flags);
680
0
  _trp->flags |= TR_BYXXX;
681
0
  return 0;
682
0
}
683
684
int tr_parse_byweekno(tmrec_p _trp, char *_in)
685
0
{
686
0
  if (!_in)
687
0
    return -1;
688
689
0
  _trp->byweekno = ic_parse_byxxx(_in, _trp->flags);
690
0
  _trp->flags |= TR_BYXXX;
691
0
  return 0;
692
0
}
693
694
int tr_parse_wkst(tmrec_p _trp, char *_in)
695
0
{
696
0
  if (!_in)
697
0
    return -1;
698
699
0
  _trp->wkst = ic_parse_wkst(_in);
700
0
  return 0;
701
0
}
702
703
int tmrec_print(const tmrec *tr)
704
0
{
705
0
  static char *_wdays[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
706
0
  tmrec_p _trp = (tmrec_p)tr;
707
0
  int i;
708
709
0
  if(!_trp)
710
0
  {
711
0
    printf("\n(null)\n");
712
0
    return -1;
713
0
  }
714
0
  printf("Recurrence definition\n-- start time ---\n");
715
0
  printf("Sys time: %d\n", (int)_trp->dtstart);
716
0
  printf("Time: %02d:%02d:%02d\n", _trp->ts.tm_hour,
717
0
        _trp->ts.tm_min, _trp->ts.tm_sec);
718
0
  printf("Date: %s, %04d-%02d-%02d\n", _wdays[_trp->ts.tm_wday],
719
0
        _trp->ts.tm_year+1900, _trp->ts.tm_mon+1, _trp->ts.tm_mday);
720
0
  printf("---\n");
721
0
  printf("End time: %d\n", (int)_trp->dtend);
722
0
  printf("Duration: %d\n", (int)_trp->duration);
723
0
  printf("Until: %d\n", (int)_trp->until);
724
0
  printf("Freq: %d\n", (int)_trp->freq);
725
0
  printf("Interval: %d\n", (int)_trp->interval);
726
0
  if(_trp->byday)
727
0
  {
728
0
    printf("Byday: ");
729
0
    for(i=0; i<_trp->byday->nr; i++)
730
0
      printf(" %d%s", _trp->byday->req[i], _wdays[_trp->byday->xxx[i]]);
731
0
    printf("\n");
732
0
  }
733
0
  if(_trp->bymday)
734
0
  {
735
0
    printf("Bymday: %d:", _trp->bymday->nr);
736
0
    for(i=0; i<_trp->bymday->nr; i++)
737
0
      printf(" %d", _trp->bymday->xxx[i]*_trp->bymday->req[i]);
738
0
    printf("\n");
739
0
  }
740
0
  if(_trp->byyday)
741
0
  {
742
0
    printf("Byyday:");
743
0
    for(i=0; i<_trp->byyday->nr; i++)
744
0
      printf(" %d", _trp->byyday->xxx[i]*_trp->byyday->req[i]);
745
0
    printf("\n");
746
0
  }
747
0
  if(_trp->bymonth)
748
0
  {
749
0
    printf("Bymonth: %d:", _trp->bymonth->nr);
750
0
    for(i=0; i< _trp->bymonth->nr; i++)
751
0
      printf(" %d", _trp->bymonth->xxx[i]*_trp->bymonth->req[i]);
752
0
    printf("\n");
753
0
  }
754
0
  if(_trp->byweekno)
755
0
  {
756
0
    printf("Byweekno: ");
757
0
    for(i=0; i<_trp->byweekno->nr; i++)
758
0
      printf(" %d", _trp->byweekno->xxx[i]*_trp->byweekno->req[i]);
759
0
    printf("\n");
760
0
  }
761
0
  printf("Weekstart: %d\n", _trp->wkst);
762
0
  return 0;
763
0
}
764
765
time_t ic_parse_datetime(char *_in, struct tm *_tm)
766
0
{
767
0
  struct tm t;
768
769
0
  if (!_in || strlen(_in)!=15)
770
0
    return TR_NOVAL;
771
772
0
  t.tm_year = _D(_in[0])*1000 + _D(_in[1])*100
773
0
      + _D(_in[2])*10 + _D(_in[3]);
774
0
  if (t.tm_year < 1970) {
775
0
    LM_ERR("invalid year in Date-Time: '%s'\n", _in);
776
0
    return TR_NOVAL;
777
0
  }
778
779
0
  t.tm_year -= 1900; /* per man ctime(3) */
780
0
  t.tm_mon = _D(_in[4])*10 + _D(_in[5]) - 1;
781
0
  t.tm_mday = _D(_in[6])*10 + _D(_in[7]);
782
0
  if (t.tm_mon == -1 || t.tm_mday == 0) {
783
0
    LM_ERR("month or month day cannot be zero in Date-Time: '%s'\n", _in);
784
0
    return TR_NOVAL;
785
0
  }
786
787
0
  t.tm_hour = _D(_in[9])*10 + _D(_in[10]);
788
0
  t.tm_min = _D(_in[11])*10 + _D(_in[12]);
789
0
  t.tm_sec = _D(_in[13])*10 + _D(_in[14]);
790
0
  t.tm_isdst = -1 /*daylight*/;
791
792
0
  *_tm = t;
793
0
  return mktime(_tm);
794
0
}
795
796
time_t ic_parse_duration(char *_in)
797
0
{
798
0
  time_t _t, _ft;
799
0
  char *_p;
800
0
  int _fl;
801
802
0
  if(!_in || strlen(_in)<2)
803
0
    return TR_NOVAL;
804
805
0
  if(*_in == 'P' || *_in=='p')
806
0
  {
807
0
    _p = _in+1;
808
0
    _fl = 1;
809
0
  } else {
810
0
    _p = _in;
811
0
    _fl = 0;
812
0
  }
813
814
0
  _t = _ft = 0;
815
816
0
  while(*_p)
817
0
  {
818
0
    switch(*_p)
819
0
    {
820
0
      case '0': case '1': case '2':
821
0
      case '3': case '4': case '5':
822
0
      case '6': case '7': case '8':
823
0
      case '9':
824
0
        _t = _t*10 + *_p - '0';
825
0
      break;
826
827
0
      case 'w':
828
0
      case 'W':
829
0
        if(!_fl)
830
0
        {
831
0
          LM_ERR("week duration not allowed"
832
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
833
0
          return 0;
834
0
        }
835
0
        _ft += _t*7*24*3600;
836
0
        _t = 0;
837
0
      break;
838
0
      case 'd':
839
0
      case 'D':
840
0
        if(!_fl)
841
0
        {
842
0
          LM_ERR("day duration not allowed"
843
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
844
0
          return 0;
845
0
        }
846
0
        _ft += _t*24*3600;
847
0
        _t = 0;
848
0
      break;
849
0
      case 'h':
850
0
      case 'H':
851
0
        if(_fl)
852
0
        {
853
0
          LM_ERR("hour duration not allowed"
854
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
855
0
          return 0;
856
0
        }
857
0
        _ft += _t*3600;
858
0
        _t = 0;
859
0
      break;
860
0
      case 'm':
861
0
      case 'M':
862
0
        if(_fl)
863
0
        {
864
0
          LM_ERR("minute duration not allowed"
865
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
866
0
          return 0;
867
0
        }
868
0
        _ft += _t*60;
869
0
        _t = 0;
870
0
      break;
871
0
      case 's':
872
0
      case 'S':
873
0
        if(_fl)
874
0
        {
875
0
          LM_ERR("second duration not allowed"
876
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
877
0
          return 0;
878
0
        }
879
0
        _ft += _t;
880
0
        _t = 0;
881
0
      break;
882
0
      case 't':
883
0
      case 'T':
884
0
        if(!_fl)
885
0
        {
886
0
          LM_ERR("'T' not allowed"
887
0
            " here (%d) [%s]\n", (int)(_p-_in), _in);
888
0
          return 0;
889
0
        }
890
0
        _fl = 0;
891
0
      break;
892
0
      default:
893
0
        LM_ERR("bad character here (%d) [%s]\n",
894
0
          (int)(_p-_in), _in);
895
0
        return 0;
896
0
    }
897
0
    _p++;
898
0
  }
899
900
0
  return _ft;
901
0
}
902
903
tr_byxxx_p ic_parse_byday(char *_in, char type)
904
0
{
905
0
  tr_byxxx_p _bxp = NULL;
906
0
  int _nr, _s, _v;
907
0
  char *_p;
908
909
0
  if(!_in)
910
0
    return NULL;
911
0
  _bxp = tr_byxxx_new(type);
912
0
  if(!_bxp)
913
0
    return NULL;
914
0
  _p = _in;
915
0
  _nr = 1;
916
0
  while(*_p)
917
0
  {
918
0
    if(*_p == ',')
919
0
      _nr++;
920
0
    _p++;
921
0
  }
922
0
  if(tr_byxxx_init(_bxp, _nr) < 0)
923
0
  {
924
0
    tr_byxxx_free(_bxp);
925
0
    return NULL;
926
0
  }
927
0
  _p = _in;
928
0
  _nr = _v = 0;
929
0
  _s = 1;
930
0
  while(*_p && _nr < _bxp->nr)
931
0
  {
932
0
    switch(*_p)
933
0
    {
934
0
      case '0': case '1': case '2':
935
0
      case '3': case '4': case '5':
936
0
      case '6': case '7': case '8':
937
0
      case '9':
938
0
        _v = _v*10 + *_p - '0';
939
0
      break;
940
941
0
      case 's':
942
0
      case 'S':
943
0
        _p++;
944
0
        switch(*_p)
945
0
        {
946
0
          case 'a':
947
0
          case 'A':
948
0
            _bxp->xxx[_nr] = WDAY_SA;
949
0
          break;
950
0
          case 'u':
951
0
          case 'U':
952
0
            _bxp->xxx[_nr] = WDAY_SU;
953
0
          break;
954
0
          default:
955
0
            goto error;
956
0
        }
957
958
0
        _bxp->req[_nr] = _s * _v;
959
0
        if (_bxp->req[_nr] > 0)
960
0
          _bxp->req[_nr]--;
961
0
        _s = 1;
962
0
        _v = 0;
963
0
        break;
964
0
      case 'm':
965
0
      case 'M':
966
0
        _p++;
967
0
        if(*_p!='o' && *_p!='O')
968
0
          goto error;
969
0
        _bxp->xxx[_nr] = WDAY_MO;
970
971
0
        _bxp->req[_nr] = _s * _v;
972
0
        if (_bxp->req[_nr] > 0)
973
0
          _bxp->req[_nr]--;
974
0
        _s = 1;
975
0
        _v = 0;
976
0
        break;
977
0
      case 't':
978
0
      case 'T':
979
0
        _p++;
980
0
        switch(*_p)
981
0
        {
982
0
          case 'h':
983
0
          case 'H':
984
0
            _bxp->xxx[_nr] = WDAY_TH;
985
0
          break;
986
0
          case 'u':
987
0
          case 'U':
988
0
            _bxp->xxx[_nr] = WDAY_TU;
989
0
          break;
990
0
          default:
991
0
            goto error;
992
0
        }
993
994
0
        _bxp->req[_nr] = _s * _v;
995
0
        if (_bxp->req[_nr] > 0)
996
0
          _bxp->req[_nr]--;
997
0
        _s = 1;
998
0
        _v = 0;
999
0
        break;
1000
0
      case 'w':
1001
0
      case 'W':
1002
0
        _p++;
1003
0
        if(*_p!='e' && *_p!='E')
1004
0
          goto error;
1005
0
        _bxp->xxx[_nr] = WDAY_WE;
1006
0
        _s = 1;
1007
0
        _v = 0;
1008
1009
0
        _bxp->req[_nr] = _s * _v;
1010
0
        if (_bxp->req[_nr] > 0)
1011
0
          _bxp->req[_nr]--;
1012
0
        break;
1013
0
      case 'f':
1014
0
      case 'F':
1015
0
        _p++;
1016
0
        if(*_p!='r' && *_p!='R')
1017
0
          goto error;
1018
0
        _bxp->xxx[_nr] = WDAY_FR;
1019
1020
0
        _bxp->req[_nr] = _s * _v;
1021
0
        if (_bxp->req[_nr] > 0)
1022
0
          _bxp->req[_nr]--;
1023
0
        _s = 1;
1024
0
        _v = 0;
1025
0
        break;
1026
0
      case '-':
1027
0
        _s = -1;
1028
0
      break;
1029
0
      case '+':
1030
0
      case ' ':
1031
0
      case '\t':
1032
0
      break;
1033
0
      case ',':
1034
0
        _nr++;
1035
0
      break;
1036
0
      default:
1037
0
        goto error;
1038
0
    }
1039
0
    _p++;
1040
0
  }
1041
1042
0
  return _bxp;
1043
1044
0
error:
1045
0
  tr_byxxx_free(_bxp);
1046
0
  return NULL;
1047
0
}
1048
1049
tr_byxxx_p ic_parse_byxxx(char *_in, char type)
1050
0
{
1051
0
  tr_byxxx_p _bxp = NULL;
1052
0
  int _nr, _s, _v;
1053
0
  char *_p;
1054
1055
0
  if(!_in)
1056
0
    return NULL;
1057
0
  _bxp = tr_byxxx_new(type);
1058
0
  if(!_bxp)
1059
0
    return NULL;
1060
0
  _p = _in;
1061
0
  _nr = 1;
1062
0
  while(*_p)
1063
0
  {
1064
0
    if(*_p == ',')
1065
0
      _nr++;
1066
0
    _p++;
1067
0
  }
1068
0
  if(tr_byxxx_init(_bxp, _nr) < 0)
1069
0
  {
1070
0
    tr_byxxx_free(_bxp);
1071
0
    return NULL;
1072
0
  }
1073
1074
0
  _nr = _v = 0;
1075
0
  _s = 1;
1076
1077
0
  for (_p = _in; *_p; _p++) {
1078
0
    switch (*_p) {
1079
0
    case '0': case '1': case '2':
1080
0
    case '3': case '4': case '5':
1081
0
    case '6': case '7': case '8':
1082
0
    case '9':
1083
0
      _v = _v*10 + *_p - '0';
1084
0
      break;
1085
1086
0
    case '-':
1087
0
      _s = -1;
1088
0
      break;
1089
0
    case '+':
1090
0
    case ' ':
1091
0
    case '\t':
1092
0
      break;
1093
1094
0
    case ',':
1095
0
      _bxp->xxx[_nr] = _v;
1096
0
      _bxp->req[_nr] = _s;
1097
0
      _s = 1;
1098
0
      _v = 0;
1099
0
      _nr++;
1100
0
      break;
1101
1102
0
    default:
1103
0
      goto error;
1104
0
    }
1105
0
  }
1106
1107
  /* store the last item of the list */
1108
0
  _bxp->xxx[_nr] = _v;
1109
0
  _bxp->req[_nr] = _s;
1110
1111
0
  return _bxp;
1112
1113
0
error:
1114
0
  tr_byxxx_free(_bxp);
1115
0
  return NULL;
1116
0
}
1117
1118
int ic_parse_wkst(char *_in)
1119
0
{
1120
0
  if(!_in || strlen(_in)!=2)
1121
0
    goto error;
1122
1123
0
  switch(_in[0])
1124
0
  {
1125
0
    case 's':
1126
0
    case 'S':
1127
0
      switch(_in[1])
1128
0
      {
1129
0
        case 'a':
1130
0
        case 'A':
1131
0
          return WDAY_SA;
1132
0
        case 'u':
1133
0
        case 'U':
1134
0
          return WDAY_SU;
1135
0
        default:
1136
0
          goto error;
1137
0
      }
1138
0
    case 'm':
1139
0
    case 'M':
1140
0
      if(_in[1]!='o' && _in[1]!='O')
1141
0
        goto error;
1142
0
      return WDAY_MO;
1143
0
    case 't':
1144
0
    case 'T':
1145
0
      switch(_in[1])
1146
0
      {
1147
0
        case 'h':
1148
0
        case 'H':
1149
0
          return WDAY_TH;
1150
0
        case 'u':
1151
0
        case 'U':
1152
0
          return WDAY_TU;
1153
0
        default:
1154
0
          goto error;
1155
0
      }
1156
0
    case 'w':
1157
0
    case 'W':
1158
0
      if(_in[1]!='e' && _in[1]!='E')
1159
0
        goto error;
1160
0
      return WDAY_WE;
1161
0
    case 'f':
1162
0
    case 'F':
1163
0
      if(_in[1]!='r' && _in[1]!='R')
1164
0
        goto error;
1165
0
      return WDAY_FR;
1166
0
    break;
1167
0
    default:
1168
0
      goto error;
1169
0
  }
1170
1171
0
error:
1172
#ifdef USE_YWEEK_U
1173
  return WDAY_SU;
1174
#else
1175
0
  return WDAY_MO;
1176
0
#endif
1177
0
}
1178
1179
1180
1181
/*** local headers ***/
1182
int check_min_unit(tmrec_p _trp, ac_tm_p _atp);
1183
int check_freq_interval(tmrec_p _trp, ac_tm_p _atp);
1184
int check_byxxx(tmrec_p, ac_tm_p);
1185
1186
/**
1187
 *
1188
 * return 0/REC_MATCH - the time falls in
1189
 *       -1/REC_ERR - error
1190
 *        1/REC_NOMATCH - the time falls out
1191
 */
1192
int check_tmrec(const tmrec_p _trp, ac_tm_p _atp)
1193
0
{
1194
  /* it is before the start date or outside a non-recurring interval? */
1195
0
  if (_atp->time < _trp->dtstart ||
1196
0
          (!_IS_SET(_trp->freq) && _atp->time >= _trp->dtend))
1197
0
    return REC_NOMATCH;
1198
1199
0
  LM_DEV("1) %ld + %ld = %ld\n", _trp->dtstart, _trp->duration, _trp->dtend);
1200
1201
0
  if (!_IS_SET(_trp->freq) && _atp->time < _trp->dtend)
1202
0
    return REC_MATCH;
1203
1204
0
  LM_DEV("2) check freq\n");
1205
1206
  /* check if the instance of recurrence matches the 'interval' */
1207
0
  if (check_freq_interval(_trp, _atp) != REC_MATCH)
1208
0
    return REC_NOMATCH;
1209
1210
0
  LM_DEV("3) check min unit\n");
1211
1212
0
  if (check_min_unit(_trp, _atp) != REC_MATCH)
1213
0
    return REC_NOMATCH;
1214
1215
0
  LM_DEV("4) check byxxx\n");
1216
1217
0
  if (check_byxxx(_trp, _atp) != REC_MATCH)
1218
0
    return REC_NOMATCH;
1219
1220
0
  LM_DEV("5) check until\n");
1221
1222
0
  return (!_IS_SET(_trp->until) || _atp->time <= _trp->until) ?
1223
0
              REC_MATCH : REC_NOMATCH;
1224
0
}
1225
1226
1227
int check_freq_interval(tmrec_p _trp, ac_tm_p _atp)
1228
0
{
1229
0
  int _t0, _t1;
1230
0
  struct tm _tm;
1231
1232
0
  if(!_IS_SET(_trp->freq))
1233
0
    return REC_NOMATCH;
1234
1235
0
  LM_DEV("have freq\n");
1236
1237
0
  if(!_IS_SET(_trp->interval) || _trp->interval==1)
1238
0
    return REC_MATCH;
1239
1240
0
  LM_DEV("have interval (%d)\n", _trp->interval);
1241
1242
0
  switch(_trp->freq)
1243
0
  {
1244
0
    case FREQ_DAILY:
1245
0
    case FREQ_WEEKLY:
1246
0
      memset(&_tm, 0, sizeof(struct tm));
1247
0
      _tm.tm_year = _trp->ts.tm_year;
1248
0
      _tm.tm_mon = _trp->ts.tm_mon;
1249
0
      _tm.tm_mday = _trp->ts.tm_mday;
1250
0
      _t0 = (int)mktime(&_tm);
1251
0
      memset(&_tm, 0, sizeof(struct tm));
1252
0
      _tm.tm_year = _atp->t.tm_year;
1253
0
      _tm.tm_mon = _atp->t.tm_mon;
1254
0
      _tm.tm_mday = _atp->t.tm_mday;
1255
0
      _t1 = (int)mktime(&_tm);
1256
0
      if(_trp->freq == FREQ_DAILY)
1257
0
        return (((_t1-_t0)/(24*3600))%_trp->interval==0)?
1258
0
          REC_MATCH:REC_NOMATCH;
1259
#ifdef USE_YWEEK_U
1260
      _t0 -= _trp->ts.tm_wday*24*3600;
1261
      _t1 -= _atp->t.tm_wday*24*3600;
1262
#else
1263
0
      _t0 -= ((_trp->ts.tm_wday+6)%7)*24*3600;
1264
0
      _t1 -= ((_atp->t.tm_wday+6)%7)*24*3600;
1265
0
#endif
1266
0
      return (((_t1-_t0)/(7*24*3600))%_trp->interval==0)?
1267
0
          REC_MATCH:REC_NOMATCH;
1268
0
    case FREQ_MONTHLY:
1269
0
      _t0 = (_atp->t.tm_year-_trp->ts.tm_year)*12
1270
0
          + _atp->t.tm_mon-_trp->ts.tm_mon;
1271
0
      return (_t0%_trp->interval==0)?REC_MATCH:REC_NOMATCH;
1272
0
    case FREQ_YEARLY:
1273
0
      return ((_atp->t.tm_year-_trp->ts.tm_year)%_trp->interval==0)?
1274
0
          REC_MATCH:REC_NOMATCH;
1275
0
  }
1276
1277
0
  return REC_NOMATCH;
1278
0
}
1279
1280
static inline int get_min_interval(tmrec_p _trp)
1281
0
{
1282
0
  if(_trp->freq == FREQ_DAILY || _trp->byday || _trp->bymday || _trp->byyday)
1283
0
    return FREQ_DAILY;
1284
0
  if(_trp->freq == FREQ_WEEKLY || _trp->byweekno)
1285
0
    return FREQ_WEEKLY;
1286
0
  if(_trp->freq == FREQ_MONTHLY || _trp->bymonth)
1287
0
    return FREQ_MONTHLY;
1288
0
  if(_trp->freq == FREQ_YEARLY)
1289
0
    return FREQ_YEARLY;
1290
1291
0
  return FREQ_NOFREQ;
1292
0
}
1293
1294
1295
int check_recur_itv(struct tm *x, struct tm *bgn, struct tm *end,
1296
                    time_t dur, int freq)
1297
0
{
1298
0
  int d1, d2, dx;
1299
0
  long diff;
1300
1301
0
  switch (freq) {
1302
0
  case FREQ_YEARLY:
1303
0
    LM_DEV("YEARLY\n");
1304
0
    d1 = bgn->tm_yday;
1305
0
    d2 = end->tm_yday;
1306
0
    dx = x->tm_yday;
1307
0
    break;
1308
1309
0
  case FREQ_MONTHLY:
1310
0
    LM_DEV("MONTHLY\n");
1311
0
    d1 = bgn->tm_mday;
1312
0
    d2 = end->tm_mday;
1313
0
    dx = x->tm_mday;
1314
0
    break;
1315
1316
0
  case FREQ_WEEKLY:
1317
0
    LM_DEV("WEEKLY\n");
1318
0
    d1 = bgn->tm_wday;
1319
0
    d2 = end->tm_wday;
1320
0
    dx = x->tm_wday;
1321
0
    break;
1322
1323
0
  case FREQ_DAILY:
1324
0
  default:
1325
0
    LM_DEV("DAILY\n");
1326
0
    if (bgn->tm_mday == end->tm_mday) {
1327
0
      LM_DEV("DAILY-1\n");
1328
0
      diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1329
0
          (bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec);
1330
0
      if (diff < 0)
1331
0
        return REC_NOMATCH;
1332
1333
0
      diff = end->tm_hour*3600 + end->tm_min*60 + end->tm_sec -
1334
0
          (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1335
0
      if (diff <= 0)
1336
0
        return REC_NOMATCH;
1337
1338
0
      LM_DEV("MATCH\n");
1339
0
      return REC_MATCH;
1340
1341
0
    } else {
1342
0
      LM_DEV("DAILY-2\n");
1343
0
      diff = bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec -
1344
0
          (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1345
0
      if (diff <= 0)
1346
0
        return REC_MATCH;
1347
1348
0
      diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1349
0
          (end->tm_hour*3600 + end->tm_min*60 + end->tm_sec);
1350
0
      if (diff < 0)
1351
0
        return REC_MATCH;
1352
1353
0
      LM_DEV("NOMATCH\n");
1354
0
      return REC_NOMATCH;
1355
0
    }
1356
0
  }
1357
1358
0
  LM_DEV("check intervals\n");
1359
1360
  /* continuous interval (e.g. "M [ T W T F ] S S") */
1361
0
  if (d1 < d2) {
1362
0
    LM_DEV("CI-1\n");
1363
0
    if (dx < d1 || dx > d2)
1364
0
      return REC_NOMATCH;
1365
1366
0
    if (dx > d1 && dx < d2)
1367
0
      return REC_MATCH;
1368
1369
  /* overlapping interval (e.g. "1 2 ... 20 ] 21 ... 29 [ 30 31") */
1370
0
  } else if (d2 < d1) {
1371
0
    LM_DEV("CI-2\n");
1372
0
    if (dx > d2 && dx < d1)
1373
0
      return REC_NOMATCH;
1374
1375
0
    if (dx < d2 || dx > d1)
1376
0
      return REC_MATCH;
1377
1378
0
  } else if (dx != d1) {
1379
0
    LM_DEV("CI-3\n");
1380
0
    if (dur <= SEC_DAILY)
1381
0
      return REC_NOMATCH;
1382
0
    else
1383
0
      return REC_MATCH;
1384
1385
0
  } else {
1386
0
    LM_DEV("CI-4\n");
1387
0
    diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1388
0
        (bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec);
1389
0
    if (diff < 0)
1390
0
      return REC_NOMATCH;
1391
1392
0
    diff = end->tm_hour*3600 + end->tm_min*60 + end->tm_sec -
1393
0
        (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1394
0
    if (diff <= 0)
1395
0
      return REC_NOMATCH;
1396
1397
0
    return REC_MATCH;
1398
0
  }
1399
1400
0
  if (dx == d1)
1401
0
    diff = x->tm_hour*3600 + x->tm_min*60 + x->tm_sec -
1402
0
      (bgn->tm_hour*3600 + bgn->tm_min*60 + bgn->tm_sec);
1403
0
  else
1404
0
    diff = end->tm_hour*3600 + end->tm_min*60 + end->tm_sec -
1405
0
      (x->tm_hour*3600 + x->tm_min*60 + x->tm_sec);
1406
1407
0
  return diff > 0 ? REC_MATCH : REC_NOMATCH;
1408
0
}
1409
1410
1411
int check_min_unit(tmrec_p _trp, ac_tm_p _atp)
1412
0
{
1413
0
  int min_itv;
1414
0
  struct tm end;
1415
1416
0
  min_itv = get_min_interval(_trp);
1417
0
  LM_DEV("min_itv: %d\n", min_itv);
1418
1419
0
  switch (min_itv) {
1420
0
  case FREQ_DAILY:
1421
0
    if (_trp->duration >= SEC_DAILY)
1422
0
      return REC_MATCH;
1423
0
    break;
1424
1425
0
  case FREQ_WEEKLY:
1426
0
    if (_trp->duration >= SEC_WEEKLY)
1427
0
      return REC_MATCH;
1428
0
    break;
1429
1430
0
  case FREQ_MONTHLY:
1431
0
    if (_trp->duration >= SEC_MONTHLY_MAX)
1432
0
      return REC_MATCH;
1433
0
    break;
1434
1435
0
  case FREQ_YEARLY:
1436
0
    if (_trp->duration >= SEC_YEARLY_MAX)
1437
0
      return REC_MATCH;
1438
0
    break;
1439
1440
0
  default:
1441
0
    return REC_NOMATCH;
1442
0
  }
1443
1444
0
  LM_DEV("check recur...\n");
1445
1446
0
  localtime_r(&_trp->dtend, &end);
1447
0
  return check_recur_itv(&_atp->t, &_trp->ts, &end, _trp->duration, min_itv);
1448
0
}
1449
1450
int check_byxxx(tmrec_p _trp, ac_tm_p _atp)
1451
0
{
1452
0
  int i;
1453
0
  ac_maxval_p _amp;
1454
1455
0
  if (!(_trp->flags & TR_BYXXX))
1456
0
    return REC_MATCH;
1457
1458
0
  _amp = ac_get_maxval(_atp);
1459
1460
0
  if(_trp->bymonth)
1461
0
  {
1462
0
    for(i=0; i<_trp->bymonth->nr; i++)
1463
0
    {
1464
0
      if(_atp->t.tm_mon ==
1465
0
          ((_trp->bymonth->xxx[i] - 1)*_trp->bymonth->req[i]+12)%12)
1466
0
        break;
1467
0
    }
1468
0
    if(i>=_trp->bymonth->nr)
1469
0
      return REC_NOMATCH;
1470
0
  }
1471
0
  if(_trp->freq==FREQ_YEARLY && _trp->byweekno)
1472
0
  {
1473
0
    for(i=0; i<_trp->byweekno->nr; i++)
1474
0
    {
1475
0
      if(_atp->yweek == ((_trp->byweekno->xxx[i] - 1) *_trp->byweekno->req[i]+
1476
0
              _amp->yweek)%_amp->yweek)
1477
0
        break;
1478
0
    }
1479
0
    if(i>=_trp->byweekno->nr)
1480
0
      return REC_NOMATCH;
1481
0
  }
1482
0
  if(_trp->byyday)
1483
0
  {
1484
0
    for(i=0; i<_trp->byyday->nr; i++)
1485
0
    {
1486
0
      if(_atp->t.tm_yday == ((_trp->byyday->xxx[i] - 1)*_trp->byyday->req[i]+
1487
0
            _amp->yday)%_amp->yday)
1488
0
        break;
1489
0
    }
1490
0
    if(i>=_trp->byyday->nr)
1491
0
      return REC_NOMATCH;
1492
0
  }
1493
0
  if(_trp->bymday)
1494
0
  {
1495
0
    for(i=0; i<_trp->bymday->nr; i++)
1496
0
    {
1497
#ifdef EXTRA_DEBUG
1498
      LM_DBG("%d == %d\n", _atp->t.tm_mday,
1499
        (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
1500
        _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0));
1501
#endif
1502
0
      if(_atp->t.tm_mday == (_trp->bymday->xxx[i]*_trp->bymday->req[i]+
1503
0
            _amp->mday)%_amp->mday + ((_trp->bymday->req[i]<0)?1:0))
1504
0
        break;
1505
0
    }
1506
0
    if(i>=_trp->bymday->nr)
1507
0
      return REC_NOMATCH;
1508
0
  }
1509
0
  if(_trp->byday)
1510
0
  {
1511
0
    for(i=0; i<_trp->byday->nr; i++)
1512
0
    {
1513
0
      if(_trp->freq==FREQ_YEARLY)
1514
0
      {
1515
#ifdef EXTRA_DEBUG
1516
        LM_DBG("%d==%d && %d==%d\n", _atp->t.tm_wday,
1517
          _trp->byday->xxx[i], _atp->ywday,
1518
          (_trp->byday->req[i]+_amp->ywday)%_amp->ywday);
1519
#endif
1520
0
        if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
1521
0
            _atp->ywday == (_trp->byday->req[i]+_amp->ywday)%
1522
0
            _amp->ywday)
1523
0
          break;
1524
0
      }
1525
0
      else
1526
0
      {
1527
0
        if(_trp->freq==FREQ_MONTHLY)
1528
0
        {
1529
#ifdef EXTRA_DEBUG
1530
          LM_DBG("%d==%d && %d==%d [%d]\n", _atp->t.tm_wday,
1531
            _trp->byday->xxx[i], _atp->wom,
1532
            (_trp->byday->req[i]+_amp->mwday)%_amp->mwday,
1533
            _amp->mwday);
1534
#endif
1535
0
          if(_atp->t.tm_wday == _trp->byday->xxx[i] &&
1536
0
              _atp->wom==(_trp->byday->req[i]+
1537
0
              _amp->mwday)%_amp->mwday)
1538
0
            break;
1539
0
        }
1540
0
        else
1541
0
        {
1542
0
          if(_atp->t.tm_wday == _trp->byday->xxx[i])
1543
0
            break;
1544
0
        }
1545
0
      }
1546
0
    }
1547
0
    if(i>=_trp->byday->nr)
1548
0
      return REC_NOMATCH;
1549
0
  }
1550
1551
0
  return REC_MATCH;
1552
0
}
1553
1554
1555
static inline int _tmrec_parse(const char *tr, tmrec_t *time_rec)
1556
0
{
1557
0
  char *p, *s;
1558
0
  osips_free_t free_f = (time_rec->flags & PKG_ALLOC ?
1559
0
                             osips_pkg_free : osips_shm_free);
1560
1561
  /* empty definition? */
1562
0
  if (!tr || *tr == '\0')
1563
0
    return 0;
1564
1565
0
  p = (char *)tr;
1566
1567
0
  load_TR_value(p, s, time_rec, tr_parse_tz, parse_error, done);
1568
1569
  /* Important: make sure to set the tz NOW, before more parsing... */
1570
0
  if (time_rec->tz)
1571
0
    _tz_set(time_rec->tz);
1572
1573
0
  load_TR_value(p, s, time_rec, tr_parse_dtstart, parse_error, done);
1574
0
  load_TR_value(p, s, time_rec, tr_parse_dtend, parse_error, done);
1575
0
  load_TR_value(p, s, time_rec, tr_parse_duration, parse_error, done);
1576
0
  load_TR_value(p, s, time_rec, tr_parse_freq, parse_error, done);
1577
0
  load_TR_value(p, s, time_rec, tr_parse_until, parse_error, done);
1578
0
  load_TR_value(p, s, time_rec, tr_parse_interval, parse_error, done);
1579
0
  load_TR_value(p, s, time_rec, tr_parse_byday, parse_error, done);
1580
0
  load_TR_value(p, s, time_rec, tr_parse_bymday, parse_error, done);
1581
0
  load_TR_value(p, s, time_rec, tr_parse_byyday, parse_error, done);
1582
0
  load_TR_value(p, s, time_rec, tr_parse_byweekno, parse_error, done);
1583
0
  load_TR_value(p, s, time_rec, tr_parse_bymonth, parse_error, done);
1584
1585
0
done:
1586
0
  if (time_rec->tz)
1587
0
    tz_reset();
1588
1589
0
  if (!_IS_SET(time_rec->dtstart))
1590
0
    time_rec->dtstart = 0; /* invalid; auto-fix to 19700101T000000 */
1591
1592
0
  if (!_IS_SET(time_rec->duration)) {
1593
0
    if (!_IS_SET(time_rec->dtend)) {
1594
      /* invalid; since we don't support mementos, we need a duration */
1595
0
      switch (get_min_interval(time_rec)) {
1596
0
      case FREQ_DAILY:
1597
0
        time_rec->dtend = time_rec->dtstart + SEC_DAILY -
1598
0
            (time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1599
0
             + time_rec->ts.tm_sec); /* until the end of the day */
1600
0
        break;
1601
0
      case FREQ_WEEKLY:
1602
0
        time_rec->dtend = time_rec->dtstart + SEC_WEEKLY -
1603
0
            (time_rec->ts.tm_wday * SEC_DAILY
1604
0
             + time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1605
0
             + time_rec->ts.tm_sec); /* until the end of the week */
1606
0
        break;
1607
0
      case FREQ_MONTHLY:
1608
0
        time_rec->dtend = time_rec->dtstart + SEC_MONTHLY_MAX -
1609
0
            (time_rec->ts.tm_mday * SEC_DAILY
1610
0
             + time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1611
0
             + time_rec->ts.tm_sec); /* until the end of the month */
1612
0
        break;
1613
0
      case FREQ_YEARLY:
1614
0
        time_rec->dtend = time_rec->dtstart + SEC_YEARLY_MAX -
1615
0
            (time_rec->ts.tm_yday * SEC_DAILY
1616
0
             + time_rec->ts.tm_hour * 3600 + time_rec->ts.tm_min * 60
1617
0
             + time_rec->ts.tm_sec); /* until the end of the year */
1618
0
        break;
1619
0
      default:
1620
0
        time_rec->dtend = 4294967295UL; /* auto-fix to +136 years */
1621
0
      }
1622
0
    }
1623
1624
0
    time_rec->duration = time_rec->dtend - time_rec->dtstart;
1625
0
  } else {
1626
0
    time_rec->dtend = time_rec->dtstart + time_rec->duration;
1627
0
  }
1628
1629
0
  if (!_IS_SET(time_rec->freq) &&
1630
0
          (_IS_SET(time_rec->until) || _IS_SET(time_rec->interval)
1631
0
           || time_rec->flags & TR_BYXXX)) {
1632
0
    LM_ERR("missing FREQ component in time rec: '%s'\n", tr);
1633
0
    return -1;
1634
0
  }
1635
1636
0
  return 0;
1637
1638
0
parse_error:
1639
0
  LM_ERR("parse error in <%s> around position %i\n",
1640
0
         tr, (int)(long)(p-tr));
1641
0
  if (time_rec->tz) {
1642
0
    free_f(time_rec->tz);
1643
0
    tz_reset();
1644
0
    time_rec->tz = NULL;
1645
0
  }
1646
1647
0
  return -1;
1648
0
}
1649
1650
1651
tmrec *tmrec_parse(const char *tr, char alloc)
1652
0
{
1653
0
  tmrec_p time_rec;
1654
1655
0
  time_rec = tmrec_new(alloc);
1656
0
  if (!time_rec) {
1657
0
    LM_ERR("oom\n");
1658
0
    return NULL;
1659
0
  }
1660
1661
0
  if (_tmrec_parse(tr, time_rec) < 0) {
1662
0
    tmrec_free(time_rec);
1663
0
    return NULL;
1664
0
  }
1665
1666
0
  return (tmrec *)time_rec;
1667
0
}
1668
1669
1670
int _tmrec_check(const tmrec *_tr, time_t time)
1671
0
{
1672
0
  tmrec_t *tr = (tmrec_p)_tr;
1673
0
  ac_tm_t att;
1674
0
  int rc;
1675
1676
  /* shortcut: if there is no dstart, timerec is valid */
1677
0
  if (!_IS_SET(tr->dtstart))
1678
0
    return 1;
1679
1680
0
  if (tr->tz)
1681
0
    _tz_set(tr->tz);
1682
1683
  /* set current time */
1684
0
  ac_tm_set_time(&att, time);
1685
1686
  /* does the recv_time match the specified interval?  */
1687
0
  rc = check_tmrec(tr, &att);
1688
1689
0
  if (tr->tz)
1690
0
    tz_reset();
1691
1692
0
  return rc == 0;
1693
0
}
1694
1695
1696
int _tmrec_check_str(const char *tr, time_t check_time)
1697
0
{
1698
0
  tmrec_t time_rec;
1699
0
  int rc;
1700
1701
0
  LM_DBG("checking: '%s'\n", tr);
1702
0
  tmrec_init(&time_rec);
1703
0
  time_rec.flags = PKG_ALLOC;
1704
1705
0
  if (_tmrec_parse(tr, &time_rec) < 0) {
1706
0
    LM_ERR("failed to parse time rec\n");
1707
0
    return -2;
1708
0
  }
1709
1710
0
  rc = _tmrec_check(&time_rec, check_time) ? 1 : -1;
1711
1712
0
  if (time_rec.tz)
1713
0
    pkg_free(time_rec.tz);
1714
1715
0
  return rc;
1716
0
}
1717
1718
1719
int _tmrec_expr_check_str(const char *trx, time_t check_time)
1720
0
{
1721
0
  char *p, *q, bkp, tmp = 77, need_close, invert_next = 0, op = 0;
1722
0
  str aux;
1723
0
  enum {
1724
0
    NEED_OPERAND,
1725
0
    NEED_OPERATOR,
1726
0
  } state = NEED_OPERAND;
1727
1728
0
  int rc = 0, _rc;
1729
0
  char is_valid;
1730
1731
0
  LM_DBG("checking: %s\n", trx);
1732
1733
  /* NULL input -> nothing to match against -> no match! */
1734
0
  if (!trx)
1735
0
    return -1;
1736
1737
0
  for (p = (char *)trx; *p != '\0'; p++) {
1738
0
    if (is_ws(*p))
1739
0
      continue;
1740
1741
0
    switch (state) {
1742
0
    case NEED_OPERAND:
1743
0
      switch (*p) {
1744
0
      case ')':
1745
0
      case '&':
1746
0
      case '/':
1747
0
        LM_ERR("failed to parse time rec (unexpected '%c')\n", *p);
1748
0
        goto parse_err;
1749
1750
0
      case '!':
1751
0
        invert_next = !invert_next;
1752
0
        continue;
1753
1754
0
      case '(':
1755
0
        for (need_close = 1, q = p + 1; *q != '\0'; q++) {
1756
0
          switch (*q) {
1757
0
          case '(':
1758
0
            need_close++;
1759
0
            break;
1760
1761
0
          case ')':
1762
0
            need_close--;
1763
0
            break;
1764
1765
0
          default:
1766
0
            continue;
1767
0
          }
1768
1769
0
          if (!need_close)
1770
0
            break;
1771
0
        }
1772
1773
0
        if (need_close) {
1774
0
          LM_ERR("failed to parse time rec (bad parentheses)\n");
1775
0
          goto parse_err;
1776
0
        }
1777
1778
0
        aux.s = p + 1;
1779
0
        aux.len = q - aux.s;
1780
0
        trim(&aux);
1781
1782
0
        bkp = aux.s[aux.len];
1783
0
        aux.s[aux.len] = '\0';
1784
0
        _rc = _tmrec_expr_check_str(aux.s, check_time) + 1;
1785
0
        aux.s[aux.len] = bkp;
1786
1787
0
        if (_rc < 0)
1788
0
          goto parse_err;
1789
1790
0
        p = q;
1791
1792
0
        if (invert_next) {
1793
0
          invert_next = 0;
1794
0
          _rc = (_rc + 2) % 4;
1795
0
        }
1796
1797
0
        if (op == 1)
1798
0
          rc &= _rc;
1799
0
        else if (op == 2)
1800
0
          rc |= _rc;
1801
0
        else
1802
0
          rc = tmp = _rc;
1803
1804
0
        state = NEED_OPERATOR;
1805
0
        break;
1806
1807
0
      default:
1808
0
        for (is_valid = 0, q = p + 1; *q != '\0'; q++) {
1809
0
          if (*q == '!' || *q == '(' || *q == ')') {
1810
0
            LM_ERR("failed to parse multi time rec at '%c' "
1811
0
                   "(unexpected character)\n", *q);
1812
0
            goto parse_err;
1813
0
          } else if (*q == TR_SEPARATOR) {
1814
0
            is_valid = 1;
1815
0
          }
1816
1817
0
          if (is_ws(*q)) {
1818
0
            state = NEED_OPERATOR;
1819
0
            break;
1820
0
          } else if (*q == '&' || (*q == '/' && is_valid)) {
1821
0
            break;
1822
0
          }
1823
0
        }
1824
1825
0
        aux.s = p;
1826
0
        aux.len = q - aux.s;
1827
0
        trim(&aux);
1828
1829
0
        bkp = aux.s[aux.len];
1830
0
        aux.s[aux.len] = '\0';
1831
0
        _rc = _tmrec_check_str(aux.s, check_time) + 1;
1832
0
        aux.s[aux.len] = bkp;
1833
1834
0
        if (_rc < 0) {
1835
0
          LM_ERR("failed to parse single time rec: '%.*s'\n",
1836
0
                 aux.len, aux.s);
1837
0
          return _rc - 1;
1838
0
        }
1839
1840
0
        if (invert_next) {
1841
0
          invert_next = 0;
1842
0
          _rc = (_rc + 2) % 4;
1843
0
        }
1844
1845
0
        if (*q == '&') {
1846
0
          if (op == 2) {
1847
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
1848
0
                   "type is allowed within an expression)\n");
1849
0
            goto parse_err;
1850
0
          }
1851
1852
0
          if (op == 0)
1853
0
            rc = _rc;
1854
0
          op = 1;
1855
0
        } else if (*q == '/') {
1856
0
          if (op == 1) {
1857
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
1858
0
                   "type is allowed within an expression)\n");
1859
0
            goto parse_err;
1860
0
          }
1861
1862
0
          op = 2;
1863
0
        }
1864
1865
0
        if (op == 1)
1866
0
          rc &= _rc;
1867
0
        else if (op == 2)
1868
0
          rc |= _rc;
1869
0
        else
1870
0
          rc = tmp = _rc;
1871
1872
0
        if (*q == '\0')
1873
0
          return rc - 1;
1874
1875
0
        p = q;
1876
0
      }
1877
0
      break;
1878
1879
0
    case NEED_OPERATOR:
1880
0
      switch (*p) {
1881
0
        case '&':
1882
0
          if (op == 2) {
1883
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
1884
0
                   "type is allowed within an expression)\n");
1885
0
            goto parse_err;
1886
0
          } else if (op == 0) {
1887
0
            rc = tmp;
1888
0
          }
1889
1890
0
          op = 1;
1891
0
          state = NEED_OPERAND;
1892
0
          break;
1893
1894
0
        case '/':
1895
0
          if (op == 1) {
1896
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
1897
0
                   "type is allowed within an expression)\n");
1898
0
            goto parse_err;
1899
0
          }
1900
1901
0
          op = 2;
1902
0
          state = NEED_OPERAND;
1903
0
          break;
1904
1905
0
        default:
1906
0
          LM_ERR("failed to parse the rec string (bad char: '%c', "
1907
0
                 "expected operator)\n", *p);
1908
0
          goto parse_err;
1909
0
      }
1910
0
    }
1911
0
  }
1912
1913
0
  if (state == NEED_OPERAND && op != 0) {
1914
0
    LM_ERR("failed to parse the rec string (missing operand)\n");
1915
0
    LM_ERR("input given: '%s'\n", trx);
1916
0
    return -2;
1917
0
  }
1918
1919
0
  if (invert_next)
1920
0
    return (rc + 2) % 4 - 1;
1921
0
  else
1922
0
    return rc - 1;
1923
1924
0
  return rc;
1925
1926
0
parse_err:
1927
0
  LM_ERR("input given: '%s'\n", trx);
1928
0
  return -2;
1929
0
}
1930
1931
1932
tmrec_expr *tmrec_expr_parse(const char *trx, char alloc_type)
1933
0
{
1934
0
  enum {
1935
0
    NEED_OPERAND,
1936
0
    NEED_OPERATOR,
1937
0
  } state = NEED_OPERAND;
1938
1939
0
  tmrec_expr_t *exp, *e;
1940
0
  osips_malloc_t malloc_f;
1941
0
  char *p, *q, bkp, need_close, invert_next = 0, is_valid;
1942
0
  str aux;
1943
0
  int rc;
1944
1945
0
  LM_DBG("checking: %s\n", trx);
1946
1947
0
  if (!trx)
1948
0
    return NULL;
1949
1950
0
  malloc_f = (alloc_type & PKG_ALLOC ?
1951
0
                  osips_pkg_malloc : osips_shm_malloc);
1952
0
  exp = malloc_f(sizeof *exp);
1953
0
  if (!exp) {
1954
0
    LM_ERR("oom\n");
1955
0
    return NULL;
1956
0
  }
1957
1958
0
  tmrec_expr_init(exp);
1959
0
  exp->flags = alloc_type;
1960
1961
0
  for (p = (char *)trx; *p != '\0'; p++) {
1962
0
    if (is_ws(*p))
1963
0
      continue;
1964
1965
0
    switch (state) {
1966
0
    case NEED_OPERAND:
1967
0
      switch (*p) {
1968
0
      case ')':
1969
0
      case '&':
1970
0
      case '/':
1971
0
        LM_ERR("failed to parse time rec (unexpected '%c')\n", *p);
1972
0
        goto parse_err;
1973
1974
0
      case '!':
1975
0
        invert_next = !invert_next;
1976
0
        continue;
1977
1978
0
      case '(':
1979
0
        for (need_close = 1, q = p + 1; *q != '\0'; q++) {
1980
0
          switch (*q) {
1981
0
          case '(':
1982
0
            need_close++;
1983
0
            break;
1984
1985
0
          case ')':
1986
0
            need_close--;
1987
0
            break;
1988
1989
0
          default:
1990
0
            continue;
1991
0
          }
1992
1993
0
          if (!need_close)
1994
0
            break;
1995
0
        }
1996
1997
0
        if (need_close) {
1998
0
          LM_ERR("failed to parse time rec (bad parentheses)\n");
1999
0
          goto parse_err;
2000
0
        }
2001
2002
0
        aux.s = p + 1;
2003
0
        aux.len = q - aux.s;
2004
0
        trim(&aux);
2005
2006
0
        bkp = aux.s[aux.len];
2007
0
        aux.s[aux.len] = '\0';
2008
0
        e = tmrec_expr_parse(aux.s, alloc_type);
2009
0
        aux.s[aux.len] = bkp;
2010
2011
0
        if (!e)
2012
0
          goto parse_err;
2013
2014
0
        if (invert_next) {
2015
0
          invert_next = 0;
2016
0
          e->inverted = 1;
2017
0
        }
2018
2019
0
        list_add_tail(&e->list, &exp->operands);
2020
0
        p = q;
2021
0
        state = NEED_OPERATOR;
2022
0
        break;
2023
2024
0
      default:
2025
0
        for (is_valid = 0, q = p + 1; *q != '\0'; q++) {
2026
0
          if (*q == '!' || *q == '(' || *q == ')') {
2027
0
            LM_ERR("failed to parse multi time rec at '%c' "
2028
0
                   "(unexpected character)\n", *q);
2029
0
            goto parse_err;
2030
0
          } else if (*q == TR_SEPARATOR) {
2031
0
            is_valid = 1;
2032
0
          }
2033
2034
0
          if (is_ws(*q)) {
2035
0
            state = NEED_OPERATOR;
2036
0
            break;
2037
0
          } else if (*q == '&' || (*q == '/' && is_valid)) {
2038
0
            break;
2039
0
          }
2040
0
        }
2041
2042
0
        e = malloc_f(sizeof *e);
2043
0
        if (!e) {
2044
0
          LM_ERR("oom\n");
2045
0
          goto parse_err;
2046
0
        }
2047
2048
0
        tmrec_expr_init(e);
2049
0
        e->flags = e->tr.flags = alloc_type;
2050
0
        e->is_leaf = 1;
2051
2052
0
        list_add_tail(&e->list, &exp->operands);
2053
2054
0
        aux.s = p;
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
        rc = _tmrec_parse(aux.s, &e->tr);
2061
0
        aux.s[aux.len] = bkp;
2062
2063
0
        if (rc < 0)
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
        if (*q == '&') {
2072
0
          if (exp->op == TR_OP_OR) {
2073
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
2074
0
                   "type is allowed within an expression)\n");
2075
0
            goto parse_err;
2076
0
          }
2077
2078
0
          exp->op = TR_OP_AND;
2079
0
        } else if (*q == '/') {
2080
0
          if (exp->op == TR_OP_AND) {
2081
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
2082
0
                   "type is allowed within an expression)\n");
2083
0
            goto parse_err;
2084
0
          }
2085
2086
0
          exp->op = TR_OP_OR;
2087
0
        }
2088
2089
0
        if (*q == '\0')
2090
0
          return exp;
2091
2092
0
        p = q;
2093
0
      }
2094
0
      break;
2095
2096
0
    case NEED_OPERATOR:
2097
0
      switch (*p) {
2098
0
        case '&':
2099
0
          if (exp->op == TR_OP_OR) {
2100
0
            LM_ERR("failed to parse rec at '&' (only 1 operator "
2101
0
                   "type is allowed within an expression)\n");
2102
0
            goto parse_err;
2103
0
          }
2104
2105
0
          exp->op = TR_OP_AND;
2106
0
          state = NEED_OPERAND;
2107
0
          break;
2108
2109
0
        case '/':
2110
0
          if (exp->op == TR_OP_AND) {
2111
0
            LM_ERR("failed to parse rec at '/' (only 1 operator "
2112
0
                   "type is allowed within an expression)\n");
2113
0
            goto parse_err;
2114
0
          }
2115
2116
0
          exp->op = TR_OP_OR;
2117
0
          state = NEED_OPERAND;
2118
0
          break;
2119
2120
0
        default:
2121
0
          LM_ERR("failed to parse the rec string (bad char: '%c', "
2122
0
                 "expected operator)\n", *p);
2123
0
          goto parse_err;
2124
0
      }
2125
0
    }
2126
0
  }
2127
2128
0
  if (state == NEED_OPERAND && exp->op != TR_OP_NUL) {
2129
0
    LM_ERR("failed to parse the rec string (missing operand)\n");
2130
0
    goto parse_err;
2131
0
  }
2132
2133
0
  if (invert_next)
2134
0
    exp->inverted = 1;
2135
2136
0
  return exp;
2137
2138
0
parse_err:
2139
0
  tmrec_expr_free(exp);
2140
0
  LM_ERR("parsing failed, input given: '%s'\n", trx);
2141
0
  return NULL;
2142
0
}
2143
2144
2145
int _tmrec_expr_check(const tmrec_expr *_trx, time_t check_time)
2146
0
{
2147
0
  struct list_head *el;
2148
0
  const tmrec_expr_t *exp, *trx = (const tmrec_expr_t *)_trx;
2149
0
  int rc = 0;
2150
2151
0
  if (!trx)
2152
0
    return -1;
2153
2154
0
  if (trx->is_leaf) {
2155
0
    rc = _tmrec_check(&trx->tr, check_time) ? 2 : 0;
2156
0
    goto out;
2157
0
  }
2158
2159
0
  if (list_empty(&trx->operands))
2160
0
    goto out;
2161
2162
0
  rc = (trx->op == TR_OP_AND ? 2 : 0);
2163
2164
0
  list_for_each (el, &trx->operands) {
2165
0
    exp = list_entry(el, tmrec_expr_t, list);
2166
2167
0
    if (trx->op == TR_OP_AND)
2168
0
      rc = rc & (_tmrec_expr_check(exp, check_time) + 1);
2169
0
    else
2170
0
      rc = rc | (_tmrec_expr_check(exp, check_time) + 1);
2171
0
  }
2172
2173
0
out:
2174
0
  if (trx->inverted) {
2175
0
    LM_DEV("result: %d\n", (rc + 2) % 4 - 1);
2176
0
    return (rc + 2) % 4 - 1;
2177
0
  }
2178
2179
0
  LM_DEV("result: %d\n", rc - 1);
2180
0
  return rc - 1;
2181
0
}
2182
2183
2184
void tmrec_expr_free(tmrec_expr *_trx)
2185
0
{
2186
0
  struct list_head *el, *next;
2187
0
  tmrec_expr_t *exp, *trx = (tmrec_expr_t *)_trx;
2188
0
  osips_free_t free_f;
2189
2190
0
  if (!trx)
2191
0
    return;
2192
2193
0
  free_f = (trx->flags & PKG_ALLOC ?
2194
0
                osips_pkg_free : osips_shm_free);
2195
2196
0
  list_for_each_safe (el, next, &trx->operands) {
2197
0
    exp = list_entry(el, tmrec_expr_t, list);
2198
0
    tmrec_expr_free(exp);
2199
0
  }
2200
2201
0
  free_f(trx->tr.tz);
2202
0
  free_f(trx);
2203
0
}
2204
2205
2206
int _tz_offset(const char *tz, time_t t)
2207
0
{
2208
0
  struct tm lt = {0};
2209
2210
0
  _tz_set(tz);
2211
0
  localtime_r(&t, &lt);
2212
0
  tz_reset();
2213
2214
0
  return lt.tm_gmtoff;
2215
0
}