xref: /haiku/src/kits/network/libnetservices/HttpTime.cpp (revision b5efad4eae1a0754ce3e4eb87458279e81f61f61)
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 
16 
17 // The formats used should be, in order of preference (according to RFC2616,
18 // section 3.3):
19 // RFC1123 / RFC822: "Sun, 06 Nov 1994 08:49:37 GMT"
20 // RFC1036 / RFC850: "Sunday, 06-Nov-94 08:49:37 GMT"
21 // asctime         : "Sun Nov  6 08:49:37 1994"
22 //
23 // RFC1123 is the preferred one because it has 4 digit years.
24 //
25 // But of course in real life, all possible mixes of the formats are used.
26 // Believe it or not, it's even possible to find some website that gets this
27 // right and use one of the 3 formats above.
28 // Often seen variants are:
29 // - RFC1036 but with 4 digit year,
30 // - Missing or different timezone indicator
31 // - Invalid weekday
32 static const char* kDateFormats[] = {
33 	// RFC1123
34 	"%a, %d %b %Y %H:%M:%S",     // without timezone
35 	"%a, %d %b %Y %H:%M:%S GMT", // canonical
36 
37 	// RFC1036
38 	"%A, %d-%b-%y %H:%M:%S",     // without timezone
39 	"%A, %d-%b-%y %H:%M:%S GMT", // canonical
40 
41 	// RFC1036 with 4 digit year
42 	"%a, %d-%b-%Y %H:%M:%S",     // without timezone
43 	"%a, %d-%b-%Y %H:%M:%S GMT", // with 4-digit year
44 	"%a, %d-%b-%Y %H:%M:%S UTC", // "UTC" timezone
45 
46 	// asctime
47 	"%a %d %b %H:%M:%S %Y"
48 };
49 
50 #ifdef LIBNETAPI_DEPRECATED
51 using namespace BPrivate;
52 #else
53 using namespace BPrivate::Network;
54 #endif
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 	fDateFormat = B_HTTP_TIME_FORMAT_PARSED;
113 	unsigned int i;
114 	for (i = 0; i < sizeof(kDateFormats) / sizeof(const char*);
115 		i++) {
116 		const char* result = strptime(fDateString.String(), kDateFormats[i],
117 			&expireTime);
118 
119 		// We need to parse the complete value for the "Expires" key.
120 		// Otherwise, we consider this to be a session cookie (or try another
121 		// one of the date formats).
122 		if (result == fDateString.String() + fDateString.Length()) {
123 			fDateFormat = i;
124 			break;
125 		}
126 	}
127 
128 	// Did we identify some valid format?
129 	if (fDateFormat == B_HTTP_TIME_FORMAT_PARSED)
130 		return 0;
131 
132 	// Now convert the struct tm from strptime into a BDateTime.
133 	BTime time(expireTime.tm_hour, expireTime.tm_min, expireTime.tm_sec);
134 	BDate date(expireTime.tm_year + 1900, expireTime.tm_mon + 1,
135 		expireTime.tm_mday);
136 	BDateTime dateTime(date, time);
137 	return dateTime;
138 }
139 
140 
141 BString
142 BHttpTime::ToString(int8 format)
143 {
144 	BString expirationFinal;
145 	struct tm expirationTm;
146 	expirationTm.tm_sec = fDate.Time().Second();
147 	expirationTm.tm_min = fDate.Time().Minute();
148 	expirationTm.tm_hour = fDate.Time().Hour();
149 	expirationTm.tm_mday = fDate.Date().Day();
150 	expirationTm.tm_mon = fDate.Date().Month() - 1;
151 	expirationTm.tm_year = fDate.Date().Year() - 1900;
152 	// strftime starts weekday count at 0 for Sunday,
153 	// while DayOfWeek starts at 1 for Monday and thus uses 7 for Sunday
154 	expirationTm.tm_wday = fDate.Date().DayOfWeek() % 7;
155 	expirationTm.tm_yday = 0;
156 	expirationTm.tm_isdst = 0;
157 
158 	if (format == B_HTTP_TIME_FORMAT_PARSED)
159 		format = fDateFormat;
160 
161 	if (format != B_HTTP_TIME_FORMAT_PARSED) {
162 		static const uint16 kTimetToStringMaxLength = 128;
163 		char expirationString[kTimetToStringMaxLength + 1];
164 		size_t strLength;
165 
166 		strLength = strftime(expirationString, kTimetToStringMaxLength,
167 			kDateFormats[format], &expirationTm);
168 
169 		expirationFinal.SetTo(expirationString, strLength);
170 	}
171 	return expirationFinal;
172 }
173