Blob Blame History Raw
# This work is licensed under the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.

import configparser
import os
from logging import getLogger
import urllib.parse

from ._util import listify

log = getLogger(__name__)


def _parse_hostname(url):
    # If http://example.com is passed, netloc=example.com path=""
    # If just example.com is passed, netloc="" path=example.com
    parsedbits = urllib.parse.urlparse(url)
    return parsedbits.netloc or parsedbits.path


def _makedirs(path):
    if os.path.exists(os.path.dirname(path)):
        return
    os.makedirs(os.path.dirname(path), 0o700)


def _default_cache_location(filename):
    """
    Determine default location for passed xdg filename.
    example: ~/.cache/python-bugzilla/bugzillarc
    """
    return os.path.expanduser("~/.cache/python-bugzilla/%s" % filename)


class _BugzillaRCFile(object):
    """
    Helper class for interacting with bugzillarc files
    """
    @staticmethod
    def get_default_configpaths():
        paths = [
            '/etc/bugzillarc',
            '~/.bugzillarc',
            '~/.config/python-bugzilla/bugzillarc',
        ]
        return paths

    def __init__(self):
        self._cfg = None
        self._configpaths = None
        self.set_configpaths(None)

    def set_configpaths(self, configpaths):
        configpaths = [os.path.expanduser(p) for p in
                       listify(configpaths or [])]

        cfg = configparser.ConfigParser()
        read_files = cfg.read(configpaths)
        if read_files:
            log.info("Found bugzillarc files: %s", read_files)

        self._cfg = cfg
        self._configpaths = configpaths or []

    def get_configpaths(self):
        return self._configpaths[:]

    def get_default_url(self):
        """
        Grab a default URL from bugzillarc [DEFAULT] url=X
        """
        cfgurl = self._cfg.defaults().get("url", None)
        if cfgurl is not None:
            log.debug("bugzillarc: found cli url=%s", cfgurl)
            return cfgurl

    def parse(self, url):
        """
        Find the section for the passed URL domain, and return all the fields
        """
        section = ""
        log.debug("bugzillarc: Searching for config section matching %s", url)

        urlhost = _parse_hostname(url)
        for sectionhost in sorted(self._cfg.sections()):
            # If the section is just a hostname, make it match
            # If the section has a / in it, do a substring match
            if "/" not in sectionhost:
                if sectionhost == urlhost:
                    section = sectionhost
            elif sectionhost in url:
                section = sectionhost
            if section:
                log.debug("bugzillarc: Found matching section: %s", section)
                break

        if not section:
            log.debug("bugzillarc: No section found")
            return {}
        return dict(self._cfg.items(section))


    def save_api_key(self, url, api_key):
        """
        Save the API_KEY in the config file. We use the last file
        in the configpaths list, which is the one with the highest
        precedence.
        """
        configpaths = self.get_configpaths()
        if not configpaths:
            return None

        config_filename = configpaths[-1]
        section = _parse_hostname(url)
        cfg = configparser.ConfigParser()
        cfg.read(config_filename)

        if section not in cfg.sections():
            cfg.add_section(section)

        cfg.set(section, 'api_key', api_key.strip())

        _makedirs(config_filename)
        with open(config_filename, 'w') as configfile:
            cfg.write(configfile)

        return config_filename


class _BugzillaTokenCache(object):
    """
    Class for interacting with a .bugzillatoken cache file
    """
    @staticmethod
    def get_default_path():
        return _default_cache_location("bugzillatoken")

    def __init__(self):
        self._filename = None
        self._cfg = None

    def _get_domain(self, url):
        domain = urllib.parse.urlparse(url)[1]
        if domain and domain not in self._cfg.sections():
            self._cfg.add_section(domain)
        return domain

    def get_value(self, url):
        domain = self._get_domain(url)
        if domain and self._cfg.has_option(domain, 'token'):
            return self._cfg.get(domain, 'token')
        return None

    def set_value(self, url, value):
        if self.get_value(url) == value:
            return

        domain = self._get_domain(url)
        if value is None:
            self._cfg.remove_option(domain, 'token')
        else:
            self._cfg.set(domain, 'token', value)

        if self._filename:
            _makedirs(self._filename)
            with open(self._filename, 'w') as _cfg:
                log.debug("Saving to _cfg")
                self._cfg.write(_cfg)

    def get_filename(self):
        return self._filename

    def set_filename(self, filename):
        log.debug("Using tokenfile=%s", filename)
        cfg = configparser.ConfigParser()
        if filename:
            cfg.read(filename)
        self._filename = filename
        self._cfg = cfg