Add repo output to command line tool ndncert-ca-server

refs: #4455

Change-Id: I8e451af5c636bb52e6171ad2d82b43e6ce77f07f
diff --git a/tools/ndncert-ca-server.cpp b/tools/ndncert-ca-server.cpp
index 7377fc1..9e25555 100644
--- a/tools/ndncert-ca-server.cpp
+++ b/tools/ndncert-ca-server.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2017, Regents of the University of California.
+ * Copyright (c) 2017-2018, Regents of the University of California.
  *
  * This file is part of ndncert, a certificate management system based on NDN.
  *
@@ -19,26 +19,45 @@
  */
 
 #include "ca-module.hpp"
+#include "challenge-module.hpp"
 
 #include <iostream>
-
+#include <sstream>
+#include <string>
+#include <ndn-cxx/util/io.hpp>
 #include <boost/program_options/options_description.hpp>
 #include <boost/program_options/variables_map.hpp>
 #include <boost/program_options/parsers.hpp>
+#include <boost/date_time/posix_time/posix_time_duration.hpp>
+#include <boost/asio.hpp>
 
 namespace ndn {
 namespace ndncert {
 
-
 int
 main(int argc, char* argv[])
 {
-  namespace po = boost::program_options;
   std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/ca.conf";
-  po::options_description description("General Usage\n  ndncert-ca [-h] [-f] configFilePath-file\n");
+  std::string repoPrefix;
+  std::string repoCaIdentity;
+  std::string repoHost;
+  std::string repoPort;
+  bool isRepoOut = false;
+
+  namespace po = boost::program_options;
+  po::options_description description("General Usage\n  ndncert-ca [-h] [-f] [-r] [-c]\n");
   description.add_options()
-    ("help,h", "produce help message")
-    ("config-file,f", po::value<std::string>(&configFilePath), "config file name");
+    ("help,h",
+     "produce help message")
+    ("config-file,f", po::value<std::string>(&configFilePath),
+     "config file name")
+    ("repo-output,r",
+     "when enabled, all issued certificates will be published to repo-ng")
+    ("repo-host,H", po::value<std::string>(&repoHost)->default_value("localhost"),
+     "repo-ng host")
+    ("repo-port,P", po::value<std::string>(&repoPort)->default_value("7376"),
+     "repo-ng port");
+
   po::positional_options_description p;
   po::variables_map vm;
   try {
@@ -46,13 +65,17 @@
     po::notify(vm);
   }
   catch (const std::exception& e) {
-    std::cerr << "ERROR: " << e.what() << std::endl;
+    std::cerr << "ERROR: " << e.what()
+              << "\n" << description << std::endl;
     return 1;
   }
   if (vm.count("help") != 0) {
     std::cerr << description << std::endl;
     return 0;
   }
+  if (vm.count("repo-ng-output") != 0) {
+    isRepoOut = true;
+  }
 
   Face face;
   security::v2::KeyChain keyChain;
@@ -73,6 +96,30 @@
       return std::make_tuple(recommendedCa, identity);
     });
 
+  if (isRepoOut) {
+    auto config = ca.getCaConf();
+    for (const auto& caItem : config.m_caItems) {
+      ca.setStatusUpdateCallback(caItem.m_caName,
+        [&] (const CertificateRequest& request) {
+          if (request.getStatus() == ChallengeModule::SUCCESS) {
+            auto issuedCert = request.getCert();
+            using namespace boost::asio::ip;
+            tcp::iostream requestStream;
+            requestStream.expires_from_now(boost::posix_time::seconds(3));
+            requestStream.connect(repoHost, repoPort);
+            if (!requestStream) {
+              std::cerr << "ERROR: Cannot publish certificate to repo-ng"
+                        << " (" << requestStream.error().message() << ")"
+                        << std::endl;
+              return;
+            }
+            requestStream.write(reinterpret_cast<const char*>(issuedCert.wireEncode().wire()),
+                                issuedCert.wireEncode().size());
+          }
+        });
+    }
+  }
+
   face.processEvents();
   return 0;
 }