1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter/* 18251876Speter * apr_date.c: date parsing utility routines 19251876Speter * These routines are (hopefully) platform independent. 20251876Speter * 21251876Speter * 27 Oct 1996 Roy Fielding 22251876Speter * Extracted (with many modifications) from mod_proxy.c and 23251876Speter * tested with over 50,000 randomly chosen valid date strings 24251876Speter * and several hundred variations of invalid date strings. 25251876Speter * 26251876Speter */ 27251876Speter 28251876Speter#include "apr.h" 29251876Speter#include "apr_lib.h" 30251876Speter 31251876Speter#define APR_WANT_STRFUNC 32251876Speter#include "apr_want.h" 33251876Speter 34251876Speter#if APR_HAVE_STDLIB_H 35251876Speter#include <stdlib.h> 36251876Speter#endif 37251876Speter 38251876Speter#if APR_HAVE_CTYPE_H 39251876Speter#include <ctype.h> 40251876Speter#endif 41251876Speter 42251876Speter#include "apr_date.h" 43251876Speter 44251876Speter/* 45251876Speter * Compare a string to a mask 46251876Speter * Mask characters (arbitrary maximum is 256 characters, just in case): 47251876Speter * @ - uppercase letter 48251876Speter * $ - lowercase letter 49251876Speter * & - hex digit 50251876Speter * # - digit 51251876Speter * ~ - digit or space 52251876Speter * * - swallow remaining characters 53251876Speter * <x> - exact match for any other character 54251876Speter */ 55251876SpeterAPU_DECLARE(int) apr_date_checkmask(const char *data, const char *mask) 56251876Speter{ 57251876Speter int i; 58251876Speter char d; 59251876Speter 60251876Speter for (i = 0; i < 256; i++) { 61251876Speter d = data[i]; 62251876Speter switch (mask[i]) { 63251876Speter case '\0': 64251876Speter return (d == '\0'); 65251876Speter 66251876Speter case '*': 67251876Speter return 1; 68251876Speter 69251876Speter case '@': 70251876Speter if (!apr_isupper(d)) 71251876Speter return 0; 72251876Speter break; 73251876Speter case '$': 74251876Speter if (!apr_islower(d)) 75251876Speter return 0; 76251876Speter break; 77251876Speter case '#': 78251876Speter if (!apr_isdigit(d)) 79251876Speter return 0; 80251876Speter break; 81251876Speter case '&': 82251876Speter if (!apr_isxdigit(d)) 83251876Speter return 0; 84251876Speter break; 85251876Speter case '~': 86251876Speter if ((d != ' ') && !apr_isdigit(d)) 87251876Speter return 0; 88251876Speter break; 89251876Speter default: 90251876Speter if (mask[i] != d) 91251876Speter return 0; 92251876Speter break; 93251876Speter } 94251876Speter } 95251876Speter return 0; /* We only get here if mask is corrupted (exceeds 256) */ 96251876Speter} 97251876Speter 98251876Speter/* 99251876Speter * Parses an HTTP date in one of three standard forms: 100251876Speter * 101251876Speter * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 102251876Speter * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 103251876Speter * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format 104251876Speter * 105251876Speter * and returns the apr_time_t number of microseconds since 1 Jan 1970 GMT, 106251876Speter * or APR_DATE_BAD if this would be out of range or if the date is invalid. 107251876Speter * 108251876Speter * The restricted HTTP syntax is 109251876Speter * 110251876Speter * HTTP-date = rfc1123-date | rfc850-date | asctime-date 111251876Speter * 112251876Speter * rfc1123-date = wkday "," SP date1 SP time SP "GMT" 113251876Speter * rfc850-date = weekday "," SP date2 SP time SP "GMT" 114251876Speter * asctime-date = wkday SP date3 SP time SP 4DIGIT 115251876Speter * 116251876Speter * date1 = 2DIGIT SP month SP 4DIGIT 117251876Speter * ; day month year (e.g., 02 Jun 1982) 118251876Speter * date2 = 2DIGIT "-" month "-" 2DIGIT 119251876Speter * ; day-month-year (e.g., 02-Jun-82) 120251876Speter * date3 = month SP ( 2DIGIT | ( SP 1DIGIT )) 121251876Speter * ; month day (e.g., Jun 2) 122251876Speter * 123251876Speter * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT 124251876Speter * ; 00:00:00 - 23:59:59 125251876Speter * 126251876Speter * wkday = "Mon" | "Tue" | "Wed" 127251876Speter * | "Thu" | "Fri" | "Sat" | "Sun" 128251876Speter * 129251876Speter * weekday = "Monday" | "Tuesday" | "Wednesday" 130251876Speter * | "Thursday" | "Friday" | "Saturday" | "Sunday" 131251876Speter * 132251876Speter * month = "Jan" | "Feb" | "Mar" | "Apr" 133251876Speter * | "May" | "Jun" | "Jul" | "Aug" 134251876Speter * | "Sep" | "Oct" | "Nov" | "Dec" 135251876Speter * 136251876Speter * However, for the sake of robustness (and Netscapeness), we ignore the 137251876Speter * weekday and anything after the time field (including the timezone). 138251876Speter * 139251876Speter * This routine is intended to be very fast; 10x faster than using sscanf. 140251876Speter * 141251876Speter * Originally from Andrew Daviel <andrew@vancouver-webpages.com>, 29 Jul 96 142251876Speter * but many changes since then. 143251876Speter * 144251876Speter */ 145251876SpeterAPU_DECLARE(apr_time_t) apr_date_parse_http(const char *date) 146251876Speter{ 147251876Speter apr_time_exp_t ds; 148251876Speter apr_time_t result; 149251876Speter int mint, mon; 150251876Speter const char *monstr, *timstr; 151251876Speter static const int months[12] = 152251876Speter { 153251876Speter ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b', 154251876Speter ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r', 155251876Speter ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n', 156251876Speter ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g', 157251876Speter ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't', 158251876Speter ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c'}; 159251876Speter 160251876Speter if (!date) 161251876Speter return APR_DATE_BAD; 162251876Speter 163251876Speter while (*date && apr_isspace(*date)) /* Find first non-whitespace char */ 164251876Speter ++date; 165251876Speter 166251876Speter if (*date == '\0') 167251876Speter return APR_DATE_BAD; 168251876Speter 169251876Speter if ((date = strchr(date, ' ')) == NULL) /* Find space after weekday */ 170251876Speter return APR_DATE_BAD; 171251876Speter 172251876Speter ++date; /* Now pointing to first char after space, which should be */ 173251876Speter 174251876Speter /* start of the actual date information for all 4 formats. */ 175251876Speter 176251876Speter if (apr_date_checkmask(date, "## @$$ #### ##:##:## *")) { 177251876Speter /* RFC 1123 format with two days */ 178251876Speter ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100; 179251876Speter if (ds.tm_year < 0) 180251876Speter return APR_DATE_BAD; 181251876Speter 182251876Speter ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0'); 183251876Speter 184251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 185251876Speter 186251876Speter monstr = date + 3; 187251876Speter timstr = date + 12; 188251876Speter } 189251876Speter else if (apr_date_checkmask(date, "##-@$$-## ##:##:## *")) { 190251876Speter /* RFC 850 format */ 191251876Speter ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); 192251876Speter if (ds.tm_year < 70) 193251876Speter ds.tm_year += 100; 194251876Speter 195251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 196251876Speter 197251876Speter monstr = date + 3; 198251876Speter timstr = date + 10; 199251876Speter } 200251876Speter else if (apr_date_checkmask(date, "@$$ ~# ##:##:## ####*")) { 201251876Speter /* asctime format */ 202251876Speter ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100; 203251876Speter if (ds.tm_year < 0) 204251876Speter return APR_DATE_BAD; 205251876Speter 206251876Speter ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0'); 207251876Speter 208251876Speter if (date[4] == ' ') 209251876Speter ds.tm_mday = 0; 210251876Speter else 211251876Speter ds.tm_mday = (date[4] - '0') * 10; 212251876Speter 213251876Speter ds.tm_mday += (date[5] - '0'); 214251876Speter 215251876Speter monstr = date; 216251876Speter timstr = date + 7; 217251876Speter } 218251876Speter else if (apr_date_checkmask(date, "# @$$ #### ##:##:## *")) { 219251876Speter /* RFC 1123 format with one day */ 220251876Speter ds.tm_year = ((date[6] - '0') * 10 + (date[7] - '0') - 19) * 100; 221251876Speter if (ds.tm_year < 0) 222251876Speter return APR_DATE_BAD; 223251876Speter 224251876Speter ds.tm_year += ((date[8] - '0') * 10) + (date[9] - '0'); 225251876Speter 226251876Speter ds.tm_mday = (date[0] - '0'); 227251876Speter 228251876Speter monstr = date + 2; 229251876Speter timstr = date + 11; 230251876Speter } 231251876Speter else 232251876Speter return APR_DATE_BAD; 233251876Speter 234251876Speter if (ds.tm_mday <= 0 || ds.tm_mday > 31) 235251876Speter return APR_DATE_BAD; 236251876Speter 237251876Speter ds.tm_hour = ((timstr[0] - '0') * 10) + (timstr[1] - '0'); 238251876Speter ds.tm_min = ((timstr[3] - '0') * 10) + (timstr[4] - '0'); 239251876Speter ds.tm_sec = ((timstr[6] - '0') * 10) + (timstr[7] - '0'); 240251876Speter 241251876Speter if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61)) 242251876Speter return APR_DATE_BAD; 243251876Speter 244251876Speter mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2]; 245251876Speter for (mon = 0; mon < 12; mon++) 246251876Speter if (mint == months[mon]) 247251876Speter break; 248251876Speter 249251876Speter if (mon == 12) 250251876Speter return APR_DATE_BAD; 251251876Speter 252251876Speter if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10)) 253251876Speter return APR_DATE_BAD; 254251876Speter 255251876Speter /* February gets special check for leapyear */ 256251876Speter if ((mon == 1) && 257251876Speter ((ds.tm_mday > 29) || 258251876Speter ((ds.tm_mday == 29) 259251876Speter && ((ds.tm_year & 3) 260251876Speter || (((ds.tm_year % 100) == 0) 261251876Speter && (((ds.tm_year % 400) != 100))))))) 262251876Speter return APR_DATE_BAD; 263251876Speter 264251876Speter ds.tm_mon = mon; 265251876Speter 266251876Speter /* ap_mplode_time uses tm_usec and tm_gmtoff fields, but they haven't 267251876Speter * been set yet. 268251876Speter * It should be safe to just zero out these values. 269251876Speter * tm_usec is the number of microseconds into the second. HTTP only 270251876Speter * cares about second granularity. 271251876Speter * tm_gmtoff is the number of seconds off of GMT the time is. By 272251876Speter * definition all times going through this function are in GMT, so this 273251876Speter * is zero. 274251876Speter */ 275251876Speter ds.tm_usec = 0; 276251876Speter ds.tm_gmtoff = 0; 277251876Speter if (apr_time_exp_get(&result, &ds) != APR_SUCCESS) 278251876Speter return APR_DATE_BAD; 279251876Speter 280251876Speter return result; 281251876Speter} 282251876Speter 283251876Speter/* 284251876Speter * Parses a string resembling an RFC 822 date. This is meant to be 285251876Speter * leinent in its parsing of dates. Hence, this will parse a wider 286251876Speter * range of dates than apr_date_parse_http. 287251876Speter * 288251876Speter * The prominent mailer (or poster, if mailer is unknown) that has 289251876Speter * been seen in the wild is included for the unknown formats. 290251876Speter * 291251876Speter * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 292251876Speter * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 293251876Speter * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format 294251876Speter * Sun, 6 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 295251876Speter * Sun, 06 Nov 94 08:49:37 GMT ; RFC 822 296251876Speter * Sun, 6 Nov 94 08:49:37 GMT ; RFC 822 297251876Speter * Sun, 06 Nov 94 08:49 GMT ; Unknown [drtr@ast.cam.ac.uk] 298251876Speter * Sun, 6 Nov 94 08:49 GMT ; Unknown [drtr@ast.cam.ac.uk] 299251876Speter * Sun, 06 Nov 94 8:49:37 GMT ; Unknown [Elm 70.85] 300251876Speter * Sun, 6 Nov 94 8:49:37 GMT ; Unknown [Elm 70.85] 301251876Speter * Mon, 7 Jan 2002 07:21:22 GMT ; Unknown [Postfix] 302251876Speter * Sun, 06-Nov-1994 08:49:37 GMT ; RFC 850 with four digit years 303251876Speter * 304251876Speter */ 305251876Speter 306251876Speter#define TIMEPARSE(ds,hr10,hr1,min10,min1,sec10,sec1) \ 307251876Speter { \ 308251876Speter ds.tm_hour = ((hr10 - '0') * 10) + (hr1 - '0'); \ 309251876Speter ds.tm_min = ((min10 - '0') * 10) + (min1 - '0'); \ 310251876Speter ds.tm_sec = ((sec10 - '0') * 10) + (sec1 - '0'); \ 311251876Speter } 312251876Speter#define TIMEPARSE_STD(ds,timstr) \ 313251876Speter { \ 314251876Speter TIMEPARSE(ds, timstr[0],timstr[1], \ 315251876Speter timstr[3],timstr[4], \ 316251876Speter timstr[6],timstr[7]); \ 317251876Speter } 318251876Speter 319251876SpeterAPU_DECLARE(apr_time_t) apr_date_parse_rfc(const char *date) 320251876Speter{ 321251876Speter apr_time_exp_t ds; 322251876Speter apr_time_t result; 323251876Speter int mint, mon; 324251876Speter const char *monstr, *timstr, *gmtstr; 325251876Speter static const int months[12] = 326251876Speter { 327251876Speter ('J' << 16) | ('a' << 8) | 'n', ('F' << 16) | ('e' << 8) | 'b', 328251876Speter ('M' << 16) | ('a' << 8) | 'r', ('A' << 16) | ('p' << 8) | 'r', 329251876Speter ('M' << 16) | ('a' << 8) | 'y', ('J' << 16) | ('u' << 8) | 'n', 330251876Speter ('J' << 16) | ('u' << 8) | 'l', ('A' << 16) | ('u' << 8) | 'g', 331251876Speter ('S' << 16) | ('e' << 8) | 'p', ('O' << 16) | ('c' << 8) | 't', 332251876Speter ('N' << 16) | ('o' << 8) | 'v', ('D' << 16) | ('e' << 8) | 'c' }; 333251876Speter 334251876Speter if (!date) 335251876Speter return APR_DATE_BAD; 336251876Speter 337251876Speter /* Not all dates have text days at the beginning. */ 338251876Speter if (!apr_isdigit(date[0])) 339251876Speter { 340251876Speter while (*date && apr_isspace(*date)) /* Find first non-whitespace char */ 341251876Speter ++date; 342251876Speter 343251876Speter if (*date == '\0') 344251876Speter return APR_DATE_BAD; 345251876Speter 346251876Speter if ((date = strchr(date, ' ')) == NULL) /* Find space after weekday */ 347251876Speter return APR_DATE_BAD; 348251876Speter 349251876Speter ++date; /* Now pointing to first char after space, which should be */ } 350251876Speter 351251876Speter /* start of the actual date information for all 11 formats. */ 352251876Speter if (apr_date_checkmask(date, "## @$$ #### ##:##:## *")) { /* RFC 1123 format */ 353251876Speter ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100; 354251876Speter 355251876Speter if (ds.tm_year < 0) 356251876Speter return APR_DATE_BAD; 357251876Speter 358251876Speter ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0'); 359251876Speter 360251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 361251876Speter 362251876Speter monstr = date + 3; 363251876Speter timstr = date + 12; 364251876Speter gmtstr = date + 21; 365251876Speter 366251876Speter TIMEPARSE_STD(ds, timstr); 367251876Speter } 368251876Speter else if (apr_date_checkmask(date, "##-@$$-## ##:##:## *")) {/* RFC 850 format */ 369251876Speter ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); 370251876Speter 371251876Speter if (ds.tm_year < 70) 372251876Speter ds.tm_year += 100; 373251876Speter 374251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 375251876Speter 376251876Speter monstr = date + 3; 377251876Speter timstr = date + 10; 378251876Speter gmtstr = date + 19; 379251876Speter 380251876Speter TIMEPARSE_STD(ds, timstr); 381251876Speter } 382251876Speter else if (apr_date_checkmask(date, "@$$ ~# ##:##:## ####*")) { 383251876Speter /* asctime format */ 384251876Speter ds.tm_year = ((date[16] - '0') * 10 + (date[17] - '0') - 19) * 100; 385251876Speter if (ds.tm_year < 0) 386251876Speter return APR_DATE_BAD; 387251876Speter 388251876Speter ds.tm_year += ((date[18] - '0') * 10) + (date[19] - '0'); 389251876Speter 390251876Speter if (date[4] == ' ') 391251876Speter ds.tm_mday = 0; 392251876Speter else 393251876Speter ds.tm_mday = (date[4] - '0') * 10; 394251876Speter 395251876Speter ds.tm_mday += (date[5] - '0'); 396251876Speter 397251876Speter monstr = date; 398251876Speter timstr = date + 7; 399251876Speter gmtstr = NULL; 400251876Speter 401251876Speter TIMEPARSE_STD(ds, timstr); 402251876Speter } 403251876Speter else if (apr_date_checkmask(date, "# @$$ #### ##:##:## *")) { 404251876Speter /* RFC 1123 format*/ 405251876Speter ds.tm_year = ((date[6] - '0') * 10 + (date[7] - '0') - 19) * 100; 406251876Speter 407251876Speter if (ds.tm_year < 0) 408251876Speter return APR_DATE_BAD; 409251876Speter 410251876Speter ds.tm_year += ((date[8] - '0') * 10) + (date[9] - '0'); 411251876Speter ds.tm_mday = (date[0] - '0'); 412251876Speter 413251876Speter monstr = date + 2; 414251876Speter timstr = date + 11; 415251876Speter gmtstr = date + 20; 416251876Speter 417251876Speter TIMEPARSE_STD(ds, timstr); 418251876Speter } 419251876Speter else if (apr_date_checkmask(date, "## @$$ ## ##:##:## *")) { 420251876Speter /* This is the old RFC 1123 date format - many many years ago, people 421251876Speter * used two-digit years. Oh, how foolish. 422251876Speter * 423251876Speter * Two-digit day, two-digit year version. */ 424251876Speter ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); 425251876Speter 426251876Speter if (ds.tm_year < 70) 427251876Speter ds.tm_year += 100; 428251876Speter 429251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 430251876Speter 431251876Speter monstr = date + 3; 432251876Speter timstr = date + 10; 433251876Speter gmtstr = date + 19; 434251876Speter 435251876Speter TIMEPARSE_STD(ds, timstr); 436251876Speter } 437251876Speter else if (apr_date_checkmask(date, " # @$$ ## ##:##:## *")) { 438251876Speter /* This is the old RFC 1123 date format - many many years ago, people 439251876Speter * used two-digit years. Oh, how foolish. 440251876Speter * 441251876Speter * Space + one-digit day, two-digit year version.*/ 442251876Speter ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); 443251876Speter 444251876Speter if (ds.tm_year < 70) 445251876Speter ds.tm_year += 100; 446251876Speter 447251876Speter ds.tm_mday = (date[1] - '0'); 448251876Speter 449251876Speter monstr = date + 3; 450251876Speter timstr = date + 10; 451251876Speter gmtstr = date + 19; 452251876Speter 453251876Speter TIMEPARSE_STD(ds, timstr); 454251876Speter } 455251876Speter else if (apr_date_checkmask(date, "# @$$ ## ##:##:## *")) { 456251876Speter /* This is the old RFC 1123 date format - many many years ago, people 457251876Speter * used two-digit years. Oh, how foolish. 458251876Speter * 459251876Speter * One-digit day, two-digit year version. */ 460251876Speter ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0'); 461251876Speter 462251876Speter if (ds.tm_year < 70) 463251876Speter ds.tm_year += 100; 464251876Speter 465251876Speter ds.tm_mday = (date[0] - '0'); 466251876Speter 467251876Speter monstr = date + 2; 468251876Speter timstr = date + 9; 469251876Speter gmtstr = date + 18; 470251876Speter 471251876Speter TIMEPARSE_STD(ds, timstr); 472251876Speter } 473251876Speter else if (apr_date_checkmask(date, "## @$$ ## ##:## *")) { 474251876Speter /* Loser format. This is quite bogus. */ 475251876Speter ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); 476251876Speter 477251876Speter if (ds.tm_year < 70) 478251876Speter ds.tm_year += 100; 479251876Speter 480251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 481251876Speter 482251876Speter monstr = date + 3; 483251876Speter timstr = date + 10; 484251876Speter gmtstr = NULL; 485251876Speter 486251876Speter TIMEPARSE(ds, timstr[0],timstr[1], timstr[3],timstr[4], '0','0'); 487251876Speter } 488251876Speter else if (apr_date_checkmask(date, "# @$$ ## ##:## *")) { 489251876Speter /* Loser format. This is quite bogus. */ 490251876Speter ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0'); 491251876Speter 492251876Speter if (ds.tm_year < 70) 493251876Speter ds.tm_year += 100; 494251876Speter 495251876Speter ds.tm_mday = (date[0] - '0'); 496251876Speter 497251876Speter monstr = date + 2; 498251876Speter timstr = date + 9; 499251876Speter gmtstr = NULL; 500251876Speter 501251876Speter TIMEPARSE(ds, timstr[0],timstr[1], timstr[3],timstr[4], '0','0'); 502251876Speter } 503251876Speter else if (apr_date_checkmask(date, "## @$$ ## #:##:## *")) { 504251876Speter /* Loser format. This is quite bogus. */ 505251876Speter ds.tm_year = ((date[7] - '0') * 10) + (date[8] - '0'); 506251876Speter 507251876Speter if (ds.tm_year < 70) 508251876Speter ds.tm_year += 100; 509251876Speter 510251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 511251876Speter 512251876Speter monstr = date + 3; 513251876Speter timstr = date + 9; 514251876Speter gmtstr = date + 18; 515251876Speter 516251876Speter TIMEPARSE(ds, '0',timstr[1], timstr[3],timstr[4], timstr[6],timstr[7]); 517251876Speter } 518251876Speter else if (apr_date_checkmask(date, "# @$$ ## #:##:## *")) { 519251876Speter /* Loser format. This is quite bogus. */ 520251876Speter ds.tm_year = ((date[6] - '0') * 10) + (date[7] - '0'); 521251876Speter 522251876Speter if (ds.tm_year < 70) 523251876Speter ds.tm_year += 100; 524251876Speter 525251876Speter ds.tm_mday = (date[0] - '0'); 526251876Speter 527251876Speter monstr = date + 2; 528251876Speter timstr = date + 8; 529251876Speter gmtstr = date + 17; 530251876Speter 531251876Speter TIMEPARSE(ds, '0',timstr[1], timstr[3],timstr[4], timstr[6],timstr[7]); 532251876Speter } 533251876Speter else if (apr_date_checkmask(date, " # @$$ #### ##:##:## *")) { 534251876Speter /* RFC 1123 format with a space instead of a leading zero. */ 535251876Speter ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100; 536251876Speter 537251876Speter if (ds.tm_year < 0) 538251876Speter return APR_DATE_BAD; 539251876Speter 540251876Speter ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0'); 541251876Speter 542251876Speter ds.tm_mday = (date[1] - '0'); 543251876Speter 544251876Speter monstr = date + 3; 545251876Speter timstr = date + 12; 546251876Speter gmtstr = date + 21; 547251876Speter 548251876Speter TIMEPARSE_STD(ds, timstr); 549251876Speter } 550251876Speter else if (apr_date_checkmask(date, "##-@$$-#### ##:##:## *")) { 551251876Speter /* RFC 1123 with dashes instead of spaces between date/month/year 552251876Speter * This also looks like RFC 850 with four digit years. 553251876Speter */ 554251876Speter ds.tm_year = ((date[7] - '0') * 10 + (date[8] - '0') - 19) * 100; 555251876Speter if (ds.tm_year < 0) 556251876Speter return APR_DATE_BAD; 557251876Speter 558251876Speter ds.tm_year += ((date[9] - '0') * 10) + (date[10] - '0'); 559251876Speter 560251876Speter ds.tm_mday = ((date[0] - '0') * 10) + (date[1] - '0'); 561251876Speter 562251876Speter monstr = date + 3; 563251876Speter timstr = date + 12; 564251876Speter gmtstr = date + 21; 565251876Speter 566251876Speter TIMEPARSE_STD(ds, timstr); 567251876Speter } 568251876Speter else 569251876Speter return APR_DATE_BAD; 570251876Speter 571251876Speter if (ds.tm_mday <= 0 || ds.tm_mday > 31) 572251876Speter return APR_DATE_BAD; 573251876Speter 574251876Speter if ((ds.tm_hour > 23) || (ds.tm_min > 59) || (ds.tm_sec > 61)) 575251876Speter return APR_DATE_BAD; 576251876Speter 577251876Speter mint = (monstr[0] << 16) | (monstr[1] << 8) | monstr[2]; 578251876Speter for (mon = 0; mon < 12; mon++) 579251876Speter if (mint == months[mon]) 580251876Speter break; 581251876Speter 582251876Speter if (mon == 12) 583251876Speter return APR_DATE_BAD; 584251876Speter 585251876Speter if ((ds.tm_mday == 31) && (mon == 3 || mon == 5 || mon == 8 || mon == 10)) 586251876Speter return APR_DATE_BAD; 587251876Speter 588251876Speter /* February gets special check for leapyear */ 589251876Speter 590251876Speter if ((mon == 1) && 591251876Speter ((ds.tm_mday > 29) 592251876Speter || ((ds.tm_mday == 29) 593251876Speter && ((ds.tm_year & 3) 594251876Speter || (((ds.tm_year % 100) == 0) 595251876Speter && (((ds.tm_year % 400) != 100))))))) 596251876Speter return APR_DATE_BAD; 597251876Speter 598251876Speter ds.tm_mon = mon; 599251876Speter 600251876Speter /* tm_gmtoff is the number of seconds off of GMT the time is. 601251876Speter * 602251876Speter * We only currently support: [+-]ZZZZ where Z is the offset in 603251876Speter * hours from GMT. 604251876Speter * 605251876Speter * If there is any confusion, tm_gmtoff will remain 0. 606251876Speter */ 607251876Speter ds.tm_gmtoff = 0; 608251876Speter 609251876Speter /* Do we have a timezone ? */ 610251876Speter if (gmtstr) { 611251876Speter int offset; 612251876Speter switch (*gmtstr) { 613251876Speter case '-': 614251876Speter offset = atoi(gmtstr+1); 615251876Speter ds.tm_gmtoff -= (offset / 100) * 60 * 60; 616251876Speter ds.tm_gmtoff -= (offset % 100) * 60; 617251876Speter break; 618251876Speter case '+': 619251876Speter offset = atoi(gmtstr+1); 620251876Speter ds.tm_gmtoff += (offset / 100) * 60 * 60; 621251876Speter ds.tm_gmtoff += (offset % 100) * 60; 622251876Speter break; 623251876Speter } 624251876Speter } 625251876Speter 626251876Speter /* apr_time_exp_get uses tm_usec field, but it hasn't been set yet. 627251876Speter * It should be safe to just zero out this value. 628251876Speter * tm_usec is the number of microseconds into the second. HTTP only 629251876Speter * cares about second granularity. 630251876Speter */ 631251876Speter ds.tm_usec = 0; 632251876Speter 633251876Speter if (apr_time_exp_gmt_get(&result, &ds) != APR_SUCCESS) 634251876Speter return APR_DATE_BAD; 635251876Speter 636251876Speter return result; 637251876Speter} 638