Coverage Report

Created: 2026-10-15 06:20

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
47.5k
#define PARSEDATE_OK     0
95
53.9k
#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
8.05k
#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
89.7k
#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
27.3k
{
214
27.3k
  int day = -1;
215
27.3k
  if(len < 3)
216
5.62k
    return -1; /* too short */
217
218
21.7k
  switch(LOWERCASE(check[1])) {
219
1.47k
  case 'o': /* monday */
220
1.47k
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[2]) == 'n')
221
68
      day = 0;
222
1.47k
    break;
223
3.35k
  case 'u': /* tuesday or sunday */
224
3.35k
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'e')
225
339
      day = 1;
226
3.02k
    else if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 'n')
227
206
      day = 6;
228
3.35k
    break;
229
4.85k
  case 'e': /* wednesday */
230
4.85k
    if((LOWERCASE(check[0]) == 'w') && LOWERCASE(check[2]) == 'd')
231
194
      day = 2;
232
4.85k
    break;
233
550
  case 'h': /* thursday */
234
550
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'u')
235
74
      day = 3;
236
550
    break;
237
5.62k
  case 'r': /* friday */
238
5.62k
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[2]) == 'i')
239
4.21k
      day = 4;
240
5.62k
    break;
241
1.45k
  case 'a': /* saturday */
242
1.45k
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 't')
243
215
      day = 5;
244
1.45k
    break;
245
21.7k
  }
246
21.7k
  if((len > 3) && (day != -1)) {
247
    /* when more than three letters are provided, verify the tail name case
248
       insensitively */
249
793
    static const char * const daysuffix[] = {
250
      /* without the leading three letters */
251
793
      "day", "sday", "nesday", "rsday", "day", "urday", "day"
252
793
    };
253
254
793
    size_t wlen = strlen(daysuffix[day]);
255
793
    if(((len - 3) != wlen) ||
256
249
       !curl_strnequal(&check[3], daysuffix[day], wlen))
257
793
      return -1;
258
793
  }
259
20.9k
  return day;
260
21.7k
}
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
23.1k
{
267
23.1k
  if(len != 3)
268
8.12k
    return -1; /* not a month */
269
270
15.0k
  switch(LOWERCASE(check[2])) {
271
1.89k
  case 'n': /* jan, jun */
272
1.89k
    if(LOWERCASE(check[0]) == 'j') {
273
917
      uint8_t c2 = LOWERCASE(check[1]);
274
917
      if(c2 == 'a')
275
79
        return 0;
276
838
      else if(c2 == 'u')
277
350
        return 5;
278
917
    }
279
1.46k
    break;
280
3.08k
  case 'b': /* feb */
281
3.08k
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[1]) == 'e')
282
2.29k
      return 1;
283
789
    break;
284
1.49k
  case 'r': /* mar, apr */
285
1.49k
    if(LOWERCASE(check[0]) == 'm') {
286
390
      if(LOWERCASE(check[1]) == 'a')
287
194
        return 2;
288
390
    }
289
1.10k
    else if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'p')
290
213
      return 3;
291
1.08k
    break;
292
1.08k
  case 'y': /* may */
293
729
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[1]) == 'a')
294
329
      return 4;
295
400
    break;
296
1.99k
  case 'l': /* jul */
297
1.99k
    if((LOWERCASE(check[0]) == 'j') && LOWERCASE(check[1]) == 'u')
298
1.01k
      return 6;
299
982
    break;
300
982
  case 'g': /* aug */
301
614
    if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'u')
302
219
      return 7;
303
395
    break;
304
616
  case 'p': /* sep */
305
616
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[1]) == 'e')
306
219
      return 8;
307
397
    break;
308
855
  case 't': /* oct */
309
855
    if((LOWERCASE(check[0]) == 'o') && LOWERCASE(check[1]) == 'c')
310
197
      return 9;
311
658
    break;
312
658
  case 'v': /* nov */
313
381
    if((LOWERCASE(check[0]) == 'n') && LOWERCASE(check[1]) == 'o')
314
80
      return 10;
315
301
    break;
316
2.09k
  case 'c': /* dec */
317
2.09k
    if((LOWERCASE(check[0]) == 'd') && LOWERCASE(check[1]) == 'e')
318
1.31k
      return 11;
319
779
    break;
320
15.0k
  }
321
8.53k
  return -1; /* return the offset or -1, no real offset is -1 */
322
15.0k
}
323
324
static int tzcompare(const void *m1, const void *m2)
325
38.5k
{
326
38.5k
  const struct tzinfo *tz1 = m1;
327
38.5k
  const struct tzinfo *tz2 = m2;
328
38.5k
  return tz1->tz - tz2->tz;
329
38.5k
}
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
20.3k
{
339
20.3k
  char first;
340
20.3k
  if(len > 4)
341
1.71k
    return -1;
342
18.6k
  first = check[0];
343
18.6k
  if(first < 'A')
344
0
    return -1;
345
18.6k
  if(len == 1) {
346
    /* short-cut single letter names */
347
5.54k
    if((first > 'Z') || (first == 'J'))
348
1.37k
      return -1; /* no such tz */
349
4.16k
    return tzone[first - 'A'] * 60;
350
5.54k
  }
351
13.0k
  else if(len == 2) {
352
    /* two-letter names */
353
1.53k
    if(check[1] == 'T') {
354
749
      if(first == 'N')
355
197
        return 11 * 60 * 60;
356
552
      else if(first == 'U')
357
197
        return 0;
358
749
    }
359
1.14k
    return -1; /* nope */
360
1.53k
  }
361
11.5k
  else if(len == 3) {
362
    /* three-letter name */
363
9.69k
    const struct tzinfo *what;
364
9.69k
    struct tzinfo find;
365
9.69k
    if(first > 'Y')
366
2.62k
      return -1;
367
7.06k
    find.tz = MKTZ(first, check[1], check[2], 0);
368
7.06k
    what = bsearch(&find, tzthree, CURL_ARRAYSIZE(tzthree),
369
7.06k
                   sizeof(tzthree[0]), tzcompare);
370
7.06k
    if(what)
371
347
      return what->offset * 60;
372
7.06k
  }
373
1.85k
  else {
374
    /* four-letter name */
375
1.85k
    const struct tzinfo *what;
376
1.85k
    struct tzinfo find;
377
1.85k
    if(first > 'W')
378
864
      return -1;
379
989
    find.tz = MKTZ(first, check[1], check[2], check[3]);
380
989
    what = bsearch(&find, tzfoura, CURL_ARRAYSIZE(tzfoura),
381
989
                   sizeof(tzfoura[0]), tzcompare);
382
989
    if(what)
383
297
      return what->offset * 60;
384
989
  }
385
7.41k
  return -1;
386
18.6k
}
387
388
static void skip(const char **date)
389
65.3k
{
390
  /* skip everything that are not letters or digits */
391
110k
  while(**date && !ISALNUM(**date))
392
44.7k
    (*date)++;
393
65.3k
}
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
2.51k
#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
2.51k
{
422
2.51k
  static const int cumulative_days[12] = {
423
2.51k
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
424
2.51k
  };
425
2.51k
  int y = w->year - (w->mon <= 1);
426
2.51k
  int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969;
427
2.51k
  curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days +
428
2.51k
    cumulative_days[w->mon] + w->mday - 1;
429
430
2.51k
  return (((days * 24 + w->hour) * 60 + w->min) * 60) + w->sec;
431
2.51k
}
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
34.4k
{
438
34.4k
  int num = date[0] - '0';
439
34.4k
  if(ISDIGIT(date[1])) {
440
19.7k
    *endp = &date[2];
441
19.7k
    return (num * 10) + (date[1] - '0');
442
19.7k
  }
443
14.6k
  *endp = &date[1];
444
14.6k
  return num;
445
34.4k
}
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
27.7k
{
450
27.7k
  const char *p;
451
27.7k
  int hh, mm, ss = 0;
452
27.7k
  hh = oneortwodigit(date, &p);
453
27.7k
  if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) {
454
4.18k
    mm = oneortwodigit(&p[1], &p);
455
4.18k
    if(mm < 60) {
456
3.84k
      if((*p == ':') && ISDIGIT(p[1])) {
457
2.50k
        ss = oneortwodigit(&p[1], &p);
458
2.50k
        if(ss <= 60) {
459
          /* valid HH:MM:SS */
460
2.23k
          goto match;
461
2.23k
        }
462
2.50k
      }
463
1.34k
      else {
464
        /* valid HH:MM */
465
1.34k
        goto match;
466
1.34k
      }
467
3.84k
    }
468
4.18k
  }
469
24.1k
  return FALSE; /* not a time string */
470
3.57k
match:
471
3.57k
  w->hour = hh;
472
3.57k
  w->min = mm;
473
3.57k
  w->sec = ss;
474
3.57k
  *endp = (char *)CURL_UNCONST(p);
475
3.57k
  return TRUE;
476
27.7k
}
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
143k
#define NAME_LEN 12
491
492
static void initwhen(struct when *w)
493
28.2k
{
494
28.2k
  w->wday = w->mon = w->mday = w->hour = w->min = w->sec = w->year = w->tzoff =
495
28.2k
    -1;
496
28.2k
}
497
498
static int datestring(const char **datep, struct when *w)
499
34.3k
{
500
  /* a name coming up */
501
34.3k
  size_t len = 0;
502
34.3k
  const char *p = *datep;
503
34.3k
  bool found = FALSE;
504
142k
  while(ISALPHA(*p) && (len < NAME_LEN)) {
505
108k
    p++;
506
108k
    len++;
507
108k
  }
508
509
34.3k
  if(len != NAME_LEN) {
510
33.3k
    if(w->wday == -1) {
511
27.3k
      w->wday = checkday(*datep, len);
512
27.3k
      if(w->wday != -1)
513
4.51k
        found = TRUE;
514
27.3k
    }
515
33.3k
    if(!found && (w->mon == -1)) {
516
23.1k
      w->mon = checkmonth(*datep, len);
517
23.1k
      if(w->mon != -1)
518
6.50k
        found = TRUE;
519
23.1k
    }
520
521
33.3k
    if(!found && (w->tzoff == -1)) {
522
      /* this must be a time zone string */
523
20.3k
      w->tzoff = checktz(*datep, len);
524
20.3k
      if(w->tzoff != -1)
525
5.20k
        found = TRUE;
526
20.3k
    }
527
33.3k
  }
528
34.3k
  if(!found)
529
18.1k
    return PARSEDATE_FAIL; /* bad string */
530
531
16.2k
  *datep += len;
532
16.2k
  return PARSEDATE_OK;
533
34.3k
}
534
535
static int datenum(const char *indate, const char **datep, struct when *w,
536
                   enum assume *dignextp)
537
28.4k
{
538
  /* a digit */
539
28.4k
  unsigned int val;
540
28.4k
  char *end;
541
28.4k
  const char *date = *datep;
542
28.4k
  enum assume dignext = *dignextp;
543
544
28.4k
  if((w->sec == -1) && match_time(date, w, &end)) {
545
    /* time stamp */
546
3.57k
    date = end;
547
3.57k
  }
548
24.8k
  else {
549
24.8k
    bool found = FALSE;
550
24.8k
    curl_off_t lval;
551
24.8k
    int num_digits = 0;
552
24.8k
    const char *p = *datep;
553
24.8k
    if(curlx_str_number(&p, &lval, 99999999))
554
281
      return PARSEDATE_FAIL;
555
556
    /* we know num_digits cannot be larger than 8 */
557
24.5k
    num_digits = (int)(p - *datep);
558
24.5k
    val = (unsigned int)lval;
559
560
24.5k
    if((w->tzoff == -1) &&
561
20.4k
       (num_digits == 4) &&
562
2.02k
       (val <= 1400) &&
563
951
       (indate < date) &&
564
745
       (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
509
      found = TRUE;
574
509
      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
509
      w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff;
579
509
    }
580
581
24.0k
    else if((num_digits == 8) && (w->year == -1) &&
582
1.49k
            (w->mon == -1) && (w->mday == -1)) {
583
      /* 8 digits, no year, month or day yet. This is YYYYMMDD */
584
1.08k
      found = TRUE;
585
1.08k
      w->year = val / 10000;
586
1.08k
      w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */
587
1.08k
      w->mday = val % 100;
588
1.08k
    }
589
590
24.5k
    if(!found && (dignext == DATE_MDAY) && (w->mday == -1)) {
591
17.3k
      if((val > 0) && (val < 32)) {
592
9.87k
        w->mday = val;
593
9.87k
        found = TRUE;
594
9.87k
      }
595
17.3k
      dignext = DATE_YEAR;
596
17.3k
    }
597
598
24.5k
    if(!found && (dignext == DATE_YEAR) && (w->year == -1)) {
599
11.2k
      w->year = val;
600
11.2k
      found = TRUE;
601
11.2k
      if(w->year < 100) {
602
7.79k
        if(w->year > 70)
603
463
          w->year += 1900;
604
7.33k
        else
605
7.33k
          w->year += 2000;
606
7.79k
      }
607
11.2k
      if(w->mday == -1)
608
6.96k
        dignext = DATE_MDAY;
609
11.2k
    }
610
611
24.5k
    if(!found)
612
1.88k
      return PARSEDATE_FAIL;
613
614
22.6k
    date = p;
615
22.6k
  }
616
26.2k
  *datep = date;
617
26.2k
  *dignextp = dignext;
618
26.2k
  return PARSEDATE_OK;
619
28.4k
}
620
621
static int datecheck(struct when *w)
622
7.94k
{
623
7.94k
  if(w->sec == -1)
624
5.28k
    w->sec = w->min = w->hour = 0; /* no time, make it zero */
625
626
7.94k
  if((w->mday == -1) || (w->mon == -1) || (w->year == -1))
627
    /* lacks vital info, fail */
628
4.56k
    return PARSEDATE_FAIL;
629
630
  /* The Gregorian calendar was introduced 1582 */
631
3.37k
  else if(w->year < 1583)
632
276
    return PARSEDATE_FAIL;
633
634
3.10k
  else if((w->mday > 31) || (w->mon > 11) || (w->hour > 23) ||
635
2.51k
          (w->min > 59) || (w->sec > 60))
636
593
    return PARSEDATE_FAIL; /* clearly an illegal date */
637
638
2.51k
  return PARSEDATE_OK;
639
7.94k
}
640
641
static void tzadjust(curl_off_t *tp, struct when *w)
642
2.51k
{
643
2.51k
  if(w->tzoff == -1) /* unknown tz means no offset */
644
360
    w->tzoff = 0;
645
646
  /* Add the time zone diff between local time zone and GMT. */
647
2.51k
  if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff)))
648
0
    *tp = CURL_OFF_T_MAX;
649
2.51k
  else
650
2.51k
    *tp += w->tzoff;
651
  /* this needs no minimum check since we require a year > 1582 */
652
2.51k
}
653
654
static int mktimet(curl_off_t seconds, time_t *output)
655
2.51k
{
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
2.51k
  *output = (time_t)seconds;
672
2.51k
  return PARSEDATE_OK;
673
2.51k
}
674
675
static int parsedate(const char *date, time_t *output)
676
28.2k
{
677
28.2k
  curl_off_t seconds = 0;
678
28.2k
  enum assume dignext = DATE_MDAY;
679
28.2k
  const char *indate = date; /* save the original pointer */
680
28.2k
  int part = 0; /* max 6 parts */
681
28.2k
  int rc = 0;
682
28.2k
  struct when w;
683
28.2k
  initwhen(&w);
684
685
73.3k
  while(*date && (part < 6)) {
686
65.3k
    skip(&date);
687
688
65.3k
    if(ISALPHA(*date))
689
34.3k
      rc = datestring(&date, &w);
690
31.0k
    else if(ISDIGIT(*date))
691
28.4k
      rc = datenum(indate, &date, &w, &dignext);
692
65.3k
    if(rc)
693
20.2k
      return rc;
694
695
45.0k
    part++;
696
45.0k
  }
697
698
7.94k
  rc = datecheck(&w);
699
7.94k
  if(rc)
700
5.43k
    return rc;
701
702
2.51k
  seconds = time2epoch(&w); /* get number of seconds */
703
2.51k
  tzadjust(&seconds, &w); /* handle the time zone offset */
704
2.51k
  rc = mktimet(seconds, output); /* squeeze seconds into a time_t */
705
706
2.51k
  return rc;
707
7.94k
}
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
28.2k
{
744
28.2k
  int rc = parsedate(p, tp);
745
28.2k
  return (rc == PARSEDATE_FAIL);
746
28.2k
}