RPC call info_actions_folder seem to work almost perfectly

Use ccnpeek to test:
    ccnpeek -c /localhost/<your-user-name>/chronoshare/<folder>/info/actions/folder/<nonce>/%00
or
    ccnpeek -c /localhost/<your-user-name>/chronoshare/<folder>/info/actions/folder/<specific-folder>/<nonce>/%00

+ json_spirit used to format the whole message, not just parts

Change-Id: I40c15459a8ae0ef6278364c45e84e5cbd0b08241
diff --git a/src/action-log.cc b/src/action-log.cc
index 14b30ca..0e22483 100644
--- a/src/action-log.cc
+++ b/src/action-log.cc
@@ -474,7 +474,7 @@
 {
   sqlite3_stmt *stmt;
   sqlite3_prepare_v2 (m_db,
-                      "SELECT device_name, seq_no, file_mtime, file_chmod, file_seg_num, file_hash "
+                      "SELECT device_name, seq_no, strftime('%s', file_mtime), file_chmod, file_seg_num, file_hash "
                       " FROM ActionLog "
                       " WHERE action = 0 AND "
                       "       filename=? AND "
@@ -644,20 +644,23 @@
 }
 
 
-void
+bool
 ActionLog::LookupActionsInFolderRecursively (const boost::function<void (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &)> &visitor,
                                              const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
 {
   _LOG_DEBUG ("LookupActionsInFolderRecursively: [" << folder << "]");
 
+  if (limit >= 0)
+    limit += 1; // to check if there is more data
+
   sqlite3_stmt *stmt;
   if (folder != "")
     {
       /// @todo Do something to improve efficiency of this query. Right now it is basically scanning the whole database
 
       sqlite3_prepare_v2 (m_db,
-                          "SELECT device_name,seq_no,action,filename,directory,version,action_timestamp, "
-                          "       file_hash,file_mtime,file_chmod,file_seg_num, "
+                          "SELECT device_name,seq_no,action,filename,directory,version,strftime('%s', action_timestamp), "
+                          "       file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num, "
                           "       parent_device_name,parent_seq_no "
                           "   FROM ActionLog "
                           "   WHERE is_prefix (?, directory)=1 "
@@ -674,8 +677,8 @@
   else
     {
       sqlite3_prepare_v2 (m_db,
-                          "SELECT device_name,seq_no,action,filename,directory,version,action_timestamp, "
-                          "       file_hash,file_mtime,file_chmod,file_seg_num, "
+                          "SELECT device_name,seq_no,action,filename,directory,version,strftime('%s', action_timestamp), "
+                          "       file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num, "
                           "       parent_device_name,parent_seq_no "
                           "   FROM ActionLog "
                           "   ORDER BY action_timestamp DESC "
@@ -688,6 +691,9 @@
 
   while (sqlite3_step (stmt) == SQLITE_ROW)
     {
+      if (limit == 1)
+        break;
+
       ActionItem action;
 
       Ccnx::Name device_name (sqlite3_column_blob  (stmt, 0), sqlite3_column_bytes (stmt, 0));
@@ -711,11 +717,14 @@
         }
 
       visitor (device_name, seq_no, action);
+      limit --;
     }
 
   _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
 
   sqlite3_finalize (stmt);
+
+  return (limit == 1); // more data is available
 }
 
 
diff --git a/src/action-log.h b/src/action-log.h
index cfcaf7a..cef0a65 100644
--- a/src/action-log.h
+++ b/src/action-log.h
@@ -105,7 +105,7 @@
   /**
    * @brief Lookup up to [limit] actions starting [offset] in decreasing order (by timestamp) and calling visitor(device_name,seqno,action) for each action
    */
-  void
+  bool
   LookupActionsInFolderRecursively (const boost::function<void (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &)> &visitor,
                                     const std::string &folder, int offset=0, int limit=-1);
 
diff --git a/src/state-server.cc b/src/state-server.cc
index 6f73950..c264edf 100644
--- a/src/state-server.cc
+++ b/src/state-server.cc
@@ -27,6 +27,7 @@
 #include "periodic-task.h"
 #include "simple-interval-generator.h"
 #include <boost/lexical_cast.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
 
 INIT_LOGGER ("StateServer");
 
@@ -121,9 +122,77 @@
 //     }
 // }
 
-void debugAction (const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action)
+void
+StateServer::formatActionJson (json_spirit::Array &actions,
+                               const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action)
 {
-  std::cout << name << ", " << seq_no << ", " << action.filename () << std::endl;
+/*
+ *      {
+ *          "id": {
+ *              "userName": "<NDN-NAME-OF-THE-USER>",
+ *              "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ *          },
+ *          "timestamp": "<ACTION-TIMESTAMP>",
+ *          "filename": "<FILENAME>",
+ *
+ *          "action": "UPDATE | DELETE",
+ *
+ *          // only if update
+ *          "update": {
+ *              "hash": "<FILE-HASH>",
+ *              "timestamp": "<FILE-TIMESTAMP>",
+ *              "chmod": "<FILE-MODE>",
+ *              "segNum": "<NUMBER-OF-SEGMENTS (~file size)>"
+ *          },
+ *
+ *          // if parent_device_name is set
+ *          "parentId": {
+ *              "userName": "<NDN-NAME-OF-THE-USER>",
+ *              "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ *          }
+ *      }
+ */
+
+  using namespace json_spirit;
+  using namespace boost::posix_time;
+
+  Object json;
+  Object id;
+
+  id.push_back (Pair ("userName", boost::lexical_cast<string> (name)));
+  id.push_back (Pair ("seqNo",    seq_no));
+
+  json.push_back (Pair ("id", id));
+
+  json.push_back (Pair ("timestamp", to_iso_string (from_time_t (action.timestamp ()))));
+  json.push_back (Pair ("filename",  action.filename ()));
+  json.push_back (Pair ("action", (action.action () == 0) ? "UPDATE" : "DELETE"));
+
+  if (action.action () == 0)
+    {
+      Object update;
+      update.push_back (Pair ("hash", boost::lexical_cast<string> (Hash (action.file_hash ().c_str (), action.file_hash ().size ()))));
+      update.push_back (Pair ("timestamp", to_iso_string (from_time_t (action.mtime ()))));
+
+      ostringstream chmod;
+      chmod << setbase (8) << setfill ('0') << setw (4) << action.mode ();
+      update.push_back (Pair ("chmod", chmod.str ()));
+
+      update.push_back (Pair ("segNum", action.seg_num ()));
+      json.push_back (Pair ("update", update));
+    }
+
+  if (action.has_parent_device_name ())
+    {
+      Object parentId;
+      Ccnx::Name parent_device_name (action.parent_device_name ().c_str (), action.parent_device_name ().size ());
+      id.push_back (Pair ("userName", boost::lexical_cast<string> (parent_device_name)));
+      id.push_back (Pair ("seqNo",    action.parent_seq_no ()));
+
+      json.push_back (Pair ("parentId", parentId));
+    }
+
+  actions.push_back (json);
 }
 
 void
@@ -156,10 +225,36 @@
         folder = interest.getCompFromBackAsString (2);
       else // == 4
         folder = "";
+/*
+ *   {
+ *      "actions": [
+ *           ...
+ *      ],
+ *
+ *      // only if there are more actions available
+ *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-ACTION>"
+ *   }
+ */
 
-      m_actionLog->LookupActionsInFolderRecursively (debugAction, folder, offset*100, 100);
+      using namespace json_spirit;
+      Object json;
 
-      // m_ccnx->publishData (interest, "FAIL: Not implemented", 1);
+      Array actions;
+      bool more = m_actionLog->LookupActionsInFolderRecursively
+        (boost::bind (StateServer::formatActionJson, boost::ref(actions), _1, _2, _3),
+         folder, offset*10, 10);
+
+      json.push_back (Pair ("actions", actions));
+
+      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 24c9b05..b4d69e0 100644
--- a/src/state-server.h
+++ b/src/state-server.h
@@ -32,6 +32,13 @@
 #include <boost/thread/locks.hpp>
 #include "executor.h"
 
+#include "../contrib/json_spirit/json_spirit_writer_template.h"
+#include "../contrib/json_spirit/json_spirit_value.h"
+
+#ifndef JSON_SPIRIT_VALUE_ENABLED
+#error Please define JSON_SPIRIT_VALUE_ENABLED for the Value type to be enabled
+#endif
+
 /**
  * @brief Class serving state information from ChronoShare
  *
@@ -56,9 +63,44 @@
  *   Actions are ordered in decreasing order (latest will go first).
  *
  *   Each data packet contains up to 100 actions.
+ *
+ *   TEMPORARILY LIMIT IS REDUCED TO 10 ! (for debug purposes)
+ *
  *   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": [
+ *      {
+ *          "id": {
+ *              "userName": "<NDN-NAME-OF-THE-USER>",
+ *              "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ *          },
+ *          "timestamp": "<ACTION-TIMESTAMP>",
+ *          "filename": "<FILENAME>",
+ *
+ *          "action": "UPDATE | DELETE",
+ *
+ *          // only if update
+ *          "update": {
+ *              "hash": "<FILE-HASH>",
+ *              "timestamp": "<FILE-TIMESTAMP>",
+ *              "chmod": "<FILE-MODE>",
+ *              "segNum": "<NUMBER-OF-SEGMENTS (~file size)>"
+ *          },
+ *
+ *          // if parent_device_name is set
+ *          "parentId": {
+ *              "userName": "<NDN-NAME-OF-THE-USER>",
+ *              "seqNo": "<SEQ_NO_OF_THE_ACTION>"
+ *          };
+ *      },
+ *
+ *      // only if there are more actions available
+ *      "more": "<NDN-NAME-OF-NEXT-SEGMENT-OF-ACTION>"
+ *   }
+ *
  *
  * - file
  *
@@ -120,6 +162,9 @@
   void
   deregisterPrefixes ();
 
+  static void
+  formatActionJson (json_spirit::Array &actions, const Ccnx::Name &name, sqlite3_int64 seq_no, const ActionItem &action);
+
 private:
   Ccnx::CcnxWrapperPtr m_ccnx;
   ActionLogPtr m_actionLog;
diff --git a/wscript b/wscript
index a20b70c..5b0e6a5 100644
--- a/wscript
+++ b/wscript
@@ -104,7 +104,7 @@
 
     conf.load('boost')
 
-    conf.check_boost(lib='system test iostreams filesystem regex thread')
+    conf.check_boost(lib='system test iostreams filesystem regex thread date_time')
 
     boost_version = conf.env.BOOST_VERSION.split('_')
     if int(boost_version[0]) < 1 or int(boost_version[1]) < 46:
@@ -168,7 +168,7 @@
         target="chronoshare",
         features=['cxx'],
         source = bld.path.ant_glob(['src/**/*.cc', 'src/**/*.cpp', 'src/**/*.proto']),
-        use = "BOOST BOOST_FILESYSTEM SQLITE3 LOG4CXX scheduler ccnx",
+        use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 LOG4CXX scheduler ccnx",
         includes = "ccnx scheduler src executor",
         )
 
@@ -188,7 +188,7 @@
           features = "qt4 cxx cxxprogram",
           defines = "WAF",
           source = bld.path.ant_glob(['test/*.cc']),
-          use = 'BOOST_TEST BOOST_FILESYSTEM LOG4CXX SQLITE3 QTCORE QTGUI ccnx database fs_watcher chronoshare',
+          use = 'BOOST_TEST BOOST_FILESYSTEM BOOST_DATE_TIME LOG4CXX SQLITE3 QTCORE QTGUI ccnx database fs_watcher chronoshare',
           includes = "ccnx scheduler src executor gui fs-watcher",
           install_prefix = None,
           )
@@ -207,7 +207,7 @@
         defines = "WAF",
         source = bld.path.ant_glob(['gui/*.cpp', 'gui/*.cc', 'gui/*.qrc']),
         includes = "ccnx scheduler executor fs-watcher gui src adhoc server . ",
-        use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare http_server"
+        use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare http_server"
         )
 
     if Utils.unversioned_sys_platform () == "darwin":
@@ -246,7 +246,7 @@
 	defines = "WAF",
 	source = "cmd/csd.cc",
 	includes = "ccnx scheduler executor gui fs-watcher src . ",
-	use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare"
+	use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 QTCORE QTGUI LOG4CXX fs_watcher ccnx database chronoshare"
 	)
 
     dump_db = bld (
@@ -254,7 +254,7 @@
         features = "cxx cxxprogram",
 	source = "cmd/dump-db.cc",
 	includes = "ccnx scheduler executor gui fs-watcher src . ",
-	use = "BOOST BOOST_FILESYSTEM SQLITE3 QTCORE LOG4CXX fs_watcher ccnx database chronoshare"
+	use = "BOOST BOOST_FILESYSTEM BOOST_DATE_TIME SQLITE3 QTCORE LOG4CXX fs_watcher ccnx database chronoshare"
         )
 
 from waflib import TaskGen