spiegel-keyman/windows/src/desktop/setup/Keyman.Setup.System.OnlineResourceCheck.pas
Marc Durdin 35c82ea128 fix(windows): handle invalid package names during install
Fixes #4886.

This patch includes 5 separate fixes for the various scenarios we
encountered during package installation.

1. RunTools.pas: don't attempt to install TIPs if package is not also
   being installed.

2. Keyman.Setup.System.OnlineResourceCheck.pas, OnlineUpdateCheck.pas:
   handle online update check for packages with spaces or periods in the
   id (legacy packages only).

3. Keyman.Setup.System.InstallInfo.pas: Handle browsers renaming
   downloaded files with `(n)` and `[n]` patterns as well as the current
   ` (n)` pattern.

4. Keyman.Setup.System.InstallInfo.pas: Handle browsers renaming
   downloaded files with `_n` pattern. See code comments for details.

5. Keyman.Configuration.System.TIPMaintenance.pas: Avoid crashing if
   `kmshell -install-tips-for-packages` is passed an invalid package
   name.

The crash is actually fixed in point 5 above, but the root causes are
addressed in the other points.
2021-04-19 08:25:31 +10:00

113 lines
3.5 KiB
ObjectPascal

{**
* Wrapper for checking api.keyman.com for latest version of resources to be
* installed. Uses the /windows/14.0/update endpoints.
*}
unit Keyman.Setup.System.OnlineResourceCheck;
interface
uses
Keyman.Setup.System.InstallInfo;
type
TOnlineResourceCheck = class sealed
class procedure QueryServer(ASilent: Boolean; AInstallInfo: TInstallInfo); static;
end;
implementation
uses
System.SysUtils,
GlobalProxySettings,
httpuploader,
Keyman.System.UpdateCheckResponse,
KeymanVersion,
PackageInfo,
Upload_Settings,
versioninfo;
class procedure TOnlineResourceCheck.QueryServer(ASilent: Boolean; AInstallInfo: TInstallInfo);
var
http: THTTPUploader;
pack: TInstallInfoPackage;
ucr: TUpdateCheckResponse;
ucrpack: TUpdateCheckResponsePackage;
location: TInstallInfoFileLocation;
packLocation: TInstallInfoPackageFileLocation;
currentVersion: string;
lang: TUpdateCheckResponseLanguage;
iipl: TInstallInfoPackageLanguage;
u: AnsiString;
i: Integer;
begin
currentVersion := AInstallInfo.MsiLocations.LatestVersion(SKeymanVersion_Min_Evergreen);
http := THttpUploader.Create(nil);
try
http.ShowUI := not ASilent;
http.Request.SetURL(MakeAPIURL(API_Path_UpdateCheck_Windows));
http.Fields.Add('version', AnsiString(currentVersion));
http.Fields.Add('tier', AnsiString(AInstallInfo.Tier));
http.Fields.Add('update', '0'); // This is probably a fresh install of a package, not an update
http.Fields.Add('manual', '1'); // Treat this as a manual update so the staggered rollout doesn't apply
for i := 0 to AInstallInfo.Packages.Count - 1 do
begin
pack := AInstallInfo.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.
http.Fields.Add(AnsiString('packageid_'+IntToStr(i)), AnsiString(pack.ID));
http.Fields.Add(AnsiString('packageversion_'+IntToStr(i)), AnsiString(pack.Locations.LatestVersion));
end;
http.Upload;
if http.Response.StatusCode <> 200 then
begin
// TODO: log failed response
//raise EOnlineUpdateCheck.Create('Error '+IntToStr(Response.StatusCode));
Exit;
end;
u := http.response.PMessageBody;
finally
http.Free;
end;
if ucr.Parse(u, 'msi', currentVersion) then
begin
for ucrpack in ucr.Packages do
begin
pack := AInstallInfo.Packages.FindById(ucrpack.ID, False);
if Assigned(pack) then
begin
packLocation := TInstallInfoPackageFileLocation.Create(iilOnline);
pack.Locations.Add(packLocation);
packLocation.Name := ucrpack.Name;
packLocation.Path := ucrpack.FileName;
packLocation.URL := ucrpack.DownloadURL;
packLocation.Version := ucrpack.NewVersion;
packLocation.Size := ucrpack.DownloadSize;
for lang in ucrpack.Languages do
begin
iipl := TInstallInfoPackageLanguage.Create(lang.ID, lang.displayName);
packLocation.Languages.Add(iipl);
end;
end;
end;
if ucr.Status = ucrsUpdateReady then
begin
location := TInstallInfoFileLocation.Create(iilOnline);
location.URL := ucr.InstallURL;
location.Path := ucr.FileName;
location.Version := ucr.NewVersion;
location.VersionWithTag := ucr.NewVersionWithTag;
location.Size := ucr.InstallSize;
AInstallInfo.MsiLocations.Add(location);
end;
end;
end;
end.