Implementing FileState RPC call (now it is called info/files/folder RPC call)

For testing:
    ccnpeek -c /localhost/<username>/chronoshare/<shared-folder>/info/files/folder/<folder-of-interest>/nonce/%00
or
    ccnpeek -c /localhost/<username>/chronoshare/<shared-folder>/info/files/folder/nonce/%00

Returned items are lexicographically ordered by filename (not really natural ordering, but should be ok for now...)

Change-Id: I4e02cd44cf2daf632b25729e9f936512d17f2bc0
diff --git a/src/state-server.cc b/src/state-server.cc
index c264edf..e70b891 100644
--- a/src/state-server.cc
+++ b/src/state-server.cc
@@ -81,7 +81,7 @@
   m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("actions")("folder"), bind(&StateServer::info_actions_folder, this, _1));
 
   // <PREFIX_INFO>/"filestate"/"all"/<nonce>/<segment>
-  m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("filestate")("folder"), bind(&StateServer::info_filestate_folder, this, _1));
+  m_ccnx->setInterestFilter (Name (m_PREFIX_INFO)("files")("folder"), bind(&StateServer::info_files_folder, this, _1));
 
   // <PREFIX_CMD>/"restore"/"file"/<one-component-relative-file-name>/<version>/<file-hash>
   m_ccnx->setInterestFilter (Name (m_PREFIX_CMD)("restore")("file"), bind(&StateServer::cmd_restore_file, this, _1));
@@ -91,7 +91,7 @@
 StateServer::deregisterPrefixes ()
 {
   m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("actions")("folder"));
-  m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("filestate")("folder"));
+  m_ccnx->clearInterestFilter (Name (m_PREFIX_INFO)("files")("folder"));
   m_ccnx->clearInterestFilter (Name (m_PREFIX_CMD) ("restore")("file"));
 }
 
@@ -263,13 +263,65 @@
     }
 }
 
+void
+StateServer::formatFilestateJson (json_spirit::Array &files, const FileItem &file)
+{
+/**
+ *   {
+ *      "filestate": [
+ *      {
+ *          "filename": "<FILENAME>",
+ *          "owner": {
+ *              "userName": "<NDN-NAME-OF-THE-USER>",
+ *              "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ *          },
+ *
+ *          "hash": "<FILE-HASH>",
+ *          "timestamp": "<FILE-TIMESTAMP>",
+ *          "chmod": "<FILE-MODE>",
+ *          "segNum": "<NUMBER-OF-SEGMENTS (~file size)>"
+ *      }, ...,
+ *      ]
+ *
+ *      // only if there are more actions available
+ *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-FILESTATE>"
+ *   }
+ */
+  using namespace json_spirit;
+  using namespace boost::posix_time;
+
+  Object json;
+
+  json.push_back (Pair ("filename",  file.filename ()));
+  json.push_back (Pair ("version",   file.version ()));
+  {
+    Object owner;
+    Ccnx::Name device_name (file.device_name ().c_str (), file.device_name ().size ());
+    owner.push_back (Pair ("userName", boost::lexical_cast<string> (device_name)));
+    owner.push_back (Pair ("seqNo",    file.seq_no ()));
+
+    json.push_back (Pair ("owner", owner));
+  }
+
+  json.push_back (Pair ("hash", boost::lexical_cast<string> (Hash (file.file_hash ().c_str (), file.file_hash ().size ()))));
+  json.push_back (Pair ("timestamp", to_iso_string (from_time_t (file.mtime ()))));
+
+  ostringstream chmod;
+  chmod << setbase (8) << setfill ('0') << setw (4) << file.mode ();
+  json.push_back (Pair ("chmod", chmod.str ()));
+
+  json.push_back (Pair ("segNum", file.seg_num ()));
+
+  files.push_back (json);
+}
+
 void debugFileState (const FileItem &file)
 {
   std::cout << file.filename () << std::endl;
 }
 
 void
-StateServer::info_filestate_folder (const Ccnx::Name &interest)
+StateServer::info_files_folder (const Ccnx::Name &interest)
 {
   if (interest.size () - m_PREFIX_INFO.size () != 4 &&
       interest.size () - m_PREFIX_INFO.size () != 5)
@@ -279,12 +331,12 @@
     }
 
   _LOG_DEBUG (">> info_filestate_folder: " << interest);
-  m_executor.execute (bind (&StateServer::info_filestate_folder_Execute, this, interest));
+  m_executor.execute (bind (&StateServer::info_files_folder_Execute, this, interest));
 }
 
 
 void
-StateServer::info_filestate_folder_Execute (const Ccnx::Name &interest)
+StateServer::info_files_folder_Execute (const Ccnx::Name &interest)
 {
   // <PREFIX_INFO>/"filestate"/"folder"/<one-component-relative-folder-name>/<nonce>/<offset>
   try
@@ -299,10 +351,38 @@
       else // == 4
         folder = "";
 
-      FileStatePtr fileState = m_actionLog->GetFileState ();
-      fileState->LookupFilesInFolderRecursively (debugFileState, folder, offset*100, 100);
+/*
+ *   {
+ *      "files": [
+ *           ...
+ *      ],
+ *
+ *      // only if there are more actions available
+ *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-ACTION>"
+ *   }
+ */
 
-      // m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
+      using namespace json_spirit;
+      Object json;
+
+      Array files;
+      bool more = m_actionLog
+        ->GetFileState ()
+        ->LookupFilesInFolderRecursively
+        (boost::bind (StateServer::formatFilestateJson, boost::ref (files), _1),
+         folder, offset*10, 10);
+
+      json.push_back (Pair ("files", files));
+
+      if (more)
+        {
+          Ccnx::Name more = Name (interest.getPartialName (0, interest.size () - 1))(offset + 1);
+          json.push_back (Pair ("more", lexical_cast<string> (more)));
+        }
+
+      ostringstream os;
+      write_stream (Value (json), os, pretty_print | raw_utf8);
+      m_ccnx->publishData (interest, os.str (), 1);
     }
   catch (Ccnx::NameException &ne)
     {
diff --git a/src/state-server.h b/src/state-server.h
index b4d69e0..72168a1 100644
--- a/src/state-server.h
+++ b/src/state-server.h
@@ -65,10 +65,10 @@
  *   Each data packet contains up to 100 actions.
  *
  *   TEMPORARILY LIMIT IS REDUCED TO 10 ! (for debug purposes)
+ *   (may be even not temporarily...)
  *
  *   If more items are available, application data will specify URL for the next packet
  *
- *   @todo SPECIFY FORMAT OF THIS FIELD
  *   Format of returned data (JSON):
  *   {
  *      "actions": [
@@ -104,14 +104,36 @@
  *
  * - file
  *
- *   <PREFIX_INFO>/"filestate"/"folder"/<nonce>/<offset>   (full filestate)
+ *   <PREFIX_INFO>/"files"/"folder"/<nonce>/<offset>   (full filestate)
  *   or
- *   <PREFIX_INFO>/"filestate"/"folder"/<one-component-relative-folder-name>/<nonce>/<offset>
+ *   <PREFIX_INFO>/"files"/"folder"/<one-component-relative-folder-name>/<nonce>/<offset>
  *
  *   Each Data packets contains a list of up to 100 files.
  *   If more items are available, application data will specify URL for the next packet
  *
- *   @todo SPECIFY FORMAT OF THIS FIELD
+ *   TEMPORARILY LIMIT IS REDUCED TO 10 ! (for debug purposes)
+ *   (may be even not temporarily...)
+ *
+ *   Format of returned data (JSON):
+ *   {
+ *      "files": [
+ *      {
+ *          "filename": "<FILENAME>",
+ *          "owner": {
+ *              "userName": "<NDN-NAME-OF-THE-USER>",
+ *              "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ *          },
+ *
+ *          "hash": "<FILE-HASH>",
+ *          "timestamp": "<FILE-TIMESTAMP>",
+ *          "chmod": "<FILE-MODE>",
+ *          "segNum": "<NUMBER-OF-SEGMENTS (~file size)>"
+ *      }, ...,
+ *      ]
+ *
+ *      // only if there are more actions available
+ *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-FILESTATE>"
+ *   }
  *
  * Commands available:
  *
@@ -144,10 +166,10 @@
   info_actions_folder_Execute (const Ccnx::Name &interest);
 
   void
-  info_filestate_folder (const Ccnx::Name &interest);
+  info_files_folder (const Ccnx::Name &interest);
 
   void
-  info_filestate_folder_Execute (const Ccnx::Name &interest);
+  info_files_folder_Execute (const Ccnx::Name &interest);
 
   void
   cmd_restore_file (const Ccnx::Name &interest);
@@ -165,6 +187,9 @@
   static void
   formatActionJson (json_spirit::Array &actions, const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action);
 
+  static void
+  formatFilestateJson (json_spirit::Array &files, const FileItem &file);
+
 private:
   Ccnx::CcnxWrapperPtr m_ccnx;
   ActionLogPtr m_actionLog;