Package com.threerings.getdown.launcher

Source Code of com.threerings.getdown.launcher.GetdownAppletConfig

//
// Getdown - application installer, patcher and launcher
// Copyright (C) 2004-2014 Three Rings Design, Inc.
// https://raw.github.com/threerings/getdown/master/LICENSE

package com.threerings.getdown.launcher;

import java.awt.Color;
import java.awt.Rectangle;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.swing.JApplet;

import com.samskivert.util.RunAnywhere;
import com.samskivert.util.StringUtil;
import com.threerings.getdown.data.Application;
import com.threerings.getdown.launcher.ImageLoader;
import com.threerings.getdown.launcher.RotatingBackgrounds;
import com.threerings.getdown.util.ConfigUtil;

import static com.threerings.getdown.Log.log;

public class GetdownAppletConfig
{
    public static final String APPBASE = "appbase";
    public static final String APPNAME = "appname";
    public static final String BGIMAGE = "bgimage";
    public static final String ERRORBGIMAGE = "errorbgimage";
    public static final String PARAM_DELIMITER = ".";
    public static final String DIRECT = "direct";
    public static final String CONNECT = "connect";
    public static final String REDIRECT_ON_FINISH = "redirect_on_finish";
    public static final String REDIRECT_ON_FINISH_TARGET = "redirect_on_finish_target";

    /** Used to specify the names of additional jvmargs properties. Each jvmarg property will
     * be suffixed by a monotonically increasing integer starting at zero, e.g.
     * <pre>{@code
     * <param name="jvmarg0" value="-Xmx256M"/>
     * <param name="jvmarg1" value="-Dfoo=bar"/>
     * }</pre>
     * When a number is reached for which no value exists, we stop looking. */
    public static final String JVMARG_PREFIX = "jvmargs";

    /** A property specifying the names of additional appargs properties. Each apparg property will
     * be suffixed by a monotonically increasing integer starting at zero, e.g.
     * <pre>{@code
     * <param name="apparg0" value="my awesome value"/>
     * <param name="apparg1" value="monkeys"/>
     * }</pre>
     * When a number is reached for which no value exists, we stop looking. */
    public static final String APPARG_PREFIX = "appargs";

    public String appbase;

    public String appname;

    /** The list of background images and their display time */
    public String imgpath;

    /** The error background image */
    public String errorimgpath;

    public File appdir;

    /** Additional jvmargs to supply to the launching app. */
    public String[] jvmargs;

    /** Additional appargs to supply to the launching app. */
    public String[] appargs;

    /** An optional URL to which the applet should redirect when done. */
    public URL redirectUrl;

    public String redirectTarget;

    public String installerFileContents;

    /** Indicates whether the downloaded app should be launched in the parent applet (true) or as a
     * separate java process (false). */
    public boolean invokeDirect;

    /** Indicates whether Getdown should allow the launched app to connect to it through a server
     * socket bound to localhost on any available port in order to allow interaction with
     * JavaScript code on the page containing the applet. */
    public boolean allowConnect;

    /** Optional default bounds for the status panel. */
    public Rectangle statusBounds;

    public Color statusColor;

    public GetdownAppletConfig (JApplet applet)
    {
        this(applet, null);
    }

    public GetdownAppletConfig (JApplet applet, String paramPrefix)
    {
        _applet = applet;
        _prefix = paramPrefix;

        appbase = getParameter(APPBASE, "");
        appname = getParameter(APPNAME, "");
        log.info("App Base: " + appbase);
        log.info("App Name: " + appname);

        imgpath = getParameter(BGIMAGE);
        errorimgpath = getParameter(ERRORBGIMAGE);

        // DEPRECATED LEGACY CRAP: extract system properties to set in applet JVM (not app JVM)
        String props = getParameter("properties");
        if (props != null) {
            String[] proparray = props.split(" ");
            for (String property : proparray) {
                String key = property.substring(property.indexOf("-D") + 2, property.indexOf("="));
                String value = property.substring(property.indexOf("=") + 1);
                _properties.setProperty(key, value);
            }
        }
        // END DLC

        String root;
        if (RunAnywhere.isWindows()) {
            root = "Application Data";
            String verStr = System.getProperty("os.version");
            try {
                if (Float.parseFloat(verStr) >= 6.0f) {
                    // Vista makes us write it here.... Yay.
                    root = "AppData" + File.separator + "LocalLow";
                }
            } catch (Exception e) {
                log.warning("Couldn't parse OS version", "vers", verStr, "error", e);
            }
        } else if (RunAnywhere.isMacOS()) {
            root = "Library" + File.separator + "Application Support";
        } else /* isLinux() or something wacky */{
            root = ".getdown";
        }
        appdir = new File(System.getProperty("user.home") + File.separator + root + File.separator
            + appname);

        installerFileContents = getParameter("installer");

        // extract arguments to be added to getdown.txt's jvmargs
        List<String> jvmalist = parseArgList(JVMARG_PREFIX);
        // DEPRECATED LEGACY CRAP: extract system properties to set in app JVM
        String appprops = getParameter("app_properties");
        if (appprops != null) {
            for (String jvmarg : appprops.split(",")) {
                jvmalist.add("-D" + jvmarg);
            }
        }
        // END DLC
        jvmargs = jvmalist.toArray(new String[jvmalist.size()]);

        // extract arguments to be added to getdown.txt's appargs
        List<String> appalist = parseArgList(APPARG_PREFIX);
        appargs = appalist.toArray(new String[appalist.size()]);

        String direct = getParameter(DIRECT, "false");
        invokeDirect = Boolean.valueOf(direct);

        String connect = getParameter(CONNECT, "false");
        allowConnect = Boolean.valueOf(connect);

        String redirectURL = getParameter(REDIRECT_ON_FINISH);
        if (redirectURL != null) {
            try {
                redirectUrl = new URL(redirectURL);
                redirectTarget = getParameter(REDIRECT_ON_FINISH_TARGET);
            } catch (MalformedURLException e) {
                log.warning("URL in " + REDIRECT_ON_FINISH + " param is malformed: " + e);
            }
        }

        // This allows us to configure the status panel from applet parameters in case something
        // goes horribly wrong before we get a chance to read getdown.txt (like when the user
        // rejects write permission for the applet)
        statusBounds = Application.parseRect("ui.status", getParameter("ui.status"));
        statusColor = Application.parseColor(getParameter("ui.status_text"));
    }

    /**
     * Does all the fiddly initialization of Getdown and throws an exception if something goes
     * horribly wrong.
     */
    public void init () throws Exception
    {
        securityCheck();
        setSystemProperties();
        ensureAppdirExists();
        createFiles();

        // record a few things for posterity
        log.info("------------------ VM Info ------------------");
        log.info("-- OS Name: " + System.getProperty("os.name"));
        log.info("-- OS Arch: " + System.getProperty("os.arch"));
        log.info("-- OS Vers: " + System.getProperty("os.version"));
        log.info("-- Java Vers: " + System.getProperty("java.version"));
        log.info("-- Java Home: " + System.getProperty("java.home"));
        log.info("-- User Name: " + System.getProperty("user.name"));
        log.info("-- User Home: " + System.getProperty("user.home"));
        log.info("-- Cur dir: " + System.getProperty("user.dir"));
        log.info("---------------------------------------------");
    }

    /**
     * Sets getdown status panel size and text color from applet params.
     */
    public void config (Getdown getdown)
    {
        if (statusBounds != null) {
            getdown._ifc.status = statusBounds;
        }
        if (statusColor != null) {
            getdown._ifc.statusText = statusColor;
        }
    }

    /**
     * Gets the value of the named parameter. If the param is not set, this will return null.
     */
    public String getParameter (String param)
    {
        return (_prefix == null) ?
            _applet.getParameter(param) :
            _applet.getParameter(_prefix + PARAM_DELIMITER + param);
    }

    /**
     * Gets the value of an optional Applet parameter. If the param is not set, the default value is
     * returned.
     */
    public String getParameter (String param, String defaultValue)
    {
        String value = getParameter(param);
        return (value == null) ? defaultValue : value;
    }

    /**
     * Uses the Applet context to redirect the browser to a URL (intended for use when the applet
     * is done downloading). If the redirect_on_finish parameter was not set, this does nothing.
     */
    public void redirect ()
    {
        if (redirectUrl != null) {
            if (redirectTarget == null) {
                _applet.getAppletContext().showDocument(redirectUrl);
            } else {
                _applet.getAppletContext().showDocument(redirectUrl, redirectTarget);
            }
        }
    }

    /**
     * Gets the rotation background images and error image.The given image loader will be used if
     * the images have not been loaded yet. The locations of the images are pulled from the Applet
     * parameters.
     */
    public RotatingBackgrounds getBackgroundImages (ImageLoader loader)
    {
        if (bgimages == null) {
            // Load background images
            bgimages = getBackgroundImages(imgpath, errorimgpath, loader);
        }
        return bgimages;
    }

    /**
     * Gets the rotation background images and error image.The given image loader will be used if
     * the images have not been loaded yet.
     */
    public static RotatingBackgrounds getBackgroundImages (String imageParam,
        String errorImagePath, ImageLoader loader)
    {
        // Parse the image parameter and load the background images
        RotatingBackgrounds images;
        if (imageParam == null) {
            images = new RotatingBackgrounds();
        } else if (imageParam.indexOf(",") > -1) {
            images = new RotatingBackgrounds(imageParam.split(","), errorImagePath, loader);
        } else {
            images = new RotatingBackgrounds(loader.loadImage(imageParam));
        }
        return images;
    }

    /**
     * Parses parameters named {@code prefix0}, {@code prefix1}, ... into a list.
     */
    protected List<String> parseArgList (String prefix)
    {
        List<String> arglist = new ArrayList<String>();
        String value;
        for (int ii = 0; (value = getParameter(prefix + ii)) != null; ii++) {
            arglist.add(value);
        }
        return arglist;
    }

    /**
     * This checks whether the user has accepted our signed
     */
    protected void securityCheck () throws Exception
    {
        // getdown requires full read/write permissions to the system; if we don't have this, then
        // we need to not do anything unsafe, and display a message to the user telling them they
        // need to (groan) close out of the web browser entirely and re-launch the browser, go to
        // our site, and accept the certificate
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            try {
                sm.checkPropertiesAccess();
                sm.checkWrite("getdown");
            } catch (SecurityException se) {
                log.warning("Signed applet rejected by user", "se", se);
                throw new Exception("m.insufficient_permissions_error");
            }
        }
    }

    /**
     * Dumps the contents of the <code>properties</code> into the System properties.
     */
    protected void setSystemProperties ()
    {
        System.getProperties().putAll(_properties);
    }

    /**
     * Makes sire that the appdir directory exists, creating it if necessary. Throws an exception if
     * the directory does not exist and cannot be created.
     */
    protected void ensureAppdirExists () throws Exception
    {
        // if our application directory does not exist, auto-create it
        if (!appdir.exists() || !appdir.isDirectory()) {
            if (!appdir.mkdirs()) {
                throw new Exception("m.create_appdir_failed");
            }
        }
    }

    /**
     * Writes the installer.txt and getdown.txt files to the local file system as needed.
     */
    protected void createFiles () throws Exception
    {
        // if an installer.txt file is desired, create that
        if (!StringUtil.isBlank(installerFileContents)) {
            File infile = new File(appdir, "installer.txt");
            if (!infile.exists()) {
                writeToFile(infile, installerFileContents);
            }
        }

        // if our getdown.txt file does not exist, or it is corrupt, auto-/recreate it
        File gdfile = new File(appdir, "getdown.txt");
        boolean createGetdown = !gdfile.exists();
        if (!createGetdown) {
            try {
                Map<String,Object> cdata = ConfigUtil.parseConfig(gdfile, false);
                String oappbase = StringUtil.trim((String)cdata.get(APPBASE));
                createGetdown = (appbase != null && !appbase.trim().equals(oappbase));
                if (createGetdown) {
                    log.warning("Recreating getdown.txt due to appbase mismatch",
                                "nappbase", appbase, "oappbase", oappbase);
                }
            } catch (Exception e) {
                log.warning("Failure checking validity of getdown.txt, forcing recreate.",
                            "error", e);
                createGetdown = true;
            }
        }
        if (createGetdown) {
            if (StringUtil.isBlank(appbase)) {
                throw new Exception("m.missing_appbase");
            }
            if (!writeToFile(gdfile, "appbase = " + appbase)) {
                throw new Exception("m.create_getdown_failed");
            }
        }
    }

    /**
     * Creates the specified file and writes the supplied contents to it.
     */
    protected boolean writeToFile (File tofile, String contents)
    {
        try {
            PrintStream out = new PrintStream(new FileOutputStream(tofile));
            out.println(contents);
            out.close();
            return true;
        } catch (IOException ioe) {
            log.warning("Failed to create '" + tofile + "'.", ioe);
            return false;
        }
    }

    /** A reference to the Applet in which Getdown is running */
    protected JApplet _applet;

    /** An optional prefix to prepend when looking for Getdown Applet params */
    protected String _prefix;

    /** The background images displayed on the status panel as Getdown is getting down. */
    protected RotatingBackgrounds bgimages;

    /** System properties to set in the applet JVM. */
    protected Properties _properties = new Properties();
}
TOP

Related Classes of com.threerings.getdown.launcher.GetdownAppletConfig

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.