view netx/net/sourceforge/jnlp/runtime/Boot.java @ 1538:67eb4fc36fa0

Added optional windows desktop integration * AUTHORS: added Joel * Makefile: Excluded (WindowsDesktopEntry.java) if mslinks are not included, added mslinks to UNIFIED_CLASSPATH_SEGMENTS included mslinks to windows and linux runtime libs, added MSLINKS_JAR to other composeclasspath calls * NEWS: mentioned windows desktop support, mentioned listing of cache and operations via id. * acinclude.m4: added check (IT_CHECK_FOR_MSLINKS) for optional mslinks.jar, strong warning printed if build is on windows * configure.ac: call (IT_CHECK_FOR_MSLINKS) * netx/net/sourceforge/jnlp/Launcher.java: new variable of (KEY_JAVAWS_LOCATION) to replace hardcoded icedtea-web.bin.location over netx. * netx/net/sourceforge/jnlp/OptionsDefinitions.java: re-declared clear cache to take none or one argument. Added Xcacheids switch for listing the cache (works with verbose) * netx/net/sourceforge/jnlp/cache/CacheDirectory.java: refactored hardcoded ".info" to constant. * netx/net/sourceforge/jnlp/cache/CacheEntry.java: introduced KEY_JNLP_PATH and used to set jnlp-path attribute * netx/net/sourceforge/jnlp/cache/CacheLRUWrapper.java: hide private constructor, declared and provided (windowsShortcutList) * netx/net/sourceforge/jnlp/cache/CacheUtil.java: extracted and used (checkToClearCache). Added second method clearCache with arg to clear only specific part of cache. Clear cache also alerts windows desktop files now via new removeWindowsShortcuts. Added methods to lists ids and details from cache listCacheIds and getCacheIds. Included new inner class CacheId to encapsualte various types of id - CacheJnlpId and CacheDomainId now. * netx/net/sourceforge/jnlp/cache/DirectoryNode.java: only adapted to .info refactoring * netx/net/sourceforge/jnlp/cache/ResourceDownloader.java: Save main argument, or jnlp argument or html argument to jnlp-path .info entry if found. * netx/net/sourceforge/jnlp/controlpanel/CacheAppViewer.java: gui to itweb-settings cache pane to allow comfortable listing of ids and deleting via those grouping. New file. * netx/net/sourceforge/jnlp/controlpanel/CachePane.java: added logic to show .info details for each file shown by cache viewer. (generateData) made jnlp-path aware, made public and reused several times * netx/net/sourceforge/jnlp/controlpanel/TemporaryInternetFilesPanel.java: added button to show dilog which is deleting by id * netx/net/sourceforge/jnlp/resources/Messages.properties: added BXclearcache BXSingleCacheCleared BXSingleCacheClearNotFound BXSingleCacheMoreThenOneId BXSingleCacheFileCount BXcacheids NOAnonorone WinDesktopError. Modified BXclearcache. Improved EXAWdesktopWants EXAWdesktopDontWants EXAWsubmenu EXAWmenuWants EXAWmenuDontWants EXAWrememberByApp EXAWrememberByPage EXAWrememberByAppTooltip EXAWbrowsersTolltip SDesktopShortcut * netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java: added if isWindows reflective calls to WindowsDesktopEntry and original XdesktopEntry work moved to else part. * netx/net/sourceforge/jnlp/runtime/Boot.java: now offer getter for optionParser (so it van be reused in ResourceDownloader) Added understanding to Xcacheids. Understanding to Xclearcache adapted to its new optional argument * netx/net/sourceforge/jnlp/util/GenericDesktopEntry.java: interface for (future) WindowsDesktopEntry and XDesktopEntry unification * netx/net/sourceforge/jnlp/util/WindowsDesktopEntry.java: new file, implementation fo windos desktop integration via lnk files generated by mslinks.jar. Unlike XDesktop integration, it swarms also uninstall shortcuts. * netx/net/sourceforge/jnlp/util/XDesktopEntry.java: Mostly adapted to refactorings. Extracted extraction of favicon to method, reused, and improved to try more locations on server. * netx/net/sourceforge/jnlp/util/optionparser/OptionParser.java: adapted to refactorings * shell-launcher/launchers.bat.in: mslinks included in bootclasspath * tests/netx/unit/net/sourceforge/jnlp/cache/CacheUtilTest.java: addd tests for CacheId * tests/netx/unit/net/sourceforge/jnlp/util/XDesktopEntryTest.java: Added tests for favicon extracti
author Jiri Vanek <jvanek@redhat.com>
date Tue, 15 Jan 2019 16:12:46 +0100
parents b99a42be9966
children 3a3e13df995c
line wrap: on
line source

// 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.runtime;

import java.io.File;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.UIManager;
import net.sourceforge.jnlp.LaunchException;
import net.sourceforge.jnlp.OptionsDefinitions;
import net.sourceforge.jnlp.ParserSettings;
import net.sourceforge.jnlp.PropertyDesc;
import net.sourceforge.jnlp.about.AboutDialog;
import net.sourceforge.jnlp.cache.CacheUtil;
import net.sourceforge.jnlp.cache.UpdatePolicy;
import net.sourceforge.jnlp.config.DeploymentConfiguration;
import net.sourceforge.jnlp.security.viewer.CertificateViewer;
import net.sourceforge.jnlp.services.ServiceUtil;
import net.sourceforge.jnlp.util.docprovider.IcedTeaWebTextsProvider;
import net.sourceforge.jnlp.util.docprovider.JavaWsTextsProvider;
import net.sourceforge.jnlp.util.docprovider.TextsProvider;
import net.sourceforge.jnlp.util.docprovider.formatters.formatters.PlainTextFormatter;
import net.sourceforge.jnlp.util.logging.OutputController;
import net.sourceforge.jnlp.util.optionparser.InvalidArgumentException;
import net.sourceforge.jnlp.util.optionparser.OptionParser;
import net.sourceforge.jnlp.util.optionparser.UnevenParameterException;
import sun.awt.AppContext;
import sun.awt.SunToolkit;

import static net.sourceforge.jnlp.runtime.Translator.R;
import net.sourceforge.jnlp.runtime.html.browser.LinkingBrowser;
import net.sourceforge.swing.SwingUtils;

/**
 * This is the main entry point for the JNLP client. The main method parses the
 * command line parameters and loads a JNLP file into the secure runtime
 * environment. This class is meant to be called from the command line or file
 * association; to initialize the netx engine from other code invoke the
 * {@link JNLPRuntime#initialize} method after configuring the runtime.
 *
 * @author <a href="mailto:jmaxwell@users.sourceforge.net">Jon A. Maxwell
 * (JAM)</a> - initial author
 * @version $Revision: 1.21 $
 */
public final class Boot implements PrivilegedAction<Void> {

    // todo: decide whether a spawned netx (external launch)
    // should inherit the same options as this instance (store argv?)
    public static final String name = Boot.class.getPackage().getImplementationTitle();
    public static final String version = Boot.class.getPackage().getImplementationVersion();

    private static final String nameAndVersion = name + " " + version;

    private static final String miniLicense = "\n"
            + "   netx - an open-source JNLP client.\n"
            + "   Copyright (C) 2001-2003 Jon A. Maxwell (JAM)\n"
            + "\n"
            + "   // This library is free software; you can redistribute it and/or\n"
            + "   modify it under the terms of the GNU Lesser General Public\n"
            + "   License as published by the Free Software Foundation; either\n"
            + "   version 2.1 of the License, or (at your option) any later version.\n"
            + "\n"
            + "   This library is distributed in the hope that it will be useful,\n"
            + "   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
            + "   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
            + "   Lesser General Public License for more details.\n"
            + "\n"
            + "   You should have received a copy of the GNU Lesser General Public\n"
            + "   License along with this library; if not, write to the Free Software\n"
            + "   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n"
            + "\n";

    private static OptionParser optionParser;

    public static OptionParser getOptionParser() {
        return optionParser;
    }
    
    

    /**
     * Launch the JNLP file specified by the command-line arguments.
     *
     * @param argsIn launching arguments
     */
    public static void main(String[] argsIn) throws UnevenParameterException {
        // setup Swing EDT tracing:
        SwingUtils.setup();

        optionParser = new OptionParser(argsIn, OptionsDefinitions.getJavaWsOptions());

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.VERBOSE)) {
            JNLPRuntime.setDebug(true);
        }

        if (AppContext.getAppContext() == null) {
            SunToolkit.createNewAppContext();
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.HEADLESS)) {
            JNLPRuntime.setHeadless(true);
        }

        DeploymentConfiguration.move14AndOlderFilesTo15StructureCatched();

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.VIEWER)) {
            try {
                CertificateViewer.main(null);
            } catch (Exception e) {
                OutputController.getLogger().log(OutputController.Level.ERROR_ALL, e);
            } finally {
                //no matter what happens, terminate
                return;
            }
        }

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.VERSION)) {
            OutputController.getLogger().printOutLn(nameAndVersion);
            JNLPRuntime.exit(0);
        }

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.LICENSE)) {
            OutputController.getLogger().printOutLn(miniLicense);
            JNLPRuntime.exit(0);
        }

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.HELP1)) {
            handleMessage();
            JNLPRuntime.exit(0);
        }
        List<String> properties = optionParser.getParams(OptionsDefinitions.OPTIONS.PROPERTY);
        if (properties != null) {
            for (String prop : properties) {
                try {
                    PropertyDesc propDesc = PropertyDesc.fromString(prop);
                    JNLPRuntime.getConfiguration().setProperty(propDesc.getKey(), propDesc.getValue());
                } catch (LaunchException ex) {
                    OutputController.getLogger().log(ex);
                }
            }
        }

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.ABOUT)) {
            handleAbout();
            if (JNLPRuntime.isHeadless()) {
                JNLPRuntime.exit(0);
            } else {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception e) {
                    OutputController.getLogger().log("Unable to set system look and feel");
                }
                OutputController.getLogger().printOutLn(R("BLaunchAbout"));
                AboutDialog.display(TextsProvider.JAVAWS);
                return;
            }
        }

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.UPDATE)) {
            int value = Integer.parseInt(optionParser.getParam(OptionsDefinitions.OPTIONS.UPDATE));
            JNLPRuntime.setDefaultUpdatePolicy(new UpdatePolicy(value * 1000l));
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.NOUPDATE)) {
            JNLPRuntime.setDefaultUpdatePolicy(UpdatePolicy.NEVER);
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.NOFORK)) {
            JNLPRuntime.setForksAllowed(false);
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.TRUSTALL)) {
            JNLPRuntime.setTrustAll(true);
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.HTML)) {
            JNLPRuntime.setHtml(true);
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.TRUSTNONE)) {
            JNLPRuntime.setTrustNone(true);
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.NOHEADERS)) {
            JNLPRuntime.setIgnoreHeaders(true);
        }
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.REDIRECT)) {
            JNLPRuntime.setAllowRedirect(true);
        }

        //if it is browser go by ots own, otherwise procedd with normal ITW logic
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.BROWSER)) {
            String url = optionParser.getParam(OptionsDefinitions.OPTIONS.BROWSER);
            LinkingBrowser.showStandAloneWindow(url, false);
        } else {

            JNLPRuntime.setInitialArgments(Arrays.asList(argsIn));

            AccessController.doPrivileged(new Boot());
        }

    }

    private static void handleMessage() {
        final TextsProvider helpMessagesProvider = new JavaWsTextsProvider("utf-8", new PlainTextFormatter(), true, true);

        String helpMessage = "\n";
        if (JNLPRuntime.isDebug()) {
            helpMessage += helpMessagesProvider.writeToString();
        } else {
            helpMessage = helpMessage
                    + helpMessagesProvider.prepare().getSynopsis()
                    + helpMessagesProvider.getFormatter().getNewLine()
                    + helpMessagesProvider.prepare().getOptions()
                    + helpMessagesProvider.getFormatter().getNewLine();
        }

        OutputController.getLogger().printOut(helpMessage);
    }

    private static void handleAbout() {
        final TextsProvider aboutMessagesProvider = new IcedTeaWebTextsProvider("utf-8", new PlainTextFormatter(), false, true);
        String itwInfoMessage = ""
                + nameAndVersion
                + "\n\n";

        if (JNLPRuntime.isDebug()) {
            itwInfoMessage += aboutMessagesProvider.writeToString();
        } else {
            itwInfoMessage = itwInfoMessage
                    + aboutMessagesProvider.prepare().getIntroduction();
        }
        OutputController.getLogger().printOut(itwInfoMessage);
    }

    static String fixJnlpProtocol(String param) {
        //remove jnlp: for case like jnlp:https://some.app/file.jnlp
        if (param.matches("^jnlp[s]?:.*://.*")) {
            param = param.replaceFirst("^jnlp[s]?:", "");
        }
        //transalte jnlp://some.app/file.jnlp to http/https
        return param.replaceFirst("^jnlp:", "http:").replaceFirst("^jnlps:", "https:");
    }

    /**
     * The privileged part (jdk1.3 compatibility).
     */
    @Override
    public Void run() {

        Map<String, List<String>> extra = new HashMap<>();

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.HTML)) {
            boolean run = new HtmlBoot(optionParser).run(extra);
            if (!run) {
                return null;
            }
        } else {
            boolean run = new JnlpBoot(optionParser).run(extra);
            if (!run) {
                return null;
            }
        }
        return null;
    }

    static void fatalError(String message) {
        OutputController.getLogger().log(OutputController.Level.ERROR_ALL, "netx: " + message);
        JNLPRuntime.exit(1);
    }

    /**
     * Returns the url of file to open; does not return if no file was
     * specified, or if the file location was invalid.
     */
    static URL getFileLocation() {

        String location = null;
        try {
            location = getMainFile();
        } catch (InvalidArgumentException e) {
            OutputController.getLogger().log(e);
            fatalError("Invalid argument: " + e);
        }

        if (location == null) {
            handleMessage();
            JNLPRuntime.exit(1);
        }

        OutputController.getLogger().log(R("BFileLoc") + ": " + location);

        URL url = null;

        try {
            if (new File(location).exists()) // TODO: Should be toURI().toURL()
            {
                url = new File(location).toURL(); // Why use file.getCanonicalFile?
            } else if (ServiceUtil.getBasicService() != null) {
                OutputController.getLogger().log("Warning, null basicService");
                url = new URL(ServiceUtil.getBasicService().getCodeBase(), location);
            } else {
                url = new URL(location);
            }
        } catch (Exception e) {
            OutputController.getLogger().log(e);
            fatalError("Invalid jnlp file " + location);
        }

        return url;
    }

    /**
     * Gets the JNLP file from the command line arguments, or exits upon error.
     */
    private static String getMainFile() throws InvalidArgumentException {
        if (optionParser.getMainArgs().size() > 1
                || (optionParser.mainArgExists() && optionParser.hasOption(OptionsDefinitions.OPTIONS.JNLP))
                || (optionParser.mainArgExists() && optionParser.hasOption(OptionsDefinitions.OPTIONS.HTML))
                || (optionParser.hasOption(OptionsDefinitions.OPTIONS.JNLP) && optionParser.hasOption(OptionsDefinitions.OPTIONS.HTML))) {
            throw new InvalidArgumentException(optionParser.getMainArgs().toString());
        } else if (optionParser.hasOption(OptionsDefinitions.OPTIONS.JNLP)) {
            return fixJnlpProtocol(optionParser.getParam(OptionsDefinitions.OPTIONS.JNLP));
        } else if (optionParser.hasOption(OptionsDefinitions.OPTIONS.HTML)) {
            return optionParser.getParam(OptionsDefinitions.OPTIONS.HTML);
        } else if (optionParser.mainArgExists()) {
            return fixJnlpProtocol(optionParser.getMainArg());
        }

        handleMessage();
        JNLPRuntime.exit(0);
        return null;
    }

    static ParserSettings init(Map<String, List<String>> extra) {
        JNLPRuntime.setSecurityEnabled(!optionParser.hasOption(OptionsDefinitions.OPTIONS.NOSEC));
        JNLPRuntime.setOfflineForced(optionParser.hasOption(OptionsDefinitions.OPTIONS.OFFLINE));
        JNLPRuntime.initialize(true);

        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.LISTCACHEIDS)) {
            List<String> optionArgs = optionParser.getMainArgs();
            if (optionArgs.size() > 0) {
                //clear one app 
                CacheUtil.listCacheIds(optionArgs.get(0));
            } else {
                // clear all cache
                CacheUtil.listCacheIds(".*");
            }
            return null;
        }

        /*
         * FIXME
         * This should have been done with the rest of the argument parsing
         * code. But we need to know what the cache and base directories are,
         * and baseDir is initialized here
         */
        if (optionParser.hasOption(OptionsDefinitions.OPTIONS.CLEARCACHE)) {
            List<String> optionArgs = optionParser.getMainArgs();
            if (optionArgs.size() > 0) {
                //clear one app 
                CacheUtil.clearCache(optionArgs.get(0));
            } else {
                // clear all cache
                CacheUtil.clearCache();
            }
            return null;
        }

        extra.put("arguments", optionParser.getParams(OptionsDefinitions.OPTIONS.ARG));
        extra.put("parameters", optionParser.getParams(OptionsDefinitions.OPTIONS.PARAM));
        extra.put("properties", optionParser.getParams(OptionsDefinitions.OPTIONS.PROPERTY));

        return ParserSettings.setGlobalParserSettingsFromOptionParser(optionParser);
    }

}