浏览代码

Bug 2065 – Scripting command "ls" in FTP session displays timestamps without year even if the server (notably IIS) provided the year, if it did not provide seconds

https://winscp.net/tracker/2065

Source commit: 4d719f707f54a5ce7d1ff2ad327a1159ccb3fefe
Martin Prikryl 3 年之前
父节点
当前提交
191729720a

+ 1 - 0
source/components/UnixDirView.cpp

@@ -951,6 +951,7 @@ TDateTime __fastcall TUnixDirView::ItemFileTime(TListItem * Item,
       break;
       break;
 
 
     case mfMDHM:
     case mfMDHM:
+    case mfYMDHM:
       Precision = tpMinute;
       Precision = tpMinute;
       break;
       break;
 
 

+ 3 - 5
source/core/FtpFileSystem.cpp

@@ -4344,9 +4344,7 @@ void __fastcall TFTPFileSystem::RemoteFileTimeToDateTimeAndPrecision(const TRemo
       DateTime = DateTime +
       DateTime = DateTime +
         EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
         EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
           (unsigned short)Source.Second, 0);
           (unsigned short)Source.Second, 0);
-      // not exact as we got year as well, but it is most probably
-      // guessed by FZAPI anyway
-      ModificationFmt = Source.HasSeconds ? mfFull : mfMDHM;
+      ModificationFmt = Source.HasSeconds ? mfFull : (Source.HasYear ? mfYMDHM : mfMDHM);
 
 
       // With IIS, the Utc should be false only for MDTM
       // With IIS, the Utc should be false only for MDTM
       if (FWindowsServer && !Source.Utc)
       if (FWindowsServer && !Source.Utc)
@@ -4469,11 +4467,11 @@ bool __fastcall TFTPFileSystem::HandleListData(const wchar_t * Path,
       catch (Exception & E)
       catch (Exception & E)
       {
       {
         UnicodeString EntryData =
         UnicodeString EntryData =
-          FORMAT(L"%s/%s/%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
+          FORMAT(L"%s/%s/%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
             (Entry->Name, Entry->Permissions, Entry->HumanPerm, Entry->Owner, Entry->Group, Entry->OwnerGroup, IntToStr(Entry->Size),
             (Entry->Name, Entry->Permissions, Entry->HumanPerm, Entry->Owner, Entry->Group, Entry->OwnerGroup, IntToStr(Entry->Size),
              int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
              int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
              Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
              Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
-             int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
+             int(Entry->Time.HasYear), int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
         throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
         throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
       }
       }
 
 

+ 10 - 2
source/core/RemoteFiles.cpp

@@ -397,6 +397,7 @@ TDateTime __fastcall ReduceDateTimePrecision(TDateTime DateTime,
     DecodeTime(DateTime, H, N, S, MS);
     DecodeTime(DateTime, H, N, S, MS);
     switch (Precision)
     switch (Precision)
     {
     {
+      case mfYMDHM:
       case mfMDHM:
       case mfMDHM:
         S = 0;
         S = 0;
         MS = 0;
         MS = 0;
@@ -433,6 +434,7 @@ UnicodeString __fastcall UserModificationStr(TDateTime DateTime,
       return L"";
       return L"";
     case mfMDY:
     case mfMDY:
       return FormatDateTime(L"ddddd", DateTime);
       return FormatDateTime(L"ddddd", DateTime);
+    case mfYMDHM:
     case mfMDHM:
     case mfMDHM:
       return FormatDateTime(L"ddddd t", DateTime);
       return FormatDateTime(L"ddddd t", DateTime);
     case mfFull:
     case mfFull:
@@ -459,6 +461,10 @@ UnicodeString __fastcall ModificationStr(TDateTime DateTime,
       return FORMAT(L"%3s %2d %2d:%2.2d",
       return FORMAT(L"%3s %2d %2d:%2.2d",
         (EngShortMonthNames[Month-1], Day, Hour, Min));
         (EngShortMonthNames[Month-1], Day, Hour, Min));
 
 
+    case mfYMDHM:
+      return FORMAT(L"%3s %2d %2d:%2.2d %4d",
+        (EngShortMonthNames[Month-1], Day, Hour, Min, Year));
+
     default:
     default:
       DebugFail();
       DebugFail();
       // fall thru
       // fall thru
@@ -1012,7 +1018,7 @@ bool __fastcall TRemoteFile::IsTimeShiftingApplicable()
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 bool __fastcall TRemoteFile::IsTimeShiftingApplicable(TModificationFmt ModificationFmt)
 bool __fastcall TRemoteFile::IsTimeShiftingApplicable(TModificationFmt ModificationFmt)
 {
 {
-  return (ModificationFmt == mfMDHM) || (ModificationFmt == mfFull);
+  return (ModificationFmt == mfMDHM) || (ModificationFmt == mfYMDHM) || (ModificationFmt == mfFull);
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TRemoteFile::ShiftTimeInSeconds(__int64 Seconds)
 void __fastcall TRemoteFile::ShiftTimeInSeconds(__int64 Seconds)
@@ -1294,7 +1300,9 @@ void __fastcall TRemoteFile::SetListingStr(UnicodeString value)
         FModification = EncodeDateVerbose(Year, Month, Day) + EncodeTimeVerbose(Hour, Min, Sec, 0);
         FModification = EncodeDateVerbose(Year, Month, Day) + EncodeTimeVerbose(Hour, Min, Sec, 0);
         // adjust only when time is known,
         // adjust only when time is known,
         // adjusting default "midnight" time makes no sense
         // adjusting default "midnight" time makes no sense
-        if ((FModificationFmt == mfMDHM) || (FModificationFmt == mfFull))
+        if ((FModificationFmt == mfMDHM) ||
+             DebugAlwaysFalse(FModificationFmt == mfYMDHM) ||
+             (FModificationFmt == mfFull))
         {
         {
           DebugAssert(Terminal != NULL);
           DebugAssert(Terminal != NULL);
           FModification = AdjustDateTimeFromUnix(FModification,
           FModification = AdjustDateTimeFromUnix(FModification,

+ 1 - 1
source/core/RemoteFiles.h

@@ -5,7 +5,7 @@
 #include <vector>
 #include <vector>
 #include <map>
 #include <map>
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-enum TModificationFmt { mfNone, mfMDHM, mfMDY, mfFull };
+enum TModificationFmt { mfNone, mfMDHM, mfYMDHM, mfMDY, mfFull };
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 #define SYMLINKSTR L" -> "
 #define SYMLINKSTR L" -> "
 #define ROOTDIRECTORY L"/"
 #define ROOTDIRECTORY L"/"

+ 1 - 0
source/core/WebDAVFileSystem.cpp

@@ -892,6 +892,7 @@ void __fastcall TWebDAVFileSystem::ParsePropResultSet(TRemoteFile * File,
           EncodeDateVerbose((unsigned short)Year, (unsigned short)Month, (unsigned short)Day) +
           EncodeDateVerbose((unsigned short)Year, (unsigned short)Month, (unsigned short)Day) +
           EncodeTimeVerbose((unsigned short)Hour, (unsigned short)Min, (unsigned short)Sec, 0);
           EncodeTimeVerbose((unsigned short)Hour, (unsigned short)Min, (unsigned short)Sec, 0);
         File->Modification = ConvertTimestampFromUTC(Modification);
         File->Modification = ConvertTimestampFromUTC(Modification);
+        // Should use mfYMDHM or mfMDY when appropriate according to Filled
         File->ModificationFmt = mfFull;
         File->ModificationFmt = mfFull;
       }
       }
       else
       else

+ 1 - 0
source/filezilla/FileZillaIntf.cpp

@@ -293,6 +293,7 @@ void __fastcall CopyFileTime(TRemoteFileTime & Dest, const t_directory::t_dirent
   Dest.Second = Source.second;
   Dest.Second = Source.second;
   Dest.HasTime = Source.hastime;
   Dest.HasTime = Source.hastime;
   Dest.HasDate = Source.hasdate;
   Dest.HasDate = Source.hasdate;
+  Dest.HasYear = Source.hasyear;
   Dest.HasSeconds = Source.hasseconds;
   Dest.HasSeconds = Source.hasseconds;
   Dest.Utc = Source.utc;
   Dest.Utc = Source.utc;
 }
 }

+ 1 - 0
source/filezilla/FileZillaIntf.h

@@ -20,6 +20,7 @@ struct TRemoteFileTime
   int Minute;
   int Minute;
   int Second;
   int Second;
   bool HasTime;
   bool HasTime;
+  bool HasYear;
   bool HasSeconds;
   bool HasSeconds;
   bool HasDate;
   bool HasDate;
   bool Utc;
   bool Utc;

+ 1 - 0
source/filezilla/FtpControlSocket.cpp

@@ -4416,6 +4416,7 @@ bool CFtpControlSocket::HandleMdtm(int code, t_directory::t_direntry::t_date & d
             date.minute = m;
             date.minute = m;
             date.second = s;
             date.second = s;
             date.hastime = true;
             date.hastime = true;
+            date.hasyear = true;
             date.hasseconds = hasseconds;
             date.hasseconds = hasseconds;
             date.hasdate = true;
             date.hasdate = true;
             date.utc = true;
             date.utc = true;

+ 15 - 3
source/filezilla/FtpListResult.cpp

@@ -674,6 +674,7 @@ bool CFtpListResult::ParseShortDate(const char *str, int len, t_directory::t_dir
   }
   }
 
 
   date.hasdate = TRUE;
   date.hasdate = TRUE;
+  date.hasyear = TRUE;
   return true;
   return true;
 }
 }
 
 
@@ -803,6 +804,7 @@ BOOL CFtpListResult::parseAsVMS(const char *line, const int linelen, t_directory
   p++;
   p++;
 
 
   dir.date.year = static_cast<int>(strntoi64(p, tokenlen - (p - str)));
   dir.date.year = static_cast<int>(strntoi64(p, tokenlen - (p - str)));
+  dir.date.hasyear = TRUE;
 
 
   //Get time
   //Get time
   str = GetNextToken(line, linelen, tokenlen, pos, 0);
   str = GetNextToken(line, linelen, tokenlen, pos, 0);
@@ -1169,6 +1171,7 @@ bool CFtpListResult::parseMlsdDateTime(const CString value, t_directory::t_diren
   if (result)
   if (result)
   {
   {
     date.year = Year;
     date.year = Year;
+    date.hasyear = TRUE;
     date.month = Month;
     date.month = Month;
     date.day = Day;
     date.day = Day;
     date.hour = Hours;
     date.hour = Hours;
@@ -1185,7 +1188,7 @@ void CFtpListResult::GuessYearIfUnknown(t_directory::t_direntry::t_date & Date)
   // others use one year as limit. IIS shows time for files from the current year (jan-dec).
   // others use one year as limit. IIS shows time for files from the current year (jan-dec).
   // So there is no support for files with time
   // So there is no support for files with time
   // dated in the near future. Under normal conditions there should not be such files.
   // dated in the near future. Under normal conditions there should not be such files.
-  if (!Date.year)
+  if (!Date.year) // might use direntry.date.hasyear now?
   {
   {
     CTime curtime = CTime::GetCurrentTime();
     CTime curtime = CTime::GetCurrentTime();
     int curday = curtime.GetDay();
     int curday = curtime.GetDay();
@@ -1201,6 +1204,7 @@ void CFtpListResult::GuessYearIfUnknown(t_directory::t_direntry::t_date & Date)
     {
     {
       Date.year = curyear - 1;
       Date.year = curyear - 1;
     }
     }
+    // year is guessed, not setting hasyear
   }
   }
 }
 }
 
 
@@ -1492,6 +1496,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth == 4) //2002-10-14
     else if (p-smonth == 4) //2002-10-14
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
+      direntry.date.hasyear = TRUE;
       sday = pos2 + 1;
       sday = pos2 + 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1513,6 +1518,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth) //14-10-2002 or 01-jun-99
     else if (p-smonth) //14-10-2002 or 01-jun-99
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
+      direntry.date.hasyear = TRUE;
       sday = smonth;
       sday = smonth;
       sdaylen = p - smonth;
       sdaylen = p - smonth;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1554,6 +1560,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth==4)
     else if (p-smonth==4)
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
       direntry.date.year = static_cast<int>(strntoi64(smonth, p-smonth));
+      direntry.date.hasyear = TRUE;
       sday = pos2 + 1;
       sday = pos2 + 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       sdaylen = smonthlen - (pos2 - smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1575,6 +1582,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     else if (p-smonth==2)
     else if (p-smonth==2)
     {
     {
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
       direntry.date.year = static_cast<int>(strntoi64(pos2+1, tokenlen - (pos2-smonth) - 1));
+      direntry.date.hasyear = TRUE;
       sday = smonth;
       sday = smonth;
       sdaylen = p - smonth;
       sdaylen = p - smonth;
       smonthlen = pos2-smonth - (p-smonth) - 1;
       smonthlen = pos2-smonth - (p-smonth) - 1;
@@ -1677,6 +1685,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
   {
   {
     gotYear = true;
     gotYear = true;
     direntry.date.year = year;
     direntry.date.year = year;
+    direntry.date.hasyear = TRUE;
   }
   }
   else
   else
   {
   {
@@ -1767,12 +1776,13 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
         return false;
         return false;
       }
       }
     }
     }
-    else if (!direntry.date.year)
+    else if (!direntry.date.year) // might use direntry.date.hasyear now?
     {
     {
       //No delimiters -> year
       //No delimiters -> year
 
 
       direntry.date.hastime = FALSE;
       direntry.date.hastime = FALSE;
       direntry.date.year = static_cast<int>(strntoi64(stimeyear, stimeyearlen));
       direntry.date.year = static_cast<int>(strntoi64(stimeyear, stimeyearlen));
+      direntry.date.hasyear = TRUE;
     }
     }
     else
     else
     {
     {
@@ -1781,7 +1791,7 @@ BOOL CFtpListResult::parseAsUnix(const char *line, const int linelen, t_director
     }
     }
   }
   }
 
 
-  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid!
+  if (!direntry.date.year) //Year 0? Really ancient file, this is invalid! might use direntry.date.hasyear now?
   {
   {
     return FALSE;
     return FALSE;
   }
   }
@@ -1927,6 +1937,7 @@ void CFtpListResult::TimeTToDate(time_t TimeT, t_directory::t_direntry::t_date &
 {
 {
   tm * sTime = gmtime(&TimeT);
   tm * sTime = gmtime(&TimeT);
   Date.year = sTime->tm_year + 1900;
   Date.year = sTime->tm_year + 1900;
+  Date.hasyear = TRUE;
   Date.month = sTime->tm_mon+1;
   Date.month = sTime->tm_mon+1;
   Date.day = sTime->tm_mday;
   Date.day = sTime->tm_mday;
   Date.hour = sTime->tm_hour;
   Date.hour = sTime->tm_hour;
@@ -2105,6 +2116,7 @@ BOOL CFtpListResult::parseAsOther(const char *line, const int linelen, t_directo
         return FALSE;
         return FALSE;
 
 
       direntry.date.year = static_cast<int>(strntoi64(str, tokenlen));
       direntry.date.year = static_cast<int>(strntoi64(str, tokenlen));
+      direntry.date.hasyear = TRUE;
       if (direntry.date.year < 50)
       if (direntry.date.year < 50)
         direntry.date.year += 2000;
         direntry.date.year += 2000;
       else if (direntry.date.year < 1000)
       else if (direntry.date.year < 1000)

+ 1 - 1
source/filezilla/structures.cpp

@@ -47,5 +47,5 @@ t_directory::t_direntry::t_direntry()
 t_directory::t_direntry::t_date::t_date()
 t_directory::t_direntry::t_date::t_date()
 {
 {
   year=month=day=hour=minute=second=0;
   year=month=day=hour=minute=second=0;
-  hasdate=hastime=hasseconds=utc=FALSE;
+  hasdate=hastime=hasyear=hasseconds=utc=FALSE;
 }
 }

+ 1 - 0
source/filezilla/structures.h

@@ -33,6 +33,7 @@ public:
       t_date();
       t_date();
       int year,month,day,hour,minute,second;
       int year,month,day,hour,minute,second;
       bool hastime;
       bool hastime;
+      bool hasyear; // ignored and assumed true when hasseconds
       bool hasseconds;
       bool hasseconds;
       bool hasdate;
       bool hasdate;
       bool utc;
       bool utc;