<commit_before>/*
 * Built-in AT.
 * Handles the registration and de-registration of the Windows built-in AT (magnifier, osk, and narrator).
 *
 * Copyright 2016 Raising the Floor - International
 *
 * Licensed under the New BSD license. You may not use this file except in
 * compliance with this License.
 *
 * The research leading to these results has received funding from the European Union's
 * Seventh Framework Programme (FP7/2007-2013)
 * under grant agreement no. 289016.
 *
 * You may obtain a copy of the License at
 * https://github.com/GPII/universal/blob/master/LICENSE.txt
 */

"use strict";

var fluid = require("universal");
var gpii = fluid.registerNamespace("gpii");
var child_process = require("child_process");

require("../registrySettingsHandler/src/RegistrySettingsHandler.js");

/*
 * The method of (de)registering AT is described in https://msdn.microsoft.com/library/windows/desktop/bb879984.aspx:
 *
 * "An application notifies the Ease of Access Center by setting a temporary registry key and then injecting the Windows
 * Logo key + U key combination into the input stream."
 *
 * "The application should create the temporary key at the following location.
 * HKCU\Software\Microsoft\Windows NT\CurrentVersion\AccessibilityTemp
 *
 * The temporary key should have the same name as the registered application name, such as [magnifierpane, osk, or
 * Narrator].
 * The value of the key is a DWORD set to 0x0003 when it is starting, or 0x0002 when the application is exiting."
 *
 * Sending this key sequence is not necessary for GPII, as Windows will read this value when switching desktops and
 * perform the actual registration then.
 */

// The key where the temporary values are kept.
var accessibilityTempKey = {
    baseKey: "HKEY_CURRENT_USER",
    path: "Software\\Microsoft\\Windows NT\\CurrentVersion\\AccessibilityTemp"
};
var registeredAtKey = {
    baseKey: "HKEY_LOCAL_MACHINE",
    path: "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\ATs"
};


/**
 * Enable or disable a registered AT application.
 *
 * While an application is enabled, Windows will automatically start and stop the application when the user switches
 * desktops. Disabling will stop this from happening.
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @param {Boolean} enable true to enable the application.
 * @param {{}} options Options
 * @param {Boolean} options.configOnly true to only (de)register the application and not to start or stop it.
 * @return {*}
 */
gpii.windows.enableRegisteredAt = function (name, enable, options) {
    var defaultOptions = {
        configOnly: false
    };
    options = fluid.extend(defaultOptions, options);


    // The known values of this setting are: 0 (removed), 1 (installed), 2 (removal required), 3 (install required)
    // Values 0 and 1 are informational, which are set as a result of 2 and 3 to prevent a repeat action.
    var targetValue = enable ? 3 : 2;
    gpii.windows.writeRegistryKey(accessibilityTempKey.baseKey, accessibilityTempKey.path, name, targetValue, "REG_DWORD");

    if (!options.configOnly) {
        if (enable) {
            gpii.windows.startRegisteredAt(name);
        } else {
            gpii.windows.stopRegisteredAt(name);
        }
    }
};

/**
 * Starts a registered AT application.
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 */
gpii.windows.startRegisteredAt = function (name) {
    // StartExe + StartParams is the command to execute
    var atInfo = gpii.windows.getAtInformation(name, ["StartExe", "StartParams"]);
    var command = atInfo.StartExe;
    if (atInfo.StartParams !== undefined) {
        command += " " + atInfo.StartParams;
    }

    if (gpii.windows.isWow64()) {
        // When running as a 32-bit process on a 64-bit OS, make sure the native (64-bit) version is executed.
        // For this to happen, the 64-bit cmd.exe will be used to perform the invocation.
        command = "%SystemRoot%\\Sysnative\\cmd.exe /s /c " + command;
    }

    child_process.exec(command + " /noutilman");
};

/**
 * Stops a registered AT application.
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @return {promise} A promise that will resolve when the application has closed.
 */
gpii.windows.stopRegisteredAt = function (name) {
    // ATExe is the executable name, which is used to determine if the process is running.
    var atInfo = gpii.windows.getAtInformation(name, ["ATExe"]);
    return gpii.windows.closeProcessByName(atInfo.ATExe, {timeout: 2000});
};

/**
 * Gets some pieces of information about a registered AT application.
 *
 * The values are documented in https://msdn.microsoft.com/library/windows/desktop/bb879984.aspx
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @param {String[]} values A list of the values required.
 * @return An object containing the values from the registry.
 */<commit_msg>GPII-2073: Uses the applications registered with the Ease of Access Centre to control built-in AT<commit_after>/*
 * Built-in AT.
 * Handles the registration and de-registration of the Windows built-in AT (magnifier, osk, and narrator).
 *
 * Copyright 2016 Raising the Floor - International
 *
 * Licensed under the New BSD license. You may not use this file except in
 * compliance with this License.
 *
 * The research leading to these results has received funding from the European Union's
 * Seventh Framework Programme (FP7/2007-2013)
 * under grant agreement no. 289016.
 *
 * You may obtain a copy of the License at
 * https://github.com/GPII/universal/blob/master/LICENSE.txt
 */

"use strict";

var fluid = require("universal");
var gpii = fluid.registerNamespace("gpii");
var child_process = require("child_process");

require("../registrySettingsHandler/src/RegistrySettingsHandler.js");

/*
 * The method of (de)registering AT is described in https://msdn.microsoft.com/library/windows/desktop/bb879984.aspx:
 *
 * "An application notifies the Ease of Access Center by setting a temporary registry key and then injecting the Windows
 * Logo key + U key combination into the input stream."
 *
 * "The application should create the temporary key at the following location.
 * HKCU\Software\Microsoft\Windows NT\CurrentVersion\AccessibilityTemp
 *
 * The temporary key should have the same name as the registered application name, such as [magnifierpane, osk, or
 * Narrator].
 * The value of the key is a DWORD set to 0x0003 when it is starting, or 0x0002 when the application is exiting."
 *
 * Sending this key sequence is not necessary for GPII, as Windows will read this value when switching desktops and
 * perform the actual registration then.
 */

// The key where the temporary values are kept.
var accessibilityTempKey = {
    baseKey: "HKEY_CURRENT_USER",
    path: "Software\\Microsoft\\Windows NT\\CurrentVersion\\AccessibilityTemp"
};
var registeredAtKey = {
    baseKey: "HKEY_LOCAL_MACHINE",
    path: "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Accessibility\\ATs"
};

// The AccessibilityTemp values.
// The known values of this setting are: 0 (removed), 1 (installed), 2 (removal required), 3 (install required).
// Values 0 and 1 are informational, which are set (by utilman) as a result of 2 and 3 to prevent a repeat action.
var disableAt = 2,
    enableAt = 3;



/**
 * Enable or disable a registered AT application.
 *
 * While an application is enabled, Windows will automatically start and stop the application when the user switches
 * desktops. Disabling will stop this from happening.
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @param {Boolean} enable true to enable the application.
 * @param {{}} options Options
 * @param {Boolean} options.configOnly true to only (de)register the application and not to start or stop it.
 * @param {String} options.startCommand Overrides the command used to start the AT application.
 * @param {String} options.processName Overrides the executable to terminate when disabling.
 * @return {*}
 */
gpii.windows.enableRegisteredAt = function (name, enable, options) {
    var defaultOptions = {
        configOnly: false
    };
    options = fluid.extend(defaultOptions, options);

    if (!options.configOnly) {
        if (enable) {
            gpii.windows.startRegisteredAt(name, options);
        } else {
            gpii.windows.stopRegisteredAt(name, options);
        }
    }

    var promise = fluid.promise();

    // The AT will have enabled itself when it starts, so only update the registry when disabling.
    if (enable) {
        promise.resolve();
    } else {
        // On Windows 7/8, if there's more than one action value in the registry then it will display the Ease of Access
        // Centre. (This could be because when the first AT is loaded, it then invokes yet another instance of utilman,
        // which will work on the next value pulling the rug from under the first instance).
        // To work around this, wait until it has been cleared.
        gpii.windows.whilePendingAt().then(function () {
            var targetValue = enable ? enableAt : disableAt;
            gpii.windows.writeRegistryKey(accessibilityTempKey.baseKey, accessibilityTempKey.path, name, targetValue, "REG_DWORD");
            // Invoke utilman to trigger the de-registration.
            gpii.windows.nativeExec("%SystemRoot%\\System32\\utilman.exe");
            promise.resolve();
        });
    }

    return promise;
};

/**
 * Waits while there are pending actions for utilman.
 *
 * @return {promise} Returns a promise, whose value is true upon timeout, resolving when there are no pending actions.
 */
gpii.windows.whilePendingAt = function (options) {
    var defaultOptions = {
        pollDelay: 500,
        timeout: 5000
    };

    options = fluid.extend(true, defaultOptions, options);

    var timeTaken = 0;
    var promise = fluid.promise();

    var checkActions = function () {
        // Scan the AccessibilityTemp key for action values.
        var found = false;
        gpii.windows.enumRegistryValues(accessibilityTempKey.baseKey, accessibilityTempKey.path,
            function (name, data) {
                if ((data === enableAt) || (data === enableAt)) {
                    found = true;
                    return true;
                }
            });

        timeTaken += options.pollDelay;
        if (found) {
            promise.resolve(false);
        } else if (timeTaken > options.timeout) {
            promise.resolve(true);
        } else {
            setTimeout(checkActions, options.pollDelay);
        }
    };

    checkActions();
    return promise;
};

/**
 * Starts a registered AT application.
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @param {{}} options Options
 * @param {String} options.startCommand Overrides the command used to start the AT application.
 */
gpii.windows.startRegisteredAt = function (name, options) {
    var command;
    if (options && options.startCommand) {
        command = options.startCommand;
    } else {
        // StartExe + StartParams is the command to execute
        var atInfo = gpii.windows.getAtInformation(name, ["StartExe", "StartParams"]);
        command = atInfo.StartExe;
        if (atInfo.StartParams !== undefined) {
            command += " " + atInfo.StartParams;
        }
    }

    gpii.windows.waitForProcessTermination("utilman.exe").then(function () {
        gpii.windows.nativeExec(command);
    });
};

/**
 * Stops a registered AT application.
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @param {{}} options Options
 * @param {String} options.processName Overrides the executable name to terminate.
 * @return {promise} A promise that will resolve when the application has closed.
 */
gpii.windows.stopRegisteredAt = function (name, options) {
    var exeName;
    if (options && options.processName) {
        exeName = options.processName;
    } else {
        // ATExe is the executable name, which is used to determine if the process is running.
        var atInfo = gpii.windows.getAtInformation(name, ["ATExe"]);
        exeName = atInfo.ATExe;
    }

    return gpii.windows.killProcessByName(exeName);
};

/**
 * Execute a command, ensuring it uses the native System32 directory instead of the 32-bit version.
 *
 * When running as a 32-bit process on a 64-bit OS, in order make sure the native version is executed the 64-bit
 * cmd.exe will be used to perform the invocation.
 *
 * @param command The command to execute.
 * @param options The options for child_process.exec.
 * @return {*} Returns what child_process.exec returns.
 */
gpii.windows.nativeExec = function (command, options) {
    var execOptions = fluid.extend({}, options);
    if (gpii.windows.isWow64()) {
        execOptions.shell = process.env.SYSTEMROOT + "\\Sysnative\\cmd.exe";
    }
    return child_process.exec(command, execOptions);
};

/**
 * Gets some pieces of information about a registered AT application.
 *
 * The values are documented in https://msdn.microsoft.com/library/windows/desktop/bb879984.aspx
 *
 * @param {String} name The registered name of the application. This can be "magnifierpane", "osk", "Narrator", or
 *   some other 3rd party name.
 * @param {String[]} values A list of the values required.
 * @return An object containing the values from the registry.
 */
{'fraction_non_alphanumeric': 0.09900565133414407, 'fraction_numerical': 0.010873453036697903, 'mean_word_length': 3.2830882352941178, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5003559', 'n_tokens_mistral': 4044, 'n_tokens_neox': 3820, 'n_words': 1473}