]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/contrib/strutl.cc
__deprecated is already defined by sys/cdefs.h :/.
[apt.git] / apt-pkg / contrib / strutl.cc
index 7fdc5922899b02ca31aee9b5fe50424ea37f71b0..cf8feb970a102883908d3f83cc9aaf0983d9bc97 100644 (file)
@@ -21,6 +21,7 @@
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/error.h>
 
+#include <array>
 #include <algorithm>
 #include <iomanip>
 #include <locale>
@@ -749,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<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                            /*{{{*/
@@ -871,7 +879,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)) 
    {
@@ -934,46 +942,89 @@ 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);
+   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;
-        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;
 }
                                                                        /*}}}*/
@@ -1120,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;
@@ -1589,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)
    {