docs+ndnsec: improve description and parsing of command options, rewrite man pages

Replace custom redmine_issue extension with sphinx.ext.extlinks

Refs: #4907
Change-Id: Ib0cb94156ae4fc80cdcaf4c70d7c8d55c16fcbc3
diff --git a/tools/ndnsec/cert-dump.cpp b/tools/ndnsec/cert-dump.cpp
index 39d5baf..52b9f9f 100644
--- a/tools/ndnsec/cert-dump.cpp
+++ b/tools/ndnsec/cert-dump.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2018 Regents of the University of California.
+ * Copyright (c) 2013-2019 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -22,6 +22,7 @@
 #include "ndnsec.hpp"
 #include "util.hpp"
 
+#include <boost/asio/ip/tcp.hpp>
 #if BOOST_VERSION < 106700
 #include <boost/date_time/posix_time/posix_time_duration.hpp>
 #endif // BOOST_VERSION < 106700
@@ -35,39 +36,37 @@
   namespace po = boost::program_options;
 
   std::string name;
-  bool isKeyName = false;
   bool isIdentityName = false;
-  bool isCertName = true;
-  // bool isFileName = false;
+  bool isKeyName = false;
+  bool isFileName = false;
   bool isPretty = false;
-  bool isStdOut = true;
   bool isRepoOut = false;
   std::string repoHost;
   std::string repoPort;
-  // bool isDnsOut = false;
 
-  po::options_description description("General Usage\n"
-                                      "  ndnsec cert-dump [-h] [-p] [-d] [-r [-H repo-host] "
-                                         "[-P repo-port] ] [-i|k|f] name\n"
-                                      "General options");
+  po::options_description description(
+    "Usage: ndnsec cert-dump [-h] [-p] [-r [-H HOST] [-P PORT]] [-i|-k|-f] [-n] NAME\n"
+    "\n"
+    "Options");
   description.add_options()
-    ("help,h",     "produce help message")
-    ("pretty,p",   "display certificate in human readable format")
-    ("identity,i", "treat the name parameter as identity name (e.g., /ndn/edu/ucla/alice")
-    ("key,k",      "treat the name parameter as key name "
-                   "(e.g., /ndn/edu/ucla/alice/ksk-123456789)")
-    ("file,f",     "treat the name parameter as file name with base64 encoded certificate, "
-                   "- for stdin")
-    ("repo-output,r", "publish the certificate to the repo-ng")
-    ("repo-host,H", po::value<std::string>(&repoHost)->default_value("localhost"),
-                   "the repo host if repo-output is specified")
-    ("repo-port,P", po::value<std::string>(&repoPort)->default_value("7376"),
-                   "the repo port if repo-output is specified")
-    // ("dns-output,d", "published the certificate to NDNS")
-    ("name,n", po::value<std::string>(&name),
-                   "unless overridden with --identity or --key parameter, the certificate name, "
-                   "for example, /ndn/edu/ucla/KEY/cs/alice/ksk-1234567890"
-                                "/ID-CERT/%FD%FF%FF%FF%FF%FF%FF%FF")
+    ("help,h", "produce help message")
+    ("pretty,p",   po::bool_switch(&isPretty), "display certificate in human readable format")
+    ("identity,i", po::bool_switch(&isIdentityName),
+                   "treat the NAME argument as an identity name (e.g., /ndn/edu/ucla/alice)")
+    ("key,k",      po::bool_switch(&isKeyName),
+                   "treat the NAME argument as a key name (e.g., /ndn/edu/ucla/alice/ksk-123456789)")
+    ("file,f",     po::bool_switch(&isFileName),
+                   "treat the NAME argument as the name of a file containing a base64-encoded "
+                   "certificate, '-' for stdin")
+    ("name,n",     po::value<std::string>(&name),
+                   "unless overridden by -i/-k/-f, the name of the certificate to be exported "
+                   "(e.g., /ndn/edu/ucla/KEY/cs/alice/ksk-1234567890/ID-CERT/%FD%FF%FF%FF%FF%FF%FF%FF)")
+    ("repo-output,r", po::bool_switch(&isRepoOut),
+                      "publish the certificate into a repo-ng instance")
+    ("repo-host,H",   po::value<std::string>(&repoHost)->default_value("localhost"),
+                      "repo hostname if --repo-output is specified")
+    ("repo-port,P",   po::value<std::string>(&repoPort)->default_value("7376"),
+                      "repo port number if --repo-output is specified")
     ;
 
   po::positional_options_description p;
@@ -79,119 +78,87 @@
     po::notify(vm);
   }
   catch (const std::exception& e) {
-    std::cerr << "ERROR: " << e.what() << std::endl;
-    std::cerr << description << std::endl;
-    return 1;
+    std::cerr << "ERROR: " << e.what() << "\n\n"
+              << description << std::endl;
+    return 2;
   }
 
-  if (vm.count("help") != 0) {
-    std::cerr << description << std::endl;
+  if (vm.count("help") > 0) {
+    std::cout << description << std::endl;
     return 0;
   }
 
   if (vm.count("name") == 0) {
-    std::cerr << "identity_name must be specified" << std::endl;
-    std::cerr << description << std::endl;
-    return 1;
+    std::cerr << "ERROR: you must specify a name" << std::endl;
+    return 2;
   }
 
-  if (vm.count("key") != 0) {
-    isCertName = false;
-    isKeyName = true;
-  }
-  else if (vm.count("identity") != 0) {
-    isCertName = false;
-    isIdentityName = true;
-  }
-  else if (vm.count("file") != 0) {
-    isCertName = false;
-    // isFileName = true;
+  if (isIdentityName + isKeyName + isFileName > 1) {
+    std::cerr << "ERROR: at most one of '--identity', '--key', "
+                 "or '--file' may be specified" << std::endl;
+    return 2;
   }
 
-  if (vm.count("pretty") != 0)
-    isPretty = true;
-
-  if (vm.count("repo-output") != 0) {
-    isRepoOut = true;
-    isStdOut = false;
+  if (isPretty && isRepoOut) {
+    std::cerr << "ERROR: '--pretty' is incompatible with '--repo-output'" << std::endl;
+    return 2;
   }
-  else if (vm.count("dns-output") != 0) {
-    // isDnsOut = true;
-    isStdOut = false;
-    std::cerr << "Error: DNS output is not supported yet!" << std::endl;
-    return 1;
-  }
-
-  if (isPretty && !isStdOut) {
-    std::cerr << "Error: pretty option can only be specified when other "
-              << "output option is specified" << std::endl;
-    return 1;
-  }
-
-  security::v2::Certificate certificate;
 
   security::v2::KeyChain keyChain;
 
+  security::v2::Certificate certificate;
   try {
-    if (isIdentityName || isKeyName || isCertName) {
-      if (isIdentityName) {
-        certificate = keyChain.getPib()
-          .getIdentity(name)
-          .getDefaultKey()
-          .getDefaultCertificate();
-      }
-      else if (isKeyName) {
-        certificate = keyChain.getPib()
-          .getIdentity(security::v2::extractIdentityFromKeyName(name))
-          .getKey(name)
-          .getDefaultCertificate();
-      }
-      else {
-        certificate = keyChain.getPib()
-          .getIdentity(security::v2::extractIdentityFromCertName(name))
-          .getKey(security::v2::extractKeyNameFromCertName(name))
-          .getCertificate(name);
-      }
+    if (isIdentityName) {
+      certificate = keyChain.getPib()
+                    .getIdentity(name)
+                    .getDefaultKey()
+                    .getDefaultCertificate();
     }
-    else {
+    else if (isKeyName) {
+      certificate = keyChain.getPib()
+                    .getIdentity(security::v2::extractIdentityFromKeyName(name))
+                    .getKey(name)
+                    .getDefaultCertificate();
+    }
+    else if (isFileName) {
       certificate = loadCertificate(name);
     }
-  }
-  catch (const security::Pib::Error& e) {
-    std::cerr << "ERROR: " << e.what() << std::endl;
-    return 1;
+    else {
+      certificate = keyChain.getPib()
+                    .getIdentity(security::v2::extractIdentityFromCertName(name))
+                    .getKey(security::v2::extractKeyNameFromCertName(name))
+                    .getCertificate(name);
+    }
   }
   catch (const CannotLoadCertificate&) {
-    std::cerr << "Cannot load certificate from `" << name << "`" << std::endl;
+    std::cerr << "ERROR: Cannot load the certificate from `" << name << "`" << std::endl;
     return 1;
   }
 
   if (isPretty) {
     std::cout << certificate << std::endl;
+    return 0;
   }
-  else {
-    if (isStdOut) {
-      io::save(certificate, std::cout);
-      return 0;
-    }
-    if (isRepoOut) {
-      boost::asio::ip::tcp::iostream requestStream;
-#if BOOST_VERSION >= 106700
-      requestStream.expires_after(std::chrono::seconds(3));
-#else
-      requestStream.expires_from_now(boost::posix_time::seconds(3));
-#endif // BOOST_VERSION >= 106700
-      requestStream.connect(repoHost, repoPort);
-      if (!requestStream) {
-        std::cerr << "fail to open the stream!" << std::endl;
-        return 1;
-      }
-      requestStream.write(reinterpret_cast<const char*>(certificate.wireEncode().wire()),
-                          certificate.wireEncode().size());
 
-      return 0;
+  if (isRepoOut) {
+    boost::asio::ip::tcp::iostream requestStream;
+#if BOOST_VERSION >= 106700
+    requestStream.expires_after(std::chrono::seconds(10));
+#else
+    requestStream.expires_from_now(boost::posix_time::seconds(10));
+#endif // BOOST_VERSION >= 106700
+    requestStream.connect(repoHost, repoPort);
+    if (!requestStream) {
+      std::cerr << "ERROR: Failed to connect to repo instance" << std::endl;
+      return 1;
     }
+    requestStream.write(reinterpret_cast<const char*>(certificate.wireEncode().wire()),
+                        certificate.wireEncode().size());
+    return 0;
   }
+
+  io::save(certificate, std::cout);
+
   return 0;
 }