#include <apt-pkg/fileutl.h>
#include <apt-pkg/error.h>
+#include <array>
+#include <algorithm>
+#include <iomanip>
+#include <locale>
+#include <sstream>
+#include <string>
+#include <vector>
+
#include <stddef.h>
#include <stdlib.h>
#include <time.h>
-#include <string>
-#include <vector>
#include <ctype.h>
#include <string.h>
-#include <sstream>
#include <stdio.h>
-#include <algorithm>
#include <unistd.h>
#include <regex.h>
#include <errno.h>
{
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);
}
}
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)
{
// Find the end of line and strip the leading/trailing spaces
string::const_iterator J;
I += Length + 1;
- for (; isspace(*I) != 0 && I < Message.end(); ++I);
+ for (; isspace_ascii(*I) != 0 && I < Message.end(); ++I);
for (J = I; *J != '\n' && J < Message.end(); ++J);
- for (; J > I && isspace(J[-1]) != 0; --J);
+ for (; J > I && isspace_ascii(J[-1]) != 0; --J);
return string(I,J);
}
/* 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<std::time_put<char>>(posix).put(
+ std::ostreambuf_iterator<char>(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 /*{{{*/
return false;
// No data
+#if EAGAIN != EWOULDBLOCK
if (Res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
+#else
+ if (Res < 0 && errno == EAGAIN)
+#endif
return true;
if (Res < 0)
return false;
// ---------------------------------------------------------------------
/* 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))
{
}
#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<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;
+ 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;
}
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;
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
- return 0;
+ return -1;
}
/*}}}*/
// Hex2Num - Convert a long hex number into a buffer /*{{{*/
// ---------------------------------------------------------------------
/* The length of the buffer must be exactly 1/2 the length of the string. */
bool Hex2Num(const string &Str,unsigned char *Num,unsigned int Length)
+{
+ return Hex2Num(APT::StringView(Str), Num, Length);
+}
+
+bool Hex2Num(const APT::StringView Str,unsigned char *Num,unsigned int Length)
{
if (Str.length() != Length*2)
return false;
// Convert each digit. We store it in the same order as the string
int J = 0;
- for (string::const_iterator I = Str.begin(); I != Str.end();J++, I += 2)
+ for (auto I = Str.begin(); I != Str.end();J++, I += 2)
{
- if (isxdigit(*I) == 0 || isxdigit(I[1]) == 0)
+ int first_half = HexDigit(I[0]);
+ int second_half;
+ if (first_half < 0)
return false;
- Num[J] = HexDigit(I[0]) << 4;
- Num[J] += HexDigit(I[1]);
+ second_half = HexDigit(I[1]);
+ if (second_half < 0)
+ return false;
+ Num[J] = first_half << 4;
+ Num[J] += second_half;
}
return true;
vector<string> split;
size_t start, pos;
- // no seperator given, this is bogus
+ // no separator given, this is bogus
if(sep.size() == 0)
return split;
return VerStr.substr(i+1);
}
/*}}}*/
+
// tolower_ascii - tolower() function that ignores the locale /*{{{*/
// ---------------------------------------------------------------------
/* This little function is the most called method we have and tries
therefore to do the absolut minimum - and is notable faster than
standard tolower/toupper and as a bonus avoids problems with different
locales - we only operate on ascii chars anyway. */
+#undef tolower_ascii
+int tolower_ascii(int const c) APT_CONST APT_COLD;
int tolower_ascii(int const c)
{
- if (c >= 'A' && c <= 'Z')
- return c + 32;
- return c;
+ return tolower_ascii_inline(c);
+}
+ /*}}}*/
+
+// isspace_ascii - isspace() function that ignores the locale /*{{{*/
+// ---------------------------------------------------------------------
+/* This little function is one of the most called methods we have and tries
+ therefore to do the absolut minimum - and is notable faster than
+ standard isspace() and as a bonus avoids problems with different
+ locales - we only operate on ascii chars anyway. */
+#undef isspace_ascii
+int isspace_ascii(int const c) APT_CONST APT_COLD;
+int isspace_ascii(int const c)
+{
+ return isspace_ascii_inline(c);
}
/*}}}*/
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)
{
Res << Host;
if (Port != 0)
- Res << ':' << Port;
+ Res << ':' << std::to_string(Port);
}
if (Path.empty() == false)