Coverage Report

Created: 2025-06-13 06:29

/src/MapServer/src/maptime.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * $id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  Date/Time utility functions.
6
 * Author:   Steve Lime and the MapServer team.
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies of this Software or works derived from this Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 ****************************************************************************/
29
30
#define _GNU_SOURCE /* glibc2 needs this for strptime() */
31
#include <stdlib.h>
32
#include <stdio.h>
33
#include <time.h>
34
35
#include "mapserver.h"
36
#include "maptime.h"
37
#include "maperror.h"
38
#include "mapthread.h"
39
40
typedef struct {
41
  char pattern[64];
42
  ms_regex_t *regex;
43
  char format[32];
44
  char userformat[32];
45
  MS_TIME_RESOLUTION resolution;
46
} timeFormatObj;
47
48
static timeFormatObj ms_timeFormats[] = {
49
    {"^[0-9]{8}", NULL, "%Y%m%d", "YYYYMMDD", TIME_RESOLUTION_DAY},
50
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", NULL,
51
     "%Y-%m-%dT%H:%M:%SZ", "YYYY-MM-DDTHH:MM:SSZ", TIME_RESOLUTION_SECOND},
52
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}", NULL,
53
     "%Y-%m-%dT%H:%M:%S", "YYYY-MM-DDTHH:MM:SS", TIME_RESOLUTION_SECOND},
54
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}", NULL,
55
     "%Y-%m-%d %H:%M:%S", "YYYY-MM-DD HH:MM:SS", TIME_RESOLUTION_SECOND},
56
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}", NULL, "%Y-%m-%dT%H:%M",
57
     "YYYY-MM-DDTHH:MM", TIME_RESOLUTION_MINUTE},
58
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}", NULL, "%Y-%m-%d %H:%M",
59
     "YYYY-MM-DD HH:MM", TIME_RESOLUTION_MINUTE},
60
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}", NULL, "%Y-%m-%dT%H",
61
     "YYYY-MM-DDTHH", TIME_RESOLUTION_HOUR},
62
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}", NULL, "%Y-%m-%d %H",
63
     "YYYY-MM-DD HH", TIME_RESOLUTION_HOUR},
64
    {"^[0-9]{4}-[0-9]{2}-[0-9]{2}", NULL, "%Y-%m-%d", "YYYY-MM-DD",
65
     TIME_RESOLUTION_DAY},
66
    {"^[0-9]{4}-[0-9]{2}", NULL, "%Y-%m", "YYYY-MM", TIME_RESOLUTION_MONTH},
67
    {"^[0-9]{4}", NULL, "%Y", "YYYY", TIME_RESOLUTION_YEAR},
68
    {"^T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", NULL, "T%H:%M:%SZ", "THH:MM:SSZ",
69
     TIME_RESOLUTION_SECOND},
70
    {"^T[0-9]{2}:[0-9]{2}:[0-9]{2}", NULL, "T%H:%M:%S", "THH:MM:SS",
71
     TIME_RESOLUTION_SECOND},
72
    {"^[0-9]{2}:[0-9]{2}:[0-9]{2}Z", NULL, "%H:%M:%SZ", "HH:MM:SSZ",
73
     TIME_RESOLUTION_SECOND},
74
    {"^[0-9]{2}:[0-9]{2}:[0-9]{2}", NULL, "%H:%M:%S", "HH:MM:SS",
75
     TIME_RESOLUTION_SECOND}};
76
77
#define MS_NUMTIMEFORMATS                                                      \
78
0
  (int)(sizeof(ms_timeFormats) / sizeof(ms_timeFormats[0]))
79
80
int *ms_limited_pattern = NULL;
81
int ms_num_limited_pattern;
82
83
int ms_time_inited = 0;
84
0
int msTimeSetup() {
85
0
  if (!ms_time_inited) {
86
0
    msAcquireLock(TLOCK_TIME);
87
0
    if (!ms_time_inited) {
88
0
      int i;
89
0
      for (i = 0; i < MS_NUMTIMEFORMATS; i++) {
90
0
        ms_timeFormats[i].regex = msSmallMalloc(sizeof(ms_regex_t));
91
0
        if (0 != ms_regcomp(ms_timeFormats[i].regex, ms_timeFormats[i].pattern,
92
0
                            MS_REG_EXTENDED | MS_REG_NOSUB)) {
93
0
          msSetError(MS_REGEXERR, "Failed to compile expression (%s).",
94
0
                     "msTimeSetup()", ms_timeFormats[i].pattern);
95
0
          return MS_FAILURE;
96
          /* TODO: free already init'd regexes */
97
0
        }
98
0
      }
99
0
      ms_limited_pattern =
100
0
          (int *)msSmallMalloc(sizeof(int) * MS_NUMTIMEFORMATS);
101
0
      ms_num_limited_pattern = 0;
102
0
      ms_time_inited = 1;
103
0
    }
104
0
    msReleaseLock(TLOCK_TIME);
105
0
  }
106
0
  return MS_SUCCESS;
107
0
}
108
109
0
void msTimeCleanup() {
110
0
  if (ms_time_inited) {
111
0
    int i;
112
0
    for (i = 0; i < MS_NUMTIMEFORMATS; i++) {
113
0
      if (ms_timeFormats[i].regex) {
114
0
        ms_regfree(ms_timeFormats[i].regex);
115
0
        msFree(ms_timeFormats[i].regex);
116
0
        ms_timeFormats[i].regex = NULL;
117
0
      }
118
0
    }
119
0
    msFree(ms_limited_pattern);
120
0
    ms_time_inited = 0;
121
0
  }
122
0
}
123
124
0
void msTimeInit(struct tm *time) {
125
0
  time->tm_sec = 0; /* set all members to zero */
126
0
  time->tm_min = 0;
127
0
  time->tm_hour = 0;
128
0
  time->tm_mday = 0;
129
0
  time->tm_mon = 0;
130
0
  time->tm_year = 0;
131
0
  time->tm_wday = 0;
132
0
  time->tm_yday = 0;
133
0
  time->tm_isdst = 0;
134
135
0
  return;
136
0
}
137
138
0
static int compareIntVals(int a, int b) {
139
0
  if (a < b)
140
0
    return -1;
141
0
  else if (a > b)
142
0
    return 1;
143
0
  else
144
0
    return 0;
145
0
}
146
147
0
int msDateCompare(struct tm *time1, struct tm *time2) {
148
0
  int result;
149
150
0
  if ((result = compareIntVals(time1->tm_year, time2->tm_year)) != 0)
151
0
    return result; /* not equal based on year */
152
0
  else if ((result = compareIntVals(time1->tm_mon, time2->tm_mon)) != 0)
153
0
    return result; /* not equal based on month */
154
0
  else if ((result = compareIntVals(time1->tm_mday, time2->tm_mday)) != 0)
155
0
    return result; /* not equal based on day of month */
156
157
0
  return (0); /* must be equal */
158
0
}
159
160
0
int msTimeCompare(struct tm *time1, struct tm *time2) {
161
0
  int result;
162
163
  // fprintf(stderr, "in msTimeCompare()...\n");
164
  // fprintf(stderr, "time1: %d %d %d %d %d %d\n", time1->tm_year,
165
  // time1->tm_mon, time1->tm_mday, time1->tm_hour, time1->tm_min,
166
  // time1->tm_sec); fprintf(stderr, "time2: %d %d %d %d %d %d\n",
167
  // time2->tm_year, time2->tm_mon, time2->tm_mday, time2->tm_hour,
168
  // time2->tm_min, time2->tm_sec);
169
170
0
  if ((result = compareIntVals(time1->tm_year, time2->tm_year)) != 0)
171
0
    return result; /* not equal based on year */
172
0
  else if ((result = compareIntVals(time1->tm_mon, time2->tm_mon)) != 0)
173
0
    return result; /* not equal based on month */
174
0
  else if ((result = compareIntVals(time1->tm_mday, time2->tm_mday)) != 0)
175
0
    return result; /* not equal based on day of month */
176
0
  else if ((result = compareIntVals(time1->tm_hour, time2->tm_hour)) != 0)
177
0
    return result; /* not equal based on hour */
178
0
  else if ((result = compareIntVals(time1->tm_min, time2->tm_min)) != 0)
179
0
    return result; /* not equal based on minute */
180
0
  else if ((result = compareIntVals(time1->tm_sec, time2->tm_sec)) != 0)
181
0
    return result; /* not equal based on second */
182
183
0
  return (0); /* must be equal */
184
0
}
185
186
#if defined(_WIN32) && !defined(__CYGWIN__)
187
#include <sys/timeb.h>
188
void msGettimeofday(struct mstimeval *tp, void *tzp) {
189
  struct _timeb theTime;
190
191
  _ftime(&theTime);
192
  tp->tv_sec = theTime.time;
193
  tp->tv_usec = theTime.millitm * 1000;
194
}
195
#endif
196
197
#if defined(_WIN32) && !defined(__CYGWIN__)
198
/* we need to provide our own prototype on windows. */
199
char *strptime(const char *buf, const char *format, struct tm *timeptr);
200
#endif
201
202
0
char *msStrptime(const char *s, const char *format, struct tm *tm) {
203
0
  memset(tm, 0, sizeof(struct tm));
204
0
  return strptime(s, format, tm);
205
0
}
206
207
/**
208
   return MS_TRUE if the time string matches the timeformat.
209
   else return MS_FALSE.
210
 */
211
0
int msTimeMatchPattern(const char *timestring, const char *timeformat) {
212
0
  int i = -1;
213
0
  if (msTimeSetup() != MS_SUCCESS) {
214
0
    return MS_FALSE;
215
0
  }
216
217
  /* match the pattern format first and then check if the time string  */
218
  /* matches the pattern. If it is the case retrurn the MS_TRUE */
219
0
  for (i = 0; i < MS_NUMTIMEFORMATS; i++) {
220
0
    if (strcasecmp(ms_timeFormats[i].userformat, timeformat) == 0)
221
0
      break;
222
0
  }
223
224
0
  if (i >= 0 && i < MS_NUMTIMEFORMATS) {
225
0
    int match = ms_regexec(ms_timeFormats[i].regex, timestring, 0, NULL, 0);
226
0
    if (match == 0)
227
0
      return MS_TRUE;
228
0
  }
229
0
  return MS_FALSE;
230
0
}
231
232
0
void msUnsetLimitedPatternToUse() {
233
0
  msTimeSetup();
234
0
  ms_num_limited_pattern = 0;
235
0
}
236
237
0
void msSetLimitedPatternsToUse(const char *patternstring) {
238
0
  int *limitedpatternindice = NULL;
239
0
  int numpatterns = 0, ntmp = 0;
240
0
  char **patterns = NULL;
241
0
  msTimeSetup();
242
243
0
  limitedpatternindice = (int *)msSmallMalloc(sizeof(int) * MS_NUMTIMEFORMATS);
244
245
  /* free previous setting */
246
0
  msUnsetLimitedPatternToUse();
247
248
0
  if (patternstring) {
249
0
    patterns = msStringSplit(patternstring, ',', &ntmp);
250
0
    if (patterns && ntmp >= 1) {
251
252
0
      for (int i = 0; i < ntmp; i++) {
253
0
        for (int j = 0; j < MS_NUMTIMEFORMATS; j++) {
254
0
          if (strcasecmp(ms_timeFormats[j].userformat, patterns[i]) == 0) {
255
0
            limitedpatternindice[numpatterns] = j;
256
0
            numpatterns++;
257
0
            break;
258
0
          }
259
0
        }
260
0
      }
261
0
    }
262
0
    msFreeCharArray(patterns, ntmp);
263
0
  }
264
265
0
  if (numpatterns > 0) {
266
0
    for (int i = 0; i < numpatterns; i++)
267
0
      ms_limited_pattern[i] = limitedpatternindice[i];
268
269
0
    ms_num_limited_pattern = numpatterns;
270
0
  }
271
0
  free(limitedpatternindice);
272
0
}
273
274
0
int msParseTime(const char *string, struct tm *tm) {
275
0
  int num_patterns = 0;
276
277
0
  if (MS_STRING_IS_NULL_OR_EMPTY(string))
278
0
    return MS_FALSE; /* nothing to parse so bail */
279
280
0
  if (msTimeSetup() != MS_SUCCESS) {
281
0
    return MS_FALSE;
282
0
  }
283
284
  /* if limited patterns are set, use them, else use all the patterns defined */
285
0
  if (ms_num_limited_pattern > 0)
286
0
    num_patterns = ms_num_limited_pattern;
287
0
  else
288
0
    num_patterns = MS_NUMTIMEFORMATS;
289
290
0
  for (int i = 0; i < num_patterns; i++) {
291
0
    int match;
292
0
    int indice;
293
0
    if (ms_num_limited_pattern > 0)
294
0
      indice = ms_limited_pattern[i];
295
0
    else
296
0
      indice = i;
297
298
0
    match = ms_regexec(ms_timeFormats[indice].regex, string, 0, NULL, 0);
299
    /* test the expression against the string */
300
0
    if (match == 0) {
301
      /* match    */
302
0
      msStrptime(string, ms_timeFormats[indice].format, tm);
303
0
      return (MS_TRUE);
304
0
    }
305
0
  }
306
307
0
  msSetError(MS_REGEXERR, "Unrecognized date or time format (%s).",
308
0
             "msParseTime()", string);
309
0
  return (MS_FALSE);
310
0
}
311
312
/**
313
 * Parse the time string and return the reslution
314
 */
315
0
int msTimeGetResolution(const char *timestring) {
316
0
  int i = 0;
317
318
0
  if (!timestring)
319
0
    return -1;
320
321
0
  for (i = 0; i < MS_NUMTIMEFORMATS; i++) {
322
0
    ms_regex_t *regex = (ms_regex_t *)msSmallMalloc(sizeof(ms_regex_t));
323
0
    if (ms_regcomp(regex, ms_timeFormats[i].pattern,
324
0
                   MS_REG_EXTENDED | MS_REG_NOSUB) != 0) {
325
0
      msSetError(MS_REGEXERR, "Failed to compile expression (%s).",
326
0
                 "msParseTime()", ms_timeFormats[i].pattern);
327
0
      msFree(regex);
328
0
      return -1;
329
0
    }
330
    /* test the expression against the string */
331
0
    if (ms_regexec(regex, timestring, 0, NULL, 0) == 0) {
332
      /* match    */
333
0
      ms_regfree(regex);
334
0
      msFree(regex);
335
0
      return ms_timeFormats[i].resolution;
336
0
    }
337
0
    ms_regfree(regex);
338
0
    msFree(regex);
339
0
  }
340
341
0
  return -1;
342
0
}
343
344
0
int _msValidateTime(const char *timestring, const char *timeextent) {
345
0
  int numelements, numextents, i, numranges;
346
0
  struct tm tmtimestart, tmtimeend, tmstart, tmend;
347
0
  char **atimerange = NULL, **atimeelements = NULL, **atimeextents = NULL;
348
349
0
  if (!timestring || !timeextent)
350
0
    return MS_FALSE;
351
352
0
  if (strlen(timestring) == 0 || strlen(timeextent) == 0)
353
0
    return MS_FALSE;
354
355
  /* we first need to parse the timesting that is passed
356
     so that we can determine if it is a descrete time
357
     or a range */
358
359
0
  numelements = 0;
360
0
  atimeelements = msStringSplit(timestring, '/', &numelements);
361
0
  msTimeInit(&tmtimestart);
362
0
  msTimeInit(&tmtimeend);
363
364
0
  if (numelements == 1) { /*descrete time*/
365
    /*start end end times are the same*/
366
0
    if (msParseTime(timestring, &tmtimestart) != MS_TRUE) {
367
0
      msFreeCharArray(atimeelements, numelements);
368
0
      return MS_FALSE;
369
0
    }
370
0
    if (msParseTime(timestring, &tmtimeend) != MS_TRUE) {
371
0
      msFreeCharArray(atimeelements, numelements);
372
0
      return MS_FALSE;
373
0
    }
374
0
  } else if (numelements >= 2) { /*range */
375
0
    if (msParseTime(atimeelements[0], &tmtimestart) != MS_TRUE) {
376
0
      msFreeCharArray(atimeelements, numelements);
377
0
      return MS_FALSE;
378
0
    }
379
0
    if (msParseTime(atimeelements[1], &tmtimeend) != MS_TRUE) {
380
0
      msFreeCharArray(atimeelements, numelements);
381
0
      return MS_FALSE;
382
0
    }
383
0
  }
384
385
0
  msFreeCharArray(atimeelements, numelements);
386
387
  /* Now parse the time extent. Extents can be
388
    -  one range (2004-09-21/2004-09-25/resolution)
389
    -  multiple rages 2004-09-21/2004-09-25/res1,2004-09-21/2004-09-25/res2
390
    - one value 2004-09-21
391
    - multiple values 2004-09-21,2004-09-22,2004-09-23
392
  */
393
394
0
  numextents = 0;
395
0
  atimeextents = msStringSplit(timeextent, ',', &numextents);
396
0
  if (numextents <= 0) {
397
0
    msFreeCharArray(atimeextents, numextents);
398
0
    return MS_FALSE;
399
0
  }
400
401
  /*the time timestring should at be valid in one of the extents
402
    defined */
403
404
0
  for (i = 0; i < numextents; i++) {
405
    /* build time structure for the extents */
406
0
    msTimeInit(&tmstart);
407
0
    msTimeInit(&tmend);
408
409
0
    numranges = 0;
410
0
    atimerange = msStringSplit(atimeextents[i], '/', &numranges);
411
    /* - one value 2004-09-21 */
412
0
    if (numranges == 1) {
413
      /*time tested can either be descrete or a range */
414
415
0
      if (msParseTime(atimerange[0], &tmstart) == MS_TRUE &&
416
0
          msParseTime(atimerange[0], &tmend) == MS_TRUE &&
417
0
          msTimeCompare(&tmstart, &tmtimestart) <= 0 &&
418
0
          msTimeCompare(&tmend, &tmtimeend) >= 0) {
419
0
        msFreeCharArray(atimerange, numranges);
420
0
        msFreeCharArray(atimeextents, numextents);
421
0
        return MS_TRUE;
422
0
      }
423
0
    }
424
    /*2004-09-21/2004-09-25/res1*/
425
0
    else if (numranges >= 2) {
426
0
      if (msParseTime(atimerange[0], &tmstart) == MS_TRUE &&
427
0
          msParseTime(atimerange[1], &tmend) == MS_TRUE &&
428
0
          msTimeCompare(&tmstart, &tmtimestart) <= 0 &&
429
0
          msTimeCompare(&tmend, &tmtimeend) >= 0) {
430
0
        msFreeCharArray(atimerange, numranges);
431
0
        msFreeCharArray(atimeextents, numextents);
432
0
        return MS_TRUE;
433
0
      }
434
0
    }
435
0
    msFreeCharArray(atimerange, numranges);
436
0
  }
437
0
  msFreeCharArray(atimeextents, numextents);
438
0
  return MS_FALSE;
439
0
}
440
441
0
int msValidateTimeValue(const char *timestring, const char *timeextent) {
442
0
  char **atimes = NULL;
443
0
  int i, numtimes = 0;
444
445
  /* we need to validate the time passsed in the request */
446
  /* against the time extent defined */
447
448
0
  if (!timestring || !timeextent)
449
0
    return MS_FALSE;
450
451
  /* To avoid SQL injections */
452
0
  if (strchr(timestring, '\''))
453
0
    return MS_FALSE;
454
455
  /* parse the time string. We support descrete times (eg 2004-09-21), */
456
  /* multiple times (2004-09-21, 2004-09-22, ...) */
457
  /* and range(s) (2004-09-21/2004-09-25, 2004-09-27/2004-09-29) */
458
0
  if (strstr(timestring, ",") == NULL &&
459
0
      strstr(timestring, "/") == NULL) { /* discrete time */
460
0
    return _msValidateTime(timestring, timeextent);
461
0
  } else {
462
0
    atimes = msStringSplit(timestring, ',', &numtimes);
463
0
    if (numtimes >= 1) { /* multiple times */
464
0
      for (i = 0; i < numtimes; i++) {
465
0
        if (_msValidateTime(atimes[i], timeextent) == MS_FALSE) {
466
0
          msFreeCharArray(atimes, numtimes);
467
0
          return MS_FALSE;
468
0
        }
469
0
      }
470
0
      msFreeCharArray(atimes, numtimes);
471
0
      return MS_TRUE;
472
0
    } else {
473
0
      msFreeCharArray(atimes, numtimes);
474
0
    }
475
0
  }
476
0
  return MS_FALSE;
477
0
}