Skip to content

Commit 2a97da9

Browse files
Make Date header parsing locale-independent
Use boost::date_time to ensure cross platform compatibility. Supersedes the b877dc4 Relates-To: HERESUP-57013 Signed-off-by: Mykhailo Diachenko <ext-mykhailo.z.diachenko@here.com>
1 parent f5c0029 commit 2a97da9

File tree

2 files changed

+82
-22
lines changed

2 files changed

+82
-22
lines changed

external/boost/CMakeLists.txt.boost.in

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,16 @@ include(ExternalProject)
2424
ExternalProject_Add(boost-download
2525
GIT_REPOSITORY @OLP_SDK_CPP_BOOST_URL@
2626
GIT_TAG @OLP_SDK_CPP_BOOST_TAG@
27-
GIT_SUBMODULES libs/any
27+
GIT_SUBMODULES libs/algorithm
28+
libs/any
29+
libs/array
2830
libs/assert
31+
libs/concept_check
2932
libs/config
33+
libs/container
3034
libs/container_hash
3135
libs/core
36+
libs/date_time
3237
libs/detail
3338
libs/describe
3439
libs/format
@@ -37,6 +42,7 @@ ExternalProject_Add(boost-download
3742
libs/integer
3843
libs/io
3944
libs/iterator
45+
libs/lexical_cast
4046
libs/move
4147
libs/mpl
4248
libs/mp11
@@ -45,10 +51,12 @@ ExternalProject_Add(boost-download
4551
libs/predef
4652
libs/preprocessor
4753
libs/random
54+
libs/range
4855
libs/serialization
4956
libs/smart_ptr
5057
libs/static_assert
5158
libs/throw_exception
59+
libs/tokenizer
5260
libs/tti
5361
libs/type_index
5462
libs/type_traits

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

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
#include <rapidjson/istreamwrapper.h>
3131
#include <rapidjson/stringbuffer.h>
3232
#include <rapidjson/writer.h>
33+
#include <boost/date_time/gregorian/gregorian.hpp>
34+
#include <boost/date_time/posix_time/posix_time.hpp>
35+
#include <boost/date_time/time_facet.hpp>
3336
#include "Constants.h"
3437
#include "ResponseFromJsonBuilder.h"
3538
#include "olp/core/http/NetworkResponse.h"
@@ -59,6 +62,18 @@ constexpr auto kOauthSignatureMethod = "oauth_signature_method";
5962
constexpr auto kVersion = "1.0";
6063
constexpr auto kHmac = "HMAC-SHA256";
6164
constexpr auto kLogTag = "AuthenticationClientUtils";
65+
// %e: day with optional leading space/zero.
66+
// %H remains strict two-digit hour in input facet.
67+
constexpr auto kRfc1123GmtFormat = "%a, %e %b %Y %H:%M:%S GMT";
68+
69+
std::string TrimDateHeaderValue(const std::string& value) {
70+
const auto begin = value.find_first_not_of(" \t\r\n");
71+
if (begin == std::string::npos) {
72+
return {};
73+
}
74+
const auto end = value.find_last_not_of(" \t\r\n");
75+
return value.substr(begin, end - begin + 1);
76+
}
6277

6378
std::string Base64Encode(const Crypto::Sha256Digest& digest) {
6479
std::string ret = olp::utils::Base64Encode(digest.data(), digest.size());
@@ -109,37 +124,74 @@ std::time_t ParseTime(const std::string& value) {
109124

110125
#else
111126

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-
121127
std::time_t ParseTime(const std::string& value) {
122-
std::tm tm = {};
123128
const auto trimmed_value = TrimDateHeaderValue(value);
129+
if (trimmed_value.empty()) {
130+
OLP_SDK_LOG_WARNING_F(kLogTag,
131+
"Failed to parse Date header '%s': value is empty "
132+
"after trimming whitespace",
133+
value.c_str());
134+
return static_cast<std::time_t>(-1);
135+
}
136+
137+
std::istringstream stream(trimmed_value);
138+
139+
// Facet has internal counter, which is incremented by the std::locale.
140+
// When last locale pointing to the facet is destroyed, the counter is
141+
// decremented and the facet is destroyed.
142+
stream.imbue(
143+
std::locale(std::locale::classic(),
144+
new boost::posix_time::time_input_facet(kRfc1123GmtFormat)));
145+
146+
boost::posix_time::ptime parsed_time;
147+
stream >> parsed_time;
148+
if (stream.fail()) {
149+
OLP_SDK_LOG_WARNING_F(kLogTag,
150+
"Failed to parse Date header '%s': format mismatch "
151+
"for RFC1123 timestamp",
152+
value.c_str());
153+
return static_cast<std::time_t>(-1);
154+
}
124155

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");
156+
if (parsed_time.is_not_a_date_time()) {
157+
OLP_SDK_LOG_WARNING_F(kLogTag,
158+
"Failed to parse Date header '%s': parsed value is "
159+
"not a valid date/time",
160+
value.c_str());
130161
return static_cast<std::time_t>(-1);
131162
}
132163

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);
164+
stream >> std::ws;
165+
if (!stream.eof()) {
166+
OLP_SDK_LOG_WARNING_F(kLogTag,
167+
"Failed to parse Date header '%s': unexpected "
168+
"trailing characters after timestamp",
169+
value.c_str());
170+
return static_cast<std::time_t>(-1);
171+
}
172+
173+
const auto epoch =
174+
boost::posix_time::ptime(boost::gregorian::date(1970, 1, 1));
175+
if (parsed_time < epoch) {
176+
OLP_SDK_LOG_WARNING_F(
177+
kLogTag,
178+
"Failed to parse Date header '%s': timestamp is before Unix epoch",
179+
value.c_str());
180+
return static_cast<std::time_t>(-1);
181+
}
136182

137-
if (parsed_until != trimmed_value.c_str() + trimmed_value.size()) {
138-
OLP_SDK_LOG_WARNING(kLogTag, "Timestamp is not fully parsed " << value);
183+
const auto seconds_since_epoch = (parsed_time - epoch).total_seconds();
184+
using SecondsType = boost::remove_cv_t<decltype(seconds_since_epoch)>;
185+
if (seconds_since_epoch >
186+
static_cast<SecondsType>(std::numeric_limits<std::time_t>::max())) {
187+
OLP_SDK_LOG_WARNING_F(
188+
kLogTag,
189+
"Failed to parse Date header '%s': timestamp exceeds std::time_t range",
190+
value.c_str());
139191
return static_cast<std::time_t>(-1);
140192
}
141193

142-
return timegm(&tm);
194+
return static_cast<std::time_t>(seconds_since_epoch);
143195
}
144196

145197
#endif

0 commit comments

Comments
 (0)