[gtk/uac.meson.3] Meson: Avoid UAC on 32-bit Windows for gtk-update-icon-cache




commit 3a70344639651d3ee71f8d3a08fa47e892535f56
Author: Chun-wei Fan <fanchunwei src gnome org>
Date:   Wed Feb 3 11:30:15 2021 +0800

    Meson: Avoid UAC on 32-bit Windows for gtk-update-icon-cache
    
    As the program executable name has 'update' in its filename,
    gtk-update-icon-cache.exe is considered to be an installer program on 32-bit
    Windows [1], which will cause the program to fail to run unless it is running
    with elevated privileges (i.e. UAC).
    
    Avoid this situation by embedding a manifest file into the final executable
    that tells Windows that this is not a program that requires elevation.
    
    Also make the autotools build files dist the new script and use the new script
    to generate the manifest and rc files, instead of hardcoding the generating
    bits in gtk/Makefile.am
    
    Fixes issue #3632.
    
    [1]: 
https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc709628(v=ws.10)?redirectedfrom=MSDN,
    under section "Installer Detection  Technology"

 gtk/Makefile.am              |  24 ++--------
 gtk/generate-uac-manifest.py | 110 +++++++++++++++++++++++++++++++++++++++++++
 gtk/meson.build              |  23 +++++++++
 3 files changed, 136 insertions(+), 21 deletions(-)
---
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 1d22172522..b142c94e5e 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -1710,27 +1710,8 @@ GTK_UPDATE_ICON_CACHE_MANIFEST = gtk-update-icon-cache.exe.manifest
 GTK_UPDATE_ICON_CACHE_RC = gtk-update-icon-cache.rc
 GTK_UPDATE_ICON_CACHE_MANIFEST_OBJECT = gtk-update-icon-cache_manifest.o
 
-$(GTK_UPDATE_ICON_CACHE_MANIFEST):
-       (echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' ; \
-        echo '<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">' ; \
-        echo '  <assemblyIdentity version="1.0.0.0"' ; \
-        echo '     processorArchitecture="'$(EXE_MANIFEST_ARCHITECTURE)'"' ; \
-        echo '     name="gtk-update-icon-cache.exe"' ; \
-        echo '     type="win32"/>' ; \
-        echo '  <!-- Identify the application security requirements. -->' ; \
-        echo '  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">' ; \
-        echo '    <security>' ; \
-        echo '      <requestedPrivileges>' ; \
-        echo '        <requestedExecutionLevel' ; \
-        echo '          level="asInvoker"' ; \
-        echo '          uiAccess="false"/>' ; \
-        echo '        </requestedPrivileges>' ; \
-        echo '       </security>' ; \
-        echo '  </trustInfo>' ; \
-        echo '</assembly>' ) >$@
-
-$(GTK_UPDATE_ICON_CACHE_RC):
-       (echo -e '#include <winuser.h>\nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST 
'$(GTK_UPDATE_ICON_CACHE_MANIFEST)) >$@
+$(GTK_UPDATE_ICON_CACHE_MANIFEST) $(GTK_UPDATE_ICON_CACHE_RC):
+       $(PYTHON) $(srcdir)/generate-uac-manifest.py -p=gtk3 -n=gtk-update-icon-cache$(EXEEXT) 
--pkg-version=$(GTK_VERSION)
 
 $(GTK_UPDATE_ICON_CACHE_MANIFEST_OBJECT): $(GTK_UPDATE_ICON_CACHE_RC) $(GTK_UPDATE_ICON_CACHE_MANIFEST)
        $(WINDRES) --input $< --output $@ --output-format=coff
@@ -1793,6 +1774,7 @@ EXTRA_DIST +=                   \
        meson.build \
        gen-gtk-gresources-xml.py \
        gen-rc.py \
+       generate-uac-manifest.py \
        gentypefuncs.py \
        a11y/meson.build \
        deprecated/meson.build \
diff --git a/gtk/generate-uac-manifest.py b/gtk/generate-uac-manifest.py
new file mode 100644
index 0000000000..ede6ba5044
--- /dev/null
+++ b/gtk/generate-uac-manifest.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+
+"""
+This script generates a Windows manifest file and optionally a resource file to
+determine whether a specified program requires UAC elevation
+"""
+
+import os
+import argparse
+
+DOMAIN_NAME='gnome'
+
+def main():
+    parser = argparse.ArgumentParser(
+        description=__doc__)
+    parser.add_argument('-p', '--package', required=True,
+                        help='package name of the executable')
+    parser.add_argument('-n', '--name', required=True,
+                        help='name of executable')
+    parser.add_argument('--pkg-version', required=True, dest='version',
+                        help='version of package')
+    parser.add_argument('--require-admin', action='store_true', dest='admin',
+                        default=False,
+                        help='require admin access to application')
+    parser.add_argument('--input-resource-file', dest='resource',
+                        default=None,
+                        help='existing .rc file to embed UAC manifest (do not generate a new .rc file), must 
have included winuser.h in it')
+    parser.add_argument('--output-dir', dest='outdir',
+                        default=None,
+                        help='directory to output resulting files')
+    args = parser.parse_args()
+
+    if args.resource is not None:
+        if not os.path.isfile(args.resource):
+            raise FileNotFoundError("Specified resource file '%s' does not exist" % args.resource)
+
+    generate_manifest(args.package, args.name, args.version, args.admin, args.outdir)
+    write_rc_file(args.name, args.resource, args.outdir)
+
+def generate_manifest(package, name, version, admin, outdir):
+    if version.count('.') == 0:
+        manifest_package_version = version + '.0.0.0'
+    elif version.count('.') == 1:
+        manifest_package_version = version + '.0.0'
+    elif version.count('.') == 2:
+        manifest_package_version = version + '.0'
+    elif version.count('.') == 3:
+        manifest_package_version = version
+    else:
+        parts = version.split('.')
+        manifest_package_version = ''
+        for x in (0, 1, 2, 3):
+            if x == 0:
+                manifest_package_version += parts[x]
+            else:
+                manifest_package_version += '.' + parts[x]
+
+    if outdir is not None:
+        output_file_base_name = os.path.join(outdir, name)
+    else:
+        output_file_base_name = name
+
+    outfile = open(output_file_base_name + '.exe.manifest', 'w+')
+    outfile.write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n')
+    outfile.write('<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\n')
+    outfile.write('  <assemblyIdentity version="%s"\n' % manifest_package_version)
+    outfile.write('    processorArchitecture="*"\n')
+    outfile.write('    name="%s.%s.%s.exe"\n' % (DOMAIN_NAME, package, name))
+    outfile.write('    type="win32" />\n')
+    outfile.write('  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">\n')
+    outfile.write('    <security>\n')
+    outfile.write('      <requestedPrivileges>\n')
+    outfile.write('        <requestedExecutionLevel\n')
+
+    if admin:
+        outfile.write('          level="requireAdministrator"\n')
+    else:
+        outfile.write('          level="asInvoker"\n')
+
+    outfile.write('          uiAccess="false" />\n')
+    outfile.write('      </requestedPrivileges>\n')
+    outfile.write('    </security>\n')
+    outfile.write('  </trustInfo>\n')
+    outfile.write('</assembly>\n')
+    outfile.close()
+
+def write_rc_file(name, resource, outdir):
+    if outdir is not None:
+        output_file_base_name = os.path.join(outdir, name)
+    else:
+        output_file_base_name = name
+
+    if resource is None:
+        outfile = open(output_file_base_name + '.rc', 'w+')
+        outfile.write('#include <winuser.h>')
+    else:
+        if resource != output_file_base_name + '.rc':
+            outfile = open(output_file_base_name + '.rc', 'w+')
+        else:
+            outfile = open(output_file_base_name + '.final.rc', 'w+')
+        srcfile = open(resource, 'r')
+        outfile.write(srcfile.read())
+        srcfile.close()
+
+    outfile.write('\n')
+    outfile.write('CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "%s.exe.manifest"' % name)
+    outfile.close()
+
+if __name__ == '__main__':
+    main()
diff --git a/gtk/meson.build b/gtk/meson.build
index 2a72b91047..43a6e2bfef 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -1065,9 +1065,32 @@ gtk_builder_tool = executable(
   install: true
 )
 
+extra_update_icon_cache_objs = []
+if win32_enabled
+  gen_uac_manifest = find_program('generate-uac-manifest.py')
+
+  uac_exe_pkg = 'gtk3'
+  uac_exe_name = 'gtk-update-icon-cache'
+
+  # Well, we have to forgo the xxx.exe.manifest in the output listing, since
+  # compile_resources doesn't like to consume targets with multiple outputs,
+  # and the xxx.exe.manifest and xxx.rc are tied together
+  uac_rc = custom_target(
+    'gtk/@0@.rc'.format(uac_exe_name),
+    output: ['@0@.rc'.format(uac_exe_name)],
+    command: [gen_uac_manifest,
+              '-p=@0@'.format(uac_exe_pkg),
+              '-n=@0@'.format(uac_exe_name),
+              '--pkg-version=@0@'.format(meson.project_version()),
+              '--output-dir=@OUTDIR@'],
+  )
+  extra_update_icon_cache_objs = import('windows').compile_resources(uac_rc)
+endif
+
 gtk_update_icon_cache = executable(
   'gtk-update-icon-cache',
   'updateiconcache.c',
+  extra_update_icon_cache_objs,
   c_args: gtk_cargs,
   dependencies: libgtk_dep,
   install: true


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]