Martin Prikryl 15 years ago
parent
commit
c113e8686d

+ 4 - 4
Console.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 2,4,0,118
-PRODUCTVERSION 2,4,0,118
+FILEVERSION 2,5,0,121
+PRODUCTVERSION 2,5,0,121
 FILEOS 0x4
 FILETYPE 0x1
 {
@@ -10,13 +10,13 @@ FILETYPE 0x1
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Console interface for WinSCP\0"
-            VALUE "FileVersion", "2.4.0.118\0"
+            VALUE "FileVersion", "2.5.0.121\0"
             VALUE "InternalName", "console\0"
             VALUE "LegalCopyright", "(c) 2000-2010 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "winscp.com\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.7.0\0"
+            VALUE "ProductVersion", "4.2.8.0\0"
             VALUE "ReleaseType", "stable\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 4 - 4
DragExt.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 1,1,9,112
-PRODUCTVERSION 1,1,9,112
+FILEVERSION 1,1,9,114
+PRODUCTVERSION 1,1,9,114
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -10,13 +10,13 @@ FILETYPE 0x2
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Drag&Drop shell extension for WinSCP (32-bit)\0"
-            VALUE "FileVersion", "1.1.9.112\0"
+            VALUE "FileVersion", "1.1.9.114\0"
             VALUE "InternalName", "dragext32\0"
             VALUE "LegalCopyright", "(c) 2000-2010 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.7.0\0"
+            VALUE "ProductVersion", "4.2.8.0\0"
             VALUE "ReleaseType", "stable\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 4 - 4
DragExt64.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 1,1,9,112
-PRODUCTVERSION 1,1,9,112
+FILEVERSION 1,1,9,114
+PRODUCTVERSION 1,1,9,114
 FILEOS 0x4
 FILETYPE 0x2
 {
@@ -10,13 +10,13 @@ FILETYPE 0x2
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "Drag&Drop shell extension for WinSCP (64-bit)\0"
-            VALUE "FileVersion", "1.1.9.112\0"
+            VALUE "FileVersion", "1.1.9.114\0"
             VALUE "InternalName", "dragext64\0"
             VALUE "LegalCopyright", "(c) 2000-2010 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "dragext64.dll\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.7.0\0"
+            VALUE "ProductVersion", "4.2.8.0\0"
             VALUE "ReleaseType", "stable\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 14 - 12
Putty.bpr

@@ -5,17 +5,17 @@
     <VERSION value="BCB.06.00"/>
     <PROJECT value="lib\Putty.lib"/>
     <OBJFILES value="putty\CPROXY.obj putty\INT64.obj putty\LOGGING.obj
-      putty\MISC.obj putty\PORTFWD_.obj putty\PROXY.obj putty\SSH_.obj
-      putty\SSHAES_.obj putty\SSHARCF.obj putty\SSHBLOWF.obj putty\SSHBN.obj
-      putty\SSHCRC.obj putty\SSHCRCDA.obj putty\SSHDES.obj putty\SSHDH.obj
-      putty\SSHDSS.obj putty\SSHMD5.obj putty\SSHPUBK.obj putty\SSHRAND.obj
-      putty\SSHRSA.obj putty\SSHSH256.obj putty\SSHSH512.obj putty\SSHSHA_.obj
-      putty\SSHZLIB.obj putty\TREE234.obj putty\CHARSET\UTF8.obj
-      putty\WILDCARD.obj putty\WINDOWS\WINGSS.obj putty\WINDOWS\WINHANDL.obj
-      putty\WINDOWS\WINMISC.obj putty\WINDOWS\WINNET.obj
-      putty\WINDOWS\WINNOISE.obj putty\WINDOWS\WINPGNTC.obj
-      putty\WINDOWS\WINPROXY.obj putty\WINDOWS\WINSTORE_.obj
-      putty\WINDOWS\WINTIME.obj putty\X11FWD.obj"/>
+      putty\MISC.obj putty\PGSSAPI.obj putty\PORTFWD_.obj putty\PROXY.obj
+      putty\SSH_.obj putty\SSHAES_.obj putty\SSHARCF.obj putty\SSHBLOWF.obj
+      putty\SSHBN.obj putty\SSHCRC.obj putty\SSHCRCDA.obj putty\SSHDES.obj
+      putty\SSHDH.obj putty\SSHDSS.obj putty\SSHGSSC.obj putty\SSHMD5.obj
+      putty\SSHPUBK.obj putty\SSHRAND.obj putty\SSHRSA.obj putty\SSHSH256.obj
+      putty\SSHSH512.obj putty\SSHSHA_.obj putty\SSHZLIB.obj putty\TREE234.obj
+      putty\CHARSET\UTF8.obj putty\WILDCARD.obj putty\WINDOWS\WINGSS.obj
+      putty\WINDOWS\WINHANDL.obj putty\WINDOWS\WINMISC.obj
+      putty\WINDOWS\WINNET.obj putty\WINDOWS\WINNOISE.obj
+      putty\WINDOWS\WINPGNTC.obj putty\WINDOWS\WINPROXY.obj
+      putty\WINDOWS\WINSTORE_.obj putty\WINDOWS\WINTIME.obj putty\X11FWD.obj"/>
     <RESFILES value=""/>
     <DEFFILE value=""/>
     <RESDEPEN value="$(RESFILES)"/>
@@ -27,7 +27,7 @@
     <PATHRC value=".;"/>
     <PATHASM value=".;"/>
     <LINKER value="TLib"/>
-    <USERDEFINES value="SSPI_MECH;MPEXT;_WINDOWS"/>
+    <USERDEFINES value="SECURITY_WIN32;MPEXT;_WINDOWS"/>
     <SYSDEFINES value="_RTLDLL;NO_STRICT"/>
     <MAINSOURCE value="Putty.bpf"/>
     <INCLUDEPATH value="putty;putty\CHARSET;putty\WINDOWS;$(BCB)\include;$(BCB)\include\vcl"/>
@@ -56,6 +56,7 @@
       <FILE FILENAME="putty\INT64.C" FORMNAME="" UNITNAME="INT64.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\LOGGING.C" FORMNAME="" UNITNAME="LOGGING.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\MISC.C" FORMNAME="" UNITNAME="MISC.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="putty\PGSSAPI.C" FORMNAME="" UNITNAME="PGSSAPI.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\PORTFWD_.C" FORMNAME="" UNITNAME="PORTFWD_.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\PROXY.C" FORMNAME="" UNITNAME="PROXY.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSH_.C" FORMNAME="" UNITNAME="SSH_.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
@@ -68,6 +69,7 @@
       <FILE FILENAME="putty\SSHDES.C" FORMNAME="" UNITNAME="SSHDES.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHDH.C" FORMNAME="" UNITNAME="SSHDH.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHDSS.C" FORMNAME="" UNITNAME="SSHDSS.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="putty\SSHGSSC.C" FORMNAME="" UNITNAME="SSHGSSC.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHMD5.C" FORMNAME="" UNITNAME="SSHMD5.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHPUBK.C" FORMNAME="" UNITNAME="SSHPUBK.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
       <FILE FILENAME="putty\SSHRAND.C" FORMNAME="" UNITNAME="SSHRAND.C" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

+ 4 - 4
WinSCP.rc

@@ -1,6 +1,6 @@
 1 VERSIONINFO
-FILEVERSION 4,2,7,758
-PRODUCTVERSION 4,2,7,758
+FILEVERSION 4,2,8,818
+PRODUCTVERSION 4,2,8,818
 FILEOS 0x4
 FILETYPE 0x1
 {
@@ -10,13 +10,13 @@ FILETYPE 0x1
         {
             VALUE "CompanyName", "Martin Prikryl\0"
             VALUE "FileDescription", "WinSCP: SFTP, FTP and SCP client\0"
-            VALUE "FileVersion", "4.2.7.758\0"
+            VALUE "FileVersion", "4.2.8.818\0"
             VALUE "InternalName", "winscp\0"
             VALUE "LegalCopyright", "(c) 2000-2010 Martin Prikryl\0"
             VALUE "LegalTrademarks", "\0"
             VALUE "OriginalFilename", "winscp.exe\0"
             VALUE "ProductName", "WinSCP\0"
-            VALUE "ProductVersion", "4.2.7.0\0"
+            VALUE "ProductVersion", "4.2.8.0\0"
             VALUE "ReleaseType", "stable\0"
             VALUE "WWW", "http://winscp.net/\0"
         }

+ 21 - 18
components/UnixDriveView.cpp

@@ -139,14 +139,25 @@ bool __fastcall TCustomUnixDriveView::NodeIsHidden(const TTreeNode * Node)
   #endif
 }
 //---------------------------------------------------------------------------
-bool __fastcall TCustomUnixDriveView::NodeCanDelete(TTreeNode * Node, bool RememberIfNot)
+bool __fastcall TCustomUnixDriveView::NodeTryDelete(TTreeNode * Node, bool RememberIfFails)
 {
   bool Result =
     (Selected == NULL) ||
     ((Selected != Node) && !Selected->HasAsParent(Node));
-  if (!Result && RememberIfNot)
+  if (Result)
   {
-    FPendingDelete->Add(Node);
+    Node->Delete();
+  }
+  else
+  {
+    if (RememberIfFails)
+    {
+      int I = FPendingDelete->IndexOf(Node);
+      if (I < 0)
+      {
+        FPendingDelete->Add(Node);
+      }
+    }
   }
   return Result;
 }
@@ -211,9 +222,9 @@ void __fastcall TCustomUnixDriveView::UpdatePath(TTreeNode * Node, bool Force,
       {
         TTreeNode * ChildNode = dynamic_cast<TTreeNode *>(ChildrenDirs->Objects[i]);
         TNodeData * ChildData = NodeData(ChildNode);
-        if ((ChildData->File == NULL) && NodeCanDelete(ChildNode, true))
+        if (ChildData->File == NULL)
         {
-          ChildNode->Delete();
+          NodeTryDelete(ChildNode, true);
         }
       }
 
@@ -231,16 +242,12 @@ void __fastcall TCustomUnixDriveView::UpdatePath(TTreeNode * Node, bool Force,
     {
       TTreeNode * PrevChildNode = Node->GetPrevChild(ChildNode);
       TRemoteFile * File = NodeFile(ChildNode);
-      if (!NodeCanDelete(ChildNode, true) ||
-          ((ShowHiddenDirs || !NodeIsHidden(ChildNode)) &&
-           (ShowInaccesibleDirectories || (File == NULL) || !File->IsInaccesibleDirectory)))
+      if (((ShowHiddenDirs || !NodeIsHidden(ChildNode)) &&
+            (ShowInaccesibleDirectories || (File == NULL) || !File->IsInaccesibleDirectory)) ||
+          !NodeTryDelete(ChildNode, true))
       {
         UpdatePath(ChildNode, true);
       }
-      else
-      {
-        ChildNode->Delete();
-      }
       ChildNode = PrevChildNode;
     }
   }
@@ -494,12 +501,8 @@ void __fastcall TCustomUnixDriveView::CheckPendingDeletes()
   while (Index < FPendingDelete->Count)
   {
     TTreeNode * Node = static_cast<TTreeNode *>(FPendingDelete->Items[Index]);
-    if (NodeCanDelete(Node, false))
-    {
-      // this as well deletes node from FPendingDelete
-      Node->Delete();
-    }
-    else
+    // this as well deletes node from FPendingDelete
+    if (!NodeTryDelete(Node, false))
     {
       Index++;
     }

+ 1 - 1
components/UnixDriveView.h

@@ -47,7 +47,7 @@ protected:
   inline TRemoteFile * __fastcall NodeFile(const TTreeNode * Node);
   inline TRemoteFile * __fastcall NodeFileForce(TTreeNode * Node);
   inline bool __fastcall NodeIsHidden(const TTreeNode * Node);
-  inline bool __fastcall NodeCanDelete(TTreeNode * Node, bool RememberIfNot);
+  inline bool __fastcall NodeTryDelete(TTreeNode * Node, bool RememberIfFails);
 
   virtual TCustomDirView * __fastcall GetCustomDirView();
   virtual void __fastcall SetCustomDirView(TCustomDirView * Value);

+ 17 - 13
console/Main.cpp

@@ -35,9 +35,11 @@ inline void FreeCommStruct(TConsoleCommStruct* CommStruct)
   UnmapViewOfFile(CommStruct);
 }
 //---------------------------------------------------------------------------
-void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& ResponseEvent,
+void InitializeConsole(char* InstanceName, HANDLE& RequestEvent, HANDLE& ResponseEvent,
   HANDLE& CancelEvent, HANDLE& FileMapping, HANDLE& Job)
 {
+  unsigned int Process = GetCurrentProcessId();
+
   int Attempts = 0;
   char Name[MAX_PATH];
   bool UniqEvent;
@@ -49,12 +51,14 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
       throw runtime_error("Cannot find unique name for event object.");
     }
 
+    int InstanceNumber;
     #ifdef CONSOLE_TEST
     InstanceNumber = 1;
     #else
     InstanceNumber = random(1000);
     #endif
-    sprintf(Name, "%s%d", CONSOLE_EVENT_REQUEST, InstanceNumber);
+    sprintf(InstanceName, "_%u_%d", Process, InstanceNumber);
+    sprintf(Name, "%s%s", CONSOLE_EVENT_REQUEST, InstanceName);
     HANDLE EventHandle = OpenEvent(EVENT_ALL_ACCESS, false, Name);
     UniqEvent = (EventHandle == NULL);
     if (!UniqEvent)
@@ -71,21 +75,21 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
     throw runtime_error("Cannot create request event object.");
   }
 
-  sprintf(Name, "%s%d", CONSOLE_EVENT_RESPONSE, InstanceNumber);
+  sprintf(Name, "%s%s", CONSOLE_EVENT_RESPONSE, InstanceName);
   ResponseEvent = CreateEvent(NULL, false, false, Name);
   if (ResponseEvent == NULL)
   {
     throw runtime_error("Cannot create response event object.");
   }
 
-  sprintf(Name, "%s%d", CONSOLE_EVENT_CANCEL, InstanceNumber);
+  sprintf(Name, "%s%s", CONSOLE_EVENT_CANCEL, InstanceName);
   CancelEvent = CreateEvent(NULL, false, false, Name);
   if (CancelEvent == NULL)
   {
     throw runtime_error("Cannot create cancel event object.");
   }
 
-  sprintf(Name, "%s%d", CONSOLE_MAPPING, InstanceNumber);
+  sprintf(Name, "%s%s", CONSOLE_MAPPING, InstanceName);
   FileMapping = CreateFileMapping((HANDLE)0xFFFFFFFF, NULL, PAGE_READWRITE,
     0, sizeof(TConsoleCommStruct), Name);
   if (FileMapping == NULL)
@@ -104,7 +108,7 @@ void InitializeConsole(int& InstanceNumber, HANDLE& RequestEvent, HANDLE& Respon
     (TSetInformationJobObject)GetProcAddress(Kernel32, "SetInformationJobObject");
   if ((CreateJobObject != NULL) && (SetInformationJobObject != NULL))
   {
-    sprintf(Name, "%s%d", CONSOLE_JOB, InstanceNumber);
+    sprintf(Name, "%s%s", CONSOLE_JOB, InstanceName);
     Job = CreateJobObject(NULL, Name);
     if (Job == NULL)
     {
@@ -198,7 +202,7 @@ bool __fastcall CutToken(const char *& Str, char * Token)
   return Result;
 }
 //---------------------------------------------------------------------------
-void InitializeChild(const char* CommandLine, int InstanceNumber, HANDLE& Child)
+void InitializeChild(const char* CommandLine, const char* InstanceName, HANDLE& Child)
 {
   int SkipParam = 0;
   char ChildPath[MAX_PATH] = "";
@@ -256,7 +260,7 @@ void InitializeChild(const char* CommandLine, int InstanceNumber, HANDLE& Child)
   }
 
   char* Parameters = new char[(CommandLineLen * 2) + 100 + (Count * 3) + 1];
-  sprintf(Parameters, "\"%s\" /console /consoleinstance=%d ", ChildPath, InstanceNumber);
+  sprintf(Parameters, "\"%s\" /console /consoleinstance=%s ", ChildPath, InstanceName);
   P = CommandLine;
   // skip executable path
   CutToken(P, Buffer);
@@ -316,7 +320,7 @@ void FinalizeChild(HANDLE Child)
   }
 }
 //---------------------------------------------------------------------------
-void FinalizeConsole(int /*InstanceNumber*/, HANDLE RequestEvent,
+void FinalizeConsole(const char* /*InstanceName*/, HANDLE RequestEvent,
   HANDLE ResponseEvent, HANDLE CancelEvent, HANDLE FileMapping, HANDLE Job)
 {
   CloseHandle(RequestEvent);
@@ -656,9 +660,9 @@ int main(int /*argc*/, char* /*argv*/[])
     HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
     OutputType = GetFileType(ConsoleOutput);
 
-    int InstanceNumber;
+    char InstanceName[MAX_PATH];
     HANDLE RequestEvent, ResponseEvent, FileMapping, Job;
-    InitializeConsole(InstanceNumber, RequestEvent, ResponseEvent,
+    InitializeConsole(InstanceName, RequestEvent, ResponseEvent,
       CancelEvent, FileMapping, Job);
 
     char SavedTitle[512];
@@ -667,7 +671,7 @@ int main(int /*argc*/, char* /*argv*/[])
     try
     {
       #ifndef CONSOLE_TEST
-      InitializeChild(GetCommandLine(), InstanceNumber, Child);
+      InitializeChild(GetCommandLine(), InstanceName, Child);
       #endif
 
       try
@@ -727,7 +731,7 @@ int main(int /*argc*/, char* /*argv*/[])
       Result = RESULT_INIT_ERROR;
     }
 
-    FinalizeConsole(InstanceNumber, RequestEvent, ResponseEvent,
+    FinalizeConsole(InstanceName, RequestEvent, ResponseEvent,
       CancelEvent, FileMapping, Job);
   }
   catch(const exception& e)

+ 88 - 69
core/Common.cpp

@@ -393,13 +393,16 @@ const char ShellCommandFileNamePattern[] = "!.!";
 //---------------------------------------------------------------------------
 void __fastcall ReformatFileNameCommand(AnsiString & Command)
 {
-  AnsiString Program, Params, Dir;
-  SplitCommand(Command, Program, Params, Dir);
-  if (Params.Pos(ShellCommandFileNamePattern) == 0)
+  if (!Command.IsEmpty())
   {
-    Params = Params + (Params.IsEmpty() ? "" : " ") + ShellCommandFileNamePattern;
+    AnsiString Program, Params, Dir;
+    SplitCommand(Command, Program, Params, Dir);
+    if (Params.Pos(ShellCommandFileNamePattern) == 0)
+    {
+      Params = Params + (Params.IsEmpty() ? "" : " ") + ShellCommandFileNamePattern;
+    }
+    Command = FormatCommand(Program, Params);
   }
-  Command = FormatCommand(Program, Params);
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall ExpandFileNameCommand(const AnsiString Command,
@@ -703,6 +706,7 @@ struct TDateTimeParams
   long DaylightDifferenceSec;
   SYSTEMTIME StandardDate;
   SYSTEMTIME DaylightDate;
+  bool DaylightHack;
 };
 static bool DateTimeParamsInitialized = false;
 static TDateTimeParams DateTimeParams;
@@ -762,6 +766,8 @@ static TDateTimeParams * __fastcall GetDateTimeParams()
       DateTimeParams.StandardDate = TZI.StandardDate;
       DateTimeParams.DaylightDate = TZI.DaylightDate;
 
+      DateTimeParams.DaylightHack = !IsWin7();
+
       DateTimeParamsInitialized = true;
     }
   }
@@ -871,6 +877,11 @@ static bool __fastcall IsDateInDST(const TDateTime & DateTime)
   return Result;
 }
 //---------------------------------------------------------------------------
+bool __fastcall UsesDaylightHack()
+{
+  return GetDateTimeParams()->DaylightHack;
+}
+//---------------------------------------------------------------------------
 TDateTime __fastcall UnixToDateTime(__int64 TimeStamp, TDSTMode DSTMode)
 {
   TDateTimeParams * Params = GetDateTimeParams();
@@ -878,11 +889,18 @@ TDateTime __fastcall UnixToDateTime(__int64 TimeStamp, TDSTMode DSTMode)
   TDateTime Result;
   Result = Params->UnixEpoch + (double(TimeStamp) / 86400);
 
-  if ((DSTMode == dstmWin) || (DSTMode == dstmUnix))
+  if (Params->DaylightHack)
   {
-    Result -= Params->CurrentDifference;
+    if ((DSTMode == dstmWin) || (DSTMode == dstmUnix))
+    {
+      Result -= Params->CurrentDifference;
+    }
+    else if (DSTMode == dstmKeep)
+    {
+      Result -= Params->BaseDifference;
+    }
   }
-  else if (DSTMode == dstmKeep)
+  else
   {
     Result -= Params->BaseDifference;
   }
@@ -922,7 +940,16 @@ FILETIME __fastcall DateTimeToFileTime(const TDateTime DateTime,
   FILETIME Result;
   __int64 UnixTimeStamp = DateTimeToUnix(DateTime);
 
+  TDateTimeParams * Params = GetDateTimeParams();
+  if (!Params->DaylightHack)
+  {
+    UnixTimeStamp += (IsDateInDST(DateTime) ?
+      Params->DaylightDifferenceSec : Params->StandardDifferenceSec);
+    UnixTimeStamp -= Params->CurrentDaylightDifferenceSec;
+  }
+
   TIME_POSIX_TO_WIN(UnixTimeStamp, Result);
+
   return Result;
 }
 //---------------------------------------------------------------------------
@@ -932,22 +959,36 @@ __int64 __fastcall ConvertTimestampToUnix(const FILETIME & FileTime,
   __int64 Result;
   TIME_WIN_TO_POSIX(FileTime, Result);
 
-  if ((DSTMode == dstmUnix) || (DSTMode == dstmKeep))
+  TDateTimeParams * Params = GetDateTimeParams();
+  if (Params->DaylightHack)
   {
-    FILETIME LocalFileTime;
-    SYSTEMTIME SystemTime;
-    TDateTime DateTime;
-    FileTimeToLocalFileTime(&FileTime, &LocalFileTime);
-    FileTimeToSystemTime(&LocalFileTime, &SystemTime);
-    DateTime = SystemTimeToDateTime(SystemTime);
-
-    TDateTimeParams * Params = GetDateTimeParams();
-    Result += (IsDateInDST(DateTime) ?
-      Params->DaylightDifferenceSec : Params->StandardDifferenceSec);
-
-    if (DSTMode == dstmKeep)
+    if ((DSTMode == dstmUnix) || (DSTMode == dstmKeep))
+    {
+      FILETIME LocalFileTime;
+      SYSTEMTIME SystemTime;
+      FileTimeToLocalFileTime(&FileTime, &LocalFileTime);
+      FileTimeToSystemTime(&LocalFileTime, &SystemTime);
+      TDateTime DateTime = SystemTimeToDateTime(SystemTime);
+      Result += (IsDateInDST(DateTime) ?
+        Params->DaylightDifferenceSec : Params->StandardDifferenceSec);
+
+      if (DSTMode == dstmKeep)
+      {
+        Result -= Params->CurrentDaylightDifferenceSec;
+      }
+    }
+  }
+  else
+  {
+    if (DSTMode == dstmWin)
     {
-      Result -= Params->CurrentDaylightDifferenceSec;
+      FILETIME LocalFileTime;
+      SYSTEMTIME SystemTime;
+      FileTimeToLocalFileTime(&FileTime, &LocalFileTime);
+      FileTimeToSystemTime(&LocalFileTime, &SystemTime);
+      TDateTime DateTime = SystemTimeToDateTime(SystemTime);
+      Result -= (IsDateInDST(DateTime) ?
+        Params->DaylightDifferenceSec : Params->StandardDifferenceSec);
     }
   }
 
@@ -986,63 +1027,41 @@ TDateTime __fastcall AdjustDateTimeFromUnix(TDateTime DateTime, TDSTMode DSTMode
 {
   TDateTimeParams * Params = GetDateTimeParams();
 
-  if ((DSTMode == dstmWin) || (DSTMode == dstmUnix))
+  if (Params->DaylightHack)
   {
-    DateTime = DateTime - Params->CurrentDaylightDifference;
-  }
+    if ((DSTMode == dstmWin) || (DSTMode == dstmUnix))
+    {
+      DateTime = DateTime - Params->CurrentDaylightDifference;
+    }
 
-  if (!IsDateInDST(DateTime))
-  {
-    if (DSTMode == dstmWin)
+    if (!IsDateInDST(DateTime))
     {
-      DateTime = DateTime - Params->DaylightDifference;
+      if (DSTMode == dstmWin)
+      {
+        DateTime = DateTime - Params->DaylightDifference;
+      }
+    }
+    else
+    {
+      DateTime = DateTime - Params->StandardDifference;
     }
   }
   else
   {
-    DateTime = DateTime - Params->StandardDifference;
-  }
-
-  return DateTime;
-}
-//---------------------------------------------------------------------------
-__inline static bool __fastcall UnifySignificance(unsigned short & V1,
-  unsigned short & V2)
-{
-  bool Result = (V1 == 0) || (V2 == 0);
-  if (Result)
-  {
-    V1 = 0;
-    V2 = 0;
-  }
-  return Result;
-}
-//---------------------------------------------------------------------------
-void __fastcall UnifyDateTimePrecision(TDateTime & DateTime1, TDateTime & DateTime2)
-{
-  unsigned short Y1, M1, D1, H1, N1, S1, MS1;
-  unsigned short Y2, M2, D2, H2, N2, S2, MS2;
-  bool Changed;
-
-  if (DateTime1 != DateTime2)
-  {
-    DateTime1.DecodeDate(&Y1, &M1, &D1);
-    DateTime1.DecodeTime(&H1, &N1, &S1, &MS1);
-    DateTime2.DecodeDate(&Y2, &M2, &D2);
-    DateTime2.DecodeTime(&H2, &N2, &S2, &MS2);
-    Changed = UnifySignificance(MS1, MS2);
-    if (Changed && UnifySignificance(S1, S2) && UnifySignificance(N1, N2) &&
-        UnifySignificance(H1, H2) && UnifySignificance(D1, D2) &&
-        UnifySignificance(M1, M2))
-    {
-      UnifySignificance(Y1, Y2);
-    }
-    if (Changed)
+    if (DSTMode == dstmWin)
     {
-      DateTime1 = EncodeDateVerbose(Y1, M1, D1) + EncodeTimeVerbose(H1, N1, S1, MS1);
-      DateTime2 = EncodeDateVerbose(Y2, M2, D2) + EncodeTimeVerbose(H2, N2, S2, MS2);
+      if (IsDateInDST(DateTime))
+      {
+        DateTime = DateTime + Params->DaylightDifference;
+      }
+      else
+      {
+        DateTime = DateTime + Params->StandardDifference;
+      }
     }
   }
+
+  return DateTime;
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall FixedLenDateTimeFormat(const AnsiString & Format)

+ 1 - 0
core/Common.h

@@ -88,6 +88,7 @@ enum TDSTMode
   dstmUnix = 1, // adjust UTC time to Windows "bug"
   dstmKeep = 2
 };
+bool __fastcall UsesDaylightHack();
 TDateTime __fastcall EncodeDateVerbose(Word Year, Word Month, Word Day);
 TDateTime __fastcall EncodeTimeVerbose(Word Hour, Word Min, Word Sec, Word MSec);
 TDateTime __fastcall UnixToDateTime(__int64 TimeStamp, TDSTMode DSTMode);

+ 0 - 5
core/Configuration.cpp

@@ -1008,11 +1008,6 @@ AnsiString __fastcall TConfiguration::GetDefaultKeyFile()
   return "";
 }
 //---------------------------------------------------------------------------
-AnsiString __fastcall TConfiguration::GetLocalInvalidChars()
-{
-  return "/\\:*?\"<>|";
-}
-//---------------------------------------------------------------------------
 bool __fastcall TConfiguration::GetRememberPassword()
 {
   return false;

+ 0 - 2
core/Configuration.h

@@ -89,7 +89,6 @@ private:
   void __fastcall SetIniFileStorageName(AnsiString value);
   AnsiString __fastcall GetPartialExt() const;
   AnsiString __fastcall GetFileInfoString(const AnsiString Key);
-  AnsiString __fastcall GetLocalInvalidChars();
   bool __fastcall GetGSSAPIInstalled();
   void __fastcall SetSessionReopenAuto(int value);
   void __fastcall SetSessionReopenBackground(int value);
@@ -210,7 +209,6 @@ public:
   __property AnsiString RegistryStorageKey  = { read=GetRegistryStorageKey };
   __property AnsiString IniFileStorageName  = { read=GetIniFileStorageName, write=SetIniFileStorageName };
   __property AnsiString DefaultKeyFile = { read = GetDefaultKeyFile };
-  __property AnsiString LocalInvalidChars = { read = GetLocalInvalidChars };
 
   __property bool DisablePasswordStoring = { read = FDisablePasswordStoring };
   __property bool ForceBanners = { read = FForceBanners };

+ 1 - 1
core/FtpFileSystem.cpp

@@ -1565,7 +1565,7 @@ void __fastcall TFTPFileSystem::DeleteFile(const AnsiString AFileName,
 
     if (Dir)
     {
-      // Is current remote directory is in the directory being removed,
+      // If current remote directory is in the directory being removed,
       // some servers may refuse to delete it
       // This is common as ProcessDirectory above would CWD to
       // the directory to LIST it.

+ 4 - 2
core/HierarchicalStorage.cpp

@@ -823,7 +823,8 @@ bool __fastcall TIniFileStorage::ReadBool(const AnsiString Name, bool Default)
 //---------------------------------------------------------------------------
 int __fastcall TIniFileStorage::ReadInteger(const AnsiString Name, int Default)
 {
-  return FIniFile->ReadInteger(CurrentSection, MungeIniName(Name), Default);
+  int Result = FIniFile->ReadInteger(CurrentSection, MungeIniName(Name), Default);
+  return Result;
 }
 //---------------------------------------------------------------------------
 __int64 __fastcall TIniFileStorage::ReadInt64(const AnsiString Name, __int64 Default)
@@ -902,7 +903,8 @@ double __fastcall TIniFileStorage::ReadFloat(const AnsiString Name, double Defau
 //---------------------------------------------------------------------------
 AnsiString __fastcall TIniFileStorage::ReadStringRaw(const AnsiString Name, AnsiString Default)
 {
-  return FIniFile->ReadString(CurrentSection, MungeIniName(Name), Default);
+  AnsiString Result = FIniFile->ReadString(CurrentSection, MungeIniName(Name), Default);
+  return Result;
 }
 //---------------------------------------------------------------------------
 int __fastcall TIniFileStorage::ReadBinaryData(const AnsiString Name,

+ 15 - 6
core/PuttyIntf.cpp

@@ -594,12 +594,21 @@ bool __fastcall HasGSSAPI()
   static int has = -1;
   if (has < 0)
   {
-    Ssh_gss_ctx ctx;
-    memset(&ctx, 0, sizeof(ctx));
-    has =
-      ((ssh_gss_init() == 1) &&
-       (ssh_gss_acquire_cred(&ctx) == SSH_GSS_OK) &&
-       (ssh_gss_release_cred(&ctx) == SSH_GSS_OK)) ? 1 : 0;
+    ssh_gss_init();
+    for (int Index = 0; (has <= 0) && (Index < n_ssh_gss_libraries); Index++)
+    {
+      ssh_gss_library * library = &ssh_gss_libraries[Index];
+      Ssh_gss_ctx ctx;
+      memset(&ctx, 0, sizeof(ctx));
+      has =
+        ((library->acquire_cred(library, &ctx) == SSH_GSS_OK) &&
+         (library->release_cred(library, &ctx) == SSH_GSS_OK)) ? 1 : 0;
+    }
+
+    if (has < 0)
+    {
+      has = 0;
+    }
   }
   return (has > 0);
 }

+ 1 - 6
core/RemoteFiles.cpp

@@ -479,7 +479,7 @@ TRemoteToken & __fastcall TRemoteToken::operator =(const TRemoteToken & rht)
 //---------------------------------------------------------------------------
 int __fastcall TRemoteToken::Compare(const TRemoteToken & rht) const
 {
-  bool Result;
+  int Result;
   if (!FName.IsEmpty())
   {
     if (!rht.FName.IsEmpty())
@@ -598,16 +598,11 @@ void __fastcall TRemoteTokenList::Add(const TRemoteToken & Token)
   {
     std::pair<TIDMap::iterator, bool> Position =
       FIDMap.insert(TIDMap::value_type(Token.ID, FTokens.size() - 1));
-    // can indeed happen in real life,
-    // but in our test evironment, we want to know about it
-    assert(Position.second);
   }
   if (Token.NameValid)
   {
     std::pair<TNameMap::iterator, bool> Position =
       FNameMap.insert(TNameMap::value_type(Token.Name, FTokens.size() - 1));
-    // dtto
-    assert(Position.second);
   }
 }
 //---------------------------------------------------------------------------

+ 14 - 1
core/Script.cpp

@@ -361,12 +361,22 @@ void __fastcall TScript::SetSynchronizeParams(int value)
      TTerminal::spNotByTime | TTerminal::spBySize));
 }
 //---------------------------------------------------------------------------
+void __fastcall TScript::Log(TLogLineType Type, AnsiString Str)
+{
+  if ((Terminal != NULL) && Terminal->Log->Logging)
+  {
+    Terminal->Log->Add(Type, FORMAT("Script: %s", (Str)));
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TScript::Command(AnsiString Cmd)
 {
   try
   {
     if (!Cmd.Trim().IsEmpty() && (Cmd[1] != ';') && (Cmd[1] != '#'))
     {
+      Log(llInput, Cmd);
+
       if (FEcho)
       {
         PrintLine(Cmd);
@@ -448,8 +458,10 @@ TStrings * __fastcall TScript::CreateFileList(TScriptProcParams * Parameters, in
           for (int i = 0; i < FileList->Count; i++)
           {
             TRemoteFile * File = FileList->Files[i];
+            TFileMasks::TParams Params;
+            Params.Size = File->Size;
             if (!File->IsThisDirectory && !File->IsParentDirectory &&
-                Mask.Matches(File->FileName))
+                Mask.Matches(File->FileName, false, AnsiString(), &Params))
             {
               Result->AddObject(FileDirectory + File->FileName,
                 FLAGSET(ListType, fltQueryServer) ? File->Duplicate() : NULL);
@@ -579,6 +591,7 @@ void __fastcall TScript::Print(const AnsiString Str)
 //---------------------------------------------------------------------------
 void __fastcall TScript::PrintLine(const AnsiString Str)
 {
+  Log(llOutput, Str);
   Print(Str + "\n");
 }
 //---------------------------------------------------------------------------

+ 1 - 0
core/Script.h

@@ -38,6 +38,7 @@ public:
   virtual __fastcall ~TScript();
 
   void __fastcall Command(AnsiString Cmd);
+  void __fastcall Log(TLogLineType Type, AnsiString Str);
 
   void __fastcall Synchronize(const AnsiString LocalDirectory,
     const AnsiString RemoteDirectory, const TCopyParamType & CopyParam,

+ 4 - 0
core/SecureShell.cpp

@@ -290,6 +290,10 @@ void __fastcall TSecureShell::StoreToConfig(TSessionData * Data, Config * cfg, b
   // permanent settings
   cfg->nopty = TRUE;
   cfg->tcp_keepalives = 0;
+  for (int Index = 0; Index < n_ssh_gss_libraries; Index++)
+  {
+    cfg->ssh_gsslist[Index] = ssh_gss_libraries[Index].id;
+  }
 }
 //---------------------------------------------------------------------------
 void __fastcall TSecureShell::Open()

+ 5 - 9
core/SessionData.cpp

@@ -915,15 +915,7 @@ bool __fastcall TSessionData::ParseUrl(AnsiString Url, TOptions * Options,
   {
     AFSProtocol = fsFTP;
     AFtps = ftpsImplicit;
-    // adjust default port number to default Ftps mode
-    if (AFtps == ftpsImplicit)
-    {
-      APortNumber = FtpsImplicitPortNumber;
-    }
-    else
-    {
-      APortNumber = FtpPortNumber;
-    }
+    APortNumber = FtpsImplicitPortNumber;
     Url.Delete(1, 5);
     ProtocolDefined = true;
   }
@@ -1017,6 +1009,10 @@ bool __fastcall TSessionData::ParseUrl(AnsiString Url, TOptions * Options,
       else if (ProtocolDefined)
       {
         PortNumber = APortNumber;
+      }
+
+      if (ProtocolDefined)
+      {
         Ftps = AFtps;
       }
 

+ 2 - 1
core/SessionInfo.cpp

@@ -980,7 +980,8 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
       ADF("Return code variable: %s; Lookup user groups: %s",
         ((Data->DetectReturnVar ? AnsiString("Autodetect") : Data->ReturnVar),
          BooleanToEngStr(Data->LookupUserGroups)));
-      ADF("Shell: %s, EOL: %d", ((Data->Shell.IsEmpty()? AnsiString("default") : Data->Shell), Data->EOLType));
+      ADF("Shell: %s", ((Data->Shell.IsEmpty()? AnsiString("default") : Data->Shell)));
+      ADF("EOL: %d, UTF: %d", (Data->EOLType, Data->NotUtf));
       ADF("Clear aliases: %s, Unset nat.vars: %s, Resolve symlinks: %s",
         (BooleanToEngStr(Data->ClearAliases), BooleanToEngStr(Data->UnsetNationalVars),
          BooleanToEngStr(Data->ResolveSymlinks)));

+ 9 - 6
core/SftpFileSystem.cpp

@@ -1661,7 +1661,7 @@ __fastcall TSFTPFileSystem::TSFTPFileSystem(TTerminal * ATerminal,
 {
   FSecureShell = SecureShell;
   FPacketReservations = new TList();
-  FPacketNumbers = VarArrayCreate(OPENARRAY(int, (0, 1)), varInteger);
+  FPacketNumbers = VarArrayCreate(OPENARRAY(int, (0, 1)), varLongWord);
   FPreviousLoggedPacket = 0;
   FNotLoggedPackets = 0;
   FBusy = 0;
@@ -2214,13 +2214,13 @@ int __fastcall TSFTPFileSystem::ReceivePacket(TSFTPPacket * Packet,
       }
 
       if (Reservation < 0 ||
-          Packet->MessageNumber != (unsigned long)FPacketNumbers.GetElement(Reservation))
+          Packet->MessageNumber != (unsigned int)FPacketNumbers.GetElement(Reservation))
       {
         TSFTPPacket * ReservedPacket;
-        unsigned long MessageNumber;
+        unsigned int MessageNumber;
         for (int Index = 0; Index < FPacketReservations->Count; Index++)
         {
-          MessageNumber = FPacketNumbers.GetElement(Index);
+          MessageNumber = (unsigned int)FPacketNumbers.GetElement(Index);
           if (MessageNumber == Packet->MessageNumber)
           {
             ReservedPacket = (TSFTPPacket *)FPacketReservations->Items[Index];
@@ -2254,7 +2254,7 @@ int __fastcall TSFTPFileSystem::ReceivePacket(TSFTPPacket * Packet,
   // (and it have not worked anyway until recent fix to UnreserveResponse)
   if (Reservation >= 0)
   {
-    assert(Packet->MessageNumber == (unsigned long)FPacketNumbers.GetElement(Reservation));
+    assert(Packet->MessageNumber == (unsigned int)FPacketNumbers.GetElement(Reservation));
     RemoveReservation(Reservation);
   }
 
@@ -2336,7 +2336,10 @@ int __fastcall TSFTPFileSystem::ReceiveResponse(
   }
   __finally
   {
-    if (!Response) delete AResponse;
+    if (!Response)
+    {
+      delete AResponse;
+    }
   }
   return Result;
 }

+ 7 - 3
forms/Copy.cpp

@@ -154,6 +154,7 @@ void __fastcall TCopyDialog::AdjustTransferControls()
 
   EnableControl(NewerOnlyCheck, FLAGCLEAR(Options, coDisableNewerOnly) && !RemoteTransfer);
   EnableControl(TransferSettingsButton, !RemoteTransfer);
+  EnableControl(CopyParamGroup, !RemoteTransfer);
 }
 //---------------------------------------------------------------------------
 void __fastcall TCopyDialog::AdjustControls()
@@ -317,7 +318,7 @@ void __fastcall TCopyDialog::UpdateControls()
   if (!ToRemote && FLAGSET(Options, coAllowRemoteTransfer))
   {
     AnsiString Directory = DirectoryEdit->Text;
-    bool RemoteTransfer = (Directory.Pos("\\") == 0) && (Directory.Pos("/") > 0);
+    bool RemoteTransfer = !Directory.IsEmpty() && ExtractFileDrive(Directory).IsEmpty();
     if (RemoteTransfer != FLAGSET(FOutputOptions, cooRemoteTransfer))
     {
       FOutputOptions =
@@ -490,9 +491,12 @@ void __fastcall TCopyDialog::HelpButtonClick(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 void __fastcall TCopyDialog::CopyParamGroupDblClick(TObject * /*Sender*/)
 {
-  if (DoCopyParamCustomDialog(FCopyParams, CopyParamAttrs))
+  if (CopyParamGroup->Enabled)
   {
-    UpdateControls();
+    if (DoCopyParamCustomDialog(FCopyParams, CopyParamAttrs))
+    {
+      UpdateControls();
+    }
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 2
forms/FileSystemInfo.dfm

@@ -1,6 +1,6 @@
 object FileSystemInfoDialog: TFileSystemInfoDialog
-  Left = 320
-  Top = 130
+  Left = 165
+  Top = 69
   HelpType = htKeyword
   HelpKeyword = 'ui_fsinfo'
   BorderIcons = [biSystemMenu, biMinimize, biMaximize, biHelp]

+ 7 - 19
forms/Login.cpp

@@ -1098,6 +1098,7 @@ void __fastcall TLoginDialog::UpdateControls()
       EnableControl(EOLTypeCombo, (FSProtocol != fsFTP) && EnvironmentSheet->Enabled);
       EnableControl(EOLTypeLabel, EOLTypeCombo->Enabled);
       EnableControl(DSTModeGroup, (FSProtocol != fsFTP) && EnvironmentSheet->Enabled);
+      EnableControl(DSTModeKeepCheck, UsesDaylightHack() && DSTModeGroup->Enabled);
       EnableControl(UtfCombo, (FSProtocol != fsSCPonly) && EnvironmentSheet->Enabled);
       EnableControl(UtfLabel, UtfCombo->Enabled);
       // should be enabled for fsSFTP (SCP fallback) too, but it would cause confusion
@@ -1330,12 +1331,6 @@ void __fastcall TLoginDialog::SaveSessionActionExecute(TObject * /*Sender*/)
   }
 
   AnsiString SessionName = FSessionData->SessionName;
-  // when saving new session, save by default to current session folder
-  if (FEditingSessionData == NULL)
-  {
-    SessionName.Insert(
-      UnixIncludeTrailingBackslash(SessionNodePath(CurrentSessionFolderNode())), 1);
-  }
   if (DoSaveSessionDialog(SessionName, PSavePassword, FEditingSessionData))
   {
     if ((PSavePassword != NULL) && !*PSavePassword)
@@ -1804,6 +1799,12 @@ void __fastcall TLoginDialog::SetDefaultSessionActionExecute(
         qaOK | qaCancel, HELP_SESSION_SAVE_DEFAULT) == qaOK)
   {
     SaveSession(FSessionData);
+    if (!Configuration->DisablePasswordStoring &&
+        FSessionData->HasAnyPassword() &&
+        CustomWinConfiguration->UseMasterPassword)
+    {
+      CustomWinConfiguration->AskForMasterPasswordIfNotSet();
+    }
     StoredSessions->DefaultSettings = FSessionData;
   }
 }
@@ -2206,19 +2207,6 @@ void __fastcall TLoginDialog::SessionTreeEdited(TObject * /*Sender*/,
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TLoginDialog::UnixEnvironmentButtonClick(TObject * /*Sender*/)
-{
-  EOLTypeCombo->ItemIndex = 0;
-  DSTModeUnixCheck->Checked = true;
-}
-//---------------------------------------------------------------------------
-void __fastcall TLoginDialog::WindowsEnvironmentButtonClick(
-  TObject * /*Sender*/)
-{
-  EOLTypeCombo->ItemIndex = 1;
-  DSTModeWinCheck->Checked = true;
-}
-//---------------------------------------------------------------------------
 void __fastcall TLoginDialog::PathEditBeforeDialog(TObject * /*Sender*/,
   AnsiString & Name, bool & /*Action*/)
 {

+ 6 - 24
forms/Login.dfm

@@ -474,18 +474,18 @@ object LoginDialog: TLoginDialog
           286)
         object EnvironmentOtherLabel: TLabel
           Left = 0
-          Top = 244
+          Top = 211
           Width = 68
           Height = 13
           Caption = 'Other settings:'
         end
         object RecycleBinLinkLabel: TStaticText
           Left = 88
-          Top = 244
+          Top = 211
           Width = 60
           Height = 17
           Caption = 'Recycle bin'
-          TabOrder = 4
+          TabOrder = 2
           TabStop = True
           OnClick = RecycleBinLinkLabelClick
         end
@@ -608,7 +608,7 @@ object LoginDialog: TLoginDialog
             Width = 317
             Height = 17
             Anchors = [akLeft, akTop, akRight]
-            Caption = 'Adjust remote timestamp to local co&nventions (Unix)'
+            Caption = 'Adjust remote timestamp to local co&nventions'
             TabOrder = 0
             OnClick = DataChange
           end
@@ -618,7 +618,7 @@ object LoginDialog: TLoginDialog
             Width = 317
             Height = 17
             Anchors = [akLeft, akTop, akRight]
-            Caption = 'Adjust remote timestamp with &DST (Windows)'
+            Caption = 'Adjust remote timestamp with &DST'
             TabOrder = 1
             OnClick = DataChange
           end
@@ -628,29 +628,11 @@ object LoginDialog: TLoginDialog
             Width = 317
             Height = 17
             Anchors = [akLeft, akTop, akRight]
-            Caption = 'Preser&ve remote timestamp (Unix)'
+            Caption = 'Preser&ve remote timestamp'
             TabOrder = 2
             OnClick = DataChange
           end
         end
-        object UnixEnvironmentButton: TButton
-          Left = 0
-          Top = 211
-          Width = 75
-          Height = 25
-          Caption = 'Uni&x'
-          TabOrder = 2
-          OnClick = UnixEnvironmentButtonClick
-        end
-        object WindowsEnvironmentButton: TButton
-          Left = 88
-          Top = 211
-          Width = 75
-          Height = 25
-          Caption = '&Windows'
-          TabOrder = 3
-          OnClick = WindowsEnvironmentButtonClick
-        end
       end
       object DirectoriesSheet: TTabSheet
         Tag = 2

+ 0 - 4
forms/Login.h

@@ -235,8 +235,6 @@ __published:
   TLabel *Label21;
   TComboBox *TunnelLocalPortNumberEdit;
   TRadioButton *DSTModeKeepCheck;
-  TButton *UnixEnvironmentButton;
-  TButton *WindowsEnvironmentButton;
   TComboBox *TransferProtocolCombo;
   TLabel *Label22;
   TCheckBox *AllowScpFallbackCheck;
@@ -334,8 +332,6 @@ __published:
   void __fastcall ColorDefaultItemClick(TObject *Sender);
   void __fastcall PickColorItemClick(TObject *Sender);
   void __fastcall RenameSessionActionExecute(TObject * Sender);
-  void __fastcall UnixEnvironmentButtonClick(TObject *Sender);
-  void __fastcall WindowsEnvironmentButtonClick(TObject *Sender);
   void __fastcall PathEditBeforeDialog(TObject *Sender, AnsiString &Name,
     bool &Action);
   void __fastcall TransferProtocolComboChange(TObject *Sender);

+ 15 - 4
forms/Progress.cpp

@@ -47,6 +47,17 @@ __fastcall TProgressForm::TProgressForm(TComponent* AOwner)
   UseSystemSettings(this);
   ResetOnceDoneOperation();
 
+  if (CustomWinConfiguration->OperationProgressOnTop)
+  {
+    FOperationProgress = TopProgress;
+    FFileProgress = BottomProgress;
+  }
+  else
+  {
+    FOperationProgress = BottomProgress;
+    FFileProgress = TopProgress;
+  }
+
   if (!IsGlobalMinimizeHandler())
   {
     SetGlobalMinimizeHandler(GlobalMinimize);
@@ -186,8 +197,8 @@ void __fastcall TProgressForm::UpdateControls()
     FileLabel->Caption = ExtractFileName(FData.FileName);
   }
   int OverallProgress = FData.OverallProgress();
-  OperationProgress->Position = OverallProgress;
-  OperationProgress->Hint = FORMAT("%d%%", (OverallProgress));
+  FOperationProgress->Position = OverallProgress;
+  FOperationProgress->Hint = FORMAT("%d%%", (OverallProgress));
   Caption = FORMAT("%d%% %s", (OverallProgress, OperationName(FData.Operation)));
 
   if (TransferOperation)
@@ -210,8 +221,8 @@ void __fastcall TProgressForm::UpdateControls()
     TimeElapsedLabel->Caption = FormatDateTimeSpan(Configuration->TimeFormat, FData.TimeElapsed());
     BytesTransferedLabel->Caption = FormatBytes(FData.TotalTransfered);
     CPSLabel->Caption = FORMAT("%s/s", (FormatBytes(FData.CPS())));
-    FileProgress->Position = FData.TransferProgress();
-    FileProgress->Hint = FORMAT("%d%%", (FileProgress->Position));
+    FFileProgress->Position = FData.TransferProgress();
+    FFileProgress->Hint = FORMAT("%d%%", (FFileProgress->Position));
   }
 }
 //---------------------------------------------------------------------

+ 2 - 2
forms/Progress.dfm

@@ -105,7 +105,7 @@ object ProgressForm: TProgressForm
       Anchors = [akLeft, akTop, akRight]
       AutoSize = False
     end
-    object OperationProgress: TProgressBar
+    object TopProgress: TProgressBar
       Left = 0
       Top = 42
       Width = 301
@@ -213,7 +213,7 @@ object ProgressForm: TProgressForm
       Anchors = [akTop, akRight]
       Caption = 'Speed:'
     end
-    object FileProgress: TProgressBar
+    object BottomProgress: TProgressBar
       Left = 0
       Top = 37
       Width = 301

+ 4 - 2
forms/Progress.h

@@ -29,7 +29,7 @@ __published:
   TPathLabel *FileLabel;
   TLabel *TargetLabel;
   TPathLabel *TargetPathLabel;
-  TProgressBar *OperationProgress;
+  TProgressBar *TopProgress;
   TPanel *TransferPanel;
   TLabel *Label3;
   TLabel *TimeElapsedLabel;
@@ -39,7 +39,7 @@ __published:
   TLabel *BytesTransferedLabel;
   TLabel *Label12;
   TLabel *CPSLabel;
-  TProgressBar *FileProgress;
+  TProgressBar *BottomProgress;
   TTimer *UpdateTimer;
   TPanel *SpeedPanel;
   TLabel *SpeedLabel2;
@@ -74,6 +74,8 @@ private:
   bool FReadOnly;
   unsigned long FCPSLimit;
   TOnceDoneOperation FOnceDoneOperation;
+  TProgressBar * FOperationProgress;
+  TProgressBar * FFileProgress;
 
   void __fastcall SetOnceDoneOperation(TOnceDoneOperation value);
   void __fastcall SetAllowMinimize(bool value);

+ 17 - 2
packages/filemng/DirView.pas

@@ -555,6 +555,9 @@ uses
   ShellDialogs, IEDriveInfo,
   FileChanges, Math;
 
+var
+  DaylightHack: Boolean;
+
 procedure Register;
 begin
   RegisterComponents('DriveDir', [TDirView]);
@@ -576,10 +579,19 @@ end; {MatchesFileExt}
 function FileTimeToDateTime(FileTime: TFileTime): TDateTime;
 var
   SysTime: TSystemTime;
+  UniverzalSysTime: TSystemTime;
   LocalFileTime: TFileTime;
 begin
-  FileTimeToLocalFileTime(FileTime, LocalFileTime);
-  FileTimeToSystemTime(LocalFileTime, SysTime);
+  if not DaylightHack then
+  begin
+    FileTimeToSystemTime(FileTime, UniverzalSysTime);
+    SystemTimeToTzSpecificLocalTime(nil, UniverzalSysTime, SysTime);
+  end
+    else
+  begin
+    FileTimeToLocalFileTime(FileTime, LocalFileTime);
+    FileTimeToSystemTime(LocalFileTime, SysTime);
+  end;
   Result := SystemTimeToDateTime(SysTime);
 end;
 
@@ -4358,4 +4370,7 @@ end;
 initialization
   LastClipBoardOperation := cboNone;
   LastIOResult := 0;
+  DaylightHack := not
+    ((Win32MajorVersion > 6) or
+     ((Win32MajorVersion = 6) and (Win32MinorVersion >= 1)));
 end.

+ 105 - 0
putty/PGSSAPI.C

@@ -0,0 +1,105 @@
+/* This file actually defines the GSSAPI function pointers for
+ * functions we plan to import from a GSSAPI library.
+ */
+#include "putty.h"
+
+#ifndef NO_GSSAPI
+
+#include "pgssapi.h"
+
+#ifndef NO_LIBDL
+
+/* Reserved static storage for GSS_oids.  Comments are quotes from RFC 2744. */
+static const gss_OID_desc oids[] = {
+    /* The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) member-body(2) United States(840) mit(113554)
+     * infosys(1) gssapi(2) generic(1) user_name(1)}.  The constant
+     * GSS_C_NT_USER_NAME should be initialized to point
+     * to that gss_OID_desc.
+
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) member-body(2) United States(840) mit(113554)
+     * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}.
+     * The constant GSS_C_NT_MACHINE_UID_NAME should be
+     * initialized to point to that gss_OID_desc.
+
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) member-body(2) United States(840) mit(113554)
+     * infosys(1) gssapi(2) generic(1) string_uid_name(3)}.
+     * The constant GSS_C_NT_STRING_UID_NAME should be
+     * initialized to point to that gss_OID_desc.
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {6, (void *)"\x2b\x06\x01\x05\x06\x02"},
+    /* corresponding to an object-identifier value of
+     * {iso(1) org(3) dod(6) internet(1) security(5)
+     * nametypes(6) gss-host-based-services(2)).  The constant
+     * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point
+     * to that gss_OID_desc.  This is a deprecated OID value, and
+     * implementations wishing to support hostbased-service names
+     * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID,
+     * defined below, to identify such names;
+     * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym
+     * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input
+     * parameter, but should not be emitted by GSS-API
+     * implementations
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+     {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"},
+    /* corresponding to an object-identifier value of {iso(1)
+     * member-body(2) Unites States(840) mit(113554) infosys(1)
+     * gssapi(2) generic(1) service_name(4)}.  The constant
+     * GSS_C_NT_HOSTBASED_SERVICE should be initialized
+     * to point to that gss_OID_desc.
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {6, (void *)"\x2b\x06\01\x05\x06\x03"},
+    /* corresponding to an object identifier value of
+     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+     * 6(nametypes), 3(gss-anonymous-name)}.  The constant
+     * and GSS_C_NT_ANONYMOUS should be initialized to point
+     * to that gss_OID_desc.
+     *
+     * The implementation must reserve static storage for a
+     * gss_OID_desc object containing the value */
+    {6, (void *)"\x2b\x06\x01\x05\x06\x04"},
+     /* corresponding to an object-identifier value of
+     * {1(iso), 3(org), 6(dod), 1(internet), 5(security),
+     * 6(nametypes), 4(gss-api-exported-name)}.  The constant
+     * GSS_C_NT_EXPORT_NAME should be initialized to point
+     * to that gss_OID_desc.
+     */
+};
+
+/* Here are the constants which point to the static structure above.
+ *
+ * Constants of the form GSS_C_NT_* are specified by rfc 2744.
+ */
+const_gss_OID GSS_C_NT_USER_NAME           = oids+0;
+const_gss_OID GSS_C_NT_MACHINE_UID_NAME    = oids+1;
+const_gss_OID GSS_C_NT_STRING_UID_NAME     = oids+2;
+const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3;
+const_gss_OID GSS_C_NT_HOSTBASED_SERVICE   = oids+4;
+const_gss_OID GSS_C_NT_ANONYMOUS           = oids+5;
+const_gss_OID GSS_C_NT_EXPORT_NAME         = oids+6;
+
+#endif /* NO_LIBDL */
+
+static gss_OID_desc gss_mech_krb5_desc =
+{ 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+/* iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2)*/
+const gss_OID GSS_MECH_KRB5 = &gss_mech_krb5_desc;
+
+#endif /* NO_GSSAPI */

+ 296 - 0
putty/PGSSAPI.H

@@ -0,0 +1,296 @@
+#ifndef PUTTY_PGSSAPI_H
+#define PUTTY_PGSSAPI_H
+
+#include "putty.h"
+
+#ifndef NO_GSSAPI
+
+/*
+ * On Unix, if we're statically linking against GSSAPI, we leave the
+ * declaration of all this lot to the official header. If we're
+ * dynamically linking, we declare it ourselves, because that avoids
+ * us needing the official header at compile time.
+ *
+ * However, we still need the function pointer types, because even
+ * with statically linked GSSAPI we use the ssh_gss_library wrapper.
+ */
+#ifdef STATIC_GSSAPI
+#include <gssapi/gssapi.h>
+typedef gss_OID const_gss_OID;	       /* for our prototypes below */
+#else /* STATIC_GSSAPI */
+
+/*******************************************************************************
+ *  GSSAPI Definitions, taken from RFC 2744
+ ******************************************************************************/
+
+/* GSSAPI Type Definitions */
+typedef uint32 OM_uint32;
+
+typedef struct gss_OID_desc_struct {
+    OM_uint32 length;
+    void *elements;
+} gss_OID_desc;
+typedef const gss_OID_desc *const_gss_OID;
+typedef gss_OID_desc *gss_OID;
+
+typedef struct gss_OID_set_desc_struct  {
+    size_t  count;
+    gss_OID elements;
+} gss_OID_set_desc;
+typedef const gss_OID_set_desc *const_gss_OID_set;
+typedef gss_OID_set_desc *gss_OID_set;
+
+typedef struct gss_buffer_desc_struct {
+    size_t length;
+    void *value;
+} gss_buffer_desc, *gss_buffer_t;
+
+typedef struct gss_channel_bindings_struct {
+    OM_uint32 initiator_addrtype;
+    gss_buffer_desc initiator_address;
+    OM_uint32 acceptor_addrtype;
+    gss_buffer_desc acceptor_address;
+    gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+
+typedef void * gss_ctx_id_t;
+typedef void * gss_name_t;
+typedef void * gss_cred_id_t;
+
+typedef OM_uint32 gss_qop_t;
+
+/* Flag bits for context-level services. */
+
+#define GSS_C_DELEG_FLAG      1
+#define GSS_C_MUTUAL_FLAG     2
+#define GSS_C_REPLAY_FLAG     4
+#define GSS_C_SEQUENCE_FLAG   8
+#define GSS_C_CONF_FLAG       16
+#define GSS_C_INTEG_FLAG      32
+#define GSS_C_ANON_FLAG       64
+#define GSS_C_PROT_READY_FLAG 128
+#define GSS_C_TRANS_FLAG      256
+
+/* Credential usage options */
+#define GSS_C_BOTH     0
+#define GSS_C_INITIATE 1
+#define GSS_C_ACCEPT   2
+
+/* Status code types for gss_display_status */
+#define GSS_C_GSS_CODE  1
+#define GSS_C_MECH_CODE 2
+
+/* The constant definitions for channel-bindings address families */
+#define GSS_C_AF_UNSPEC     0
+#define GSS_C_AF_LOCAL      1
+#define GSS_C_AF_INET       2
+#define GSS_C_AF_IMPLINK    3
+#define GSS_C_AF_PUP        4
+#define GSS_C_AF_CHAOS      5
+#define GSS_C_AF_NS         6
+#define GSS_C_AF_NBS        7
+#define GSS_C_AF_ECMA       8
+#define GSS_C_AF_DATAKIT    9
+#define GSS_C_AF_CCITT      10
+#define GSS_C_AF_SNA        11
+#define GSS_C_AF_DECnet     12
+#define GSS_C_AF_DLI        13
+#define GSS_C_AF_LAT        14
+#define GSS_C_AF_HYLINK     15
+#define GSS_C_AF_APPLETALK  16
+#define GSS_C_AF_BSC        17
+#define GSS_C_AF_DSS        18
+#define GSS_C_AF_OSI        19
+#define GSS_C_AF_X25        21
+
+#define GSS_C_AF_NULLADDR   255
+
+/* Various Null values */
+#define GSS_C_NO_NAME ((gss_name_t) 0)
+#define GSS_C_NO_BUFFER ((gss_buffer_t) 0)
+#define GSS_C_NO_OID ((gss_OID) 0)
+#define GSS_C_NO_OID_SET ((gss_OID_set) 0)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0)
+#define GSS_C_EMPTY_BUFFER {0, NULL}
+
+/* Major status codes */
+#define GSS_S_COMPLETE 0
+
+/* Some "helper" definitions to make the status code macros obvious. */
+#define GSS_C_CALLING_ERROR_OFFSET 24
+#define GSS_C_ROUTINE_ERROR_OFFSET 16
+
+#define GSS_C_SUPPLEMENTARY_OFFSET 0
+#define GSS_C_CALLING_ERROR_MASK 0377ul
+#define GSS_C_ROUTINE_ERROR_MASK 0377ul
+#define GSS_C_SUPPLEMENTARY_MASK 0177777ul
+
+/*
+ * The macros that test status codes for error conditions.
+ * Note that the GSS_ERROR() macro has changed slightly from
+ * the V1 GSS-API so that it now evaluates its argument
+ * only once.
+ */
+#define GSS_CALLING_ERROR(x)                                            \
+    (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
+#define GSS_ROUTINE_ERROR(x)                                            \
+    (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
+#define GSS_SUPPLEMENTARY_INFO(x)                                       \
+    (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
+#define GSS_ERROR(x)                                                    \
+    (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) |    \
+          (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
+
+/* Now the actual status code definitions */
+
+/* Calling errors: */
+#define GSS_S_CALL_INACCESSIBLE_READ            \
+    (1ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_INACCESSIBLE_WRITE           \
+    (2ul << GSS_C_CALLING_ERROR_OFFSET)
+#define GSS_S_CALL_BAD_STRUCTURE                \
+    (3ul << GSS_C_CALLING_ERROR_OFFSET)
+
+/* Routine errors: */
+#define GSS_S_BAD_MECH             (1ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAME             (2ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_NAMETYPE         (3ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_BINDINGS         (4ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_STATUS           (5ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_SIG              (6ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_MIC GSS_S_BAD_SIG
+#define GSS_S_NO_CRED              (7ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NO_CONTEXT           (8ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_TOKEN      (9ul <<                      \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DEFECTIVE_CREDENTIAL (10ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CREDENTIALS_EXPIRED  (11ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_CONTEXT_EXPIRED      (12ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_FAILURE              (13ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_BAD_QOP              (14ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAUTHORIZED         (15ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_UNAVAILABLE          (16ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_DUPLICATE_ELEMENT    (17ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+#define GSS_S_NAME_NOT_MN          (18ul <<                     \
+                                    GSS_C_ROUTINE_ERROR_OFFSET)
+
+/* Supplementary info bits: */
+#define GSS_S_CONTINUE_NEEDED                                           \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
+#define GSS_S_DUPLICATE_TOKEN                                           \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
+#define GSS_S_OLD_TOKEN                                                 \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
+#define GSS_S_UNSEQ_TOKEN                                               \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
+#define GSS_S_GAP_TOKEN                                                 \
+                           (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
+
+extern const_gss_OID GSS_C_NT_USER_NAME;
+extern const_gss_OID GSS_C_NT_MACHINE_UID_NAME;
+extern const_gss_OID GSS_C_NT_STRING_UID_NAME;
+extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X;
+extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE;
+extern const_gss_OID GSS_C_NT_ANONYMOUS;
+extern const_gss_OID GSS_C_NT_EXPORT_NAME;
+
+#endif /* STATIC_GSSAPI */
+
+extern const gss_OID GSS_MECH_KRB5;
+
+/* GSSAPI functions we use.
+ * TODO: Replace with all GSSAPI functions from RFC?
+ */
+
+/* Calling convention, just in case we need one. */
+#ifndef GSS_CC
+#define GSS_CC
+#endif /*GSS_CC*/
+
+typedef OM_uint32 (GSS_CC *t_gss_release_cred)
+            (OM_uint32                    * /*minor_status*/,
+             gss_cred_id_t                * /*cred_handle*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_init_sec_context)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_cred_id_t            /*initiator_cred_handle*/,
+             gss_ctx_id_t                 * /*context_handle*/,
+             const gss_name_t               /*target_name*/,
+             const gss_OID                  /*mech_type*/,
+             OM_uint32                      /*req_flags*/,
+             OM_uint32                      /*time_req*/,
+             const gss_channel_bindings_t   /*input_chan_bindings*/,
+             const gss_buffer_t             /*input_token*/,
+             gss_OID                      * /*actual_mech_type*/,
+             gss_buffer_t                   /*output_token*/,
+             OM_uint32                    * /*ret_flags*/,
+             OM_uint32                    * /*time_rec*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_delete_sec_context)
+            (OM_uint32                    * /*minor_status*/,
+             gss_ctx_id_t                 * /*context_handle*/,
+             gss_buffer_t                   /*output_token*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_get_mic)
+            (OM_uint32                    * /*minor_status*/,
+             const gss_ctx_id_t             /*context_handle*/,
+             gss_qop_t                      /*qop_req*/,
+             const gss_buffer_t             /*message_buffer*/,
+             gss_buffer_t                   /*msg_token*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_display_status)
+            (OM_uint32                   * /*minor_status*/,
+             OM_uint32                     /*status_value*/,
+             int                           /*status_type*/,
+             const gss_OID                 /*mech_type*/,
+             OM_uint32                   * /*message_context*/,
+             gss_buffer_t                  /*status_string*/);
+
+
+typedef OM_uint32 (GSS_CC *t_gss_import_name)
+            (OM_uint32                   * /*minor_status*/,
+             const gss_buffer_t            /*input_name_buffer*/,
+             const_gss_OID                 /*input_name_type*/,
+             gss_name_t                  * /*output_name*/);
+
+
+typedef OM_uint32 (GSS_CC *t_gss_release_name)
+            (OM_uint32                   * /*minor_status*/,
+             gss_name_t                  * /*name*/);
+
+typedef OM_uint32 (GSS_CC *t_gss_release_buffer)
+            (OM_uint32                   * /*minor_status*/,
+             gss_buffer_t                  /*buffer*/);
+
+struct gssapi_functions {
+    t_gss_delete_sec_context delete_sec_context;
+    t_gss_display_status display_status;
+    t_gss_get_mic get_mic;
+    t_gss_import_name import_name;
+    t_gss_init_sec_context init_sec_context;
+    t_gss_release_buffer release_buffer;
+    t_gss_release_cred release_cred;
+    t_gss_release_name release_name;
+};
+
+#endif /* NO_GSSAPI */
+
+#endif /* PUTTY_PGSSAPI_H */

+ 86 - 33
putty/SSH.C

@@ -13,6 +13,7 @@
 #include "tree234.h"
 #include "ssh.h"
 #ifndef NO_GSSAPI
+#include "sshgssc.h"
 #include "sshgss.h"
 #endif
 
@@ -194,6 +195,7 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_SSH2_REKEY                           64
 #define BUG_SSH2_PK_SESSIONID                   128
 #define BUG_SSH2_MAXPKT				256
+#define BUG_CHOKES_ON_SSH2_IGNORE               512
 
 /*
  * Codes for terminal modes.
@@ -2015,7 +2017,8 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
 {
     int len;
     if (ssh->cscipher != NULL && (ssh->cscipher->flags & SSH_CIPHER_IS_CBC) &&
-	ssh->deferred_len == 0 && !noignore) {
+	ssh->deferred_len == 0 && !noignore &&
+	!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
 	/*
 	 * Interpose an SSH_MSG_IGNORE to ensure that user data don't
 	 * get encrypted with a known IV.
@@ -2145,7 +2148,8 @@ static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
 	 * unavailable, we don't do this trick at all, because we
 	 * gain nothing by it.)
 	 */
-	if (ssh->cscipher) {
+	if (ssh->cscipher &&
+	    !(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
 	    int stringlen, i;
 
 	    stringlen = (256 - ssh->deferred_len);
@@ -2512,6 +2516,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
 	ssh->remote_bugs |= BUG_SSH2_MAXPKT;
 	logevent("We believe remote version ignores SSH-2 maximum packet size");
     }
+
+    if (ssh->cfg.sshbug_ignore2 == FORCE_ON) {
+	/*
+	 * Servers that don't support SSH2_MSG_IGNORE. Currently,
+	 * none detected automatically.
+	 */
+	ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
+	logevent("We believe remote version has SSH-2 ignore bug");
+    }
 }
 
 /*
@@ -6679,11 +6692,13 @@ static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
 	 * wrap up and close the channel ourselves.
 	 */
 	x11_close(c->u.x11.s);
+	c->u.x11.s = NULL;
 	sshfwd_close(c);
     } else if (c->type == CHAN_AGENT) {
 	sshfwd_close(c);
     } else if (c->type == CHAN_SOCKDATA) {
 	pfd_close(c->u.pfd.s);
+	c->u.pfd.s = NULL;
 	sshfwd_close(c);
     }
 }
@@ -7204,6 +7219,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 	int num_env, env_left, env_ok;
 	struct Packet *pktout;
 #ifndef NO_GSSAPI
+	struct ssh_gss_library *gsslib;
 	Ssh_gss_ctx gss_ctx;
 	Ssh_gss_buf gss_buf;
 	Ssh_gss_buf gss_rcvtok, gss_sndtok;
@@ -7582,9 +7598,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		s->can_keyb_inter = ssh->cfg.try_ki_auth &&
 		    in_commasep_string("keyboard-interactive", methods, methlen);
 #ifndef NO_GSSAPI		
+		ssh_gss_init();
 		s->can_gssapi = ssh->cfg.try_gssapi_auth &&
-		  in_commasep_string("gssapi-with-mic", methods, methlen) &&
-		  ssh_gss_init();
+		    in_commasep_string("gssapi-with-mic", methods, methlen) &&
+		    n_ssh_gss_libraries > 0;
 #endif
 	    }
 
@@ -7927,6 +7944,35 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		s->gotit = TRUE;
 		ssh->pkt_actx = SSH2_PKTCTX_GSSAPI;
 
+		/*
+		 * Pick the highest GSS library on the preference
+		 * list.
+		 */
+		{
+		    int i, j;
+		    s->gsslib = NULL;
+		    for (i = 0; i < ngsslibs; i++) {
+			int want_id = ssh->cfg.ssh_gsslist[i];
+			for (j = 0; j < n_ssh_gss_libraries; j++)
+			    if (ssh_gss_libraries[j].id == want_id) {
+				s->gsslib = &ssh_gss_libraries[j];
+				goto got_gsslib;   /* double break */
+			    }
+		    }
+		    got_gsslib:
+		    /*
+		     * We always expect to have found something in
+		     * the above loop: we only came here if there
+		     * was at least one viable GSS library, and the
+		     * preference list should always mention
+		     * everything and only change the order.
+		     */
+		    assert(s->gsslib);
+		}
+
+		if (s->gsslib->gsslogmsg)
+		    logevent(s->gsslib->gsslogmsg);
+
 		/* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */
 		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
 		ssh2_pkt_addstring(s->pktout, s->username);
@@ -7934,7 +7980,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		ssh2_pkt_addstring(s->pktout, "gssapi-with-mic");
 
 		/* add mechanism info */
-		ssh_gss_indicate_mech(&s->gss_buf);
+		s->gsslib->indicate_mech(s->gsslib, &s->gss_buf);
 
 		/* number of GSSAPI mechanisms */
 		ssh2_pkt_adduint32(s->pktout,1);
@@ -7970,8 +8016,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		}
 
 		/* now start running */
-		s->gss_stat = ssh_gss_import_name(ssh->fullhostname,
-						  &s->gss_srv_name);
+		s->gss_stat = s->gsslib->import_name(s->gsslib,
+						     ssh->fullhostname,
+						     &s->gss_srv_name);
 		if (s->gss_stat != SSH_GSS_OK) {
 		    if (s->gss_stat == SSH_GSS_BAD_HOST_NAME)
 			logevent("GSSAPI import name failed - Bad service name");
@@ -7981,11 +8028,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		}
 
 		/* fetch TGT into GSS engine */
-		s->gss_stat = ssh_gss_acquire_cred(&s->gss_ctx);
+		s->gss_stat = s->gsslib->acquire_cred(s->gsslib, &s->gss_ctx);
 
 		if (s->gss_stat != SSH_GSS_OK) {
 		    logevent("GSSAPI authentication failed to get credentials");
-		    ssh_gss_release_name(&s->gss_srv_name);
+		    s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
 		    continue;
 		}
 
@@ -7995,17 +8042,20 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
 		/* now enter the loop */
 		do {
-		    s->gss_stat = ssh_gss_init_sec_context(&s->gss_ctx,
-							   s->gss_srv_name,
-							   ssh->cfg.gssapifwd,
-							   &s->gss_rcvtok,
-							   &s->gss_sndtok);
+		    s->gss_stat = s->gsslib->init_sec_context
+			(s->gsslib,
+			 &s->gss_ctx,
+			 s->gss_srv_name,
+			 ssh->cfg.gssapifwd,
+			 &s->gss_rcvtok,
+			 &s->gss_sndtok);
 
 		    if (s->gss_stat!=SSH_GSS_S_COMPLETE &&
 			s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) {
 			logevent("GSSAPI authentication initialisation failed");
 
-			if (ssh_gss_display_status(s->gss_ctx,&s->gss_buf) == SSH_GSS_OK) {
+			if (s->gsslib->display_status(s->gsslib, s->gss_ctx,
+						      &s->gss_buf) == SSH_GSS_OK) {
 			    logevent(s->gss_buf.value);
 			    sfree(s->gss_buf.value);
 			}
@@ -8022,7 +8072,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 			ssh_pkt_addstring_start(s->pktout);
 			ssh_pkt_addstring_data(s->pktout,s->gss_sndtok.value,s->gss_sndtok.length);
 			ssh2_pkt_send(ssh, s->pktout);
-			ssh_gss_free_tok(&s->gss_sndtok);
+			s->gsslib->free_tok(s->gsslib, &s->gss_sndtok);
 		    }
 
 		    if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) {
@@ -8039,8 +8089,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		} while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED);
 
 		if (s->gss_stat != SSH_GSS_OK) {
-		    ssh_gss_release_name(&s->gss_srv_name);
-		    ssh_gss_release_cred(&s->gss_ctx);
+		    s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+		    s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
 		    continue;
 		}
 		logevent("GSSAPI authentication loop finished OK");
@@ -8059,17 +8109,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		s->gss_buf.value = (char *)s->pktout->data + micoffset;
 		s->gss_buf.length = s->pktout->length - micoffset;
 
-		ssh_gss_get_mic(s->gss_ctx, &s->gss_buf, &mic);
+		s->gsslib->get_mic(s->gsslib, s->gss_ctx, &s->gss_buf, &mic);
 		s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_GSSAPI_MIC);
 		ssh_pkt_addstring_start(s->pktout);
 		ssh_pkt_addstring_data(s->pktout, mic.value, mic.length);
 		ssh2_pkt_send(ssh, s->pktout);
-		ssh_gss_free_mic(&mic);
+		s->gsslib->free_mic(s->gsslib, &mic);
 
 		s->gotit = FALSE;
 
-		ssh_gss_release_name(&s->gss_srv_name);
-		ssh_gss_release_cred(&s->gss_ctx);
+		s->gsslib->release_name(s->gsslib, &s->gss_srv_name);
+		s->gsslib->release_cred(s->gsslib, &s->gss_ctx);
 		continue;
 #endif
 	    } else if (s->can_keyb_inter && !s->kbd_inter_refused) {
@@ -8238,12 +8288,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 		s->cur_prompt = new_prompts(ssh->frontend);
 		s->cur_prompt->to_server = TRUE;
 		s->cur_prompt->name = dupstr("SSH password");
-#ifdef MPEXT
-		// To suppress CodeGuard warning
-		add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ",
-#else
 		add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",
-#endif
 						    s->username,
 						    ssh->savedhost),
 			   FALSE, SSH_MAX_PASSWORD_LEN);
@@ -9449,8 +9494,10 @@ static const struct telnet_special *ssh_get_specials(void *handle)
     static const struct telnet_special ssh1_ignore_special[] = {
 	{"IGNORE message", TS_NOP}
     };
-    static const struct telnet_special ssh2_transport_specials[] = {
+    static const struct telnet_special ssh2_ignore_special[] = {
 	{"IGNORE message", TS_NOP},
+    };
+    static const struct telnet_special ssh2_rekey_special[] = {
 	{"Repeat key exchange", TS_REKEY},
     };
     static const struct telnet_special ssh2_session_specials[] = {
@@ -9475,7 +9522,8 @@ static const struct telnet_special *ssh_get_specials(void *handle)
 	{NULL, TS_EXITMENU}
     };
     /* XXX review this length for any changes: */
-    static struct telnet_special ssh_specials[lenof(ssh2_transport_specials) +
+    static struct telnet_special ssh_specials[lenof(ssh2_ignore_special) +
+					      lenof(ssh2_rekey_special) +
 					      lenof(ssh2_session_specials) +
 					      lenof(specials_end)];
     Ssh ssh = (Ssh) handle;
@@ -9494,7 +9542,10 @@ static const struct telnet_special *ssh_get_specials(void *handle)
 	if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
 	    ADD_SPECIALS(ssh1_ignore_special);
     } else if (ssh->version == 2) {
-	ADD_SPECIALS(ssh2_transport_specials);
+	if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE))
+	    ADD_SPECIALS(ssh2_ignore_special);
+	if (!(ssh->remote_bugs & BUG_SSH2_REKEY))
+	    ADD_SPECIALS(ssh2_rekey_special);
 	if (ssh->mainchan)
 	    ADD_SPECIALS(ssh2_session_specials);
     } /* else we're not ready yet */
@@ -9544,9 +9595,11 @@ static void ssh_special(void *handle, Telnet_Special code)
 	    if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
 		send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
 	} else {
-	    pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
-	    ssh2_pkt_addstring_start(pktout);
-	    ssh2_pkt_send_noqueue(ssh, pktout);
+	    if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) {
+		pktout = ssh2_pkt_init(SSH2_MSG_IGNORE);
+		ssh2_pkt_addstring_start(pktout);
+		ssh2_pkt_send_noqueue(ssh, pktout);
+	    }
 	}
     } else if (code == TS_REKEY) {
 	if (!ssh->kex_in_progress && ssh->version == 2) {

+ 13 - 1
putty/SSH.H

@@ -71,8 +71,12 @@ unsigned char *rsa_public_blob(struct RSAKey *key, int *len);
 int rsa_public_blob_len(void *data, int maxlen);
 void freersakey(struct RSAKey *key);
 
-typedef unsigned int word32;
+#ifndef PUTTY_UINT32_DEFINED
+/* This makes assumptions about the int type. */
 typedef unsigned int uint32;
+#define PUTTY_UINT32_DEFINED
+#endif
+typedef uint32 word32;
 
 unsigned long crc32_compute(const void *s, size_t len);
 unsigned long crc32_update(unsigned long crc_input, const void *s, size_t len);
@@ -300,6 +304,14 @@ extern const struct ssh_mac ssh_hmac_sha1_buggy;
 extern const struct ssh_mac ssh_hmac_sha1_96;
 extern const struct ssh_mac ssh_hmac_sha1_96_buggy;
 
+void *aes_make_context(void);
+void aes_free_context(void *handle);
+void aes128_key(void *handle, unsigned char *key);
+void aes192_key(void *handle, unsigned char *key);
+void aes256_key(void *handle, unsigned char *key);
+void aes_iv(void *handle, unsigned char *iv);
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len);
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len);
 
 /*
  * PuTTY version number formatted as an SSH version string. 

+ 8 - 8
putty/SSHAES.C

@@ -1097,35 +1097,35 @@ static void aes_sdctr(unsigned char *blk, int len, AESContext *ctx)
     memcpy(ctx->iv, iv, sizeof(iv));
 }
 
-static void *aes_make_context(void)
+void *aes_make_context(void)
 {
     return snew(AESContext);
 }
 
-static void aes_free_context(void *handle)
+void aes_free_context(void *handle)
 {
     sfree(handle);
 }
 
-static void aes128_key(void *handle, unsigned char *key)
+void aes128_key(void *handle, unsigned char *key)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, 16, key, 16);
 }
 
-static void aes192_key(void *handle, unsigned char *key)
+void aes192_key(void *handle, unsigned char *key)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, 16, key, 24);
 }
 
-static void aes256_key(void *handle, unsigned char *key)
+void aes256_key(void *handle, unsigned char *key)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_setup(ctx, 16, key, 32);
 }
 
-static void aes_iv(void *handle, unsigned char *iv)
+void aes_iv(void *handle, unsigned char *iv)
 {
     AESContext *ctx = (AESContext *)handle;
     int i;
@@ -1133,13 +1133,13 @@ static void aes_iv(void *handle, unsigned char *iv)
 	ctx->iv[i] = GET_32BIT_MSB_FIRST(iv + 4 * i);
 }
 
-static void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
+void aes_ssh2_encrypt_blk(void *handle, unsigned char *blk, int len)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_encrypt_cbc(blk, len, ctx);
 }
 
-static void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
+void aes_ssh2_decrypt_blk(void *handle, unsigned char *blk, int len)
 {
     AESContext *ctx = (AESContext *)handle;
     aes_decrypt_cbc(blk, len, ctx);

+ 92 - 22
putty/SSHGSS.H

@@ -1,4 +1,9 @@
-#include "puttyps.h"
+#ifndef PUTTY_SSHGSS_H
+#define PUTTY_SSHGSS_H
+#include "putty.h"
+#include "pgssapi.h"
+
+#ifndef NO_GSSAPI
 
 #define SSH2_GSS_OIDTYPE 0x06
 typedef void *Ssh_gss_ctx;
@@ -18,46 +23,54 @@ typedef enum Ssh_gss_stat {
     (*buf).value = NULL;				\
 } while (0)
 
-/* Functions, provided by either wingss.c or uxgss.c */
+typedef gss_buffer_desc Ssh_gss_buf;
+typedef gss_name_t Ssh_gss_name;
+
+/* Functions, provided by either wingss.c or sshgssc.c */
+
+struct ssh_gss_library;
 
 /*
- * Do startup-time initialisation for using GSSAPI. (On Windows,
- * for instance, this dynamically loads the GSSAPI DLL and
- * retrieves some function pointers.)
- *
- * Return value is 1 on success, or 0 if initialisation failed.
+ * Do startup-time initialisation for using GSSAPI. This should
+ * correctly initialise the array of struct ssh_gss_library declared
+ * below.
  *
- * May be called multiple times (since the most convenient place
- * to call it _from_ is the ssh.c setup code), and will harmlessly
+ * Must be callable multiple times (since the most convenient place
+ * to call it _from_ is the ssh.c setup code), and should harmlessly
  * return success if already initialised.
  */
-int ssh_gss_init(void);
+void ssh_gss_init(void);
 
 /*
  * Fills in buf with a string describing the GSSAPI mechanism in
  * use. buf->data is not dynamically allocated.
  */
-Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *buf);
+typedef Ssh_gss_stat (*t_ssh_gss_indicate_mech)(struct ssh_gss_library *lib,
+						Ssh_gss_buf *buf);
 
 /*
  * Converts a name such as a hostname into a GSSAPI internal form,
  * which is placed in "out". The result should be freed by
  * ssh_gss_release_name().
  */
-Ssh_gss_stat ssh_gss_import_name(char *in, Ssh_gss_name *out);
+typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib,
+					      char *in, Ssh_gss_name *out);
 
 /*
  * Frees the contents of an Ssh_gss_name structure filled in by
  * ssh_gss_import_name().
  */
-Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *name);
+typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib,
+					       Ssh_gss_name *name);
 
 /*
  * The main GSSAPI security context setup function. The "out"
  * parameter will need to be freed by ssh_gss_free_tok.
  */
-Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
-				      Ssh_gss_buf *in, Ssh_gss_buf *out);
+typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context)
+    (struct ssh_gss_library *lib,
+     Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate,
+     Ssh_gss_buf *in, Ssh_gss_buf *out);
 
 /*
  * Frees the contents of an Ssh_gss_buf filled in by
@@ -66,26 +79,30 @@ Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx, Ssh_gss_name name, int d
  * different free function) or something filled in by any other
  * way.
  */
-Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *);
+typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib,
+					   Ssh_gss_buf *);
 
 /*
  * Acquires the credentials to perform authentication in the first
  * place. Needs to be freed by ssh_gss_release_cred().
  */
-Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *);
+typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib,
+					       Ssh_gss_ctx *);
 
 /*
  * Frees the contents of an Ssh_gss_ctx filled in by
  * ssh_gss_acquire_cred().
  */
-Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *);
+typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib,
+					       Ssh_gss_ctx *);
 
 /*
  * Gets a MIC for some input data. "out" needs to be freed by
  * ssh_gss_free_mic().
  */
-Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *in,
-			     Ssh_gss_buf *out);
+typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib,
+					  Ssh_gss_ctx ctx, Ssh_gss_buf *in,
+                 Ssh_gss_buf *out);
 
 /*
  * Frees the contents of an Ssh_gss_buf filled in by
@@ -94,7 +111,8 @@ Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *in,
  * different free function) or something filled in by any other
  * way.
  */
-Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *);
+typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib,
+					   Ssh_gss_buf *);
 
 /*
  * Return an error message after authentication failed. The
@@ -103,4 +121,56 @@ Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *);
  * containing one more character which is a trailing NUL.
  * buf->data should be manually freed by the caller. 
  */
-Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx, Ssh_gss_buf *buf);
+typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib,
+						 Ssh_gss_ctx, Ssh_gss_buf *buf);
+
+struct ssh_gss_library {
+    /*
+     * Identifying number in the enumeration used by the
+     * configuration code to specify a preference order.
+     */
+    int id;
+
+    /*
+     * Filled in at initialisation time, if there's anything
+     * interesting to say about how GSSAPI was initialised (e.g.
+     * which of a number of alternative libraries was used).
+     */
+    const char *gsslogmsg;
+
+    /*
+     * Function pointers implementing the SSH wrapper layer on top
+     * of GSSAPI. (Defined in sshgssc, typically, though Windows
+     * provides an alternative layer to sit on top of the annoyingly
+     * different SSPI.)
+     */
+    t_ssh_gss_indicate_mech indicate_mech;
+    t_ssh_gss_import_name import_name;
+    t_ssh_gss_release_name release_name;
+    t_ssh_gss_init_sec_context init_sec_context;
+    t_ssh_gss_free_tok free_tok;
+    t_ssh_gss_acquire_cred acquire_cred;
+    t_ssh_gss_release_cred release_cred;
+    t_ssh_gss_get_mic get_mic;
+    t_ssh_gss_free_mic free_mic;
+    t_ssh_gss_display_status display_status;
+
+    /*
+     * Additional data for the wrapper layers.
+     */
+    union {
+	struct gssapi_functions gssapi;
+	/*
+	 * The SSPI wrappers don't need to store their Windows API
+	 * function pointers in this structure, because there can't
+	 * be more than one set of them available.
+	 */
+    } u;
+};
+
+extern struct ssh_gss_library ssh_gss_libraries[];
+extern int n_ssh_gss_libraries;
+
+#endif /* NO_GSSAPI */
+
+#endif /*PUTTY_SSHGSS_H*/

+ 209 - 0
putty/SSHGSSC.C

@@ -0,0 +1,209 @@
+#include "putty.h"
+
+#include <string.h>
+#include "sshgssc.h"
+#include "misc.h"
+
+#ifndef NO_GSSAPI
+
+static Ssh_gss_stat ssh_gssapi_indicate_mech(struct ssh_gss_library *lib,
+					     Ssh_gss_buf *mech)
+{
+    /* Copy constant into mech */
+    mech->length  = GSS_MECH_KRB5->length;
+    mech->value = GSS_MECH_KRB5->elements;
+    return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,
+					   char *host,
+					   Ssh_gss_name *srv_name)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    OM_uint32 min_stat,maj_stat;
+    gss_buffer_desc host_buf;
+    char *pStr;
+
+    pStr = dupcat("host@", host, NULL);
+
+    host_buf.value = pStr;
+    host_buf.length = strlen(pStr);
+
+    maj_stat = gss->import_name(&min_stat, &host_buf,
+				GSS_C_NT_HOSTBASED_SERVICE, srv_name);
+    /* Release buffer */
+    sfree(pStr);
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_acquire_cred(struct ssh_gss_library *lib,
+					    Ssh_gss_ctx *ctx)
+{
+    gssapi_ssh_gss_ctx *gssctx = snew(gssapi_ssh_gss_ctx);
+
+    gssctx->maj_stat =  gssctx->min_stat = GSS_S_COMPLETE;
+    gssctx->ctx = GSS_C_NO_CONTEXT;
+    *ctx = (Ssh_gss_ctx) gssctx;
+
+    return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib,
+						Ssh_gss_ctx *ctx,
+						Ssh_gss_name srv_name,
+						int to_deleg,
+						Ssh_gss_buf *recv_tok,
+						Ssh_gss_buf *send_tok)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx*) *ctx;
+    OM_uint32 ret_flags;
+
+    if (to_deleg) to_deleg = GSS_C_DELEG_FLAG;
+    gssctx->maj_stat = gss->init_sec_context(&gssctx->min_stat,
+					     GSS_C_NO_CREDENTIAL,
+					     &gssctx->ctx,
+					     srv_name,
+					     (gss_OID) GSS_MECH_KRB5,
+					     GSS_C_MUTUAL_FLAG |
+					     GSS_C_INTEG_FLAG | to_deleg,
+					     0,
+					     GSS_C_NO_CHANNEL_BINDINGS,
+					     recv_tok,
+					     NULL,   /* ignore mech type */
+					     send_tok,
+					     &ret_flags,
+					     NULL);  /* ignore time_rec */
+
+    if (gssctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE;
+    if (gssctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_display_status(struct ssh_gss_library *lib,
+					      Ssh_gss_ctx ctx,
+					      Ssh_gss_buf *buf)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
+    OM_uint32 lmin,lmax;
+    OM_uint32 ccc;
+    gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER;
+
+    /* Return empty buffer in case of failure */
+    SSH_GSS_CLEAR_BUF(buf);
+
+    /* get first mesg from GSS */
+    ccc=0;
+    lmax=gss->display_status(&lmin,gssctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_maj);
+
+    if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE;
+
+    /* get first mesg from Kerberos */
+    ccc=0;
+    lmax=gss->display_status(&lmin,gssctx->min_stat,GSS_C_MECH_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_min);
+
+    if (lmax != GSS_S_COMPLETE) {
+        gss->release_buffer(&lmin, &msg_maj);
+        return SSH_GSS_FAILURE;
+    }
+
+    /* copy data into buffer */
+    buf->length = msg_maj.length + msg_min.length + 1;
+    buf->value = snewn(buf->length + 1, char);
+
+    /* copy mem */
+    memcpy((char *)buf->value, msg_maj.value, msg_maj.length);
+    ((char *)buf->value)[msg_maj.length] = ' ';
+    memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length);
+    ((char *)buf->value)[buf->length] = 0;
+    /* free mem & exit */
+    gss->release_buffer(&lmin, &msg_maj);
+    gss->release_buffer(&lmin, &msg_min);
+    return SSH_GSS_OK;
+}
+
+static Ssh_gss_stat ssh_gssapi_free_tok(struct ssh_gss_library *lib,
+					Ssh_gss_buf *send_tok)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    OM_uint32 min_stat,maj_stat;
+    maj_stat = gss->release_buffer(&min_stat, send_tok);
+
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_release_cred(struct ssh_gss_library *lib,
+					    Ssh_gss_ctx *ctx)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) *ctx;
+    OM_uint32 min_stat;
+    OM_uint32 maj_stat=GSS_S_COMPLETE;
+
+    if (gssctx == NULL) return SSH_GSS_FAILURE;
+    if (gssctx->ctx != GSS_C_NO_CONTEXT)
+        maj_stat = gss->delete_sec_context(&min_stat,&gssctx->ctx,GSS_C_NO_BUFFER);
+    sfree(gssctx);
+
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+
+static Ssh_gss_stat ssh_gssapi_release_name(struct ssh_gss_library *lib,
+					    Ssh_gss_name *srv_name)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    OM_uint32 min_stat,maj_stat;
+    maj_stat = gss->release_name(&min_stat, srv_name);
+
+    if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK;
+    return SSH_GSS_FAILURE;
+}
+
+static Ssh_gss_stat ssh_gssapi_get_mic(struct ssh_gss_library *lib,
+				       Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+				       Ssh_gss_buf *hash)
+{
+    struct gssapi_functions *gss = &lib->u.gssapi;
+    gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx;
+    if (gssctx == NULL) return SSH_GSS_FAILURE;
+    return gss->get_mic(&(gssctx->min_stat), gssctx->ctx, 0, buf, hash);
+}
+
+static Ssh_gss_stat ssh_gssapi_free_mic(struct ssh_gss_library *lib,
+					Ssh_gss_buf *hash)
+{
+    /* On Unix this is the same freeing process as ssh_gssapi_free_tok. */
+    return ssh_gssapi_free_tok(lib, hash);
+}
+
+void ssh_gssapi_bind_fns(struct ssh_gss_library *lib)
+{
+    lib->indicate_mech = ssh_gssapi_indicate_mech;
+    lib->import_name = ssh_gssapi_import_name;
+    lib->release_name = ssh_gssapi_release_name;
+    lib->init_sec_context = ssh_gssapi_init_sec_context;
+    lib->free_tok = ssh_gssapi_free_tok;
+    lib->acquire_cred = ssh_gssapi_acquire_cred;
+    lib->release_cred = ssh_gssapi_release_cred;
+    lib->get_mic = ssh_gssapi_get_mic;
+    lib->free_mic = ssh_gssapi_free_mic;
+    lib->display_status = ssh_gssapi_display_status;
+}
+
+#else
+
+/* Dummy function so this source file defines something if NO_GSSAPI
+   is defined. */
+
+int ssh_gssapi_init(void)
+{
+    return 0;
+}
+
+#endif

+ 23 - 0
putty/SSHGSSC.H

@@ -0,0 +1,23 @@
+#ifndef PUTTY_SSHGSSC_H
+#define PUTTY_SSHGSSC_H
+#include "putty.h"
+#ifndef NO_GSSAPI
+
+#include "pgssapi.h"
+#include "sshgss.h"
+
+typedef struct gssapi_ssh_gss_ctx {
+    OM_uint32 maj_stat;
+    OM_uint32 min_stat;
+    gss_ctx_id_t ctx;
+} gssapi_ssh_gss_ctx;
+
+void ssh_gssapi_bind_fns(struct ssh_gss_library *lib);
+
+#else
+
+int ssh_gssapi_init(void);
+
+#endif /*NO_GSSAPI*/
+
+#endif /*PUTTY_SSHGSSC_H*/

+ 19 - 1
putty/putty.h

@@ -348,6 +348,19 @@ enum {
     SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR
 };
 
+/*
+ * Tables of string <-> enum value mappings used in settings.c.
+ * Defined here so that backends can export their GSS library tables
+ * to the cross-platform settings code.
+ */
+struct keyval { char *s; int v; };
+
+#ifndef NO_GSSAPI
+extern const int ngsslibs;
+extern const char *const gsslibnames[];/* for displaying in configuration */
+extern const struct keyval gsslibkeywords[];   /* for storing by settings.c */
+#endif
+
 extern const char *const ttymodes[];
 
 enum {
@@ -461,6 +474,7 @@ struct config_tag {
     int try_ki_auth;
     int try_gssapi_auth;               /* attempt gssapi auth */
     int gssapifwd;                     /* forward tgt via gss */
+    int ssh_gsslist[4];		       /* preference order for local GSS libs */
     int ssh_subsys;		       /* run a subsystem rather than a command */
     int ssh_subsys2;		       /* fallback to go with remote_cmd_ptr2 */
     int ssh_no_shell;		       /* avoid running a shell */
@@ -592,7 +606,8 @@ struct config_tag {
     /* SSH bug compatibility modes */
     int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1,
 	sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2,
-	sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2;
+	sshbug_pksessid2, sshbug_rekey2, sshbug_maxpkt2,
+	sshbug_ignore2;
     /*
      * ssh_simple means that we promise never to open any channel other
      * than the main one, which means it can safely use a very large
@@ -825,6 +840,7 @@ void term_free(Terminal *);
 void term_size(Terminal *, int, int, int);
 void term_paint(Terminal *, Context, int, int, int, int, int);
 void term_scroll(Terminal *, int, int);
+void term_scroll_to_selection(Terminal *, int);
 void term_pwron(Terminal *, int);
 void term_clrsb(Terminal *);
 void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action,
@@ -854,6 +870,8 @@ char *term_get_ttymode(Terminal *term, const char *mode);
 int term_get_userpass_input(Terminal *term, prompts_t *p,
 			    unsigned char *in, int inlen);
 
+int format_arrow_key(char *buf, Terminal *term, int xkey, int ctrl);
+
 /*
  * Exports from logging.c.
  */

+ 114 - 36
putty/windows/WINGSS.C

@@ -2,12 +2,30 @@
 
 #include "putty.h"
 
-#define SECURITY_WIN32
 #include <security.h>
 
+#include "pgssapi.h"
 #include "sshgss.h"
+#include "sshgssc.h"
+
 #include "misc.h"
 
+/* Windows code to set up the GSSAPI library list. */
+
+struct ssh_gss_library ssh_gss_libraries[2];
+int n_ssh_gss_libraries = 0;
+static int initialised = FALSE;
+
+const int ngsslibs = 2;
+const char *const gsslibnames[2] = {
+    "GSSAPI32.DLL (MIT Kerberos)",
+    "SSPI.DLL (Microsoft SSPI)",
+};
+const struct keyval gsslibkeywords[] = {
+    { "gssapi32", 0 },
+    { "sspi", 1 },
+};
+
 DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
 		      AcquireCredentialsHandleA,
 		      (SEC_CHAR *, SEC_CHAR *, ULONG, PLUID,
@@ -33,8 +51,6 @@ DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS,
 		      MakeSignature,
 		      (PCtxtHandle, ULONG, PSecBufferDesc, ULONG));
 
-static HMODULE security_module = NULL;
-
 typedef struct winSsh_gss_ctx {
     unsigned long maj_stat;
     unsigned long min_stat;
@@ -47,33 +63,75 @@ typedef struct winSsh_gss_ctx {
 
 const Ssh_gss_buf gss_mech_krb5={9,"\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"};
 
-int ssh_gss_init(void)
+const char *gsslogmsg = NULL;
+
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib);
+
+void ssh_gss_init(void)
 {
-    if (security_module)
-	return 1;		       /* already initialised */
-
-    security_module = LoadLibrary("secur32.dll");
-    if (security_module) {
-	GET_WINDOWS_FUNCTION(security_module, AcquireCredentialsHandleA);
-	GET_WINDOWS_FUNCTION(security_module, InitializeSecurityContextA);
-	GET_WINDOWS_FUNCTION(security_module, FreeContextBuffer);
-	GET_WINDOWS_FUNCTION(security_module, FreeCredentialsHandle);
-	GET_WINDOWS_FUNCTION(security_module, DeleteSecurityContext);
-	GET_WINDOWS_FUNCTION(security_module, QueryContextAttributesA);
-	GET_WINDOWS_FUNCTION(security_module, MakeSignature);
-	return 1;
+    HMODULE module;
+
+    if (initialised) return;
+    initialised = TRUE;
+
+    /* MIT Kerberos GSSAPI implementation */
+    /* TODO: For 64-bit builds, check for gssapi64.dll */
+    module = LoadLibrary("gssapi32.dll");
+    if (module) {
+	struct ssh_gss_library *lib =
+	    &ssh_gss_libraries[n_ssh_gss_libraries++];
+
+	lib->id = 0;
+	lib->gsslogmsg = "Using GSSAPI from GSSAPI32.DLL";
+
+#define BIND_GSS_FN(name) \
+    lib->u.gssapi.name = (t_gss_##name) GetProcAddress(module, "gss_" #name)
+
+        BIND_GSS_FN(delete_sec_context);
+        BIND_GSS_FN(display_status);
+        BIND_GSS_FN(get_mic);
+        BIND_GSS_FN(import_name);
+        BIND_GSS_FN(init_sec_context);
+        BIND_GSS_FN(release_buffer);
+        BIND_GSS_FN(release_cred);
+        BIND_GSS_FN(release_name);
+
+#undef BIND_GSS_FN
+
+        ssh_gssapi_bind_fns(lib);
+    }
+
+    /* Microsoft SSPI Implementation */
+    module = LoadLibrary("secur32.dll");
+    if (module) {
+	struct ssh_gss_library *lib =
+	    &ssh_gss_libraries[n_ssh_gss_libraries++];
+
+	lib->id = 1;
+	lib->gsslogmsg = "Using SSPI from SECUR32.DLL";
+
+	GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA);
+	GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA);
+	GET_WINDOWS_FUNCTION(module, FreeContextBuffer);
+	GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle);
+	GET_WINDOWS_FUNCTION(module, DeleteSecurityContext);
+	GET_WINDOWS_FUNCTION(module, QueryContextAttributesA);
+	GET_WINDOWS_FUNCTION(module, MakeSignature);
+
+	ssh_sspi_bind_fns(lib);
     }
-    return 0;
 }
 
-Ssh_gss_stat ssh_gss_indicate_mech(Ssh_gss_buf *mech)
+static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib,
+					   Ssh_gss_buf *mech)
 {
     *mech = gss_mech_krb5;
     return SSH_GSS_OK;
 }
 
 
-Ssh_gss_stat ssh_gss_import_name(char *host, Ssh_gss_name *srv_name)
+static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
+					 char *host, Ssh_gss_name *srv_name)
 {
     char *pStr;
 
@@ -88,7 +146,8 @@ Ssh_gss_stat ssh_gss_import_name(char *host, Ssh_gss_name *srv_name)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_acquire_cred(Ssh_gss_ctx *ctx)
+static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib,
+					  Ssh_gss_ctx *ctx)
 {
     winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx);
     memset(winctx, 0, sizeof(winSsh_gss_ctx));
@@ -148,12 +207,12 @@ static SecBufferDesc ssh_gss_init_sec_buffer_desc(unsigned long version,
 #define MPEXT_INIT_SEC_BUFFERDESC(VERSION, BUFFERSCOUNT, BUFFERS) \
   {VERSION, BUFFERSCOUNT, BUFFERS}
 #endif
-
-Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
-				      Ssh_gss_name srv_name,
-				      int to_deleg,
-				      Ssh_gss_buf *recv_tok,
-				      Ssh_gss_buf *send_tok)
+static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib,
+					      Ssh_gss_ctx *ctx,
+					      Ssh_gss_name srv_name,
+					      int to_deleg,
+					      Ssh_gss_buf *recv_tok,
+					      Ssh_gss_buf *send_tok)
 {
     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx;
     SecBuffer wsend_tok = MPEXT_INIT_SEC_BUFFER(send_tok->length,SECBUFFER_TOKEN,send_tok->value);
@@ -191,7 +250,8 @@ Ssh_gss_stat ssh_gss_init_sec_context(Ssh_gss_ctx *ctx,
     return SSH_GSS_FAILURE;
 }
 
-Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
+static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib,
+				      Ssh_gss_buf *send_tok)
 {
     /* check input */
     if (send_tok == NULL) return SSH_GSS_FAILURE;
@@ -203,7 +263,8 @@ Ssh_gss_stat ssh_gss_free_tok(Ssh_gss_buf *send_tok)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
+static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib,
+					  Ssh_gss_ctx *ctx)
 {
     winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx;
 
@@ -225,7 +286,8 @@ Ssh_gss_stat ssh_gss_release_cred(Ssh_gss_ctx *ctx)
 }
 
 
-Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
+static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib,
+					  Ssh_gss_name *srv_name)
 {
     char *pStr= (char *) *srv_name;
 
@@ -236,7 +298,8 @@ Ssh_gss_stat ssh_gss_release_name(Ssh_gss_name *srv_name)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
+static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib,
+					    Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
 {
     winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx;
     char *msg;
@@ -286,8 +349,9 @@ Ssh_gss_stat ssh_gss_display_status(Ssh_gss_ctx ctx, Ssh_gss_buf *buf)
     return SSH_GSS_OK;
 }
 
-Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
-			     Ssh_gss_buf *hash)
+static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib,
+				     Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
+				     Ssh_gss_buf *hash)
 {
     winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx;
     SecPkgContext_Sizes ContextSizes;
@@ -331,20 +395,34 @@ Ssh_gss_stat ssh_gss_get_mic(Ssh_gss_ctx ctx, Ssh_gss_buf *buf,
     return winctx->maj_stat;
 }
 
-Ssh_gss_stat ssh_gss_free_mic(Ssh_gss_buf *hash)
+static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib,
+				      Ssh_gss_buf *hash)
 {
     sfree(hash->value);
     return SSH_GSS_OK;
 }
 
+static void ssh_sspi_bind_fns(struct ssh_gss_library *lib)
+{
+    lib->indicate_mech = ssh_sspi_indicate_mech;
+    lib->import_name = ssh_sspi_import_name;
+    lib->release_name = ssh_sspi_release_name;
+    lib->init_sec_context = ssh_sspi_init_sec_context;
+    lib->free_tok = ssh_sspi_free_tok;
+    lib->acquire_cred = ssh_sspi_acquire_cred;
+    lib->release_cred = ssh_sspi_release_cred;
+    lib->get_mic = ssh_sspi_get_mic;
+    lib->free_mic = ssh_sspi_free_mic;
+    lib->display_status = ssh_sspi_display_status;
+}
+
 #else
 
 /* Dummy function so this source file defines something if NO_GSSAPI
    is defined. */
 
-int ssh_gss_init(void)
+void ssh_gss_init(void)
 {
-    return 0;
 }
 
 #endif

+ 50 - 9
putty/windows/WINMISC.C

@@ -5,6 +5,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "putty.h"
+#include <security.h>
 
 OSVERSIONINFO osVersion;
 
@@ -40,21 +41,61 @@ char *get_username(void)
 {
     DWORD namelen;
     char *user;
+    int got_username = FALSE;
+    DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
+			  (EXTENDED_NAME_FORMAT, LPSTR, PULONG));
+
+    {
+	static int tried_usernameex = FALSE;
+	if (!tried_usernameex) {
+	    /* Not available on Win9x, so load dynamically */
+	    HMODULE secur32 = LoadLibrary("SECUR32.DLL");
+	    GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
+	    tried_usernameex = TRUE;
+	}
+    }
 
-    namelen = 0;
-    if (GetUserName(NULL, &namelen) == FALSE) {
+    if (p_GetUserNameExA) {
 	/*
-	 * Apparently this doesn't work at least on Windows XP SP2.
-	 * Thus assume a maximum of 256. It will fail again if it
-	 * doesn't fit.
+	 * If available, use the principal -- this avoids the problem
+	 * that the local username is case-insensitive but Kerberos
+	 * usernames are case-sensitive.
 	 */
-	namelen = 256;
+
+	/* Get the length */
+	namelen = 0;
+	(void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
+
+	user = snewn(namelen, char);
+	got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
+	if (got_username) {
+	    char *p = strchr(user, '@');
+	    if (p) *p = 0;
+	} else {
+	    sfree(user);
+	}
     }
 
-    user = snewn(namelen, char);
-    GetUserName(user, &namelen);
+    if (!got_username) {
+	/* Fall back to local user name */
+	namelen = 0;
+	if (GetUserName(NULL, &namelen) == FALSE) {
+	    /*
+	     * Apparently this doesn't work at least on Windows XP SP2.
+	     * Thus assume a maximum of 256. It will fail again if it
+	     * doesn't fit.
+	     */
+	    namelen = 256;
+	}
+
+	user = snewn(namelen, char);
+	got_username = GetUserName(user, &namelen);
+	if (!got_username) {
+	    sfree(user);
+	}
+    }
 
-    return user;
+    return got_username ? user : NULL;
 }
 
 BOOL init_winver(void)

+ 25 - 5
putty/windows/WINSTUFF.H

@@ -76,17 +76,31 @@ struct FontSpec {
 #define DF_END 0x0001
 
 /*
- * Dynamically linked functions.
- * This is somewhat circuitous to allow function-renaming macros to be
- * expanded, principally the ANSI/Unicode DoSomethingA/DoSomethingW.
+ * Dynamically linked functions. These come in two flavours:
+ *
+ *  - GET_WINDOWS_FUNCTION does not expose "name" to the preprocessor,
+ *    so will always dynamically link against exactly what is specified
+ *    in "name". If you're not sure, use this one.
+ *
+ *  - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via
+ *    preprocessor definitions like "#define foo bar"; this is principally
+ *    intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW.
+ *    If your function has an argument of type "LPTSTR" or similar, this
+ *    is the variant to use.
+ *    (However, it can't always be used, as it trips over more complicated
+ *    macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.)
+ *
+ * (DECL_WINDOWS_FUNCTION works with both these variants.)
  */
 #define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \
     typedef rettype (WINAPI *t_##name) params; \
     linkage t_##name p_##name
 #define STR1(x) #x
 #define STR(x) STR1(x)
-#define GET_WINDOWS_FUNCTION(module, name) \
+#define GET_WINDOWS_FUNCTION_PP(module, name) \
     p_##name = module ? (t_##name) GetProcAddress(module, STR(name)) : NULL
+#define GET_WINDOWS_FUNCTION(module, name) \
+    p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
 
 /*
  * Global variables. Most modules declare these `extern', but
@@ -126,17 +140,23 @@ typedef struct terminal_tag Terminal;
 
 typedef HDC Context;
 
+typedef unsigned int uint32; /* int is 32-bits on Win32 and Win64. */
+#define PUTTY_UINT32_DEFINED
+
 #ifndef NO_GSSAPI
 /*
  * GSS-API stuff
  */
+#define GSS_CC CALLBACK
+/*
 typedef struct Ssh_gss_buf {
-    int length;
+    size_t length;
     char *value;
 } Ssh_gss_buf;
 
 #define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL}
 typedef void *Ssh_gss_name;
+*/
 #endif
 
 /*

+ 2 - 2
release/licence.setup

@@ -98,6 +98,6 @@ Each version is given a distinguishing version number. If the Program specifies
 
 
   B. OPENCANDY END USER LICENSE AGREEMENT
-  August 5, 2009
+  January 26, 2010
 
-The installer you are about to initiate uses the OpenCandy, Inc. network to recommend other software you may find valuable during installation of this software. By proceeding with this installation, you agree that OpenCandy may collect and use  *NON personally identifiable*  information about THIS installation (like: install initiated and completed, operating system and language, and country) as well as whether the software recommendation was accepted or not. Collection of such information by OpenCandy will ONLY occur during this installation and the recommendation process; in accordance with the policies and practices set forth in OpenCandy's Privacy Policy, available at www.opencandy.com/privacy-policy.
+This installer uses the OpenCandy network to recommend other software you may find valuable during the installation of this software. OpenCandy collects *NON-personally identifiable* information about this installation and the recommendation process.  Collection of this information ONLY occurs during this installation and the recommendation process; in accordance with OpenCandy's Privacy Policy, available at www.opencandy.com/privacy-policy

+ 1 - 1
release/winscp.u3i

@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <u3manifest version="1.0">
-  <application uuid="48b341d1-d411-4b5a-a82c-f3b5d65602fc" version="4.2.7">
+  <application uuid="48b341d1-d411-4b5a-a82c-f3b5d65602fc" version="4.2.8">
     <icon>winscp.ico</icon>
     <name>WinSCP</name>
     <description>Freeware SFTP (SSH File Transfer Protocol), FTP (File Transfer Protocol) and SCP (Secure CoPy) client for Windows using SSH (Secure SHell). Its main function is safe copying of files between a local and a remote computer.</description>

+ 1 - 1
release/winscpsetup.iss

@@ -737,7 +737,7 @@ begin
 
 #ifdef OpenCandy
   OpenCandyInitRemnant('WinSCP', '{#OpenCandyKey}', '3d0f240d63cf2239f9e45c3562d8bdbc',
-    ExpandConstant('{cm:LanguageISOCode}'), '{#RegistryKey}\OpenCandy',
+    ExpandConstant('{cm:LanguageISOCode}'), '{#ParentRegistryKey}\OpenCandy',
     WizardSilent());
 #endif
 end;

+ 2 - 0
resource/TextsWin1.rc

@@ -270,6 +270,8 @@ BEGIN
         NEW_FILE, "New file"
         EDIT_FILE_CAPTION, "Edit file"
         EDIT_FILE_PROMPT, "&Enter file name:"
+        REMOTE_COPY_FILE, "Duplicate file '%s' to remote directory:"
+        REMOTE_COPY_FILES, "Duplicate %d files to remote directory:"
         REMOTE_COPY_TITLE, "Duplicate"
         PROGRESS_REMOTE_COPY, "Copying"
         CUSTOM_COMMAND_ADD, "Add Custom Command"

+ 2 - 1
windows/ConsoleRunner.cpp

@@ -929,7 +929,7 @@ void __fastcall TConsoleRunner::TimerTimer(TObject * /*Sender*/)
 //---------------------------------------------------------------------------
 unsigned int TConsoleRunner::InputTimeout()
 {
-  return (FScript->Batch != TScript::BatchOff ? BATCH_INPUT_TIMEOUT : 0);
+  return ((FScript != NULL) && (FScript->Batch != TScript::BatchOff) ? BATCH_INPUT_TIMEOUT : 0);
 }
 //---------------------------------------------------------------------------
 void __fastcall TConsoleRunner::Input(const AnsiString Prompt, AnsiString & Str, bool Echo)
@@ -1608,6 +1608,7 @@ int __fastcall TConsoleRunner::Run(const AnsiString Session, TOptions * Options,
 
           if (FCommandError)
           {
+            FScript->Log(llMessage, "Failed");
             AnyError = true;
             if (FScript->Batch == TScript::BatchAbort)
             {

+ 1 - 0
windows/CustomWinConfiguration.cpp

@@ -84,6 +84,7 @@ void __fastcall TCustomWinConfiguration::Default()
   FFindFile.ListParams = "3;1|125,1;181,1;80,1;122,1|0;1;2;3";
   FConsoleWin.WindowSize = "570,430";
   FConfirmExitOnCompletion = true;
+  FOperationProgressOnTop = true;
 
   DefaultHistory();
 }

+ 2 - 0
windows/CustomWinConfiguration.h

@@ -43,6 +43,7 @@ private:
   TInterface FDefaultInterface;
   bool FDefaultShowAdvancedLoginOptions;
   bool FConfirmExitOnCompletion;
+  bool FOperationProgressOnTop;
   TNotifyEvent FOnMasterPasswordRecrypt;
 
   void __fastcall SetInterface(TInterface value);
@@ -79,6 +80,7 @@ public:
   __property TFindFileConfiguration FindFile = { read = FFindFile, write = SetFindFile };
   __property TConsoleWinConfiguration ConsoleWin = { read = FConsoleWin, write = SetConsoleWin };
   __property bool ConfirmExitOnCompletion  = { read=FConfirmExitOnCompletion, write=SetConfirmExitOnCompletion };
+  __property bool OperationProgressOnTop  = { read=FOperationProgressOnTop, write=FOperationProgressOnTop };
   __property bool UseMasterPassword = { read = GetUseMasterPassword };
   __property TNotifyEvent OnMasterPasswordRecrypt = { read = FOnMasterPasswordRecrypt, write = FOnMasterPasswordRecrypt };
 };