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