|  | 
 | /* | 
 |  * Copyright (C) Igor Sysoev | 
 |  */ | 
 |  | 
 |  | 
 | #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_http_parse_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 Grigorian 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; | 
 | } |