tools: split nfdc into multiple files

refs #3749

Change-Id: I24a3fd287773d0577d438e9ca24dc1dfab335a4f
diff --git a/tools/nfdc/face-id-fetcher.cpp b/tools/nfdc/face-id-fetcher.cpp
new file mode 100644
index 0000000..da09c11
--- /dev/null
+++ b/tools/nfdc/face-id-fetcher.cpp
@@ -0,0 +1,203 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "face-id-fetcher.hpp"
+
+#include <boost/lexical_cast.hpp>
+#include <boost/regex.hpp>
+
+#include <ndn-cxx/management/nfd-face-query-filter.hpp>
+#include <ndn-cxx/management/nfd-face-status.hpp>
+#include <ndn-cxx/util/segment-fetcher.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+FaceIdFetcher::FaceIdFetcher(ndn::Face& face,
+                             ndn::nfd::Controller& controller,
+                             bool allowCreate,
+                             const SuccessCallback& onSucceed,
+                             const FailureCallback& onFail)
+  : m_face(face)
+  , m_controller(controller)
+  , m_allowCreate(allowCreate)
+  , m_onSucceed(onSucceed)
+  , m_onFail(onFail)
+{
+}
+
+void
+FaceIdFetcher::start(ndn::Face& face,
+                     ndn::nfd::Controller& controller,
+                     const std::string& input,
+                     bool allowCreate,
+                     const SuccessCallback& onSucceed,
+                     const FailureCallback& onFail)
+{
+  // 1. Try parse input as FaceId, if input is FaceId, succeed with parsed FaceId
+  // 2. Try parse input as FaceUri, if input is not FaceUri, fail
+  // 3. Canonize faceUri
+  // 4. If canonization fails, fail
+  // 5. Query for face
+  // 6. If query succeeds and finds a face, succeed with found FaceId
+  // 7. Create face
+  // 8. If face creation succeeds, succeed with created FaceId
+  // 9. Fail
+
+  boost::regex e("^[a-z0-9]+\\:.*");
+  if (!boost::regex_match(input, e)) {
+    try {
+      uint32_t faceId = boost::lexical_cast<uint32_t>(input);
+      onSucceed(faceId);
+      return;
+    }
+    catch (const boost::bad_lexical_cast&) {
+      onFail("No valid faceId or faceUri is provided");
+      return;
+    }
+  }
+  else {
+    FaceUri faceUri;
+    if (!faceUri.parse(input)) {
+      onFail("FaceUri parse failed");
+      return;
+    }
+
+    auto fetcher = new FaceIdFetcher(std::ref(face), std::ref(controller),
+                                     allowCreate, onSucceed, onFail);
+    fetcher->startGetFaceId(faceUri);
+  }
+}
+
+void
+FaceIdFetcher::startGetFaceId(const FaceUri& faceUri)
+{
+  faceUri.canonize(bind(&FaceIdFetcher::onCanonizeSuccess, this, _1),
+                   bind(&FaceIdFetcher::onCanonizeFailure, this, _1),
+                   m_face.getIoService(), time::seconds(4));
+}
+
+void
+FaceIdFetcher::onCanonizeSuccess(const FaceUri& canonicalUri)
+{
+  ndn::Name queryName("/localhost/nfd/faces/query");
+  ndn::nfd::FaceQueryFilter queryFilter;
+  queryFilter.setRemoteUri(canonicalUri.toString());
+  queryName.append(queryFilter.wireEncode());
+
+  ndn::Interest interestPacket(queryName);
+  interestPacket.setMustBeFresh(true);
+  interestPacket.setInterestLifetime(time::milliseconds(4000));
+  auto interest = std::make_shared<ndn::Interest>(interestPacket);
+
+  ndn::util::SegmentFetcher::fetch(
+    m_face, *interest, m_validator,
+    bind(&FaceIdFetcher::onQuerySuccess, this, _1, canonicalUri),
+    bind(&FaceIdFetcher::onQueryFailure, this, _1, canonicalUri));
+}
+
+void
+FaceIdFetcher::onCanonizeFailure(const std::string& reason)
+{
+  fail("Canonize faceUri failed : " + reason);
+}
+
+void
+FaceIdFetcher::onQuerySuccess(const ndn::ConstBufferPtr& data,
+                              const FaceUri& canonicalUri)
+{
+  size_t offset = 0;
+  bool isOk = false;
+  ndn::Block block;
+  std::tie(isOk, block) = ndn::Block::fromBuffer(data, offset);
+
+  if (!isOk) {
+    if (m_allowCreate) {
+      startFaceCreate(canonicalUri);
+    }
+    else {
+      fail("Fail to find faceId");
+    }
+  }
+  else {
+    try {
+      ndn::nfd::FaceStatus status(block);
+      succeed(status.getFaceId());
+    }
+    catch (const ndn::tlv::Error& e) {
+      std::string errorMessage(e.what());
+      fail("ERROR: " + errorMessage);
+    }
+  }
+}
+
+void
+FaceIdFetcher::onQueryFailure(uint32_t errorCode,
+                              const FaceUri& canonicalUri)
+{
+  std::stringstream ss;
+  ss << "Cannot fetch data (code " << errorCode << ")";
+  fail(ss.str());
+}
+
+void
+FaceIdFetcher::onFaceCreateError(uint32_t code,
+                                 const std::string& error,
+                                 const std::string& message)
+{
+  std::stringstream ss;
+  ss << message << " : " << error << " (code " << code << ")";
+  fail(ss.str());
+}
+
+void
+FaceIdFetcher::startFaceCreate(const FaceUri& canonicalUri)
+{
+  ndn::nfd::ControlParameters parameters;
+  parameters.setUri(canonicalUri.toString());
+
+  m_controller.start<ndn::nfd::FaceCreateCommand>(parameters,
+    [this] (const ndn::nfd::ControlParameters& result) { succeed(result.getFaceId()); },
+    bind(&FaceIdFetcher::onFaceCreateError, this, _1, _2, "Face creation failed"));
+}
+
+void
+FaceIdFetcher::succeed(uint32_t faceId)
+{
+  m_onSucceed(faceId);
+  delete this;
+}
+
+void
+FaceIdFetcher::fail(const std::string& reason)
+{
+  m_onFail(reason);
+  delete this;
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/face-id-fetcher.hpp b/tools/nfdc/face-id-fetcher.hpp
new file mode 100644
index 0000000..50e0083
--- /dev/null
+++ b/tools/nfdc/face-id-fetcher.hpp
@@ -0,0 +1,114 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_FACE_ID_FETCHER_HPP
+#define NFD_TOOLS_NFDC_FACE_ID_FETCHER_HPP
+
+#include "core/common.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+#include <ndn-cxx/util/face-uri.hpp>
+#include <ndn-cxx/security/validator-null.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+using ndn::util::FaceUri;
+
+class FaceIdFetcher
+{
+public:
+  typedef std::function<void(uint32_t)> SuccessCallback;
+  typedef std::function<void(const std::string&)> FailureCallback;
+
+  /** \brief obtain FaceId from input
+   *  \param face Reference to the Face that should be used to fetch data
+   *  \param controller Reference to the controller that should be used to sign the Interest
+   *  \param input User input, either FaceId or FaceUri
+   *  \param allowCreate Whether creating face is allowed
+   *  \param onSucceed Callback to be fired when faceId is obtained
+   *  \param onFail Callback to be fired when an error occurs
+   */
+  static void
+  start(ndn::Face& face,
+        ndn::nfd::Controller& controller,
+        const std::string& input,
+        bool allowCreate,
+        const SuccessCallback& onSucceed,
+        const FailureCallback& onFail);
+
+private:
+  FaceIdFetcher(ndn::Face& face,
+                ndn::nfd::Controller& controller,
+                bool allowCreate,
+                const SuccessCallback& onSucceed,
+                const FailureCallback& onFail);
+
+  void
+  onQuerySuccess(const ndn::ConstBufferPtr& data,
+                 const FaceUri& canonicalUri);
+
+  void
+  onQueryFailure(uint32_t errorCode,
+                 const FaceUri& canonicalUri);
+
+  void
+  onCanonizeSuccess(const FaceUri& canonicalUri);
+
+  void
+  onCanonizeFailure(const std::string& reason);
+
+  void
+  startGetFaceId(const FaceUri& faceUri);
+
+  void
+  startFaceCreate(const FaceUri& canonicalUri);
+
+  void
+  onFaceCreateError(uint32_t code,
+                    const std::string& error,
+                    const std::string& message);
+
+  void
+  succeed(uint32_t faceId);
+
+  void
+  fail(const std::string& reason);
+
+private:
+  ndn::Face& m_face;
+  ndn::nfd::Controller& m_controller;
+  bool m_allowCreate;
+  SuccessCallback m_onSucceed;
+  FailureCallback m_onFail;
+  ndn::ValidatorNull m_validator;
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_FACE_ID_FETCHER_HPP
diff --git a/tools/nfdc/legacy-nfdc.cpp b/tools/nfdc/legacy-nfdc.cpp
new file mode 100644
index 0000000..83a3db8
--- /dev/null
+++ b/tools/nfdc/legacy-nfdc.cpp
@@ -0,0 +1,290 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "legacy-nfdc.hpp"
+#include "face-id-fetcher.hpp"
+
+#include <boost/regex.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+using ndn::nfd::ControlParameters;
+
+const time::milliseconds LegacyNfdc::DEFAULT_EXPIRATION_PERIOD = time::milliseconds::max();
+const uint64_t LegacyNfdc::DEFAULT_COST = 0;
+
+LegacyNfdc::LegacyNfdc(ndn::Face& face)
+  : m_flags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT)
+  , m_cost(DEFAULT_COST)
+  , m_origin(ndn::nfd::ROUTE_ORIGIN_STATIC)
+  , m_expires(DEFAULT_EXPIRATION_PERIOD)
+  , m_facePersistency(ndn::nfd::FACE_PERSISTENCY_PERSISTENT)
+  , m_face(face)
+  , m_controller(face, m_keyChain)
+{
+}
+
+bool
+LegacyNfdc::dispatch(const std::string& command)
+{
+  if (command == "add-nexthop") {
+    if (m_nOptions != 2)
+      return false;
+    fibAddNextHop();
+  }
+  else if (command == "remove-nexthop") {
+    if (m_nOptions != 2)
+      return false;
+    fibRemoveNextHop();
+  }
+  else if (command == "register") {
+    if (m_nOptions != 2)
+      return false;
+    ribRegisterPrefix();
+  }
+  else if (command == "unregister") {
+    if (m_nOptions != 2)
+      return false;
+    ribUnregisterPrefix();
+  }
+  else if (command == "create") {
+    if (m_nOptions != 1)
+      return false;
+    faceCreate();
+  }
+  else if (command == "destroy") {
+    if (m_nOptions != 1)
+      return false;
+    faceDestroy();
+  }
+  else if (command == "set-strategy") {
+    if (m_nOptions != 2)
+      return false;
+    strategyChoiceSet();
+  }
+  else if (command == "unset-strategy") {
+    if (m_nOptions != 1)
+      return false;
+    strategyChoiceUnset();
+  }
+  else
+    return false;
+
+  return true;
+}
+
+void
+LegacyNfdc::fibAddNextHop()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, true,
+    [this] (const uint32_t faceId) {
+      ControlParameters parameters;
+      parameters
+        .setName(m_name)
+        .setCost(m_cost)
+        .setFaceId(faceId);
+
+      m_controller.start<ndn::nfd::FibAddNextHopCommand>(parameters,
+        bind(&LegacyNfdc::onSuccess, this, _1, "Nexthop insertion succeeded"),
+        bind(&LegacyNfdc::onError, this, _1, _2, "Nexthop insertion failed"));
+    },
+    bind(&LegacyNfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+LegacyNfdc::fibRemoveNextHop()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, false,
+    [this] (const uint32_t faceId) {
+      ControlParameters parameters;
+      parameters
+        .setName(m_name)
+        .setFaceId(faceId);
+
+      m_controller.start<ndn::nfd::FibRemoveNextHopCommand>(parameters,
+        bind(&LegacyNfdc::onSuccess, this, _1, "Nexthop removal succeeded"),
+        bind(&LegacyNfdc::onError, this, _1, _2, "Nexthop removal failed"));
+    },
+    bind(&LegacyNfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+LegacyNfdc::ribRegisterPrefix()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, true,
+    [this] (const uint32_t faceId) {
+      ControlParameters parameters;
+      parameters
+        .setName(m_name)
+        .setCost(m_cost)
+        .setFlags(m_flags)
+        .setOrigin(m_origin)
+        .setFaceId(faceId);
+
+      if (m_expires != DEFAULT_EXPIRATION_PERIOD)
+        parameters.setExpirationPeriod(m_expires);
+
+      m_controller.start<ndn::nfd::RibRegisterCommand>(parameters,
+        bind(&LegacyNfdc::onSuccess, this, _1, "Successful in name registration"),
+        bind(&LegacyNfdc::onError, this, _1, _2, "Failed in name registration"));
+    },
+    bind(&LegacyNfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+LegacyNfdc::ribUnregisterPrefix()
+{
+  m_name = m_commandLineArguments[0];
+  const std::string& faceName = m_commandLineArguments[1];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, false,
+    [this] (const uint32_t faceId) {
+      ControlParameters parameters;
+      parameters
+        .setName(m_name)
+        .setFaceId(faceId)
+        .setOrigin(m_origin);
+
+      m_controller.start<ndn::nfd::RibUnregisterCommand>(parameters,
+        bind(&LegacyNfdc::onSuccess, this, _1, "Successful in unregistering name"),
+        bind(&LegacyNfdc::onError, this, _1, _2, "Failed in unregistering name"));
+    },
+    bind(&LegacyNfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+LegacyNfdc::onCanonizeFailure(const std::string& reason)
+{
+  BOOST_THROW_EXCEPTION(Error(reason));
+}
+
+void
+LegacyNfdc::onObtainFaceIdFailure(const std::string& message)
+{
+  BOOST_THROW_EXCEPTION(Error(message));
+}
+
+void
+LegacyNfdc::faceCreate()
+{
+  boost::regex e("^[a-z0-9]+\\:.*");
+  if (!boost::regex_match(m_commandLineArguments[0], e))
+    BOOST_THROW_EXCEPTION(Error("invalid uri format"));
+
+  FaceUri faceUri;
+  faceUri.parse(m_commandLineArguments[0]);
+
+  faceUri.canonize(bind(&LegacyNfdc::startFaceCreate, this, _1),
+                   bind(&LegacyNfdc::onCanonizeFailure, this, _1),
+                   m_face.getIoService(), time::seconds(4));
+}
+
+void
+LegacyNfdc::startFaceCreate(const FaceUri& canonicalUri)
+{
+  ControlParameters parameters;
+  parameters.setUri(canonicalUri.toString());
+  parameters.setFacePersistency(m_facePersistency);
+
+  m_controller.start<ndn::nfd::FaceCreateCommand>(parameters,
+    bind(&LegacyNfdc::onSuccess, this, _1, "Face creation succeeded"),
+    bind(&LegacyNfdc::onError, this, _1, _2, "Face creation failed"));
+}
+
+void
+LegacyNfdc::faceDestroy()
+{
+  ControlParameters parameters;
+  const std::string& faceName = m_commandLineArguments[0];
+
+  FaceIdFetcher::start(m_face, m_controller, faceName, false,
+    [this] (const uint32_t faceId) {
+      ControlParameters faceParameters;
+      faceParameters.setFaceId(faceId);
+
+      m_controller.start<ndn::nfd::FaceDestroyCommand>(faceParameters,
+        bind(&LegacyNfdc::onSuccess, this, _1, "Face destroy succeeded"),
+        bind(&LegacyNfdc::onError, this, _1, _2, "Face destroy failed"));
+    },
+    bind(&LegacyNfdc::onObtainFaceIdFailure, this, _1));
+}
+
+void
+LegacyNfdc::strategyChoiceSet()
+{
+  const std::string& name = m_commandLineArguments[0];
+  const std::string& strategy = m_commandLineArguments[1];
+
+  ControlParameters parameters;
+  parameters
+    .setName(name)
+    .setStrategy(strategy);
+
+  m_controller.start<ndn::nfd::StrategyChoiceSetCommand>(parameters,
+    bind(&LegacyNfdc::onSuccess, this, _1, "Successfully set strategy choice"),
+    bind(&LegacyNfdc::onError, this, _1, _2, "Failed to set strategy choice"));
+}
+
+void
+LegacyNfdc::strategyChoiceUnset()
+{
+  const std::string& name = m_commandLineArguments[0];
+
+  ControlParameters parameters;
+  parameters.setName(name);
+
+  m_controller.start<ndn::nfd::StrategyChoiceUnsetCommand>(parameters,
+    bind(&LegacyNfdc::onSuccess, this, _1, "Successfully unset strategy choice"),
+    bind(&LegacyNfdc::onError, this, _1, _2, "Failed to unset strategy choice"));
+}
+
+void
+LegacyNfdc::onSuccess(const ControlParameters& commandSuccessResult, const std::string& message)
+{
+  std::cout << message << ": " << commandSuccessResult << std::endl;
+}
+
+void
+LegacyNfdc::onError(uint32_t code, const std::string& error, const std::string& message)
+{
+  std::ostringstream os;
+  os << message << ": " << error << " (code: " << code << ")";
+  BOOST_THROW_EXCEPTION(Error(os.str()));
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
diff --git a/tools/nfdc/legacy-nfdc.hpp b/tools/nfdc/legacy-nfdc.hpp
new file mode 100644
index 0000000..01ed7b9
--- /dev/null
+++ b/tools/nfdc/legacy-nfdc.hpp
@@ -0,0 +1,186 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TOOLS_NFDC_LEGACY_NFDC_HPP
+#define NFD_TOOLS_NFDC_LEGACY_NFDC_HPP
+
+#include "core/common.hpp"
+#include <ndn-cxx/face.hpp>
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/management/nfd-controller.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+class LegacyNfdc : noncopyable
+{
+public:
+  static const time::milliseconds DEFAULT_EXPIRATION_PERIOD;
+  static const uint64_t DEFAULT_COST;
+
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+  explicit
+  LegacyNfdc(ndn::Face& face);
+
+  bool
+  dispatch(const std::string& cmd);
+
+  /**
+   * \brief Adds a nexthop to a FIB entry
+   *
+   * If the FIB entry does not exist, it is inserted automatically
+   *
+   * cmd format:
+   *  [-c cost]  name faceId|faceUri
+   *
+   */
+  void
+  fibAddNextHop();
+
+  /**
+   * \brief Removes a nexthop from an existing FIB entry
+   *
+   * If the last nexthop record in a FIB entry is removed, the FIB entry is also deleted
+   *
+   * cmd format:
+   *  name faceId
+   *
+   */
+  void
+  fibRemoveNextHop();
+
+  /**
+   * \brief Registers name to the given faceId or faceUri
+   *
+   * cmd format:
+   *  [-I] [-C] [-c cost] name faceId|faceUri
+   */
+  void
+  ribRegisterPrefix();
+
+  /**
+   * \brief Unregisters name from the given faceId/faceUri
+   *
+   * cmd format:
+   *  name faceId/faceUri
+   */
+  void
+  ribUnregisterPrefix();
+
+  /**
+   * \brief Creates new face
+   *
+   * This command allows creation of UDP unicast and TCP faces only
+   *
+   * cmd format:
+   *  uri
+   *
+   */
+  void
+  faceCreate();
+
+  /**
+   * \brief Destroys face
+   *
+   * cmd format:
+   *  faceId|faceUri
+   *
+   */
+  void
+  faceDestroy();
+
+  /**
+   * \brief Sets the strategy for a namespace
+   *
+   * cmd format:
+   *  name strategy
+   *
+   */
+  void
+  strategyChoiceSet();
+
+  /**
+   * \brief Unset the strategy for a namespace
+   *
+   * cmd format:
+   *  name strategy
+   *
+   */
+  void
+  strategyChoiceUnset();
+
+private:
+
+  void
+  onSuccess(const ndn::nfd::ControlParameters& commandSuccessResult,
+            const std::string& message);
+
+  void
+  onError(uint32_t code, const std::string& error, const std::string& message);
+
+  void
+  onCanonizeFailure(const std::string& reason);
+
+  void
+  startFaceCreate(const ndn::util::FaceUri& canonicalUri);
+
+  void
+  onObtainFaceIdFailure(const std::string& message);
+
+public:
+  const char* m_programName;
+
+  // command parameters without leading 'cmd' component
+  const char* const* m_commandLineArguments;
+  int m_nOptions;
+  uint64_t m_flags;
+  uint64_t m_cost;
+  uint64_t m_faceId;
+  uint64_t m_origin;
+  time::milliseconds m_expires;
+  std::string m_name;
+  ndn::nfd::FacePersistency m_facePersistency;
+
+private:
+  ndn::KeyChain m_keyChain;
+  ndn::Face& m_face;
+  ndn::nfd::Controller m_controller;
+};
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+#endif // NFD_TOOLS_NFDC_LEGACY_NFDC_HPP
diff --git a/tools/nfdc/main.cpp b/tools/nfdc/main.cpp
new file mode 100644
index 0000000..7d9f763
--- /dev/null
+++ b/tools/nfdc/main.cpp
@@ -0,0 +1,182 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD 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.
+ *
+ * NFD 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
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "legacy-nfdc.hpp"
+#include "core/version.hpp"
+
+#include <boost/lexical_cast.hpp>
+
+namespace nfd {
+namespace tools {
+namespace nfdc {
+
+static void
+usage(const char* programName)
+{
+  std::cout << "Usage:\n" << programName  << " [-h] [-V] COMMAND [<Command Options>]\n"
+    "       -h print usage and exit\n"
+    "       -V print version and exit\n"
+    "\n"
+    "   COMMAND can be one of the following:\n"
+    "       register [-I] [-C] [-c cost] [-e expiration time] [-o origin] name <faceId | faceUri>\n"
+    "           register name to the given faceId or faceUri\n"
+    "           -I: unset CHILD_INHERIT flag\n"
+    "           -C: set CAPTURE flag\n"
+    "           -c: specify cost (default 0)\n"
+    "           -e: specify expiration time in ms\n"
+    "               (by default the entry remains in FIB for the lifetime of the associated face)\n"
+    "           -o: specify origin\n"
+    "               0 for Local producer applications, 128 for NLSR, 255(default) for static routes\n"
+    "       unregister [-o origin] name <faceId | faceUri>\n"
+    "           unregister name from the given faceId\n"
+    "       create [-P] <faceUri> \n"
+    "           Create a face in one of the following formats:\n"
+    "           UDP unicast:    udp[4|6]://<remote-IP-or-host>[:<remote-port>]\n"
+    "           TCP:            tcp[4|6]://<remote-IP-or-host>[:<remote-port>] \n"
+    "           -P: create permanent (instead of persistent) face\n"
+    "       destroy <faceId | faceUri> \n"
+    "           Destroy a face\n"
+    "       set-strategy <name> <strategy> \n"
+    "           Set the strategy for a namespace \n"
+    "       unset-strategy <name> \n"
+    "           Unset the strategy for a namespace \n"
+    "       add-nexthop [-c <cost>] <name> <faceId | faceUri>\n"
+    "           Add a nexthop to a FIB entry\n"
+    "           -c: specify cost (default 0)\n"
+    "       remove-nexthop <name> <faceId | faceUri> \n"
+    "           Remove a nexthop from a FIB entry\n"
+    << std::endl;
+}
+
+static int
+main(int argc, char** argv)
+{
+  ndn::Face face;
+  LegacyNfdc p(face);
+
+  p.m_programName = argv[0];
+
+  if (argc < 2) {
+    usage(p.m_programName);
+    return 0;
+  }
+
+  if (!strcmp(argv[1], "-h")) {
+    usage(p.m_programName);
+    return 0;
+  }
+
+  if (!strcmp(argv[1], "-V")) {
+    std::cout << NFD_VERSION_BUILD_STRING << std::endl;
+    return 0;
+  }
+
+  ::optind = 2; //start reading options from 2nd argument i.e. Command
+  int opt;
+  while ((opt = ::getopt(argc, argv, "ICc:e:o:P")) != -1) {
+    switch (opt) {
+      case 'I':
+        p.m_flags = p.m_flags & ~(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT);
+        break;
+
+      case 'C':
+        p.m_flags = p.m_flags | ndn::nfd::ROUTE_FLAG_CAPTURE;
+        break;
+
+      case 'c':
+        try {
+          p.m_cost = boost::lexical_cast<uint64_t>(::optarg);
+        }
+        catch (const boost::bad_lexical_cast&) {
+          std::cerr << "Error: cost must be in unsigned integer format" << std::endl;
+          return 1;
+        }
+        break;
+
+      case 'e':
+        uint64_t expires;
+        try {
+          expires = boost::lexical_cast<uint64_t>(::optarg);
+        }
+        catch (const boost::bad_lexical_cast&) {
+          std::cerr << "Error: expiration time must be in unsigned integer format" << std::endl;
+          return 1;
+        }
+        p.m_expires = ndn::time::milliseconds(expires);
+        break;
+
+      case 'o':
+        try {
+          p.m_origin = boost::lexical_cast<uint64_t>(::optarg);
+        }
+        catch (const boost::bad_lexical_cast&) {
+          std::cerr << "Error: origin must be in unsigned integer format" << std::endl;
+          return 1;
+        }
+        break;
+
+      case 'P':
+        p.m_facePersistency = ndn::nfd::FACE_PERSISTENCY_PERMANENT;
+        break;
+
+      default:
+        usage(p.m_programName);
+        return 1;
+    }
+  }
+
+  if (argc == ::optind) {
+    usage(p.m_programName);
+    return 1;
+  }
+
+  try {
+    p.m_commandLineArguments = argv + ::optind;
+    p.m_nOptions = argc - ::optind;
+
+    //argv[1] points to the command, so pass it to the dispatch
+    bool isOk = p.dispatch(argv[1]);
+    if (!isOk) {
+      usage(p.m_programName);
+      return 1;
+    }
+    face.processEvents();
+  }
+  catch (const std::exception& e) {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+    return 2;
+  }
+  return 0;
+}
+
+} // namespace nfdc
+} // namespace tools
+} // namespace nfd
+
+int
+main(int argc, char** argv)
+{
+  return nfd::tools::nfdc::main(argc, argv);
+}