ndnsec: improve error reporting when loading a Certificate or SafeBag fails

Refs: #5164
Change-Id: I6f594a921bb063ad808f311d8ff978bf0f7d528d
diff --git a/tools/ndnsec/cert-dump.cpp b/tools/ndnsec/cert-dump.cpp
index 5a388d7..9f86f1c 100644
--- a/tools/ndnsec/cert-dump.cpp
+++ b/tools/ndnsec/cert-dump.cpp
@@ -22,8 +22,6 @@
 #include "ndnsec.hpp"
 #include "util.hpp"
 
-#include "ndn-cxx/util/io.hpp"
-
 #include <boost/asio/ip/tcp.hpp>
 #if BOOST_VERSION < 106700
 #include <boost/date_time/posix_time/posix_time_duration.hpp>
@@ -109,13 +107,7 @@
 
   security::Certificate certificate;
   if (isFileName) {
-    try {
-      certificate = loadCertificate(name);
-    }
-    catch (const CannotLoadCertificate&) {
-      std::cerr << "ERROR: Cannot load the certificate from `" << name << "`" << std::endl;
-      return 1;
-    }
+    certificate = loadFromFile<security::Certificate>(name);
   }
   else {
     KeyChain keyChain;
diff --git a/tools/ndnsec/cert-gen.cpp b/tools/ndnsec/cert-gen.cpp
index 9c11ca4..8772062 100644
--- a/tools/ndnsec/cert-gen.cpp
+++ b/tools/ndnsec/cert-gen.cpp
@@ -124,14 +124,7 @@
 
   KeyChain keyChain;
 
-  security::Certificate certRequest;
-  try {
-    certRequest = loadCertificate(requestFile);
-  }
-  catch (const CannotLoadCertificate&) {
-    std::cerr << "ERROR: Cannot load the request from `" << requestFile << "`" << std::endl;
-    return 1;
-  }
+  auto certRequest = loadFromFile<security::Certificate>(requestFile);
 
   // validate that the content is a public key
   Buffer keyContent = certRequest.getPublicKey();
@@ -165,9 +158,9 @@
 
   keyChain.sign(cert, security::SigningInfo(identity).setSignatureInfo(signatureInfo));
 
-  const Block& wire = cert.wireEncode();
   {
     using namespace security::transform;
+    const auto& wire = cert.wireEncode();
     bufferSource(wire.wire(), wire.size()) >> base64Encode(true) >> streamSink(std::cout);
   }
 
diff --git a/tools/ndnsec/cert-install.cpp b/tools/ndnsec/cert-install.cpp
index 7a6afa2..caa10b6 100644
--- a/tools/ndnsec/cert-install.cpp
+++ b/tools/ndnsec/cert-install.cpp
@@ -159,8 +159,8 @@
   }
 
   security::Certificate cert;
-  try {
-    if (certFile.find("http://") == 0) {
+  if (certFile.find("http://") == 0) {
+    try {
       std::string host;
       std::string port;
       std::string path;
@@ -172,7 +172,6 @@
         NDN_THROW(HttpException("Request line is not correctly formatted"));
 
       size_t posPort = certFile.find(':', pos);
-
       if (posPort != std::string::npos && posPort < posSlash) {
         // port is specified
         port = certFile.substr(posPort + 1, posSlash - posPort - 1);
@@ -187,13 +186,14 @@
 
       cert = getCertificateHttp(host, port, path);
     }
-    else {
-      cert = loadCertificate(certFile);
+    catch (const std::runtime_error& e) {
+      std::cerr << "ERROR: Cannot download the certificate from '" << certFile
+                << "': " << e.what() << std::endl;
+      return 1;
     }
   }
-  catch (const CannotLoadCertificate&) {
-    std::cerr << "ERROR: Cannot load the certificate from `" << certFile << "`" << std::endl;
-    return 1;
+  else {
+    cert = loadFromFile<security::Certificate>(certFile);
   }
 
   KeyChain keyChain;
diff --git a/tools/ndnsec/export.cpp b/tools/ndnsec/export.cpp
index f0a9395..f11cc96 100644
--- a/tools/ndnsec/export.cpp
+++ b/tools/ndnsec/export.cpp
@@ -23,7 +23,6 @@
 #include "util.hpp"
 
 #include "ndn-cxx/security/impl/openssl.hpp"
-#include "ndn-cxx/util/io.hpp"
 #include "ndn-cxx/util/scope.hpp"
 
 namespace ndn {
diff --git a/tools/ndnsec/import.cpp b/tools/ndnsec/import.cpp
index 7e27bee..2fb6755 100644
--- a/tools/ndnsec/import.cpp
+++ b/tools/ndnsec/import.cpp
@@ -23,7 +23,6 @@
 #include "util.hpp"
 
 #include "ndn-cxx/security/impl/openssl.hpp"
-#include "ndn-cxx/util/io.hpp"
 #include "ndn-cxx/util/scope.hpp"
 
 namespace ndn {
@@ -74,11 +73,7 @@
 
   KeyChain keyChain;
 
-  shared_ptr<security::SafeBag> safeBag;
-  if (input == "-")
-    safeBag = io::load<security::SafeBag>(std::cin);
-  else
-    safeBag = io::load<security::SafeBag>(input);
+  auto safeBag = loadFromFile<security::SafeBag>(input);
 
   if (password.empty()) {
     int count = 3;
@@ -91,7 +86,7 @@
     }
   }
 
-  keyChain.importSafeBag(*safeBag, password.data(), password.size());
+  keyChain.importSafeBag(safeBag, password.data(), password.size());
 
   return 0;
 }
diff --git a/tools/ndnsec/key-gen.cpp b/tools/ndnsec/key-gen.cpp
index 2090f74..6773e72 100644
--- a/tools/ndnsec/key-gen.cpp
+++ b/tools/ndnsec/key-gen.cpp
@@ -22,8 +22,6 @@
 #include "ndnsec.hpp"
 #include "util.hpp"
 
-#include "ndn-cxx/util/io.hpp"
-
 namespace ndn {
 namespace ndnsec {
 
diff --git a/tools/ndnsec/main.cpp b/tools/ndnsec/main.cpp
index 024e5d5..e731b5c 100644
--- a/tools/ndnsec/main.cpp
+++ b/tools/ndnsec/main.cpp
@@ -22,6 +22,7 @@
 #include "ndnsec.hpp"
 
 #include "ndn-cxx/util/logger.hpp"
+#include "ndn-cxx/util/logging.hpp"
 #include "ndn-cxx/version.hpp"
 
 #include <boost/exception/diagnostic_information.hpp>
@@ -111,6 +112,7 @@
   catch (const std::exception& e) {
     std::cerr << "ERROR: " << e.what() << std::endl;
     NDN_LOG_ERROR(boost::diagnostic_information(e));
+    ndn::util::Logging::flush();
     return 1;
   }
 
diff --git a/tools/ndnsec/ndnsec-pch.hpp b/tools/ndnsec/ndnsec-pch.hpp
index c72b650..c059d36 100644
--- a/tools/ndnsec/ndnsec-pch.hpp
+++ b/tools/ndnsec/ndnsec-pch.hpp
@@ -24,8 +24,6 @@
 
 #include "util.hpp"
 
-#include "ndn-cxx/util/io.hpp"
-
 #include <boost/asio/ip/tcp.hpp>
 
 #endif // NDN_CXX_TOOLS_NDNSEC_NDNSEC_PCH_HPP
diff --git a/tools/ndnsec/sign-req.cpp b/tools/ndnsec/sign-req.cpp
index 3548228..6151714 100644
--- a/tools/ndnsec/sign-req.cpp
+++ b/tools/ndnsec/sign-req.cpp
@@ -22,8 +22,6 @@
 #include "ndnsec.hpp"
 #include "util.hpp"
 
-#include "ndn-cxx/util/io.hpp"
-
 namespace ndn {
 namespace ndnsec {
 
diff --git a/tools/ndnsec/util.cpp b/tools/ndnsec/util.cpp
index 0ff7e74..d79df47 100644
--- a/tools/ndnsec/util.cpp
+++ b/tools/ndnsec/util.cpp
@@ -22,7 +22,6 @@
 #include "util.hpp"
 
 #include "ndn-cxx/security/impl/openssl.hpp"
-#include "ndn-cxx/util/io.hpp"
 
 #include <unistd.h>
 
@@ -90,20 +89,5 @@
   NDN_CXX_UNREACHABLE;
 }
 
-security::Certificate
-loadCertificate(const std::string& fileName)
-{
-  shared_ptr<security::Certificate> cert;
-  if (fileName == "-")
-    cert = io::load<security::Certificate>(std::cin);
-  else
-    cert = io::load<security::Certificate>(fileName);
-
-  if (cert == nullptr) {
-    NDN_THROW(CannotLoadCertificate(fileName));
-  }
-  return *cert;
-}
-
 } // namespace ndnsec
 } // namespace ndn
diff --git a/tools/ndnsec/util.hpp b/tools/ndnsec/util.hpp
index 4ce3c04..913198c 100644
--- a/tools/ndnsec/util.hpp
+++ b/tools/ndnsec/util.hpp
@@ -23,6 +23,7 @@
 #define NDN_CXX_TOOLS_NDNSEC_UTIL_HPP
 
 #include "ndn-cxx/security/key-chain.hpp"
+#include "ndn-cxx/util/io.hpp"
 
 #include <iostream>
 
@@ -49,17 +50,29 @@
 getCertificateFromPib(const security::pib::Pib& pib, const Name& name,
                       bool isIdentityName, bool isKeyName, bool isCertName);
 
-class CannotLoadCertificate : public std::runtime_error
+/**
+ * @brief Load a TLV-encoded, base64-armored object from a file named @p filename.
+ */
+template<typename T>
+T
+loadFromFile(const std::string& filename)
 {
-public:
-  CannotLoadCertificate(const std::string& msg)
-    : std::runtime_error(msg)
-  {
-  }
-};
+  try {
+    if (filename == "-") {
+      return io::loadTlv<T>(std::cin, io::BASE64);
+    }
 
-security::Certificate
-loadCertificate(const std::string& fileName);
+    std::ifstream file(filename);
+    if (!file) {
+      NDN_THROW(std::runtime_error("Cannot open '" + filename + "'"));
+    }
+    return io::loadTlv<T>(file, io::BASE64);
+  }
+  catch (const io::Error& e) {
+    NDN_THROW_NESTED(std::runtime_error("Cannot load '" + filename +
+                                        "': malformed TLV or not in base64 format (" + e.what() + ")"));
+  }
+}
 
 bool
 getPassword(std::string& password, const std::string& prompt, bool shouldConfirm = true);