1 /* 2 * Copyright 2010-2016 Haiku Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Christophe Huriaux, c.huriaux@gmail.com 7 * Adrien Destugues, pulkomandy@gmail.com 8 */ 9 10 #include <HttpTime.h> 11 12 #include <new> 13 14 #include <cstdio> 15 #include <locale.h> 16 17 using namespace BPrivate::Network; 18 19 20 // The formats used should be, in order of preference (according to RFC2616, 21 // section 3.3): 22 // RFC1123 / RFC822: "Sun, 06 Nov 1994 08:49:37 GMT" 23 // RFC1036 / RFC850: "Sunday, 06-Nov-94 08:49:37 GMT" 24 // asctime : "Sun Nov 6 08:49:37 1994" 25 // 26 // RFC1123 is the preferred one because it has 4 digit years. 27 // 28 // But of course in real life, all possible mixes of the formats are used. 29 // Believe it or not, it's even possible to find some website that gets this 30 // right and use one of the 3 formats above. 31 // Often seen variants are: 32 // - RFC1036 but with 4 digit year, 33 // - Missing or different timezone indicator 34 // - Invalid weekday 35 static const char* kDateFormats[] = { 36 // RFC1123 37 "%a, %d %b %Y %H:%M:%S", // without timezone 38 "%a, %d %b %Y %H:%M:%S GMT", // canonical 39 40 // RFC1036 41 "%A, %d-%b-%y %H:%M:%S", // without timezone 42 "%A, %d-%b-%y %H:%M:%S GMT", // canonical 43 44 // RFC1036 with 4 digit year 45 "%a, %d-%b-%Y %H:%M:%S", // without timezone 46 "%a, %d-%b-%Y %H:%M:%S GMT", // with 4-digit year 47 "%a, %d-%b-%Y %H:%M:%S UTC", // "UTC" timezone 48 49 // asctime 50 "%a %d %b %H:%M:%S %Y" 51 }; 52 53 54 static locale_t posix = newlocale(LC_ALL_MASK, "POSIX", (locale_t)0); 55 56 57 BHttpTime::BHttpTime() 58 : 59 fDate(0), 60 fDateFormat(B_HTTP_TIME_FORMAT_PREFERRED) 61 { 62 } 63 64 65 BHttpTime::BHttpTime(BDateTime date) 66 : 67 fDate(date), 68 fDateFormat(B_HTTP_TIME_FORMAT_PREFERRED) 69 { 70 } 71 72 73 BHttpTime::BHttpTime(const BString& dateString) 74 : 75 fDateString(dateString), 76 fDate(0), 77 fDateFormat(B_HTTP_TIME_FORMAT_PREFERRED) 78 { 79 } 80 81 82 // #pragma mark Date modification 83 84 85 void 86 BHttpTime::SetString(const BString& string) 87 { 88 fDateString = string; 89 } 90 91 92 void 93 BHttpTime::SetDate(BDateTime date) 94 { 95 fDate = date; 96 } 97 98 99 // #pragma mark Date conversion 100 101 102 BDateTime 103 BHttpTime::Parse() 104 { 105 struct tm expireTime; 106 107 if (fDateString.Length() < 4) 108 return 0; 109 110 memset(&expireTime, 0, sizeof(struct tm)); 111 112 // Save the current locale, switch to POSIX for strptime to match strings 113 // in English, switch back when we're done. 114 locale_t current = uselocale(posix); 115 116 fDateFormat = B_HTTP_TIME_FORMAT_PARSED; 117 unsigned int i; 118 for (i = 0; i < sizeof(kDateFormats) / sizeof(const char*); 119 i++) { 120 const char* result = strptime(fDateString.String(), kDateFormats[i], 121 &expireTime); 122 123 // We need to parse the complete value for the "Expires" key. 124 // Otherwise, we consider this to be a session cookie (or try another 125 // one of the date formats). 126 if (result == fDateString.String() + fDateString.Length()) { 127 fDateFormat = i; 128 break; 129 } 130 } 131 132 uselocale(current); 133 134 // Did we identify some valid format? 135 if (fDateFormat == B_HTTP_TIME_FORMAT_PARSED) 136 return 0; 137 138 // Now convert the struct tm from strptime into a BDateTime. 139 BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec); 140 BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1, 141 expireTime.tm_mday); 142 BDateTime dateTime(date, time); 143 return dateTime; 144 } 145 146 147 BString 148 BHttpTime::ToString(int8 format) 149 { 150 BString expirationFinal; 151 struct tm expirationTm; 152 expirationTm.tm_sec = fDate.Time().Second(); 153 expirationTm.tm_min = fDate.Time().Minute(); 154 expirationTm.tm_hour = fDate.Time().Hour(); 155 expirationTm.tm_mday = fDate.Date().Day(); 156 expirationTm.tm_mon = fDate.Date().Month() - 1; 157 expirationTm.tm_year = fDate.Date().Year() - 1900; 158 // strftime starts weekday count at 0 for Sunday, 159 // while DayOfWeek starts at 1 for Monday and thus uses 7 for Sunday 160 expirationTm.tm_wday = fDate.Date().DayOfWeek() % 7; 161 expirationTm.tm_yday = 0; 162 expirationTm.tm_isdst = 0; 163 164 if (format == B_HTTP_TIME_FORMAT_PARSED) 165 format = fDateFormat; 166 167 if (format != B_HTTP_TIME_FORMAT_PARSED) { 168 static const uint16 kTimetToStringMaxLength = 128; 169 char expirationString[kTimetToStringMaxLength + 1]; 170 size_t strLength; 171 172 strLength = strftime(expirationString, kTimetToStringMaxLength, 173 kDateFormats[format], &expirationTm); 174 175 expirationFinal.SetTo(expirationString, strLength); 176 } 177 return expirationFinal; 178 } 179