[gtk/uac.meson.4] gtk4-update-icon-cache: Avoid UAC on 32-bit Windows




commit 5f6bde8c9588f54e0c1386d3e1e63a9d308c1996
Author: Chun-wei Fan <fanc999 yahoo com tw>
Date:   Wed Feb 3 02:16:58 2021 +0000

    gtk4-update-icon-cache: Avoid UAC on 32-bit Windows
    
    As the program executable name has 'update' in its filename,
    gtk4-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.
    
    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/tools/generate-uac-manifest.py | 110 +++++++++++++++++++++++++++++++++++++
 gtk/tools/meson.build              |  25 ++++++++-
 2 files changed, 134 insertions(+), 1 deletion(-)
---
diff --git a/gtk/tools/generate-uac-manifest.py b/gtk/tools/generate-uac-manifest.py
new file mode 100644
index 0000000000..ede6ba5044
--- /dev/null
+++ b/gtk/tools/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/tools/meson.build b/gtk/tools/meson.build
index 8f7a62db17..a8999842f6 100644
--- a/gtk/tools/meson.build
+++ b/gtk/tools/meson.build
@@ -1,4 +1,27 @@
 # Installed tools
+
+extra_update_icon_cache_objs = []
+if win32_enabled
+  gen_uac_manifest = find_program('generate-uac-manifest.py')
+
+  uac_exe_pkg = 'gtk4'
+  uac_exe_name = '@0@-update-icon-cache'.format(uac_exe_pkg)
+
+  # 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/tools/@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_tools = [
   ['gtk4-query-settings', ['gtk-query-settings.c']],
   ['gtk4-builder-tool', ['gtk-builder-tool.c',
@@ -6,7 +29,7 @@ gtk_tools = [
                          'gtk-builder-tool-validate.c',
                          'gtk-builder-tool-enumerate.c',
                          'gtk-builder-tool-preview.c']],
-  ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c']],
+  ['gtk4-update-icon-cache', ['updateiconcache.c', 'gtkiconcachevalidator.c'] + 
extra_update_icon_cache_objs],
   ['gtk4-encode-symbolic-svg', ['encodesymbolic.c', 'gdkpixbufutils.c']],
 ]
 


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