From e0ca07e65d8a1872ff365c7ec3c5e3ae25796b46 Mon Sep 17 00:00:00 2001
From: Russ Housley <housley@vigilsec.com>
Date: Sat, 25 Jul 2020 21:09:50 +0000
Subject: [PATCH] Send email to AD when an IETF Last Call expires that contains
 downrefs. Fixes #2472  - Legacy-Id: 18276

---
 ietf/doc/lastcall.py                        |  7 +++++-
 ietf/doc/mails.py                           | 12 ++++++++++
 ietf/doc/tests_draft.py                     | 25 +++++++++++++++++++++
 ietf/templates/doc/mail/downrefs_notice.txt | 15 +++++++++++++
 4 files changed, 58 insertions(+), 1 deletion(-)
 create mode 100644 ietf/templates/doc/mail/downrefs_notice.txt

diff --git a/ietf/doc/lastcall.py b/ietf/doc/lastcall.py
index fa6446780..a00fc16d2 100644
--- a/ietf/doc/lastcall.py
+++ b/ietf/doc/lastcall.py
@@ -9,7 +9,7 @@ from ietf.doc.models import IESG_SUBSTATE_TAGS
 from ietf.person.models import Person
 from ietf.doc.utils import add_state_change_event
 from ietf.doc.mails import generate_ballot_writeup, generate_approval_mail, generate_last_call_announcement
-from ietf.doc.mails import send_last_call_request, email_last_call_expired
+from ietf.doc.mails import send_last_call_request, email_last_call_expired, email_last_call_expired_with_downref
 
 def request_last_call(request, doc):
     if not doc.latest_event(type="changed_ballot_writeup_text"):
@@ -65,3 +65,8 @@ def expire_last_call(doc):
         doc.save_with_history([e])
 
     email_last_call_expired(doc)
+
+    if doc.type_id == 'draft':
+        lc_text = doc.latest_event(LastCallDocEvent, type="sent_last_call").desc
+        if "document makes the following downward references" in lc_text:
+            email_last_call_expired_with_downref(doc, lc_text)           
\ No newline at end of file
diff --git a/ietf/doc/mails.py b/ietf/doc/mails.py
index 9737e73be..afd1c00fa 100644
--- a/ietf/doc/mails.py
+++ b/ietf/doc/mails.py
@@ -504,6 +504,18 @@ def email_last_call_expired(doc):
                    url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()),
               cc = addrs.cc)
 
+def email_last_call_expired_with_downref(doc, last_call_text):
+    if doc.type_id != 'draft':
+        return
+    send_mail(None,
+              (doc.ad.email().address, ),
+              "DraftTracker Mail System <iesg-secretary@ietf.org>",
+              "Review Downrefs From Expired Last Call: %s" % doc.file_tag(),
+              "doc/mail/downrefs_notice.txt",
+              dict(last_call_text=last_call_text,
+                   doc=doc,
+                   url=settings.IDTRACKER_BASE_URL + "/downref/add/"))
+
 def email_intended_status_changed(request, doc, text):
     (to,cc) = gather_address_lists('doc_intended_status_changed',doc=doc)
 
diff --git a/ietf/doc/tests_draft.py b/ietf/doc/tests_draft.py
index 0b2d8327e..945d0e9bd 100644
--- a/ietf/doc/tests_draft.py
+++ b/ietf/doc/tests_draft.py
@@ -794,6 +794,31 @@ class ExpireLastCallTests(TestCase):
         self.assertTrue('aread@' in outbox[-1]['To'])
         self.assertTrue('draft-ietf-mars-test@' in outbox[-1]['To'])
 
+    def test_expire_last_call_with_downref(self):
+        from ietf.doc.lastcall import get_expired_last_calls, expire_last_call
+
+        secretary = Person.objects.get(name="Sec Retary")
+        ad = Person.objects.get(user__username='ad')
+        draft = WgDraftFactory(ad=ad,name='draft-ietf-mars-test')
+        draft.set_state(State.objects.get(used=True, type="draft-iesg", slug="lc"))
+
+        e = LastCallDocEvent(doc=draft, rev=draft.rev, type="sent_last_call", by=secretary)
+        e.text = "Last call sent"
+        e.desc = "Blah, blah, blah.\n\nThis document makes the following downward references (downrefs):\n  ** Downref: Normative reference to an Experimental RFC: RFC 4764"
+        e.expires = datetime.datetime.now()
+        e.save()
+        
+        drafts = list(get_expired_last_calls())
+        self.assertEqual(len(drafts), 1)
+
+        mailbox_before = len(outbox)    
+        expire_last_call(drafts[0])
+
+        d = Document.objects.get(name=draft.name)
+        self.assertEqual(len(outbox), mailbox_before + 2)
+        self.assertTrue("Review Downrefs From Expired Last Call" in outbox[-1]["Subject"])
+        self.assertTrue(d.ad.email().address in outbox[-1]['To'])
+
 class IndividualInfoFormsTests(TestCase):
 
     def setUp(self):
diff --git a/ietf/templates/doc/mail/downrefs_notice.txt b/ietf/templates/doc/mail/downrefs_notice.txt
new file mode 100644
index 000000000..f4eefd2fb
--- /dev/null
+++ b/ietf/templates/doc/mail/downrefs_notice.txt
@@ -0,0 +1,15 @@
+{% autoescape off %}
+Please DO NOT reply to this email.
+
+I-D: {{ doc.file_tag|safe }}
+
+IETF Last Call recently ended for this Internet-Draft, and the Last Call
+included downward references (downrefs).
+
+If the Last Call consensus is that these RFCs are to be added to the
+downref registry, then please do so by going to {{ url }}.
+
+The Last Call announcement said... 
+
+{{ last_call_text }}
+{% endautoescape%}