diff --git a/ietf/doc/utils.py b/ietf/doc/utils.py
index d45d40158..3c83b16be 100644
--- a/ietf/doc/utils.py
+++ b/ietf/doc/utils.py
@@ -763,9 +763,11 @@ def rebuild_reference_relations(doc, filenames):
errors = []
unfound = set()
for ( ref, refType ) in refs.items():
- # As of Dec 2021, DocAlias has a unique constraint on the name field, so count > 1 should not occur
- refdoc = DocAlias.objects.filter( name=ref )
+ refdoc = DocAlias.objects.filter(name=ref)
+ if not refdoc and re.match(r"^draft-.*-\d{2}$", ref):
+ refdoc = DocAlias.objects.filter(name=ref[:-3])
count = refdoc.count()
+ # As of Dec 2021, DocAlias has a unique constraint on the name field, so count > 1 should not occur
if count == 0:
unfound.add( "%s" % ref )
continue
diff --git a/ietf/utils/test_draft_with_references_v3.xml b/ietf/utils/test_draft_with_references_v3.xml
index a04880d1d..ad9b2b280 100644
--- a/ietf/utils/test_draft_with_references_v3.xml
+++ b/ietf/utils/test_draft_with_references_v3.xml
@@ -1,6 +1,6 @@
-
+
Test Draft with References
@@ -37,9 +37,25 @@
+
+
+ Cloud Software
+
+
+
+
+
+
+
+
Informative References
+
+
+
+
+
Status of network hosts
@@ -51,6 +67,34 @@
+
+
+ Key Consistency and Discovery
+
+ Brave Software
+
+
+ The Tor Project
+
+
+ Mozilla
+
+
+ Cloudflare
+
+
+
+ This document describes the key consistency and correctness
+ requirements of protocols such as Privacy Pass, Oblivious DoH, and
+ Oblivious HTTP for user privacy. It discusses several mechanisms and
+ proposals for enabling user privacy in varying threat models. In
+ concludes with discussion of open problems in this area.
+
+
+
+
+
+
@@ -191,4 +235,4 @@
-
\ No newline at end of file
+
diff --git a/ietf/utils/tests.py b/ietf/utils/tests.py
index 2095b5fa4..69c16be6d 100644
--- a/ietf/utils/tests.py
+++ b/ietf/utils/tests.py
@@ -374,10 +374,17 @@ class XMLDraftTests(TestCase):
draft.get_refs(),
{
'rfc1': XMLDraft.REF_TYPE_NORMATIVE,
+ 'rfc2': XMLDraft.REF_TYPE_NORMATIVE,
+ 'draft-wood-key-consistency-03': XMLDraft.REF_TYPE_INFORMATIVE,
'rfc255': XMLDraft.REF_TYPE_INFORMATIVE,
'bcp6': XMLDraft.REF_TYPE_INFORMATIVE,
+ 'bcp14': XMLDraft.REF_TYPE_INFORMATIVE,
'rfc1207': XMLDraft.REF_TYPE_UNKNOWN,
'rfc4086': XMLDraft.REF_TYPE_NORMATIVE,
+ 'draft-ietf-teas-pcecc-use-cases-00': XMLDraft.REF_TYPE_INFORMATIVE,
+ 'draft-ietf-teas-pcecc-use-cases': XMLDraft.REF_TYPE_INFORMATIVE,
+ 'draft-ietf-sipcore-multiple-reasons-00': XMLDraft.REF_TYPE_INFORMATIVE,
+ 'draft-ietf-sipcore-multiple-reasons': XMLDraft.REF_TYPE_INFORMATIVE,
}
)
diff --git a/ietf/utils/xmldraft.py b/ietf/utils/xmldraft.py
index 15bf745cc..7e8674ea7 100644
--- a/ietf/utils/xmldraft.py
+++ b/ietf/utils/xmldraft.py
@@ -60,17 +60,47 @@ class XMLDraft(Draft):
tree.tree = v2v3.convert2to3()
return tree, xml_version
- def _document_name(self, anchor):
- """Guess document name from reference anchor
+ def _document_name(self, ref):
+ """Get document name from reference."""
+ series = ["rfc", "bcp", "fyi", "std"]
+ # handle xinclude first
+ # FIXME: this assumes the xinclude is a bibxml href; if it isn't, there can
+ # still be false negatives. it would be better to expand the xinclude and parse
+ # its seriesInfo.
+ if ref.tag.endswith("}include"):
+ name = re.search(
+ rf"reference\.({'|'.join(series).upper()})\.(\d{{4}})\.xml",
+ ref.attrib["href"],
+ )
+ if name:
+ return f"{name.group(1)}{int(name.group(2))}".lower()
+ name = re.search(
+ r"reference\.I-D\.(?:draft-)?(.*)\.xml", ref.attrib["href"]
+ )
+ if name:
+ return f"draft-{name.group(1)}"
+ # can't extract the name, give up
+ return ""
- Looks for series numbers and removes leading 0s from the number.
- """
- anchor = anchor.lower() # always give back lowercase
- label = anchor.rstrip('0123456789') # remove trailing digits
- if label in ['rfc', 'bcp', 'fyi', 'std']:
- number = int(anchor[len(label):])
- return f'{label}{number}'
- return anchor
+ # check the anchor next
+ anchor = ref.get("anchor").lower() # always give back lowercase
+ label = anchor.rstrip("0123456789") # remove trailing digits
+ if label in series:
+ number = int(anchor[len(label) :])
+ return f"{label}{number}"
+
+ # if we couldn't find a match so far, try the seriesInfo
+ series_query = " or ".join(f"@name='{x.upper()}'" for x in series)
+ for info in ref.xpath(
+ f"./seriesInfo[{series_query} or @name='Internet-Draft']"
+ ):
+ if not info.attrib["value"]:
+ continue
+ if info.attrib["name"] == "Internet-Draft":
+ return info.attrib["value"]
+ else:
+ return f'{info.attrib["name"].lower()}{info.attrib["value"]}'
+ return ""
def _reference_section_type(self, section_name):
"""Determine reference type from name of references section"""
@@ -154,10 +184,20 @@ class XMLDraft(Draft):
"""Extract references from the draft"""
refs = {}
# accept nested sections
- for section in self.xmlroot.findall('back//references'):
- ref_type = self._reference_section_type(self._reference_section_name(section))
- for ref in (section.findall('./reference') + section.findall('./referencegroup')):
- refs[self._document_name(ref.get('anchor'))] = ref_type
+ for section in self.xmlroot.findall("back//references"):
+ ref_type = self._reference_section_type(
+ self._reference_section_name(section)
+ )
+ for ref in (
+ section.findall("./reference")
+ + section.findall("./referencegroup")
+ + section.findall(
+ "./xi:include", {"xi": "http://www.w3.org/2001/XInclude"}
+ )
+ ):
+ name = self._document_name(ref)
+ if name:
+ refs[name] = ref_type
return refs