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, <ime); |
283 | 0 | ac_tm_fill(_atp, <ime); |
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, <); |
2212 | 0 | tz_reset(); |
2213 | |
|
2214 | 0 | return lt.tm_gmtoff; |
2215 | 0 | } |