🏡 index : quine-global/quine-anaconda-webui.git

/*
 * Copyright (C) 2022 Red Hat, Inc.
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */
import cockpit from "cockpit";

const CONF_PATH = "/run/anaconda/anaconda.conf";

const isEmpty = (inStr) => {
    // is string empty?
    return !(inStr || inStr.trim());
};

export const parseIni = content => {
    if (!content) {
        return;
    }
    /*
    Anaconda config files are a dialect of INI files based on Python's configparser. Unfortunately,
    that parser extends the commonly understood format with multi-line values, which is treated
    by most other parsers as an error. Because of that, custom parsing is required.
    */
    const lines = content.split(/\r\n|\r|\n/);
    const dataStore = {};
    const rxHeader = /\[([^\]]+)\].*/;
    const rxKeyValue = /(\w+) ?= ?(.*)/;

    let curHeader = "";
    let curKey = "";

    for (const line of lines) {
        let find;

        if (isEmpty(line)) {
            continue;
        }
        /* Another condition to skip a line is if it is a comment, which starts with "#".
           Fortunately, the file we use is already pre-processed by Anaconda - read and saved
           again - which strips all comments as a side effect. */

        find = rxHeader.exec(line);
        if (find && !isEmpty(find[0])) {
            curHeader = find[1];
            dataStore[curHeader] = {}; // caution, header must not repeat
            continue;
        }

        find = rxKeyValue.exec(line);
        if (find && !isEmpty(find[0])) {
            curKey = find[1];
            const value = find[2];
            dataStore[curHeader][curKey] = value;
            continue;
        }

        // by now, the line must be something else
        const fragment = line.trim();
        dataStore[curHeader][curKey] = (dataStore[curHeader][curKey] + "\n" + fragment).trim();
    }

    return dataStore;
};

/**
 * Raw string value for one key under an INI section (e.g. `Runtime`, `automated_install`).
 * @param {object} conf
 * @param {string} section
 * @param {string} key
 * @returns {string|undefined}
 */
export const getInstallerConfValue = (conf, section, key) => conf?.[section]?.[key];

/**
 * Parse booleans from `/run/anaconda/anaconda.conf` string values ("True" / "False").
 *
 * @param {string|undefined} raw
 * @returns {boolean}
 */
export const parseAnacondaConfBool = (raw) => {
    const t = raw?.trim()?.toLowerCase();
    return t === "true" || t === "yes" || t === "1";
};

export const readConf = () => {
    const confFile = cockpit.file(CONF_PATH, { superuser: "try", });
    return confFile.read()
            .then(parseIni)
            .finally(confFile.close);
};