def run(host,
        node,
        port,
        endpoint,
        tls,
        username,
        password,
        keyring,
        search_from,
        search_to,
        mode,
        output,
        follow,
        interval,
        limit,
        latency,
        stream,
        field,
        template_option,
        sort,
        asc,
        proxy,
        query):
    """
    Bonfire - A graylog CLI client
    """

    cfg = get_config()

    # Configure the graylog API object
    if node is not None:
        # The user specified a preconfigured node, take the config from there
        gl_api = api_from_config(cfg, node_name=node)
    else:
        if host is not None:
            # A manual host configuration is used
            if username is None:
                username = click.prompt("Enter username for {host}:{port}".format(host=host, port=port),
                                        default=getpass.getuser())
            if tls:
                scheme = "https"
            else:
                scheme = "http"

            if proxy:
                proxies = {scheme: proxy}
            else:
                proxies = None

            gl_api = api_from_host(host=host, port=port, endpoint=endpoint, username=username, scheme=scheme,
                                   proxies=proxies)
        else:
            if cfg.has_section("node:default"):
                gl_api = api_from_config(cfg)
            else:
                cli_error("Error: No host or node configuration specified and no default found.")

    if username is not None:
        gl_api.username = username

    if keyring and password is None:
        password = get_password_from_keyring(gl_api.host, gl_api.username)

    if password is None:
        password = click.prompt("Enter password for {username}@{api}".format(
            username=gl_api.username, api=gl_api), hide_input=True)

    gl_api.password = password

    if keyring:
        store_password_in_keyring(gl_api.host, gl_api.username, password)

    username = gl_api.username

    # Check if the query should be retrieved from the configuration
    if query[0] == ":":
        section_name = "query" + query
        template_options = dict(map(lambda t: tuple(str(t).split("=", 1)), template_option))
        query = get_templated_option(cfg, section_name, "query", template_options)

        if cfg.has_option(section_name, "limit"):
            limit = get_templated_option(cfg, section_name, "limit", template_options)

        if cfg.has_option(section_name, "from"):
            search_from = get_templated_option(cfg, section_name, "from", template_options)

        if cfg.has_option(section_name, "to"):
            search_to = get_templated_option(cfg, section_name, "to", template_options)

        if cfg.has_option(section_name, "sort"):
            sort = get_templated_option(cfg, section_name, "sort", template_options)

        if cfg.has_option(section_name, "asc"):
            asc = get_templated_option(cfg, section_name, "asc", template_options)

        if cfg.has_option(section_name, "fields"):
            field = get_templated_option(cfg, section_name, "fields", template_options).split(",")

        if cfg.has_option(section_name, "stream"):
            stream = get_templated_option(cfg, section_name, "stream", template_options)

    # Configure the base query
    sr = SearchRange(from_time=search_from, to_time=search_to)

    # Pass none if the list of fields is empty
    fields = None
    if field:
        fields = list(field)

    if limit <= 0:
        limit = None

    # Set limit to None, sort to none and start time to now, if follow is active
    if follow:
        limit = None
        sort = None
        sr.from_time = arrow.now('local').replace(seconds=-latency - 1)
        sr.to_time = arrow.now('local').replace(seconds=-latency)

    # Get the user permissions
    userinfo = gl_api.user_info(username)

    # If the permissions are not set or a stream is specified
    stream_filter = None
    if stream or (userinfo["permissions"] != ["*"] and gl_api.default_stream is None):
        if not stream:
            streams = gl_api.streams()["streams"]
            click.echo("Please select a stream to query:")
            for i, stream in enumerate(streams):
                click.echo("{}: Stream '{}' (id: {})".format(i, stream["title"], stream["id"]))
            i = click.prompt("Enter stream number:", type=int, default=0)
            stream = streams[i]["id"]
        stream_filter = "streams:{}".format(stream)

    # Create the initial query object
    q = SearchQuery(search_range=sr, query=query, limit=limit, filter=stream_filter, fields=fields, sort=sort,
                    ascending=asc)

    # Check the mode in which the program should run (dump, tail or interactive mode)
    if mode == "tail":
        if fields:
            formatter = tail_format(fields)
        else:
            formatter = tail_format()
    elif mode == "dump":
        formatter = dump_format()

    run_logprint(gl_api, q, formatter, follow, interval, latency, output)