Coverage Report

Created: 2026-10-15 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/parsedate.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#include "parsedate.h"
27
#include "curlx/strparse.h"
28
#include "curlx/strcopy.h"
29
30
/* A brief summary of the date string formats this parser groks:
31
32
   RFC 2616 3.3.1
33
34
   Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
35
   Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
36
   Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
37
38
   we support dates without week day name:
39
40
   06 Nov 1994 08:49:37 GMT
41
   06-Nov-94 08:49:37 GMT
42
   Nov  6 08:49:37 1994
43
44
   without the time zone:
45
46
   06 Nov 1994 08:49:37
47
   06-Nov-94 08:49:37
48
49
   weird order:
50
51
   1994 Nov 6 08:49:37  (GNU date fails)
52
   GMT 08:49:37 06-Nov-94 Sunday
53
   94 6 Nov 08:49:37    (GNU date fails)
54
55
   time left out:
56
57
   1994 Nov 6
58
   06-Nov-94
59
   Sun Nov 6 94
60
61
   unusual separators:
62
63
   1994.Nov.6
64
   Sun/Nov/6/94/GMT
65
66
   commonly used time zone names:
67
68
   Sun, 06 Nov 1994 08:49:37 CET
69
   06 Nov 1994 08:49:37 EST
70
71
   time zones specified using RFC822 style:
72
73
   Sun, 12 Sep 2004 15:05:58 -0700
74
   Sat, 11 Sep 2004 21:32:11 +0200
75
76
   compact numerical date strings:
77
78
   20040912 15:05:58 -0700
79
   20040911 +0200
80
 */
81
82
#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \
83
  !defined(CURL_DISABLE_FILE) || defined(USE_GNUTLS)
84
/* These names are also used by FTP and FILE code */
85
const char * const Curl_wkday[] = {
86
  "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"
87
};
88
const char * const Curl_month[] = {
89
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
90
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
91
};
92
#endif
93
94
0
#define PARSEDATE_OK     0
95
0
#define PARSEDATE_FAIL   (-1)
96
97
#ifndef CURL_DISABLE_PARSEDATE
98
99
#if SIZEOF_TIME_T < 5
100
#define PARSEDATE_LATER  1
101
#endif
102
#if SIZEOF_TIME_T < 5 || defined(HAVE_TIME_T_UNSIGNED)
103
#define PARSEDATE_SOONER 2
104
#endif
105
106
struct tzinfo {
107
  uint32_t tz;
108
  int16_t offset; /* +/- in minutes */
109
};
110
111
#define DST (-60)     /* offset for daylight savings time */
112
113
/* alpha-sorted list of single-letter time zones, A - Z */
114
static const int16_t tzone[] = {
115
  -1 * 60,            /* Alpha */
116
  -2 * 60,            /* Bravo */
117
  -3 * 60,            /* Charlie */
118
  -4 * 60,            /* Delta */
119
  -5 * 60,            /* Echo */
120
  -6 * 60,            /* Foxtrot */
121
  -7 * 60,            /* Golf */
122
  -8 * 60,            /* Hotel */
123
  -9 * 60,            /* India */
124
  -1,                 /* J - not a timezone */
125
  -10 * 60,           /* Kilo */
126
  -11 * 60,           /* Lima */
127
  -12 * 60,           /* Mike */
128
  60,                 /* November */
129
   2 * 60,            /* Oscar */
130
   3 * 60,            /* Papa */
131
   4 * 60,            /* Quebec */
132
   5 * 60,            /* Romeo */
133
   6 * 60,            /* Sierra */
134
   7 * 60,            /* Tango */
135
   8 * 60,            /* Uniform */
136
   9 * 60,            /* Victor */
137
  10 * 60,            /* Whiskey */
138
  11 * 60,            /* X-ray */
139
  12 * 60,            /* Yankee */
140
  0                   /* Zulu, zero meridian, a.k.a. UTC */
141
};
142
143
/* two-letter time zones
144
145
  11 * 60,              NT - Nome    spellchecker:disable-line
146
  0,                    UT - Universal Time
147
148
*/
149
150
0
#define MKTZ(a,b,c,d) (((a)<<24) | ((b)<<16) | ((c)<<8) | (d))
151
152
/* three-letter time zones */
153
static const struct tzinfo tzthree[] = {
154
  { MKTZ('A', 'D', 'T', '\0'),   240 + DST }, /* Atlantic Daylight */
155
  { MKTZ('A', 'S', 'T', '\0'),   240 },       /* Atlantic Standard */
156
  { MKTZ('B', 'S', 'T', '\0'),     0 + DST }, /* British Summer */
157
  { MKTZ('C', 'A', 'T', '\0'),   600 },       /* Central Alaska */
158
  { MKTZ('C', 'C', 'T', '\0'),  -480 },       /* China Coast, USSR Zone 7 */
159
  { MKTZ('C', 'D', 'T', '\0'),   360 + DST }, /* Central Daylight */
160
  { MKTZ('C', 'E', 'T', '\0'),   -60 },       /* Central European */
161
  { MKTZ('C', 'S', 'T', '\0'),   360 },       /* Central Standard */
162
  { MKTZ('E', 'D', 'T', '\0'),   300 + DST }, /* Eastern Daylight */
163
  { MKTZ('E', 'E', 'T', '\0'),  -120 },       /* Eastern Europe, USSR Zone 1 */
164
  { MKTZ('E', 'S', 'T', '\0'),   300 },       /* Eastern Standard */
165
  { MKTZ('F', 'S', 'T', '\0'),   -60 + DST }, /* French Summer */
166
  { MKTZ('F', 'W', 'T', '\0'),   -60 },       /* French Winter */
167
  { MKTZ('G', 'M', 'T', '\0'),     0 },       /* Greenwich Mean */
168
  { MKTZ('G', 'S', 'T', '\0'),  -600 },       /* Guam Standard, USSR Zone 9 */
169
  { MKTZ('H', 'D', 'T', '\0'),   600 + DST }, /* Hawaii Daylight */
170
  { MKTZ('H', 'S', 'T', '\0'),   600 },       /* Hawaii Standard */
171
  { MKTZ('J', 'S', 'T', '\0'),  -540 },       /* Japan Standard, USSR Zone 8 */
172
  { MKTZ('M', 'D', 'T', '\0'),   420 + DST }, /* Mountain Daylight */
173
  { MKTZ('M', 'E', 'T', '\0'),   -60 },       /* Middle European */
174
  { MKTZ('M', 'S', 'T', '\0'),   420 },       /* Mountain Standard */
175
  { MKTZ('N', 'Z', 'T', '\0'),  -720 },       /* New Zealand */
176
  { MKTZ('P', 'D', 'T', '\0'),   480 + DST }, /* Pacific Daylight */
177
  { MKTZ('P', 'S', 'T', '\0'),   480 },       /* Pacific Standard */
178
  { MKTZ('U', 'T', 'C', '\0'),     0 },       /* Universal (Coordinated) */
179
  { MKTZ('W', 'A', 'T', '\0'),    60 },       /* West Africa */
180
  { MKTZ('W', 'E', 'T', '\0'),     0 },       /* Western European */
181
  { MKTZ('Y', 'D', 'T', '\0'),   540 + DST }, /* Yukon Daylight */
182
  { MKTZ('Y', 'S', 'T', '\0'),   540 },       /* Yukon Standard */
183
};
184
185
/* four-letter time zones */
186
static const struct tzinfo tzfoura[] = {
187
  { MKTZ('A', 'H', 'S', 'T'),  600 },       /* Alaska-Hawaii Standard */
188
  { MKTZ('C', 'E', 'S', 'T'),  -60 + DST }, /* Central European Summer */
189
  { MKTZ('E', 'A', 'D', 'T'), -600 + DST }, /* Eastern Australian Daylight */
190
  { MKTZ('E', 'A', 'S', 'T'), -600 },       /* Eastern Australian Standard */
191
  { MKTZ('I', 'D', 'L', 'E'), -720 },       /* International Date Line East */
192
  { MKTZ('I', 'D', 'L', 'W'),  720 },       /* International Date Line West */
193
  { MKTZ('M', 'E', 'S', 'T'),  -60 + DST }, /* Middle European Summer */
194
  { MKTZ('M', 'E', 'S', 'Z'),  -60 + DST }, /* Middle European Summer */
195
  { MKTZ('M', 'E', 'W', 'T'),  -60 },       /* Middle European Winter */
196
  { MKTZ('N', 'Z', 'D', 'T'), -720 + DST }, /* New Zealand Daylight */
197
  { MKTZ('N', 'Z', 'S', 'T'), -720 },       /* New Zealand Standard */
198
  { MKTZ('W', 'A', 'D', 'T'), -420 + DST }, /* West Australian Daylight */
199
  { MKTZ('W', 'A', 'S', 'T'), -420 }, /* spellchecker:disable-line */
200
                               /* West Australian Standard */
201
};
202
203
0
#define LOWERCASE(x) ((x) | 0x20)
204
205
/* returns:
206
   -1 no day
207
   0 monday - 6 sunday
208
209
   @unittest 4781
210
*/
211
UNITTEST int checkday(const char *check, size_t len);
212
UNITTEST int checkday(const char *check, size_t len)
213
0
{
214
0
  int day = -1;
215
0
  if(len < 3)
216
0
    return -1; /* too short */
217
218
0
  switch(LOWERCASE(check[1])) {
219
0
  case 'o': /* monday */
220
0
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[2]) == 'n')
221
0
      day = 0;
222
0
    break;
223
0
  case 'u': /* tuesday or sunday */
224
0
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'e')
225
0
      day = 1;
226
0
    else if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 'n')
227
0
      day = 6;
228
0
    break;
229
0
  case 'e': /* wednesday */
230
0
    if((LOWERCASE(check[0]) == 'w') && LOWERCASE(check[2]) == 'd')
231
0
      day = 2;
232
0
    break;
233
0
  case 'h': /* thursday */
234
0
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'u')
235
0
      day = 3;
236
0
    break;
237
0
  case 'r': /* friday */
238
0
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[2]) == 'i')
239
0
      day = 4;
240
0
    break;
241
0
  case 'a': /* saturday */
242
0
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 't')
243
0
      day = 5;
244
0
    break;
245
0
  }
246
0
  if((len > 3) && (day != -1)) {
247
    /* when more than three letters are provided, verify the tail name case
248
       insensitively */
249
0
    static const char * const daysuffix[] = {
250
      /* without the leading three letters */
251
0
      "day", "sday", "nesday", "rsday", "day", "urday", "day"
252
0
    };
253
254
0
    size_t wlen = strlen(daysuffix[day]);
255
0
    if(((len - 3) != wlen) ||
256
0
       !curl_strnequal(&check[3], daysuffix[day], wlen))
257
0
      return -1;
258
0
  }
259
0
  return day;
260
0
}
261
262
/* @unittest 4781 */
263
264
UNITTEST int checkmonth(const char *check, size_t len);
265
UNITTEST int checkmonth(const char *check, size_t len)
266
0
{
267
0
  if(len != 3)
268
0
    return -1; /* not a month */
269
270
0
  switch(LOWERCASE(check[2])) {
271
0
  case 'n': /* jan, jun */
272
0
    if(LOWERCASE(check[0]) == 'j') {
273
0
      uint8_t c2 = LOWERCASE(check[1]);
274
0
      if(c2 == 'a')
275
0
        return 0;
276
0
      else if(c2 == 'u')
277
0
        return 5;
278
0
    }
279
0
    break;
280
0
  case 'b': /* feb */
281
0
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[1]) == 'e')
282
0
      return 1;
283
0
    break;
284
0
  case 'r': /* mar, apr */
285
0
    if(LOWERCASE(check[0]) == 'm') {
286
0
      if(LOWERCASE(check[1]) == 'a')
287
0
        return 2;
288
0
    }
289
0
    else if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'p')
290
0
      return 3;
291
0
    break;
292
0
  case 'y': /* may */
293
0
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[1]) == 'a')
294
0
      return 4;
295
0
    break;
296
0
  case 'l': /* jul */
297
0
    if((LOWERCASE(check[0]) == 'j') && LOWERCASE(check[1]) == 'u')
298
0
      return 6;
299
0
    break;
300
0
  case 'g': /* aug */
301
0
    if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'u')
302
0
      return 7;
303
0
    break;
304
0
  case 'p': /* sep */
305
0
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[1]) == 'e')
306
0
      return 8;
307
0
    break;
308
0
  case 't': /* oct */
309
0
    if((LOWERCASE(check[0]) == 'o') && LOWERCASE(check[1]) == 'c')
310
0
      return 9;
311
0
    break;
312
0
  case 'v': /* nov */
313
0
    if((LOWERCASE(check[0]) == 'n') && LOWERCASE(check[1]) == 'o')
314
0
      return 10;
315
0
    break;
316
0
  case 'c': /* dec */
317
0
    if((LOWERCASE(check[0]) == 'd') && LOWERCASE(check[1]) == 'e')
318
0
      return 11;
319
0
    break;
320
0
  }
321
0
  return -1; /* return the offset or -1, no real offset is -1 */
322
0
}
323
324
static int tzcompare(const void *m1, const void *m2)
325
0
{
326
0
  const struct tzinfo *tz1 = m1;
327
0
  const struct tzinfo *tz2 = m2;
328
0
  return tz1->tz - tz2->tz;
329
0
}
330
331
/* return the time zone offset between GMT and the input one, in number of
332
   seconds or -1 if the timezone was not found/legal
333
334
   @unittest 4781
335
*/
336
UNITTEST int checktz(const char *check, size_t len);
337
UNITTEST int checktz(const char *check, size_t len)
338
0
{
339
0
  char first;
340
0
  if(len > 4)
341
0
    return -1;
342
0
  first = check[0];
343
0
  if(first < 'A')
344
0
    return -1;
345
0
  if(len == 1) {
346
    /* short-cut single letter names */
347
0
    if((first > 'Z') || (first == 'J'))
348
0
      return -1; /* no such tz */
349
0
    return tzone[first - 'A'] * 60;
350
0
  }
351
0
  else if(len == 2) {
352
    /* two-letter names */
353
0
    if(check[1] == 'T') {
354
0
      if(first == 'N')
355
0
        return 11 * 60 * 60;
356
0
      else if(first == 'U')
357
0
        return 0;
358
0
    }
359
0
    return -1; /* nope */
360
0
  }
361
0
  else if(len == 3) {
362
    /* three-letter name */
363
0
    const struct tzinfo *what;
364
0
    struct tzinfo find;
365
0
    if(first > 'Y')
366
0
      return -1;
367
0
    find.tz = MKTZ(first, check[1], check[2], 0);
368
0
    what = bsearch(&find, tzthree, CURL_ARRAYSIZE(tzthree),
369
0
                   sizeof(tzthree[0]), tzcompare);
370
0
    if(what)
371
0
      return what->offset * 60;
372
0
  }
373
0
  else {
374
    /* four-letter name */
375
0
    const struct tzinfo *what;
376
0
    struct tzinfo find;
377
0
    if(first > 'W')
378
0
      return -1;
379
0
    find.tz = MKTZ(first, check[1], check[2], check[3]);
380
0
    what = bsearch(&find, tzfoura, CURL_ARRAYSIZE(tzfoura),
381
0
                   sizeof(tzfoura[0]), tzcompare);
382
0
    if(what)
383
0
      return what->offset * 60;
384
0
  }
385
0
  return -1;
386
0
}
387
388
static void skip(const char **date)
389
0
{
390
  /* skip everything that are not letters or digits */
391
0
  while(**date && !ISALNUM(**date))
392
0
    (*date)++;
393
0
}
394
395
/* each field is exactly -1 when unknown */
396
struct when {
397
  int wday;  /* day of the week, 0-6 (mon-sun) */
398
  int mon;   /* month of the year, 0-11 */
399
  int mday;  /* day of month, 1 - 31 */
400
  int hour;  /* hour of day, 0 - 23 */
401
  int min;   /* minute of hour, 0 - 59 */
402
  int sec;   /* second of minute, 0 - 60 (leap second) */
403
  int year;  /* year, >= 1583 */
404
  int tzoff; /* time zone offset in seconds */
405
};
406
407
enum assume {
408
  DATE_MDAY,
409
  DATE_YEAR,
410
  DATE_TIME
411
};
412
413
/* (1969 / 4) - (1969 / 100) + (1969 / 400) = 492 - 19 + 4 = 477 */
414
0
#define LEAP_DAYS_BEFORE_1969 477
415
416
/*
417
 * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to
418
 * mktime but for GMT only.
419
 */
420
static curl_off_t time2epoch(struct when *w)
421
0
{
422
0
  static const int cumulative_days[12] = {
423
0
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
424
0
  };
425
0
  int y = w->year - (w->mon <= 1);
426
0
  int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969;
427
0
  curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days +
428
0
    cumulative_days[w->mon] + w->mday - 1;
429
430
0
  return (((days * 24 + w->hour) * 60 + w->min) * 60) + w->sec;
431
0
}
432
433
/* Returns the value of a single-digit or two-digit decimal number, return
434
   then pointer to after the number. The 'date' pointer is known to point to a
435
   digit. */
436
static int oneortwodigit(const char *date, const char **endp)
437
0
{
438
0
  int num = date[0] - '0';
439
0
  if(ISDIGIT(date[1])) {
440
0
    *endp = &date[2];
441
0
    return (num * 10) + (date[1] - '0');
442
0
  }
443
0
  *endp = &date[1];
444
0
  return num;
445
0
}
446
447
/* HH:MM:SS or HH:MM and accept single-digits too */
448
static bool match_time(const char *date, struct when *w, char **endp)
449
0
{
450
0
  const char *p;
451
0
  int hh, mm, ss = 0;
452
0
  hh = oneortwodigit(date, &p);
453
0
  if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) {
454
0
    mm = oneortwodigit(&p[1], &p);
455
0
    if(mm < 60) {
456
0
      if((*p == ':') && ISDIGIT(p[1])) {
457
0
        ss = oneortwodigit(&p[1], &p);
458
0
        if(ss <= 60) {
459
          /* valid HH:MM:SS */
460
0
          goto match;
461
0
        }
462
0
      }
463
0
      else {
464
        /* valid HH:MM */
465
0
        goto match;
466
0
      }
467
0
    }
468
0
  }
469
0
  return FALSE; /* not a time string */
470
0
match:
471
0
  w->hour = hh;
472
0
  w->min = mm;
473
0
  w->sec = ss;
474
0
  *endp = (char *)CURL_UNCONST(p);
475
0
  return TRUE;
476
0
}
477
478
/*
479
 * parsedate()
480
 *
481
 * Returns:
482
 *
483
 * PARSEDATE_OK     - a fine conversion
484
 * PARSEDATE_FAIL   - failed to convert
485
 * PARSEDATE_LATER  - time overflow at the far end of time_t
486
 * PARSEDATE_SOONER - time underflow at the low end of time_t
487
 */
488
489
/* Wednesday is the longest name this parser knows about */
490
0
#define NAME_LEN 12
491
492
static void initwhen(struct when *w)
493
0
{
494
0
  w->wday = w->mon = w->mday = w->hour = w->min = w->sec = w->year = w->tzoff =
495
0
    -1;
496
0
}
497
498
static int datestring(const char **datep, struct when *w)
499
0
{
500
  /* a name coming up */
501
0
  size_t len = 0;
502
0
  const char *p = *datep;
503
0
  bool found = FALSE;
504
0
  while(ISALPHA(*p) && (len < NAME_LEN)) {
505
0
    p++;
506
0
    len++;
507
0
  }
508
509
0
  if(len != NAME_LEN) {
510
0
    if(w->wday == -1) {
511
0
      w->wday = checkday(*datep, len);
512
0
      if(w->wday != -1)
513
0
        found = TRUE;
514
0
    }
515
0
    if(!found && (w->mon == -1)) {
516
0
      w->mon = checkmonth(*datep, len);
517
0
      if(w->mon != -1)
518
0
        found = TRUE;
519
0
    }
520
521
0
    if(!found && (w->tzoff == -1)) {
522
      /* this must be a time zone string */
523
0
      w->tzoff = checktz(*datep, len);
524
0
      if(w->tzoff != -1)
525
0
        found = TRUE;
526
0
    }
527
0
  }
528
0
  if(!found)
529
0
    return PARSEDATE_FAIL; /* bad string */
530
531
0
  *datep += len;
532
0
  return PARSEDATE_OK;
533
0
}
534
535
static int datenum(const char *indate, const char **datep, struct when *w,
536
                   enum assume *dignextp)
537
0
{
538
  /* a digit */
539
0
  unsigned int val;
540
0
  char *end;
541
0
  const char *date = *datep;
542
0
  enum assume dignext = *dignextp;
543
544
0
  if((w->sec == -1) && match_time(date, w, &end)) {
545
    /* time stamp */
546
0
    date = end;
547
0
  }
548
0
  else {
549
0
    bool found = FALSE;
550
0
    curl_off_t lval;
551
0
    int num_digits = 0;
552
0
    const char *p = *datep;
553
0
    if(curlx_str_number(&p, &lval, 99999999))
554
0
      return PARSEDATE_FAIL;
555
556
    /* we know num_digits cannot be larger than 8 */
557
0
    num_digits = (int)(p - *datep);
558
0
    val = (unsigned int)lval;
559
560
0
    if((w->tzoff == -1) &&
561
0
       (num_digits == 4) &&
562
0
       (val <= 1400) &&
563
0
       (indate < date) &&
564
0
       (date[-1] == '+' || date[-1] == '-')) {
565
      /* four digits and a value less than or equal to 1400 (to take into
566
         account all sorts of funny time zone diffs) and it is preceded
567
         with a plus or minus. This is a time zone indication. 1400 is
568
         picked since +1300 is frequently used and +1400 is mentioned as
569
         an edge number in the document "ISO C 200X Proposal: Timezone
570
         Functions" at http://david.tribble.com/text/c0xtimezone.html If
571
         anyone has a more authoritative source for the exact maximum time
572
         zone offsets, please speak up! */
573
0
      found = TRUE;
574
0
      w->tzoff = ((val / 100 * 60) + (val % 100)) * 60;
575
576
      /* the + and - prefix indicates the local time compared to GMT,
577
         this we need their reversed math to get what we want */
578
0
      w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff;
579
0
    }
580
581
0
    else if((num_digits == 8) && (w->year == -1) &&
582
0
            (w->mon == -1) && (w->mday == -1)) {
583
      /* 8 digits, no year, month or day yet. This is YYYYMMDD */
584
0
      found = TRUE;
585
0
      w->year = val / 10000;
586
0
      w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */
587
0
      w->mday = val % 100;
588
0
    }
589
590
0
    if(!found && (dignext == DATE_MDAY) && (w->mday == -1)) {
591
0
      if((val > 0) && (val < 32)) {
592
0
        w->mday = val;
593
0
        found = TRUE;
594
0
      }
595
0
      dignext = DATE_YEAR;
596
0
    }
597
598
0
    if(!found && (dignext == DATE_YEAR) && (w->year == -1)) {
599
0
      w->year = val;
600
0
      found = TRUE;
601
0
      if(w->year < 100) {
602
0
        if(w->year > 70)
603
0
          w->year += 1900;
604
0
        else
605
0
          w->year += 2000;
606
0
      }
607
0
      if(w->mday == -1)
608
0
        dignext = DATE_MDAY;
609
0
    }
610
611
0
    if(!found)
612
0
      return PARSEDATE_FAIL;
613
614
0
    date = p;
615
0
  }
616
0
  *datep = date;
617
0
  *dignextp = dignext;
618
0
  return PARSEDATE_OK;
619
0
}
620
621
static int datecheck(struct when *w)
622
0
{
623
0
  if(w->sec == -1)
624
0
    w->sec = w->min = w->hour = 0; /* no time, make it zero */
625
626
0
  if((w->mday == -1) || (w->mon == -1) || (w->year == -1))
627
    /* lacks vital info, fail */
628
0
    return PARSEDATE_FAIL;
629
630
  /* The Gregorian calendar was introduced 1582 */
631
0
  else if(w->year < 1583)
632
0
    return PARSEDATE_FAIL;
633
634
0
  else if((w->mday > 31) || (w->mon > 11) || (w->hour > 23) ||
635
0
          (w->min > 59) || (w->sec > 60))
636
0
    return PARSEDATE_FAIL; /* clearly an illegal date */
637
638
0
  return PARSEDATE_OK;
639
0
}
640
641
static void tzadjust(curl_off_t *tp, struct when *w)
642
0
{
643
0
  if(w->tzoff == -1) /* unknown tz means no offset */
644
0
    w->tzoff = 0;
645
646
  /* Add the time zone diff between local time zone and GMT. */
647
0
  if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff)))
648
0
    *tp = CURL_OFF_T_MAX;
649
0
  else
650
0
    *tp += w->tzoff;
651
  /* this needs no minimum check since we require a year > 1582 */
652
0
}
653
654
static int mktimet(curl_off_t seconds, time_t *output)
655
0
{
656
#if SIZEOF_TIME_T < 5
657
  if(seconds > TIME_T_MAX) {
658
    *output = TIME_T_MAX;
659
    return PARSEDATE_LATER;
660
  }
661
  else if(seconds < TIME_T_MIN) {
662
    *output = TIME_T_MIN;
663
    return PARSEDATE_SOONER;
664
  }
665
#elif defined(HAVE_TIME_T_UNSIGNED)
666
  if(seconds < 0) {
667
    *output = 0;
668
    return PARSEDATE_SOONER;
669
  }
670
#endif
671
0
  *output = (time_t)seconds;
672
0
  return PARSEDATE_OK;
673
0
}
674
675
static int parsedate(const char *date, time_t *output)
676
0
{
677
0
  curl_off_t seconds = 0;
678
0
  enum assume dignext = DATE_MDAY;
679
0
  const char *indate = date; /* save the original pointer */
680
0
  int part = 0; /* max 6 parts */
681
0
  int rc = 0;
682
0
  struct when w;
683
0
  initwhen(&w);
684
685
0
  while(*date && (part < 6)) {
686
0
    skip(&date);
687
688
0
    if(ISALPHA(*date))
689
0
      rc = datestring(&date, &w);
690
0
    else if(ISDIGIT(*date))
691
0
      rc = datenum(indate, &date, &w, &dignext);
692
0
    if(rc)
693
0
      return rc;
694
695
0
    part++;
696
0
  }
697
698
0
  rc = datecheck(&w);
699
0
  if(rc)
700
0
    return rc;
701
702
0
  seconds = time2epoch(&w); /* get number of seconds */
703
0
  tzadjust(&seconds, &w); /* handle the time zone offset */
704
0
  rc = mktimet(seconds, output); /* squeeze seconds into a time_t */
705
706
0
  return rc;
707
0
}
708
#else
709
/* disabled */
710
static int parsedate(const char *date, time_t *output)
711
{
712
  (void)date;
713
  *output = 0;
714
  return PARSEDATE_OK; /* a lie */
715
}
716
#endif
717
718
time_t curl_getdate(const char *p, const time_t *unused)
719
0
{
720
0
  time_t parsed = -1;
721
0
  int rc;
722
0
  (void)unused; /* legacy argument from the past that we ignore */
723
724
0
  if(!p)
725
0
    return -1;
726
727
0
  rc = parsedate(p, &parsed);
728
0
  if(rc == PARSEDATE_OK) {
729
0
    if(parsed == (time_t)-1)
730
      /* avoid returning -1 for a working scenario */
731
0
      parsed++;
732
0
    return parsed;
733
0
  }
734
  /* everything else is fail */
735
0
  return -1;
736
0
}
737
738
/* Curl_getdate_capped() differs from curl_getdate() in that this will return
739
   TIME_T_MAX in case the parsed time value was too big, instead of an
740
   error. Returns non-zero on error. */
741
742
int Curl_getdate_capped(const char *p, time_t *tp)
743
0
{
744
0
  int rc = parsedate(p, tp);
745
0
  return (rc == PARSEDATE_FAIL);
746
0
}