1############################################################################
2# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3#
4# SPDX-License-Identifier: MPL-2.0
5#
6# This Source Code Form is subject to the terms of the Mozilla Public
7# License, v. 2.0. If a copy of the MPL was not distributed with this
8# file, you can obtain one at https://mozilla.org/MPL/2.0/.
9#
10# See the COPYRIGHT file distributed with this work for additional
11# information regarding copyright ownership.
12############################################################################
13
14from pathlib import Path
15import re
16import sys
17
18from typing import List, Tuple
19
20from docutils import nodes
21from docutils.nodes import Node, system_message
22from docutils.parsers.rst import roles
23
24from sphinx import addnodes
25
26try:
27    from sphinx.util.docutils import ReferenceRole
28except ImportError:
29    # pylint: disable=too-few-public-methods
30    class ReferenceRole(roles.GenericRole):
31        """
32        The ReferenceRole class (used as a base class by GitLabRefRole
33        below) is only defined in Sphinx >= 2.0.0.  For older Sphinx
34        versions, this stub version of the ReferenceRole class is used
35        instead.
36        """
37
38        def __init__(self):
39            super().__init__("", nodes.strong)
40
41
42GITLAB_BASE_URL = "https://gitlab.isc.org/isc-projects/bind9/-/"
43KNOWLEDGEBASE_BASE_URL = "https://kb.isc.org/docs/"
44
45
46# Custom Sphinx role enabling automatic hyperlinking to security advisory in
47# ISC Knowledgebase
48class CVERefRole(ReferenceRole):
49    def __init__(self, base_url: str) -> None:
50        self.base_url = base_url
51        super().__init__()
52
53    def run(self) -> Tuple[List[Node], List[system_message]]:
54        cve_identifier = "(CVE-%s)" % self.target
55
56        target_id = "index-%s" % self.env.new_serialno("index")
57        entries = [
58            ("single", "ISC Knowledgebase; " + cve_identifier, target_id, "", None)
59        ]
60
61        index = addnodes.index(entries=entries)
62        target = nodes.target("", "", ids=[target_id])
63        self.inliner.document.note_explicit_target(target)
64
65        try:
66            refuri = self.base_url + "cve-%s" % self.target
67            reference = nodes.reference(
68                "", "", internal=False, refuri=refuri, classes=["cve"]
69            )
70            if self.has_explicit_title:
71                reference += nodes.strong(self.title, self.title)
72            else:
73                reference += nodes.strong(cve_identifier, cve_identifier)
74        except ValueError:
75            error_text = "invalid ISC Knowledgebase identifier %s" % self.target
76            msg = self.inliner.reporter.error(error_text, line=self.lineno)
77            prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
78            return [prb], [msg]
79
80        return [index, target, reference], []
81
82
83# Custom Sphinx role enabling automatic hyperlinking to GitLab issues/MRs.
84class GitLabRefRole(ReferenceRole):
85    def __init__(self, base_url: str) -> None:
86        self.base_url = base_url
87        super().__init__()
88
89    def run(self) -> Tuple[List[Node], List[system_message]]:
90        gl_identifier = "[GL %s]" % self.target
91
92        target_id = "index-%s" % self.env.new_serialno("index")
93        entries = [("single", "GitLab; " + gl_identifier, target_id, "", None)]
94
95        index = addnodes.index(entries=entries)
96        target = nodes.target("", "", ids=[target_id])
97        self.inliner.document.note_explicit_target(target)
98
99        try:
100            refuri = self.build_uri()
101            reference = nodes.reference(
102                "", "", internal=False, refuri=refuri, classes=["gl"]
103            )
104            if self.has_explicit_title:
105                reference += nodes.strong(self.title, self.title)
106            else:
107                reference += nodes.strong(gl_identifier, gl_identifier)
108        except ValueError:
109            error_text = "invalid GitLab identifier %s" % self.target
110            msg = self.inliner.reporter.error(error_text, line=self.lineno)
111            prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
112            return [prb], [msg]
113
114        return [index, target, reference], []
115
116    def build_uri(self):
117        if self.target[0] == "#":
118            return self.base_url + "issues/%d" % int(self.target[1:])
119        if self.target[0] == "!":
120            return self.base_url + "merge_requests/%d" % int(self.target[1:])
121        raise ValueError
122
123
124def setup(app):
125    roles.register_local_role("cve", CVERefRole(KNOWLEDGEBASE_BASE_URL))
126    roles.register_local_role("gl", GitLabRefRole(GITLAB_BASE_URL))
127    app.add_crossref_type("iscman", "iscman", "pair: %s; manual page")
128
129
130#
131# Configuration file for the Sphinx documentation builder.
132#
133# This file only contains a selection of the most common options. For a full
134# list see the documentation:
135# http://www.sphinx-doc.org/en/master/config
136
137# -- Path setup --------------------------------------------------------------
138
139# If extensions (or modules to document with autodoc) are in another directory,
140# add these directories to sys.path here. If the directory is relative to the
141# documentation root, make it absolute.
142#
143sys.path.append(str(Path(__file__).resolve().parent / "_ext"))
144sys.path.append(str(Path(__file__).resolve().parent.parent / "misc"))
145
146# -- Project information -----------------------------------------------------
147
148project = "BIND 9"
149# pylint: disable=redefined-builtin
150copyright = "2023, Internet Systems Consortium"
151author = "Internet Systems Consortium"
152
153m4_vars = {}
154with open("../../configure.ac", encoding="utf-8") as configure_ac:
155    for line in configure_ac:
156        match = re.match(
157            r"m4_define\(\[(?P<key>bind_VERSION_[A-Z]+)\], (?P<val>[^)]*)\)dnl", line
158        )
159        if match:
160            m4_vars[match.group("key")] = match.group("val")
161
162version = "%s.%s.%s%s" % (
163    m4_vars["bind_VERSION_MAJOR"],
164    m4_vars["bind_VERSION_MINOR"],
165    m4_vars["bind_VERSION_PATCH"],
166    m4_vars["bind_VERSION_EXTRA"],
167)
168release = version
169
170# -- General configuration ---------------------------------------------------
171
172# Add any Sphinx extension module names here, as strings. They can be
173# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
174# ones.
175extensions = ["namedconf", "rndcconf"]
176
177# Add any paths that contain templates here, relative to this directory.
178templates_path = ["_templates"]
179
180# List of patterns, relative to source directory, that match files and
181# directories to ignore when looking for source files.
182# This pattern also affects html_static_path and html_extra_path.
183exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "*.inc.rst"]
184
185# The master toctree document.
186master_doc = "index"
187
188# -- Options for HTML output -------------------------------------------------
189
190# The theme to use for HTML and HTML Help pages.  See the documentation for
191# a list of builtin themes.
192#
193html_theme = "sphinx_rtd_theme"
194html_static_path = ["_static"]
195html_css_files = ["custom.css"]
196
197# -- Options for EPUB output -------------------------------------------------
198
199epub_basename = "Bv9ARM"
200
201# -- Options for LaTeX output ------------------------------------------------
202latex_engine = "xelatex"
203
204# pylint disable=line-too-long
205latex_documents = [
206    (
207        master_doc,
208        "Bv9ARM.tex",
209        "BIND 9 Administrator Reference Manual",
210        author,
211        "manual",
212    ),
213]
214
215latex_logo = "isc-logo.pdf"
216
217#
218# The rst_epilog will be completely overwritten from the Makefile,
219# the definition here is provided purely for situations when
220# sphinx-build is run by hand.
221#
222rst_epilog = """
223.. |rndc_conf| replace:: ``/etc/rndc.conf``
224.. |rndc_key| replace:: ``/etc/rndc.key``
225.. |named_conf| replace:: ``/etc/named.conf``
226.. |bind_keys| replace:: ``/etc/bind.keys``
227.. |named_pid| replace:: ``/run/named.pid``
228.. |session_key| replace:: ``/run/session.key``
229"""
230