|  |  | 
|  | /* | 
|  | * Copyright (C) Igor Sysoev | 
|  | * Copyright (C) Nginx, Inc. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include <ngx_config.h> | 
|  | #include <ngx_core.h> | 
|  |  | 
|  |  | 
|  | static ngx_uint_t  mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | 
|  |  | 
|  | time_t | 
|  | ngx_parse_http_time(u_char *value, size_t len) | 
|  | { | 
|  | u_char      *p, *end; | 
|  | ngx_int_t    month; | 
|  | ngx_uint_t   day, year, hour, min, sec; | 
|  | uint64_t     time; | 
|  | enum { | 
|  | no = 0, | 
|  | rfc822,   /* Tue, 10 Nov 2002 23:50:13   */ | 
|  | rfc850,   /* Tuesday, 10-Dec-02 23:50:13 */ | 
|  | isoc      /* Tue Dec 10 23:50:13 2002    */ | 
|  | } fmt; | 
|  |  | 
|  | fmt = 0; | 
|  | end = value + len; | 
|  |  | 
|  | #if (NGX_SUPPRESS_WARN) | 
|  | day = 32; | 
|  | year = 2038; | 
|  | #endif | 
|  |  | 
|  | for (p = value; p < end; p++) { | 
|  | if (*p == ',') { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (*p == ' ') { | 
|  | fmt = isoc; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (p++; p < end; p++) | 
|  | if (*p != ' ') { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (end - p < 18) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (fmt != isoc) { | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | day = (*p - '0') * 10 + *(p + 1) - '0'; | 
|  | p += 2; | 
|  |  | 
|  | if (*p == ' ') { | 
|  | if (end - p < 18) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | fmt = rfc822; | 
|  |  | 
|  | } else if (*p == '-') { | 
|  | fmt = rfc850; | 
|  |  | 
|  | } else { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | p++; | 
|  | } | 
|  |  | 
|  | switch (*p) { | 
|  |  | 
|  | case 'J': | 
|  | month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6; | 
|  | break; | 
|  |  | 
|  | case 'F': | 
|  | month = 1; | 
|  | break; | 
|  |  | 
|  | case 'M': | 
|  | month = *(p + 2) == 'r' ? 2 : 4; | 
|  | break; | 
|  |  | 
|  | case 'A': | 
|  | month = *(p + 1) == 'p' ? 3 : 7; | 
|  | break; | 
|  |  | 
|  | case 'S': | 
|  | month = 8; | 
|  | break; | 
|  |  | 
|  | case 'O': | 
|  | month = 9; | 
|  | break; | 
|  |  | 
|  | case 'N': | 
|  | month = 10; | 
|  | break; | 
|  |  | 
|  | case 'D': | 
|  | month = 11; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | p += 3; | 
|  |  | 
|  | if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | p++; | 
|  |  | 
|  | if (fmt == rfc822) { | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' | 
|  | || *(p + 2) < '0' || *(p + 2) > '9' | 
|  | || *(p + 3) < '0' || *(p + 3) > '9') | 
|  | { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 | 
|  | + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; | 
|  | p += 4; | 
|  |  | 
|  | } else if (fmt == rfc850) { | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | year = (*p - '0') * 10 + *(p + 1) - '0'; | 
|  | year += (year < 70) ? 2000 : 1900; | 
|  | p += 2; | 
|  | } | 
|  |  | 
|  | if (fmt == isoc) { | 
|  | if (*p == ' ') { | 
|  | p++; | 
|  | } | 
|  |  | 
|  | if (*p < '0' || *p > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | day = *p++ - '0'; | 
|  |  | 
|  | if (*p != ' ') { | 
|  | if (*p < '0' || *p > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | day = day * 10 + *p++ - '0'; | 
|  | } | 
|  |  | 
|  | if (end - p < 14) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (*p++ != ' ') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | hour = (*p - '0') * 10 + *(p + 1) - '0'; | 
|  | p += 2; | 
|  |  | 
|  | if (*p++ != ':') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | min = (*p - '0') * 10 + *(p + 1) - '0'; | 
|  | p += 2; | 
|  |  | 
|  | if (*p++ != ':') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | sec = (*p - '0') * 10 + *(p + 1) - '0'; | 
|  |  | 
|  | if (fmt == isoc) { | 
|  | p += 2; | 
|  |  | 
|  | if (*p++ != ' ') { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9' | 
|  | || *(p + 2) < '0' || *(p + 2) > '9' | 
|  | || *(p + 3) < '0' || *(p + 3) > '9') | 
|  | { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100 | 
|  | + (*(p + 2) - '0') * 10 + *(p + 3) - '0'; | 
|  | } | 
|  |  | 
|  | if (hour > 23 || min > 59 || sec > 59) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | if (day == 29 && month == 1) { | 
|  | if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | } else if (day > mday[month]) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * shift new year to March 1 and start months from 1 (not 0), | 
|  | * it is needed for Gauss' formula | 
|  | */ | 
|  |  | 
|  | if (--month <= 0) { | 
|  | month += 12; | 
|  | year -= 1; | 
|  | } | 
|  |  | 
|  | /* Gauss' formula for Gregorian days since March 1, 1 BC */ | 
|  |  | 
|  | time = (uint64_t) ( | 
|  | /* days in years including leap years since March 1, 1 BC */ | 
|  |  | 
|  | 365 * year + year / 4 - year / 100 + year / 400 | 
|  |  | 
|  | /* days before the month */ | 
|  |  | 
|  | + 367 * month / 12 - 30 | 
|  |  | 
|  | /* days before the day */ | 
|  |  | 
|  | + day - 1 | 
|  |  | 
|  | /* | 
|  | * 719527 days were between March 1, 1 BC and March 1, 1970, | 
|  | * 31 and 28 days were in January and February 1970 | 
|  | */ | 
|  |  | 
|  | - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec; | 
|  |  | 
|  | #if (NGX_TIME_T_SIZE <= 4) | 
|  |  | 
|  | if (time > 0x7fffffff) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  |  | 
|  | #endif | 
|  |  | 
|  | return (time_t) time; | 
|  | } |