Basic implementation of ObjectDb and ObjectManager.  Local files can be
put as a set of fully formatted ContentObjects to the ObjectDb, and
extracted from this Db.
diff --git a/include/ccnx-common.h b/include/ccnx-common.h
index 031d27d..41d7814 100644
--- a/include/ccnx-common.h
+++ b/include/ccnx-common.h
@@ -38,6 +38,7 @@
 #include <sstream>
 #include <map>
 #include <utility>
+#include <string.h>
 
 using namespace std;
 namespace Ccnx {
@@ -45,13 +46,45 @@
 typedef vector<string>Comps;
 
 typedef boost::shared_ptr<Bytes> BytesPtr;
- 
-// --- Bytes operations start ---
-void
-readRaw(Bytes &bytes, const unsigned char *src, size_t len);
 
+inline
 const unsigned char *
-head(const Bytes &bytes);
+head(const Bytes &bytes)
+{
+  return &bytes[0];
+}
+
+inline
+unsigned char *
+head (Bytes &bytes)
+{
+  return &bytes[0];
+}
+
+// --- Bytes operations start ---
+inline void
+readRaw(Bytes &bytes, const unsigned char *src, size_t len)
+{
+  if (len > 0)
+  {
+    bytes.resize(len);
+    memcpy (head (bytes), src, len);
+  }
+}
+
+inline BytesPtr
+readRawPtr (const unsigned char *src, size_t len)
+{
+  if (len > 0)
+    {
+      BytesPtr ret (new Bytes (len));
+      memcpy (head (*ret), src, len);
+
+      return ret;
+    }
+  else
+    return BytesPtr ();
+}
 
 // --- Bytes operations end ---
 
diff --git a/include/ccnx-pco.h b/include/ccnx-pco.h
index 7956dad..0c52c7a 100644
--- a/include/ccnx-pco.h
+++ b/include/ccnx-pco.h
@@ -22,6 +22,9 @@
   Bytes
   content() const;
 
+  BytesPtr
+  contentPtr() const;
+  
   Name
   name() const;
 
diff --git a/src/ccnx-pco.cpp b/src/ccnx-pco.cpp
index af10c7a..8c2fd3f 100644
--- a/src/ccnx-pco.cpp
+++ b/src/ccnx-pco.cpp
@@ -54,6 +54,20 @@
   return bytes;
 }
 
+BytesPtr
+ParsedContentObject::contentPtr() const
+{
+  const unsigned char *content;
+  size_t len;
+  int res = ccn_content_get_value(head(m_bytes), m_pco.offset[CCN_PCO_E], &m_pco, &content, &len);
+  if (res < 0)
+  {
+    boost::throw_exception(MisformedContentObjectException());
+  }
+
+  return readRawPtr (content, len);
+}
+
 Name
 ParsedContentObject::name() const
 {
diff --git a/src/ccnx-wrapper.cpp b/src/ccnx-wrapper.cpp
index b6902a8..3f82889 100644
--- a/src/ccnx-wrapper.cpp
+++ b/src/ccnx-wrapper.cpp
@@ -16,22 +16,6 @@
 
 namespace Ccnx {
 
-void
-readRaw(Bytes &bytes, const unsigned char *src, size_t len)
-{
-  if (len > 0)
-  {
-    bytes.resize(len);
-    memcpy(&bytes[0], src, len);
-  }
-}
-
-const unsigned char *
-head(const Bytes &bytes)
-{
-  return &bytes[0];
-}
-
 CcnxWrapper::CcnxWrapper()
   : m_handle (0)
   , m_running (true)
diff --git a/src/object-db.cc b/src/object-db.cc
index 8cadf7e..0d1a2a6 100644
--- a/src/object-db.cc
+++ b/src/object-db.cc
@@ -39,6 +39,7 @@
                                                                         \
         PRIMARY KEY (device_name, segment)                              \n\
     );                                                                  \n\
+CREATE INDEX device ON File(device_name);                               \n\
 ";
 
 ObjectDb::ObjectDb (const fs::path &folder, const std::string &hash)
@@ -61,11 +62,50 @@
   res = sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, &errmsg);
   if (res != SQLITE_OK && errmsg != 0)
     {
-      std::cerr << "DEBUG: " << errmsg << std::endl;
+      // std::cerr << "DEBUG: " << errmsg << std::endl;
       sqlite3_free (errmsg);
     }  
 }
 
+bool
+ObjectDb::DoesExist (const boost::filesystem::path &folder, const Ccnx::Name &deviceName, const std::string &hash)
+{
+  fs::path actualFolder = folder / "objects" / hash.substr (0, 2);
+  bool retval = false;
+
+  sqlite3 *db;
+  int res = sqlite3_open((actualFolder / hash.substr (2, hash.size () - 2)).c_str (), &db);
+  if (res == SQLITE_OK)
+    {
+      sqlite3_stmt *stmt;
+      sqlite3_prepare_v2 (db, "SELECT count(*), count(nullif(content_object,0)) FROM File WHERE device_name=?", -1, &stmt, 0);
+
+      CcnxCharbufPtr buf = deviceName.toCcnxCharbuf ();
+      sqlite3_bind_blob (stmt, 1, buf->buf (), buf->length (), SQLITE_TRANSIENT);
+
+      int res = sqlite3_step (stmt);
+      if (res == SQLITE_ROW)
+        {
+          int countAll = sqlite3_column_int (stmt, 0);
+          int countNonNull = sqlite3_column_int (stmt, 1);
+
+          cout << countAll << ", " << countNonNull << endl;
+
+          if (countAll > 0 && countAll==countNonNull)
+            {
+              cout << "2" << endl;
+              retval = true;
+            }
+        }
+
+      sqlite3_finalize (stmt);      
+    }
+
+  sqlite3_close (db);
+  return retval;
+}
+
+
 ObjectDb::~ObjectDb ()
 {
   int res = sqlite3_close (m_db);
@@ -83,6 +123,8 @@
                       "(device_name, segment, content_object) "
                       "VALUES (?, ?, ?)", -1, &stmt, 0);
 
+  cout << deviceName << endl;
+  
   CcnxCharbufPtr buf = deviceName.toCcnxCharbuf ();
   sqlite3_bind_blob (stmt, 1, buf->buf (), buf->length (), SQLITE_TRANSIENT);
   sqlite3_bind_int64 (stmt, 2, segment);
@@ -118,7 +160,20 @@
   return ret;
 }
 
+
 // sqlite3_int64
 // ObjectDb::getNumberOfSegments (const Ccnx::Name &deviceName)
 // {
+//   sqlite3_stmt *stmt;
+//   sqlite3_prepare_v2 (m_db, "SELECT count(*) FROM File WHERE device_name=?", -1, &stmt, 0); 
+
+//   bool retval = false;
+//   int res = sqlite3_step (stmt);
+//   if (res == SQLITE_ROW)
+//     {
+//       retval = true; 
+//     }
+//   sqlite3_finalize (stmt);
+
+//   return retval;
 // }
diff --git a/src/object-db.h b/src/object-db.h
index 9e824de..9ecd753 100644
--- a/src/object-db.h
+++ b/src/object-db.h
@@ -43,6 +43,9 @@
 
   // sqlite3_int64
   // getNumberOfSegments (const Ccnx::Name &deviceName);
+
+  static bool
+  DoesExist (const boost::filesystem::path &folder, const Ccnx::Name &deviceName, const std::string &hash);
   
 private:
   sqlite3 *m_db;
diff --git a/src/object-manager.cc b/src/object-manager.cc
index a2de9f1..7f66b2a 100644
--- a/src/object-manager.cc
+++ b/src/object-manager.cc
@@ -22,6 +22,7 @@
 #include "object-manager.h"
 #include "ccnx-name.h"
 #include "ccnx-common.h"
+#include "ccnx-pco.h"
 #include "object-db.h"
 
 #include <sys/stat.h>
@@ -29,6 +30,7 @@
 #include <fstream>
 #include <boost/lexical_cast.hpp>
 #include <boost/throw_exception.hpp>
+#include <boost/filesystem/fstream.hpp>
 
 using namespace Ccnx;
 using namespace boost;
@@ -37,9 +39,8 @@
 
 const int MAX_FILE_SEGMENT_SIZE = 1024;
 
-ObjectManager::ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const Ccnx::Name &localDeviceName, const fs::path &folder)
+ObjectManager::ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const fs::path &folder)
   : m_ccnx (ccnx)
-  , m_localDeviceName (localDeviceName)
   , m_folder (folder / ".chronoshare")
 {
   fs::create_directories (m_folder);
@@ -50,30 +51,64 @@
 }
 
 HashPtr
-ObjectManager::storeLocalFile (const fs::path &file)
+ObjectManager::localFileToObjects (const fs::path &file, const Ccnx::Name &deviceName)
 {
   HashPtr fileHash = Hash::FromFileContent (file);
   ObjectDb fileDb (m_folder, lexical_cast<string> (*fileHash));
   
-  ifstream iff (file.c_str (), std::ios::in | std::ios::binary);
-  int segment = 0;
+  fs::ifstream iff (file, std::ios::in | std::ios::binary);
+  sqlite3_int64 segment = 0;
   while (iff.good ())
     {
       char buf[MAX_FILE_SEGMENT_SIZE];
       iff.read (buf, MAX_FILE_SEGMENT_SIZE);
 
-
-      Name name (m_localDeviceName);
+      Name name (deviceName);
       name
         .appendComp ("file")
         .appendComp (fileHash->GetHash (), fileHash->GetHashBytes ())
         .appendComp (segment);
 
-      cout << *fileHash << endl;
-      cout << name << endl;
+      // cout << *fileHash << endl;
+      // cout << name << endl;
+
+      Bytes data = m_ccnx->createContentObject (name, buf, iff.gcount ());
+      fileDb.saveContentObject (deviceName, segment, data);
       
       segment ++;
     }
   
   return fileHash;
 }
+
+bool
+ObjectManager::objectsToLocalFile (/*in*/const Ccnx::Name &deviceName, /*in*/const Hash &fileHash, /*out*/ const fs::path &file)
+{
+  string hashStr = lexical_cast<string> (fileHash);
+  if (!ObjectDb::DoesExist (m_folder, deviceName, hashStr))
+    {
+      cout << "Brr" << endl;
+      // file does not exist or not all segments are available
+      return false;
+    }
+
+  fs::ofstream off (file, std::ios::out | std::ios::binary);
+  ObjectDb fileDb (m_folder, hashStr);
+
+  sqlite3_int64 segment = 0;
+  BytesPtr bytes = fileDb.fetchSegment (deviceName, 0);
+  while (bytes != BytesPtr())
+    {
+      ParsedContentObject obj (*bytes);
+      BytesPtr data = obj.contentPtr ();
+
+      off.write (reinterpret_cast<const char*> (head(*data)), data->size());
+      
+      segment ++;
+      bytes = fileDb.fetchSegment (deviceName, segment);
+    }
+
+  // permission and timestamp should be assigned somewhere else (ObjectManager has no idea about that)
+  
+  return true;
+}
diff --git a/src/object-manager.h b/src/object-manager.h
index 4ae719b..f0cd6e6 100644
--- a/src/object-manager.h
+++ b/src/object-manager.h
@@ -32,15 +32,17 @@
 class ObjectManager
 {
 public:
-  ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const Ccnx::Name &localDeviceName, const boost::filesystem::path &folder);
+  ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &folder);
   virtual ~ObjectManager ();
 
   HashPtr
-  storeLocalFile (const boost::filesystem::path &file);
+  localFileToObjects (const boost::filesystem::path &file, const Ccnx::Name &deviceName);
+
+  bool
+  objectsToLocalFile (/*in*/const Ccnx::Name &deviceName, /*in*/const Hash &hash, /*out*/ const boost::filesystem::path &file);
   
 private:
   Ccnx::CcnxWrapperPtr m_ccnx;
-  Ccnx::Name m_localDeviceName;
   boost::filesystem::path m_folder;
 };
 
diff --git a/test/database-test.cc b/test/database-test.cc
index 633c4cf..dcbcc2b 100644
--- a/test/database-test.cc
+++ b/test/database-test.cc
@@ -39,8 +39,6 @@
 BOOST_AUTO_TEST_CASE (BasicDatabaseTest)
 {
   fs::path tmpdir = fs::unique_path (fs::temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
-  fs::create_directories (tmpdir);
-  
   SyncLog db (tmpdir, "/alex");
 
   HashPtr hash = db.RememberStateInStateLog ();
diff --git a/test/test-object-manager.cc b/test/test-object-manager.cc
index a27fd1b..78337b3 100644
--- a/test/test-object-manager.cc
+++ b/test/test-object-manager.cc
@@ -22,29 +22,46 @@
 #include "object-manager.h"
 
 #include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+
 #include <boost/test/unit_test.hpp>
 #include <unistd.h>
 #include <boost/make_shared.hpp>
 #include <iostream>
+#include <iterator>
 
 using namespace Ccnx;
 using namespace std;
 using namespace boost;
-using namespace boost::filesystem;
+namespace fs = boost::filesystem;
 
 BOOST_AUTO_TEST_SUITE(ObjectManagerTests)
 
 BOOST_AUTO_TEST_CASE (ObjectManagerTest)
 {
-  path tmpdir = unique_path (temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
-  create_directories (tmpdir);
-
+  fs::path tmpdir = fs::unique_path (fs::temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
+  cout << tmpdir << endl;
   Name deviceName ("/device");
   
   CcnxWrapperPtr ccnx = make_shared<CcnxWrapper> ();
-  ObjectManager manager (ccnx, deviceName, tmpdir);
+  ObjectManager manager (ccnx, tmpdir);
 
+  HashPtr hash = manager.localFileToObjects (fs::path("test") / "test-object-manager.cc", deviceName);
 
+  bool ok = manager.objectsToLocalFile (deviceName, *hash, tmpdir / "test.cc");
+  BOOST_CHECK_EQUAL (ok, true);
+
+  {
+    fs::ifstream origFile (fs::path("test") / "test-object-manager.cc");
+    fs::ifstream newFile (tmpdir / "test.cc");
+
+    istream_iterator<char> eof,
+      origFileI (origFile),
+      newFileI (newFile);
+
+    BOOST_CHECK_EQUAL_COLLECTIONS (origFileI, eof, newFileI, eof);
+  }
+  
   remove_all (tmpdir);
 }