Skip to content

Instantly share code, notes, and snippets.

@konsolebox
Created August 1, 2025 04:06
Show Gist options
  • Select an option

  • Save konsolebox/d55f28bc391af8e1b8d470c672c14575 to your computer and use it in GitHub Desktop.

Select an option

Save konsolebox/d55f28bc391af8e1b8d470c672c14575 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# Copyright (c) 2025 konsolebox
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Developed with assistance from Grok, created by xAI
require 'ffi'
require 'optparse'
require 'fileutils'
# Extend FFI to interface with libxfce4util and glib
module LibXfce4Util
extend FFI::Library
ffi_lib 'libxfce4util.so.7' # Adjust version if needed (check /usr/lib/libxfce4util.so*)
# gboolean xfce_g_file_set_trusted(GFile *file, gboolean is_trusted, GCancellable *cancellable, GError **error)
attach_function :xfce_g_file_set_trusted, [:pointer, :bool, :pointer, :pointer], :bool
# gboolean xfce_g_file_is_trusted(GFile *file, GCancellable *cancellable, GError **error)
attach_function :xfce_g_file_is_trusted, [:pointer, :pointer, :pointer], :bool
end
module GLib
extend FFI::Library
ffi_lib 'libgio-2.0.so.0' # GLib/GIO library
# GFile* g_file_new_for_path(const char *path)
attach_function :g_file_new_for_path, [:string], :pointer
# void g_object_unref(gpointer object)
attach_function :g_object_unref, [:pointer], :void
end
# Function to validate .desktop file
def valid_desktop_file?(file)
return false unless file =~ /\.desktop$/
content = File.read(file) rescue nil
return false unless content
return false unless content.match?(/^\[Desktop Entry\]/)
return false unless content.match?(/^Type=Application/)
return false unless content.match?(/^Exec=/)
true
end
# Function to set executable bit
def set_executable(file)
if File.executable?(file)
return true
end
begin
File.chmod(0755, file)
true
rescue
$stderr.puts "[ERROR] Failed to set executable bit on #{file}"
false
end
end
# Function to check trust status using xfce_g_file_is_trusted
def get_trust_status(file)
begin
gfile = GLib.g_file_new_for_path(file)
unless gfile
$stderr.puts "[ERROR] Failed to create GFile for #{file}"
return nil
end
error_ptr = FFI::MemoryPointer.new(:pointer, 1)
error_ptr.write_pointer(nil)
cancellable = nil # NULL for GCancellable
result = LibXfce4Util.xfce_g_file_is_trusted(gfile, cancellable, error_ptr)
GLib.g_object_unref(gfile)
if error_ptr.read_pointer.null?
return result ? "trusted" : "untrusted"
else
$stderr.puts "[ERROR] Failed to check trust status for #{file}"
return nil
end
rescue
$stderr.puts "[ERROR] Failed to call xfce_g_file_is_trusted for #{file}; trying to use fallback"
return get_trust_status_fallback(file)
end
end
# Fallback function to check trust status using gio
def get_trust_status_fallback(file)
unless system('command -v gio >/dev/null 2>&1')
$stderr.puts "[ERROR] 'gio' not found. Install 'libglib2.0-bin' (Debian/Ubuntu) or 'glib2' (Fedora)."
return nil
end
output = `gio info "#{file}" 2>/dev/null | grep metadata::xfce-exe-checksum`
if $?.success? && output.match?(/metadata::xfce-exe-checksum:\s*[0-9a-f]{64}/)
return "trusted"
else
return "untrusted"
end
end
# Function to set trust status using xfce_g_file_set_trusted
def set_trust_attribute(file, is_trusted)
begin
gfile = GLib.g_file_new_for_path(file)
unless gfile
$stderr.puts "[ERROR] Failed to create GFile for #{file}"
return false
end
error_ptr = FFI::MemoryPointer.new(:pointer, 1)
error_ptr.write_pointer(nil)
cancellable = nil # NULL for GCancellable
result = LibXfce4Util.xfce_g_file_set_trusted(gfile, is_trusted, cancellable, error_ptr)
GLib.g_object_unref(gfile)
if result
tag = is_trusted ? "TRUSTED" : "UNTRUSTED"
puts "[#{tag}] #{file}"
return true
else
$stderr.puts "[ERROR] Failed to set trust status for #{file}"
return false
end
rescue
$stderr.puts "[ERROR] Failed to call xfce_g_file_set_trusted for #{file}; trying to use fallback"
return set_trust_attribute_fallback(file, is_trusted)
end
end
# Fallback function to set/unset metadata::xfce-exe-checksum using gio
def set_trust_attribute_fallback(file, is_trusted)
unless system('command -v gio >/dev/null 2>&1')
$stderr.puts "[ERROR] 'gio' not found. Install 'libglib2.0-bin' (Debian/Ubuntu) or 'glib2' (Fedora)."
return false
end
if is_trusted
unless system('command -v sha256sum >/dev/null 2>&1')
$stderr.puts "[ERROR] 'sha256sum' not found. Install 'coreutils'."
return false
end
hash = `sha256sum "#{file}" 2>/dev/null`.split.first
unless hash && hash.match?(/^[0-9a-f]{64}$/)
$stderr.puts "[ERROR] Failed to compute SHA-256 hash for #{file}"
return false
end
if system("gio set '#{file}' metadata::xfce-exe-checksum '#{hash}' 2>/dev/null")
puts "[TRUSTED] #{file}"
true
else
$stderr.puts "[ERROR] Failed to set metadata::xfce-exe-checksum on #{file}"
false
end
else
if system("gio set -t unset '#{file}' metadata::xfce-exe-checksum 2>/dev/null")
puts "[UNTRUSTED] #{file}"
true
else
$stderr.puts "[ERROR] Failed to unset metadata::xfce-exe-checksum on #{file}"
false
end
end
end
# Function to reload xfdesktop
def reload_desktop
if system('command -v xfdesktop >/dev/null 2>&1')
system('xfdesktop --reload 2>/dev/null')
else
$stderr.puts "[ERROR] 'xfdesktop' not found. Refresh manually."
end
end
# Function for interactive confirmation
def confirm_action(file)
loop do
print "[CONFIRM] Allow trust change for #{file}? (y/n): "
response = gets.chomp.downcase
return true if response == 'y'
return false if response == 'n'
puts "[ERROR] Please enter 'y' or 'n'."
end
end
# Function to show usage info
def show_usage_info_and_exit
$stderr.puts <<~HELP
Usage: xfce-desktop-file-tool.rb <subcommand> [options] <file1> [file2 ...]
Subcommands:
get-trusted Check trust status of .desktop files
set-trusted Set trust status of .desktop files
Options for set-trusted:
-i, --interactive Prompt for confirmation before setting trust
-r, --reload-desktop Reload xfdesktop after setting trust
-u, --untrusted Mark files as untrusted (opposite of trusted)
General options:
-h, --help Show this help message
HELP
exit 0
end
# Parse command-line options
def parse_options
options = { subcommand: nil, interactive: false, reload: false, untrusted: false }
ARGV.each_with_index do |arg, i|
if arg == 'get-trusted' || arg == 'set-trusted'
options[:subcommand] = arg
ARGV.delete_at(i)
break
end
end
OptionParser.new do |opts|
opts.on('-i', '--interactive', 'Prompt for confirmation') { options[:interactive] = true }
opts.on('-r', '--reload-desktop', 'Reload xfdesktop') { options[:reload] = true }
opts.on('-u', '--untrusted', 'Mark as untrusted') { options[:untrusted] = true }
opts.on('-h', '--help', 'Show help') { show_usage_info_and_exit }
end.parse!
options
end
# Main script
options = parse_options
unless options[:subcommand]
$stderr.puts "[ERROR] No subcommand specified. Use get-trusted or set-trusted."
show_usage_info_and_exit
end
if ARGV.empty?
$stderr.puts "[ERROR] No files specified."
show_usage_info_and_exit
end
case options[:subcommand]
when 'get-trusted'
ARGV.each do |file|
unless File.exist?(file)
$stderr.puts "[ERROR] #{file} does not exist"
next
end
unless valid_desktop_file?(file)
$stderr.puts "[ERROR] #{file} is not a valid .desktop file"
next
end
status = get_trust_status(file)
puts "#{file}: #{status}" if status
end
when 'set-trusted'
ARGV.each do |file|
unless File.exist?(file)
$stderr.puts "[ERROR] #{file} does not exist"
next
end
unless valid_desktop_file?(file)
$stderr.puts "[ERROR] #{file} is not a valid .desktop file"
next
end
status = get_trust_status(file)
if status == "trusted" && !options[:untrusted]
puts "[ALREADY_TRUSTED] #{file}"
next
elsif status == "untrusted" && options[:untrusted]
puts "[ALREADY_UNTRUSTED] #{file}"
next
end
if options[:interactive]
next unless confirm_action(file)
end
unless options[:untrusted]
unless set_executable(file)
next
end
end
set_trust_attribute(file, !options[:untrusted])
end
reload_desktop if options[:reload]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment