Skip to content

Commit b877dc4

Browse files
Make Date header parsing locale-independent (#1663)
Parse RFC1123 Date headers with strptime_l using the classic locale and a literal GMT token, and trim header whitespace before parsing. Treat invalid/partial parses as missing timestamps and remove the extra GMT offset correction now that UTC parsing is explicit. Relates-To: HERESUP-57013 Signed-off-by: Mykhailo Diachenko <ext-mykhailo.z.diachenko@here.com> Signed-off-by: Rustam Gamidov <ext-rustam.gamidov@here.com>
1 parent df55ff5 commit b877dc4

File tree

1 file changed

+33
-10
lines changed

1 file changed

+33
-10
lines changed

olp-cpp-sdk-authentication/src/AuthenticationClientUtils.cpp

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <chrono>
2323
#include <iomanip>
24+
#include <locale>
2425
#include <sstream>
2526
#include <utility>
2627

@@ -108,18 +109,37 @@ std::time_t ParseTime(const std::string& value) {
108109

109110
#else
110111

112+
std::string TrimDateHeaderValue(const std::string& value) {
113+
const auto begin = value.find_first_not_of(" \t\r\n");
114+
if (begin == std::string::npos) {
115+
return {};
116+
}
117+
const auto end = value.find_last_not_of(" \t\r\n");
118+
return value.substr(begin, end - begin + 1);
119+
}
120+
111121
std::time_t ParseTime(const std::string& value) {
112122
std::tm tm = {};
113-
const auto format = "%a, %d %b %Y %H:%M:%S %Z";
114-
const auto parsed_until = ::strptime(value.c_str(), format, &tm);
115-
if (parsed_until != value.c_str() + value.size()) {
116-
OLP_SDK_LOG_WARNING(kLogTag, "Timestamp is not fully parsed" << value);
123+
const auto trimmed_value = TrimDateHeaderValue(value);
124+
125+
// Use a C locale to keep RFC1123 parsing locale-independent.
126+
// Literal "GMT" avoids platform-specific %Z behaviour.
127+
locale_t c_locale = newlocale(LC_ALL_MASK, "C", (locale_t)0);
128+
if (c_locale == (locale_t)0) {
129+
OLP_SDK_LOG_WARNING(kLogTag, "Failed to create C locale");
130+
return static_cast<std::time_t>(-1);
131+
}
132+
133+
const auto parsed_until = ::strptime_l(
134+
trimmed_value.c_str(), "%a, %d %b %Y %H:%M:%S GMT", &tm, c_locale);
135+
freelocale(c_locale);
136+
137+
if (parsed_until != trimmed_value.c_str() + trimmed_value.size()) {
138+
OLP_SDK_LOG_WARNING(kLogTag, "Timestamp is not fully parsed " << value);
139+
return static_cast<std::time_t>(-1);
117140
}
118-
// MacOS updates `tm_isdst`, `tm_zone` and `tm_gmtoff` fields in `timegm`
119-
// call.
120-
const auto gmtoff = tm.tm_gmtoff;
121-
const auto local_time = timegm(&tm);
122-
return local_time - gmtoff;
141+
142+
return timegm(&tm);
123143
}
124144

125145
#endif
@@ -133,7 +153,10 @@ porting::optional<std::time_t> GetTimestampFromHeaders(
133153
obg.first, kDate);
134154
});
135155
if (it != end(headers)) {
136-
return ParseTime(it->second);
156+
const auto timestamp = ParseTime(it->second);
157+
if (timestamp != static_cast<std::time_t>(-1)) {
158+
return timestamp;
159+
}
137160
}
138161
return porting::none;
139162
}

0 commit comments

Comments
 (0)