X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/3b0e76ec9c9386e428944f621b970d691884b84a..a473295d01ed9c599926d9a8c212d4e1a404f78b:/apt-pkg/contrib/strutl.cc diff --git a/apt-pkg/contrib/strutl.cc b/apt-pkg/contrib/strutl.cc index 5e641a02b..cf8feb970 100644 --- a/apt-pkg/contrib/strutl.cc +++ b/apt-pkg/contrib/strutl.cc @@ -21,16 +21,20 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + #include #include #include -#include -#include #include #include -#include #include -#include #include #include #include @@ -71,14 +75,14 @@ bool Endswith(const std::string &s, const std::string &end) { if (end.size() > s.size()) return false; - return (s.substr(s.size() - end.size(), s.size()) == end); + return (s.compare(s.size() - end.size(), end.size(), end) == 0); } bool Startswith(const std::string &s, const std::string &start) { if (start.size() > s.size()) return false; - return (s.substr(0, start.size()) == start); + return (s.compare(0, start.size(), start) == 0); } } @@ -464,7 +468,9 @@ string SubstVar(const string &Str,const string &Subst,const string &Contents) if (OldPos >= Str.length()) return Temp; - return Temp + string(Str,OldPos); + + Temp.append(Str, OldPos, string::npos); + return Temp; } string SubstVar(string Str,const struct SubstVar *Vars) { @@ -744,20 +750,27 @@ int StringToBool(const string &Text,int Default) /* This converts a time_t into a string time representation that is year 2000 complient and timezone neutral */ string TimeRFC1123(time_t Date) +{ + return TimeRFC1123(Date, false); +} +string TimeRFC1123(time_t Date, bool const NumericTimezone) { struct tm Conv; if (gmtime_r(&Date, &Conv) == NULL) return ""; - char Buf[300]; - const char *Day[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; - const char *Month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul", - "Aug","Sep","Oct","Nov","Dec"}; - - snprintf(Buf, sizeof(Buf), "%s, %02i %s %i %02i:%02i:%02i GMT",Day[Conv.tm_wday], - Conv.tm_mday,Month[Conv.tm_mon],Conv.tm_year+1900,Conv.tm_hour, - Conv.tm_min,Conv.tm_sec); - return Buf; + auto const posix = std::locale::classic(); + std::ostringstream datestr; + datestr.imbue(posix); + APT::StringView const fmt("%a, %d %b %Y %H:%M:%S"); + std::use_facet>(posix).put( + std::ostreambuf_iterator(datestr), + datestr, ' ', &Conv, fmt.data(), fmt.data() + fmt.size()); + if (NumericTimezone) + datestr << " +0000"; + else + datestr << " GMT"; + return datestr.str(); } /*}}}*/ // ReadMessages - Read messages from the FD /*{{{*/ @@ -866,7 +879,7 @@ bool ReadMessages(int Fd, vector &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)) { @@ -919,28 +932,98 @@ static time_t timegm(struct tm *t) } #endif /*}}}*/ -// FullDateToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/ +// RFC1123StrToTime - Converts a HTTP1.1 full date strings into a time_t /*{{{*/ // --------------------------------------------------------------------- -/* tries to parses a full date as specified in RFC2616 Section 3.3.1 - with one exception: All timezones (%Z) are accepted but the protocol - says that it MUST be GMT, but this one is equal to UTC which we will - encounter from time to time (e.g. in Release files) so we accept all - here and just assume it is GMT (or UTC) later on */ +/* tries to parses a full date as specified in RFC7231 §7.1.1.1 + with one exception: HTTP/1.1 valid dates need to have GMT as timezone. + As we encounter dates from UTC or with a numeric timezone in other places, + we allow them here to to be able to reuse the method. Either way, a date + must be in UTC or parsing will fail. Previous implementations of this + method used to ignore the timezone and assume always UTC. */ bool RFC1123StrToTime(const char* const str,time_t &time) { - struct tm Tm; - setlocale (LC_ALL,"C"); - bool const invalid = - // Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 - (strptime(str, "%a, %d %b %Y %H:%M:%S %Z", &Tm) == NULL && - // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 - strptime(str, "%A, %d-%b-%y %H:%M:%S %Z", &Tm) == NULL && - // Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format - strptime(str, "%a %b %d %H:%M:%S %Y", &Tm) == NULL); - setlocale (LC_ALL,""); - if (invalid == true) + 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); + auto const &posix = std::locale::classic(); + ss.imbue(posix); + 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 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; + 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; + } + break; + } + + if (ss.fail() || ss.bad() || !ss.eof()) + return false; + + 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; } @@ -1088,10 +1171,11 @@ bool Base256ToNum(const char *Str,unsigned long long &Res,unsigned int Len) tar files */ bool Base256ToNum(const char *Str,unsigned long &Res,unsigned int Len) { - unsigned long long Num; + unsigned long long Num = 0; bool rc; rc = Base256ToNum(Str, Num, Len); + // rudimentary check for overflow (Res = ulong, Num = ulonglong) Res = Num; if (Res != Num) return false; @@ -1215,7 +1299,7 @@ vector StringSplit(std::string const &s, std::string const &sep, vector split; size_t start, pos; - // no seperator given, this is bogus + // no separator given, this is bogus if(sep.size() == 0) return split; @@ -1557,13 +1641,15 @@ void URI::CopyFrom(const string &U) I = FirstColon + 1; if (I > SingleSlash) I = SingleSlash; - for (; I < SingleSlash && *I != ':'; ++I); - string::const_iterator SecondColon = I; - - // Search for the @ after the colon - for (; I < SingleSlash && *I != '@'; ++I); - string::const_iterator At = I; - + + // Search for the @ separating user:pass from host + auto const RevAt = std::find( + std::string::const_reverse_iterator(SingleSlash), + std::string::const_reverse_iterator(I), '@'); + string::const_iterator const At = RevAt.base() == I ? SingleSlash : std::prev(RevAt.base()); + // and then look for the colon between user and pass + string::const_iterator const SecondColon = std::find(I, At, ':'); + // Now write the host and user/pass if (At == SingleSlash) { @@ -1651,7 +1737,7 @@ URI::operator string() Res << Host; if (Port != 0) - Res << ':' << Port; + Res << ':' << std::to_string(Port); } if (Path.empty() == false)