From 59a907ee041082b4387da444575b8aa4c83feedf Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Mon, 27 May 2024 12:14:53 +0700 Subject: [PATCH 1/2] fix(developer): handle invalid default project path in options If a relative or otherwise invalid path is given for `default project path` in .keymandeveloper/options.json, then this will reset the path to the default of `%Documents%/Keyman Developer/Projects`. Fixes: #11554 Fixes: KEYMAN-DEVELOPER-1ZB --- .../src/tike/main/KeymanDeveloperOptions.pas | 13 +++++++++++- developer/src/tike/main/UfrmMain.pas | 21 ++++++++++++++----- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/developer/src/tike/main/KeymanDeveloperOptions.pas b/developer/src/tike/main/KeymanDeveloperOptions.pas index 3e31f27688..6eb0573096 100644 --- a/developer/src/tike/main/KeymanDeveloperOptions.pas +++ b/developer/src/tike/main/KeymanDeveloperOptions.pas @@ -82,6 +82,7 @@ type procedure optWriteBool(const nm: string; value: Boolean); procedure optWriteInt(const nm: string; value: Integer); procedure WriteServerConfigurationJson; + class function Default_DefaultProjectPath: string; static; public procedure Read; procedure Write; @@ -365,7 +366,12 @@ begin FFix183_LadderLength := optReadInt(SRegValue_IDEOpt_WebLadderLength, CRegValue_IDEOpt_WebLadderLength_Default); - FDefaultProjectPath := IncludeTrailingPathDelimiter(optReadString(SRegValue_IDEOpt_DefaultProjectPath, GetFolderPath(CSIDL_PERSONAL) + CDefaultProjectPath)); + FDefaultProjectPath := IncludeTrailingPathDelimiter(optReadString(SRegValue_IDEOpt_DefaultProjectPath, Default_DefaultProjectPath)); + if (FDefaultProjectPath = '\') or IsRelativePath(FDefaultProjectPath) then + begin + // #11554 + FDefaultProjectPath := Default_DefaultProjectPath; + end; // for consistency with Keyman.System.KeymanSentryClient, we need to use // reg.ReadInteger, as regReadInt, which in the dim dark past started @@ -613,6 +619,11 @@ begin Result := DefaultEditorThemeItemIndex(s) >= 0; end; +class function TKeymanDeveloperOptions.Default_DefaultProjectPath: string; +begin + Result := GetFolderPath(CSIDL_PERSONAL) + CDefaultProjectPath; +end; + function LoadKeymanDeveloperSentryFlags: TKeymanSentryClientFlags; begin Result := [kscfCaptureExceptions, kscfShowUI, kscfTerminate]; diff --git a/developer/src/tike/main/UfrmMain.pas b/developer/src/tike/main/UfrmMain.pas index b6468ba222..44b5873245 100644 --- a/developer/src/tike/main/UfrmMain.pas +++ b/developer/src/tike/main/UfrmMain.pas @@ -552,11 +552,22 @@ begin FFilesToOpen := TStringList.Create; - if not ForceDirectories(FKeymanDeveloperOptions.DefaultProjectPath) then - begin - // Fall back to Documents folder if we cannot create the default project path - // Documents folder should always exist - FKeymanDeveloperOptions.DefaultProjectPath := GetFolderPath(CSIDL_PERSONAL); + try + if not ForceDirectories(FKeymanDeveloperOptions.DefaultProjectPath) then + begin + // Fall back to Documents folder if we cannot create the default project path + // Documents folder should always exist + FKeymanDeveloperOptions.DefaultProjectPath := GetFolderPath(CSIDL_PERSONAL); + end; + except + on E:EInOutError do + begin + // If the DefaultProjectPath is a relative, invalid path, then it may + // cause EInOutError (#11554). Note that this exception should no longer + // be possible because the path is sanitized when loaded in + // KeymanDeveloperOptions.pas, but keeping this fallback just in case. + FKeymanDeveloperOptions.DefaultProjectPath := GetFolderPath(CSIDL_PERSONAL); + end; end; FFirstShow := True; From 951eb571feba3e1d6b28c9f1778adcde3ab64c16 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Tue, 28 May 2024 14:39:07 +0700 Subject: [PATCH 2/2] chore(developer): rename Default_DefaultProjectPath to Get_Initial_DefaultProjectPath --- developer/src/tike/main/KeymanDeveloperOptions.pas | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/developer/src/tike/main/KeymanDeveloperOptions.pas b/developer/src/tike/main/KeymanDeveloperOptions.pas index 6eb0573096..a2c1b2af03 100644 --- a/developer/src/tike/main/KeymanDeveloperOptions.pas +++ b/developer/src/tike/main/KeymanDeveloperOptions.pas @@ -82,7 +82,7 @@ type procedure optWriteBool(const nm: string; value: Boolean); procedure optWriteInt(const nm: string; value: Integer); procedure WriteServerConfigurationJson; - class function Default_DefaultProjectPath: string; static; + class function Get_Initial_DefaultProjectPath: string; static; public procedure Read; procedure Write; @@ -366,11 +366,11 @@ begin FFix183_LadderLength := optReadInt(SRegValue_IDEOpt_WebLadderLength, CRegValue_IDEOpt_WebLadderLength_Default); - FDefaultProjectPath := IncludeTrailingPathDelimiter(optReadString(SRegValue_IDEOpt_DefaultProjectPath, Default_DefaultProjectPath)); + FDefaultProjectPath := IncludeTrailingPathDelimiter(optReadString(SRegValue_IDEOpt_DefaultProjectPath, Get_Initial_DefaultProjectPath)); if (FDefaultProjectPath = '\') or IsRelativePath(FDefaultProjectPath) then begin // #11554 - FDefaultProjectPath := Default_DefaultProjectPath; + FDefaultProjectPath := Get_Initial_DefaultProjectPath; end; // for consistency with Keyman.System.KeymanSentryClient, we need to use @@ -619,7 +619,7 @@ begin Result := DefaultEditorThemeItemIndex(s) >= 0; end; -class function TKeymanDeveloperOptions.Default_DefaultProjectPath: string; +class function TKeymanDeveloperOptions.Get_Initial_DefaultProjectPath: string; begin Result := GetFolderPath(CSIDL_PERSONAL) + CDefaultProjectPath; end;