From 9a14039e8cb1f91e2d7c45e5cd54a9f315433472 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 22 Nov 2023 14:16:32 +1000 Subject: [PATCH 01/11] =?UTF-8?q?chore:=20establish=20epic/windows-updates?= =?UTF-8?q?=20=F0=9F=92=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- windows/src/TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/src/TODO.md b/windows/src/TODO.md index 2a0d49470e..301a8409db 100644 --- a/windows/src/TODO.md +++ b/windows/src/TODO.md @@ -3,3 +3,4 @@ 2. Suggestion: the 'repair profiles' pattern should look at the list of enabled Keyman keyboards vs the list of profiles in HKCU\Control Panel\International and fixup discrepancies there. This should probably be an automatic task. This will probably fix the issues that Makara noted around missing keyboards across multiple user profiles etc. 3. uninstall and reinstall of package in same ELEVATED session of kmshell causes error 4. Review Joshua's corrections document +5. Implement Windows Updates Stage 1 -- https://github.com/keymanapp/keyman/issues/10038 \ No newline at end of file From 8756e4016c163aa74f4a7d1f307a43372fa3d623 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Wed, 22 Nov 2023 14:23:30 +1000 Subject: [PATCH 02/11] feat(windows): Online Update check stream update data Starts to refactor the online update check in Windows so that the results are stored to disk and can be checked at any time. This commit: * removes keepintouch frame * adds update frame * adds serialization of update check data * presents the serialized update check data in the update frame Online updates won't currently work. --- .../Keyman.System.UpdateCheckResponse.pas | 41 +++++- common/windows/delphi/general/KeymanPaths.pas | 9 ++ windows/src/desktop/kmshell/kmshell.dpr | 6 +- windows/src/desktop/kmshell/kmshell.dproj | 2 + .../main/Keyman.System.UpdateCheckStorage.pas | 52 ++++++++ .../kmshell/main/OnlineUpdateCheck.pas | 106 +++++++++------ windows/src/desktop/kmshell/main/UfrmMain.pas | 17 +++ .../kmshell/render/UpdateXMLRenderer.pas | 91 +++++++++++++ windows/src/desktop/kmshell/xml/config.css | 10 +- windows/src/desktop/kmshell/xml/config.js | 2 +- windows/src/desktop/kmshell/xml/keyman.xsl | 4 +- .../kmshell/xml/keyman_keepintouch.xsl | 23 ---- .../src/desktop/kmshell/xml/keyman_menu.xsl | 6 +- .../desktop/kmshell/xml/keyman_support.xsl | 1 - .../src/desktop/kmshell/xml/keyman_update.xsl | 125 ++++++++++++++++++ 15 files changed, 409 insertions(+), 86 deletions(-) create mode 100644 windows/src/desktop/kmshell/main/Keyman.System.UpdateCheckStorage.pas create mode 100644 windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas delete mode 100644 windows/src/desktop/kmshell/xml/keyman_keepintouch.xsl create mode 100644 windows/src/desktop/kmshell/xml/keyman_update.xsl diff --git a/common/windows/delphi/general/Keyman.System.UpdateCheckResponse.pas b/common/windows/delphi/general/Keyman.System.UpdateCheckResponse.pas index 918c396411..adc90ab152 100644 --- a/common/windows/delphi/general/Keyman.System.UpdateCheckResponse.pas +++ b/common/windows/delphi/general/Keyman.System.UpdateCheckResponse.pas @@ -35,6 +35,7 @@ type TUpdateCheckResponse = record private + FOriginalData: string; FInstallSize: Int64; FInstallURL: string; FNewVersion: string; @@ -46,9 +47,13 @@ type FFileName: string; function ParseKeyboards(nodes: TJSONObject): Boolean; function ParseLanguages(i: Integer; v: TJSONValue): Boolean; + function DoParse(const message, app, currentVersion: string): Boolean; public function Parse(const message: AnsiString; const app, currentVersion: string): Boolean; + procedure SaveToFile(const Filename: string); + function LoadFromFile(const Filename, app, currentVersion: string): Boolean; + property CurrentVersion: string read FCurrentVersion; property NewVersion: string read FNewVersion; property NewVersionWithTag: string read FNewVersionWithTag; @@ -58,25 +63,32 @@ type property ErrorMessage: string read FErrorMessage; property Status: TUpdateCheckResponseStatus read FStatus; property Packages: TUpdateCheckResponsePackages read FPackages; + property OriginalData: string read FOriginalData; end; implementation uses + System.Classes, System.Generics.Collections, versioninfo; { TUpdateCheckResponse } function TUpdateCheckResponse.Parse(const message: AnsiString; const app, currentVersion: string): Boolean; +begin + Result := DoParse(string(UTF8String(message)), app, currentVersion); +end; + +function TUpdateCheckResponse.DoParse(const message, app, currentVersion: string): Boolean; var node, doc: TJSONObject; begin + FOriginalData := message; FCurrentVersion := currentVersion; FStatus := ucrsNoUpdate; - // TODO: test with UTF8 characters in response - doc := TJSONObject.ParseJSONValue(UTF8String(message)) as TJSONObject; + doc := TJSONObject.ParseJSONValue(UTF8String(FOriginalData)) as TJSONObject; if doc = nil then begin FErrorMessage := Format('Invalid response:'#13#10'%s', [string(message)]); @@ -168,4 +180,29 @@ begin Result := True; end; +function TUpdateCheckResponse.LoadFromFile(const Filename, app, currentVersion: string): Boolean; +var + ss: TStringStream; +begin + ss := TStringStream.Create('', TEncoding.UTF8); + try + ss.LoadFromFile(Filename); + Result := DoParse(ss.DataString, app, currentVersion); + finally + ss.Free; + end; +end; + +procedure TUpdateCheckResponse.SaveToFile(const Filename: string); +var + ss: TStringStream; +begin + ss := TStringStream.Create(FOriginalData, TEncoding.UTF8); + try + ss.SaveToFile(Filename); + finally + ss.Free; + end; +end; + end. diff --git a/common/windows/delphi/general/KeymanPaths.pas b/common/windows/delphi/general/KeymanPaths.pas index 88ef8282ef..76251c3b51 100644 --- a/common/windows/delphi/general/KeymanPaths.pas +++ b/common/windows/delphi/general/KeymanPaths.pas @@ -16,6 +16,8 @@ type const S_CEF_LibCef = 'libcef.dll'; const S_CEF_SubProcess = 'kmbrowserhost.exe'; const S_CustomisationFilename = 'desktop_pro.pxx'; + + const S_KeymanAppData_UpdateCache = 'Keyman\UpdateCache\'; public const S_KMShell = 'kmshell.exe'; const S_TSysInfoExe = 'tsysinfo.exe'; @@ -26,8 +28,10 @@ type const S_FallbackKeyboardPath = 'Keyboards\'; const S__Package = '_Package\'; const S_MCompileExe = 'mcompile.exe'; + const S_UpdateCache_Metadata = 'cache.json'; class function ErrorLogPath(const app: string = ''): string; static; class function KeymanHelpPath(const HelpFile: string): string; static; + class function KeymanUpdateCachePath(const filename: string = ''): string; static; class function KeymanDesktopInstallPath(const filename: string = ''): string; static; class function KeymanEngineInstallPath(const filename: string = ''): string; static; class function KeymanDesktopInstallDir: string; static; @@ -417,6 +421,11 @@ begin Result := ''; end; +class function TKeymanPaths.KeymanUpdateCachePath(const filename: string): string; +begin + Result := GetFolderPath(CSIDL_LOCAL_APPDATA) + S_KeymanAppData_UpdateCache + filename; +end; + class function TKeymanPaths.RunningFromSource(var keyman_root: string): Boolean; begin // On developer machines, if we are running within the source repo, then use diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr index befb47fcf9..1497982ccb 100644 --- a/windows/src/desktop/kmshell/kmshell.dpr +++ b/windows/src/desktop/kmshell/kmshell.dpr @@ -176,7 +176,9 @@ uses TaskScheduler_TLB in '..\..\global\delphi\winapi\TaskScheduler_TLB.pas', Keyman.Configuration.System.HttpServer.App.TextEditorFonts in 'startup\help\Keyman.Configuration.System.HttpServer.App.TextEditorFonts.pas', Keyman.Configuration.System.HttpServer.App.Locale in 'web\Keyman.Configuration.System.HttpServer.App.Locale.pas', - Keyman.System.AndroidStringToKeymanLocaleString in '..\..\..\..\common\windows\delphi\general\Keyman.System.AndroidStringToKeymanLocaleString.pas'; + Keyman.System.AndroidStringToKeymanLocaleString in '..\..\..\..\common\windows\delphi\general\Keyman.System.AndroidStringToKeymanLocaleString.pas', + UpdateXMLRenderer in 'render\UpdateXMLRenderer.pas', + Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas'; {$R VERSION.RES} {$R manifest.res} @@ -198,7 +200,7 @@ begin Application.Initialize; Application.Title := 'Keyman Configuration'; Application.CreateForm(TmodWebHttpServer, modWebHttpServer); - try + try Run; finally FreeAndNil(modWebHttpServer); diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj index 5d716d8c4a..71c91d9e56 100644 --- a/windows/src/desktop/kmshell/kmshell.dproj +++ b/windows/src/desktop/kmshell/kmshell.dproj @@ -353,6 +353,8 @@ + + Cfg_2 diff --git a/windows/src/desktop/kmshell/main/Keyman.System.UpdateCheckStorage.pas b/windows/src/desktop/kmshell/main/Keyman.System.UpdateCheckStorage.pas new file mode 100644 index 0000000000..f1a156bfa0 --- /dev/null +++ b/windows/src/desktop/kmshell/main/Keyman.System.UpdateCheckStorage.pas @@ -0,0 +1,52 @@ +unit Keyman.System.UpdateCheckStorage; + +interface + +uses + Keyman.System.UpdateCheckResponse; + +type + TUpdateCheckStorage = class sealed + private + class function MetadataFilename: string; static; + public + class function HasUpdates: Boolean; static; + class function LoadUpdateCacheData(var data: TUpdateCheckResponse): Boolean; static; + class procedure SaveUpdateCacheData(const data: TUpdateCheckResponse); static; + end; + +implementation + +uses + System.SysUtils, + + KeymanPaths, + KeymanVersion; + +{ TUpdateCheckStorage } + +class function TUpdateCheckStorage.MetadataFilename: string; +begin + Result := TKeymanPaths.KeymanUpdateCachePath(TKeymanPaths.S_UpdateCache_Metadata); +end; + +class procedure TUpdateCheckStorage.SaveUpdateCacheData( + const data: TUpdateCheckResponse); +begin + ForceDirectories(TKeymanPaths.KeymanUpdateCachePath); + data.SaveToFile(MetadataFilename); +end; + +class function TUpdateCheckStorage.HasUpdates: Boolean; +begin + Result := FileExists(MetadataFilename); +end; + +class function TUpdateCheckStorage.LoadUpdateCacheData(var data: TUpdateCheckResponse): Boolean; +begin + Result := + HasUpdates and + data.LoadFromFile(MetadataFilename, 'bundle', CKeymanVersionInfo.Version); +end; + +end. diff --git a/windows/src/desktop/kmshell/main/OnlineUpdateCheck.pas b/windows/src/desktop/kmshell/main/OnlineUpdateCheck.pas index 15fb06f4e9..c202742813 100644 --- a/windows/src/desktop/kmshell/main/OnlineUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/OnlineUpdateCheck.pas @@ -98,6 +98,7 @@ type FShowErrors: Boolean; FDownload: TOnlineUpdateCheckDownloadParams; + FCheckOnly: Boolean; function DownloadUpdates: Boolean; procedure DoDownloadUpdates(AOwner: TfrmDownloadProgress; var Result: Boolean); @@ -112,7 +113,9 @@ type public public - constructor Create(AOwner: TCustomForm; AForce, ASilent: Boolean); + function ResponseToParams(const ucr: TUpdateCheckResponse): TOnlineUpdateCheckParams; + + constructor Create(AOwner: TCustomForm; AForce, ASilent: Boolean; ACheckOnly: Boolean = False); destructor Destroy; override; function Run: TOnlineUpdateCheckResult; property ShowErrors: Boolean read FShowErrors write FShowErrors; @@ -146,6 +149,7 @@ uses KLog, keymanapi_TLB, KeymanVersion, + Keyman.System.UpdateCheckStorage, kmint, ErrorControlledRegistry, RegistryKeys, @@ -165,7 +169,7 @@ const { TOnlineUpdateCheck } -constructor TOnlineUpdateCheck.Create(AOwner: TCustomForm; AForce, ASilent: Boolean); +constructor TOnlineUpdateCheck.Create(AOwner: TCustomForm; AForce, ASilent: Boolean; ACheckOnly: Boolean); begin inherited Create; @@ -176,6 +180,7 @@ begin FSilent := ASilent; FForce := AForce; + FCheckOnly := ACheckOnly; KL.Log('TOnlineUpdateCheck.Create'); end; @@ -477,10 +482,9 @@ end; function TOnlineUpdateCheck.DoRun: TOnlineUpdateCheckResult; var flags: DWord; - i, n: Integer; - pkg: IKeymanPackage; - j: Integer; + i: Integer; ucr: TUpdateCheckResponse; + pkg: IKeymanPackage; begin {FProxyHost := ''; FProxyPort := 0;} @@ -567,45 +571,15 @@ begin begin if ucr.Parse(Response.MessageBodyAsString, 'bundle', CKeymanVersionInfo.Version) then begin - SetLength(FParams.Packages,0); - for i := Low(ucr.Packages) to High(ucr.Packages) do + ResponseToParams(ucr); + + if FCheckOnly then begin - n := kmcom.Packages.IndexOf(ucr.Packages[i].ID); - if n >= 0 then - begin - pkg := kmcom.Packages[n]; - j := Length(FParams.Packages); - SetLength(FParams.Packages, j+1); - FParams.Packages[j].NewID := ucr.Packages[i].NewID; - FParams.Packages[j].ID := ucr.Packages[i].ID; - FParams.Packages[j].Description := ucr.Packages[i].Name; - FParams.Packages[j].OldVersion := pkg.Version; - FParams.Packages[j].NewVersion := ucr.Packages[i].NewVersion; - FParams.Packages[j].DownloadSize := ucr.Packages[i].DownloadSize; - FParams.Packages[j].DownloadURL := ucr.Packages[i].DownloadURL; - FParams.Packages[j].FileName := ucr.Packages[i].FileName; - pkg := nil; - end - else - FErrorMessage := 'Unable to find package '+ucr.Packages[i].ID; - end; - - case ucr.Status of - ucrsNoUpdate: - begin - FErrorMessage := ucr.ErrorMessage; - end; - ucrsUpdateReady: - begin - FParams.Keyman.OldVersion := ucr.CurrentVersion; - FParams.Keyman.NewVersion := ucr.NewVersion; - FParams.Keyman.DownloadURL := ucr.InstallURL; - FParams.Keyman.DownloadSize := ucr.InstallSize; - FParams.Keyman.FileName := ucr.FileName; - end; - end; - - if (Length(FParams.Packages) > 0) or (FParams.Keyman.DownloadURL <> '') then + // TODO: Refactor this + TUpdateCheckStorage.SaveUpdateCacheData(ucr); + Result := FParams.Result; + end + else if (Length(FParams.Packages) > 0) or (FParams.Keyman.DownloadURL <> '') then begin if not FSilent then ShowUpdateForm @@ -651,6 +625,52 @@ begin end; end; +function TOnlineUpdateCheck.ResponseToParams(const ucr: TUpdateCheckResponse): TOnlineUpdateCheckParams; +var + i, j, n: Integer; + pkg: IKeymanPackage; +begin + SetLength(FParams.Packages,0); + for i := Low(ucr.Packages) to High(ucr.Packages) do + begin + n := kmcom.Packages.IndexOf(ucr.Packages[i].ID); + if n >= 0 then + begin + pkg := kmcom.Packages[n]; + j := Length(FParams.Packages); + SetLength(FParams.Packages, j+1); + FParams.Packages[j].NewID := ucr.Packages[i].NewID; + FParams.Packages[j].ID := ucr.Packages[i].ID; + FParams.Packages[j].Description := ucr.Packages[i].Name; + FParams.Packages[j].OldVersion := pkg.Version; + FParams.Packages[j].NewVersion := ucr.Packages[i].NewVersion; + FParams.Packages[j].DownloadSize := ucr.Packages[i].DownloadSize; + FParams.Packages[j].DownloadURL := ucr.Packages[i].DownloadURL; + FParams.Packages[j].FileName := ucr.Packages[i].FileName; + pkg := nil; + end + else + FErrorMessage := 'Unable to find package '+ucr.Packages[i].ID; + end; + + case ucr.Status of + ucrsNoUpdate: + begin + FErrorMessage := ucr.ErrorMessage; + end; + ucrsUpdateReady: + begin + FParams.Keyman.OldVersion := ucr.CurrentVersion; + FParams.Keyman.NewVersion := ucr.NewVersion; + FParams.Keyman.DownloadURL := ucr.InstallURL; + FParams.Keyman.DownloadSize := ucr.InstallSize; + FParams.Keyman.FileName := ucr.FileName; + end; + end; + + Result := FParams; +end; + procedure OnlineUpdateAdmin(OwnerForm: TCustomForm; Path: string); var Package: TOnlineUpdateCheckParamsPackage; diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas index d9a6f8b441..969e85d99c 100644 --- a/windows/src/desktop/kmshell/main/UfrmMain.pas +++ b/windows/src/desktop/kmshell/main/UfrmMain.pas @@ -148,6 +148,7 @@ type procedure OpenSite(params: TStringList); procedure DoApply; procedure DoRefresh; + procedure Update_CheckNow; protected procedure FireCommand(const command: WideString; params: TStringList); override; @@ -202,6 +203,7 @@ uses UfrmTextEditor, uninstall, Upload_Settings, + UpdateXMLRenderer, utildir, utilexecute, utilkmshell, @@ -301,6 +303,7 @@ begin FXMLRenderers.Add(TOptionsXMLRenderer.Create(FXMLRenderers)); FXMLRenderers.Add(TLanguagesXMLRenderer.Create(FXMLRenderers)); FXMLRenderers.Add(TSupportXMLRenderer.Create(FXMLRenderers)); + FXMLRenderers.Add(TUpdateXMLRenderer.Create(FXMLRenderers)); xml := FXMLRenderers.RenderToString(s); sharedData.Init( @@ -345,6 +348,9 @@ begin else if command = 'support_updatecheck' then Support_UpdateCheck else if command = 'support_proxyconfig' then Support_ProxyConfig + else if command = 'update_checknow' then Update_CheckNow + + else if command = 'contact_support' then Support_ContactSupport(params) // I4390 else if command = 'opensite' then OpenSite(params) @@ -815,6 +821,17 @@ begin end; end; +procedure TfrmMain.Update_CheckNow; +begin + with TOnlineUpdateCheck.Create(Self, True, True, True) do + try + Run; + finally + Free; + end; + DoRefresh; +end; + procedure TfrmMain.TntFormCloseQuery(Sender: TObject; var CanClose: Boolean); begin inherited; diff --git a/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas b/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas new file mode 100644 index 0000000000..0c102fdca7 --- /dev/null +++ b/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas @@ -0,0 +1,91 @@ +(* + Name: UpdateXMLRenderer + Copyright: Copyright (C) SIL International. +*) +unit UpdateXMLRenderer; + +interface + +uses + XMLRenderer, + Windows; + +type + TUpdateXMLRenderer = class(TXMLRenderer) + protected + function XMLData: WideString; override; + end; + +implementation + +uses + StrUtils, + SysUtils, + VersionInfo, + kmint, + KeymanVersion, + keymanapi_TLB, + Keyman.System.LocaleStrings, + Keyman.System.UpdateCheckResponse, + Keyman.System.UpdateCheckStorage, + MessageIdentifierConsts, + MessageIdentifiers, + OnlineUpdateCheck, + utilxml; + +{ TUpdateXMLRenderer } + +function TUpdateXMLRenderer.XMLData: WideString; +var + xml: string; + ucr: TUpdateCheckResponse; + ouc: TOnlineUpdateCheck; + params: TOnlineUpdateCheckParams; + i: Integer; +begin + xml := ''; + + if TUpdateCheckStorage.LoadUpdateCacheData(ucr) then + begin + ouc := TOnlineUpdateCheck.Create(nil, False, True); + params := ouc.ResponseToParams(ucr); + + if (Params.Keyman.DownloadURL <> '') then + begin + xml := xml + + ''+ + '0'+ + IfThen(not kmcom.SystemInfo.IsAdministrator, '')+ + ''+ + ''+xmlencode(TLocaleStrings.MsgFromIdFormat(kmcom, SKUpdate_KeymanText, [Params.Keyman.NewVersion]))+''+ + ''+xmlencode(Params.Keyman.NewVersion)+''+ + ''+xmlencode(Params.Keyman.OldVersion)+''+ + ''+xmlencode(Format('%d', [Params.Keyman.DownloadSize div 1024]))+'KB'+ + ''+xmlencode(Params.Keyman.DownloadURL)+''+ + ''+ + ''; + end; + + for i := 0 to High(Params.Packages) do + begin + xml := xml + + ''+ + ''+IntToStr(i+1)+''+ + IfThen(not kmcom.SystemInfo.IsAdministrator, '')+ + ''+ + ''+xmlencode(TLocaleStrings.MsgFromIdFormat(kmcom, SKUpdate_PackageText, + [Params.Packages[i].Description, Params.Packages[i].NewVersion]))+''+ + ''+xmlencode(Params.Packages[i].NewVersion)+''+ + ''+xmlencode(Params.Packages[i].OldVersion)+''+ + ''+xmlencode(Format('%d', [Params.Packages[i].DownloadSize div 1024]))+'KB'+ + ''+xmlencode(Params.Packages[i].DownloadURL)+''+ + ''+ + ''; + end; + end; + + Result := ''+xml+''; +end; + +end. + diff --git a/windows/src/desktop/kmshell/xml/config.css b/windows/src/desktop/kmshell/xml/config.css index 88c8d22b39..704c4e6f92 100644 --- a/windows/src/desktop/kmshell/xml/config.css +++ b/windows/src/desktop/kmshell/xml/config.css @@ -1093,19 +1093,11 @@ th height: 16px; } -#keepintouch_content { +#update_content { height: 100%; overflow: hidden; } -#keepintouch_frame { - box-sizing: border-box; - width:100%; - height:100%; - border:none; - user-select: none; -} - /* QRCodes */ .qrcode { diff --git a/windows/src/desktop/kmshell/xml/config.js b/windows/src/desktop/kmshell/xml/config.js index cf761ce6c2..47b66ca89c 100644 --- a/windows/src/desktop/kmshell/xml/config.js +++ b/windows/src/desktop/kmshell/xml/config.js @@ -35,7 +35,7 @@ function windowResize() e = _$('subcontent_pro'); if(e) e.style.height = h; _$('subcontent_support').style.height = h; - _$('subcontent_keepintouch').style.height = h; + _$('subcontent_update').style.height = h; } } diff --git a/windows/src/desktop/kmshell/xml/keyman.xsl b/windows/src/desktop/kmshell/xml/keyman.xsl index 94c7ed7e95..a774e0aa9c 100644 --- a/windows/src/desktop/kmshell/xml/keyman.xsl +++ b/windows/src/desktop/kmshell/xml/keyman.xsl @@ -12,7 +12,7 @@ - + @@ -50,7 +50,7 @@
-
+
diff --git a/windows/src/desktop/kmshell/xml/keyman_keepintouch.xsl b/windows/src/desktop/kmshell/xml/keyman_keepintouch.xsl deleted file mode 100644 index 780166619f..0000000000 --- a/windows/src/desktop/kmshell/xml/keyman_keepintouch.xsl +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - -
- - -
- -
-
- -
-
-
-
\ No newline at end of file diff --git a/windows/src/desktop/kmshell/xml/keyman_menu.xsl b/windows/src/desktop/kmshell/xml/keyman_menu.xsl index 4fc1b0f225..99d5e05fac 100644 --- a/windows/src/desktop/kmshell/xml/keyman_menu.xsl +++ b/windows/src/desktop/kmshell/xml/keyman_menu.xsl @@ -9,20 +9,20 @@ - + - + diff --git a/windows/src/desktop/kmshell/xml/keyman_support.xsl b/windows/src/desktop/kmshell/xml/keyman_support.xsl index d472f88155..e33d031a47 100644 --- a/windows/src/desktop/kmshell/xml/keyman_support.xsl +++ b/windows/src/desktop/kmshell/xml/keyman_support.xsl @@ -41,7 +41,6 @@
  • keyman:link?url=/keyman.com
  • -
  • keyman:link?url=/go//support
  • diff --git a/windows/src/desktop/kmshell/xml/keyman_update.xsl b/windows/src/desktop/kmshell/xml/keyman_update.xsl new file mode 100644 index 0000000000..9acfd28d3b --- /dev/null +++ b/windows/src/desktop/kmshell/xml/keyman_update.xsl @@ -0,0 +1,125 @@ + + + + + + + +
    + + +
    + +
    +
    + +
    + +
    + +
    +  
    +
    + +
    Updates are available which will be applied when Windows is next restarted:
    + +
    + + + + + + + + + +
    + +
    + +
    + + + keyman:update_applynow + 220px + + + + + keyman:update_checknow + 220px + +
    + + + +
    +
    +
    + + + + + + javascript:updateTick(""); + Update_ + checked + Update_ + + + Update__RequiresAdmin + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    +
    + + + +
    + + + + +
    +
    + +
    + +
    \ No newline at end of file From 2d2f9198c245318d2d25acb51f6be87037786fde Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:56:03 +1000 Subject: [PATCH 03/11] feat(windows): address review comments Co-authored-by: Eberhard Beilharz --- windows/src/desktop/kmshell/kmshell.dpr | 2 +- windows/src/desktop/kmshell/main/UfrmMain.pas | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr index 1497982ccb..c4f21ea44d 100644 --- a/windows/src/desktop/kmshell/kmshell.dpr +++ b/windows/src/desktop/kmshell/kmshell.dpr @@ -200,7 +200,7 @@ begin Application.Initialize; Application.Title := 'Keyman Configuration'; Application.CreateForm(TmodWebHttpServer, modWebHttpServer); - try + try Run; finally FreeAndNil(modWebHttpServer); diff --git a/windows/src/desktop/kmshell/main/UfrmMain.pas b/windows/src/desktop/kmshell/main/UfrmMain.pas index 969e85d99c..a779fdcdd2 100644 --- a/windows/src/desktop/kmshell/main/UfrmMain.pas +++ b/windows/src/desktop/kmshell/main/UfrmMain.pas @@ -350,7 +350,6 @@ begin else if command = 'update_checknow' then Update_CheckNow - else if command = 'contact_support' then Support_ContactSupport(params) // I4390 else if command = 'opensite' then OpenSite(params) From cf8d6ea3337e94b43efaebc208823619313ea148 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Wed, 6 Dec 2023 15:12:42 +1000 Subject: [PATCH 04/11] feat(windows): remove UI comment for downloading updates --- windows/src/desktop/kmshell/kmshell.dpr | 5 +- windows/src/desktop/kmshell/kmshell.dproj | 15 +- .../kmshell/main/RemoteUpdateCheck.pas | 460 ++++++++++++++++++ windows/src/desktop/kmshell/main/initprog.pas | 16 + 4 files changed, 487 insertions(+), 9 deletions(-) create mode 100644 windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr index c4f21ea44d..2b1e14b517 100644 --- a/windows/src/desktop/kmshell/kmshell.dpr +++ b/windows/src/desktop/kmshell/kmshell.dpr @@ -178,7 +178,8 @@ uses Keyman.Configuration.System.HttpServer.App.Locale in 'web\Keyman.Configuration.System.HttpServer.App.Locale.pas', Keyman.System.AndroidStringToKeymanLocaleString in '..\..\..\..\common\windows\delphi\general\Keyman.System.AndroidStringToKeymanLocaleString.pas', UpdateXMLRenderer in 'render\UpdateXMLRenderer.pas', - Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas'; + Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas', + RemoteUpdateCheck in 'main\RemoteUpdateCheck.pas'; {$R VERSION.RES} {$R manifest.res} @@ -200,7 +201,7 @@ begin Application.Initialize; Application.Title := 'Keyman Configuration'; Application.CreateForm(TmodWebHttpServer, modWebHttpServer); - try + try Run; finally FreeAndNil(modWebHttpServer); diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj index 71c91d9e56..2e6d27e6cd 100644 --- a/windows/src/desktop/kmshell/kmshell.dproj +++ b/windows/src/desktop/kmshell/kmshell.dproj @@ -355,6 +355,7 @@ + Cfg_2 @@ -416,24 +417,24 @@ False - + kmshell.rsm true + + + kmshell.exe + true + + .\ true - - - kmshell.exe - true - - 1 diff --git a/windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas new file mode 100644 index 0000000000..75c57315a9 --- /dev/null +++ b/windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas @@ -0,0 +1,460 @@ +(* + Name: WebUpdateCheck + Copyright: Copyright (C) SIL International. + Documentation: + Description: + Create Date: 5 Dec 2023 + + Modified Date: + Authors: rcruickshank + Related Files: + Dependencies: + + Bugs: + Todo: + Notes: + History: +*) +unit RemoteUpdateCheck; // I3306 + +interface + +uses + System.Classes, + System.SysUtils, + System.UITypes, + System.IOUtils, + Vcl.Forms, + KeymanPaths, + + httpuploader, + Keyman.System.UpdateCheckResponse, + UfrmDownloadProgress, + OnlineUpdateCheck; + +type + ERemoteUpdateCheck = class(Exception); + + TRemoteUpdateCheckResult = (wucUnknown, wucSuccess, wucNoUpdates, wucFailure, wucOffline); + + TRemoteUpdateCheckParams = record + Keyman: TOnlineUpdateCheckParamsKeyman; + Packages: array of TOnlineUpdateCheckParamsPackage; + Result: TRemoteUpdateCheckResult; + end; + + TRemoteUpdateCheckDownloadParams = record + TotalSize: Integer; + TotalDownloads: Integer; + StartPosition: Integer; + end; + + TRemoteUpdateCheck = class + private + FForce: Boolean; + FParams: TRemoteUpdateCheckParams; + + FErrorMessage: string; + + FShowErrors: Boolean; + FDownload: TRemoteUpdateCheckDownloadParams; + FCheckOnly: Boolean; + + function DownloadUpdates: Boolean; + procedure DoDownloadUpdates(SavePath: string; var Result: Boolean); + function DoRun: TRemoteUpdateCheckResult; + public + function ResponseToParams(const ucr: TUpdateCheckResponse): TRemoteUpdateCheckParams; + + constructor Create(AForce : Boolean; ACheckOnly: Boolean = False); + destructor Destroy; override; + function Run: TRemoteUpdateCheckResult; + property ShowErrors: Boolean read FShowErrors write FShowErrors; + end; + +procedure LogMessage(LogMessage: string); + +implementation + +uses + System.WideStrUtils, + Vcl.Dialogs, + Winapi.ShellApi, + Winapi.Windows, + Winapi.WinINet, + + GlobalProxySettings, + KLog, + keymanapi_TLB, + KeymanVersion, + Keyman.System.UpdateCheckStorage, + kmint, + ErrorControlledRegistry, + RegistryKeys, + Upload_Settings, + utildir, + utilexecute, + OnlineUpdateCheckMessages, + UfrmOnlineUpdateIcon, + UfrmOnlineUpdateNewVersion, + utilkmshell, + utilsystem, + utiluac, + versioninfo; + +{ TRemoteUpdateCheck } + +constructor TRemoteUpdateCheck.Create(AForce, ACheckOnly: Boolean); +begin + inherited Create; + + FShowErrors := True; + FParams.Result := wucUnknown; + + FForce := AForce; + FCheckOnly := ACheckOnly; + + KL.Log('TRemoteUpdateCheck.Create'); +end; + +destructor TRemoteUpdateCheck.Destroy; +begin + if (FErrorMessage <> '') and FShowErrors then + LogMessage(FErrorMessage); + + KL.Log('TRemoteUpdateCheck.Destroy: FErrorMessage = '+FErrorMessage); + KL.Log('TRemoteUpdateCheck.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result))); + + inherited Destroy; +end; + +function TRemoteUpdateCheck.Run: TRemoteUpdateCheckResult; +begin + Result := DoRun; + + if Result in [ wucSuccess] then + begin + kmcom.Keyboards.Refresh; + kmcom.Keyboards.Apply; + kmcom.Packages.Refresh; + end; + + FParams.Result := Result; +end; + + +procedure TRemoteUpdateCheck.DoDownloadUpdates(SavePath: string; var Result: Boolean); +var + i, downloadCount: Integer; + + function DownloadFile(const url, savepath: string): Boolean; + begin + Result := False; + with THttpUploader.Create(nil) do + try + Proxy.Server := GetProxySettings.Server; + Proxy.Port := GetProxySettings.Port; + Proxy.Username := GetProxySettings.Username; + Proxy.Password := GetProxySettings.Password; + Request.Agent := API_UserAgent; + + Request.SetURL(url); + Upload; + if Response.StatusCode = 200 then + begin + with TFileStream.Create(savepath, fmCreate) do + try + Write(Response.PMessageBody^, Response.MessageBodyLength); + finally + Free; + end; + Result := True; + end + else // I2742 + // If it fails we set to false but will try the other files + Result := False; + Exit; + finally + Free; + end; + end; + + +begin + Result := False; + try + FDownload.TotalSize := 0; + FDownload.TotalDownloads := 0; + downloadCount := 0; + + for i := 0 to High(FParams.Packages) do + if FParams.Packages[i].Install then + begin + Inc(FDownload.TotalDownloads); + Inc(FDownload.TotalSize, FParams.Packages[i].DownloadSize); + + FParams.Packages[i].SavePath := SavePath + FParams.Packages[i].FileName; + end; + + if FParams.Keyman.Install then + begin + Inc(FDownload.TotalDownloads); + Inc(FDownload.TotalSize, FParams.Keyman.DownloadSize); + FParams.Keyman.SavePath := SavePath + FParams.Keyman.FileName; + end; + + FDownload.StartPosition := 0; + for i := 0 to High(FParams.Packages) do + if FParams.Packages[i].Install then + begin + if not DownloadFile(FParams.Packages[i].DownloadURL, FParams.Packages[i].SavePath) then // I2742 + begin + FParams.Packages[i].Install := False; // Download failed but install other files + end + else + Inc(downloadCount); + FDownload.StartPosition := FDownload.StartPosition + FParams.Packages[i].DownloadSize; + end; + + if FParams.Keyman.Install then + if not DownloadFile(FParams.Keyman.DownloadURL, FParams.Keyman.SavePath) then // I2742 + begin + FParams.Keyman.Install := False; // Download failed but user wants to install other files + end; + + // There needs to be at least one file successfully downloaded to return + // TRUE that files where downloaded + if downloadCount > 0 then + Result := True; + except + on E:EHTTPUploader do + begin + if (E.ErrorCode = 12007) or (E.ErrorCode = 12029) + then LogMessage(S_OnlineUpdate_UnableToContact) + else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message])); + Result := False; + end; + end; +end; + +function TRemoteUpdateCheck.DownloadUpdates: Boolean; +var + i: Integer; + DownloadBackGroundSavePath : String; + DownloadResult : Boolean; +begin + DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath); + { For now lets download all the updates. Set all as true } + + if FParams.Keyman.DownloadURL <> '' then + FParams.Keyman.Install := True; + + for i := 0 to High(FParams.Packages) do + FParams.Packages[i].Install := True; + + DoDownloadUpdates(DownloadBackGroundSavePath, DownloadResult); + KL.Log('TRemoteUpdateCheck.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult))); + Result := DownloadResult; + +end; + +function TRemoteUpdateCheck.DoRun: TRemoteUpdateCheckResult; +var + flags: DWord; + i: Integer; + ucr: TUpdateCheckResponse; + pkg: IKeymanPackage; + downloadResult: boolean; +begin + {FProxyHost := ''; + FProxyPort := 0;} + + { Check if user is currently online } + if not InternetGetConnectedState(@flags, 0) then + begin + Result := wucOffline; + Exit; + end; + + { Verify that it has been at least 7 days since last update check } + try + with TRegistryErrorControlled.Create do // I2890 + try + if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then + begin + if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) and not FForce then + begin + Result := wucNoUpdates; + Exit; + end; + if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 7) and not FForce then + begin + Result := wucNoUpdates; + // TODO: This exit is just to remove the time check for testing. + //Exit; + end; + + {if ValueExists(SRegValue_UpdateCheck_UseProxy) and ReadBool(SRegValue_UpdateCheck_UseProxy) then + begin + FProxyHost := ReadString(SRegValue_UpdateCheck_ProxyHost); + FProxyPort := StrToIntDef(ReadString(SRegValue_UpdateCheck_ProxyPort), 80); + end;} + end; + finally + Free; + end; + except + { we will not run the check if an error occurs reading the settings } + on E:Exception do + begin + Result := wucFailure; + FErrorMessage := E.Message; + Exit; + end; + end; + + Result := wucNoUpdates; + + try + with THTTPUploader.Create(nil) do + try + Fields.Add('version', ansistring(CKeymanVersionInfo.Version)); + Fields.Add('tier', ansistring(CKeymanVersionInfo.Tier)); + if FForce + then Fields.Add('manual', '1') + else Fields.Add('manual', '0'); + + for i := 0 to kmcom.Packages.Count - 1 do + begin + pkg := kmcom.Packages[i]; + + // Due to limitations in PHP parsing of query string parameters names with + // space or period, we need to split the parameters up. The legacy pattern + // is still supported on the server side. Relates to #4886. + Fields.Add(AnsiString('packageid_'+IntToStr(i)), AnsiString(pkg.ID)); + Fields.Add(AnsiString('packageversion_'+IntToStr(i)), AnsiString(pkg.Version)); + pkg := nil; + end; + + Proxy.Server := GetProxySettings.Server; + Proxy.Port := GetProxySettings.Port; + Proxy.Username := GetProxySettings.Username; + Proxy.Password := GetProxySettings.Password; + + Request.HostName := API_Server; + Request.Protocol := API_Protocol; + Request.UrlPath := API_Path_UpdateCheck_Windows; + //OnStatus := + Upload; + if Response.StatusCode = 200 then + begin + if ucr.Parse(Response.MessageBodyAsString, 'bundle', CKeymanVersionInfo.Version) then + begin + ResponseToParams(ucr); + + if FCheckOnly then + begin + // TODO: Refactor this + TUpdateCheckStorage.SaveUpdateCacheData(ucr); + Result := FParams.Result; + end + else if (Length(FParams.Packages) > 0) or (FParams.Keyman.DownloadURL <> '') then + begin + // TODO: Integrate in to the Background update state machine. + // for now just go straight to DownloadUpdates + downloadResult := DownloadUpdates; + if DownloadResult then + begin + Result := wucSuccess; + end; + end; + end + else + begin + FErrorMessage := ucr.ErrorMessage; + Result := wucFailure; + end; + end + else + raise ERemoteUpdateCheck.Create('Error '+IntToStr(Response.StatusCode)); + finally + Free; + end; + except + on E:EHTTPUploader do + begin + if (E.ErrorCode = 12007) or (E.ErrorCode = 12029) + then FErrorMessage := S_OnlineUpdate_UnableToContact + else FErrorMessage := WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message]); + Result := wucFailure; + end; + on E:Exception do + begin + FErrorMessage := E.Message; + Result := wucFailure; + end; + end; + + with TRegistryErrorControlled.Create do // I2890 + try + if OpenKey(SRegKey_KeymanDesktop_CU, True) then + WriteDateTime(SRegValue_LastUpdateCheckTime, Now); + finally + Free; + end; +end; + +function TRemoteUpdateCheck.ResponseToParams(const ucr: TUpdateCheckResponse): TRemoteUpdateCheckParams; +var + i, j, n: Integer; + pkg: IKeymanPackage; +begin + SetLength(FParams.Packages,0); + for i := Low(ucr.Packages) to High(ucr.Packages) do + begin + n := kmcom.Packages.IndexOf(ucr.Packages[i].ID); + if n >= 0 then + begin + pkg := kmcom.Packages[n]; + j := Length(FParams.Packages); + SetLength(FParams.Packages, j+1); + FParams.Packages[j].NewID := ucr.Packages[i].NewID; + FParams.Packages[j].ID := ucr.Packages[i].ID; + FParams.Packages[j].Description := ucr.Packages[i].Name; + FParams.Packages[j].OldVersion := pkg.Version; + FParams.Packages[j].NewVersion := ucr.Packages[i].NewVersion; + FParams.Packages[j].DownloadSize := ucr.Packages[i].DownloadSize; + FParams.Packages[j].DownloadURL := ucr.Packages[i].DownloadURL; + FParams.Packages[j].FileName := ucr.Packages[i].FileName; + pkg := nil; + end + else + FErrorMessage := 'Unable to find package '+ucr.Packages[i].ID; + end; + + case ucr.Status of + ucrsNoUpdate: + begin + FErrorMessage := ucr.ErrorMessage; + end; + ucrsUpdateReady: + begin + FParams.Keyman.OldVersion := ucr.CurrentVersion; + FParams.Keyman.NewVersion := ucr.NewVersion; + FParams.Keyman.DownloadURL := ucr.InstallURL; + FParams.Keyman.DownloadSize := ucr.InstallSize; + FParams.Keyman.FileName := ucr.FileName; + end; + end; + + Result := FParams; +end; + + // temp wrapper for converting showmessage to logs don't know where + // if nt using klog + procedure LogMessage(LogMessage: string); + begin + KL.Log(LogMessage); + end; + +end. diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas index c22591e79c..29fe102d21 100644 --- a/windows/src/desktop/kmshell/main/initprog.pas +++ b/windows/src/desktop/kmshell/main/initprog.pas @@ -82,6 +82,7 @@ type fmMigrate, fmSplash, fmStart, fmUpgradeKeyboards, fmOnlineUpdateCheck,// I2548 fmOnlineUpdateAdmin, fmTextEditor, + fmBackgroundUpdateCheck, fmFirstRun, // I2562 fmKeyboardWelcome, // I2569 fmKeyboardPrint, // I2329 @@ -120,6 +121,7 @@ uses KMShellHints, KeymanMutex, OnlineUpdateCheck, + RemoteUpdateCheck, RegistryKeys, UfrmBaseKeyboard, UfrmKeymanBase, @@ -249,6 +251,7 @@ begin else if s = '-h' then FMode := fmHelp else if s = '-t' then FMode := fmTextEditor else if s = '-ouc' then FMode := fmOnlineUpdateCheck + else if s = '-buc' then FMode := fmBackgroundUpdateCheck else if s = '-basekeyboard' then FMode := fmBaseKeyboard // I4169 else if s = '-nowelcome' then FNoWelcome := True else if s = '-kw' then FMode := fmKeyboardWelcome // I2569 @@ -427,6 +430,19 @@ begin ShowMessage(MsgFromId(SKOSNotSupported)); Exit; end; + // TODO: #10038 Will add this as part of the background update state machine + // for now just verifing the download happens via -buc switch. + with TRemoteUpdateCheck.Create(False, False) do + try + if (FMode = fmBackgroundUpdateCheck) then + begin + Run; + Exit; + end + finally + Free; + end; + if not FSilent or (FMode = fmUpgradeMnemonicLayout) then // I4553 begin From e1c2b6aa79f9e8a72f045eb99fc496f9377d0028 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Thu, 7 Dec 2023 13:20:21 +1000 Subject: [PATCH 05/11] feat(windows): remove UI from uses list rename module --- windows/src/desktop/kmshell/kmshell.dpr | 2 +- windows/src/desktop/kmshell/kmshell.dproj | 14 ++++---- ...as => Keyman.System.RemoteUpdateCheck.pas} | 34 ++++++++----------- windows/src/desktop/kmshell/main/initprog.pas | 2 +- 4 files changed, 24 insertions(+), 28 deletions(-) rename windows/src/desktop/kmshell/main/{RemoteUpdateCheck.pas => Keyman.System.RemoteUpdateCheck.pas} (96%) diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr index 2b1e14b517..71e434af91 100644 --- a/windows/src/desktop/kmshell/kmshell.dpr +++ b/windows/src/desktop/kmshell/kmshell.dpr @@ -179,7 +179,7 @@ uses Keyman.System.AndroidStringToKeymanLocaleString in '..\..\..\..\common\windows\delphi\general\Keyman.System.AndroidStringToKeymanLocaleString.pas', UpdateXMLRenderer in 'render\UpdateXMLRenderer.pas', Keyman.System.UpdateCheckStorage in 'main\Keyman.System.UpdateCheckStorage.pas', - RemoteUpdateCheck in 'main\RemoteUpdateCheck.pas'; + Keyman.System.RemoteUpdateCheck in 'main\Keyman.System.RemoteUpdateCheck.pas'; {$R VERSION.RES} {$R manifest.res} diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj index 2e6d27e6cd..16f49e757d 100644 --- a/windows/src/desktop/kmshell/kmshell.dproj +++ b/windows/src/desktop/kmshell/kmshell.dproj @@ -355,7 +355,7 @@ - + Cfg_2 @@ -417,12 +417,6 @@ False - - - kmshell.rsm - true - - kmshell.exe @@ -435,6 +429,12 @@ true + + + kmshell.rsm + true + + 1 diff --git a/windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas similarity index 96% rename from windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas rename to windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas index 75c57315a9..310eca36f4 100644 --- a/windows/src/desktop/kmshell/main/RemoteUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas @@ -15,21 +15,18 @@ Notes: History: *) -unit RemoteUpdateCheck; // I3306 +unit Keyman.System.RemoteUpdateCheck; // I3306 interface uses System.Classes, System.SysUtils, - System.UITypes, - System.IOUtils, - Vcl.Forms, + //System.UITypes, + //System.IOUtils, KeymanPaths, - httpuploader, Keyman.System.UpdateCheckResponse, - UfrmDownloadProgress, OnlineUpdateCheck; type @@ -78,8 +75,7 @@ implementation uses System.WideStrUtils, - Vcl.Dialogs, - Winapi.ShellApi, + //Winapi.ShellApi, Winapi.Windows, Winapi.WinINet, @@ -92,15 +88,13 @@ uses ErrorControlledRegistry, RegistryKeys, Upload_Settings, - utildir, - utilexecute, - OnlineUpdateCheckMessages, - UfrmOnlineUpdateIcon, - UfrmOnlineUpdateNewVersion, - utilkmshell, - utilsystem, - utiluac, - versioninfo; + //utildir, + //utilexecute, + OnlineUpdateCheckMessages; + //utilkmshell, + // utilsystem, + //utiluac, + //versioninfo; { TRemoteUpdateCheck } @@ -358,10 +352,12 @@ begin TUpdateCheckStorage.SaveUpdateCacheData(ucr); Result := FParams.Result; end + // TODO: #10038 + // Integerate into state machine. in the download state + // the process can call LoadUpdateCacheData if needed to get the + // response result. else if (Length(FParams.Packages) > 0) or (FParams.Keyman.DownloadURL <> '') then begin - // TODO: Integrate in to the Background update state machine. - // for now just go straight to DownloadUpdates downloadResult := DownloadUpdates; if DownloadResult then begin diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas index 29fe102d21..5affe65caf 100644 --- a/windows/src/desktop/kmshell/main/initprog.pas +++ b/windows/src/desktop/kmshell/main/initprog.pas @@ -121,7 +121,7 @@ uses KMShellHints, KeymanMutex, OnlineUpdateCheck, - RemoteUpdateCheck, + Keyman.System.RemoteUpdateCheck, RegistryKeys, UfrmBaseKeyboard, UfrmKeymanBase, From 78f75aded977ce5dd889af5ced88361e41a83ea4 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Fri, 8 Dec 2023 13:25:45 +1000 Subject: [PATCH 06/11] feat(windows): remove TRemoteUpdateCheckParams Just using the TUpdateCheckResponse gives us enough detail for now as we no longer have a UI set which packages to install --- .../main/Keyman.System.RemoteUpdateCheck.pas | 140 +++++------------- 1 file changed, 35 insertions(+), 105 deletions(-) diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas index 310eca36f4..fc40f584cd 100644 --- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas @@ -22,8 +22,6 @@ interface uses System.Classes, System.SysUtils, - //System.UITypes, - //System.IOUtils, KeymanPaths, httpuploader, Keyman.System.UpdateCheckResponse, @@ -34,12 +32,6 @@ type TRemoteUpdateCheckResult = (wucUnknown, wucSuccess, wucNoUpdates, wucFailure, wucOffline); - TRemoteUpdateCheckParams = record - Keyman: TOnlineUpdateCheckParamsKeyman; - Packages: array of TOnlineUpdateCheckParamsPackage; - Result: TRemoteUpdateCheckResult; - end; - TRemoteUpdateCheckDownloadParams = record TotalSize: Integer; TotalDownloads: Integer; @@ -49,7 +41,7 @@ type TRemoteUpdateCheck = class private FForce: Boolean; - FParams: TRemoteUpdateCheckParams; + FRemoteResult: TRemoteUpdateCheckResult; FErrorMessage: string; @@ -57,11 +49,10 @@ type FDownload: TRemoteUpdateCheckDownloadParams; FCheckOnly: Boolean; - function DownloadUpdates: Boolean; - procedure DoDownloadUpdates(SavePath: string; var Result: Boolean); + function DownloadUpdates(Params: TUpdateCheckResponse) : Boolean; + procedure DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean); function DoRun: TRemoteUpdateCheckResult; public - function ResponseToParams(const ucr: TUpdateCheckResponse): TRemoteUpdateCheckParams; constructor Create(AForce : Boolean; ACheckOnly: Boolean = False); destructor Destroy; override; @@ -75,7 +66,6 @@ implementation uses System.WideStrUtils, - //Winapi.ShellApi, Winapi.Windows, Winapi.WinINet, @@ -88,13 +78,8 @@ uses ErrorControlledRegistry, RegistryKeys, Upload_Settings, - //utildir, - //utilexecute, + OnlineUpdateCheckMessages; - //utilkmshell, - // utilsystem, - //utiluac, - //versioninfo; { TRemoteUpdateCheck } @@ -103,7 +88,7 @@ begin inherited Create; FShowErrors := True; - FParams.Result := wucUnknown; + FRemoteResult := wucUnknown; FForce := AForce; FCheckOnly := ACheckOnly; @@ -117,7 +102,7 @@ begin LogMessage(FErrorMessage); KL.Log('TRemoteUpdateCheck.Destroy: FErrorMessage = '+FErrorMessage); - KL.Log('TRemoteUpdateCheck.Destroy: FParams.Result = '+IntToStr(Ord(FParams.Result))); + KL.Log('TRemoteUpdateCheck.Destroy: FRemoteResult = '+IntToStr(Ord(FRemoteResult))); inherited Destroy; end; @@ -133,17 +118,16 @@ begin kmcom.Packages.Refresh; end; - FParams.Result := Result; + FRemoteResult := Result; end; -procedure TRemoteUpdateCheck.DoDownloadUpdates(SavePath: string; var Result: Boolean); +procedure TRemoteUpdateCheck.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean); var i, downloadCount: Integer; function DownloadFile(const url, savepath: string): Boolean; begin - Result := False; with THttpUploader.Create(nil) do try Proxy.Server := GetProxySettings.Server; @@ -181,40 +165,40 @@ begin FDownload.TotalDownloads := 0; downloadCount := 0; - for i := 0 to High(FParams.Packages) do - if FParams.Packages[i].Install then + // Keyboard Packages + for i := 0 to High(Params.Packages) do begin Inc(FDownload.TotalDownloads); - Inc(FDownload.TotalSize, FParams.Packages[i].DownloadSize); - - FParams.Packages[i].SavePath := SavePath + FParams.Packages[i].FileName; + Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize); + Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName; end; - if FParams.Keyman.Install then - begin - Inc(FDownload.TotalDownloads); - Inc(FDownload.TotalSize, FParams.Keyman.DownloadSize); - FParams.Keyman.SavePath := SavePath + FParams.Keyman.FileName; - end; + // Add the Keyman installer + Inc(FDownload.TotalDownloads); + Inc(FDownload.TotalSize, Params.InstallSize); + // Keyboard Packages FDownload.StartPosition := 0; - for i := 0 to High(FParams.Packages) do - if FParams.Packages[i].Install then + for i := 0 to High(Params.Packages) do begin - if not DownloadFile(FParams.Packages[i].DownloadURL, FParams.Packages[i].SavePath) then // I2742 + if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742 begin - FParams.Packages[i].Install := False; // Download failed but install other files + Params.Packages[i].Install := False; // Download failed but install other files end else Inc(downloadCount); - FDownload.StartPosition := FDownload.StartPosition + FParams.Packages[i].DownloadSize; + FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize; end; - if FParams.Keyman.Install then - if not DownloadFile(FParams.Keyman.DownloadURL, FParams.Keyman.SavePath) then // I2742 - begin - FParams.Keyman.Install := False; // Download failed but user wants to install other files - end; + // Keyamn Installer + if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742 + begin + // TODO record fail? and log // Download failed but user wants to install other files + end + else + begin + Inc(downloadCount) + end; // There needs to be at least one file successfully downloaded to return // TRUE that files where downloaded @@ -231,22 +215,14 @@ begin end; end; -function TRemoteUpdateCheck.DownloadUpdates: Boolean; +function TRemoteUpdateCheck.DownloadUpdates(Params: TUpdateCheckResponse): Boolean; var - i: Integer; DownloadBackGroundSavePath : String; DownloadResult : Boolean; begin DownloadBackGroundSavePath := IncludeTrailingPathDelimiter(TKeymanPaths.KeymanUpdateCachePath); - { For now lets download all the updates. Set all as true } - if FParams.Keyman.DownloadURL <> '' then - FParams.Keyman.Install := True; - - for i := 0 to High(FParams.Packages) do - FParams.Packages[i].Install := True; - - DoDownloadUpdates(DownloadBackGroundSavePath, DownloadResult); + DoDownloadUpdates(DownloadBackGroundSavePath, Params, DownloadResult); KL.Log('TRemoteUpdateCheck.DownloadUpdatesBackground: DownloadResult = '+IntToStr(Ord(DownloadResult))); Result := DownloadResult; @@ -344,21 +320,21 @@ begin begin if ucr.Parse(Response.MessageBodyAsString, 'bundle', CKeymanVersionInfo.Version) then begin - ResponseToParams(ucr); + //ResponseToParams(ucr); if FCheckOnly then begin // TODO: Refactor this TUpdateCheckStorage.SaveUpdateCacheData(ucr); - Result := FParams.Result; + Result := FRemoteResult; end // TODO: #10038 // Integerate into state machine. in the download state // the process can call LoadUpdateCacheData if needed to get the // response result. - else if (Length(FParams.Packages) > 0) or (FParams.Keyman.DownloadURL <> '') then + else if (Length(ucr.Packages) > 0) or (ucr.InstallURL <> '') then begin - downloadResult := DownloadUpdates; + downloadResult := DownloadUpdates(ucr); if DownloadResult then begin Result := wucSuccess; @@ -400,52 +376,6 @@ begin end; end; -function TRemoteUpdateCheck.ResponseToParams(const ucr: TUpdateCheckResponse): TRemoteUpdateCheckParams; -var - i, j, n: Integer; - pkg: IKeymanPackage; -begin - SetLength(FParams.Packages,0); - for i := Low(ucr.Packages) to High(ucr.Packages) do - begin - n := kmcom.Packages.IndexOf(ucr.Packages[i].ID); - if n >= 0 then - begin - pkg := kmcom.Packages[n]; - j := Length(FParams.Packages); - SetLength(FParams.Packages, j+1); - FParams.Packages[j].NewID := ucr.Packages[i].NewID; - FParams.Packages[j].ID := ucr.Packages[i].ID; - FParams.Packages[j].Description := ucr.Packages[i].Name; - FParams.Packages[j].OldVersion := pkg.Version; - FParams.Packages[j].NewVersion := ucr.Packages[i].NewVersion; - FParams.Packages[j].DownloadSize := ucr.Packages[i].DownloadSize; - FParams.Packages[j].DownloadURL := ucr.Packages[i].DownloadURL; - FParams.Packages[j].FileName := ucr.Packages[i].FileName; - pkg := nil; - end - else - FErrorMessage := 'Unable to find package '+ucr.Packages[i].ID; - end; - - case ucr.Status of - ucrsNoUpdate: - begin - FErrorMessage := ucr.ErrorMessage; - end; - ucrsUpdateReady: - begin - FParams.Keyman.OldVersion := ucr.CurrentVersion; - FParams.Keyman.NewVersion := ucr.NewVersion; - FParams.Keyman.DownloadURL := ucr.InstallURL; - FParams.Keyman.DownloadSize := ucr.InstallSize; - FParams.Keyman.FileName := ucr.FileName; - end; - end; - - Result := FParams; -end; - // temp wrapper for converting showmessage to logs don't know where // if nt using klog procedure LogMessage(LogMessage: string); From ed762c07c97c93505ae3c4a2f7aeab924106e7b4 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:06:11 +1000 Subject: [PATCH 07/11] feat(windows): update XML render to use ucr --- .../kmshell/render/UpdateXMLRenderer.pas | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas b/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas index 0c102fdca7..32fd7b2cf6 100644 --- a/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas +++ b/windows/src/desktop/kmshell/render/UpdateXMLRenderer.pas @@ -30,7 +30,6 @@ uses Keyman.System.UpdateCheckStorage, MessageIdentifierConsts, MessageIdentifiers, - OnlineUpdateCheck, utilxml; { TUpdateXMLRenderer } @@ -39,48 +38,51 @@ function TUpdateXMLRenderer.XMLData: WideString; var xml: string; ucr: TUpdateCheckResponse; - ouc: TOnlineUpdateCheck; - params: TOnlineUpdateCheckParams; - i: Integer; + i, n : Integer; + pkg: IKeymanPackage; begin xml := ''; if TUpdateCheckStorage.LoadUpdateCacheData(ucr) then begin - ouc := TOnlineUpdateCheck.Create(nil, False, True); - params := ouc.ResponseToParams(ucr); - - if (Params.Keyman.DownloadURL <> '') then + if (ucr.InstallURL <> '') then begin xml := xml + ''+ '0'+ IfThen(not kmcom.SystemInfo.IsAdministrator, '')+ ''+ - ''+xmlencode(TLocaleStrings.MsgFromIdFormat(kmcom, SKUpdate_KeymanText, [Params.Keyman.NewVersion]))+''+ - ''+xmlencode(Params.Keyman.NewVersion)+''+ - ''+xmlencode(Params.Keyman.OldVersion)+''+ - ''+xmlencode(Format('%d', [Params.Keyman.DownloadSize div 1024]))+'KB'+ - ''+xmlencode(Params.Keyman.DownloadURL)+''+ + ''+xmlencode(TLocaleStrings.MsgFromIdFormat(kmcom, SKUpdate_KeymanText, [ucr.NewVersion]))+''+ + ''+xmlencode(ucr.NewVersion)+''+ + ''+xmlencode(ucr.CurrentVersion)+''+ + ''+xmlencode(Format('%d', [ucr.InstallSize div 1024]))+'KB'+ + ''+xmlencode(ucr.InstallURL)+''+ ''+ ''; end; - for i := 0 to High(Params.Packages) do + for i := 0 to High(ucr.Packages) do begin - xml := xml + - ''+ - ''+IntToStr(i+1)+''+ - IfThen(not kmcom.SystemInfo.IsAdministrator, '')+ - ''+ - ''+xmlencode(TLocaleStrings.MsgFromIdFormat(kmcom, SKUpdate_PackageText, - [Params.Packages[i].Description, Params.Packages[i].NewVersion]))+''+ - ''+xmlencode(Params.Packages[i].NewVersion)+''+ - ''+xmlencode(Params.Packages[i].OldVersion)+''+ - ''+xmlencode(Format('%d', [Params.Packages[i].DownloadSize div 1024]))+'KB'+ - ''+xmlencode(Params.Packages[i].DownloadURL)+''+ - ''+ - ''; + n := kmcom.Packages.IndexOf(ucr.Packages[i].ID); + if n >= 0 then + pkg := kmcom.Packages[n]; + begin + xml := xml + + ''+ + ''+IntToStr(i+1)+''+ + IfThen(not kmcom.SystemInfo.IsAdministrator, '')+ + ''+ + ''+xmlencode(TLocaleStrings.MsgFromIdFormat(kmcom, SKUpdate_PackageText, + [ucr.Packages[i].Name, ucr.Packages[i].NewVersion]))+''+ + ''+xmlencode(ucr.Packages[i].NewVersion)+''+ + ''+xmlencode(pkg.version)+''+ + ''+xmlencode(Format('%d', [ucr.Packages[i].DownloadSize div 1024]))+'KB'+ + ''+xmlencode(ucr.Packages[i].DownloadURL)+''+ + ''+ + ''; + pkg := nil; + end; + // else Package not found, skip end; end; From a945c08d7361fb1c7c82480480c9541e8665af00 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:23:39 +1000 Subject: [PATCH 08/11] feat(windows): address review comments Co-authored-by: Marc Durdin --- .../main/Keyman.System.RemoteUpdateCheck.pas | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas index fc40f584cd..418f720674 100644 --- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas @@ -140,11 +140,11 @@ var Upload; if Response.StatusCode = 200 then begin - with TFileStream.Create(savepath, fmCreate) do + fs := TFileStream.Create(savepath, fmCreate); try - Write(Response.PMessageBody^, Response.MessageBodyLength); + fs.Write(Response.PMessageBody^, Response.MessageBodyLength); finally - Free; + fs.Free; end; Result := True; end @@ -167,11 +167,11 @@ begin // Keyboard Packages for i := 0 to High(Params.Packages) do - begin - Inc(FDownload.TotalDownloads); - Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize); - Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName; - end; + begin + Inc(FDownload.TotalDownloads); + Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize); + Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName; + end; // Add the Keyman installer Inc(FDownload.TotalDownloads); @@ -190,7 +190,7 @@ begin FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize; end; - // Keyamn Installer + // Keyman Installer if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742 begin // TODO record fail? and log // Download failed but user wants to install other files @@ -201,8 +201,8 @@ begin end; // There needs to be at least one file successfully downloaded to return - // TRUE that files where downloaded - if downloadCount > 0 then + // True that files were downloaded + if downloadCount > 0 then Result := True; except on E:EHTTPUploader do From 1ea9bd71a5df2deafcb1ef5424871961cac5fa51 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Wed, 13 Dec 2023 09:35:43 +1000 Subject: [PATCH 09/11] feat(windows): Remove the with pattern --- .../main/Keyman.System.RemoteUpdateCheck.pas | 82 ++++++++++--------- windows/src/desktop/kmshell/main/initprog.pas | 7 +- 2 files changed, 47 insertions(+), 42 deletions(-) diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas index 418f720674..077e4b42de 100644 --- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas @@ -125,24 +125,26 @@ end; procedure TRemoteUpdateCheck.DoDownloadUpdates(SavePath: string; Params: TUpdateCheckResponse; var Result: Boolean); var i, downloadCount: Integer; + http: THttpUploader; + fs: TFileStream; function DownloadFile(const url, savepath: string): Boolean; begin - with THttpUploader.Create(nil) do + http := THttpUploader.Create(nil); try - Proxy.Server := GetProxySettings.Server; - Proxy.Port := GetProxySettings.Port; - Proxy.Username := GetProxySettings.Username; - Proxy.Password := GetProxySettings.Password; - Request.Agent := API_UserAgent; + http.Proxy.Server := GetProxySettings.Server; + http.Proxy.Port := GetProxySettings.Port; + http.Proxy.Username := GetProxySettings.Username; + http.Proxy.Password := GetProxySettings.Password; + http.Request.Agent := API_UserAgent; - Request.SetURL(url); - Upload; - if Response.StatusCode = 200 then + http.Request.SetURL(url); + http.Upload; + if http.Response.StatusCode = 200 then begin fs := TFileStream.Create(savepath, fmCreate); try - fs.Write(Response.PMessageBody^, Response.MessageBodyLength); + fs.Write(http.Response.PMessageBody^, http.Response.MessageBodyLength); finally fs.Free; end; @@ -153,7 +155,7 @@ var Result := False; Exit; finally - Free; + http.Free; end; end; @@ -235,6 +237,8 @@ var ucr: TUpdateCheckResponse; pkg: IKeymanPackage; downloadResult: boolean; + registry: TRegistryErrorControlled; + http: THttpUploader; begin {FProxyHost := ''; FProxyPort := 0;} @@ -248,16 +252,16 @@ begin { Verify that it has been at least 7 days since last update check } try - with TRegistryErrorControlled.Create do // I2890 + registry := TRegistryErrorControlled.Create; // I2890 try - if OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then + if registry.OpenKeyReadOnly(SRegKey_KeymanDesktop_CU) then begin - if ValueExists(SRegValue_CheckForUpdates) and not ReadBool(SRegValue_CheckForUpdates) and not FForce then + if registry.ValueExists(SRegValue_CheckForUpdates) and not registry.ReadBool(SRegValue_CheckForUpdates) and not FForce then begin Result := wucNoUpdates; Exit; end; - if ValueExists(SRegValue_LastUpdateCheckTime) and (Now - ReadDateTime(SRegValue_LastUpdateCheckTime) < 7) and not FForce then + if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) < 7) and not FForce then begin Result := wucNoUpdates; // TODO: This exit is just to remove the time check for testing. @@ -271,7 +275,7 @@ begin end;} end; finally - Free; + registry.Free; end; except { we will not run the check if an error occurs reading the settings } @@ -286,13 +290,13 @@ begin Result := wucNoUpdates; try - with THTTPUploader.Create(nil) do + http := THTTPUploader.Create(nil); try - Fields.Add('version', ansistring(CKeymanVersionInfo.Version)); - Fields.Add('tier', ansistring(CKeymanVersionInfo.Tier)); + http.Fields.Add('version', ansistring(CKeymanVersionInfo.Version)); + http.Fields.Add('tier', ansistring(CKeymanVersionInfo.Tier)); if FForce - then Fields.Add('manual', '1') - else Fields.Add('manual', '0'); + then http.Fields.Add('manual', '1') + else http.Fields.Add('manual', '0'); for i := 0 to kmcom.Packages.Count - 1 do begin @@ -301,24 +305,24 @@ begin // Due to limitations in PHP parsing of query string parameters names with // space or period, we need to split the parameters up. The legacy pattern // is still supported on the server side. Relates to #4886. - Fields.Add(AnsiString('packageid_'+IntToStr(i)), AnsiString(pkg.ID)); - Fields.Add(AnsiString('packageversion_'+IntToStr(i)), AnsiString(pkg.Version)); + http.Fields.Add(AnsiString('packageid_'+IntToStr(i)), AnsiString(pkg.ID)); + http.Fields.Add(AnsiString('packageversion_'+IntToStr(i)), AnsiString(pkg.Version)); pkg := nil; end; - Proxy.Server := GetProxySettings.Server; - Proxy.Port := GetProxySettings.Port; - Proxy.Username := GetProxySettings.Username; - Proxy.Password := GetProxySettings.Password; + http.Proxy.Server := GetProxySettings.Server; + http.Proxy.Port := GetProxySettings.Port; + http.Proxy.Username := GetProxySettings.Username; + http.Proxy.Password := GetProxySettings.Password; - Request.HostName := API_Server; - Request.Protocol := API_Protocol; - Request.UrlPath := API_Path_UpdateCheck_Windows; + http.Request.HostName := API_Server; + http.Request.Protocol := API_Protocol; + http.Request.UrlPath := API_Path_UpdateCheck_Windows; //OnStatus := - Upload; - if Response.StatusCode = 200 then + http.Upload; + if http.Response.StatusCode = 200 then begin - if ucr.Parse(Response.MessageBodyAsString, 'bundle', CKeymanVersionInfo.Version) then + if ucr.Parse(http.Response.MessageBodyAsString, 'bundle', CKeymanVersionInfo.Version) then begin //ResponseToParams(ucr); @@ -348,9 +352,9 @@ begin end; end else - raise ERemoteUpdateCheck.Create('Error '+IntToStr(Response.StatusCode)); + raise ERemoteUpdateCheck.Create('Error '+IntToStr(http.Response.StatusCode)); finally - Free; + http.Free; end; except on E:EHTTPUploader do @@ -367,12 +371,12 @@ begin end; end; - with TRegistryErrorControlled.Create do // I2890 + registry := TRegistryErrorControlled.Create; // I2890 try - if OpenKey(SRegKey_KeymanDesktop_CU, True) then - WriteDateTime(SRegValue_LastUpdateCheckTime, Now); + if registry.OpenKey(SRegKey_KeymanDesktop_CU, True) then + registry.WriteDateTime(SRegValue_LastUpdateCheckTime, Now); finally - Free; + registry.Free; end; end; diff --git a/windows/src/desktop/kmshell/main/initprog.pas b/windows/src/desktop/kmshell/main/initprog.pas index 5affe65caf..4076ab2101 100644 --- a/windows/src/desktop/kmshell/main/initprog.pas +++ b/windows/src/desktop/kmshell/main/initprog.pas @@ -384,6 +384,7 @@ var kdl: IKeymanDefaultLanguage; FIcon: string; FMutex: TKeymanMutex; // I2720 + RemoteUpdateCheck: TRemoteUpdateCheck; function FirstKeyboardFileName: WideString; begin if KeyboardFileNames.Count = 0 @@ -432,15 +433,15 @@ begin end; // TODO: #10038 Will add this as part of the background update state machine // for now just verifing the download happens via -buc switch. - with TRemoteUpdateCheck.Create(False, False) do + RemoteUpdateCheck := TRemoteUpdateCheck.Create(False, False); try if (FMode = fmBackgroundUpdateCheck) then begin - Run; + RemoteUpdateCheck.Run; Exit; end finally - Free; + RemoteUpdateCheck.Free; end; From 92ca7113fc599a1f5f0c5a490beab0aae41e9fa1 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Wed, 13 Dec 2023 09:51:56 +1000 Subject: [PATCH 10/11] feat(windows): Marking TODOs to be handled in next PR --- .../main/Keyman.System.RemoteUpdateCheck.pas | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas index 077e4b42de..d9fb1ab6c2 100644 --- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas @@ -1,20 +1,9 @@ -(* - Name: WebUpdateCheck - Copyright: Copyright (C) SIL International. - Documentation: - Description: - Create Date: 5 Dec 2023 - - Modified Date: - Authors: rcruickshank - Related Files: - Dependencies: - - Bugs: - Todo: - Notes: - History: -*) +{ + * Keyman is copyright (C) SIL International. MIT License. + * + * Keyman.System.RemoteUpdateCheck: Checks for keyboard package and Keyman + for Windows updates. +} unit Keyman.System.RemoteUpdateCheck; // I3306 interface @@ -195,7 +184,7 @@ begin // Keyman Installer if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742 begin - // TODO record fail? and log // Download failed but user wants to install other files + // TODO: #10210record fail? and log // Download failed but user wants to install other files end else begin @@ -264,7 +253,7 @@ begin if registry.ValueExists(SRegValue_LastUpdateCheckTime) and (Now - registry.ReadDateTime(SRegValue_LastUpdateCheckTime) < 7) and not FForce then begin Result := wucNoUpdates; - // TODO: This exit is just to remove the time check for testing. + // TODO: #10210 This exit is just to remove the time check for testing. //Exit; end; @@ -328,11 +317,10 @@ begin if FCheckOnly then begin - // TODO: Refactor this TUpdateCheckStorage.SaveUpdateCacheData(ucr); Result := FRemoteResult; end - // TODO: #10038 + // TODO: ##10210 // Integerate into state machine. in the download state // the process can call LoadUpdateCacheData if needed to get the // response result. From 9909eb759a0e0808810a1a9564f75c53fd88fcd0 Mon Sep 17 00:00:00 2001 From: rc-swag <58423624+rc-swag@users.noreply.github.com> Date: Wed, 13 Dec 2023 10:36:07 +1000 Subject: [PATCH 11/11] feat(windows): move exception for each file download --- .../main/Keyman.System.RemoteUpdateCheck.pas | 144 +++++++++--------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas index d9fb1ab6c2..0eebd52ff9 100644 --- a/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas +++ b/windows/src/desktop/kmshell/main/Keyman.System.RemoteUpdateCheck.pas @@ -119,91 +119,91 @@ var function DownloadFile(const url, savepath: string): Boolean; begin - http := THttpUploader.Create(nil); try - http.Proxy.Server := GetProxySettings.Server; - http.Proxy.Port := GetProxySettings.Port; - http.Proxy.Username := GetProxySettings.Username; - http.Proxy.Password := GetProxySettings.Password; - http.Request.Agent := API_UserAgent; + http := THttpUploader.Create(nil); + try + http.Proxy.Server := GetProxySettings.Server; + http.Proxy.Port := GetProxySettings.Port; + http.Proxy.Username := GetProxySettings.Username; + http.Proxy.Password := GetProxySettings.Password; + http.Request.Agent := API_UserAgent; - http.Request.SetURL(url); - http.Upload; - if http.Response.StatusCode = 200 then + http.Request.SetURL(url); + http.Upload; + if http.Response.StatusCode = 200 then + begin + fs := TFileStream.Create(savepath, fmCreate); + try + fs.Write(http.Response.PMessageBody^, http.Response.MessageBodyLength); + finally + fs.Free; + end; + Result := True; + end + else // I2742 + // If it fails we set to false but will try the other files + Result := False; + Exit; + finally + http.Free; + end; + except + on E:EHTTPUploader do begin - fs := TFileStream.Create(savepath, fmCreate); - try - fs.Write(http.Response.PMessageBody^, http.Response.MessageBodyLength); - finally - fs.Free; - end; - Result := True; - end - else // I2742 - // If it fails we set to false but will try the other files + if (E.ErrorCode = 12007) or (E.ErrorCode = 12029) + then LogMessage(S_OnlineUpdate_UnableToContact) + else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message])); Result := False; - Exit; - finally - http.Free; + end; end; end; - begin Result := False; - try - FDownload.TotalSize := 0; - FDownload.TotalDownloads := 0; - downloadCount := 0; - // Keyboard Packages - for i := 0 to High(Params.Packages) do - begin - Inc(FDownload.TotalDownloads); - Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize); - Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName; - end; + FDownload.TotalSize := 0; + FDownload.TotalDownloads := 0; + downloadCount := 0; - // Add the Keyman installer + // Keyboard Packages + for i := 0 to High(Params.Packages) do + begin Inc(FDownload.TotalDownloads); - Inc(FDownload.TotalSize, Params.InstallSize); - - // Keyboard Packages - FDownload.StartPosition := 0; - for i := 0 to High(Params.Packages) do - begin - if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742 - begin - Params.Packages[i].Install := False; // Download failed but install other files - end - else - Inc(downloadCount); - FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize; - end; - - // Keyman Installer - if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742 - begin - // TODO: #10210record fail? and log // Download failed but user wants to install other files - end - else - begin - Inc(downloadCount) - end; - - // There needs to be at least one file successfully downloaded to return - // True that files were downloaded - if downloadCount > 0 then - Result := True; - except - on E:EHTTPUploader do - begin - if (E.ErrorCode = 12007) or (E.ErrorCode = 12029) - then LogMessage(S_OnlineUpdate_UnableToContact) - else LogMessage(WideFormat(S_OnlineUpdate_UnableToContact_Error, [E.Message])); - Result := False; - end; + Inc(FDownload.TotalSize, Params.Packages[i].DownloadSize); + Params.Packages[i].SavePath := SavePath + Params.Packages[i].FileName; end; + + // Add the Keyman installer + Inc(FDownload.TotalDownloads); + Inc(FDownload.TotalSize, Params.InstallSize); + + // Keyboard Packages + FDownload.StartPosition := 0; + for i := 0 to High(Params.Packages) do + begin + if not DownloadFile(Params.Packages[i].DownloadURL, Params.Packages[i].SavePath) then // I2742 + begin + Params.Packages[i].Install := False; // Download failed but install other files + end + else + Inc(downloadCount); + FDownload.StartPosition := FDownload.StartPosition + Params.Packages[i].DownloadSize; + end; + + // Keyman Installer + if not DownloadFile(Params.InstallURL, SavePath + Params.FileName) then // I2742 + begin + // TODO: #10210record fail? and log // Download failed but user wants to install other files + end + else + begin + Inc(downloadCount) + end; + + // There needs to be at least one file successfully downloaded to return + // True that files were downloaded + if downloadCount > 0 then + Result := True; end; function TRemoteUpdateCheck.DownloadUpdates(Params: TUpdateCheckResponse): Boolean;