add logger response

Change-Id: I2a2888eaf27671374ba8ed7f36ca2d1ffb736ea4
diff --git a/core/logger-response.cpp b/core/logger-response.cpp
new file mode 100644
index 0000000..cef7e64
--- /dev/null
+++ b/core/logger-response.cpp
@@ -0,0 +1,131 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL 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.
+ *
+ * NSL 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
+ * NSL, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#include "logger-response.hpp"
+#include "tlv.hpp"
+
+namespace nsl {
+
+LoggerResponse::LoggerResponse()
+  : m_code(-1)
+{
+}
+
+LoggerResponse::LoggerResponse(int32_t code, const std::string& msg)
+  : m_code(code)
+  , m_msg(msg)
+  , m_dataSeqNo(0)
+{
+}
+
+LoggerResponse::LoggerResponse(const NonNegativeInteger& seqNo)
+  : m_code(0)
+  , m_dataSeqNo(seqNo)
+{
+}
+
+template<ndn::encoding::Tag TAG>
+size_t
+LoggerResponse::wireEncode(ndn::EncodingImpl<TAG>& block) const
+{
+  size_t totalLength = 0;
+
+  if (m_code != 0) {
+    totalLength += block.prependByteArrayBlock(tlv::ResultMsg,
+                                               reinterpret_cast<const uint8_t*>(m_msg.c_str()),
+                                               m_msg.size());
+  }
+  else {
+    totalLength += ndn::prependNonNegativeIntegerBlock(block, tlv::DataSeqNo, m_dataSeqNo);
+  }
+  totalLength += ndn::prependNonNegativeIntegerBlock(block, tlv::ResultCode, m_code);
+
+  totalLength += block.prependVarNumber(totalLength);
+  totalLength += block.prependVarNumber(tlv::LogResponse);
+
+  return totalLength;
+}
+
+template size_t
+LoggerResponse::wireEncode<ndn::encoding::EncoderTag>(ndn::EncodingImpl<ndn::encoding::EncoderTag>&) const;
+
+template size_t
+LoggerResponse::wireEncode<ndn::encoding::EstimatorTag>(ndn::EncodingImpl<ndn::encoding::EstimatorTag>&) const;
+
+
+const Block&
+LoggerResponse::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  ndn::EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  ndn::EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+LoggerResponse::wireDecode(const Block& wire)
+{
+  if (!wire.hasWire()) {
+    throw Error("The supplied block does not contain wire format");
+  }
+
+  m_wire = wire;
+  m_wire.parse();
+
+  if (m_wire.type() != tlv::LogResponse)
+    throw tlv::Error("Unexpected TLV type when decoding log response");
+
+  Block::element_const_iterator it = m_wire.elements_begin();
+
+  // the first block must be result code
+  if (it != m_wire.elements_end() && it->type() == tlv::ResultCode) {
+    m_code = readNonNegativeInteger(*it);
+    it++;
+  }
+  else
+    throw Error("The first sub-TLV is not ResultCode");
+
+  // the second block could be result msg
+  if (it == m_wire.elements_end())
+    return;
+  else if (it->type() == tlv::ResultMsg) {
+    m_msg = std::string(reinterpret_cast<const char*>(it->value()), it->value_size());
+    it++;
+  }
+  else if (it->type() == tlv::DataSeqNo) {
+    m_dataSeqNo = readNonNegativeInteger(*it);
+    it++;
+  }
+  else
+    throw Error("The second sub-TLV is not ResultMsg");
+
+  if (it != m_wire.elements_end())
+    throw Error("No more sub-TLV in log response");
+}
+
+} // namespace nsl
diff --git a/core/logger-response.hpp b/core/logger-response.hpp
new file mode 100644
index 0000000..8a393e4
--- /dev/null
+++ b/core/logger-response.hpp
@@ -0,0 +1,98 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL 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.
+ *
+ * NSL 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
+ * NSL, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#ifndef NSL_CORE_LOGGER_RESPONSE_HPP
+#define NSL_CORE_LOGGER_RESPONSE_HPP
+
+#include "common.hpp"
+#include "util/non-negative-integer.hpp"
+#include <ndn-cxx/encoding/buffer.hpp>
+
+namespace nsl {
+
+class LoggerResponse
+{
+public:
+  class Error : public std::runtime_error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+    }
+  };
+
+public:
+  LoggerResponse();
+
+  LoggerResponse(int32_t resultCode, const std::string& resultMsg);
+
+  LoggerResponse(const NonNegativeInteger& seqNo);
+
+  int32_t
+  getCode() const
+  {
+    return m_code;
+  }
+
+  const std::string&
+  getMsg() const
+  {
+    if (m_code == 0)
+      throw Error("Error msg is not available");
+
+    return m_msg;
+  }
+
+  const NonNegativeInteger&
+  getDataSeqNo() const
+  {
+    if (m_code != 0)
+      throw Error("Data seqNo is not available");
+
+    return m_dataSeqNo;
+  }
+
+  /// @brief Encode to a wire format or estimate wire format
+  template<ndn::encoding::Tag TAG>
+  size_t
+  wireEncode(ndn::EncodingImpl<TAG>& block) const;
+
+  /// @brief Encode to a wire format
+  const Block&
+  wireEncode() const;
+
+  /// @brief Decode from a wire format
+  void
+  wireDecode(const Block& wire);
+
+private:
+  int32_t m_code;
+  std::string m_msg; // optional
+  NonNegativeInteger m_dataSeqNo; // optional
+
+  mutable Block m_wire;
+};
+
+} // namespace nsl
+
+#endif // NSL_CORE_LOGGER_RESPONSE_HPP
diff --git a/core/tlv.hpp b/core/tlv.hpp
index 6dd7cac..56de322 100644
--- a/core/tlv.hpp
+++ b/core/tlv.hpp
@@ -33,7 +33,18 @@
   LoggerLeaf  = 128, // 0x80
   Timestamp   = 129, // 0x81
   DataSeqNo   = 130, // 0x82
-  SignerSeqNo = 131  // 0x83
+  SignerSeqNo = 131, // 0x83
+
+  LogResponse = 144, // 0x90
+  ResultCode  = 145, // 0x91
+  ResultMsg   = 146 // 0x92
+};
+
+enum {
+  LogResponse_Accept       = 0,
+  LogResponse_Error_Tree   = 1,
+  LogResponse_Error_Policy = 2,
+  LogResponse_Error_Signer = 3
 };
 
 } // namespace tlv
diff --git a/tests/core/logger-response.t.cpp b/tests/core/logger-response.t.cpp
new file mode 100644
index 0000000..37ea13a
--- /dev/null
+++ b/tests/core/logger-response.t.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014,  Regents of the University of California
+ *
+ * This file is part of NSL (NDN Signature Logger).
+ * See AUTHORS.md for complete list of NSL authors and contributors.
+ *
+ * NSL 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.
+ *
+ * NSL 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
+ * NSL, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of nsl authors and contributors.
+ */
+
+#include "logger-response.hpp"
+#include "cryptopp.hpp"
+
+#include "boost-test.hpp"
+
+namespace nsl {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(TestLoggerResponse)
+
+void
+printByte(const uint8_t* buf, size_t size)
+{
+  std::stringstream ss;
+  using namespace CryptoPP;
+  StringSource is(buf, size, true, new HexEncoder(new FileSink(ss), false));
+
+  std::string output = ss.str();
+  for (size_t i = 0; i < output.size(); i++) {
+    std::cerr << "0x" << output.at(i);
+    std::cerr << output.at(++i) << ", ";
+    if ((i + 1) % 32 == 0)
+      std::cerr << std::endl;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(Basic)
+{
+  LoggerResponse response1(5);
+  BOOST_CHECK_EQUAL(response1.getCode(), 0);
+  BOOST_CHECK_EQUAL(response1.getDataSeqNo(), 5);
+
+  LoggerResponse response2(1, "error");
+  BOOST_CHECK_EQUAL(response2.getCode(), 1);
+  BOOST_CHECK_EQUAL(response2.getMsg(), "error");
+}
+
+uint8_t RESPONSE1[] = {
+  0x90, 0x06,
+    0x91, 0x01, 0x00,
+    0x82, 0x01, 0x05
+};
+
+uint8_t RESPONSE2[] = {
+  0x90, 0x0a,
+    0x91, 0x01, 0x01,
+    0x92, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72
+};
+
+BOOST_AUTO_TEST_CASE(Encoding)
+{
+  LoggerResponse response1(5);
+  BOOST_CHECK_EQUAL_COLLECTIONS(response1.wireEncode().wire(),
+                                response1.wireEncode().wire() + response1.wireEncode().size(),
+                                RESPONSE1, RESPONSE1 + sizeof(RESPONSE1));
+
+  LoggerResponse response2(1, "error");
+  BOOST_CHECK_EQUAL_COLLECTIONS(response2.wireEncode().wire(),
+                                response2.wireEncode().wire() + response2.wireEncode().size(),
+                                RESPONSE2, RESPONSE2 + sizeof(RESPONSE2));
+}
+
+BOOST_AUTO_TEST_CASE(Decoding)
+{
+  LoggerResponse response1;
+  Block block1(RESPONSE1, sizeof(RESPONSE1));
+  BOOST_REQUIRE_NO_THROW(response1.wireDecode(block1));
+  BOOST_CHECK_EQUAL(response1.getCode(), 0);
+  BOOST_CHECK_EQUAL(response1.getDataSeqNo(), 5);
+
+  LoggerResponse response2;
+  Block block2(RESPONSE2, sizeof(RESPONSE2));
+  BOOST_REQUIRE_NO_THROW(response2.wireDecode(block2));
+  BOOST_CHECK_EQUAL(response2.getCode(), 1);
+  BOOST_CHECK_EQUAL(response2.getMsg(), "error");
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace nsl