Просмотр исходного кода

Configuration can be saved to read-only INI file by holding down Shift key while closing WinSCP or submitting Preferences dialog.

Source commit: bb0f5310f5c6d4c975b102154d394a3a1c292591
Martin Prikryl 7 лет назад
Родитель
Сommit
bae86205de

+ 3 - 1
source/core/Configuration.cpp

@@ -43,6 +43,7 @@ __fastcall TConfiguration::TConfiguration()
   FUpdating = 0;
   FStorage = stDetect;
   FDontSave = false;
+  FForceSave = false;
   FApplicationInfo = NULL;
   FUsage = new TUsage(this);
   FDefaultCollectUsage = false;
@@ -276,6 +277,7 @@ void __fastcall TConfiguration::DoSave(bool All, bool Explicit)
   {
     AStorage->AccessMode = smReadWrite;
     AStorage->Explicit = Explicit;
+    AStorage->ForceSave = FForceSave;
     if (AStorage->OpenSubKey(ConfigurationSubKey, true))
     {
       // if saving to TOptionsStorage, make sure we save everything so that
@@ -1785,7 +1787,7 @@ void __fastcall TConfiguration::SetShowFtpWelcomeMessage(bool value)
 //---------------------------------------------------------------------------
 bool __fastcall TConfiguration::GetPersistent()
 {
-  return (Storage != stNul);
+  return (Storage != stNul) && !FDontSave;
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------

+ 2 - 0
source/core/Configuration.h

@@ -23,6 +23,7 @@ class TConfiguration : public TObject
 {
 private:
   bool FDontSave;
+  bool FForceSave;
   bool FChanged;
   int FUpdating;
   TNotifyEvent FOnChange;
@@ -311,6 +312,7 @@ public:
   __property UnicodeString IniFileStorageNameForReading  = { read=GetIniFileStorageNameForReading };
   __property TStrings * OptionsStorage = { read = GetOptionsStorage, write = SetOptionsStorage };
   __property bool Persistent = { read = GetPersistent };
+  __property bool ForceSave = { read = FForceSave, write = FForceSave };
   __property bool Scripting = { read = FScripting, write = FScripting };
 
   __property UnicodeString DefaultKeyFile = { read = GetDefaultKeyFile };

+ 6 - 0
source/core/HierarchicalStorage.cpp

@@ -109,6 +109,7 @@ __fastcall THierarchicalStorage::THierarchicalStorage(const UnicodeString AStora
   FKeyHistory = new TStringList();
   AccessMode = smRead;
   Explicit = false;
+  ForceSave = false;
   // While this was implemented in 5.0 already, for some reason
   // it was disabled (by mistake?). So although enabled for 5.6.1 only,
   // data written in Unicode/UTF8 can be read by all versions back to 5.0.
@@ -1229,6 +1230,11 @@ void __fastcall TIniFileStorage::Flush()
           Attr = FILE_ATTRIBUTE_NORMAL;
         }
 
+        if (FLAGSET(Attr, FILE_ATTRIBUTE_READONLY) && ForceSave)
+        {
+          SetFileAttributes(ApiPath(Storage).c_str(), Attr & ~FILE_ATTRIBUTE_READONLY);
+        }
+
         HANDLE Handle = CreateFile(ApiPath(Storage).c_str(), GENERIC_READ | GENERIC_WRITE,
           0, NULL, CREATE_ALWAYS, Attr, 0);
 

+ 2 - 0
source/core/HierarchicalStorage.h

@@ -65,6 +65,7 @@ public:
   __property UnicodeString CurrentSubKey  = { read=GetCurrentSubKey };
   __property TStorageAccessMode AccessMode  = { read=FAccessMode, write=SetAccessMode };
   __property bool Explicit = { read = FExplicit, write = FExplicit };
+  __property bool ForceSave = { read = FForceSave, write = FForceSave };
   __property bool ForceAnsi = { read = FForceAnsi, write = FForceAnsi };
   __property bool MungeStringValues = { read = FMungeStringValues, write = FMungeStringValues };
   __property UnicodeString Source = { read = GetSource };
@@ -75,6 +76,7 @@ protected:
   TStrings * FKeyHistory;
   TStorageAccessMode FAccessMode;
   bool FExplicit;
+  bool FForceSave;
   bool FMungeStringValues;
   bool FForceAnsi;
 

+ 1 - 0
source/forms/Preferences.cpp

@@ -48,6 +48,7 @@ bool __fastcall DoPreferencesDialog(TPreferencesMode APreferencesMode,
     Result = PreferencesDialog->Execute(DialogData);
     if (Result)
     {
+      CheckConfigurationForceSave();
       Configuration->SaveExplicit();
     }
   }

+ 12 - 0
source/packages/my/PasTools.pas

@@ -73,6 +73,7 @@ function ControlHasRecreationPersistenceData(Control: TControl): Boolean;
 function IsAppIconic: Boolean;
 procedure SetAppIconic(Value: Boolean);
 procedure SetAppMainForm(Value: TForm);
+procedure SetAppTerminated(Value: Boolean);
 
 procedure ForceColorChange(Control: TWinControl);
 
@@ -600,6 +601,7 @@ type
     function IsAppIconic: Boolean;
     procedure SetAppIconic(Value: Boolean);
     procedure SetMainForm(Value: TForm);
+    procedure SetTerminated(Value: Boolean);
   end;
 
 function TApplicationHelper.IsAppIconic: Boolean;
@@ -617,6 +619,11 @@ begin
   Self.FMainForm := Value;
 end;
 
+procedure TApplicationHelper.SetTerminated(Value: Boolean);
+begin
+  Self.FTerminate := Value;
+end;
+
 function IsAppIconic: Boolean;
 begin
   Result := Application.IsAppIconic;
@@ -632,6 +639,11 @@ begin
   Application.SetMainForm(Value);
 end;
 
+procedure SetAppTerminated(Value: Boolean);
+begin
+  Application.SetTerminated(Value);
+end;
+
 function ApiPath(Path: string): string;
 begin
   Result := Path;

+ 1 - 0
source/resource/HelpWin.h

@@ -61,5 +61,6 @@
 #define HELP_CHANGE_PASSWORD         "task_change_password"
 #define HELP_FILTER                  "ui_filter"
 #define HELP_LOGIN_AUTHORIZED_KEYS   "guide_public_key"
+#define HELP_READONLY_INI_FILE       "config#ini_readonly"
 
 #endif // TextsWin

+ 1 - 0
source/resource/TextsWin.h

@@ -156,6 +156,7 @@
 #define EXTENSION_UNTRUSTED     1367
 #define EXTENSION_NOT_LATEST    1368
 #define CUSTOM_INI_FILE_OVERWRITE 1369
+#define READONLY_INI_FILE_OVERWRITE 1370
 
 #define WIN_INFORMATION_STRINGS 1400
 #define COMPARE_NO_DIFFERENCES  1402

+ 1 - 0
source/resource/TextsWin1.rc

@@ -158,6 +158,7 @@ BEGIN
         EXTENSION_UNTRUSTED, "The extension does not come from a trusted source. Are you sure you want to install it?"
         EXTENSION_NOT_LATEST, "**Using the last compatible and trusted version of the extension.**\n\nThe latest version of the extension is either not reviewed yet or is not compatible with this version of WinSCP."
         CUSTOM_INI_FILE_OVERWRITE, "**Do you want to overwrite an existing INI file '%s'?**\n\nChoose 'Overwrite' to overwrite the selected INI file with the current configuration.\n\nChoose 'Use' to restart WinSCP with a configuration loaded from the selected INI file. Your current configuration will be preserved and you can revert to it, if needed.|&Overwrite|&Use"
+        READONLY_INI_FILE_OVERWRITE, "**Do you want to overwrite a read-only INI file '%s' to save your current configuration?**This question is asked, when you hold down Shift key while closing WinSCP and your INI file is set read-only. Normally read-only INI files are not overwritten and any changes to configuration are lost when closing WinSCP."
 
         WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
         COMPARE_NO_DIFFERENCES, "No differences found."

+ 18 - 0
source/windows/WinInterface.cpp

@@ -1495,6 +1495,24 @@ UnicodeString DumpCallstackFileName(int ProcessId)
   return Result;
 }
 //---------------------------------------------------------------------------
+void CheckConfigurationForceSave()
+{
+  if (UseAlternativeFunction() && Configuration->Persistent &&
+      (Configuration->Storage == stIniFile) && Sysutils::FileExists(ApiPath(Configuration->IniFileStorageName)) &&
+      !Configuration->ForceSave)
+  {
+    int Attr = GetFileAttributes(ApiPath(Configuration->IniFileStorageName).c_str());
+    if (FLAGSET(Attr, FILE_ATTRIBUTE_READONLY))
+    {
+      UnicodeString Message = FMTLOAD(READONLY_INI_FILE_OVERWRITE, (Configuration->IniFileStorageName));
+      if (MessageDialog(Message, qtConfirmation, qaOK | qaCancel, HELP_READONLY_INI_FILE) == qaOK)
+      {
+        Configuration->ForceSave = true;
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
 class TCallstackThread : public TSignalThread
 {
 public:

+ 2 - 0
source/windows/WinInterface.h

@@ -505,6 +505,8 @@ TShortCut __fastcall NormalizeCustomShortCut(TShortCut ShortCut);
 UnicodeString DumpCallstackEventName(int ProcessId);
 UnicodeString DumpCallstackFileName(int ProcessId);
 
+void CheckConfigurationForceSave();
+
 #ifdef _DEBUG
 void __fastcall ForceTracing();
 #endif

+ 11 - 5
source/windows/WinMain.cpp

@@ -1044,10 +1044,10 @@ int __fastcall Execute()
       do
       {
         Retry = false;
-        TObjectList * DataList = new TObjectList();
+        std::unique_ptr<TObjectList> DataList(new TObjectList());
         try
         {
-          GetLoginData(AutoStartSession, Params, DataList, DownloadFile, NeedSession, NULL, pufAllowStoredSiteWithProtocol);
+          GetLoginData(AutoStartSession, Params, DataList.get(), DownloadFile, NeedSession, NULL, pufAllowStoredSiteWithProtocol);
           // GetLoginData now Aborts when session is needed and none is selected
           if (DebugAlwaysTrue(!NeedSession || (DataList->Count > 0)))
           {
@@ -1071,7 +1071,7 @@ int __fastcall Execute()
               bool CanStart;
               if (DataList->Count > 0)
               {
-                TTerminal * Terminal = TerminalManager->NewTerminals(DataList);
+                TTerminal * Terminal = TerminalManager->NewTerminals(DataList.get());
                 if (!DownloadFile.IsEmpty())
                 {
                   Terminal->AutoReadDirectory = false;
@@ -1139,6 +1139,8 @@ int __fastcall Execute()
                   }
 
                   Application->Run();
+                  // to allow dialog boxes show later (like from CheckConfigurationForceSave)
+                  SetAppTerminated(False);
                 }
                 __finally
                 {
@@ -1153,13 +1155,17 @@ int __fastcall Execute()
             }
           }
         }
-        __finally
+        // Catch EAbort from Synchronize() and similar functions, so that CheckConfigurationForceSave is processed
+        catch (EAbort & E)
         {
-          delete DataList;
+          Retry = false; // unlikely to be true, but just in case
         }
       }
       while (Retry);
     }
+
+    // In GUI mode only
+    CheckConfigurationForceSave();
   }
   __finally
   {