def get_user():
    try:
        if 'SUDO_USER' in os.environ:
            return os.environ['SUDO_USER']
        else:
            return os.environ['USER']
    except KeyError:
        # Hint: there is no $USER during debuild
        logging.warning("Unable to get user from environment variable.")
        return "root" def get_user_group():
    return getgrgid(get_user_gid()).gr_name def get_user_gid():
    return getpwnam(get_user()).pw_gid def get_user_uid():
    return getpwnam(get_user()).pw_uid def get_workdir():
    return os.getcwd() def get_hostname():
    return socket.gethostname() def get_edi_plugin_directory():
    return os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "plugins")) class FatalError(Error):
    """Exception raised for fatal errors needing corrective actions from user.

    Attributes:
        message -- explanation of the error
    """

    def __init__(self, message):
        self.message = message class ProxySetup:
    _cache = dict()
    _warn_if_auto_mode = True

    def __init__(self, clear_cache=False):
        if clear_cache:
            ProxySetup._cache = dict()
        self._env_to_getter = {
            'http_proxy': partial(self._get_value, 'http_proxy',
                                  partial(self._gsettings_get_proxy, 'org.gnome.system.proxy.http', 'http')),
            'https_proxy': partial(self._get_value, 'https_proxy',
                                   partial(self._gsettings_get_proxy, 'org.gnome.system.proxy.https', 'http')),
            'ftp_proxy': partial(self._get_value, 'ftp_proxy',
                                 partial(self._gsettings_get_proxy, 'org.gnome.system.proxy.ftp', 'http')),
            'all_proxy': partial(self._get_value, 'all_proxy',
                                 partial(self._gsettings_get_proxy, 'org.gnome.system.proxy.socks', 'socks')),
            'no_proxy': partial(self._get_value, 'no_proxy', self._gsettings_get_ignore_hosts),
        }

    def get(self, environment_variable, default=None):
        assert environment_variable in self._env_to_getter

        if environment_variable in ProxySetup._cache:
            result = ProxySetup._cache.get(environment_variable)
        else:
            result = self._env_to_getter[environment_variable]()
            ProxySetup._cache[environment_variable] = result

        if result:
            return result
        else:
            return default

    def get_requests_dict(self):
        proxy_dict = {
            'http': self.get('http_proxy', default=None),
            'https': self.get('https_proxy', default=None),
            'no_proxy': self.get('no_proxy', default=None),
        }
        return proxy_dict

    def get_environment(self):
        env_with_proxy = os.environ.copy()
        for item in self._env_to_getter.keys():
            env_with_proxy[item] = self.get(item, '')
        return env_with_proxy

    def _get_value(self, environment_variable, gsettings_getter):
        env_value = get_environment_variable(environment_variable, default='')

        if env_value:
            logging.debug('''Retrieved '{}' from environment.'''.format(environment_variable))
            return env_value

        if not self._has_gsettings():
            return None

        if self._proxy_mode() == 'auto' and ProxySetup._warn_if_auto_mode:
            ProxySetup._warn_if_auto_mode = False
            logging.warning('Automatic proxy mode is not supported!')

        if self._proxy_mode() != 'manual':
            return None

        gsettings_value = gsettings_getter()
        if gsettings_value:
            logging.debug('''Retrieved '{}' from gsettings.'''.format(environment_variable))
            return gsettings_value
        else:
            return None

    @staticmethod
    def _gsettings_get_proxy(schema, prefix):
        host = get_gsettings_value(schema, 'host')
        port = get_gsettings_value(schema, 'port')

        if host and port and port != '0':
            return '{}://{}:{}/'.format(prefix, host, port)
        else:
            return None

    @staticmethod
    def _gsettings_get_ignore_hosts():
        no_proxy = get_gsettings_value('org.gnome.system.proxy', 'ignore-hosts')

        if no_proxy and no_proxy != '@as []':
            return ','.join(ast.literal_eval(no_proxy))
        else:
            return None

    @staticmethod
    def _has_gsettings():
        key = 'has-gsettings'
        if key not in ProxySetup._cache:
            result = (edi.lib.helpers.which('gsettings') is not None)
            ProxySetup._cache[key] = result

        return ProxySetup._cache.get(key)

    @staticmethod
    def _proxy_mode():
        key = 'gsettings-proxy-mode'
        if key not in ProxySetup._cache:
            result = get_gsettings_value('org.gnome.system.proxy', 'mode')
            ProxySetup._cache[key] = result

        return ProxySetup._cache.get(key) def get_user_ssh_pub_keys():
    """
    Search for all ssh public keys of the current user (not the root user when called with sudo).
    :return: A list of ssh public keys. The list will be empty if the tool ssh is not installed.
    """
    if not edi.lib.helpers.which('ssh'):
        return []

    random_host = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
    cmd = ['ssh', '-G', random_host]
    ssh_config = run(cmd, stdout=subprocess.PIPE).stdout
    user_home = get_user_home_directory(edi.lib.helpers.get_user())
    identity_files = re.findall(r'^identityfile (.*)$', ssh_config, flags=re.MULTILINE)
    ssh_pub_keys = []
    for file in identity_files:
        expanded_file = re.sub(r'^~', user_home, file)
        expanded_pub_file = '{}.pub'.format(expanded_file)
        if os.path.isfile(expanded_file) and os.path.isfile(expanded_pub_file):
            ssh_pub_keys.append(expanded_pub_file)

    return ssh_pub_keys def get_edi_version():
    """
    Get the version of the current edi installation or the version derived from git.

    :return: full edi version string
    """
    project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
    git_dir = os.path.join(project_root, ".git")
    if os.path.isdir(git_dir):
        # do import locally so that we do not depend on setuptools_scm for the released version
        from setuptools_scm import get_version
        return get_version(root=project_root)
    else:
        try:
            return pkg_resources.get_distribution('edi').version
        except pkg_resources.DistributionNotFound:
            return edi_fallback_version def get_stripped_version(version):
    """
    Strips the suffixes from the version string.

    :param version: Version string that needs to be parsed
    :return: a stripped version string of the format MAJOR[.MINOR[.PATCH]]
    """
    result = re.match(r'\d+(\.\d+){0,2}', version)
    if result:
        return result.group(0)
    else:
        raise FatalError('''Unable to parse version '{}'.'''.format(version)) def get_user_home_directory(username):
    cmd = ['getent', 'passwd', username]
    result = run(cmd, stdout=subprocess.PIPE)
    return result.stdout.split(':')[5] def get_current_display():
    display_variable = get_environment_variable('DISPLAY', '')
    display_no, substitutions = re.subn(r'^.*:([0-9]*)[.]?[0-9]*$', r'\1', display_variable)
    if substitutions == 1:
        return display_no
    else:
        return '' def get_lxd_version():
    if not Executables.has('lxd'):
        return '0.0.0'

    cmd = [Executables.get('lxd'), '--version']
    result = run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if not result.stdout:
        # recent lxd versions print the version to stderr!
        return result.stderr.strip('\n')
    else:
        return result.stdout.strip('\n') def obfuscate_url_password(url):
    """Obfuscate password in URL (useful for logging)"""
    parts = urlparse(url)
    if parts.password:
        visible_password = ":{}@".format(parts.password)
        hidden_password = ":***@"
        url = urlunparse(
            (parts.scheme,
             parts.netloc.replace(visible_password, hidden_password, 1),
             parts.path, parts.params, parts.query, parts.fragment))
    return url def annotated_yaml_load(stream, context_hint):
    """
    Load a yaml configuration and throw a FatalError containing a hint
    if the yaml stream can not be parsed.
    :param stream: A yaml formatted stream.
    :param context_hint: A hint for the user where the yaml configuration comes from.
    :return: The content of the yaml as a Python object.
    """
    try:
        return yaml.safe_load(stream)
    except yaml.parser.ParserError as e:
        raise FatalError("Invalid yaml configuration '{}':\n{}".format(context_hint, str(e))) from e
import yaml
import collections
import copy
import hashlib
import os
import logging
from jinja2 import Template
from contextlib import contextmanager
from os.path import dirname, abspath, basename, splitext, isfile, join
from edi.lib.helpers import (get_user, get_user_group, get_user_gid, get_user_uid, get_workdir,
                             get_hostname, get_edi_plugin_directory, FatalError)
from edi.lib.proxyhelpers import ProxySetup
from edi.lib.sshkeyhelpers import get_user_ssh_pub_keys
from edi.lib.versionhelpers import get_edi_version, get_stripped_version
from edi.lib.shellhelpers import get_user_home_directory, get_current_display
from edi.lib.lxchelpers import get_lxd_version
from packaging.version import Version
from edi.lib.urlhelpers import obfuscate_url_password
from edi.lib.yamlhelpers import annotated_yaml_load
# -*- coding: utf-8 -*-
# Copyright (C) 2016 Matthias Luescher
#
# Authors:
#  Matthias Luescher
#
# This file is part of edi.
#
# edi 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 3 of the License, or
# (at your option) any later version.
#
# edi 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 edi.  If not, see <http://www.gnu.org/licenses/>.



def remove_passwords(dictionary):
    obfuscated_dictionary = {}

    url_items = ["edi_host_http_proxy", "edi_host_https_proxy",
                 "edi_host_ftp_proxy", "edi_host_socks_proxy"]

    for key, value in dictionary.items():
        if key in url_items:
            obfuscated_dictionary[key] = obfuscate_url_password(value)
        else:
            obfuscated_dictionary[key] = value

    return obfuscated_dictionary


def get_base_dictionary():
    base_dict = {}
    current_user_name = get_user()
    base_dict["edi_current_user_name"] = current_user_name

    base_dict["edi_current_user_group_name"] = get_user_group()