'Similar to :func:`~stem.control.Controller.get_conf` but queries multiple
configuration options, providing back a mapping of those options to their
values.
There are three use cases for GETCONF:
1. a single value is provided (e.g. **ControlPort**)
2. multiple values are provided for the option (e.g. **ExitPolicy**)
3. a set of options that weren\'t necessarily requested are returned (for
instance querying **HiddenServiceOptions** gives **HiddenServiceDir**,
**HiddenServicePort**, etc)
The vast majority of the options fall into the first two categories, in
which case calling :func:`~stem.control.Controller.get_conf` is sufficient.
However, for batch queries or the special options that give a set of values
this provides back the full response. As of tor version 0.2.1.25
**HiddenServiceOptions** was the only option that falls into the third
category.
:param str,list params: configuration option(s) to be queried
:param object default: value for the mappings if the configuration option
is either undefined or the query fails
:param bool multiple: if **True** then the values provided are lists with
all of the present values
:returns:
**dict** of the \'config key => value\' mappings. The value is a...
* **str** if **multiple** is **False**, **None** if the configuration
option is unset
* **list** if **multiple** is **True**
* the **default** if it was set and the value was either undefined or our
lookup failed
:raises:
* :class:`stem.ControllerError` if the call fails and we weren\'t provided
a default response
* :class:`stem.InvalidArguments` if the configuration option requested
was invalid'

def get_conf_map(self, params, default=UNDEFINED, multiple=True):

    start_time = time.time()
    reply = {}
    if isinstance(params, (bytes, str_type)):
        params = [params]
    params = [entry for entry in params if entry.strip()]
    if (params == []):
        return {}
    lookup_params = set([MAPPED_CONFIG_KEYS.get(entry, entry) for entry in params])
    from_cache = [param.lower() for param in lookup_params]
    cached_results = self._get_cache_map(from_cache, 'getconf')
    for key in cached_results:
        user_expected_key = _case_insensitive_lookup(lookup_params, key)
        reply[user_expected_key] = cached_results[key]
        lookup_params.remove(user_expected_key)
    if (not lookup_params):
        log.trace(('GETCONF    %s    (cache    fetch)' % '    '.join(reply.keys())))
        return self._get_conf_dict_to_response(reply, default, multiple)
    try:
        response = self.msg(('GETCONF    %s' % '    '.join(lookup_params)))
        stem.response.convert('GETCONF', response)
        reply.update(response.entries)
        if self.is_caching_enabled():
            to_cache = dict(((k.lower(), v) for (k, v) in response.entries.items()))
            for key in UNCACHEABLE_GETCONF_PARAMS:
                if (key in to_cache):
                    del to_cache[key]
            self._set_cache(to_cache, 'getconf')
        for key in reply:
            if (not (key.lower() in MAPPED_CONFIG_KEYS.values())):
                user_expected_key = _case_insensitive_lookup(params, key, key)
                if (key != user_expected_key):
                    reply[user_expected_key] = reply[key]
                    del reply[key]
        log.debug(('GETCONF    %s    (runtime:    %0.4f)' % ('    '.join(lookup_params), (time.time() - start_time))))
        return self._get_conf_dict_to_response(reply, default, multiple)
    except stem.ControllerError as exc:
        log.debug(('GETCONF    %s    (failed:    %s)' % ('    '.join(lookup_params), exc)))
        if (default != UNDEFINED):
            return dict(((param, default) for param in params))
        else:
            raise exc
