Commit 1d742e01 authored by David Kalnischkies's avatar David Kalnischkies

avoid std::get_time usage to sidestep libstdc++6 bug

As reported upstream in
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71556
the implementation of std::get_time is currently not as accepting as
strptime is, especially in how hours should be formatted.

Just reverting 9febc2b2 would be
possible, but then we would reopen the problems fixed by it, so instead
I opted here for a rewrite of the parsing logic which makes this method
a lot longer, but at least it provides the same benefits as the rewrite
in std::get_time was intended to give us and decouples us from the fix
of the issue in the standard library implementation of GCC.

LP: 1593583
parent b90faf24
......@@ -867,7 +867,7 @@ bool ReadMessages(int Fd, vector<string> &List)
// ---------------------------------------------------------------------
/* This was lifted from the boa webserver which lifted it from 'wn-v1.07'
Made it a bit more robust with a few tolower_ascii though. */
static int MonthConv(char *Month)
static int MonthConv(char const * const Month)
{
switch (tolower_ascii(*Month))
{
......@@ -930,46 +930,87 @@ static time_t timegm(struct tm *t)
method used to ignore the timezone and assume always UTC. */
bool RFC1123StrToTime(const char* const str,time_t &time)
{
struct tm t;
auto const &posix = std::locale("C.UTF-8");
auto const parse_time = [&](char const * const s, bool const has_timezone) {
std::istringstream ss(str);
ss.imbue(posix);
ss >> std::get_time(&t, s);
if (has_timezone && ss.fail() == false)
{
std::string timezone;
ss >> timezone;
if (timezone.empty())
unsigned short day = 0;
signed int year = 0; // yes, Y23K problem – we gonna worry then…
std::string weekday, month, datespec, timespec, zone;
std::istringstream ss(str);
ss >> weekday;
// we only superficially check weekday, mostly to avoid accepting localized
// weekdays here and take only its length to decide which datetime format we
// encounter here. The date isn't stored.
std::transform(weekday.begin(), weekday.end(), weekday.begin(), ::tolower);
std::array<char const * const, 7> c_weekdays = {{ "sun", "mon", "tue", "wed", "thu", "fri", "sat" }};
if (std::find(c_weekdays.begin(), c_weekdays.end(), weekday.substr(0,3)) == c_weekdays.end())
return false;
switch (weekday.length())
{
case 4:
// Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
if (weekday[3] != ',')
return false;
ss >> day >> month >> year >> timespec >> zone;
break;
case 3:
// Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
ss >> month >> day >> timespec >> year;
zone = "UTC";
break;
case 0:
case 1:
case 2:
return false;
default:
// Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
if (weekday[weekday.length() - 1] != ',')
return false;
ss >> datespec >> timespec >> zone;
auto const expldate = VectorizeString(datespec, '-');
if (expldate.size() != 3)
return false;
try {
size_t pos;
day = std::stoi(expldate[0], &pos);
if (pos != expldate[0].length())
return false;
if (timezone != "GMT" && timezone != "UTC" && timezone != "Z") // RFC 822
{
// numeric timezones as a should of RFC 1123 and generally preferred
try {
size_t pos;
auto const zone = std::stoi(timezone, &pos);
if (zone != 0 || pos != timezone.length())
return false;
} catch (...) {
return false;
}
}
year = 1900 + std::stoi(expldate[2], &pos);
if (pos != expldate[2].length())
return false;
strprintf(datespec, "%.4d-%.2d-%.2d", year, MonthConv(expldate[1].c_str()) + 1, day);
} catch (...) {
return false;
}
t.tm_isdst = 0;
return ss.fail() == false;
};
break;
}
bool const good =
// Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
parse_time("%a, %d %b %Y %H:%M:%S", true) ||
// Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
parse_time("%A, %d-%b-%y %H:%M:%S", true) ||
// Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
parse_time("%c", false); // "%a %b %d %H:%M:%S %Y"
if (good == false)
if (ss.fail() || ss.bad() || !ss.eof())
return false;
time = timegm(&t);
if (zone != "GMT" && zone != "UTC" && zone != "Z") // RFC 822
{
// numeric timezones as a should of RFC 1123 and generally preferred
try {
size_t pos;
auto const z = std::stoi(zone, &pos);
if (z != 0 || pos != zone.length())
return false;
} catch (...) {
return false;
}
}
if (datespec.empty())
{
if (month.empty())
return false;
strprintf(datespec, "%.4d-%.2d-%.2d", year, MonthConv(month.c_str()) + 1, day);
}
std::string const datetime = datespec + ' ' + timespec;
struct tm Tm;
if (strptime(datetime.c_str(), "%Y-%m-%d %H:%M:%S", &Tm) == nullptr)
return false;
time = timegm(&Tm);
return true;
}
/*}}}*/
......
......@@ -253,6 +253,18 @@ TEST(StrUtilTest,RFC1123StrToTime)
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 GMT", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun, 6 Nov 1994 08:49:37 UTC", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun, 6 Nov 1994 08:49:37 UTC", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 8:49:37 UTC", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 UTC", t));
......@@ -269,16 +281,39 @@ TEST(StrUtilTest,RFC1123StrToTime)
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 GMT", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sunday, 6-Nov-94 08:49:37 GMT", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sunday, 06-Nov-94 8:49:37 GMT", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun Nov 6 08:49:37 1994", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun Nov 06 08:49:37 1994", t));
EXPECT_EQ(784111777, t);
} {
time_t t;
EXPECT_TRUE(RFC1123StrToTime("Sun Nov 6 8:49:37 1994", t));
EXPECT_EQ(784111777, t);
}
time_t t;
EXPECT_FALSE(RFC1123StrToTime("So, 06 Nov 1994 08:49:37 UTC", t));
EXPECT_FALSE(RFC1123StrToTime(", 06 Nov 1994 08:49:37 UTC", t));
EXPECT_FALSE(RFC1123StrToTime("Son, 06 Nov 1994 08:49:37 UTC", t));
EXPECT_FALSE(RFC1123StrToTime("Sun: 06 Nov 1994 08:49:37 UTC", t));
EXPECT_FALSE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37", t));
EXPECT_FALSE(RFC1123StrToTime("Sun, 06 Nov 1994 08:49:37 GMT+1", t));
EXPECT_FALSE(RFC1123StrToTime("Sun, 06 Nov 1994 GMT", t));
EXPECT_FALSE(RFC1123StrToTime("Sunday, 06 Nov 1994 GMT", t));
EXPECT_FALSE(RFC1123StrToTime("Sonntag, 06 Nov 1994 08:49:37 GMT", t));
EXPECT_FALSE(RFC1123StrToTime("domingo Nov 6 08:49:37 1994", t));
EXPECT_FALSE(RFC1123StrToTime("Sunday: 06-Nov-94 08:49:37 GMT", t));
EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 GMT+1", t));
EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 EDT", t));
EXPECT_FALSE(RFC1123StrToTime("Sunday, 06-Nov-94 08:49:37 -0100", t));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment