interest: declare default CanBePrefix setting

refs #4581

Change-Id: I82de3b13c3010242fa7999a2564d4a5442dfd14b
diff --git a/src/interest.cpp b/src/interest.cpp
index f08eeb2..8fc04a3 100644
--- a/src/interest.cpp
+++ b/src/interest.cpp
@@ -23,7 +23,10 @@
 #include "util/random.hpp"
 #include "data.hpp"
 
+#include <boost/scope_exit.hpp>
+
 #include <cstring>
+#include <iostream>
 #include <sstream>
 
 namespace ndn {
@@ -35,16 +38,27 @@
 static_assert(std::is_base_of<tlv::Error, Interest::Error>::value,
               "Interest::Error must inherit from tlv::Error");
 
+#ifdef NDN_CXX_HAVE_TESTS
+bool Interest::s_errorIfCanBePrefixUnset = true;
+#endif // NDN_CXX_HAVE_TESTS
+boost::logic::tribool Interest::s_defaultCanBePrefix = boost::logic::indeterminate;
+
 Interest::Interest(const Name& name, time::milliseconds lifetime)
   : m_name(name)
+  , m_isCanBePrefixSet(false)
   , m_interestLifetime(lifetime)
 {
   if (lifetime < time::milliseconds::zero()) {
     BOOST_THROW_EXCEPTION(std::invalid_argument("InterestLifetime must be >= 0"));
   }
+
+  if (!boost::logic::indeterminate(s_defaultCanBePrefix)) {
+    setCanBePrefix(static_cast<bool>(s_defaultCanBePrefix));
+  }
 }
 
 Interest::Interest(const Block& wire)
+  : m_isCanBePrefixSet(true)
 {
   wireDecode(wire);
 }
@@ -55,6 +69,20 @@
 size_t
 Interest::wireEncode(EncodingImpl<TAG>& encoder) const
 {
+  static bool hasDefaultCanBePrefixWarning = false;
+  if (!m_isCanBePrefixSet) {
+    if (!hasDefaultCanBePrefixWarning) {
+      std::cerr << "WARNING: Interest.CanBePrefix will be set to 0 in the near future. "
+                << "Please declare a preferred setting via Interest::setDefaultCanBePrefix.";
+      hasDefaultCanBePrefixWarning = true;
+    }
+#ifdef NDN_CXX_HAVE_TESTS
+     if (s_errorIfCanBePrefixUnset) {
+       BOOST_THROW_EXCEPTION(std::logic_error("Interest.CanBePrefix is unset"));
+     }
+#endif // NDN_CXX_HAVE_TESTS
+  }
+
   size_t totalLength = 0;
 
   // Interest ::= INTEREST-TYPE TLV-LENGTH
@@ -131,6 +159,8 @@
       setNonce(getNonce());
     }
   }
+
+  m_isCanBePrefixSet = true; // don't trigger warning from decoded packet
 }
 
 bool
@@ -493,6 +523,21 @@
 
 // ---- operators ----
 
+bool
+operator==(const Interest& lhs, const Interest& rhs)
+{
+  bool wasCanBePrefixSetOnLhs = lhs.m_isCanBePrefixSet;
+  bool wasCanBePrefixSetOnRhs = rhs.m_isCanBePrefixSet;
+  lhs.m_isCanBePrefixSet = true;
+  rhs.m_isCanBePrefixSet = true;
+  BOOST_SCOPE_EXIT_ALL(&) {
+    lhs.m_isCanBePrefixSet = wasCanBePrefixSetOnLhs;
+    rhs.m_isCanBePrefixSet = wasCanBePrefixSetOnRhs;
+  };
+
+  return lhs.wireEncode() == rhs.wireEncode();
+}
+
 std::ostream&
 operator<<(std::ostream& os, const Interest& interest)
 {
diff --git a/src/interest.hpp b/src/interest.hpp
index 7d0705e..1357352 100644
--- a/src/interest.hpp
+++ b/src/interest.hpp
@@ -27,6 +27,7 @@
 #include "packet-base.hpp"
 #include "selectors.hpp"
 #include "util/time.hpp"
+#include <boost/logic/tribool.hpp>
 
 namespace ndn {
 
@@ -147,6 +148,26 @@
     return *this;
   }
 
+  /** @brief Declare the default CanBePrefix setting of the application.
+   *
+   *  As part of transitioning to NDN Packet Format v0.3, the default setting for CanBePrefix
+   *  will be changed from "true" to "false". Application developers are advised to review all
+   *  Interests expressed by their application and decide what CanBePrefix setting is appropriate
+   *  for each Interest, to avoid breaking changes when the transition occurs. Application may
+   *  either set CanBePrefix on a per-Interest basis, or declare a default CanBePrefix setting for
+   *  all Interests expressed by the application using this function. If an application neither
+   *  declares a default nor sets CanBePrefix on every Interest, Interest::wireEncode will print a
+   *  one-time warning message.
+   *
+   *  @note This function should not be used in libraries or in ndn-cxx unit tests.
+   *  @sa https://redmine.named-data.net/projects/nfd/wiki/Packet03Transition
+   */
+  static void
+  setDefaultCanBePrefix(bool canBePrefix)
+  {
+    s_defaultCanBePrefix = canBePrefix;
+  }
+
   /** @brief Check whether the CanBePrefix element is present.
    *
    *  This is a getter for the CanBePrefix element as defined in NDN Packet Format v0.3.
@@ -171,6 +192,7 @@
   {
     m_selectors.setMaxSuffixComponents(canBePrefix ? -1 : 1);
     m_wire.reset();
+    m_isCanBePrefixSet = true;
     return *this;
   }
 
@@ -390,14 +412,26 @@
   void
   decode03();
 
+#ifdef NDN_CXX_HAVE_TESTS
+public:
+  /** @brief If true, not setting CanBePrefix results in an error in wireEncode().
+   */
+  static bool s_errorIfCanBePrefixUnset;
+#endif // NDN_CXX_HAVE_TESTS
+
 private:
+  static boost::logic::tribool s_defaultCanBePrefix;
+
   Name m_name;
   Selectors m_selectors;
+  mutable bool m_isCanBePrefixSet;
   mutable optional<uint32_t> m_nonce;
   time::milliseconds m_interestLifetime;
   DelegationList m_forwardingHint;
 
   mutable Block m_wire;
+
+  friend bool operator==(const Interest& lhs, const Interest& rhs);
 };
 
 NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(Interest);
@@ -405,11 +439,8 @@
 std::ostream&
 operator<<(std::ostream& os, const Interest& interest);
 
-inline bool
-operator==(const Interest& lhs, const Interest& rhs)
-{
-  return lhs.wireEncode() == rhs.wireEncode();
-}
+bool
+operator==(const Interest& lhs, const Interest& rhs);
 
 inline bool
 operator!=(const Interest& lhs, const Interest& rhs)
diff --git a/src/security/command-interest-signer.cpp b/src/security/command-interest-signer.cpp
index 70e80a1..d4cb33a 100644
--- a/src/security/command-interest-signer.cpp
+++ b/src/security/command-interest-signer.cpp
@@ -56,6 +56,7 @@
 CommandInterestSigner::makeCommandInterest(const Name& name, const SigningInfo& params)
 {
   Interest commandInterest(prepareCommandInterestName(name));
+  commandInterest.setCanBePrefix(false);
   m_keyChain.sign(commandInterest, params);
   return commandInterest;
 }
diff --git a/src/security/v2/certificate-bundle-fetcher.cpp b/src/security/v2/certificate-bundle-fetcher.cpp
index 45a0c38..227a434 100644
--- a/src/security/v2/certificate-bundle-fetcher.cpp
+++ b/src/security/v2/certificate-bundle-fetcher.cpp
@@ -95,9 +95,10 @@
                                                   const ValidationContinuation& continueValidation)
 {
   Interest bundleInterest = Interest(bundleNamePrefix);
-  bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
+  bundleInterest.setCanBePrefix(true);
   bundleInterest.setMustBeFresh(true);
   bundleInterest.setChildSelector(1);
+  bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
 
   m_face.expressInterest(bundleInterest,
                          [=] (const Interest& interest, const Data& data) {
@@ -123,8 +124,9 @@
   }
 
   Interest bundleInterest(fullBundleName.getPrefix(-1).append(segmentNo));
-  bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
+  bundleInterest.setCanBePrefix(false);
   bundleInterest.setMustBeFresh(false);
+  bundleInterest.setInterestLifetime(m_bundleInterestLifetime);
 
   m_face.expressInterest(bundleInterest,
                          [=] (const Interest& interest, const Data& data) {
diff --git a/src/security/v2/certificate-request.hpp b/src/security/v2/certificate-request.hpp
index c1d88dc..9bcb93b 100644
--- a/src/security/v2/certificate-request.hpp
+++ b/src/security/v2/certificate-request.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -46,6 +46,12 @@
   {
   }
 
+  explicit
+  CertificateRequest(const Name& name)
+    : CertificateRequest(Interest(name).setCanBePrefix(true))
+  {
+  }
+
 public:
   /// @brief the name for the requested data/certificate.
   Interest m_interest;
diff --git a/src/security/v2/validation-policy-config.cpp b/src/security/v2/validation-policy-config.cpp
index 2d82079..ff3f7bf 100644
--- a/src/security/v2/validation-policy-config.cpp
+++ b/src/security/v2/validation-policy-config.cpp
@@ -255,7 +255,7 @@
   for (const auto& rule : m_dataRules) {
     if (rule->match(tlv::Data, data.getName())) {
       if (rule->check(tlv::Data, data.getName(), klName, state)) {
-        return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
+        return continueValidation(make_shared<CertificateRequest>(klName), state);
       }
       // rule->check calls state->fail(...) if the check fails
       return;
@@ -284,7 +284,7 @@
   for (const auto& rule : m_interestRules) {
     if (rule->match(tlv::Interest, interest.getName())) {
       if (rule->check(tlv::Interest, interest.getName(), klName, state)) {
-        return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
+        return continueValidation(make_shared<CertificateRequest>(klName), state);
       }
       // rule->check calls state->fail(...) if the check fails
       return;
diff --git a/src/security/v2/validation-policy-simple-hierarchy.cpp b/src/security/v2/validation-policy-simple-hierarchy.cpp
index 0ad6128..e83c9bc 100644
--- a/src/security/v2/validation-policy-simple-hierarchy.cpp
+++ b/src/security/v2/validation-policy-simple-hierarchy.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2017 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -35,7 +35,7 @@
   }
 
   if (klName.getPrefix(-2).isPrefixOf(data.getName())) {
-    continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
+    continueValidation(make_shared<CertificateRequest>(klName), state);
   }
   else {
     state->fail({ValidationError::Code::INVALID_KEY_LOCATOR, "Data signing policy violation for " +
@@ -53,7 +53,7 @@
   }
 
   if (klName.getPrefix(-2).isPrefixOf(interest.getName())) {
-    continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
+    continueValidation(make_shared<CertificateRequest>(klName), state);
   }
   else {
     state->fail({ValidationError::Code::INVALID_KEY_LOCATOR, "Interest signing policy violation for " +
diff --git a/src/util/notification-subscriber.cpp b/src/util/notification-subscriber.cpp
index e69800a..f72aa58 100644
--- a/src/util/notification-subscriber.cpp
+++ b/src/util/notification-subscriber.cpp
@@ -79,6 +79,7 @@
     return;
 
   auto interest = make_shared<Interest>(m_prefix);
+  interest->setCanBePrefix(true);
   interest->setMustBeFresh(true);
   interest->setChildSelector(1);
   interest->setInterestLifetime(getInterestLifetime());
@@ -101,6 +102,7 @@
   nextName.appendSequenceNumber(m_lastSequenceNo + 1);
 
   auto interest = make_shared<Interest>(nextName);
+  interest->setCanBePrefix(false);
   interest->setInterestLifetime(getInterestLifetime());
 
   m_lastInterestId = m_face.expressInterest(*interest,