xref: /haiku/src/kits/network/libnetservices/HttpTime.cpp (revision 4a55cc230cf7566cadcbb23b1928eefff8aea9a2)
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