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

lintian: r939 - in trunk: checks debian



Author: rra
Date: 2007-08-01 12:26:22 +0200 (Wed, 01 Aug 2007)
New Revision: 939

Modified:
   trunk/checks/menu-format
   trunk/checks/menu-format.desc
   trunk/debian/changelog
Log:
  + [RA] Add checks of desktop files.  The checks so far are basic, but
    do include key and category verification.  Based heavily on work by
    Javier Fern?\195?\161ndez-Sanguino Pe?\195?\177a.  (Closes: #277441, #433411)

Modified: trunk/checks/menu-format
===================================================================
--- trunk/checks/menu-format	2007-08-01 04:57:58 UTC (rev 938)
+++ trunk/checks/menu-format	2007-08-01 10:26:22 UTC (rev 939)
@@ -18,6 +18,10 @@
 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
 # MA 02110-1301, USA.
 
+# This script also checks desktop entries, since they share quite a bit of
+# code.  At some point, it would make sense to try to refactor this so that
+# shared code is in libraries.
+
 package Lintian::menu_format;
 use strict;
 use Tags;
@@ -116,14 +120,226 @@
 		'Window Maker'
 	       );
 
+# Authorative source of desktop keys:
+# http://standards.freedesktop.org/desktop-entry-spec/1.0/
+#
+# This is a list of all keys that should be in every desktop entry.
+my @req_desktop_keys = qw(Type Name);
+
+# This is a list of all known keys.
+my %known_desktop_keys = map { $_ => 1 }
+    qw(
+       Type
+       Version
+       Name
+       GenericName
+       NoDisplay
+       Comment
+       Icon
+       Hidden
+       OnlyShowIn
+       NotShowIn
+       TryExec
+       Exec
+       Path
+       Terminal
+       MimeType
+       Categories
+       MimeType
+       Categories
+       StartupNotify
+       StartupWMClass
+       URL
+      );
+
+my %deprecated_desktop_keys = map { $_ => 1 }
+    qw(
+       Encoding
+       MiniIcon
+       TerminalOptions
+       Protocols
+       Extensions
+       BinaryPattern
+       MapNotify
+       SwallowTitle
+       SwallowExec
+       SortOrder
+       FilePattern
+      );
+
+# KDE uses some additional keys that should start with X-KDE but don't for
+# historical reasons.
+my %kde_desktop_keys = map { $_ => 1 }
+    qw(
+       ServiceTypes
+       DocPath
+       Keywords
+       InitialPreference
+       Dev
+       FSType
+       MountPoint
+       ReadOnly
+       UnmountIcon
+      );
+
+# Authorative source of desktop categories:
+# http://standards.freedesktop.org/menu-spec/1.0/apa.html
+
+# This is a list of all main categories for .desktop files.
+my %main_categories = map { $_ => 1 }
+    qw(
+       AudioVideo
+       Audio
+       Video
+       Development
+       Education
+       Game
+       Graphics
+       Network
+       Office
+       Settings
+       System
+       Utility
+      );
+
+# This is a list of all categories, main and additional, for .desktop files.
+# Ideally we should be checking the associated main categories as well, but we
+# don't have support for that yet.
+my %categories = map { $_ => 1 }
+    qw(
+       Building
+       Debugger
+       IDE
+       GUIDesigner
+       Profiling
+       RevisionControl
+       Translation
+       Calendar
+       ContactManagement
+       Database
+       Dictionary
+       Chart
+       Email
+       Finance
+       FlowChart
+       PDA
+       ProjectManagement
+       Presentation
+       Spreadsheet
+       WordProcessor
+       2DGraphics
+       VectorGraphics
+       RasterGraphics
+       3DGraphics
+       Scanning
+       OCR
+       Photography
+       Publishing
+       Viewer
+       TextTools
+       DesktopSettings
+       HardwareSettings
+       Printing
+       PackageManager
+       Dialup
+       InstantMessaging
+       Chat
+       IRCClient
+       FileTransfer
+       HamRadio
+       News
+       P2P
+       RemoteAccess
+       Telephony
+       TelephonyTools
+       VideoConference
+       WebBrowser
+       WebDevelopment
+       Midi
+       Mixer
+       Sequencer
+       Tuner
+       TV
+       AudioVideoEditing
+       Player
+       Recorder
+       DiscBurning
+       ActionGame
+       AdventureGame
+       ArcadeGame
+       BoardGame
+       BlocksGame
+       CardGame
+       KidsGame
+       LogicGame
+       RolePlaying
+       Simulation
+       SportsGame
+       StrategyGame
+       Art
+       Construction
+       Music
+       Languages
+       Science
+       ArtificialIntelligence
+       Astronomy
+       Biology
+       Chemistry
+       ComputerScience
+       DataVisualization
+       Economy
+       Electricity
+       Geography
+       Geology
+       Geoscience
+       History
+       ImageProcessing
+       Literature
+       Math
+       NumericalAnalysis
+       MedicalSoftware
+       Physics
+       Robotics
+       Sports
+       ParallelComputing
+       Amusement
+       Archiving
+       Compression
+       Electronics
+       Emulator
+       Engineering
+       FileTools
+       FileManager
+       TerminalEmulator
+       Filesystem
+       Monitor
+       Security
+       Accessibility
+       Calculator
+       Clock
+       TextEditor
+       Documentation
+       Core
+       KDE
+       GNOME
+       GTK
+       Qt
+       Motif
+       Java
+       ConsoleOnly
+      );
+
 # Path in which to search for binaries referenced in menu entries.
 my @path = qw(/usr/local/bin/ /usr/bin/ /bin/ /usr/X11R6/bin/ /usr/games/);
 
-my %known_tags_hash;
-my %needs_tag_vals_hash;
-my %root_sections_hash;
-my %sections_hash;
+my %known_tags_hash = map { $_ => 1 } @known_tags;
+my %needs_tag_vals_hash = map { $_ => 1 } @needs_tag_vals;
+my %root_sections_hash = map { $_ => 1 } @root_sections;
+my %sections_hash = map { $_ => 1 } @sections;
 
+# Holds a hash of all files in the package, used for checking for executables.
+my %file_index;
+
 # -----------------------------------
 
 sub run {
@@ -131,20 +347,6 @@
 my $pkg = shift;
 my $type = shift;
 
-# Things worth hashing.
-foreach my $tag (@known_tags) {
-    $known_tags_hash{$tag}=1;
-}
-foreach my $val (@needs_tag_vals) {
-    $needs_tag_vals_hash{$val}=1;
-}
-foreach my $section (@root_sections) {
-    $root_sections_hash{$section}=1;
-}
-foreach my $section (@sections) {
-    $sections_hash{$section}=1;
-}
-
 my @menufiles;
 opendir (MENUDIR, "menu/lib") or fail("cannot read menu/lib file directory.");
 push @menufiles, map { "menu/lib/$_" } readdir(MENUDIR);
@@ -153,6 +355,35 @@
 push @menufiles, map { "menu/share/$_" } readdir(MENUDIR);
 closedir MENUDIR;
 
+# Find the desktop files in the package for verification and also build a hash
+# of every file in the package to use to verify that the command referenced by
+# a menu item or desktop entry is there.
+my @desktop_files;
+open(IN, "index") or fail("cannot open index file index: $!");
+while (<IN>) {
+    chomp;
+    my ($perm, $owner, $size, $date, $time, $file) = split(' ', $_, 6);
+    $file =~ s,^(\./),,;
+    $file =~ s/ link to .*//;
+    $file =~ s/ -> .*//;
+    my $operm = perm2oct($perm);
+    $file_index{$file} = 1;
+
+    if ($perm =~ m,^-,o && $file =~ m/\.desktop$/o) {
+        if ($perm =~ m,x,o) {
+            tag "executable-desktop-file", sprintf("$file %04o",$operm);
+        }
+        push (@desktop_files, $file);
+    }
+}
+close IN;
+
+# Verify all the desktop files.
+for my $desktop_file (@desktop_files) {
+    VerifyDesktopFile ($desktop_file, $desktop_file);
+}
+
+# Now all the menu files.
 foreach my $menufile (@menufiles) {
     next if -x $menufile; # don't try to parse executables
 
@@ -206,7 +437,6 @@
     close IN;
 }
 
-
 }
 
 # -----------------------------------
@@ -344,56 +574,13 @@
     $section =~ tr:/:/:s;	# eliminate duplicate slashes.
     $section =~ s:/$::;		# remove trailing slash.
 
-    # Read the file index:
-    my %file_index;
-    open(FILE_INDEX,"index") or fail("cannot open index file index: $!");
-    while(<FILE_INDEX>) {
-	$file_index{(split /\s+/, $_)[5]} = 1;
-    }
-    close FILE_INDEX;
+    # Be sure the command is provided by the package.
+    my ($okay, $command) = VerifyCmd ($fullname, $vals{'command'});
+    tag "menu-command-not-in-package", "$fullname:$linecount $command"
+        unless ($okay
+                or ($tested_packages >= 2)
+                or ($section =~ m:^(WindowManagers/Modules|FVWM Modules|Window Maker):));
 
-    # Handle su wrappers.  The option parsing here is ugly and dead-simple,
-    # but it's hopefully good enough for what will show up in menu files.
-    # su-to-root and sux require -c options, kdesu optionally allows one, and
-    # gksu has the command at the end of its arguments.
-    my @com = split(' ',$vals{'command'});
-    my $cmd;
-    if ($com[0] eq "/usr/sbin/su-to-root") {
-	tag "su-to-root-with-usr-sbin", "$fullname:$linecount";
-    } elsif ($com[0] =~ m,^(?:/usr/bin/)?(su-to-root|gksu|kdesu|sux)$,) {
-	my $wrapper = $1;
-	shift @com;
-	while (@com) {
-	    unless ($com[0]) {
-		shift @com;
-		next;
-	    }
-	    if ($com[0] eq '-c') {
-		$cmd = $com[1];
-		last;
-	    } elsif ($com[0] =~ /^-[Dfmupi]|^--(user|description|message)/) {
-		shift @com;
-		shift @com;
-	    } elsif ($com[0] =~ /^-/) {
-		shift @com;
-	    } else {
-		last;
-	    }
-	}
-	if (!$cmd && $wrapper =~ /^(gk|kde)su$/) {
-	    $cmd = $com[0];
-	} elsif (!$cmd) {
-	    tag "su-wrapper-without--c", "$fullname:$linecount $wrapper";
-	}
-    } else {
-	$cmd = $com[0];
-    }
-    tag "menu-command-not-in-package", "$fullname:$linecount $cmd"
-	if ($cmd
-	    && !($file_index{".$cmd"} || grep {$file_index{".".$_.$cmd}} @path)
-	    && ($tested_packages < 2)
-	    && ($section !~ m:^(WindowManagers/Modules|FVWM Modules|Window Maker):));
-
     if (exists($vals{'icon'})) {
 	VerifyIcon($menufile, $fullname, $linecount, $vals{'icon'}, 32);
     }
@@ -506,6 +693,134 @@
     return;
 }
 
+
+# Syntax-checks a .desktop file.
+sub VerifyDesktopFile {
+    my ($desktopfile, $file) = @_;
+    my %vals;
+    open (DESKTOP, "unpacked/$file") or fail("cannot open desktop file $file: $!");
+    my ($line, $saw_first);
+    while (defined ($line = <DESKTOP>)) {
+        chomp $line;
+        next if ($line =~ m/^\s*\#/ or $line =~ m/^\s*$/);
+
+        # Err on the side of caution for now.  If the first non-comment line
+        # is not the required [Desktop Entry] group, ignore this file.  Also
+        # ignore any keys in other groups.
+        last if ($saw_first and $line =~ /^\[(.*)\]\s*$/);
+        unless ($saw_first) {
+            return unless $line =~ /^\[Desktop Entry\]\s*$/;
+            $saw_first = 1;
+        }
+
+        # Tag = Value
+        # TODO: We do not check for properly formatted localised values for
+        # keys but might be worth checking if they are properly formatted (not
+        # their value)
+        if ($line =~ /^(.*?)\s*=\s*(.*)$/) {
+            my ($tag, $value) = ($1, $2);
+            my $basetag = $tag;
+            my ($encoding) = ($basetag =~ s/\[([^\]]+)\]$//);
+            if (exists $vals{$tag}) {
+                tag "duplicated-key-in-desktop-entry", "$file:$. $tag";
+            } elsif ($deprecated_desktop_keys{$basetag}) {
+                if ($basetag eq 'Encoding') {
+                    tag "desktop-entry-contains-encoding-key", "$file:$. $tag";
+                } else {
+                    tag "desktop-entry-contains-deprecated-key", "$file:$. $tag";
+                }
+            } elsif (    not $known_desktop_keys{$basetag}
+                     and not $kde_desktop_keys{$basetag}
+                     and not $basetag =~ /^X-/) {
+                tag "desktop-entry-contains-unknown-key", "$file:$. $tag";
+            }
+            $vals{$tag} = $value;
+        }
+    }
+    close DESKTOP;
+
+    # Now validate the data in the desktop file.
+
+    # Test for important keys.
+    for my $tag (@req_desktop_keys) {
+        unless (defined $vals{$tag}) {
+            tag "desktop-entry-missing-required-key", "$file $tag";
+        }
+    }
+
+    # TODO:  Should check quoting and the check special field codes in Exec
+    # for desktiop files.
+    if (defined $vals{'Exec'}) {
+        my ($okay, $command) = VerifyCmd ($file, $vals{'Exec'});
+        tag "desktop-command-not-in-package", "$file $command"
+            unless $okay;
+    }
+
+    # Check the Category tag.
+    if (defined $vals{'Categories'}) {
+        my @cats = split (';', $vals{'Categories'});
+        my $saw_main;
+        for my $cat (@cats) {
+            if (not $categories{$cat} and not $main_categories{$cat}) {
+                tag "desktop-entry-invalid-category", "$cat $file";
+            } elsif ($main_categories{$cat}) {
+                $saw_main = 1;
+            }
+        }
+        unless ($saw_main) {
+            tag "desktop-entry-lacks-main-category", "$file";
+        }
+    }
+}
+
+# Verify whether a command is shipped as part of the package.  Takes the full
+# path to the file being checked (for error reporting) and the binary.
+# Returns a list whose first member is true if the command is present and
+# false otherwise, and whose second member is the command (minus any leading
+# su-to-root wrapper).  Shared between the desktop and menu code.
+sub VerifyCmd {
+    my ($file, $exec) = @_;
+
+    # This routine handles su wrappers.  The option parsing here is ugly and
+    # dead-simple, but it's hopefully good enough for what will show up in
+    # desktop files.  su-to-root and sux require -c options, kdesu optionally
+    # allows one, and gksu has the command at the end of its arguments.
+    my @com = split (' ', $exec);
+    my $cmd;
+    if ($com[0] eq "/usr/sbin/su-to-root") {
+        tag 'su-to-root-with-usr-sbin', $file;
+    } elsif ($com[0] =~ m,^(?:/usr/bin/)?(su-to-root|gksu|kdesu|sux)$,) {
+        my $wrapper = $1;
+        shift @com;
+        while (@com) {
+            unless ($com[0]) {
+                shift @com;
+                next;
+            }
+            if ($com[0] eq '-c') {
+                $cmd = $com[1];
+                last;
+            } elsif ($com[0] =~ /^-[Dfmupi]|^--(user|description|message)/) {
+                shift @com;
+                shift @com;
+            } elsif ($com[0] =~ /^-/) {
+                shift @com;
+            } else {
+                last;
+            }
+        }
+        if (!$cmd && $wrapper =~ /^(gk|kde)su$/) {
+            $cmd = $com[0];
+        } elsif (!$cmd) {
+            tag 'su-wrapper-without--c', "$file $wrapper";
+        }
+    } else {
+        $cmd = $com[0];
+    }
+    my $okay = $cmd and ($file_index{".$cmd"} or grep { $file_index{'.' . $_ . $cmd} } @path);
+    return ($okay, $cmd);
+}
+
 1;
 
 # vim: syntax=perl ts=8 sw=4

Modified: trunk/checks/menu-format.desc
===================================================================
--- trunk/checks/menu-format.desc	2007-08-01 04:57:58 UTC (rev 938)
+++ trunk/checks/menu-format.desc	2007-08-01 10:26:22 UTC (rev 939)
@@ -186,3 +186,57 @@
 Info: The menu item specifies a command which is not available in the package.
  In most cases this is a typo or after you moved a binary around, but forgot
  to update the menu file.
+
+Tag: duplicated-key-in-desktop-entry
+Type: warning
+Info: The desktop entry contains two instances of the same key.  The
+ behavior of such desktop entries is not well-defined by the standard.
+
+Tag: desktop-entry-missing-required-key
+Type: error
+Info: Desktop entries must contain, at a minimum, the keys Type and Name.
+Ref: http://standards.freedesktop.org/desktop-entry-spec/1.0/ar01s05.html
+
+Tag: desktop-entry-contains-unknown-key
+Type: warning
+Info: The key on this line of the desktop entry is not one of the standard
+ keys defined in the FreeDesktop specification, not one of the legacy KDE
+ keywords, and one that does not begin with <tt>X-</tt>.  It's most likely
+ that the key was misspelled.
+Ref: http://standards.freedesktop.org/desktop-entry-spec/1.0/ar01s05.html
+
+Tag: desktop-entry-contains-deprecated-key
+Type: warning
+Info: The key on this line of the desktop entry has been deprecated in the
+ FreeDesktop specification.
+Ref: http://standards.freedesktop.org/desktop-entry-spec/1.0/apc.html
+
+Tag: desktop-entry-contains-encoding-key
+Type: info
+Info: The Encoding key is now deprecated by the FreeDesktop standard and
+ all strings are required to be encoded in UTF-8.  This desktop entry
+ explicitly specifies an Encoding of UTF-8, which is harmless but no
+ longer necessary.
+Ref: http://standards.freedesktop.org/desktop-entry-spec/1.0/apc.html
+
+Tag: desktop-entry-lacks-main-category
+Type: warning
+Info: The categories for this desktop entry do not contain any Main
+ Categories, only Additional Categories.  Additional Categories should
+ only be used on conjunction with one or more Main Categories.
+Ref: http://standards.freedesktop.org/menu-spec/1.0/apa.html
+
+Tag: desktop-entry-invalid-category
+Type: warning
+Info: This desktop entry lists a category that is not one of the
+ registered Main or Additional Categories in the FreeDesktop
+ specification.
+Ref: http://standards.freedesktop.org/menu-spec/1.0/apa.html
+
+Tag: desktop-command-not-in-package
+Type: warning
+Info: The desktop entry specifies a command that is not available in the
+ package.  In most cases, this is a typo or a forgotten update of the
+ desktop file after the install location of the binary was changed.  A
+ desktop file for a command should be included in the same package as that
+ command.

Modified: trunk/debian/changelog
===================================================================
--- trunk/debian/changelog	2007-08-01 04:57:58 UTC (rev 938)
+++ trunk/debian/changelog	2007-08-01 10:26:22 UTC (rev 939)
@@ -28,6 +28,9 @@
       guidance.  Remove the code that allowed for applications to create
       new sub-menus, since the current menu documentation says not to do
       that.  (Closes: #431844)
+    + [RA] Add checks of desktop files.  The checks so far are basic, but
+      do include key and category verification.  Based heavily on work by
+      Javier Fernández-Sanguino Peña.  (Closes: #277441, #433411)
   * checks/scripts{.desc,}:
     + [RA] If rm output is redirected to /dev/null, don't think that rm is
       removing /dev/null.  Thanks, Robert Luberda.  (Closes: #431259)
@@ -63,7 +66,7 @@
   * man/lintian.1:
     + [RA] Remove the mention of the debdiff check script.
 
- -- Russ Allbery <rra@debian.org>  Tue, 31 Jul 2007 21:57:52 -0700
+ -- Russ Allbery <rra@debian.org>  Wed, 01 Aug 2007 03:26:14 -0700
 
 lintian (1.23.32) unstable; urgency=low
 



Reply to: