Merge remote-tracking branch 'origin/master'

Conflicts:
	wscript
diff --git a/client/client.cc b/client/client.cc
index 4d20e98..4f61d52 100644
--- a/client/client.cc
+++ b/client/client.cc
@@ -31,6 +31,7 @@
 using namespace std;
 using namespace boost;
 using namespace ChronoshareClient;
+namespace fs = boost::filesystem;
 
 void
 usage ()
@@ -88,19 +89,20 @@
                   usage ();
                 }
 
-              struct stat fileStats;
-              int ok = stat (argv[2], &fileStats);
-              if (ok == 0)
+              fs::path file (argv[2]);
+              fs::file_status fileStatus = fs::status (file);
+              if (is_regular_file (fileStatus))
                 {
                   // Alex: the following code is platform specific :(
-                  HashPtr fileHash = Hash::FromFileContent (argv[2]);
+                  HashPtr fileHash = Hash::FromFileContent (file);
                   
-                  notify->updateFile (argv[2],
+                  notify->updateFile (file.generic_string (),
                                       make_pair(reinterpret_cast<const ::Ice::Byte*> (fileHash->GetHash ()),
                                                 reinterpret_cast<const ::Ice::Byte*> (fileHash->GetHash ()) +
                                                 fileHash->GetHashBytes ()),
-                                      fileStats.st_atime, fileStats.st_mtime, fileStats.st_ctime,
-                                      fileStats.st_mode);
+                                      fs::last_write_time (file),
+                                      // fileStats.st_atime, fileStats.st_mtime, fileStats.st_ctime,
+                                      fileStatus.permissions ());
                 }
               else
                 {
@@ -124,8 +126,10 @@
                   usage ();
                 }
 
+              fs::path srcFile (argv[2]);
+              fs::path dstFile (argv[3]);
               
-              notify->moveFile (argv[2], argv[3]);
+              notify->moveFile (srcFile.generic_string (), dstFile.generic_string ());
             }
           else
             {
diff --git a/daemon/notify-i.cc b/daemon/notify-i.cc
index 0994aa3..0a3880d 100644
--- a/daemon/notify-i.cc
+++ b/daemon/notify-i.cc
@@ -33,9 +33,7 @@
 void
 NotifyI::updateFile (const ::std::string &filename,
                      const ::std::pair<const Ice::Byte*, const Ice::Byte*> &hashRaw,
-                     ::Ice::Long atime,
-                     ::Ice::Long mtime,
-                     ::Ice::Long ctime,
+                     ::Ice::Long wtime,
                      ::Ice::Int mode,
                      const ::Ice::Current&)
 {
@@ -44,7 +42,7 @@
   cout << "updateFile " << filename << " with hash " << hash << endl;
   try
     {
-      m_actionLog->AddActionUpdate (filename, hash, atime, mtime, ctime, mode);
+      m_actionLog->AddActionUpdate (filename, hash, wtime, mode);
 
       m_actionLog->RememberStateInStateLog ();
     }
diff --git a/daemon/notify-i.h b/daemon/notify-i.h
index 9fd7044..426e2a9 100644
--- a/daemon/notify-i.h
+++ b/daemon/notify-i.h
@@ -33,9 +33,7 @@
   virtual void
   updateFile (const ::std::string &filename,
               const ::std::pair<const Ice::Byte*, const Ice::Byte*> &hash,
-              ::Ice::Long atime,
-              ::Ice::Long mtime,
-              ::Ice::Long ctime,
+              ::Ice::Long wtime,
               ::Ice::Int mode,
               const ::Ice::Current& = ::Ice::Current());
 
diff --git a/include/ccnx-common.h b/include/ccnx-common.h
index 06e7700..41d7814 100644
--- a/include/ccnx-common.h
+++ b/include/ccnx-common.h
@@ -1,3 +1,24 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ *	   Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
 #ifndef CCNX_COMMON_H
 #define CCNX_COMMON_H
 
@@ -17,18 +38,53 @@
 #include <sstream>
 #include <map>
 #include <utility>
+#include <string.h>
 
 using namespace std;
 namespace Ccnx {
 typedef vector<unsigned char> Bytes;
 typedef vector<string>Comps;
 
-// --- Bytes operations start ---
-void
-readRaw(Bytes &bytes, const unsigned char *src, size_t len);
+typedef boost::shared_ptr<Bytes> BytesPtr;
 
+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/include/ccnx-wrapper.h b/include/ccnx-wrapper.h
index 667d4be..baa22b1 100644
--- a/include/ccnx-wrapper.h
+++ b/include/ccnx-wrapper.h
@@ -50,7 +50,7 @@
   getLocalPrefix ();
 
   Bytes
-  createContentObject(const Name &name, const unsigned char *buf, size_t len, int freshness = 2147/* max value for ccnx*/);
+  createContentObject(const Name &name, const void *buf, size_t len, int freshness = 2147/* max value for ccnx*/);
 
   int
   putToCcnd (const Bytes &contentObject);
diff --git a/src/action-item.proto b/src/action-item.proto
index 0144a22..3218ef8 100644
--- a/src/action-item.proto
+++ b/src/action-item.proto
@@ -12,9 +12,9 @@
   required uint32 timestamp = 6;
 
   optional bytes  file_hash = 7;
-  optional uint32 atime = 8;
+  // optional uint32 atime = 8;
   optional uint32 mtime = 9;
-  optional uint32 ctime = 10;
+  // optional uint32 ctime = 10;
   optional uint32 mode  = 11;
 
   optional bytes  parent_device_name = 12;
diff --git a/src/action-log.cc b/src/action-log.cc
index ff8e257..cc0d945 100644
--- a/src/action-log.cc
+++ b/src/action-log.cc
@@ -26,7 +26,8 @@
 using namespace std;
 using namespace Ccnx;
 
-ActionLog::ActionLog (Ccnx::CcnxWrapperPtr ccnx, const std::string &path, const std::string &localName, const std::string &sharedFolder)
+ActionLog::ActionLog (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &path,
+                      const std::string &localName, const std::string &sharedFolder)
   : SyncLog (path, localName)
   , m_ccnx (ccnx)
   , m_sharedFolderName (sharedFolder)
@@ -83,7 +84,7 @@
 void
 ActionLog::AddActionUpdate (const std::string &filename,
                             const Hash &hash,
-                            time_t atime, time_t mtime, time_t ctime,
+                            time_t wtime,
                             int mode)
 {
   sqlite3_exec (m_db, "BEGIN TRANSACTION;", 0,0,0);
@@ -102,9 +103,11 @@
   int res = sqlite3_prepare_v2 (m_db, "INSERT INTO ActionLog "
                                 "(device_id, seq_no, action, filename, version, action_timestamp, "
                                 "file_hash, file_atime, file_mtime, file_ctime, file_chmod, "
-                                "parent_device_id, parent_seq_no) "
+                                "parent_device_id, parent_seq_no, "
+                                "action_name, action_content_object) "
                                 "VALUES (?, ?, ?, ?, ?, datetime(?, 'unixepoch'),"
                                 "        ?, datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?,"
+                                "        ?, ?, "
                                 "        ?, ?);", -1, &stmt, 0);
 
   // cout << "INSERT INTO ActionLog "
@@ -133,9 +136,9 @@
   
   sqlite3_bind_blob  (stmt, 7, hash.GetHash (), hash.GetHashBytes (), SQLITE_TRANSIENT);
   
-  sqlite3_bind_int64 (stmt, 8, atime);
-  sqlite3_bind_int64 (stmt, 9, mtime);
-  sqlite3_bind_int64 (stmt, 10, ctime);
+  // sqlite3_bind_int64 (stmt, 8, atime); // NULL
+  sqlite3_bind_int64 (stmt, 9, wtime);
+  // sqlite3_bind_int64 (stmt, 10, ctime); // NULL
   sqlite3_bind_int   (stmt, 11, mode);
 
   if (parent_device_id > 0 && parent_seq_no > 0)
@@ -149,8 +152,6 @@
       sqlite3_bind_null (stmt, 13);
     }
   
-  sqlite3_step (stmt);
-
   // missing part: creating ContentObject for the action !!!
 
   ActionItem item;
@@ -159,9 +160,9 @@
   item.set_version (version);
   item.set_timestamp (action_time);
   item.set_file_hash (hash.GetHash (), hash.GetHashBytes ());
-  item.set_atime (atime);
-  item.set_mtime (mtime);
-  item.set_ctime (ctime);
+  // item.set_atime (atime);
+  item.set_mtime (wtime);
+  // item.set_ctime (ctime);
   item.set_mode (mode);
 
   if (parent_device_id > 0 && parent_seq_no > 0)
@@ -174,7 +175,23 @@
     }
 
   // assign name to the action, serialize action, and create content object
+
+  string item_msg;
+  item.SerializeToString (&item_msg);
+  Name actionName (m_localName);
+  actionName
+    .appendComp ("action")
+    .appendComp (m_sharedFolderName)
+    .appendComp (seq_no);
   
+  Bytes actionData = m_ccnx->createContentObject (actionName, item_msg.c_str (), item_msg.size ());
+  CcnxCharbufPtr namePtr = actionName.toCcnxCharbuf ();
+  
+  sqlite3_bind_blob (stmt, 14, namePtr->buf (), namePtr->length (), SQLITE_TRANSIENT);
+  sqlite3_bind_blob (stmt, 15, &actionData[0], actionData.size (), SQLITE_TRANSIENT);
+  
+  sqlite3_step (stmt);
+
   sqlite3_finalize (stmt); 
                           
   sqlite3_exec (m_db, "END TRANSACTION;", 0,0,0);
@@ -212,8 +229,10 @@
   sqlite3_stmt *stmt;
   sqlite3_prepare_v2 (m_db, "INSERT INTO ActionLog "
                       "(device_id, seq_no, action, filename, version, action_timestamp, "
-                      "parent_device_id, parent_seq_no) "
+                      "parent_device_id, parent_seq_no, "
+                      "action_name, action_content_object) "
                       "VALUES (?, ?, ?, ?, ?, datetime(?, 'unixepoch'),"
+                      "        ?, ?,"
                       "        ?, ?)", -1, &stmt, 0);
 
   sqlite3_bind_int64 (stmt, 1, m_localDeviceId);
@@ -241,16 +260,15 @@
   actionName
     .appendComp ("action")
     .appendComp (m_sharedFolderName)
-    .appendComp (reinterpret_cast<void*> (seq_no), sizeof (seq_no));
+    .appendComp (seq_no);
   
-  // Bytes actionData = m_ccnx->createContentObject (?name, item_msg.c_str (), item_msg.size ());
-
+  Bytes actionData = m_ccnx->createContentObject (actionName, item_msg.c_str (), item_msg.size ());
+  CcnxCharbufPtr namePtr = actionName.toCcnxCharbuf ();
   
-  // sqlite3_bind_blob (stmt, 10, item_msg.c_str (), item_msg.size (), SQLITE_TRANSIENT);
+  sqlite3_bind_blob (stmt, 9, namePtr->buf (), namePtr->length (), SQLITE_TRANSIENT);
+  sqlite3_bind_blob (stmt, 10, &actionData[0], actionData.size (), SQLITE_TRANSIENT);
   
   sqlite3_step (stmt);
-
-
   
   // cout << Ccnx::Name (reinterpret_cast<const unsigned char *> (parent_device_name.c_str ()),
   //                     parent_device_name.size ()) << endl;
diff --git a/src/action-log.h b/src/action-log.h
index 323f6a3..7d778d8 100644
--- a/src/action-log.h
+++ b/src/action-log.h
@@ -32,12 +32,13 @@
 class ActionLog : public SyncLog
 {
 public:
-  ActionLog (Ccnx::CcnxWrapperPtr ccnx, const std::string &path, const std::string &localName, const std::string &sharedFolder);
+  ActionLog (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &path,
+             const std::string &localName, const std::string &sharedFolder);
 
   void
   AddActionUpdate (const std::string &filename,
                    const Hash &hash,
-                   time_t atime, time_t mtime, time_t ctime,
+                   time_t wtime,
                    int mode);
 
   void
diff --git a/src/ccnx-name.cpp b/src/ccnx-name.cpp
index 28761dd..a59cf8a 100644
--- a/src/ccnx-name.cpp
+++ b/src/ccnx-name.cpp
@@ -254,7 +254,7 @@
 Name::getPartialName(int start, int n) const
 {
   int size = m_comps.size();
-  if (start < 0 || start >= size || n > 0 && start + n > size)
+  if (start < 0 || start >= size || (n > 0 && start + n > size))
   {
     stringstream ss(stringstream::out);
     ss << "getPartialName() parameter out of range! ";
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-tunnel.cpp b/src/ccnx-tunnel.cpp
index ecef346..f428481 100644
--- a/src/ccnx-tunnel.cpp
+++ b/src/ccnx-tunnel.cpp
@@ -8,7 +8,7 @@
                           : CcnxWrapper()
                           , m_localPrefix("/")
 {
-  //refreshLocalPrefix();
+  refreshLocalPrefix();
 }
 
 CcnxTunnel::~CcnxTunnel()
@@ -34,6 +34,8 @@
   Closure *cp = new TunnelClosure(closure, this, interest);
   CcnxWrapper::sendInterest(tunneledInterest, cp, selectors);
   delete cp;
+
+  return 0;
 }
 
 void
diff --git a/src/ccnx-wrapper.cpp b/src/ccnx-wrapper.cpp
index 887f8ae..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)
@@ -164,7 +148,7 @@
 }
 
 Bytes
-CcnxWrapper::createContentObject(const Name  &name, const unsigned char *buf, size_t len, int freshness)
+CcnxWrapper::createContentObject(const Name  &name, const void *buf, size_t len, int freshness)
 {
   CcnxCharbufPtr ptr = name.toCcnxCharbuf();
   ccn_charbuf *pname = ptr->getBuf();
@@ -212,7 +196,7 @@
 int
 CcnxWrapper::publishData (const Name &name, const Bytes &content, int freshness)
 {
-  publishData(name, head(content), content.size(), freshness);
+  return publishData(name, head(content), content.size(), freshness);
 }
 
 
@@ -348,6 +332,8 @@
   }
 
   m_registeredInterests.insert(pair<Name, InterestCallback>(prefix, interestCallback));
+
+  return ret;
 }
 
 void
diff --git a/src/chronoshare-client.ice b/src/chronoshare-client.ice
index e3bf8e2..c2e056f 100644
--- a/src/chronoshare-client.ice
+++ b/src/chronoshare-client.ice
@@ -25,7 +25,7 @@
   
   interface Notify 
   {
-    void updateFile (string filename, ["cpp:array"] HashBytes fileHash, long atime, long mtime, long ctime, int mode);
+    void updateFile (string filename, ["cpp:array"] HashBytes fileHash, long wtime, int mode);
 
     void moveFile (string origFilename, string newFilename);
 
diff --git a/src/db-helper.cc b/src/db-helper.cc
index 217c3ee..c5455e8 100644
--- a/src/db-helper.cc
+++ b/src/db-helper.cc
@@ -26,6 +26,7 @@
 #include <boost/throw_exception.hpp>
 
 using namespace boost;
+namespace fs = boost::filesystem;
 
 const std::string INIT_DATABASE = "\
 PRAGMA foreign_keys = ON;                                       \n\
@@ -162,13 +163,16 @@
 CREATE INDEX FileState_device_id_seq_no ON FileState (device_id, seq_no); \n\
 ";
 
-DbHelper::DbHelper (const std::string &path)
+DbHelper::DbHelper (const fs::path &path)
 {
-  int res = sqlite3_open((path+"chronoshare.db").c_str (), &m_db);
+  fs::path chronoshareDirectory = path / ".chronoshare";
+  fs::create_directories (chronoshareDirectory);
+  
+  int res = sqlite3_open((chronoshareDirectory / "state.db").c_str (), &m_db);
   if (res != SQLITE_OK)
     {
       BOOST_THROW_EXCEPTION (Error::Db ()
-                             << errmsg_info_str ("Cannot open/create dabatabase: [" + path + "chronoshare.db" + "]"));
+                             << errmsg_info_str ("Cannot open/create dabatabase: [" + (chronoshareDirectory / "state.db").string () + "]"));
     }
   
   res = sqlite3_create_function (m_db, "hash", 2, SQLITE_ANY, 0, 0,
diff --git a/src/db-helper.h b/src/db-helper.h
index 51970dd..a32df64 100644
--- a/src/db-helper.h
+++ b/src/db-helper.h
@@ -28,13 +28,14 @@
 #include <boost/exception/all.hpp>
 #include <string>
 #include "hash-helper.h"
+#include <boost/filesystem.hpp>
 
 typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info_str; 
 
 class DbHelper
 {
 public:
-  DbHelper (const std::string &path);
+  DbHelper (const boost::filesystem::path &path);
   virtual ~DbHelper ();
   
 private:
diff --git a/src/hash-helper.cc b/src/hash-helper.cc
index 33c0e77..dbb70a3 100644
--- a/src/hash-helper.cc
+++ b/src/hash-helper.cc
@@ -33,11 +33,12 @@
 #include <boost/archive/iterators/transform_width.hpp>
 #include <boost/iterator/transform_iterator.hpp>
 #include <boost/archive/iterators/dataflow_exception.hpp>
+#include <boost/filesystem/fstream.hpp>
 
 using namespace boost;
 using namespace boost::archive::iterators;
 using namespace std;
-
+namespace fs = boost::filesystem;
 
 template<class CharType>
 struct hex_from_4_bit
@@ -130,7 +131,7 @@
 }
 
 HashPtr
-Hash::FromFileContent (const std::string &filename)
+Hash::FromFileContent (const fs::path &filename)
 {
   HashPtr retval = make_shared<Hash> (reinterpret_cast<void*> (0), 0);
   retval->m_buf = new unsigned char [EVP_MAX_MD_SIZE];
@@ -138,7 +139,7 @@
   EVP_MD_CTX *hash_context = EVP_MD_CTX_create ();
   EVP_DigestInit_ex (hash_context, HASH_FUNCTION (), 0);
 
-  ifstream iff (filename.c_str (), std::ios::in | std::ios::binary);
+  fs::ifstream iff (filename, std::ios::in | std::ios::binary);
   while (iff.good ())
     {
       char buf[1024];
diff --git a/src/hash-helper.h b/src/hash-helper.h
index ea12f30..d4f750d 100644
--- a/src/hash-helper.h
+++ b/src/hash-helper.h
@@ -26,6 +26,7 @@
 #include <iostream>
 #include <boost/shared_ptr.hpp>
 #include <boost/exception/all.hpp>
+#include <boost/filesystem.hpp>
 
 // Other options: VP_md2, EVP_md5, EVP_sha, EVP_sha1, EVP_sha256, EVP_dss, EVP_dss1, EVP_mdc2, EVP_ripemd160
 #define HASH_FUNCTION EVP_sha256
@@ -50,7 +51,7 @@
   FromString (const std::string &hashInTextEncoding);
 
   static HashPtr
-  FromFileContent (const std::string &hashInTextEncoding);
+  FromFileContent (const boost::filesystem::path &fileName);
   
   ~Hash ()
   {
diff --git a/src/object-db-file.cpp b/src/object-db-file.cpp
deleted file mode 100644
index 26f8fb3..0000000
--- a/src/object-db-file.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-#include "object-db-file.h"
-#include <assert.h>
-
-char *
-head(const Bytes &bytes)
-{
-  return (char *)&bytes[0];
-}
-
-void
-writeBytes(ostream &out, const Bytes &bytes)
-{
-  int size = bytes.size();
-  writeInt(out, size);
-  out.write(head(bytes), size);
-}
-
-void
-readBytes(istream &in, Bytes &bytes)
-{
-  int size;
-  readInt(in, size);
-  bytes.reserve(size);
-  in.read(head(bytes), size);
-}
-
-ObjectDBFile::ObjectDBFile(const string &filename)
-                : m_size(0)
-                , m_cap(0)
-                , m_index(0)
-                , m_initialized(false)
-                , m_filename(filename)
-                // This ensures file with filename exists (assuming having write permission)
-                // This is needed as file_lock only works with existing file
-                , m_ostream(m_filename.c_str(), ios_base::binary | ios_base::app)
-                , m_istream(m_filename.c_str(), ios_base::binary | ios_base::binary)
-                , m_filelock(m_filename.c_str())
-{
-  int magic;
-  ReadLock(m_filelock);
-  readInt(m_istream, magic);
-  if (magic == MAGIC_NUM)
-  {
-    m_initialized = true;
-    readInt(m_istream, m_cap);
-    readInt(m_istream, m_size);
-    m_istream.seekg( (3 + m_cap) * sizeof(int), ios::beg);
-  }
-}
-
-ObjectDBFile::~ObjectDBFile()
-{
-  m_istream.close();
-  m_ostream.close();
-}
-
-void
-ObjectDBFile::init(int capacity)
-{
-  WriteLock(*m_filelock);
-  if (m_initialized)
-  {
-    throwException("Trying to init already initialized ObjectDBFile object" + m_filename);
-  }
-
-  m_cap = capacity;
-  m_size = 0;
-
-  int magic = MAGIC_NUM;
-  writeInt(m_ostream, magic);
-  writeInt(m_ostream, m_cap);
-  writeInt(m_ostream, m_size);
-  m_initialized = true;
-
-  int count = m_cap;
-  int offset = 0;
-  while (count-- > 0)
-  {
-    writeInt(m_ostream, offset);
-  }
-
-  // prepare read pos
-  m_istream.seekg(m_ostream.tellp(), ios::beg);
-
-  // DEBUG
-  assert(m_ostream.tellp() == ((3 + m_cap) * sizeof(int)));
-
-}
-
-// Append is not super efficient as it needs to seek and update the pos for the
-// Content object. However, in our app, it is the case the these objects are wrote
-// once and read multiple times, so it's not a big problem.
-void
-ObjectDBFile::append(const Bytes &co)
-{
-  WriteLock(m_filelock);
-  checkInit("Trying to append to un-initialized ObjectDBFile: " + m_filename);
-
-  if (m_size >= m_cap)
-  {
-    throwException("Exceed Maximum capacity: " + boost::lexical_cast<string>(m_cap));
-  }
-
-  // pos for this CO
-  int coPos = m_ostream.tellp();
-  // index field for this CO
-  int indexPos = (3 + m_size) * sizeof(int);
-
-  m_size++;
-
-  // Update size (is it necessary?) We'll do it for now anyway
-  m_ostream.seekp( 2 * sizeof(int), ios::beg);
-  writeInt(m_ostream, m_size);
-
-  // Write the pos for the CO
-  m_ostream.seekp(indexPos, ios::beg);
-  writeInt(m_ostream, coPos);
-
-  // write the content object
-  m_ostream.seekp(coPos, ios::beg);
-  writeBytes(m_ostream, co);
-
-  // By the end, the write pos is at the end of the file
-}
-
-// forget about caching for now; but ideally, we should cache the next few COs in memory
-// and the request for COs tends to be sequential
-Bytes
-ObjectDBFile::next()
-{
-  // Scoped shared lock for cache
-  {
-    SLock(m_cacheLock);
-    // no need to read file if found in cache
-    if (m_dummyCache.find(m_index) != m_dummyCache.end())
-    {
-      int index = m_index;
-      m_index++;
-      return m_dummyCache[index];
-    }
-  }
-
-  ReadLock(m_filelock);
-
-  // m_index not found in cache
-  Bytes co;
-  if (m_index >= m_size)
-  {
-    // at the end of file, return empty
-    return co;
-  }
-
-  readBytes(m_istream, co);
-  m_index++;
-
-  // fill dummy cache with the next CACHE_SIZE COs
-  fillDummyCache();
-
-  return co;
-}
-
-void
-ObjectDBFile::fillDummyCache()
-{
-  ULock(m_cacheLock);
-  m_dummyCache.clear();
-  int stop = (m_index + CACHE_SIZE < m_size) ? m_index + CACHE_SIZE : m_size;
-  // the m_index should not change
-  int index = m_index;
-  while (index < stop)
-  {
-    Bytes co;
-    readBytes(m_istream, co);
-    m_dummyCache.insert(make_pair(index, co));
-    index++;
-  }
-}
-
-int
-ObjectDBFile::size() const
-{
-  return m_size;
-}
-
-void
-ObjectDBFile::updateSize()
-{
-  int pos = m_istream.tellg();
-  m_istream.seekg(2 * sizeof(int), ios::beg);
-  readInt(m_istream, m_size);
-  // recover the original pos
-  m_istream.seekg(pos, ios::beg);
-}
-
-int
-ObjectDBFile::fSize()
-{
-  ReadLock(m_filelock);
-  updateSize();
-  return m_size;
-}
-
-int
-ObjectDBFile::index()
-{
-  ReadLock(m_filelock);
-  return m_index;
-}
-
-bool
-ObjectDBFile::seek(int index)
-{
-  ReadLock(m_filelock);
-  updateSize();
-  if (m_size <= index)
-  {
-    return false;
-  }
-  m_index = index;
-  m_istream.seekg( (3 + m_index) * sizeof(int), ios::beg);
-  int pos;
-  readInt(m_istream, pos);
-  m_istream.seekg(pos, ios::beg);
-  return true;
-}
-
-void
-ObjectDBFile::rewind()
-{
-  ReadLock(m_filelock);
-  m_index = 0;
-  // point to the start of the CO fields
-  m_istream.seekg( (3 + m_cap) * sizeof(int), ios::beg);
-}
-
-void
-ObjectDBFile::checkInit(const string &msg)
-{
-  if (!m_initialized)
-  {
-    throwException(msg);
-  }
-}
diff --git a/src/object-db-file.h b/src/object-db-file.h
deleted file mode 100644
index 1eb1ef8..0000000
--- a/src/object-db-file.h
+++ /dev/null
@@ -1,153 +0,0 @@
-#ifndef OBJECT_DB_FILE_H
-#define OBJECT_DB_FILE_H
-
-#include "object-db.h"
-#include <stdio.h>
-#include <fstream>
-#include <sstream>
-#include <deque>
-#include <boost/thread/locks.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/thread/shared_mutex.hpp>
-#include <boost/interprocess/sync/file_lock.hpp>
-#include <boost/interprocess/sync/sharable_lock.hpp>
-#include <boost/interprocess/sync/scoped_lock.hpp>
-
-#define _OVERRIDE
-#ifdef __GNUC__
-#if __GNUC_MAJOR >= 4 && __GNUC_MINOR__ >= 7
-  #undef _OVERRIDE
-  #define _OVERRIDE override
-#endif // __GNUC__ version
-#endif // __GNUC__
-
-using namespace std;
-
-// This is a file based ObjectDB implementation
-// The assumption is, the Content Objects will be stored sequentially
-
-// To provide random access, we will have a table of "address" for each
-// ContentObject at the beginning of the file.
-// This also requires another assumption, that is the number of COs must
-// be know a priori. This requirement is reasonable for our dropbox-like
-// System, as the file we publish is static file and we can easily know
-// the number of COs before we store them into ObjectDB.
-
-/* How file looks like:
- * |MAGIC_NUM|capacity|size|pos for each CO ...|1st CO|2nd CO| ... |
- */
-
-class ObjectDBFile
-{
-public:
-  typedef boost::interprocess::file_lock Filelock;
-  typedef boost::interprocess::scoped_lock<Filelock> WriteLock;
-  typedef boost::interprocess::sharable_lock<Filelock> ReadLock;
-  typedef boost::shared_mutex Mutex;
-  typedef boost::shared_lock<Mutex> SLock;
-  typedef boost::unique_lock<Mutex> ULock;
-
-  ObjectDBFile(const string &filename);
-  virtual ~ObjectDBFile();
-
-  // reserve the "address" table for n COs; must reserve before
-  // write anything (unless reserved quota has not be consumed yet)
-  void
-  init(int capacity);
-
-  bool
-  initialized() const { return m_initialized; }
-
-  // assume sequential
-  virtual void
-  append(const Bytes &co) _OVERRIDE;
-
-  // get next CO
-  virtual Bytes
-  next() _OVERRIDE;
-
-  // size in terms of number of COs
-  // This is the lazy form of size, i.e. it returns the size cached in this object
-  // but that may not necessarily equal to the actual size kept in file
-  // This is enough if the caller knows for sure that no other thread is changing the
-  // file or the caller does not care about the new size.
-  virtual int
-  size() const _OVERRIDE;
-
-  // this returns the actual size (also update the size cache in this object), but it is more costly, and requires file IO
-  int
-  fSize();
-
-  // the index of the CO to be read
-  int
-  index();
-
-  // set the pos to be the desired CO
-  // return true if success
-  bool
-  seek(int index);
-
-  // reset pos to be zero
-  void
-  rewind();
-
-protected:
-  // read or write lock should have been grabbed already before the call
-  void
-  checkInit(const string &msg);
-
-  // read lock should have been grabbed already before the call
-  void
-  updateSize();
-
-  // read lock should have been grabbed already before the call
-  void
-  fillDummyCache();
-
-  #define MAGIC_NUM 0xAAAAAAAA
-
-protected:
-  string m_filename;
-  ifstream m_istream;
-  ofstream m_ostream;
-  Filelock m_filelock;
-  bool m_initialized;
-  // capacity in terms of number of COs
-  int m_cap;
-  int m_size;
-  // the index (or seq) of the CO to be read
-  int m_index;
-
-  // A dummy Cache that holds the next 10 (or all remaining if less than 10)
-  // COs after a next() operation
-  // If needed and time allows, we can have more complex cache
-  #define CACHE_SIZE 10
-  map<int, Bytes> m_dummyCache;
-  Mutex m_cacheMutex;
-};
-
-void inline
-writeInt(ostream &out, const int &x)
-{
-  out.write((const char *)&x, sizeof(int));
-}
-
-void inline
-readInt(istream &in, int &x)
-{
-  in.read((char *)&x, sizeof(int));
-}
-
-// write size and then the actual bytes
-// operator << overloading is not used to avoid confusion
-void
-writeBytes(ostream &out, const Bytes &bytes);
-
-// read size and then the actual bytes
-void
-readBytes(istream &in, Bytes &bytes);
-
-char *
-head(const Bytes &bytes);
-
-#endif
diff --git a/src/object-db.cc b/src/object-db.cc
new file mode 100644
index 0000000..0d1a2a6
--- /dev/null
+++ b/src/object-db.cc
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
+#include "object-db.h"
+#include <iostream>
+#include <boost/make_shared.hpp>
+#include "db-helper.h"
+#include <sys/stat.h>
+
+using namespace std;
+using namespace Ccnx;
+using namespace boost;
+namespace fs = boost::filesystem;
+
+const std::string INIT_DATABASE = "\
+CREATE TABLE                                                            \n\
+    File(                                                               \n\
+        device_name     BLOB NOT NULL,                                  \n\
+        segment         INTEGER,                                        \n\
+        content_object  BLOB,                                           \n\
+                                                                        \
+        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)
+{
+  fs::path actualFolder = folder / "objects" / hash.substr (0, 2);
+  fs::create_directories (actualFolder);
+  
+  int res = sqlite3_open((actualFolder / hash.substr (2, hash.size () - 2)).c_str (), &m_db);
+  if (res != SQLITE_OK)
+    {
+      BOOST_THROW_EXCEPTION (Error::Db ()
+                             << errmsg_info_str ("Cannot open/create dabatabase: [" +
+                                                 (actualFolder / hash.substr (2, hash.size () - 2)).string () + "]"));
+    }
+  
+  // Alex: determine if tables initialized. if not, initialize... not sure what is the best way to go...
+  // for now, just attempt to create everything
+
+  char *errmsg = 0;
+  res = sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, &errmsg);
+  if (res != SQLITE_OK && errmsg != 0)
+    {
+      // 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);
+  if (res != SQLITE_OK)
+    {
+      // complain
+    }
+}
+
+void
+ObjectDb::saveContentObject (const Ccnx::Name &deviceName, sqlite3_int64 segment, const Ccnx::Bytes &data)
+{
+  sqlite3_stmt *stmt;
+  sqlite3_prepare_v2 (m_db, "INSERT INTO File "
+                      "(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);
+  sqlite3_bind_blob (stmt, 3, &data[0], data.size (), SQLITE_TRANSIENT);
+
+  sqlite3_step (stmt);
+  sqlite3_finalize (stmt);
+}
+
+Ccnx::BytesPtr
+ObjectDb::fetchSegment (const Ccnx::Name &deviceName, sqlite3_int64 segment)
+{
+  sqlite3_stmt *stmt;
+  sqlite3_prepare_v2 (m_db, "SELECT content_object FROM File WHERE device_name=? AND segment=?", -1, &stmt, 0);
+
+  CcnxCharbufPtr buf = deviceName.toCcnxCharbuf ();
+  sqlite3_bind_blob (stmt, 1, buf->buf (), buf->length (), SQLITE_TRANSIENT);
+  sqlite3_bind_int64 (stmt, 2, segment);
+
+  BytesPtr ret;
+  
+  int res = sqlite3_step (stmt);
+  if (res == SQLITE_ROW)
+    {
+      const unsigned char *buf = reinterpret_cast<const unsigned char*> (sqlite3_column_blob (stmt, 0));
+      int bufBytes = sqlite3_column_bytes (stmt, 0);
+
+      ret = make_shared<Bytes> (buf, buf+bufBytes);
+    }
+
+  sqlite3_finalize (stmt);
+
+  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 8c31418..9ecd753 100644
--- a/src/object-db.h
+++ b/src/object-db.h
@@ -1,42 +1,54 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
 #ifndef OBJECT_DB_H
 #define OBJECT_DB_H
 
-#include <boost/exception/all.hpp>
-#include <vector>
+#include <string>
+#include <sqlite3.h>
+#include <ccnx-common.h>
+#include <ccnx-name.h>
+#include <boost/filesystem.hpp>
 
-using namespace std;
-
-struct ObjectDBException : virtual boost::exception, virtual exception { };
-typedef boost::error_info<struct tag_errmsg, std::string> error_info_str;
-
-inline void throwException(const string &msg) { boost::throw_exception(ObjectDBException() << error_info_str(msg)); }
-
-typedef unsigned char Byte;
-typedef vector<Byte> Bytes;
-
-// OK. This name is a bit miss-leading, but this ObjectDB is really some storage
-// that stores the Ccnx ContentObjects of a file. So unlike a normal database,
-// this DB is per file.
-
-// The assumption is, the ContentObjects will be write to ObjectDB sequentially
-// This guarantees that when read, the Nth ContentObject read has the sequence number N as the last component of its name
-class ObjectDB
+class ObjectDb
 {
 public:
-  virtual ~ObjectDB(){}
+  // database will be create in <folder>/<first-pair-of-hash-bytes>/<rest-of-hash>
+  ObjectDb (const boost::filesystem::path &folder, const std::string &hash);
+  ~ObjectDb ();
 
-  // assume sequential
-  virtual void
-  append(const Bytes &co) = 0;
+  void
+  saveContentObject (const Ccnx::Name &deviceName, sqlite3_int64 segment, const Ccnx::Bytes &data);
 
-  // get next CO
-  virtual Bytes
-  next() = 0;
+  Ccnx::BytesPtr
+  fetchSegment (const Ccnx::Name &deviceName, sqlite3_int64 segment);
 
-  // size in terms of number of COs
-  virtual int
-  size() = 0;
+  // 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;
 };
 
-#endif
+#endif // OBJECT_DB_H
diff --git a/src/object-manager.cc b/src/object-manager.cc
new file mode 100644
index 0000000..7f66b2a
--- /dev/null
+++ b/src/object-manager.cc
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
+#include "object-manager.h"
+#include "ccnx-name.h"
+#include "ccnx-common.h"
+#include "ccnx-pco.h"
+#include "object-db.h"
+
+#include <sys/stat.h>
+
+#include <fstream>
+#include <boost/lexical_cast.hpp>
+#include <boost/throw_exception.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+using namespace Ccnx;
+using namespace boost;
+using namespace std;
+namespace fs = boost::filesystem;
+
+const int MAX_FILE_SEGMENT_SIZE = 1024;
+
+ObjectManager::ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const fs::path &folder)
+  : m_ccnx (ccnx)
+  , m_folder (folder / ".chronoshare")
+{
+  fs::create_directories (m_folder);
+}
+
+ObjectManager::~ObjectManager ()
+{
+}
+
+HashPtr
+ObjectManager::localFileToObjects (const fs::path &file, const Ccnx::Name &deviceName)
+{
+  HashPtr fileHash = Hash::FromFileContent (file);
+  ObjectDb fileDb (m_folder, lexical_cast<string> (*fileHash));
+  
+  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 (deviceName);
+      name
+        .appendComp ("file")
+        .appendComp (fileHash->GetHash (), fileHash->GetHashBytes ())
+        .appendComp (segment);
+
+      // 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
new file mode 100644
index 0000000..f0cd6e6
--- /dev/null
+++ b/src/object-manager.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
+#ifndef OBJECT_MANAGER_H
+#define OBJECT_MANAGER_H
+
+#include <string>
+#include <ccnx-wrapper.h>
+#include <hash-helper.h>
+#include <boost/filesystem.hpp>
+
+// everything related to managing object files
+
+class ObjectManager
+{
+public:
+  ObjectManager (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &folder);
+  virtual ~ObjectManager ();
+
+  HashPtr
+  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;
+  boost::filesystem::path m_folder;
+};
+
+typedef boost::shared_ptr<ObjectManager> ObjectManagerPtr;
+
+namespace Error {
+struct ObjectManager : virtual boost::exception, virtual std::exception { };
+}
+
+#endif // OBJECT_MANAGER_H
diff --git a/src/sync-log.cc b/src/sync-log.cc
index ce5b46c..ca6adf7 100644
--- a/src/sync-log.cc
+++ b/src/sync-log.cc
@@ -26,7 +26,7 @@
 using namespace boost;
 using namespace std;
 
-SyncLog::SyncLog (const std::string &path, const std::string &localName)
+SyncLog::SyncLog (const boost::filesystem::path &path, const std::string &localName)
   : DbHelper (path)
   , m_localName (localName)
 {
diff --git a/src/sync-log.h b/src/sync-log.h
index 0316294..c75fcf7 100644
--- a/src/sync-log.h
+++ b/src/sync-log.h
@@ -31,7 +31,7 @@
 class SyncLog : public DbHelper
 {
 public:
-  SyncLog (const std::string &path, const std::string &localName);
+  SyncLog (const boost::filesystem::path &path, const std::string &localName);
 
   sqlite3_int64
   GetNextLocalSeqNo (); // side effect: local seq_no will be increased
diff --git a/test/database-test.cc b/test/database-test.cc
index a4fe0d6..dcbcc2b 100644
--- a/test/database-test.cc
+++ b/test/database-test.cc
@@ -26,19 +26,20 @@
 #include "action-log.h"
 #include <iostream>
 #include <ccnx-name.h>
+#include <boost/filesystem.hpp>
 
 using namespace std;
 using namespace boost;
 using namespace Ccnx;
+namespace fs = boost::filesystem;
 
 BOOST_AUTO_TEST_SUITE(DatabaseTest)
 
 
 BOOST_AUTO_TEST_CASE (BasicDatabaseTest)
 {
-  char dir_tmpl [] = "/tmp/tmp-chornoshare-XXXXXXXXXXX";
-  string tmp_dir = mkdtemp (dir_tmpl);
-  SyncLog db (tmp_dir, "/alex");
+  fs::path tmpdir = fs::unique_path (fs::temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
+  SyncLog db (tmpdir, "/alex");
 
   HashPtr hash = db.RememberStateInStateLog ();
   // should be empty
@@ -77,6 +78,7 @@
   // db.FindStateDifferences ("86b51f1f98662583b295b61385ae4450ff8fac955981f1ca4381144ab1d7a4e0",
   //                          "d001d4680fd9adcb48e34a795e3cc3d5d36f209fbab34fd57f70f362c2085310");
 
+  remove_all (tmpdir);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/test-object-manager.cc b/test/test-object-manager.cc
new file mode 100644
index 0000000..78337b3
--- /dev/null
+++ b/test/test-object-manager.cc
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
+#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;
+namespace fs = boost::filesystem;
+
+BOOST_AUTO_TEST_SUITE(ObjectManagerTests)
+
+BOOST_AUTO_TEST_CASE (ObjectManagerTest)
+{
+  fs::path tmpdir = fs::unique_path (fs::temp_directory_path () / "%%%%-%%%%-%%%%-%%%%");
+  cout << tmpdir << endl;
+  Name deviceName ("/device");
+  
+  CcnxWrapperPtr ccnx = make_shared<CcnxWrapper> ();
+  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);
+}
+
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/wscript b/wscript
index 6ec706f..5d38675 100644
--- a/wscript
+++ b/wscript
@@ -19,7 +19,6 @@
     conf.define ("CHRONOSHARE_VERSION", VERSION)
 
     conf.check_cfg(package='sqlite3', args=['--cflags', '--libs'], uselib_store='SQLITE3', mandatory=True)
-
     conf.check_cfg(package='libevent', args=['--cflags', '--libs'], uselib_store='LIBEVENT', mandatory=True)
     conf.check_cfg(package='libevent_pthreads', args=['--cflags', '--libs'], uselib_store='LIBEVENT_PTHREADS', mandatory=True)
 
@@ -36,7 +35,7 @@
     conf.load ('ccnx')
     conf.load('boost')
 
-    conf.check_boost(lib='system test iostreams regex thread')
+    conf.check_boost(lib='system test iostreams filesystem regex thread')
 
     boost_version = conf.env.BOOST_VERSION.split('_')
     if int(boost_version[0]) < 1 or int(boost_version[1]) < 46:
@@ -60,6 +59,16 @@
     conf.write_config_header('src/config.h')
 
 def build (bld):
+    common = bld.objects (
+        target = "common",
+        features = ["cxx"],
+        source = ['src/hash-helper.cc',
+                  'src/chronoshare-client.ice',
+                  ],
+        use = 'BOOST',
+        includes = ['include', 'src'],
+        )
+
     libccnx = bld (
         target=CCNXLIB,
         features=['cxx', 'cxxshlib'],
@@ -68,22 +77,13 @@
             'src/ccnx-pco.cpp',
             'src/ccnx-closure.cpp',
             'src/ccnx-tunnel.cpp',
-            'src/object-db-file.cpp',
+            'src/object-db.cc',
+            'src/object-manager.cc',
             'src/ccnx-name.cpp',
             'src/ccnx-selectors.cpp',
             'src/event-scheduler.cpp',
             ],
-        use = 'BOOST BOOST_THREAD SSL CCNX LIBEVENT LIBEVENT_PTHREADS',
-        includes = ['include', ],
-        )
-
-    common = bld.objects (
-        target = "common",
-        features = ["cxx"],
-        source = ['src/hash-helper.cc',
-                  'src/chronoshare-client.ice',
-                  ],
-        use = 'BOOST',
+        use = 'BOOST BOOST_THREAD BOOST_FILESYSTEM SSL SQLITE3 CCNX common LIBEVENT LIBEVENT_PTHREADS',
         includes = ['include', 'src'],
         )
 
@@ -107,7 +107,7 @@
           target="unit-tests",
           source = bld.path.ant_glob(['test/**/*.cc']),
           features=['cxx', 'cxxprogram'],
-          use = 'BOOST_TEST LIBEVENT LIBEVENT_PTHREADS ccnxx database',
+          use = 'BOOST_TEST ccnxx database',
           includes = ['include', 'src'],
           )