Fixed several ndncert-client bugs and use lower case Challenge ID

refs: #4962
Change-Id: Id10dcc15cb718d6a55f4657884b2c6be3f653867
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index 66a81ef..2c47424 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -22,7 +22,6 @@
 #include "challenge-module.hpp"
 #include <iostream>
 #include <string>
-#include <algorithm>
 #include <boost/program_options/options_description.hpp>
 #include <boost/program_options/variables_map.hpp>
 #include <boost/program_options/parsers.hpp>
@@ -37,6 +36,7 @@
 Face face;
 security::v2::KeyChain keyChain;
 std::string challengeType;
+int validityPeriod = -1;
 ClientModule client(keyChain);
 
 static std::list<std::string>
@@ -79,6 +79,31 @@
 }
 
 static void
+captureValidityPeriod()
+{
+  if (validityPeriod <= 0) {
+    std::cerr << "Step " << nStep++
+              << ": Please type in your expected validity period of your certificate."
+              << " Type the number of hours (168 for week, 730 for month, 8760 for year). The CA may change the validity"
+              << " period if your expected period is too long." << std::endl;
+    std::string periodStr = "";
+    do {
+      getline(std::cin, periodStr);
+      try {
+        validityPeriod = std::stoi(periodStr);
+      }
+      catch (const std::exception& e) {
+        validityPeriod = -1;
+      }
+      if (validityPeriod > 0) {
+        break;
+      }
+      std::cerr << "Invalid period time. Please input the period again." << std::endl;
+    } while (true);
+  }
+}
+
+static void
 onNackCb()
 {
   std::cerr << "Got NACK\n";
@@ -111,50 +136,70 @@
   }
 
   auto challenge = ChallengeModule::createChallengeModule(challengeType);
-  auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(), client.getChallengeStatus());
+  auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
+                                                           client.getChallengeStatus());
   if (requirement.size() > 0) {
     std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
     std::string redo = "";
     std::list<std::string> capturedParams;
     do {
       capturedParams = captureParams(requirement);
-      std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
+      std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
       getline(std::cin, redo);
-      std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
-    } while (redo == "REDO");
+      boost::algorithm::to_lower(redo);
+    } while (redo != "ok");
     auto it1 = capturedParams.begin();
     auto it2 = requirement.begin();
     for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
       it2->second.put("", *it1);
     }
   }
-  face.expressInterest(*client.generateChallengeInterest(
-                        challenge->genChallengeRequestJson(
-                                   client.getApplicationStatus(),
-                                   client.getChallengeStatus(),
-                                   requirement)),
-                       bind(&challengeCb, _2),
-                       bind(&onNackCb),
-                       bind(&timeoutCb));
+  face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestJson(client.getApplicationStatus(),
+                                                                                            client.getChallengeStatus(),
+                                                                                            requirement)),
+                       bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
 }
 
 static void
 newCb(const Data& reply)
 {
+  int challengeIndex = 0;
   auto challengeList = client.onNewResponse(reply);
-  std::cerr << "Step " << nStep++ << ": Please type in the challenge ID from the following challenges\n";
-  for (auto item : challengeList) {
-    std::cerr << "\t" << item << std::endl;
+  if (challengeList.size() < 1) {
+    std::cerr << "There is no available challenge provided by the CA. Exit" << std::endl;
+    return;
   }
-  std::string choice;
-  getline(std::cin, choice);
-
-  auto challenge = ChallengeModule::createChallengeModule(choice);
+  else if (challengeList.size() > 1) {
+    int count = 0;
+    std::string choice = "";
+    std::cerr << "Step " << nStep++ << ": Please type in the challenge index that you want to perform\n";
+    do {
+      count = 0;
+      for (auto item : challengeList) {
+        std::cerr << "\t" << count++ << " : "<< item << std::endl;
+      }
+      getline(std::cin, choice);
+      try {
+        challengeIndex = std::stoi(choice);
+      }
+      catch (const std::exception& e) {
+        challengeIndex = -1;
+      }
+      if (challengeIndex >= 0 && challengeIndex < count) {
+        break;
+      }
+      std::cerr << "Your input index is out of range. Please type in again." << std::endl;
+    } while (true);
+  }
+  auto it = challengeList.begin();
+  std::advance(it, challengeIndex);
+  unique_ptr<ChallengeModule> challenge = ChallengeModule::createChallengeModule(*it);
   if (challenge != nullptr) {
-    challengeType = choice;
+    challengeType = *it;
+    std::cerr << "The challenge has been selected: " << challengeType << std::endl;
   }
   else {
-    std::cerr << "Cannot recognize the specified challenge. Exit";
+    std::cerr << "Error. Cannot load selected Challenge Module. Exit." << std::endl;
     return;
   }
   auto requirement = challenge->getRequirementForChallenge(client.getApplicationStatus(),
@@ -165,24 +210,20 @@
     std::list<std::string> capturedParams;
     do {
       capturedParams = captureParams(requirement);
-      std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
+      std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
       getline(std::cin, redo);
-      std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
-    } while (redo == "REDO");
+      boost::algorithm::to_lower(redo);
+    } while (redo != "ok");
     auto it1 = capturedParams.begin();
     auto it2 = requirement.begin();
     for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
       it2->second.put("", *it1);
     }
   }
-  face.expressInterest(*client.generateChallengeInterest(
-                               challenge->genChallengeRequestJson(
-                                          client.getApplicationStatus(),
-                                          client.getChallengeStatus(),
-                                          requirement)),
-                       bind(&challengeCb, _2),
-                       bind(&onNackCb),
-                       bind(&timeoutCb));
+  face.expressInterest(*client.generateChallengeInterest(challenge->genChallengeRequestJson(client.getApplicationStatus(),
+                                                                                            client.getChallengeStatus(),
+                                                                                            requirement)),
+                       bind(&challengeCb, _2), bind(&onNackCb), bind(&timeoutCb));
 }
 
 static void
@@ -198,14 +239,14 @@
 
   std::string answer;
   getline(std::cin, answer);
-  std::transform(answer.begin(), answer.end(), answer.begin(), ::toupper);
-  if (answer == "YES") {
+  boost::algorithm::to_lower(answer);
+  if (answer == "yes") {
     client.onProbeInfoResponse(reply);
-    std::cerr << "You answered YES: new CA installed" << std::endl;
+    std::cerr << "You answered YES: new CA has been installed" << std::endl;
     startApplication();
   }
   else {
-    std::cerr << "New CA not installed" << std::endl;
+    std::cerr << "New CA will not be installed" << std::endl;
     return;
   }
 }
@@ -214,31 +255,13 @@
 probeCb(const Data& reply)
 {
   client.onProbeResponse(reply);
-  std::cerr << "Step " << nStep++
-            << ": Please type in your expected validity period of your certificate."
-            << " Type in a number in unit of hour. The CA may change the validity"
-            << " period if your expected period is too long." << std::endl;
-  std::string periodStr;
-  int hours = 0;
-  getline(std::cin, periodStr);
-  hours = std::stoi(periodStr);
-  while (hours <= 0) {
-    std::cerr << "Invalid period time: " << "Please input the period again." << std::endl;
-    getline(std::cin, periodStr);
-    try {
-      hours = std::stoi(periodStr);
-    }
-    catch (const std::exception& e) {
-      hours = -1;
-    }
-  }
+  captureValidityPeriod();
   auto probeToken = make_shared<Data>(reply);
   auto now = time::system_clock::now();
-  face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
+  std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
+  face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
                                                    Name(), probeToken),
-                       bind(&newCb, _2),
-                       bind(&onNackCb),
-                       bind(&timeoutCb));
+                       bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
 }
 
 static void
@@ -259,20 +282,28 @@
             << nStep++ << ": Please type in the CA INDEX that you want to apply"
             << " or type in NONE if your expected CA is not in the list\n";
 
-  std::string caIndexS, caIndexSUpper;
+  std::string caIndexS, caIndexSLower;
   getline(std::cin, caIndexS);
-  caIndexSUpper = caIndexS;
-  std::transform(caIndexSUpper.begin(), caIndexSUpper.end(), caIndexSUpper.begin(), ::toupper);
-  if (caIndexSUpper == "NONE") {
+  caIndexSLower = caIndexS;
+  boost::algorithm::to_lower(caIndexSLower);
+  if (caIndexSLower == "none") {
     std::cerr << "Step " << nStep << ": Please type in the CA Name\n";
     face.expressInterest(*client.generateProbeInfoInterest(Name(caIndexS)),
-                         bind(&probeInfoCb, _2),
-                         bind(&onNackCb),
-                         bind(&timeoutCb));
+                         bind(&probeInfoCb, _2), bind(&onNackCb), bind(&timeoutCb));
   }
   else {
-    int caIndex = std::stoi(caIndexS);
-    BOOST_ASSERT(caIndex <= count);
+    int caIndex;
+    try {
+      caIndex = std::stoi(caIndexS);
+    }
+    catch (const std::exception& e) {
+      std::cerr << "Your input is neither NONE nor a valid index. Exit" << std::endl;
+      return;
+    }
+    if (caIndex < 0 || caIndex >= count) {
+      std::cerr << "Your input is not an existing index. Exit" << std::endl;
+      return;
+    }
     auto targetCaItem = caVector[caIndex];
 
     if (targetCaItem.m_probe != "") {
@@ -282,10 +313,10 @@
       std::list<std::string> capturedParams;
       do {
         capturedParams = captureParams(probeFields);
-        std::cerr << "If everything is right, please type in OK; otherwise, type in REDO" << std::endl;
+        std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
         getline(std::cin, redo);
-        std::transform(redo.begin(), redo.end(), redo.begin(), ::toupper);
-      } while (redo == "REDO");
+        boost::algorithm::to_lower(redo);
+      } while (redo != "ok");
       std::string probeInfo;
       for (const auto& item : capturedParams) {
         probeInfo += item;
@@ -293,27 +324,18 @@
       }
       probeInfo = probeInfo.substr(0, probeInfo.size() - 1);
       face.expressInterest(*client.generateProbeInterest(targetCaItem, probeInfo),
-                           bind(&probeCb, _2),
-                           bind(&onNackCb),
-                           bind(&timeoutCb));
+                           bind(&probeCb, _2), bind(&onNackCb), bind(&timeoutCb));
     }
     else {
       std::cerr << "Step " << nStep++ << ": Please type in the identity name you want to get (with CA prefix)\n";
       std::string identityNameStr;
       getline(std::cin, identityNameStr);
-      std::cerr << "Step "
-                << nStep++ << ": Please type in your expected validity period of your certificate."
-                << "Type in a number in unit of hour."
-                << " The CA may change the validity period if your expected period is too long.\n";
-      std::string periodStr;
-      getline(std::cin, periodStr);
-      int hours = std::stoi(periodStr);
+      captureValidityPeriod();
+      std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
       auto now = time::system_clock::now();
-      face.expressInterest(*client.generateNewInterest(now, now + time::hours(hours),
+      face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
                                                        Name(identityNameStr)),
-                           bind(&newCb, _2),
-                           bind(&onNackCb),
-                           bind(&timeoutCb));
+                           bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
     }
   }
 }
@@ -324,10 +346,11 @@
 {
   namespace po = boost::program_options;
   std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
-  po::options_description description("General Usage\n ndncert-client [-h] [-f]\n");
+  po::options_description description("General Usage\n ndncert-client [-h] [-c] [-v]\n");
   description.add_options()
     ("help,h", "produce help message")
-    ("config-file,f", po::value<std::string>(&configFilePath), "config file name");
+    ("config-file,c", po::value<std::string>(&configFilePath), "config file name")
+    ("validity-period,v", po::value<int>(&validityPeriod)->default_value(-1), "the validity period of your certificate being requested");
   po::positional_options_description p;
 
   po::variables_map vm;