enable published data validation

add flag to indicate the last component for autocompletion

Change-Id: Ice97969b2043dd0e15d00576e605d19a7528f21d
diff --git a/catalog.conf.sample.in b/catalog.conf.sample.in
index fc32347..b119aab 100644
--- a/catalog.conf.sample.in
+++ b/catalog.conf.sample.in
@@ -6,7 +6,7 @@
   ; "ndn:/cmip5/catalog/query" and "ndn:/cmip5/catalog/query-results",
   ; PublishAdapter has the prefix "ndn:/cmip5/catalog/publish"
 
-  prefix /catalog/myUniqueName
+  prefix /cmip5
 
   ; Set name components for the scientific data, for example, the climate data
   ; contains name fields like activity, ..., time
@@ -32,8 +32,8 @@
   {
     dbServer 127.0.0.1  ; Specify the database server
     dbName testdb       ; Specify the database name
-    dbUser testuser1    ; Specify the database user name
-    dbPasswd test123    ; Specify the associated password for the dbUser
+    dbUser testuser     ; Specify the database user name
+    dbPasswd test623    ; Specify the associated password for the dbUser
   }
 }
 
@@ -46,31 +46,115 @@
 
   ; The security section contains the rules for the adapter to verify the
   ; published files indeed come from a valid publisher.
-  ; security
-  ; {
-  ;   rule
-  ;   {
-  ;     id "NDN Hierarchy Test Rule"
-  ;     for data                             ; rule for Data (to validate NDN certificates)
-  ;     filter
-  ;     {
-  ;       type name                          ; condition on data name
-  ;       regex ^(<>*)$
-  ;     }
-  ;     checker
-  ;     {
-  ;       type hierarchical                  ; the certificate name of the signing key and
-  ;                                          ; the data name must follow the hierarchical model
-  ;       sig-type rsa-sha256                ; data must have a rsa-sha256 signature
-  ;     }
-  ;   }
-  ;   trust-anchor
-  ;   {
-  ;     type file
-  ;     file-name /directory/to/the/root.ndncert ; the file name, by default this file should be
-  ;                                              ; in same folder as this config file.
-  ;   }
-  ; }
+  security
+  {
+    rule
+    {
+      id "Publishing data is signed by the Datapublisher's dsk"
+      for data              ; rule for Data (to validate NDN certificates)
+      filter
+      {
+        type name
+        regex ^<><><><>$    ; condition on data name
+      }
+      checker
+      {
+        type customized
+        sig-type rsa-sha256
+        key-locator
+        {
+          type name
+          hyper-relation
+          {
+            k-regex ^([^<DataPublisher><KEY>]*)<DataPublisher><KEY><dsk-.*><ID-CERT>$
+            k-expand \\1
+            h-relation equal
+            p-regex ^(<>)<>(<>)<>$
+            p-expand \\1\\2
+          }
+        }
+      }
+    }
+
+    rule
+    {
+      id "Datapublisher's dsk is signed by its ksk"
+      for data
+      filter
+      {
+        type name
+        regex ^[^<DataPublisher><KEY>]*<DataPublisher><KEY><dsk-.*><ID-CERT><>$
+      }
+      checker
+      {
+        type customized
+        sig-type rsa-sha256
+        key-locator
+        {
+          type name
+          hyper-relation
+          {
+            k-regex ^([^<DataPublisher><KEY>]*)<DataPublisher><KEY><ksk-.*><ID-CERT>$
+            k-expand \\1
+            h-relation equal
+            p-regex ^([^<DataPublisher><KEY>]*)<DataPublisher><KEY><dsk-.*><ID-CERT><>$
+            p-expand \\1
+          }
+        }
+      }
+    }
+
+    rule
+    {
+      id "DataPublisher's ksk signed by the site's ksk"
+      for data
+      filter
+      {
+        type name
+        regex ^[^<DataPublisher><KEY>]*<DataPublisher><KEY><ksk-.*><ID-CERT><>$
+      }
+      checker
+      {
+        type customized
+        sig-type rsa-sha256
+        key-locator
+        {
+          type name
+          hyper-relation
+          {
+            k-regex ^([^<KEY>]*)<KEY><ksk-.*><ID-CERT>$
+            k-expand \\1
+            h-relation equal
+            p-regex ^([^<DataPublisher><KEY>]*)<DataPublisher><KEY><ksk-.*><ID-CERT><>$
+            p-expand \\1
+          }
+        }
+      }
+    }
+
+    rule
+    {
+      id "publishing Hierarchical Rule"
+      for data
+      filter
+      {
+        type name
+        regex ^[^<KEY>]*<KEY><ksk-.*><ID-CERT><>$
+      }
+      checker
+      {
+        type hierarchical
+        sig-type rsa-sha256
+      }
+    }
+
+    trust-anchor
+    {
+      type file
+      file-name /usr/local/etc/ndn/nlsr/keys/cmip5_root.cert ; the file name of trust-anchor
+    }
+
+  }
 
   ; The database section contains settings of database
   ; The user in publishAdapter may differ from the one in queryAdapter, to provide different
@@ -79,8 +163,8 @@
   {
     dbServer 127.0.0.1  ; Specify the database server
     dbName testdb       ; Specify the database name
-    dbUser testuser2    ; Specify the database user name
-    dbPasswd test123    ; Specify the associated password for the dbUser
+    dbUser testuser     ; Specify the database user name
+    dbPasswd test623    ; Specify the associated password for the dbUser
   }
 
   ; The sync section contains settings of ChronoSync
diff --git a/catalog/src/publish/publish-adapter.hpp b/catalog/src/publish/publish-adapter.hpp
index 1d6f91f..c331bf7 100644
--- a/catalog/src/publish/publish-adapter.hpp
+++ b/catalog/src/publish/publish-adapter.hpp
@@ -215,8 +215,11 @@
   onFetchUpdateDataTimeout(const ndn::Interest& interest);
 
   void
-  onUpdateValidationFailed(const std::shared_ptr<const ndn::Data>& data,
-                           const std::string& failureInfo);
+  onValidationFailed(const std::shared_ptr<const ndn::Data>& data,
+                     const std::string& failureInfo);
+
+  void
+  validatePublishedDataPaylod(const std::shared_ptr<const ndn::Data>& data);
 
 protected:
   typedef std::unordered_map<ndn::Name, const ndn::RegisteredPrefixId*> RegisteredPrefixList;
@@ -496,9 +499,9 @@
   retrieveInterest->setInterestLifetime(ndn::time::milliseconds(4000));
   retrieveInterest->setMustBeFresh(m_mustBeFresh);
   m_face->expressInterest(*retrieveInterest,
-                          bind(&publish::PublishAdapter<DatabaseHandler>::onPublishedData,
+                          bind(&PublishAdapter<DatabaseHandler>::onPublishedData,
                                this,_1, _2),
-                          bind(&publish::PublishAdapter<DatabaseHandler>::onTimeout, this, _1));
+                          bind(&PublishAdapter<DatabaseHandler>::onTimeout, this, _1));
   std::cout << "Expressing Interest for: " << retrieveInterest->toUri() << std::endl;
 }
 
@@ -511,6 +514,14 @@
 
 template <typename DatabaseHandler>
 void
+PublishAdapter<DatabaseHandler>::onValidationFailed(const std::shared_ptr<const ndn::Data>& data,
+                                                    const std::string& failureInfo)
+{
+  std::cout << "Validation failed: " << data->getName() << failureInfo << std::endl;
+}
+
+template <typename DatabaseHandler>
+void
 PublishAdapter<DatabaseHandler>::onPublishedData(const ndn::Interest& interest,
                                                  const ndn::Data& data)
 {
@@ -518,38 +529,46 @@
   if (data.getContent().empty()) {
     return;
   }
+  m_publishValidator->validate(data,
+                               bind(&PublishAdapter<DatabaseHandler>::validatePublishedDataPaylod, this, _1),
+                               bind(&PublishAdapter<DatabaseHandler>::onValidationFailed, this, _1, _2));
+}
 
-  std::shared_ptr<ndn::Data> dataPtr = std::make_shared<ndn::Data>(data);
+template <typename DatabaseHandler>
+void
+PublishAdapter<DatabaseHandler>::validatePublishedDataPaylod(const ndn::shared_ptr<const ndn::Data>& data)
+{
   // validate published data payload, if failed, return
-  if (!validatePublicationChanges(dataPtr)) {
-    std::cout << "data validation failed : " << dataPtr->getName() << std::endl;
+  if (!validatePublicationChanges(data)) {
+    std::cout << "data validation failed : " << data->getName() << std::endl;
 #ifndef NDEBUG
-    const std::string payload(reinterpret_cast<const char*>(dataPtr->getContent().value()),
-                              dataPtr->getContent().value_size());
+    const std::string payload(reinterpret_cast<const char*>(data->getContent().value()),
+                              data->getContent().value_size());
     std::cout << payload << std::endl;
 #endif
     return;
   }
 
   // todo: return value to indicate if the insertion succeeds
-  processUpdateData(dataPtr);
+  processUpdateData(data);
 
   // ideally, data should not be stale?
-  m_socket->publishData(data.getContent(), ndn::time::seconds(3600));
+  m_socket->publishData(data->getContent(), ndn::time::seconds(3600));
 
   // if this is not the final block, continue to fetch the next one
-  const ndn::name::Component& finalBlockId = data.getMetaInfo().getFinalBlockId();
-  if (finalBlockId == data.getName()[-1]) {
+  const ndn::name::Component& finalBlockId = data->getMetaInfo().getFinalBlockId();
+  if (finalBlockId == data->getName()[-1]) {
     m_isFinished = true;
   }
   //else, get the next segment
   if (!m_isFinished) {
-    ndn::Name nextInterestName = data.getName().getPrefix(-1);
-    uint64_t incomingSegment = data.getName()[-1].toSegment();
-    std::cout << " Next Interest Name " << nextInterestName << " Segment " << incomingSegment++
+    ndn::Name nextInterestName = data->getName().getPrefix(-1);
+    uint64_t incomingSegment = data->getName()[-1].toSegment();
+    incomingSegment++;
+    std::cout << " Next Interest Name " << nextInterestName << " Segment " << incomingSegment
               << std::endl;
     std::shared_ptr<ndn::Interest> nextInterest =
-      std::make_shared<ndn::Interest>(nextInterestName.appendSegment(incomingSegment++));
+      std::make_shared<ndn::Interest>(nextInterestName.appendSegment(incomingSegment));
     nextInterest->setInterestLifetime(ndn::time::milliseconds(4000));
     nextInterest->setMustBeFresh(m_mustBeFresh);
     m_face->expressInterest(*nextInterest,
@@ -697,15 +716,6 @@
 
 template <typename DatabaseHandler>
 void
-PublishAdapter<DatabaseHandler>::onUpdateValidationFailed(const
-                                                          std::shared_ptr<const ndn::Data>& data,
-                                                          const std::string& failureInfo)
-{
-  std::cout << "failed to validate Data" << data->getName() << " : " << failureInfo << std::endl;
-}
-
-template <typename DatabaseHandler>
-void
 PublishAdapter<DatabaseHandler>::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>&
                                                    updates)
 {
@@ -725,7 +735,7 @@
       if (seq > localSeqNo) {
         m_socket->fetchData(updates[i].session, seq,
                             bind(&PublishAdapter<DatabaseHandler>::processUpdateData,this, _1),
-                            bind(&PublishAdapter<DatabaseHandler>::onUpdateValidationFailed,
+                            bind(&PublishAdapter<DatabaseHandler>::onValidationFailed,
                                  this, _1, _2),
                             bind(&PublishAdapter<DatabaseHandler>::onFetchUpdateDataTimeout,
                                  this, _1),
diff --git a/catalog/src/query/query-adapter.hpp b/catalog/src/query/query-adapter.hpp
index 2b85da5..0a5ae0b 100644
--- a/catalog/src/query/query-adapter.hpp
+++ b/catalog/src/query/query-adapter.hpp
@@ -144,6 +144,8 @@
    * @param resultCount:    the number of records in the query results
    * @param viewStart:      the start index of the record in the query results payload
    * @param viewEnd:        the end index of the record in the query results payload
+   * @param lastComponent:  flag to indicate the content contains the last component for
+                            autocompletion query
    */
   std::shared_ptr<ndn::Data>
   makeReplyData(const ndn::Name& segmentPrefix,
@@ -153,7 +155,8 @@
                 bool isAutocomplete,
                 uint64_t resultCount,
                 uint64_t viewStart,
-                uint64_t viewEnd);
+                uint64_t viewEnd,
+                bool lastComponent);
 
   /**
    * Helper function that generates query results from a Json query carried in the Interest
@@ -202,7 +205,8 @@
   virtual void
   prepareSegments(const ndn::Name& segmentPrefix,
                   const std::string& sqlString,
-                  bool autocomplete);
+                  bool autocomplete,
+                  bool lastComponent);
 
   /**
    * Helper function to set the DatabaseHandler
@@ -221,12 +225,14 @@
 
   /**
    * Helper function that generates the sqlQuery string for autocomplete query
-   * @param sqlQuery:     stringstream to save the sqlQuery string
-   * @param jsonValue:    Json value that contains the query information
+   * @param sqlQuery:      stringstream to save the sqlQuery string
+   * @param jsonValue:     Json value that contains the query information
+   * @param lastComponent: Flag to mark the last component query
    */
   bool
   json2AutocompletionSql(std::stringstream& sqlQuery,
-                         Json::Value& jsonValue);
+                         Json::Value& jsonValue,
+                         bool& lastComponent);
 
   bool
   json2PrefixBasedSearchSql(std::stringstream& sqlQuery,
@@ -738,7 +744,8 @@
 template <typename DatabaseHandler>
 bool
 QueryAdapter<DatabaseHandler>::json2AutocompletionSql(std::stringstream& sqlQuery,
-                                                      Json::Value& jsonValue)
+                                                      Json::Value& jsonValue,
+                                                      bool& lastComponent)
 {
 #ifndef NDEBUG
   std::cout << "jsonValue in json2AutocompletionSql: " << jsonValue.toStyledString() << std::endl;
@@ -797,6 +804,9 @@
 
   // 2. generate the sql string (append what appears in the typed string, like activity='xxx'),
   // return true
+  if (count == m_nameFields.size() - 1)
+    lastComponent = true; // indicate this query is to query the last component
+
   bool more = false;
   sqlQuery << "SELECT DISTINCT " << m_nameFields[count] << " FROM " << m_databaseTable;
   for (std::map<std::string, std::string>::iterator it = typedComponents.begin();
@@ -967,7 +977,7 @@
   m_mutex.unlock();
 
   // 3) Convert the JSON Query into a MySQL one
-  bool autocomplete = false;
+  bool autocomplete = false, lastComponent = false;
   std::stringstream sqlQuery;
 
   ndn::Name segmentPrefix(getQueryResultsName(interest, version));
@@ -977,7 +987,7 @@
   // if JSON::Value contains ? as key, is autocompletion
   if (parsedFromString.get("?", tmp) != tmp) {
     autocomplete = true;
-    if (!json2AutocompletionSql(sqlQuery, parsedFromString)) {
+    if (!json2AutocompletionSql(sqlQuery, parsedFromString, lastComponent)) {
       sendNack(segmentPrefix);
       return;
     }
@@ -996,14 +1006,15 @@
   }
 
   // 4) Run the Query
-  prepareSegments(segmentPrefix, sqlQuery.str(), autocomplete);
+  prepareSegments(segmentPrefix, sqlQuery.str(), autocomplete, lastComponent);
 }
 
 template <typename DatabaseHandler>
 void
 QueryAdapter<DatabaseHandler>::prepareSegments(const ndn::Name& segmentPrefix,
                                                const std::string& sqlString,
-                                               bool autocomplete)
+                                               bool autocomplete,
+                                               bool lastComponent)
 {
   // empty
 }
@@ -1013,7 +1024,8 @@
 void
 QueryAdapter<MYSQL>::prepareSegments(const ndn::Name& segmentPrefix,
                                      const std::string& sqlString,
-                                     bool autocomplete)
+                                     bool autocomplete,
+                                     bool lastComponent)
 {
 #ifndef NDEBUG
   std::cout << "prepareSegments() executes sql : " << sqlString << std::endl;
@@ -1057,7 +1069,7 @@
     if (tmpString.length() > PAYLOAD_LIMIT) {
       std::shared_ptr<ndn::Data> data
         = makeReplyData(segmentPrefix, resultJson, segmentNo, false,
-                        autocomplete, resultCount, viewStart, viewEnd);
+                        autocomplete, resultCount, viewStart, viewEnd, lastComponent);
       m_mutex.lock();
       m_cache.insert(*data);
       m_mutex.unlock();
@@ -1072,7 +1084,7 @@
 
   std::shared_ptr<ndn::Data> data
     = makeReplyData(segmentPrefix, resultJson, segmentNo, true,
-                    autocomplete, resultCount, viewStart, viewEnd);
+                    autocomplete, resultCount, viewStart, viewEnd, lastComponent);
   m_mutex.lock();
   m_cache.insert(*data);
   m_mutex.unlock();
@@ -1087,7 +1099,8 @@
                                              bool isAutocomplete,
                                              uint64_t resultCount,
                                              uint64_t viewStart,
-                                             uint64_t viewEnd)
+                                             uint64_t viewEnd,
+                                             bool lastComponent)
 {
   Json::Value entry;
   Json::FastWriter fastWriter;
@@ -1096,6 +1109,9 @@
   entry["viewStart"] = Json::UInt64(viewStart);
   entry["viewEnd"] = Json::UInt64(viewEnd);
 
+  if (lastComponent)
+    entry["lastComponent"] = Json::Value(true);
+
 #ifndef NDEBUG
   std::cout << "resultCount " << resultCount
             << "; viewStart " << viewStart
diff --git a/catalog/tests/unit-tests/query/test-query-adapter.cpp b/catalog/tests/unit-tests/query/test-query-adapter.cpp
index b49ea03..8b210b7 100644
--- a/catalog/tests/unit-tests/query/test-query-adapter.cpp
+++ b/catalog/tests/unit-tests/query/test-query-adapter.cpp
@@ -101,7 +101,7 @@
                  uint64_t viewEnd)
     {
       return makeReplyData(segmentPrefix, value, segmentNo, isFinalBlock,
-                           isAutocomplete, resultCount, viewStart, viewEnd);
+                           isAutocomplete, resultCount, viewStart, viewEnd, false);
     }
 
     void
@@ -113,7 +113,8 @@
     void
     prepareSegments(const ndn::Name& segmentPrefix,
                     const std::string& sqlString,
-                    bool autocomplete)
+                    bool autocomplete,
+                    bool lastComponent)
     {
       BOOST_CHECK_EQUAL(sqlString, "SELECT name FROM cmip5 WHERE name=\'test\';");
       Json::Value fileList;
@@ -122,7 +123,8 @@
       fileList.append("/ndn/test3");
 
       std::shared_ptr<ndn::Data> data = makeReplyData(segmentPrefix,
-                                                      fileList, 0, true, false, 3, 0, 2);
+                                                      fileList, 0, true, false,
+                                                      3, 0, 2, lastComponent);
       m_mutex.lock();
       m_cache.insert(*data);
       m_mutex.unlock();
@@ -158,9 +160,10 @@
 
     bool
     json2AutocompletionSqlTest(std::stringstream& sqlQuery,
-                               Json::Value& jsonValue)
+                               Json::Value& jsonValue,
+                               bool& lastComponent)
     {
-      return json2AutocompletionSql(sqlQuery, jsonValue);
+      return json2AutocompletionSql(sqlQuery, jsonValue, lastComponent);
     }
 
     bool
@@ -505,22 +508,29 @@
 
     std::stringstream ss;
     Json::Value testJson;
+    bool lastComponent = false;
     testJson["?"] = "/";
-    BOOST_CHECK_EQUAL(true, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson));
+    BOOST_CHECK_EQUAL(true,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson, lastComponent));
+    BOOST_CHECK_EQUAL(lastComponent, false);
     BOOST_CHECK_EQUAL("SELECT DISTINCT activity FROM cmip5;", ss.str());
 
     ss.str("");
     ss.clear();
     testJson.clear();
     testJson["?"] = "/Activity/";
-    BOOST_CHECK_EQUAL(true, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson));
+    BOOST_CHECK_EQUAL(true,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson, lastComponent));
+    BOOST_CHECK_EQUAL(lastComponent, false);
     BOOST_CHECK_EQUAL("SELECT DISTINCT product FROM cmip5 WHERE activity='Activity';", ss.str());
 
     ss.str("");
     ss.clear();
     testJson.clear();
     testJson["?"] = "/Activity/Product/Organization/Model/Experiment/";
-    BOOST_CHECK_EQUAL(true, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson));
+    BOOST_CHECK_EQUAL(true,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson, lastComponent));
+    BOOST_CHECK_EQUAL(lastComponent, false);
     BOOST_CHECK_EQUAL("SELECT DISTINCT frequency FROM cmip5 WHERE activity='Activity' AND \
 experiment='Experiment' AND model='Model' AND organization='Organization' AND product='Product';",
      ss.str());
@@ -530,7 +540,9 @@
     testJson.clear();
     testJson["?"] = "/Activity/Product/Organization/Model/Experiment/Frequency/Modeling/\
 Variable/Ensemble/";
-    BOOST_CHECK_EQUAL(true, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson));
+    BOOST_CHECK_EQUAL(true,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson, lastComponent));
+    BOOST_CHECK_EQUAL(lastComponent, true);
     BOOST_CHECK_EQUAL("SELECT DISTINCT time FROM cmip5 WHERE activity='Activity' AND ensemble=\
 'Ensemble' AND experiment='Experiment' AND frequency='Frequency' AND model='Model' AND \
 modeling_realm='Modeling' AND organization='Organization' AND product='Product' AND variable_name=\
@@ -543,26 +555,31 @@
 
     std::stringstream ss;
     Json::Value testJson;
+    bool lastComponent = false;
     testJson["?"] = "serchTest";
-    BOOST_CHECK_EQUAL(false, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson));
+    BOOST_CHECK_EQUAL(false,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson, lastComponent));
 
     ss.str("");
     ss.clear();
     testJson.clear();
     testJson["?"] = "/cmip5";
-    BOOST_CHECK_EQUAL(false, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson));
+    BOOST_CHECK_EQUAL(false,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson, lastComponent));
 
     ss.str("");
     ss.clear();
     Json::Value testJson2; //simply clear does not work
     testJson2[0] = "test";
-    BOOST_CHECK_EQUAL(false, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson2));
+    BOOST_CHECK_EQUAL(false,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson2, lastComponent));
 
     ss.str("");
     ss.clear();
     Json::Value testJson3;
     testJson3 = Json::Value(Json::arrayValue);
-    BOOST_CHECK_EQUAL(false, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson3));
+    BOOST_CHECK_EQUAL(false,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson3, lastComponent));
 
     ss.str("");
     ss.clear();
@@ -570,7 +587,8 @@
     Json::Value param;
     param[0] = "test";
     testJson4["name"] = param;
-    BOOST_CHECK_EQUAL(false, queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson4));
+    BOOST_CHECK_EQUAL(false,
+                      queryAdapterTest2.json2AutocompletionSqlTest(ss, testJson4, lastComponent));
 }
 
   BOOST_AUTO_TEST_CASE(QueryAdapterPrefixBasedSearchSuccessTest)
diff --git a/tools/cxx-producer.cpp b/tools/cxx-producer.cpp
index c0f028f..31e3679 100644
--- a/tools/cxx-producer.cpp
+++ b/tools/cxx-producer.cpp
@@ -33,6 +33,7 @@
     "   [-c catalogPrefix]  - set the catalog prefix\n"
     "   [-f name list file]  - set the file that contains name list\n"
     "   [-n namespace]       - set the publisher namespace\n"
+    "   [-i signingId]       - set the publisher signing ID\n"
     "   [-h]                 - print help and exit\n"
     "\n";
 }
@@ -90,11 +91,15 @@
     data->setContent(reinterpret_cast<const uint8_t*>(payload), payLoadLength);
 
     // Sign Data packet with default identity
-    m_keyChain.sign(*data);
+    if (m_signingId.empty())
+      m_keyChain.sign(*data);
+    else
+      m_keyChain.signByIdentity(*data, m_signingId);
+
+    std::cout << ">> D: " << *data << std::endl;
 
     // Return Data packet to the requester
-    std::cout << ">> D: " << *data << std::endl;
-    m_face.put(*data);
+      m_face.put(*data);
     m_face.shutdown();
   }
 
@@ -139,6 +144,7 @@
   std::string m_jsonFile;
   std::string m_namespace;
   std::string m_catalogPrefix;
+  ndn::Name m_signingId;
 
 private:
   Face m_face;
@@ -158,16 +164,19 @@
     return 0;
   }
 
-  while ((option = getopt(argc, argv, "c:f:n:h")) != -1) {
+  while ((option = getopt(argc, argv, "c:f:n:i:h")) != -1) {
     switch (option) {
       case 'c':
-        producer.m_catalogPrefix.assign(optarg);
+        producer.m_catalogPrefix = optarg;
         break;
       case 'f':
-        producer.m_jsonFile.assign(optarg);
+        producer.m_jsonFile = optarg;
         break;
       case 'n':
-        producer.m_namespace.assign(optarg);
+        producer.m_namespace = optarg;
+        break;
+      case 'i':
+        producer.m_signingId = ndn::Name(optarg);
         break;
       case 'h':
       default: