update: Add/delete prefix to the conf file

refs: #4303

Change-Id: I401885bf76be90a5076365eebc311275461813c4
diff --git a/docs/manpages/nlsrc.rst b/docs/manpages/nlsrc.rst
index 83d7795..352a537 100644
--- a/docs/manpages/nlsrc.rst
+++ b/docs/manpages/nlsrc.rst
@@ -43,6 +43,11 @@
       ``name``
         The Name prefix to be advertised
 
+    ``advertise <name> save``
+
+      ``save``
+        Advertise a prefix and also save it to the nlsr.conf file for the next start of NLSR
+
   ``withdraw``
     Remove a Name prefix advertised through NLSR
 
@@ -51,6 +56,11 @@
       ``name``
         The Name prefix to be withdrawn
 
+    ``withdraw <name> delete``
+
+      ``delete``
+        Withdraw a prefix and also delete it from the nlsr.conf file
+
 Notes
 -----
 
diff --git a/src/nlsr.cpp b/src/nlsr.cpp
index 00f4d8e..6045935 100644
--- a/src/nlsr.cpp
+++ b/src/nlsr.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2018,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -68,7 +68,8 @@
   , m_prefixUpdateProcessor(m_dispatcher,
                             m_nlsrFace,
                             m_namePrefixList,
-                            m_nlsrLsdb)
+                            m_nlsrLsdb,
+                            m_configFileName)
   , m_nfdRibCommandProcessor(m_dispatcher,
                              m_namePrefixList,
                              m_nlsrLsdb)
diff --git a/src/update/manager-base.cpp b/src/update/manager-base.cpp
index 071fc3a..f8ab6d1 100644
--- a/src/update/manager-base.cpp
+++ b/src/update/manager-base.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2018,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -61,12 +61,35 @@
 
   // Only build a Name LSA if the added name is new
   if (m_namePrefixList.insert(castParams.getName())) {
-    NLSR_LOG_INFO("Advertising/Inserting name: " << castParams.getName() << "\n");
+    NLSR_LOG_INFO("Advertising name: " << castParams.getName() << "\n");
     m_lsdb.buildAndInstallOwnNameLsa();
+    if (castParams.hasFlags() && castParams.getFlags() == PREFIX_FLAG) {
+      NLSR_LOG_INFO("Saving name to the configuration file");
+      if (afterAdvertise(castParams.getName()) == true) {
+        return done(ndn::nfd::ControlResponse(205, "OK").setBody(parameters.wireEncode()));
+      }
+      else {
+        return done(ndn::nfd::ControlResponse(406, "Failed to open configuration file.")
+                    .setBody(parameters.wireEncode()));
+      }
+    }
     return done(ndn::nfd::ControlResponse(200, "OK").setBody(parameters.wireEncode()));
   }
-
-  return done(ndn::nfd::ControlResponse(204, "Prefix is already advetised/inserted.").setBody(parameters.wireEncode()));
+  else {
+    if (castParams.hasFlags() && castParams.getFlags() == PREFIX_FLAG) {
+      // Save an already advertised prefix
+      NLSR_LOG_INFO("Saving an already advertised name: " << castParams.getName() << "\n");
+      if (afterAdvertise(castParams.getName()) == true) {
+        return done(ndn::nfd::ControlResponse(205, "OK").setBody(parameters.wireEncode()));
+      }
+      else {
+        return done(ndn::nfd::ControlResponse(406, "Prefix is already Saved/Failed to open configuration file.")
+                    .setBody(parameters.wireEncode()));
+      }
+    }
+    return done(ndn::nfd::ControlResponse(204, "Prefix is already advertised/inserted.")
+                .setBody(parameters.wireEncode()));
+  }
 }
 
 void
@@ -82,10 +105,32 @@
   if (m_namePrefixList.remove(castParams.getName())) {
     NLSR_LOG_INFO("Withdrawing/Removing name: " << castParams.getName() << "\n");
     m_lsdb.buildAndInstallOwnNameLsa();
+    if (castParams.hasFlags() && castParams.getFlags() == PREFIX_FLAG) {
+      if (afterWithdraw(castParams.getName()) == true) {
+        return done(ndn::nfd::ControlResponse(205, "OK").setBody(parameters.wireEncode()));
+      }
+      else {
+        return done(ndn::nfd::ControlResponse(406, "Failed to open configuration file.")
+                    .setBody(parameters.wireEncode()));
+      }
+    }
     return done(ndn::nfd::ControlResponse(200, "OK").setBody(parameters.wireEncode()));
   }
-
-  return done(ndn::nfd::ControlResponse(204, "Prefix is already withdrawn/removed.").setBody(parameters.wireEncode()));
+  else {
+    if (castParams.hasFlags() && castParams.getFlags() == PREFIX_FLAG) {
+      // Delete an already withdrawn prefix
+      NLSR_LOG_INFO("Deleting an already withdrawn name: " << castParams.getName() << "\n");
+      if (afterWithdraw(castParams.getName()) == true) {
+        return done(ndn::nfd::ControlResponse(205, "OK").setBody(parameters.wireEncode()));
+      }
+      else {
+        return done(ndn::nfd::ControlResponse(406, "Prefix is already deleted/Failed to open configuration file.")
+                    .setBody(parameters.wireEncode()));
+      }
+    }
+    return done(ndn::nfd::ControlResponse(204, "Prefix is already withdrawn/removed.")
+                .setBody(parameters.wireEncode()));
+  }
 }
 
 } // namespace update
diff --git a/src/update/manager-base.hpp b/src/update/manager-base.hpp
index 0dddbd6..4d279c7 100644
--- a/src/update/manager-base.hpp
+++ b/src/update/manager-base.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2017,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -44,6 +44,8 @@
 
 namespace update {
 
+enum { PREFIX_FLAG = 1 };
+
 class ManagerBase : boost::noncopyable
 {
 public:
@@ -107,6 +109,8 @@
                      Lsdb& lsdb,
                      const std::string& module);
 
+  virtual ~CommandManagerBase() {}
+
   /*! \brief add desired name prefix to the advertised name prefix list
    *         or insert a prefix into the FIB if parameters is valid.
    */
@@ -125,6 +129,18 @@
                           const ndn::mgmt::ControlParameters& parameters,
                           const ndn::mgmt::CommandContinuation& done);
 
+  /*! \brief save an advertised prefix to the nlsr configuration file
+   *         returns bool from the overridden function while nullopt here
+   */
+  virtual ndn::optional<bool>
+  afterAdvertise(const ndn::Name& prefix) { return ndn::nullopt; }
+
+  /*! \brief save an advertised prefix to the nlsr configuration file
+   *         returns bool from the overridden function while nullopt here
+   */
+  virtual ndn::optional<bool>
+  afterWithdraw(const ndn::Name& prefix) { return ndn::nullopt; }
+
 protected:
   NamePrefixList& m_namePrefixList;
   Lsdb& m_lsdb;
diff --git a/src/update/prefix-update-commands.cpp b/src/update/prefix-update-commands.cpp
index 0ec2542..e783086 100644
--- a/src/update/prefix-update-commands.cpp
+++ b/src/update/prefix-update-commands.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2017,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -29,6 +29,9 @@
 {
   m_requestValidator.required(ndn::nfd::CONTROL_PARAMETER_NAME);
   m_responseValidator.required(ndn::nfd::CONTROL_PARAMETER_NAME);
+
+  m_requestValidator.optional(ndn::nfd::CONTROL_PARAMETER_FLAGS);
+  m_responseValidator.optional(ndn::nfd::CONTROL_PARAMETER_FLAGS);
 }
 
 AdvertisePrefixCommand::AdvertisePrefixCommand()
@@ -36,6 +39,10 @@
 {
   m_requestValidator.required(ndn::nfd::CONTROL_PARAMETER_NAME);
   m_responseValidator.required(ndn::nfd::CONTROL_PARAMETER_NAME);
+
+  m_requestValidator.optional(ndn::nfd::CONTROL_PARAMETER_FLAGS);
+  m_responseValidator.optional(ndn::nfd::CONTROL_PARAMETER_FLAGS);
+
 }
 
 } // namespace update
diff --git a/src/update/prefix-update-processor.cpp b/src/update/prefix-update-processor.cpp
index 57bb579..3573ef0 100644
--- a/src/update/prefix-update-processor.cpp
+++ b/src/update/prefix-update-processor.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2018,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -23,8 +23,9 @@
 #include "lsdb.hpp"
 #include "nlsr.hpp"
 #include <ndn-cxx/mgmt/nfd/control-response.hpp>
-#include <ndn-cxx/tag.hpp>
 #include <ndn-cxx/face.hpp>
+#include <boost/algorithm/string.hpp>
+#include <algorithm>
 
 namespace nlsr {
 namespace update {
@@ -52,9 +53,10 @@
 PrefixUpdateProcessor::PrefixUpdateProcessor(ndn::mgmt::Dispatcher& dispatcher,
                                              ndn::Face& face,
                                              NamePrefixList& namePrefixList,
-                                             Lsdb& lsdb)
+                                             Lsdb& lsdb, const std::string& configFileName)
   : CommandManagerBase(dispatcher, namePrefixList, lsdb, "prefix-update")
   , m_validator(std::make_unique<ndn::security::v2::CertificateFetcherDirectFetch>(face))
+  , m_configFileName(configFileName)
 {
   NLSR_LOG_DEBUG("Setting dispatcher to capture Interests for: "
     << ndn::Name(Nlsr::LOCALHOST_PREFIX).append("prefix-update"));
@@ -102,5 +104,90 @@
   m_validator.load(section, filename);
 }
 
+bool
+PrefixUpdateProcessor::checkForPrefixInFile(const std::string prefix)
+{
+  std::string line;
+  std::fstream fp(m_configFileName);
+  if (!fp.good() || !fp.is_open()) {
+    NLSR_LOG_ERROR("Failed to open configuration file for parsing");
+    return true;
+  }
+  while (!fp.eof()) {
+    getline(fp, line);
+    if (line == prefix) {
+      return true;
+    }
+  }
+  fp.close();
+  return false;
+}
+
+bool
+PrefixUpdateProcessor::addOrDeletePrefix(const ndn::Name& prefix, bool addPrefix)
+{
+  std::string value = " prefix " + prefix.toUri();;
+  std::string fileString;
+  std::string line;
+  std::string trimedLine;
+  std::fstream input(m_configFileName, input.in);
+  if (!input.good() || !input.is_open()) {
+    NLSR_LOG_ERROR("Failed to open configuration file for parsing");
+    return false;
+  }
+
+  if (addPrefix) {
+    //check if prefix already exist in the nlsr configuration file
+    if (checkForPrefixInFile(value)) {
+      NLSR_LOG_ERROR("Prefix already exists in the configuration file");
+      return false;
+    }
+    while (!input.eof()) {
+      getline(input, line);
+      if (!line.empty()) {
+        fileString.append(line + "\n");
+        if (line == "advertising") {
+          getline(input, line);
+          fileString.append(line + "\n" + value + "\n");
+        }
+      }
+    }
+  }
+  else {
+    if (!checkForPrefixInFile(value)) {
+      NLSR_LOG_ERROR("Prefix doesn't exists in the configuration file");
+      return false;
+    }
+    boost::trim(value);
+    while (!input.eof()) {
+      getline(input, line);
+      if (!line.empty()) {
+        std::string trimLine = line;
+        boost::trim(trimLine);
+        if (trimLine != value) {
+          fileString.append(line + "\n");
+        }
+      }
+    }
+  }
+  input.close();
+  std::ofstream output(m_configFileName);
+  output << fileString;
+  output.close();
+  return true;
+}
+
+ndn::optional<bool>
+PrefixUpdateProcessor::afterAdvertise(const ndn::Name& prefix) {
+
+  return addOrDeletePrefix(prefix, true);
+}
+
+ndn::optional<bool>
+PrefixUpdateProcessor::afterWithdraw(const ndn::Name& prefix) {
+
+  return addOrDeletePrefix(prefix, false);
+}
+
 } // namespace update
 } // namespace nlsr
diff --git a/src/update/prefix-update-processor.hpp b/src/update/prefix-update-processor.hpp
index a602497..4491448 100644
--- a/src/update/prefix-update-processor.hpp
+++ b/src/update/prefix-update-processor.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2017,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -24,15 +24,11 @@
 
 #include "manager-base.hpp"
 #include "prefix-update-commands.hpp"
-#include "test-access-control.hpp"
-
 #include <ndn-cxx/util/io.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
-#include <ndn-cxx/security/v2/certificate-storage.hpp>
 #include <ndn-cxx/util/io.hpp>
-
 #include <boost/property_tree/ptree.hpp>
-#include <memory>
+#include <boost/property_tree/info_parser.hpp>
 
 namespace nlsr {
 
@@ -50,7 +46,7 @@
   PrefixUpdateProcessor(ndn::mgmt::Dispatcher& dispatcher,
                         ndn::Face& face,
                         NamePrefixList& namePrefixList,
-                        Lsdb& lsdb);
+                        Lsdb& lsdb, const std::string& configFileName);
 
   /*! \brief Load the validator's configuration from a section of a
    * configuration file.
@@ -66,6 +62,22 @@
   void
   loadValidator(ConfigSection section, const std::string& filename);
 
+  /*! \brief Add or delete an advertise or withdrawn prefix to the nlsr
+   * configuration file
+   */
+  bool
+  addOrDeletePrefix(const ndn::Name& prefix, bool addPrefix);
+
+  ndn::optional<bool>
+  afterAdvertise(const ndn::Name& prefix);
+
+  ndn::optional<bool>
+  afterWithdraw(const ndn::Name& prefix);
+
+  /*! \brief Check if a prefix exists in the nlsr configuration file */
+  bool
+  checkForPrefixInFile(const std::string prefix);
+
   ndn::security::ValidatorConfig&
   getValidator()
   {
@@ -84,6 +96,7 @@
 
 private:
   ndn::security::ValidatorConfig m_validator;
+  const std::string& m_configFileName;
 };
 
 } // namespace update
diff --git a/tests/update/test-save-delete-prefix.cpp b/tests/update/test-save-delete-prefix.cpp
new file mode 100644
index 0000000..7bbe06e
--- /dev/null
+++ b/tests/update/test-save-delete-prefix.cpp
@@ -0,0 +1,245 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2019,  The University of Memphis,
+ *                           Regents of the University of California,
+ *                           Arizona Board of Regents.
+ *
+ * This file is part of NLSR (Named-data Link State Routing).
+ * See AUTHORS.md for complete list of NLSR authors and contributors.
+ *
+ * NLSR is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NLSR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NLSR, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#include "update/prefix-update-processor.hpp"
+#include "../control-commands.hpp"
+#include "../test-common.hpp"
+#include "nlsr.hpp"
+
+#include <ndn-cxx/interest.hpp>
+#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
+#include <ndn-cxx/mgmt/nfd/control-response.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/security/pib/identity.hpp>
+#include <ndn-cxx/security/signing-helpers.hpp>
+#include <ndn-cxx/security/command-interest-signer.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+#include <boost/filesystem.hpp>
+
+namespace nlsr {
+namespace update {
+namespace test {
+
+namespace pt = boost::property_tree;
+using namespace pt;
+
+class PrefixSaveDeleteFixture : public nlsr::test::UnitTestTimeFixture
+{
+public:
+  PrefixSaveDeleteFixture()
+    : face(m_ioService, m_keyChain, {true, true})
+    , siteIdentityName(ndn::Name("/edu/test-site"))
+    , opIdentityName(ndn::Name("/edu/test-site").append(ndn::Name("%C1.Operator")))
+    , nlsr(m_ioService, m_scheduler, face, m_keyChain)
+    , SITE_CERT_PATH(boost::filesystem::current_path() / std::string("site.cert"))
+    , testConfFile("/tmp/nlsr.conf.test")
+    , counter(0)
+  {
+    std::ifstream source("/usr/local/etc/ndn/nlsr.conf.sample", std::ios::binary);
+    std::ofstream destination("/tmp/nlsr.conf.test", std::ios::binary);
+    destination << source.rdbuf();
+    source.close();
+    destination.close();
+
+    nlsr.setConfFileName(testConfFile);
+    siteIdentity = addIdentity(siteIdentityName);
+    saveCertificate(siteIdentity, SITE_CERT_PATH.string());
+
+    // Operator cert
+    opIdentity = addSubCertificate(opIdentityName, siteIdentity);
+    // Loading the security section's validator part into the validator
+    // See conf file processor for more details
+    std::ifstream inputFile;
+    inputFile.open(testConfFile);
+    BOOST_REQUIRE(inputFile.is_open());
+    pt::ptree pt;
+    boost::property_tree::read_info(inputFile, pt);
+    // Loads section and file name
+    for (const auto& section : pt) {
+      if (section.first == "security") {
+        auto it = section.second.begin();
+        it++;
+        if (it != pt.end() && it->first == "prefix-update-validator") {
+          nlsr.getPrefixUpdateProcessor().loadValidator(it->second, std::string(testConfFile));
+        }
+          break;
+      }
+    }
+    inputFile.close();
+    nlsr.loadCertToPublish(opIdentity.getDefaultKey().getDefaultCertificate());
+    // Site cert
+    siteIdentity = addIdentity(siteIdentityName);
+    saveCertificate(siteIdentity, SITE_CERT_PATH.string());
+
+    // Operator cert
+    opIdentity = addSubCertificate(opIdentityName, siteIdentity);
+    nlsr.loadCertToPublish(opIdentity.getDefaultKey().getDefaultCertificate());
+
+    // Set the network so the LSA prefix is constructed
+    nlsr.getConfParameter().setNetwork("/ndn");
+    nlsr.getConfParameter().setSiteName("/edu/test-site");
+    nlsr.getConfParameter().setRouterName("/%C1.Router/this-router");
+    nlsr.getConfParameter().buildRouterPrefix();
+    addIdentity(nlsr.getConfParameter().getRouterPrefix());
+
+    // Initialize NLSR so a sync socket is created
+    nlsr.initialize();
+    this->advanceClocks(ndn::time::milliseconds(10));
+    face.sentInterests.clear();
+  }
+
+  uint32_t
+  getResponseCode()
+  {
+    ndn::nfd::ControlResponse response;
+    for (const auto& data : face.sentData) {
+      response.wireDecode(data.getContent().blockFromValue());
+    }
+    return response.getCode();
+  }
+
+  bool
+  checkPrefix(const std::string prefixName)
+  {
+    counter = 0;
+    pt::ptree m_savePrefix;
+    pt::info_parser::read_info(testConfFile, m_savePrefix);
+    // counter helps to check if multiple prefix of same name exists on conf file
+    for (const auto& section : m_savePrefix.get_child("advertising")) {
+      std:: string b = section.second.get_value<std::string>();
+      if (b == prefixName) {
+        counter++;
+      }
+    }
+    if (counter > 0) {
+      return true;
+    }
+    return false;
+  }
+
+  ndn::Interest
+  advertiseWithdraw(std::string prefixName, std::string type, bool P_FLAG)
+  {
+    ndn::nfd::ControlParameters parameters;
+    parameters.setName(prefixName);
+    if (P_FLAG)
+    {
+      parameters.setFlags(PREFIX_FLAG);
+    }
+    ndn::Name advertiseCommand("/localhost/nlsr/prefix-update/advertise");
+    ndn::Name withdrawCommand("/localhost/nlsr/prefix-update/withdraw");
+    ndn:: Interest interestName;
+    ndn::security::CommandInterestSigner cis(m_keyChain);
+    // type true for advertise, else withdraw
+    if (type == "advertise") {
+      advertiseCommand.append(parameters.wireEncode());
+      interestName = cis.makeCommandInterest(advertiseCommand,
+                                             ndn::security::signingByIdentity(opIdentity));
+    }
+    else {
+      withdrawCommand.append(parameters.wireEncode());
+      interestName = cis.makeCommandInterest(withdrawCommand,
+                                             ndn::security::signingByIdentity(opIdentity));
+    }
+    return interestName;
+  }
+
+public:
+  ndn::util::DummyClientFace face;
+  ndn::Name siteIdentityName;
+  ndn::security::pib::Identity siteIdentity;
+
+  ndn::Name opIdentityName;
+  ndn::security::pib::Identity opIdentity;
+
+  Nlsr nlsr;
+  const boost::filesystem::path SITE_CERT_PATH;
+  ndn::Name sessionTime;
+  std::string testConfFile;
+  int counter;
+};
+
+BOOST_FIXTURE_TEST_SUITE(TestAdvertiseWithdrawPrefix, PrefixSaveDeleteFixture)
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  // only advertise
+  face.receive(advertiseWithdraw("/prefix/to/save", "advertise", false));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  BOOST_CHECK_EQUAL(checkPrefix("/prefix/to/save"), false);
+  BOOST_CHECK_EQUAL(getResponseCode(), 200);
+  face.sentData.clear();
+
+  // trying to re-advertise
+  face.receive(advertiseWithdraw("/prefix/to/save", "advertise", false));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  BOOST_CHECK_EQUAL(getResponseCode(), 204);
+  face.sentData.clear();
+
+  // only withdraw
+  face.receive(advertiseWithdraw("/prefix/to/save", "withdraw", false));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  BOOST_CHECK_EQUAL(checkPrefix("/prefix/to/save"), false);
+  BOOST_CHECK_EQUAL(getResponseCode(), 200);
+  face.sentData.clear();
+
+  // trying to re-advertise
+  face.receive(advertiseWithdraw("/prefix/to/save", "withdraw", false));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  BOOST_CHECK_EQUAL(getResponseCode(), 204);
+  face.sentData.clear();
+}
+
+BOOST_AUTO_TEST_CASE(PrefixStillSavedAfterJustWithdrawn)
+{
+  // advertise and save
+  face.receive(advertiseWithdraw("/prefix/to/save", "advertise", true));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  face.sentData.clear();
+  BOOST_CHECK_EQUAL(checkPrefix("/prefix/to/save"), true);
+
+  // trying to advertise same name prefix
+  face.receive(advertiseWithdraw("/prefix/to/save", "advertise", true));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  BOOST_REQUIRE(counter == 1);
+  BOOST_CHECK_EQUAL(getResponseCode(), 406);
+  face.sentData.clear();
+
+  // only withdraw
+  face.receive(advertiseWithdraw("/prefix/to/save", "withdraw", false));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  // after withdrawing only prefix should still be there
+  BOOST_CHECK_EQUAL(checkPrefix("/prefix/to/save"), true);
+  BOOST_CHECK_EQUAL(getResponseCode(), 200);
+
+  // delete
+  face.receive(advertiseWithdraw("/prefix/to/save", "withdraw", true));
+  this->advanceClocks(ndn::time::milliseconds(10));
+  // after withdrawn delete prefix should be deleted from the file
+  BOOST_CHECK_EQUAL(getResponseCode(), 205);
+  BOOST_CHECK_EQUAL(checkPrefix("/prefix/to/save"), false);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace test
+} // namespace update
+} // namespace nlsr
diff --git a/tools/nlsrc.cpp b/tools/nlsrc.cpp
index 1228bc8..56b1d77 100644
--- a/tools/nlsrc.cpp
+++ b/tools/nlsrc.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2018,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -46,6 +46,7 @@
 
 const uint32_t Nlsrc::ERROR_CODE_TIMEOUT = 10060;
 const uint32_t Nlsrc::RESPONSE_CODE_SUCCESS = 200;
+const uint32_t Nlsrc::RESPONSE_CODE_SAVE_OR_DELETE = 205;
 
 Nlsrc::Nlsrc(ndn::Face& face)
   : m_face(face)
@@ -68,8 +69,12 @@
     "           display all NLSR status (lsdb & routingtable)\n"
     "       advertise name\n"
     "           advertise a name prefix through NLSR\n"
+    "       advertise name save\n"
+    "           advertise and save the name prefix to the conf file\n"
     "       withdraw name\n"
-    "           remove a name prefix advertised through NLSR"
+    "           remove a name prefix advertised through NLSR\n"
+    "       withdraw name delete\n"
+    "           withdraw and delete the name prefix from the conf file"
     << std::endl;
 }
 
@@ -100,23 +105,35 @@
 Nlsrc::dispatch(const std::string& command)
 {
   if (command == "advertise") {
-    if (nOptions != 1) {
+    if (nOptions < 0) {
       return false;
     }
+    else if (nOptions == 1) {
+      std::string saveFlag = commandLineArguments[0];
+      if (saveFlag != "save") {
+        return false;
+      }
+    }
 
     advertiseName();
     return true;
   }
   else if (command == "withdraw") {
-    if (nOptions != 1) {
+    if (nOptions < 0) {
       return false;
     }
+    else if (nOptions == 1) {
+      std::string saveFlag = commandLineArguments[0];
+      if (saveFlag != "delete") {
+        return false;
+      }
+    }
 
     withdrawName();
     return true;
   }
-  else if ((command == "lsdb")|| (command == "routing")||(command == "status")) {
-    if (nOptions != 0) {
+  else if ((command == "lsdb") || (command == "routing") || (command == "status")) {
+    if (nOptions != -1) {
       return false;
     }
     commandString = command;
@@ -144,30 +161,44 @@
 void
 Nlsrc::advertiseName()
 {
-  ndn::Name name = commandLineArguments[0];
-  ndn::Name::Component verb("advertise");
-  std::string info = "(Advertise: " + name.toUri() + ")";
+  ndn::Name name = commandLineArguments[-1];
 
-  sendNamePrefixUpdate(name, verb, info);
+  bool saveFlag = false;
+  std::string info = "(Advertise: " + name.toUri() + ")";
+  if (commandLineArguments[0]) {
+    saveFlag = true;
+    info = "(Save: " + name.toUri() + ")";
+  }
+  ndn::Name::Component verb("advertise");
+  sendNamePrefixUpdate(name, verb, info, saveFlag);
 }
 
 void
 Nlsrc::withdrawName()
 {
-  ndn::Name name = commandLineArguments[0];
-  ndn::Name::Component verb("withdraw");
-  std::string info = "(Withdraw: " + name.toUri() + ")";
+  ndn::Name name = commandLineArguments[-1];
 
-  sendNamePrefixUpdate(name, verb, info);
+  bool deleteFlag = false;
+  std::string info = "(Withdraw: " + name.toUri() + ")";
+  if (commandLineArguments[0]) {
+    deleteFlag = true;
+    info = "(Delete: " + name.toUri() + ")";
+  }
+  ndn::Name::Component verb("withdraw");
+  sendNamePrefixUpdate(name, verb, info, deleteFlag);
 }
 
 void
 Nlsrc::sendNamePrefixUpdate(const ndn::Name& name,
                             const ndn::Name::Component& verb,
-                            const std::string& info)
+                            const std::string& info,
+                            bool flag)
 {
   ndn::nfd::ControlParameters parameters;
   parameters.setName(name);
+  if (flag) {
+    parameters.setFlags(1);
+  }
 
   ndn::Name commandName = NAME_UPDATE_PREFIX;
   commandName.append(verb);
@@ -208,7 +239,9 @@
 
   uint32_t code = response.getCode();
 
-  if (code != RESPONSE_CODE_SUCCESS) {
+  if (code != RESPONSE_CODE_SUCCESS && code != RESPONSE_CODE_SAVE_OR_DELETE) {
+
+    std::cerr << response.getText() << std::endl;
     std::cerr << "Name prefix update error (code: " << code << ")" << std::endl;
     return;
   }
@@ -474,7 +507,7 @@
   }
 
   try {
-    ::optind = 2; // Set ::optind to the command's index
+    ::optind = 3; // Set ::optind to the command's index
 
     nlsrc.commandLineArguments = argv + ::optind;
     nlsrc.nOptions = argc - ::optind;
diff --git a/tools/nlsrc.hpp b/tools/nlsrc.hpp
index cc5a1f6..e05891c 100644
--- a/tools/nlsrc.hpp
+++ b/tools/nlsrc.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2018,  The University of Memphis,
+ * Copyright (c) 2014-2019,  The University of Memphis,
  *                           Regents of the University of California,
  *                           Arizona Board of Regents.
  *
@@ -81,7 +81,8 @@
   void
   sendNamePrefixUpdate(const ndn::Name& name,
                        const ndn::Name::Component& verb,
-                       const std::string& info);
+                       const std::string& info,
+                       bool saveFlag);
 
   void
   onControlResponse(const std::string& info, const ndn::Data& data);
@@ -182,6 +183,8 @@
 
   static const uint32_t ERROR_CODE_TIMEOUT;
   static const uint32_t RESPONSE_CODE_SUCCESS;
+  static const uint32_t RESPONSE_CODE_SAVE_OR_DELETE;
+
 };
 
 } // namespace nlsrc