Coverage Report

Created: 2026-10-22 06:49

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
21.9k
#define PARSEDATE_OK     0
95
40.7k
#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
3.95k
#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
49.8k
#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
17.9k
{
214
17.9k
  int day = -1;
215
17.9k
  if(len < 3)
216
5.21k
    return -1; /* too short */
217
218
12.7k
  switch(LOWERCASE(check[1])) {
219
1.08k
  case 'o': /* monday */
220
1.08k
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[2]) == 'n')
221
329
      day = 0;
222
1.08k
    break;
223
2.26k
  case 'u': /* tuesday or sunday */
224
2.26k
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'e')
225
222
      day = 1;
226
2.04k
    else if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 'n')
227
194
      day = 6;
228
2.26k
    break;
229
2.56k
  case 'e': /* wednesday */
230
2.56k
    if((LOWERCASE(check[0]) == 'w') && LOWERCASE(check[2]) == 'd')
231
292
      day = 2;
232
2.56k
    break;
233
760
  case 'h': /* thursday */
234
760
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'u')
235
229
      day = 3;
236
760
    break;
237
725
  case 'r': /* friday */
238
725
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[2]) == 'i')
239
194
      day = 4;
240
725
    break;
241
1.68k
  case 'a': /* saturday */
242
1.68k
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 't')
243
269
      day = 5;
244
1.68k
    break;
245
12.7k
  }
246
12.7k
  if((len > 3) && (day != -1)) {
247
    /* when more than three letters are provided, verify the tail name case
248
       insensitively */
249
524
    static const char * const daysuffix[] = {
250
      /* without the leading three letters */
251
524
      "day", "sday", "nesday", "rsday", "day", "urday", "day"
252
524
    };
253
254
524
    size_t wlen = strlen(daysuffix[day]);
255
524
    if(((len - 3) != wlen) ||
256
240
       !curl_strnequal(&check[3], daysuffix[day], wlen))
257
524
      return -1;
258
524
  }
259
12.1k
  return day;
260
12.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
15.9k
{
267
15.9k
  if(len != 3)
268
7.05k
    return -1; /* not a month */
269
270
8.86k
  switch(LOWERCASE(check[2])) {
271
939
  case 'n': /* jan, jun */
272
939
    if(LOWERCASE(check[0]) == 'j') {
273
612
      uint8_t c2 = LOWERCASE(check[1]);
274
612
      if(c2 == 'a')
275
213
        return 0;
276
399
      else if(c2 == 'u')
277
199
        return 5;
278
612
    }
279
527
    break;
280
620
  case 'b': /* feb */
281
620
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[1]) == 'e')
282
196
      return 1;
283
424
    break;
284
1.26k
  case 'r': /* mar, apr */
285
1.26k
    if(LOWERCASE(check[0]) == 'm') {
286
391
      if(LOWERCASE(check[1]) == 'a')
287
197
        return 2;
288
391
    }
289
871
    else if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'p')
290
434
      return 3;
291
631
    break;
292
657
  case 'y': /* may */
293
657
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[1]) == 'a')
294
259
      return 4;
295
398
    break;
296
640
  case 'l': /* jul */
297
640
    if((LOWERCASE(check[0]) == 'j') && LOWERCASE(check[1]) == 'u')
298
239
      return 6;
299
401
    break;
300
880
  case 'g': /* aug */
301
880
    if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'u')
302
343
      return 7;
303
537
    break;
304
673
  case 'p': /* sep */
305
673
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[1]) == 'e')
306
281
      return 8;
307
392
    break;
308
866
  case 't': /* oct */
309
866
    if((LOWERCASE(check[0]) == 'o') && LOWERCASE(check[1]) == 'c')
310
209
      return 9;
311
657
    break;
312
657
  case 'v': /* nov */
313
630
    if((LOWERCASE(check[0]) == 'n') && LOWERCASE(check[1]) == 'o')
314
199
      return 10;
315
431
    break;
316
715
  case 'c': /* dec */
317
715
    if((LOWERCASE(check[0]) == 'd') && LOWERCASE(check[1]) == 'e')
318
299
      return 11;
319
416
    break;
320
8.86k
  }
321
5.79k
  return -1; /* return the offset or -1, no real offset is -1 */
322
8.86k
}
323
324
static int tzcompare(const void *m1, const void *m2)
325
17.8k
{
326
17.8k
  const struct tzinfo *tz1 = m1;
327
17.8k
  const struct tzinfo *tz2 = m2;
328
17.8k
  return tz1->tz - tz2->tz;
329
17.8k
}
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
12.8k
{
339
12.8k
  char first;
340
12.8k
  if(len > 4)
341
777
    return -1;
342
12.0k
  first = check[0];
343
12.0k
  if(first < 'A')
344
0
    return -1;
345
12.0k
  if(len == 1) {
346
    /* short-cut single letter names */
347
3.65k
    if((first > 'Z') || (first == 'J'))
348
745
      return -1; /* no such tz */
349
2.91k
    return tzone[first - 'A'] * 60;
350
3.65k
  }
351
8.41k
  else if(len == 2) {
352
    /* two-letter names */
353
1.11k
    if(check[1] == 'T') {
354
714
      if(first == 'N')
355
195
        return 11 * 60 * 60;
356
519
      else if(first == 'U')
357
197
        return 0;
358
714
    }
359
722
    return -1; /* nope */
360
1.11k
  }
361
7.30k
  else if(len == 3) {
362
    /* three-letter name */
363
5.64k
    const struct tzinfo *what;
364
5.64k
    struct tzinfo find;
365
5.64k
    if(first > 'Y')
366
2.75k
      return -1;
367
2.88k
    find.tz = MKTZ(first, check[1], check[2], 0);
368
2.88k
    what = bsearch(&find, tzthree, CURL_ARRAYSIZE(tzthree),
369
2.88k
                   sizeof(tzthree[0]), tzcompare);
370
2.88k
    if(what)
371
292
      return what->offset * 60;
372
2.88k
  }
373
1.66k
  else {
374
    /* four-letter name */
375
1.66k
    const struct tzinfo *what;
376
1.66k
    struct tzinfo find;
377
1.66k
    if(first > 'W')
378
590
      return -1;
379
1.07k
    find.tz = MKTZ(first, check[1], check[2], check[3]);
380
1.07k
    what = bsearch(&find, tzfoura, CURL_ARRAYSIZE(tzfoura),
381
1.07k
                   sizeof(tzfoura[0]), tzcompare);
382
1.07k
    if(what)
383
261
      return what->offset * 60;
384
1.07k
  }
385
3.40k
  return -1;
386
12.0k
}
387
388
static void skip(const char **date)
389
41.9k
{
390
  /* skip everything that are not letters or digits */
391
160k
  while(**date && !ISALNUM(**date))
392
118k
    (*date)++;
393
41.9k
}
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 dsecs; /* number of seconds into the day */
401
  int year;  /* year, >= 1583 */
402
  int tzoff; /* time zone offset in seconds */
403
};
404
405
/* (1969 / 4) - (1969 / 100) + (1969 / 400) = 492 - 19 + 4 = 477 */
406
863
#define LEAP_DAYS_BEFORE_1969 477
407
863
#define SECONDS_PER_DAY 86400
408
409
/*
410
 * time2epoch: time stamp to seconds since epoch in GMT time zone. Similar to
411
 * mktime but for GMT only.
412
 */
413
static curl_off_t time2epoch(struct when *w)
414
863
{
415
863
  static const int cumulative_days[12] = {
416
863
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
417
863
  };
418
863
  int y = w->year - (w->mon <= 1);
419
863
  int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969;
420
863
  curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days +
421
863
    cumulative_days[w->mon] + w->mday - 1;
422
423
863
  return (days * SECONDS_PER_DAY) + w->dsecs;
424
863
}
425
426
/* Returns the value of a single-digit or two-digit decimal number, return
427
   then pointer to after the number. The 'date' pointer is known to point to a
428
   digit. */
429
static int oneortwodigit(const char *date, const char **endp)
430
17.4k
{
431
17.4k
  int num = date[0] - '0';
432
17.4k
  if(ISDIGIT(date[1])) {
433
8.62k
    *endp = &date[2];
434
8.62k
    return (num * 10) + (date[1] - '0');
435
8.62k
  }
436
8.83k
  *endp = &date[1];
437
8.83k
  return num;
438
17.4k
}
439
440
/* HH:MM:SS or HH:MM and accept single-digits too */
441
static bool match_time(const char *date, struct when *w, char **endp)
442
14.8k
{
443
14.8k
  const char *p;
444
14.8k
  int hh, mm, ss = 0;
445
14.8k
  hh = oneortwodigit(date, &p);
446
14.8k
  if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) {
447
1.79k
    mm = oneortwodigit(&p[1], &p);
448
1.79k
    if(mm < 60) {
449
1.50k
      if((*p == ':') && ISDIGIT(p[1])) {
450
771
        ss = oneortwodigit(&p[1], &p);
451
771
        if(ss <= 60) {
452
          /* valid HH:MM:SS */
453
577
          goto match;
454
577
        }
455
771
      }
456
730
      else {
457
        /* valid HH:MM */
458
730
        goto match;
459
730
      }
460
1.50k
    }
461
1.79k
  }
462
13.5k
  return FALSE; /* not a time string */
463
1.30k
match:
464
1.30k
  w->dsecs = (hh * 3600) + (mm * 60) + ss;
465
1.30k
  *endp = (char *)CURL_UNCONST(p);
466
1.30k
  return TRUE;
467
14.8k
}
468
469
/*
470
 * parsedate()
471
 *
472
 * Returns:
473
 *
474
 * PARSEDATE_OK     - a fine conversion
475
 * PARSEDATE_FAIL   - failed to convert
476
 * PARSEDATE_LATER  - time overflow at the far end of time_t
477
 * PARSEDATE_SOONER - time underflow at the low end of time_t
478
 */
479
480
/* Wednesday is the longest name this parser knows about */
481
73.0k
#define NAME_LEN 10
482
483
static void initwhen(struct when *w)
484
20.8k
{
485
20.8k
  w->wday = w->mon = w->mday = w->dsecs = w->year = w->tzoff = -1;
486
20.8k
}
487
488
static int datestring(const char **datep, struct when *w)
489
18.7k
{
490
  /* a name coming up */
491
18.7k
  size_t len = 0;
492
18.7k
  const char *p = *datep;
493
72.7k
  while(ISALPHA(p[len]) && (len < NAME_LEN))
494
53.9k
    len++;
495
496
18.7k
  if(len != NAME_LEN) {
497
18.3k
    if(w->wday == -1) {
498
17.9k
      w->wday = checkday(p, len);
499
17.9k
      if(w->wday != -1)
500
1.20k
        goto found;
501
17.9k
    }
502
17.1k
    if(w->mon == -1) {
503
15.9k
      w->mon = checkmonth(p, len);
504
15.9k
      if(w->mon != -1)
505
3.06k
        goto found;
506
15.9k
    }
507
14.0k
    if(w->tzoff == -1) {
508
      /* this must be a time zone string */
509
12.8k
      w->tzoff = checktz(p, len);
510
12.8k
      if(w->tzoff != -1)
511
3.85k
        goto found;
512
12.8k
    }
513
14.0k
  }
514
10.6k
  return PARSEDATE_FAIL; /* bad string */
515
8.13k
found:
516
8.13k
  *datep += len;
517
8.13k
  return PARSEDATE_OK;
518
18.7k
}
519
520
static int datenum(const char *indate, const char **datep, struct when *w)
521
15.3k
{
522
  /* a digit */
523
15.3k
  unsigned int val;
524
15.3k
  char *end;
525
15.3k
  const char *date = *datep;
526
527
15.3k
  if((w->dsecs == -1) && match_time(date, w, &end)) {
528
    /* time stamp */
529
1.30k
    date = end;
530
1.30k
  }
531
14.0k
  else {
532
14.0k
    bool found = FALSE;
533
14.0k
    curl_off_t lval;
534
14.0k
    int num_digits = 0;
535
14.0k
    const char *p = *datep;
536
14.0k
    if(curlx_str_number(&p, &lval, 99999999))
537
279
      return PARSEDATE_FAIL;
538
539
    /* we know num_digits cannot be larger than 8 */
540
13.7k
    num_digits = (int)(p - *datep);
541
13.7k
    val = (unsigned int)lval;
542
543
13.7k
    if((w->tzoff == -1) &&
544
11.4k
       (num_digits == 4) &&
545
1.94k
       (val <= 1400) &&
546
1.58k
       (indate < date) &&
547
1.22k
       (date[-1] == '+' || date[-1] == '-')) {
548
      /* four digits and a value less than or equal to 1400 (to take into
549
         account all sorts of funny time zone diffs) and it is preceded
550
         with a plus or minus. This is a time zone indication. 1400 is
551
         picked since +1300 is frequently used and +1400 is mentioned as
552
         an edge number in the document "ISO C 200X Proposal: Timezone
553
         Functions" at http://david.tribble.com/text/c0xtimezone.html If
554
         anyone has a more authoritative source for the exact maximum time
555
         zone offsets, please speak up! */
556
392
      found = TRUE;
557
392
      w->tzoff = ((val / 100 * 60) + (val % 100)) * 60;
558
559
      /* the + and - prefix indicates the local time compared to GMT,
560
         this we need their reversed math to get what we want */
561
392
      w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff;
562
392
    }
563
564
13.3k
    else if((num_digits == 8) && (w->year == -1) &&
565
1.60k
            (w->mon == -1) && (w->mday == -1)) {
566
      /* 8 digits, no year, month or day yet. This is YYYYMMDD */
567
1.14k
      found = TRUE;
568
1.14k
      w->year = val / 10000;
569
1.14k
      w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */
570
1.14k
      if(w->mon > 11)
571
196
        return PARSEDATE_FAIL;
572
949
      w->mday = val % 100;
573
949
    }
574
575
13.5k
    if(!found && (w->mday == -1)) {
576
8.54k
      if((val > 0) && (val < 32)) {
577
4.38k
        w->mday = val;
578
4.38k
        found = TRUE;
579
4.38k
      }
580
8.54k
    }
581
582
13.5k
    if(!found && (w->year == -1)) {
583
5.92k
      w->year = val;
584
5.92k
      found = TRUE;
585
5.92k
      if(w->year < 100) {
586
4.55k
        if(w->year > 70)
587
423
          w->year += 1900;
588
4.12k
        else
589
4.12k
          w->year += 2000;
590
4.55k
      }
591
5.92k
    }
592
593
13.5k
    if(!found)
594
1.92k
      return PARSEDATE_FAIL;
595
596
11.6k
    date = p;
597
11.6k
  }
598
12.9k
  *datep = date;
599
12.9k
  return PARSEDATE_OK;
600
15.3k
}
601
602
static bool datecheck(struct when *w)
603
7.79k
{
604
7.79k
  if(w->dsecs == -1)
605
6.75k
    w->dsecs = 0; /* no time available, make it zero */
606
607
7.79k
  if((w->mday == -1) || (w->mon == -1) || (w->year == -1) ||
608
    /* lacks vital info, fail */
609
1.16k
     (w->year < 1583) ||
610
     /* The Gregorian calendar was introduced 1582 */
611
948
     (w->mday > 31) /* too long month */ )
612
6.92k
    return TRUE;
613
614
863
  return FALSE;
615
7.79k
}
616
617
static void tzadjust(curl_off_t *tp, struct when *w)
618
863
{
619
863
  if(w->tzoff == -1) /* unknown tz means no offset */
620
413
    w->tzoff = 0;
621
622
  /* Add the time zone diff between local time zone and GMT. */
623
863
  if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff)))
624
0
    *tp = CURL_OFF_T_MAX;
625
863
  else
626
863
    *tp += w->tzoff;
627
  /* this needs no minimum check since we require a year > 1582 */
628
863
}
629
630
static int mktimet(curl_off_t seconds, time_t *output)
631
863
{
632
#if SIZEOF_TIME_T < 5
633
  if(seconds > TIME_T_MAX) {
634
    *output = TIME_T_MAX;
635
    return PARSEDATE_LATER;
636
  }
637
  else if(seconds < TIME_T_MIN) {
638
    *output = TIME_T_MIN;
639
    return PARSEDATE_SOONER;
640
  }
641
#elif defined(HAVE_TIME_T_UNSIGNED)
642
  if(seconds < 0) {
643
    *output = 0;
644
    return PARSEDATE_SOONER;
645
  }
646
#endif
647
863
  *output = (time_t)seconds;
648
863
  return PARSEDATE_OK;
649
863
}
650
651
static int parsedate(const char *date, time_t *output)
652
20.8k
{
653
20.8k
  curl_off_t seconds = 0;
654
20.8k
  const char *indate = date; /* save the original pointer */
655
20.8k
  int part = 0; /* max 6 parts */
656
20.8k
  int rc = 0;
657
20.8k
  struct when w;
658
20.8k
  initwhen(&w);
659
660
49.7k
  while(*date && (part < 6)) {
661
41.9k
    skip(&date);
662
663
41.9k
    if(ISALPHA(*date))
664
18.7k
      rc = datestring(&date, &w);
665
23.1k
    else if(ISDIGIT(*date))
666
15.3k
      rc = datenum(indate, &date, &w);
667
41.9k
    if(rc)
668
13.0k
      return rc;
669
670
28.8k
    part++;
671
28.8k
  }
672
673
7.79k
  if(datecheck(&w))
674
6.92k
    return PARSEDATE_FAIL;
675
676
863
  seconds = time2epoch(&w); /* get number of seconds */
677
863
  tzadjust(&seconds, &w); /* handle the time zone offset */
678
863
  rc = mktimet(seconds, output); /* squeeze seconds into a time_t */
679
680
863
  return rc;
681
7.79k
}
682
#else
683
/* disabled */
684
static int parsedate(const char *date, time_t *output)
685
{
686
  (void)date;
687
  *output = 0;
688
  return PARSEDATE_OK; /* a lie */
689
}
690
#endif
691
692
time_t curl_getdate(const char *p, const time_t *unused)
693
0
{
694
0
  time_t parsed = -1;
695
0
  int rc;
696
0
  (void)unused; /* legacy argument from the past that we ignore */
697
698
0
  if(!p)
699
0
    return -1;
700
701
0
  rc = parsedate(p, &parsed);
702
0
  if(rc == PARSEDATE_OK) {
703
0
    if(parsed == (time_t)-1)
704
      /* avoid returning -1 for a working scenario */
705
0
      parsed++;
706
0
    return parsed;
707
0
  }
708
  /* everything else is fail */
709
0
  return -1;
710
0
}
711
712
/* Curl_getdate_capped() differs from curl_getdate() in that this will return
713
   TIME_T_MAX in case the parsed time value was too big, instead of an
714
   error. Returns non-zero on error. */
715
716
int Curl_getdate_capped(const char *p, time_t *tp)
717
20.8k
{
718
20.8k
  int rc = parsedate(p, tp);
719
20.8k
  return (rc == PARSEDATE_FAIL);
720
20.8k
}