changeset 1950:03e2ef9b5c6b

Netx: add support for desktop shortcuts 2009-07-29 Omair Majid <omajid@redhat.com> * netx/net/sourceforge/jnlp/IconDesc.java: Add new icon kind SHORTCUT. * netx/net/sourceforge/jnlp/Launcher.java: Move StreamEater class to ... * netx/net/sourceforge/jnlp/StreamEater.java: New file. * netx/net/sourceforge/jnlp/resources/Messages.properties: Add SDesktopShortcut. * netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java (initialize): Call addMenuAndDesktopEntries. (addMenuAndDesktopEntries): New function. * netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java: Add CREATE_DESKTOP_SHORTCUT to AccessType. * netx/net/sourceforge/jnlp/security/AccessWarningPane.java (installComponents): Add a case for CREATE_DESKTOP_SHORTCUT * netx/net/sourceforge/jnlp/services/ServiceUtil.java Remove duplicate enum AccessType. (checkAccess): New function. (checkAccess): Add a new argument app. * netx/net/sourceforge/jnlp/services/SingleInstanceLock.java (getLockFileName): Call FileUtils.sanitizeFileName to sanitize the filename. * netx/net/sourceforge/jnlp/util/FileUtils.java: New file. * netx/net/sourceforge/jnlp/util/XDesktopEntry.java: New file.
author Omair Majid <omajid@redhat.com>
date Wed, 29 Jul 2009 10:57:38 -0400
parents 324662e5e611
children 88c498641265
files ChangeLog netx/net/sourceforge/jnlp/IconDesc.java netx/net/sourceforge/jnlp/Launcher.java netx/net/sourceforge/jnlp/StreamEater.java netx/net/sourceforge/jnlp/resources/Messages.properties netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java netx/net/sourceforge/jnlp/security/AccessWarningPane.java netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java netx/net/sourceforge/jnlp/services/ServiceUtil.java netx/net/sourceforge/jnlp/services/SingleInstanceLock.java netx/net/sourceforge/jnlp/util/FileUtils.java netx/net/sourceforge/jnlp/util/XDesktopEntry.java
diffstat 12 files changed, 386 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jul 29 10:48:43 2009 -0400
+++ b/ChangeLog	Wed Jul 29 10:57:38 2009 -0400
@@ -1,3 +1,27 @@
+2009-07-29  Omair Majid  <omajid@redhat.com>
+
+	* netx/net/sourceforge/jnlp/IconDesc.java: Add new icon kind SHORTCUT.
+	* netx/net/sourceforge/jnlp/Launcher.java: Move StreamEater class to ...
+	* netx/net/sourceforge/jnlp/StreamEater.java: New file.
+	* netx/net/sourceforge/jnlp/resources/Messages.properties: Add
+	SDesktopShortcut.
+	* netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java
+	(initialize): Call addMenuAndDesktopEntries.
+	(addMenuAndDesktopEntries): New function.
+	* netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java: Add
+	CREATE_DESKTOP_SHORTCUT to AccessType.
+	* netx/net/sourceforge/jnlp/security/AccessWarningPane.java
+	(installComponents): Add a case for CREATE_DESKTOP_SHORTCUT
+	* netx/net/sourceforge/jnlp/services/ServiceUtil.java
+	Remove duplicate enum AccessType.
+	(checkAccess): New function.
+	(checkAccess): Add a new argument app.
+	* netx/net/sourceforge/jnlp/services/SingleInstanceLock.java
+	(getLockFileName): Call FileUtils.sanitizeFileName to sanitize the 
+	filename.
+	* netx/net/sourceforge/jnlp/util/FileUtils.java: New file.
+	* netx/net/sourceforge/jnlp/util/XDesktopEntry.java: New file. 
+
 2009-07-29  Omair Majid  <omajid@redhat.com>
 
 	* netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java
--- a/netx/net/sourceforge/jnlp/IconDesc.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/IconDesc.java	Wed Jul 29 10:57:38 2009 -0400
@@ -43,6 +43,9 @@
 
     /** splash icon */
     public static final Object SPLASH = "splash";
+    
+    /** destop shortcut icon */
+    public static final Object SHORTCUT = "shortcut";
 
 
     /** the location of the icon */
--- a/netx/net/sourceforge/jnlp/Launcher.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/Launcher.java	Wed Jul 29 10:57:38 2009 -0400
@@ -770,32 +770,6 @@
         }
     };
 
-    /**
-     * This class reads the output from a launched process and
-     * writes it to stdout.
-     */
-    private static class StreamEater extends Thread {
-        private InputStream stream;
-
-        StreamEater(InputStream stream) {
-            this.stream = new BufferedInputStream(stream);
-        }
-
-        public void run() {
-            try {
-                while (true) {
-                    int c = stream.read();
-                    if (c == -1)
-                        break;
-
-                    System.out.write(c);
-                }
-            }
-            catch (IOException ex) {
-            }
-        }
-    };
-
 }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/StreamEater.java	Wed Jul 29 10:57:38 2009 -0400
@@ -0,0 +1,45 @@
+// Copyright (C) 2001-2003 Jon A. Maxwell (JAM)
+// 
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+// 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+package net.sourceforge.jnlp;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * This class reads the output from a launched process and writes it to stdout.
+ */
+public class StreamEater extends Thread {
+    private InputStream stream;
+
+    public StreamEater(InputStream stream) {
+        this.stream = new BufferedInputStream(stream);
+    }
+
+    public void run() {
+        try {
+            while (true) {
+                int c = stream.read();
+                if (c == -1)
+                    break;
+
+                System.out.write(c);
+            }
+        } catch (IOException ex) {
+        }
+    }
+}
--- a/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/resources/Messages.properties	Wed Jul 29 10:57:38 2009 -0400
@@ -147,6 +147,7 @@
 # Security
 SFileReadAccess=The application has requested read access to a file on the machine. Do you want to allow this action?
 SFileWriteAccess=The application has requested write access to a file on the machine. Do you want to allow this action?
+SDesktopShortcut=The application has requested permission to create a desktop launcher. Do you want to allow this action?
 SSigUnverified=The application's digital signature cannot be verified. Do you want to run the application?
 SSigVerified=The application's digital signature has been verified. Do you want to run the application?
 SSignatureError=The application's digital signature has an error. Do you want to run the application?
--- a/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java	Wed Jul 29 10:57:38 2009 -0400
@@ -25,6 +25,8 @@
 
 import net.sourceforge.jnlp.*;
 import net.sourceforge.jnlp.event.*;
+import net.sourceforge.jnlp.security.SecurityWarningDialog.AccessType;
+import net.sourceforge.jnlp.services.ServiceUtil;
 import net.sourceforge.jnlp.util.*;
 
 /**
@@ -107,6 +109,32 @@
      */
     public void initialize() {
         installEnvironment();
+        addMenuAndDesktopEntries();
+    }
+
+    /**
+     * Creates menu and desktop entries if required by the jnlp file
+     */
+
+    private void addMenuAndDesktopEntries() {
+        XDesktopEntry entry = new XDesktopEntry(file);
+
+        if (file.getInformation().getShortcut().onDesktop()) {
+            if (ServiceUtil.checkAccess(this, AccessType.CREATE_DESTKOP_SHORTCUT)) {
+                entry.createDesktopShortcut();
+            }
+        }
+
+        if (file.getInformation().getShortcut().getMenu() != null) {
+            /*
+             * Sun's WebStart implementation doesnt seem to do anything under GNOME
+             */
+            if (JNLPRuntime.isDebug()) {
+                System.err.println("ApplicationInstance.addMenuAndDesktopEntries():"
+                        + " Adding menu entries NOT IMPLEMENTED");
+            }
+        }
+
     }
 
     /**
--- a/netx/net/sourceforge/jnlp/security/AccessWarningPane.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/security/AccessWarningPane.java	Wed Jul 29 10:57:38 2009 -0400
@@ -121,6 +121,9 @@
 			case WRITE_FILE:
 				topLabelText = R("SFileWriteAccess");
 				break;
+			case CREATE_DESTKOP_SHORTCUT:
+			    topLabelText = R("SDesktopShortcut");
+			    break;
 			case CLIPBOARD_READ:
 				topLabelText = R("SClipboardReadAccess");
 				break;
--- a/netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java	Wed Jul 29 10:57:38 2009 -0400
@@ -70,6 +70,7 @@
 	public static enum AccessType {
         READ_FILE,
         WRITE_FILE,
+        CREATE_DESTKOP_SHORTCUT,
         CLIPBOARD_READ,
         CLIPBOARD_WRITE,
         PRINTER,
--- a/netx/net/sourceforge/jnlp/services/ServiceUtil.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/services/ServiceUtil.java	Wed Jul 29 10:57:38 2009 -0400
@@ -57,13 +57,6 @@
     }
 
     /**
-     * Types of system access that may need permissions.
-     */
-    public static enum AccessType {
-        READ_FILE, WRITE_FILE, CLIPBOARD_READ, CLIPBOARD_WRITE, PRINTER
-    }
-
-    /**
      * Returns the BasicService reference, or null if the service is
      * unavailable.
      */
@@ -227,9 +220,29 @@
      * @return true if the access was granted, false otherwise.
      */
     public static boolean checkAccess(SecurityWarningDialog.AccessType type,
+            Object... extras) {
+        return checkAccess(null, type, extras);
+    }    
+    
+    /**
+     * Returns whether the app requesting a service is signed. If the app is
+     * unsigned, the user is prompted with a dialog asking if the action
+     * should be allowed.
+     * @param app the application which is requesting the check. If null, the current
+     * application is used.
+     * @param type the type of access being requested
+     * @param extras extra Strings (usually) that are passed to the dialog for
+     * message formatting.
+     * @return true if the access was granted, false otherwise.
+     */
+    public static boolean checkAccess(ApplicationInstance app, 
+            SecurityWarningDialog.AccessType type,
     		Object... extras) {
 
-        ApplicationInstance app = JNLPRuntime.getApplication();
+        if (app == null) {
+            app = JNLPRuntime.getApplication();
+        }
+        
         if (app != null) {
             if (!app.isSigned()) {
             	final SecurityWarningDialog.AccessType tmpType = type;
--- a/netx/net/sourceforge/jnlp/services/SingleInstanceLock.java	Wed Jul 29 10:48:43 2009 -0400
+++ b/netx/net/sourceforge/jnlp/services/SingleInstanceLock.java	Wed Jul 29 10:57:38 2009 -0400
@@ -27,6 +27,7 @@
 
 import net.sourceforge.jnlp.JNLPFile;
 import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.FileUtils;
 
 /**
  * This class represents a Lock for single instance jnlp applications
@@ -152,17 +153,7 @@
         }
         
         initialName = initialName + getCurrentDisplay();
-        String encodedName;
-
-        /*
-         * FIXME
-         * 
-         * Assuming safe characters are 'a-z','A-Z','0-9', '_', '.'
-         */
-
-        encodedName = initialName.replaceAll("[^a-zA-Z0-9.]", "_");
-
-        return encodedName;
+        return FileUtils.sanitizeFileName(initialName);
 
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/FileUtils.java	Wed Jul 29 10:57:38 2009 -0400
@@ -0,0 +1,48 @@
+// Copyright (C) 2009 Red Hat, Inc.
+// 
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+// 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+
+package net.sourceforge.jnlp.util;
+
+/**
+ * This class contains a few file-related utility functions. 
+ * 
+ * @author Omair Majid
+ */
+
+public class FileUtils {
+
+    
+    /**
+     * Given an input, return a sanitized form of the input suitable for use as
+     * a file/directory name
+     * 
+     * @param input
+     * @return a sanitized version of the input
+     */
+    public static String sanitizeFileName(String input) {
+
+        /*
+         * FIXME
+         * 
+         * Assuming safe characters are 'a-z','A-Z','0-9', '_', '.'
+         */
+
+        String sanitizedName = input.replaceAll("[^a-zA-Z0-9.]", "_");
+        return sanitizedName;
+    }
+    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/netx/net/sourceforge/jnlp/util/XDesktopEntry.java	Wed Jul 29 10:57:38 2009 -0400
@@ -0,0 +1,210 @@
+// Copyright (C) 2009 Red Hat, Inc.
+// 
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// 
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+// 
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+package net.sourceforge.jnlp.util;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import net.sourceforge.jnlp.IconDesc;
+import net.sourceforge.jnlp.JNLPFile;
+import net.sourceforge.jnlp.StreamEater;
+import net.sourceforge.jnlp.cache.CacheUtil;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+
+/**
+ * This class builds a (freedesktop.org) desktop entry out of a {@link JNLPFile}
+ * . This entry can be used to install desktop shortcuts. See xdg-desktop-icon
+ * (1) and http://standards.freedesktop.org/desktop-entry-spec/latest/ for more
+ * information
+ * 
+ * @author Omair Majid
+ * 
+ */
+public class XDesktopEntry {
+
+    public static final String JAVA_ICON_NAME = "java.png";
+    
+    private JNLPFile file = null;
+    private int iconSize = -1;
+    private String iconLocation = null;
+
+    private int[] VALID_ICON_SIZES = new int[] { 16, 22, 32, 48, 64, 128 };
+
+    /**
+     * Create a XDesktopEntry for the given JNLP file
+     * 
+     * @param file a {@link JNLPFile} that indicates the application to launch
+     */
+    public XDesktopEntry(JNLPFile file) {
+        this.file = file;
+
+        /* looks like a good initial value */
+        iconSize = VALID_ICON_SIZES[2];
+    }
+
+    /**
+     * Returns the contents of the {@link XDesktopEntry} through the
+     * {@link Reader} interface.
+     */
+    public Reader getContentsAsReader() {
+
+        String pathToJavaws = System.getProperty("java.home") + File.separator + "bin"
+                + File.separator + "javaws";
+
+        String fileContents = "[Desktop Entry]\n";
+        fileContents += "Version=1.0\n";
+        fileContents += "Name=" + file.getTitle() + "\n";
+        fileContents += "GenericName=Java Web Start Application\n";
+        fileContents += "Comment=" + file.getInformation().getDescription() + "\n";
+        fileContents += "Type=Application\n";
+        if (iconLocation != null) {
+            fileContents += "Icon=" + iconLocation + "\n";
+        } else {
+            fileContents += "Icon=" + JAVA_ICON_NAME + "\n";
+            
+        }
+        if (file.getInformation().getVendor() != null) {
+            fileContents += "Vendor=" + file.getInformation().getVendor() + "\n";
+        }
+        fileContents += "Exec=" + pathToJavaws + " \"" + file.getSourceLocation() + "\"\n";
+
+        return new StringReader(fileContents);
+
+    }
+
+    /**
+     * Get the size of the icon (in pixels) for the desktop shortcut
+     */
+    public int getIconSize() {
+        return iconSize;
+    }
+
+    /**
+     * Set the icon size to use for the desktop shortcut
+     * 
+     * @param size the size (in pixels) of the icon to use. Commonly used sizes
+     *        are of 16, 22, 32, 48, 64 and 128
+     */
+    public void setIconSize(int size) {
+        iconSize = size;
+    }
+
+    /**
+     * Create a desktop shortcut for this desktop entry
+     */
+    public void createDesktopShortcut() {
+        try {
+            cacheIcon();
+            installDesktopLauncher();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Install this XDesktopEntry into the user's desktop as a launcher
+     */
+    private void installDesktopLauncher() {
+        File shortcutFile = new File(JNLPRuntime.TMP_DIR + File.separator
+                + FileUtils.sanitizeFileName(file.getTitle()) + ".desktop");
+        try {
+
+            /*
+             * Write out a Java String (UTF-16) as a UTF-8 file
+             */
+
+            OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(shortcutFile),
+                    Charset.forName("UTF-8"));
+            Reader reader = getContentsAsReader();
+
+            char[] buffer = new char[1024];
+            int ret = 0;
+            while (-1 != (ret = reader.read(buffer))) {
+                writer.write(buffer, 0, ret);
+            }
+
+            reader.close();
+            writer.close();
+
+            /*
+             * Install the desktop entry
+             */
+
+            String[] execString = new String[] { "xdg-desktop-icon", "install", "--novendor",
+                    shortcutFile.getCanonicalPath() };
+            if (JNLPRuntime.isDebug()) {
+                System.err.println("Execing: " + Arrays.toString(execString));
+            }
+            Process installer = Runtime.getRuntime().exec(execString);
+            new StreamEater(installer.getInputStream()).start();
+            new StreamEater(installer.getErrorStream()).start();
+
+            try {
+                installer.waitFor();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+
+            if (!shortcutFile.delete()) {
+                throw new IOException("Unable to delete temporary file:" + shortcutFile);
+            }
+
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Cache the icon for the desktop entry
+     */
+    private void cacheIcon() {
+
+        URL iconLocation = file.getInformation().getIconLocation(IconDesc.SHORTCUT, iconSize,
+                iconSize);
+
+        if (iconLocation == null) {
+            iconLocation = file.getInformation().getIconLocation(IconDesc.DEFAULT, iconSize,
+                    iconSize);
+        }
+
+        if (iconLocation != null) {
+            String location = CacheUtil.getCachedResource(iconLocation, null, UpdatePolicy.SESSION)
+                    .toString();
+            if (!location.startsWith("file:")) {
+                throw new RuntimeException("Unable to cache icon");
+            }
+
+            this.iconLocation = location.substring("file:".length());
+
+            if (JNLPRuntime.isDebug()) {
+                System.err.println("Cached desktop shortcut icon: " + this.iconLocation);
+            }
+        }
+    }
+
+}