Coverage Report

Created: 2026-10-22 06:56

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
914k
#define PARSEDATE_OK     0
95
1.16M
#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
118k
#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
1.59M
#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
577k
{
214
577k
  int day = -1;
215
577k
  if(len < 3)
216
177k
    return -1; /* too short */
217
218
400k
  switch(LOWERCASE(check[1])) {
219
34.9k
  case 'o': /* monday */
220
34.9k
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[2]) == 'n')
221
7.26k
      day = 0;
222
34.9k
    break;
223
65.5k
  case 'u': /* tuesday or sunday */
224
65.5k
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'e')
225
6.84k
      day = 1;
226
58.6k
    else if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 'n')
227
24.5k
      day = 6;
228
65.5k
    break;
229
75.9k
  case 'e': /* wednesday */
230
75.9k
    if((LOWERCASE(check[0]) == 'w') && LOWERCASE(check[2]) == 'd')
231
7.93k
      day = 2;
232
75.9k
    break;
233
32.0k
  case 'h': /* thursday */
234
32.0k
    if((LOWERCASE(check[0]) == 't') && LOWERCASE(check[2]) == 'u')
235
15.3k
      day = 3;
236
32.0k
    break;
237
36.3k
  case 'r': /* friday */
238
36.3k
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[2]) == 'i')
239
18.0k
      day = 4;
240
36.3k
    break;
241
49.9k
  case 'a': /* saturday */
242
49.9k
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[2]) == 't')
243
9.23k
      day = 5;
244
49.9k
    break;
245
400k
  }
246
400k
  if((len > 3) && (day != -1)) {
247
    /* when more than three letters are provided, verify the tail name case
248
       insensitively */
249
29.5k
    static const char * const daysuffix[] = {
250
      /* without the leading three letters */
251
29.5k
      "day", "sday", "nesday", "rsday", "day", "urday", "day"
252
29.5k
    };
253
254
29.5k
    size_t wlen = strlen(daysuffix[day]);
255
29.5k
    if(((len - 3) != wlen) ||
256
15.6k
       !curl_strnequal(&check[3], daysuffix[day], wlen))
257
20.7k
      return -1;
258
29.5k
  }
259
379k
  return day;
260
400k
}
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
517k
{
267
517k
  if(len != 3)
268
243k
    return -1; /* not a month */
269
270
273k
  switch(LOWERCASE(check[2])) {
271
29.8k
  case 'n': /* jan, jun */
272
29.8k
    if(LOWERCASE(check[0]) == 'j') {
273
23.1k
      uint8_t c2 = LOWERCASE(check[1]);
274
23.1k
      if(c2 == 'a')
275
11.8k
        return 0;
276
11.2k
      else if(c2 == 'u')
277
6.55k
        return 5;
278
23.1k
    }
279
11.4k
    break;
280
27.8k
  case 'b': /* feb */
281
27.8k
    if((LOWERCASE(check[0]) == 'f') && LOWERCASE(check[1]) == 'e')
282
16.7k
      return 1;
283
11.1k
    break;
284
31.5k
  case 'r': /* mar, apr */
285
31.5k
    if(LOWERCASE(check[0]) == 'm') {
286
11.5k
      if(LOWERCASE(check[1]) == 'a')
287
6.44k
        return 2;
288
11.5k
    }
289
20.0k
    else if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'p')
290
7.15k
      return 3;
291
17.9k
    break;
292
17.9k
  case 'y': /* may */
293
16.4k
    if((LOWERCASE(check[0]) == 'm') && LOWERCASE(check[1]) == 'a')
294
4.85k
      return 4;
295
11.5k
    break;
296
15.9k
  case 'l': /* jul */
297
15.9k
    if((LOWERCASE(check[0]) == 'j') && LOWERCASE(check[1]) == 'u')
298
5.14k
      return 6;
299
10.8k
    break;
300
14.9k
  case 'g': /* aug */
301
14.9k
    if((LOWERCASE(check[0]) == 'a') && LOWERCASE(check[1]) == 'u')
302
4.71k
      return 7;
303
10.2k
    break;
304
20.4k
  case 'p': /* sep */
305
20.4k
    if((LOWERCASE(check[0]) == 's') && LOWERCASE(check[1]) == 'e')
306
8.87k
      return 8;
307
11.5k
    break;
308
28.0k
  case 't': /* oct */
309
28.0k
    if((LOWERCASE(check[0]) == 'o') && LOWERCASE(check[1]) == 'c')
310
5.76k
      return 9;
311
22.3k
    break;
312
31.0k
  case 'v': /* nov */
313
31.0k
    if((LOWERCASE(check[0]) == 'n') && LOWERCASE(check[1]) == 'o')
314
20.0k
      return 10;
315
11.0k
    break;
316
31.4k
  case 'c': /* dec */
317
31.4k
    if((LOWERCASE(check[0]) == 'd') && LOWERCASE(check[1]) == 'e')
318
19.0k
      return 11;
319
12.4k
    break;
320
273k
  }
321
155k
  return -1; /* return the offset or -1, no real offset is -1 */
322
273k
}
323
324
static int tzcompare(const void *m1, const void *m2)
325
534k
{
326
534k
  const struct tzinfo *tz1 = m1;
327
534k
  const struct tzinfo *tz2 = m2;
328
534k
  return tz1->tz - tz2->tz;
329
534k
}
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
408k
{
339
408k
  char first;
340
408k
  if(len > 4)
341
37.4k
    return -1;
342
371k
  first = check[0];
343
371k
  if(first < 'A')
344
0
    return -1;
345
371k
  if(len == 1) {
346
    /* short-cut single letter names */
347
130k
    if((first > 'Z') || (first == 'J'))
348
36.2k
      return -1; /* no such tz */
349
94.5k
    return tzone[first - 'A'] * 60;
350
130k
  }
351
240k
  else if(len == 2) {
352
    /* two-letter names */
353
40.1k
    if(check[1] == 'T') {
354
15.6k
      if(first == 'N')
355
5.20k
        return 11 * 60 * 60;
356
10.4k
      else if(first == 'U')
357
4.12k
        return 0;
358
15.6k
    }
359
30.7k
    return -1; /* nope */
360
40.1k
  }
361
200k
  else if(len == 3) {
362
    /* three-letter name */
363
156k
    const struct tzinfo *what;
364
156k
    struct tzinfo find;
365
156k
    if(first > 'Y')
366
65.2k
      return -1;
367
90.8k
    find.tz = MKTZ(first, check[1], check[2], 0);
368
90.8k
    what = bsearch(&find, tzthree, CURL_ARRAYSIZE(tzthree),
369
90.8k
                   sizeof(tzthree[0]), tzcompare);
370
90.8k
    if(what)
371
21.9k
      return what->offset * 60;
372
90.8k
  }
373
44.5k
  else {
374
    /* four-letter name */
375
44.5k
    const struct tzinfo *what;
376
44.5k
    struct tzinfo find;
377
44.5k
    if(first > 'W')
378
17.0k
      return -1;
379
27.4k
    find.tz = MKTZ(first, check[1], check[2], check[3]);
380
27.4k
    what = bsearch(&find, tzfoura, CURL_ARRAYSIZE(tzfoura),
381
27.4k
                   sizeof(tzfoura[0]), tzcompare);
382
27.4k
    if(what)
383
7.05k
      return what->offset * 60;
384
27.4k
  }
385
89.3k
  return -1;
386
371k
}
387
388
static void skip(const char **date)
389
1.41M
{
390
  /* skip everything that are not letters or digits */
391
3.25M
  while(**date && !ISALNUM(**date))
392
1.84M
    (*date)++;
393
1.41M
}
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
39.9k
#define LEAP_DAYS_BEFORE_1969 477
407
39.9k
#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
39.9k
{
415
39.9k
  static const int cumulative_days[12] = {
416
39.9k
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
417
39.9k
  };
418
39.9k
  int y = w->year - (w->mon <= 1);
419
39.9k
  int leap_days = (y / 4) - (y / 100) + (y / 400) - LEAP_DAYS_BEFORE_1969;
420
39.9k
  curl_off_t days = (curl_off_t)(w->year - 1970) * 365 + leap_days +
421
39.9k
    cumulative_days[w->mon] + w->mday - 1;
422
423
39.9k
  return (days * SECONDS_PER_DAY) + w->dsecs;
424
39.9k
}
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
742k
{
431
742k
  int num = date[0] - '0';
432
742k
  if(ISDIGIT(date[1])) {
433
397k
    *endp = &date[2];
434
397k
    return (num * 10) + (date[1] - '0');
435
397k
  }
436
344k
  *endp = &date[1];
437
344k
  return num;
438
742k
}
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
587k
{
443
587k
  const char *p;
444
587k
  int hh, mm, ss = 0;
445
587k
  hh = oneortwodigit(date, &p);
446
587k
  if((hh < 24) && (*p == ':') && ISDIGIT(p[1])) {
447
107k
    mm = oneortwodigit(&p[1], &p);
448
107k
    if(mm < 60) {
449
93.7k
      if((*p == ':') && ISDIGIT(p[1])) {
450
47.3k
        ss = oneortwodigit(&p[1], &p);
451
47.3k
        if(ss <= 60) {
452
          /* valid HH:MM:SS */
453
39.1k
          goto match;
454
39.1k
        }
455
47.3k
      }
456
46.3k
      else {
457
        /* valid HH:MM */
458
46.3k
        goto match;
459
46.3k
      }
460
93.7k
    }
461
107k
  }
462
501k
  return FALSE; /* not a time string */
463
85.4k
match:
464
85.4k
  w->dsecs = (hh * 3600) + (mm * 60) + ss;
465
85.4k
  *endp = (char *)CURL_UNCONST(p);
466
85.4k
  return TRUE;
467
587k
}
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
2.62M
#define NAME_LEN 10
482
483
static void initwhen(struct when *w)
484
602k
{
485
602k
  w->wday = w->mon = w->mday = w->dsecs = w->year = w->tzoff = -1;
486
602k
}
487
488
static int datestring(const char **datep, struct when *w)
489
671k
{
490
  /* a name coming up */
491
671k
  size_t len = 0;
492
671k
  const char *p = *datep;
493
2.61M
  while(ISALPHA(p[len]) && (len < NAME_LEN))
494
1.94M
    len++;
495
496
671k
  if(len != NAME_LEN) {
497
661k
    if(w->wday == -1) {
498
577k
      w->wday = checkday(p, len);
499
577k
      if(w->wday != -1)
500
68.4k
        goto found;
501
577k
    }
502
593k
    if(w->mon == -1) {
503
517k
      w->mon = checkmonth(p, len);
504
517k
      if(w->mon != -1)
505
117k
        goto found;
506
517k
    }
507
475k
    if(w->tzoff == -1) {
508
      /* this must be a time zone string */
509
408k
      w->tzoff = checktz(p, len);
510
408k
      if(w->tzoff != -1)
511
132k
        goto found;
512
408k
    }
513
475k
  }
514
352k
  return PARSEDATE_FAIL; /* bad string */
515
318k
found:
516
318k
  *datep += len;
517
318k
  return PARSEDATE_OK;
518
671k
}
519
520
static int datenum(const char *indate, const char **datep, struct when *w)
521
632k
{
522
  /* a digit */
523
632k
  unsigned int val;
524
632k
  char *end;
525
632k
  const char *date = *datep;
526
527
632k
  if((w->dsecs == -1) && match_time(date, w, &end)) {
528
    /* time stamp */
529
85.4k
    date = end;
530
85.4k
  }
531
547k
  else {
532
547k
    bool found = FALSE;
533
547k
    curl_off_t lval;
534
547k
    int num_digits = 0;
535
547k
    const char *p = *datep;
536
547k
    if(curlx_str_number(&p, &lval, 99999999))
537
13.3k
      return PARSEDATE_FAIL;
538
539
    /* we know num_digits cannot be larger than 8 */
540
533k
    num_digits = (int)(p - *datep);
541
533k
    val = (unsigned int)lval;
542
543
533k
    if((w->tzoff == -1) &&
544
448k
       (num_digits == 4) &&
545
63.7k
       (val <= 1400) &&
546
37.6k
       (indate < date) &&
547
29.3k
       (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
19.7k
      found = TRUE;
557
19.7k
      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
19.7k
      w->tzoff = date[-1] == '+' ? -w->tzoff : w->tzoff;
562
19.7k
    }
563
564
513k
    else if((num_digits == 8) && (w->year == -1) &&
565
39.5k
            (w->mon == -1) && (w->mday == -1)) {
566
      /* 8 digits, no year, month or day yet. This is YYYYMMDD */
567
28.2k
      found = TRUE;
568
28.2k
      w->year = val / 10000;
569
28.2k
      w->mon = ((val % 10000) / 100) - 1; /* month is 0 - 11 */
570
28.2k
      if(w->mon > 11)
571
6.79k
        return PARSEDATE_FAIL;
572
21.4k
      w->mday = val % 100;
573
21.4k
    }
574
575
526k
    if(!found && (w->mday == -1)) {
576
354k
      if((val > 0) && (val < 32)) {
577
212k
        w->mday = val;
578
212k
        found = TRUE;
579
212k
      }
580
354k
    }
581
582
526k
    if(!found && (w->year == -1)) {
583
217k
      w->year = val;
584
217k
      found = TRUE;
585
217k
      if(w->year < 100) {
586
141k
        if(w->year > 70)
587
19.6k
          w->year += 1900;
588
121k
        else
589
121k
          w->year += 2000;
590
141k
      }
591
217k
    }
592
593
526k
    if(!found)
594
55.7k
      return PARSEDATE_FAIL;
595
596
471k
    date = p;
597
471k
  }
598
556k
  *datep = date;
599
556k
  return PARSEDATE_OK;
600
632k
}
601
602
static bool datecheck(struct when *w)
603
174k
{
604
174k
  if(w->dsecs == -1)
605
123k
    w->dsecs = 0; /* no time available, make it zero */
606
607
174k
  if((w->mday == -1) || (w->mon == -1) || (w->year == -1) ||
608
    /* lacks vital info, fail */
609
49.2k
     (w->year < 1583) ||
610
     /* The Gregorian calendar was introduced 1582 */
611
43.1k
     (w->mday > 31) /* too long month */ )
612
134k
    return TRUE;
613
614
39.9k
  return FALSE;
615
174k
}
616
617
static void tzadjust(curl_off_t *tp, struct when *w)
618
39.9k
{
619
39.9k
  if(w->tzoff == -1) /* unknown tz means no offset */
620
15.4k
    w->tzoff = 0;
621
622
  /* Add the time zone diff between local time zone and GMT. */
623
39.9k
  if((w->tzoff > 0) && (*tp > (curl_off_t)(CURL_OFF_T_MAX - w->tzoff)))
624
0
    *tp = CURL_OFF_T_MAX;
625
39.9k
  else
626
39.9k
    *tp += w->tzoff;
627
  /* this needs no minimum check since we require a year > 1582 */
628
39.9k
}
629
630
static int mktimet(curl_off_t seconds, time_t *output)
631
39.9k
{
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
39.9k
  *output = (time_t)seconds;
648
39.9k
  return PARSEDATE_OK;
649
39.9k
}
650
651
static int parsedate(const char *date, time_t *output)
652
602k
{
653
602k
  curl_off_t seconds = 0;
654
602k
  const char *indate = date; /* save the original pointer */
655
602k
  int part = 0; /* max 6 parts */
656
602k
  int rc = 0;
657
602k
  struct when w;
658
602k
  initwhen(&w);
659
660
1.58M
  while(*date && (part < 6)) {
661
1.41M
    skip(&date);
662
663
1.41M
    if(ISALPHA(*date))
664
671k
      rc = datestring(&date, &w);
665
741k
    else if(ISDIGIT(*date))
666
632k
      rc = datenum(indate, &date, &w);
667
1.41M
    if(rc)
668
428k
      return rc;
669
670
983k
    part++;
671
983k
  }
672
673
174k
  if(datecheck(&w))
674
134k
    return PARSEDATE_FAIL;
675
676
39.9k
  seconds = time2epoch(&w); /* get number of seconds */
677
39.9k
  tzadjust(&seconds, &w); /* handle the time zone offset */
678
39.9k
  rc = mktimet(seconds, output); /* squeeze seconds into a time_t */
679
680
39.9k
  return rc;
681
174k
}
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
27
{
694
27
  time_t parsed = -1;
695
27
  int rc;
696
27
  (void)unused; /* legacy argument from the past that we ignore */
697
698
27
  if(!p)
699
1
    return -1;
700
701
26
  rc = parsedate(p, &parsed);
702
26
  if(rc == PARSEDATE_OK) {
703
22
    if(parsed == (time_t)-1)
704
      /* avoid returning -1 for a working scenario */
705
1
      parsed++;
706
22
    return parsed;
707
22
  }
708
  /* everything else is fail */
709
4
  return -1;
710
26
}
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
602k
{
718
602k
  int rc = parsedate(p, tp);
719
602k
  return (rc == PARSEDATE_FAIL);
720
602k
}