浏览代码

Site import from an INI file (Issue 2290)

Issue 2290 – Easier moving of site configuration between machines
https://winscp.net/tracker/2290

Source commit: 58e02521b5191ddd407922f989d351f9578cbc4d
Martin Prikryl 1 年之前
父节点
当前提交
8b120da856

+ 33 - 0
source/core/Configuration.cpp

@@ -1846,6 +1846,39 @@ TStoredSessionList * TConfiguration::SelectOpensshSessionsForImport(
   return ImportSessionList.release();
   return ImportSessionList.release();
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+TStoredSessionList * TConfiguration::SelectSessionsForImport(
+  TStoredSessionList * Sessions, const UnicodeString & FileName, UnicodeString & Error)
+{
+  std::unique_ptr<TStoredSessionList> ImportSessionList(CreateSessionsForImport(Sessions));
+
+  try
+  {
+    if (FileName.IsEmpty())
+    {
+      throw Exception(LoadStr(INI_SELECT));
+    }
+    else
+    {
+      std::unique_ptr<THierarchicalStorage> ImportStorage(TIniFileStorage::CreateFromPath(FileName));
+      ImportStorage->AccessMode = smRead;
+
+      if (ImportStorage->OpenSubKey(Configuration->StoredSessionsSubKey, false))
+      {
+        ImportSessionList->Load(ImportStorage.get());
+      }
+
+      UnicodeString NoSessionsError = FMTLOAD(INI_NO_SITES, (FileName));
+      SelectSessionsToImportIfAny(ImportSessionList.get(), Sessions, Error, NoSessionsError);
+    }
+  }
+  catch (Exception & E)
+  {
+    Error = E.Message;
+  }
+
+  return ImportSessionList.release();
+}
+//---------------------------------------------------------------------------
 void __fastcall TConfiguration::SetRandomSeedFile(UnicodeString value)
 void __fastcall TConfiguration::SetRandomSeedFile(UnicodeString value)
 {
 {
   if (RandomSeedFile != value)
   if (RandomSeedFile != value)

+ 1 - 0
source/core/Configuration.h

@@ -336,6 +336,7 @@ public:
   TStoredSessionList * __fastcall SelectKnownHostsSessionsForImport(
   TStoredSessionList * __fastcall SelectKnownHostsSessionsForImport(
     TStrings * Lines, TStoredSessionList * Sessions, UnicodeString & Error);
     TStrings * Lines, TStoredSessionList * Sessions, UnicodeString & Error);
   TStoredSessionList * SelectOpensshSessionsForImport(TStoredSessionList * Sessions, UnicodeString & Error);
   TStoredSessionList * SelectOpensshSessionsForImport(TStoredSessionList * Sessions, UnicodeString & Error);
+  TStoredSessionList * SelectSessionsForImport(TStoredSessionList * Sessions, const UnicodeString & FileName, UnicodeString & Error);
   UnicodeString GetPuttySessionsKey(const UnicodeString & RootKey);
   UnicodeString GetPuttySessionsKey(const UnicodeString & RootKey);
   void RefreshPuttySshHostCAList();
   void RefreshPuttySshHostCAList();
 
 

+ 16 - 8
source/core/SessionData.cpp

@@ -5216,9 +5216,10 @@ void __fastcall TStoredSessionList::SelectAll(bool Select)
     Sessions[Index]->Selected = Select;
     Sessions[Index]->Selected = Select;
 }
 }
 //---------------------------------------------------------------------
 //---------------------------------------------------------------------
-void __fastcall TStoredSessionList::Import(TStoredSessionList * From,
+bool TStoredSessionList::Import(TStoredSessionList * From,
   bool OnlySelected, TList * Imported)
   bool OnlySelected, TList * Imported)
 {
 {
+  bool Result = false;
   for (int Index = 0; Index < From->Count; Index++)
   for (int Index = 0; Index < From->Count; Index++)
   {
   {
     if (!OnlySelected || From->Sessions[Index]->Selected)
     if (!OnlySelected || From->Sessions[Index]->Selected)
@@ -5228,6 +5229,7 @@ void __fastcall TStoredSessionList::Import(TStoredSessionList * From,
       Session->Modified = true;
       Session->Modified = true;
       Session->MakeUniqueIn(this);
       Session->MakeUniqueIn(this);
       Add(Session);
       Add(Session);
+      Result = true;
       if (Imported != NULL)
       if (Imported != NULL)
       {
       {
         Imported->Add(Session);
         Imported->Add(Session);
@@ -5236,6 +5238,7 @@ void __fastcall TStoredSessionList::Import(TStoredSessionList * From,
   }
   }
   // only modified, explicit
   // only modified, explicit
   Save(false, true);
   Save(false, true);
+  return Result;
 }
 }
 //---------------------------------------------------------------------
 //---------------------------------------------------------------------
 void __fastcall TStoredSessionList::SelectSessionsToImport
 void __fastcall TStoredSessionList::SelectSessionsToImport
@@ -5469,9 +5472,7 @@ void __fastcall TStoredSessionList::SetDefaultSettings(TSessionData * value)
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 bool __fastcall TStoredSessionList::OpenHostKeysSubKey(THierarchicalStorage * Storage, bool CanCreate)
 bool __fastcall TStoredSessionList::OpenHostKeysSubKey(THierarchicalStorage * Storage, bool CanCreate)
 {
 {
-  return
-    Storage->OpenRootKey(CanCreate) &&
-    Storage->OpenSubKey(Configuration->SshHostKeysSubKey, CanCreate);
+  return Storage->OpenSubKey(Configuration->SshHostKeysSubKey, CanCreate);
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 THierarchicalStorage * __fastcall TStoredSessionList::CreateHostKeysStorageForWritting()
 THierarchicalStorage * __fastcall TStoredSessionList::CreateHostKeysStorageForWritting()
@@ -5483,7 +5484,7 @@ THierarchicalStorage * __fastcall TStoredSessionList::CreateHostKeysStorageForWr
   return Storage.release();
   return Storage.release();
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-int __fastcall TStoredSessionList::ImportHostKeys(
+int TStoredSessionList::ImportHostKeys(
   THierarchicalStorage * SourceStorage, THierarchicalStorage * TargetStorage, TStoredSessionList * Sessions, bool OnlySelected)
   THierarchicalStorage * SourceStorage, THierarchicalStorage * TargetStorage, TStoredSessionList * Sessions, bool OnlySelected)
 {
 {
   int Result = 0;
   int Result = 0;
@@ -5515,13 +5516,20 @@ int __fastcall TStoredSessionList::ImportHostKeys(
   return Result;
   return Result;
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-void __fastcall TStoredSessionList::ImportHostKeys(
-  const UnicodeString & SourceKey, TStoredSessionList * Sessions, bool OnlySelected)
+void TStoredSessionList::ImportHostKeys(
+  THierarchicalStorage * SourceStorage, TStoredSessionList * Sessions, bool OnlySelected)
 {
 {
   std::unique_ptr<THierarchicalStorage> TargetStorage(CreateHostKeysStorageForWritting());
   std::unique_ptr<THierarchicalStorage> TargetStorage(CreateHostKeysStorageForWritting());
+
+  ImportHostKeys(SourceStorage, TargetStorage.get(), Sessions, OnlySelected);
+}
+//---------------------------------------------------------------------------
+void TStoredSessionList::ImportHostKeys(
+  const UnicodeString & SourceKey, TStoredSessionList * Sessions, bool OnlySelected)
+{
   std::unique_ptr<THierarchicalStorage> SourceStorage(new TRegistryStorage(SourceKey));
   std::unique_ptr<THierarchicalStorage> SourceStorage(new TRegistryStorage(SourceKey));
 
 
-  ImportHostKeys(SourceStorage.get(), TargetStorage.get(), Sessions, OnlySelected);
+  ImportHostKeys(SourceStorage.get(), Sessions, OnlySelected);
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 void __fastcall TStoredSessionList::ImportSelectedKnownHosts(TStoredSessionList * Sessions)
 void __fastcall TStoredSessionList::ImportSelectedKnownHosts(TStoredSessionList * Sessions)

+ 4 - 4
source/core/SessionData.h

@@ -765,7 +765,7 @@ public:
     bool UseDefaults = false, bool PuttyImport = false);
     bool UseDefaults = false, bool PuttyImport = false);
   void __fastcall Save(THierarchicalStorage * Storage, bool All = false);
   void __fastcall Save(THierarchicalStorage * Storage, bool All = false);
   void __fastcall SelectAll(bool Select);
   void __fastcall SelectAll(bool Select);
-  void __fastcall Import(TStoredSessionList * From, bool OnlySelected, TList * Imported);
+  bool Import(TStoredSessionList * From, bool OnlySelected, TList * Imported);
   void __fastcall RecryptPasswords(TStrings * RecryptPasswordErrors);
   void __fastcall RecryptPasswords(TStrings * RecryptPasswordErrors);
   TSessionData * __fastcall AtSession(int Index)
   TSessionData * __fastcall AtSession(int Index)
     { return (TSessionData*)AtObject(Index); }
     { return (TSessionData*)AtObject(Index); }
@@ -792,10 +792,10 @@ public:
   __property TSessionData * Sessions[int Index]  = { read=AtSession };
   __property TSessionData * Sessions[int Index]  = { read=AtSession };
   __property TSessionData * DefaultSettings  = { read=FDefaultSettings, write=SetDefaultSettings };
   __property TSessionData * DefaultSettings  = { read=FDefaultSettings, write=SetDefaultSettings };
 
 
-  static int __fastcall ImportHostKeys(
+  static int ImportHostKeys(
     THierarchicalStorage * SourceStorage, THierarchicalStorage * TargetStorage, TStoredSessionList * Sessions, bool OnlySelected);
     THierarchicalStorage * SourceStorage, THierarchicalStorage * TargetStorage, TStoredSessionList * Sessions, bool OnlySelected);
-  static void __fastcall ImportHostKeys(
-    const UnicodeString & SourceKey, TStoredSessionList * Sessions, bool OnlySelected);
+  static void ImportHostKeys(THierarchicalStorage * SourceStorage, TStoredSessionList * Sessions, bool OnlySelected);
+  static void ImportHostKeys(const UnicodeString & SourceKey, TStoredSessionList * Sessions, bool OnlySelected);
   static void __fastcall ImportSelectedKnownHosts(TStoredSessionList * Sessions);
   static void __fastcall ImportSelectedKnownHosts(TStoredSessionList * Sessions);
   static bool __fastcall OpenHostKeysSubKey(THierarchicalStorage * Storage, bool CanCreate);
   static bool __fastcall OpenHostKeysSubKey(THierarchicalStorage * Storage, bool CanCreate);
   static void SelectKnownHostsForSelectedSessions(TStoredSessionList * KnownHosts, TStoredSessionList * Sessions);
   static void SelectKnownHostsForSelectedSessions(TStoredSessionList * KnownHosts, TStoredSessionList * Sessions);

+ 46 - 5
source/forms/ImportSessions.cpp

@@ -20,10 +20,14 @@
 #pragma resource "*.dfm"
 #pragma resource "*.dfm"
 //---------------------------------------------------------------------
 //---------------------------------------------------------------------
 const int OpensshIndex = 3;
 const int OpensshIndex = 3;
-const int KnownHostsIndex = 4;
+const int KnownHostsIndex = 5;
+const int IniIndex = 4;
 //---------------------------------------------------------------------
 //---------------------------------------------------------------------
 bool __fastcall DoImportSessionsDialog(TList * Imported)
 bool __fastcall DoImportSessionsDialog(TList * Imported)
 {
 {
+  std::unique_ptr<TImportSessionsDialog> ImportSessionsDialog(
+    SafeFormCreate<TImportSessionsDialog>(Application));
+
   std::unique_ptr<TStrings> Errors(new TStringList());
   std::unique_ptr<TStrings> Errors(new TStringList());
   std::unique_ptr<TList> SessionListsList(new TList());
   std::unique_ptr<TList> SessionListsList(new TList());
   UnicodeString Error;
   UnicodeString Error;
@@ -48,6 +52,11 @@ bool __fastcall DoImportSessionsDialog(TList * Imported)
   SessionListsList->Add(OpensshImportSessionList.get());
   SessionListsList->Add(OpensshImportSessionList.get());
   Errors->Add(Error);
   Errors->Add(Error);
 
 
+  std::unique_ptr<TStoredSessionList> IniImportSessionList(ImportSessionsDialog->SelectSessionsForImport(Error));
+  DebugAssert(IniIndex == SessionListsList->Count);
+  SessionListsList->Add(IniImportSessionList.get());
+  Errors->Add(Error);
+
   std::unique_ptr<TStoredSessionList> KnownHostsImportSessionList(
   std::unique_ptr<TStoredSessionList> KnownHostsImportSessionList(
     Configuration->SelectKnownHostsSessionsForImport(StoredSessions, Error));
     Configuration->SelectKnownHostsSessionsForImport(StoredSessions, Error));
   DebugAssert(KnownHostsIndex == SessionListsList->Count);
   DebugAssert(KnownHostsIndex == SessionListsList->Count);
@@ -56,9 +65,6 @@ bool __fastcall DoImportSessionsDialog(TList * Imported)
 
 
   DebugAssert(SessionListsList->Count == Errors->Count);
   DebugAssert(SessionListsList->Count == Errors->Count);
 
 
-  std::unique_ptr<TImportSessionsDialog> ImportSessionsDialog(
-    SafeFormCreate<TImportSessionsDialog>(Application));
-
   ImportSessionsDialog->Init(SessionListsList.get(), Errors.get());
   ImportSessionsDialog->Init(SessionListsList.get(), Errors.get());
 
 
   bool Result = ImportSessionsDialog->Execute();
   bool Result = ImportSessionsDialog->Execute();
@@ -69,6 +75,8 @@ bool __fastcall DoImportSessionsDialog(TList * Imported)
     TInstantOperationVisualizer Visualizer;
     TInstantOperationVisualizer Visualizer;
 
 
     UnicodeString PuttyHostKeysSourceKey = OriginalPuttyRegistryStorageKey;
     UnicodeString PuttyHostKeysSourceKey = OriginalPuttyRegistryStorageKey;
+    TStoredSessionList * AIniImportSessionList =
+      static_cast<TStoredSessionList *>(SessionListsList->Items[IniIndex]);
     TStoredSessionList * AKnownHostsImportSessionList =
     TStoredSessionList * AKnownHostsImportSessionList =
       static_cast<TStoredSessionList *>(SessionListsList->Items[KnownHostsIndex]);
       static_cast<TStoredSessionList *>(SessionListsList->Items[KnownHostsIndex]);
 
 
@@ -83,9 +91,15 @@ bool __fastcall DoImportSessionsDialog(TList * Imported)
     TStoredSessionList::ImportHostKeys(PuttyHostKeysSourceKey, FilezillaImportSessionList.get(), true);
     TStoredSessionList::ImportHostKeys(PuttyHostKeysSourceKey, FilezillaImportSessionList.get(), true);
 
 
     StoredSessions->Import(OpensshImportSessionList.get(), true, Imported);
     StoredSessions->Import(OpensshImportSessionList.get(), true, Imported);
+
+    if (StoredSessions->Import(AIniImportSessionList, true, Imported))
+    {
+      std::unique_ptr<THierarchicalStorage> IniStorage(TIniFileStorage::CreateFromPath(ImportSessionsDialog->IniFileName));
+      TStoredSessionList::ImportHostKeys(IniStorage.get(), AIniImportSessionList, true);
+    }
+
     // The actual import will be done by ImportSelectedKnownHosts
     // The actual import will be done by ImportSelectedKnownHosts
     TStoredSessionList::SelectKnownHostsForSelectedSessions(AKnownHostsImportSessionList, OpensshImportSessionList.get());
     TStoredSessionList::SelectKnownHostsForSelectedSessions(AKnownHostsImportSessionList, OpensshImportSessionList.get());
-
     TStoredSessionList::ImportSelectedKnownHosts(AKnownHostsImportSessionList);
     TStoredSessionList::ImportSelectedKnownHosts(AKnownHostsImportSessionList);
   }
   }
   return Result;
   return Result;
@@ -132,6 +146,7 @@ TStoredSessionList * __fastcall TImportSessionsDialog::GetSessionList(int Index)
 //---------------------------------------------------------------------
 //---------------------------------------------------------------------
 void __fastcall TImportSessionsDialog::UpdateControls()
 void __fastcall TImportSessionsDialog::UpdateControls()
 {
 {
+  BrowseButton->Visible = (SourceComboBox->ItemIndex == IniIndex);
   PasteButton->Visible = (SourceComboBox->ItemIndex == KnownHostsIndex);
   PasteButton->Visible = (SourceComboBox->ItemIndex == KnownHostsIndex);
   EnableControl(PasteButton, IsFormatInClipboard(CF_TEXT));
   EnableControl(PasteButton, IsFormatInClipboard(CF_TEXT));
   EnableControl(OKButton, ListViewAnyChecked(SessionListView2));
   EnableControl(OKButton, ListViewAnyChecked(SessionListView2));
@@ -445,3 +460,29 @@ void __fastcall TImportSessionsDialog::PasteButtonClick(TObject * /*Sender*/)
   LoadSessions();
   LoadSessions();
 }
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
+TStoredSessionList * TImportSessionsDialog::SelectSessionsForImport(UnicodeString & Error)
+{
+  return Configuration->SelectSessionsForImport(StoredSessions, FIniFileName, Error);
+}
+//---------------------------------------------------------------------------
+void __fastcall TImportSessionsDialog::BrowseButtonClick(TObject *)
+{
+  std::unique_ptr<TOpenDialog> OpenDialog(new TOpenDialog(Application));
+  OpenDialog->Title = LoadStr(IMPORT_INI_TITLE);
+  OpenDialog->Filter = LoadStr(EXPORT_CONF_FILTER);
+  OpenDialog->DefaultExt = L"ini";
+  OpenDialog->FileName = DefaultStr(FIniFileName, Configuration->GetDefaultIniFileExportPath());
+
+  if (OpenDialog->Execute())
+  {
+    SessionListView2->Items->Clear();
+    int Index = SourceComboBox->ItemIndex;
+    FIniFileName = OpenDialog->FileName;
+    UnicodeString Error;
+    FIniImportSessionList.reset(SelectSessionsForImport(Error));
+    FSessionListsList->Items[Index] = FIniImportSessionList.get();
+    FErrors->Strings[Index] = Error;
+    LoadSessions();
+  }
+}
+//---------------------------------------------------------------------------

+ 16 - 6
source/forms/ImportSessions.dfm

@@ -35,7 +35,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     Caption = 'OK'
     Caption = 'OK'
     Default = True
     Default = True
     ModalResult = 1
     ModalResult = 1
-    TabOrder = 4
+    TabOrder = 5
   end
   end
   object CancelButton: TButton
   object CancelButton: TButton
     Left = 215
     Left = 215
@@ -46,7 +46,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     Cancel = True
     Cancel = True
     Caption = 'Cancel'
     Caption = 'Cancel'
     ModalResult = 2
     ModalResult = 2
-    TabOrder = 5
+    TabOrder = 6
   end
   end
   object SessionListView2: TListView
   object SessionListView2: TListView
     Left = 8
     Left = 8
@@ -67,7 +67,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     ParentShowHint = False
     ParentShowHint = False
     ShowColumnHeaders = False
     ShowColumnHeaders = False
     ShowHint = True
     ShowHint = True
-    TabOrder = 2
+    TabOrder = 3
     ViewStyle = vsReport
     ViewStyle = vsReport
     OnInfoTip = SessionListView2InfoTip
     OnInfoTip = SessionListView2InfoTip
     OnKeyUp = SessionListView2KeyUp
     OnKeyUp = SessionListView2KeyUp
@@ -80,7 +80,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     Height = 25
     Height = 25
     Anchors = [akLeft, akBottom]
     Anchors = [akLeft, akBottom]
     Caption = 'Un/check &all'
     Caption = 'Un/check &all'
-    TabOrder = 3
+    TabOrder = 4
     OnClick = CheckAllButtonClick
     OnClick = CheckAllButtonClick
   end
   end
   object HelpButton: TButton
   object HelpButton: TButton
@@ -90,7 +90,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     Height = 25
     Height = 25
     Anchors = [akRight, akBottom]
     Anchors = [akRight, akBottom]
     Caption = '&Help'
     Caption = '&Help'
-    TabOrder = 6
+    TabOrder = 7
     OnClick = HelpButtonClick
     OnClick = HelpButtonClick
   end
   end
   object SourceComboBox: TComboBox
   object SourceComboBox: TComboBox
@@ -106,6 +106,7 @@ object ImportSessionsDialog: TImportSessionsDialog
       'KiTTY'
       'KiTTY'
       'FileZilla'
       'FileZilla'
       'OpenSSH'
       'OpenSSH'
+      'INI file'
       'known_hosts')
       'known_hosts')
   end
   end
   object ErrorPanel: TPanel
   object ErrorPanel: TPanel
@@ -116,7 +117,7 @@ object ImportSessionsDialog: TImportSessionsDialog
     BevelOuter = bvNone
     BevelOuter = bvNone
     Color = clWindow
     Color = clWindow
     ParentBackground = False
     ParentBackground = False
-    TabOrder = 7
+    TabOrder = 8
     object ErrorLabel: TLabel
     object ErrorLabel: TLabel
       Left = 0
       Left = 0
       Top = 0
       Top = 0
@@ -139,4 +140,13 @@ object ImportSessionsDialog: TImportSessionsDialog
     TabOrder = 1
     TabOrder = 1
     OnClick = PasteButtonClick
     OnClick = PasteButtonClick
   end
   end
+  object BrowseButton: TButton
+    Left = 232
+    Top = 8
+    Width = 75
+    Height = 25
+    Caption = 'B&rowse...'
+    TabOrder = 2
+    OnClick = BrowseButtonClick
+  end
 end
 end

+ 6 - 0
source/forms/ImportSessions.h

@@ -24,6 +24,7 @@ __published:
   TPanel *ErrorPanel;
   TPanel *ErrorPanel;
   TLabel *ErrorLabel;
   TLabel *ErrorLabel;
   TButton *PasteButton;
   TButton *PasteButton;
+  TButton *BrowseButton;
   void __fastcall SessionListView2InfoTip(TObject *Sender,
   void __fastcall SessionListView2InfoTip(TObject *Sender,
     TListItem *Item, UnicodeString &InfoTip);
     TListItem *Item, UnicodeString &InfoTip);
   void __fastcall SessionListView2MouseDown(TObject *Sender,
   void __fastcall SessionListView2MouseDown(TObject *Sender,
@@ -35,11 +36,14 @@ __published:
   void __fastcall HelpButtonClick(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);
   void __fastcall SourceComboBoxSelect(TObject *Sender);
   void __fastcall SourceComboBoxSelect(TObject *Sender);
   void __fastcall PasteButtonClick(TObject *Sender);
   void __fastcall PasteButtonClick(TObject *Sender);
+  void __fastcall BrowseButtonClick(TObject *Sender);
 
 
 private:
 private:
   TList * FSessionListsList;
   TList * FSessionListsList;
   TStrings * FErrors;
   TStrings * FErrors;
   std::unique_ptr<TStoredSessionList> FPastedKnownHosts;
   std::unique_ptr<TStoredSessionList> FPastedKnownHosts;
+  std::unique_ptr<TStoredSessionList> FIniImportSessionList;
+  UnicodeString FIniFileName;
   void __fastcall UpdateControls();
   void __fastcall UpdateControls();
   void __fastcall LoadSessions();
   void __fastcall LoadSessions();
   void __fastcall ClearSelections();
   void __fastcall ClearSelections();
@@ -56,7 +60,9 @@ private:
 public:
 public:
   virtual __fastcall TImportSessionsDialog(TComponent * AOwner);
   virtual __fastcall TImportSessionsDialog(TComponent * AOwner);
   void __fastcall Init(TList * SessionListsList, TStrings * Errors);
   void __fastcall Init(TList * SessionListsList, TStrings * Errors);
+  TStoredSessionList * SelectSessionsForImport(UnicodeString & Error);
   bool __fastcall Execute();
   bool __fastcall Execute();
+  __property UnicodeString IniFileName = { read = FIniFileName };
 };
 };
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 #endif
 #endif

+ 2 - 0
source/resource/TextsCore.h

@@ -290,6 +290,7 @@
 #define SSH_HOST_CA_INVALID     770
 #define SSH_HOST_CA_INVALID     770
 #define S3_ASSUME_ROLE_ERROR    780
 #define S3_ASSUME_ROLE_ERROR    780
 #define S3_ASSUME_ROLE_RESPONSE_ERROR 781
 #define S3_ASSUME_ROLE_RESPONSE_ERROR 781
+#define INI_NO_SITES            782
 
 
 #define CORE_CONFIRMATION_STRINGS 300
 #define CORE_CONFIRMATION_STRINGS 300
 #define CONFIRM_PROLONG_TIMEOUT3 301
 #define CONFIRM_PROLONG_TIMEOUT3 301
@@ -537,6 +538,7 @@
 #define PUBLIC_KEY_PERMISSIONS  568
 #define PUBLIC_KEY_PERMISSIONS  568
 #define TIME_RELATIVE           569
 #define TIME_RELATIVE           569
 #define DAYS_SPAN               570
 #define DAYS_SPAN               570
+#define INI_SELECT              571
 
 
 #define CORE_VARIABLE_STRINGS   600
 #define CORE_VARIABLE_STRINGS   600
 #define PUTTY_BASED_ON          601
 #define PUTTY_BASED_ON          601

+ 2 - 0
source/resource/TextsCore1.rc

@@ -266,6 +266,7 @@ BEGIN
   SSH_HOST_CA_INVALID, "Invalid '%s' key data."
   SSH_HOST_CA_INVALID, "Invalid '%s' key data."
   S3_ASSUME_ROLE_ERROR, "Error assuming role '%s'."
   S3_ASSUME_ROLE_ERROR, "Error assuming role '%s'."
   S3_ASSUME_ROLE_RESPONSE_ERROR, "Unexpected response to assume role request (%s)."
   S3_ASSUME_ROLE_RESPONSE_ERROR, "Unexpected response to assume role request (%s)."
+  INI_NO_SITES, "No sites found in \"%s\"."
 
 
   CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION"
   CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION"
   CONFIRM_PROLONG_TIMEOUT3, "Host is not communicating for %d seconds.\n\nWait for another %0:d seconds?"
   CONFIRM_PROLONG_TIMEOUT3, "Host is not communicating for %d seconds.\n\nWait for another %0:d seconds?"
@@ -510,6 +511,7 @@ BEGIN
   PUBLIC_KEY_PERMISSIONS, "Though potentially wrong permissions of \"%s\" file and/or its parent folder were detected. Please check them."
   PUBLIC_KEY_PERMISSIONS, "Though potentially wrong permissions of \"%s\" file and/or its parent folder were detected. Please check them."
   TIME_RELATIVE, "just now|today|yesterday|tomorrow|one second ago|%d seconds ago|one minute ago|%d minutes ago|one hour ago|%d hours ago|one day ago|%d days ago|one week ago|%d weeks ago|one month ago|%d months ago|one year ago|%d years ago"
   TIME_RELATIVE, "just now|today|yesterday|tomorrow|one second ago|%d seconds ago|one minute ago|%d minutes ago|one hour ago|%d hours ago|one day ago|%d days ago|one week ago|%d weeks ago|one month ago|%d months ago|one year ago|%d years ago"
   DAYS_SPAN, "%d days"
   DAYS_SPAN, "%d days"
+  INI_SELECT, "Browse for INI file to import sites from."
 
 
   CORE_VARIABLE_STRINGS, "CORE_VARIABLE"
   CORE_VARIABLE_STRINGS, "CORE_VARIABLE"
   PUTTY_BASED_ON, "SSH and SCP code based on PuTTY %s"
   PUTTY_BASED_ON, "SSH and SCP code based on PuTTY %s"

+ 1 - 0
source/resource/TextsWin.h

@@ -691,6 +691,7 @@
 #define SSH_HOST_CA_NO_HOSTS    6204
 #define SSH_HOST_CA_NO_HOSTS    6204
 #define SSH_HOST_CA_HOSTS_INVALID 6205
 #define SSH_HOST_CA_HOSTS_INVALID 6205
 #define LOGIN_NOT_SHOWING_AGAIN 6206
 #define LOGIN_NOT_SHOWING_AGAIN 6206
+#define IMPORT_INI_TITLE        6207
 
 
 // 2xxx is reserved for TextsFileZilla.h
 // 2xxx is reserved for TextsFileZilla.h
 
 

+ 1 - 0
source/resource/TextsWin1.rc

@@ -696,6 +696,7 @@ BEGIN
         SSH_HOST_CA_NO_HOSTS, "No validity expression configured."
         SSH_HOST_CA_NO_HOSTS, "No validity expression configured."
         SSH_HOST_CA_HOSTS_INVALID, "Error in validity expression."
         SSH_HOST_CA_HOSTS_INVALID, "Error in validity expression."
         LOGIN_NOT_SHOWING_AGAIN, "**Stop showing Login dialog automatically?** Please confirm if you really want to WinSCP stop showing Login dialog automatically on startup and when the last session is closed.\n\nIf you change your mind later, you can revert this in Preferences on Environment > Window page.\n\nTo open the Login dialog manually, go to Tabs > New Tab [> Remote Tab] or use corresponding toolbar button."
         LOGIN_NOT_SHOWING_AGAIN, "**Stop showing Login dialog automatically?** Please confirm if you really want to WinSCP stop showing Login dialog automatically on startup and when the last session is closed.\n\nIf you change your mind later, you can revert this in Preferences on Environment > Window page.\n\nTo open the Login dialog manually, go to Tabs > New Tab [> Remote Tab] or use corresponding toolbar button."
+        IMPORT_INI_TITLE, "Select file to import sites from"
 
 
         WIN_VARIABLE_STRINGS, "WIN_VARIABLE"
         WIN_VARIABLE_STRINGS, "WIN_VARIABLE"
         WINSCP_COPYRIGHT, "Copyright © 2000–2024 Martin Prikryl"
         WINSCP_COPYRIGHT, "Copyright © 2000–2024 Martin Prikryl"