major change: Add security support & Adjust GUI

Change-Id: I7abef37169dec1ef4b68e760dee5214c147c1915
diff --git a/src/controller.cpp b/src/controller.cpp
new file mode 100644
index 0000000..723617a
--- /dev/null
+++ b/src/controller.cpp
@@ -0,0 +1,803 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *                     Yingdi Yu
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include <QApplication>
+#include <QMessageBox>
+#include <QDir>
+#include <QTimer>
+#include "controller.h"
+
+#ifndef Q_MOC_RUN
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <ndn-cpp-dev/util/random.hpp>
+#include <cryptopp/sha.h>
+#include <cryptopp/hex.h>
+#include <cryptopp/files.h>
+#include <cryptopp/filters.h>
+#include "config.pb.h"
+#include "endorse-info.pb.h"
+#include "logging.h"
+#endif
+
+INIT_LOGGER("chronos.Controller");
+
+using namespace ndn;
+
+Q_DECLARE_METATYPE(ndn::Name)
+Q_DECLARE_METATYPE(ndn::IdentityCertificate)
+Q_DECLARE_METATYPE(chronos::EndorseInfo)
+
+namespace chronos {
+
+static const uint8_t ROUTING_PREFIX_SEPARATOR[2] = {0xF0, 0x2E};
+
+// constructor & destructor
+Controller::Controller(shared_ptr<Face> face,
+		       QWidget* parent)
+  : QDialog(parent)
+  , m_face(face)
+  , m_invitationListenerId(0)
+  , m_contactManager(m_face)
+  , m_settingDialog(new SettingDialog)
+  , m_startChatDialog(new StartChatDialog)
+  , m_profileEditor(new ProfileEditor)
+  , m_invitationDialog(new InvitationDialog)
+  , m_contactPanel(new ContactPanel)
+  , m_browseContactDialog(new BrowseContactDialog)
+  , m_addContactPanel(new AddContactPanel)
+{
+  qRegisterMetaType<ndn::Name>("ndn.Name");
+  qRegisterMetaType<ndn::IdentityCertificate>("ndn.IdentityCertificate");
+  qRegisterMetaType<chronos::EndorseInfo>("chronos.EndorseInfo");
+
+  connect(this, SIGNAL(localPrefixUpdated(const QString&)),
+          this, SLOT(onLocalPrefixUpdated(const QString&)));
+
+  // Connection to ContactManager
+  connect(this, SIGNAL(identityUpdated(const QString&)),
+          &m_contactManager, SLOT(onIdentityUpdated(const QString&)));
+  connect(&m_contactManager, SIGNAL(warning(const QString&)),
+          this, SLOT(onWarning(const QString&)));
+  connect(this, SIGNAL(refreshBrowseContact()),
+          &m_contactManager, SLOT(onRefreshBrowseContact()));
+  connect(&m_contactManager, SIGNAL(contactInfoFetchFailed(const QString&)),
+          this, SLOT(onWarning(const QString&)));
+  connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
+          this, SLOT(onContactIdListReady(const QStringList&)));
+
+  // Connection to SettingDialog
+  connect(this, SIGNAL(identityUpdated(const QString&)),
+          m_settingDialog, SLOT(onIdentityUpdated(const QString&)));
+  connect(m_settingDialog, SIGNAL(identityUpdated(const QString&)),
+          this, SLOT(onIdentityUpdated(const QString&)));
+  connect(m_settingDialog, SIGNAL(nickUpdated(const QString&)),
+          this, SLOT(onNickUpdated(const QString&)));
+
+  // Connection to ProfileEditor
+  connect(this, SIGNAL(closeDBModule()),
+          m_profileEditor, SLOT(onCloseDBModule()));
+  connect(this, SIGNAL(identityUpdated(const QString&)),
+          m_profileEditor, SLOT(onIdentityUpdated(const QString&)));
+  connect(m_profileEditor, SIGNAL(updateProfile()),
+          &m_contactManager, SLOT(onUpdateProfile()));
+
+  // Connection to StartChatDialog
+  connect(m_startChatDialog, SIGNAL(startChatroom(const QString&, bool)),
+          this, SLOT(onStartChatroom(const QString&, bool)));
+
+  // Connection to InvitationDialog
+  connect(m_invitationDialog, SIGNAL(invitationResponded(const ndn::Name&, bool)),
+          this, SLOT(onInvitationResponded(const ndn::Name&, bool)));
+
+  // Connection to AddContactPanel
+  connect(m_addContactPanel, SIGNAL(fetchInfo(const QString&)),
+          &m_contactManager, SLOT(onFetchContactInfo(const QString&)));
+  connect(m_addContactPanel, SIGNAL(addContact(const QString&)),
+          &m_contactManager, SLOT(onAddFetchedContact(const QString&)));
+  connect(&m_contactManager, SIGNAL(contactEndorseInfoReady(const chronos::EndorseInfo&)),
+          m_addContactPanel, SLOT(onContactEndorseInfoReady(const chronos::EndorseInfo&)));
+
+
+  // Connection to BrowseContactDialog
+  connect(m_browseContactDialog, SIGNAL(directAddClicked()),
+          this, SLOT(onDirectAdd()));
+  connect(m_browseContactDialog, SIGNAL(fetchIdCert(const QString&)),
+          &m_contactManager, SLOT(onFetchIdCert(const QString&)));
+  connect(m_browseContactDialog, SIGNAL(addContact(const QString&)),
+          &m_contactManager, SLOT(onAddFetchedContactIdCert(const QString&)));
+  connect(&m_contactManager, SIGNAL(idCertNameListReady(const QStringList&)),
+          m_browseContactDialog, SLOT(onIdCertNameListReady(const QStringList&)));
+  connect(&m_contactManager, SIGNAL(nameListReady(const QStringList&)),
+          m_browseContactDialog, SLOT(onNameListReady(const QStringList&)));
+  connect(&m_contactManager, SIGNAL(idCertReady(const ndn::IdentityCertificate&)),
+          m_browseContactDialog, SLOT(onIdCertReady(const ndn::IdentityCertificate&)));
+
+  // Connection to ContactPanel
+  connect(m_contactPanel, SIGNAL(waitForContactList()),
+          &m_contactManager, SLOT(onWaitForContactList()));
+  connect(m_contactPanel, SIGNAL(waitForContactInfo(const QString&)),
+          &m_contactManager, SLOT(onWaitForContactInfo(const QString&)));
+  connect(m_contactPanel, SIGNAL(removeContact(const QString&)),
+          &m_contactManager, SLOT(onRemoveContact(const QString&)));
+  connect(m_contactPanel, SIGNAL(updateAlias(const QString&, const QString&)),
+          &m_contactManager, SLOT(onUpdateAlias(const QString&, const QString&)));
+  connect(m_contactPanel, SIGNAL(updateIsIntroducer(const QString&, bool)),
+          &m_contactManager, SLOT(onUpdateIsIntroducer(const QString&, bool)));
+  connect(m_contactPanel, SIGNAL(updateEndorseCertificate(const QString&)),
+          &m_contactManager, SLOT(onUpdateEndorseCertificate(const QString&)));
+  connect(m_contactPanel, SIGNAL(warning(const QString&)),
+          this, SLOT(onWarning(const QString&)));
+  connect(this, SIGNAL(closeDBModule()),
+          m_contactPanel, SLOT(onCloseDBModule()));
+  connect(this, SIGNAL(identityUpdated(const QString&)),
+          m_contactPanel, SLOT(onIdentityUpdated(const QString&)));
+  connect(&m_contactManager, SIGNAL(contactAliasListReady(const QStringList&)),
+          m_contactPanel, SLOT(onContactAliasListReady(const QStringList&)));
+  connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
+          m_contactPanel, SLOT(onContactIdListReady(const QStringList&)));
+  connect(&m_contactManager, SIGNAL(contactInfoReady(const QString&, const QString&, const QString&, bool)),
+          m_contactPanel, SLOT(onContactInfoReady(const QString&, const QString&, const QString&, bool)));
+
+  initialize();
+
+  createTrayIcon();          
+}
+  
+Controller::~Controller()
+{
+  saveConf();
+}
+
+// public methods
+
+
+// private methods
+std::string
+Controller::getDBName()
+{
+  std::string dbName("chronos-");
+
+  std::stringstream ss;
+  {
+    using namespace CryptoPP;
+
+    SHA256 hash;
+    StringSource(m_identity.wireEncode().wire(), m_identity.wireEncode().size(), true,
+                 new HashFilter(hash, new HexEncoder(new FileSink(ss), false)));
+  }
+  dbName.append(ss.str()).append(".db");
+
+  return dbName;
+}
+
+void
+Controller::openDB()
+{
+  m_db = QSqlDatabase::addDatabase("QSQLITE");
+  QString path = (QDir::home().path());
+  path.append(QDir::separator()).append(".chronos").append(QDir::separator()).append(getDBName().c_str());
+  m_db.setDatabaseName(path);
+  bool ok = m_db.open();
+
+  _LOG_DEBUG("DB opened: " << std::boolalpha << ok );
+}
+
+void
+Controller::initialize()
+{
+  loadConf();
+  
+  m_keyChain.createIdentity(m_identity);
+
+  openDB();
+
+  emit identityUpdated(QString(m_identity.toUri().c_str()));
+
+  setInvitationListener();
+}
+
+void
+Controller::setInvitationListener()
+{
+  if(m_invitationListenerId != 0)
+    m_face->unsetInterestFilter(m_invitationListenerId);
+  
+  Name invitationPrefix;
+  Name routingPrefix = getInvitationRoutingPrefix();
+  size_t offset = 0;
+  if(!routingPrefix.isPrefixOf(m_identity))
+    {
+      invitationPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
+      offset = routingPrefix.size() + 1;
+    }
+  invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
+
+  m_invitationListenerId = m_face->setInterestFilter(invitationPrefix, 
+                                                     bind(&Controller::onInvitationInterest, this, _1, _2, offset),
+                                                     bind(&Controller::onInvitationRegisterFailed, this, _1, _2));
+}
+
+void
+Controller::loadConf()
+{
+  namespace fs = boost::filesystem;
+
+  fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
+  fs::create_directories (chronosDir);
+
+  std::ifstream is((chronosDir / "config").c_str ());
+  ChronoChat::Conf conf;
+  if(conf.ParseFromIstream(&is))
+    {
+      m_identity.clear();
+      m_identity.append(conf.identity());
+      if(conf.has_nick())
+        m_nick = conf.nick();
+      else
+        m_nick = m_identity.get(-1).toUri();
+    }
+  else
+    {
+      m_identity.clear();
+      // TODO: change below to system default;
+      m_identity.append("chronochat-tmp-identity")
+        .append(boost::lexical_cast<std::string>(random::generateWord64()));
+      
+      m_nick = m_identity.get(-1).toUri();
+    }
+}
+
+void 
+Controller::saveConf()
+{
+  namespace fs = boost::filesystem;
+
+  fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
+  fs::create_directories (chronosDir);
+
+  std::ofstream os((chronosDir / "config").c_str ());
+  ChronoChat::Conf conf;
+  conf.set_identity(m_identity.toUri());
+  if(!m_nick.empty())
+    conf.set_nick(m_nick);
+  conf.SerializeToOstream(&os);
+
+  os.close();
+}
+
+void
+Controller::createActions()
+{
+  m_startChatroom = new QAction(tr("Start new chat"), this);
+  connect(m_startChatroom, SIGNAL(triggered()), this, SLOT(onStartChatAction()));
+
+  m_settingsAction = new QAction(tr("Settings"), this);
+  connect(m_settingsAction, SIGNAL(triggered()), this, SLOT(onSettingsAction()));
+
+  m_editProfileAction = new QAction(tr("Edit profile"), this);
+  connect(m_editProfileAction, SIGNAL(triggered()), this, SLOT(onProfileEditorAction()));
+
+  m_contactListAction = new QAction(tr("Contact List"), this);
+  connect(m_contactListAction, SIGNAL(triggered()), this, SLOT(onContactListAction()));
+
+  m_addContactAction = new QAction(tr("Add contact"), this);
+  connect(m_addContactAction, SIGNAL(triggered()), this, SLOT(onAddContactAction()));
+
+  m_updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
+  connect(m_updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(onUpdateLocalPrefixAction()));
+
+  m_minimizeAction = new QAction(tr("Mi&nimize"), this);
+  connect(m_minimizeAction, SIGNAL(triggered()), this, SLOT(onMinimizeAction()));
+
+  m_quitAction = new QAction(tr("Quit"), this);
+  connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuitAction()));
+}
+
+void
+Controller::createTrayIcon()
+{
+  createActions();
+
+  m_trayIconMenu = new QMenu(this);
+  m_trayIconMenu->addAction(m_startChatroom);
+  m_trayIconMenu->addSeparator();
+  m_trayIconMenu->addAction(m_settingsAction);
+  m_trayIconMenu->addAction(m_editProfileAction);
+  m_trayIconMenu->addSeparator();
+  m_trayIconMenu->addAction(m_contactListAction);
+  m_trayIconMenu->addAction(m_addContactAction);
+  m_trayIconMenu->addSeparator();
+  m_trayIconMenu->addAction(m_updateLocalPrefixAction);
+  m_trayIconMenu->addSeparator();
+  m_trayIconMenu->addAction(m_minimizeAction);
+  m_closeMenu = m_trayIconMenu->addMenu("Close chatroom");
+  m_closeMenu->setEnabled(false);
+  m_trayIconMenu->addSeparator();
+  m_trayIconMenu->addAction(m_quitAction);
+
+  m_trayIcon = new QSystemTrayIcon(this);
+  m_trayIcon->setContextMenu(m_trayIconMenu);
+
+  m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
+  m_trayIcon->setToolTip("ChronoChat System Tray Icon");
+  m_trayIcon->setVisible(true);
+}
+
+void
+Controller::updateMenu()
+{
+  QMenu* menu = new QMenu(this);
+  QMenu* closeMenu = 0;
+  
+  menu->addAction(m_startChatroom);
+  menu->addSeparator();
+  menu->addAction(m_settingsAction);
+  menu->addAction(m_editProfileAction);
+  menu->addSeparator();
+  menu->addAction(m_addContactAction);
+  menu->addSeparator();
+  {
+    ChatActionList::const_iterator it = m_chatActionList.begin();
+    ChatActionList::const_iterator end = m_chatActionList.end();
+    if(it != end)
+      {
+        for(; it != end; it++)
+          menu->addAction(it->second);
+        menu->addSeparator();
+      }
+  }
+  menu->addAction(m_updateLocalPrefixAction);
+  menu->addSeparator();
+  menu->addAction(m_minimizeAction);
+  closeMenu = menu->addMenu("Close chatroom");
+  {
+    ChatActionList::const_iterator it = m_closeActionList.begin();
+    ChatActionList::const_iterator end = m_closeActionList.end();
+    if(it == end)
+      {
+        closeMenu->setEnabled(false);
+      }
+    else
+      {
+        for(; it != end; it++)
+          closeMenu->addAction(it->second);
+      }
+  }
+  menu->addSeparator();
+  menu->addAction(m_quitAction);
+  
+  m_trayIcon->setContextMenu(menu);
+  delete m_trayIconMenu;
+  m_trayIconMenu = menu;
+  m_closeMenu = closeMenu;
+}
+
+void
+Controller::onLocalPrefix(const Interest& interest, Data& data)
+{
+  QString localPrefixStr = QString::fromUtf8
+    (reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size())
+    .trimmed();
+
+  Name localPrefix(localPrefixStr.toStdString());
+  if(m_localPrefix.empty() || m_localPrefix != localPrefix)
+    emit localPrefixUpdated(localPrefixStr);
+}
+
+void
+Controller::onLocalPrefixTimeout(const Interest& interest)
+{
+  QString localPrefixStr("/private/local");
+
+  Name localPrefix(localPrefixStr.toStdString());
+  if(m_localPrefix.empty() || m_localPrefix != localPrefix)
+    emit localPrefixUpdated(localPrefixStr);
+}
+
+void
+Controller::onInvitationInterest(const Name& prefix, const Interest& interest, size_t routingPrefixOffset)
+{
+  shared_ptr<Interest> invitationInterest = make_shared<Interest>(boost::cref(interest.getName().getSubName(routingPrefixOffset)));
+
+  // check if the chatroom already exists;
+  try
+    {
+      Invitation invitation(invitationInterest->getName());
+      if(m_chatDialogList.find(invitation.getChatroom()) != m_chatDialogList.end())
+        return;
+    }
+  catch(Invitation::Error& e)
+    {
+      // Cannot parse the invitation;
+      return;
+    }
+
+  OnInterestValidated onValidated = bind(&Controller::onInvitationValidated, this, _1);
+  OnInterestValidationFailed onValidationFailed = bind(&Controller::onInvitationValidationFailed, this, _1, _2);
+  m_validator.validate(*invitationInterest, onValidated, onValidationFailed);
+}
+
+void
+Controller::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
+{
+  std::cerr << "Controller::onInvitationRegisterFailed: " << failInfo << std::endl;
+}
+
+void
+Controller::onInvitationValidated(const shared_ptr<const Interest>& interest)
+{
+  Invitation invitation(interest->getName());
+  std::string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri(); // Should be obtained via a method of ContactManager.
+
+  m_invitationDialog->setInvitation(alias, invitation.getChatroom(), interest->getName());
+  m_invitationDialog->show();
+}
+
+void
+Controller::onInvitationValidationFailed(const shared_ptr<const Interest>& interest, std::string failureInfo)
+{
+  std::cerr << "Invitation: " << interest->getName() << " cannot not be validated due to: " << failureInfo << std::endl;
+}
+
+std::string
+Controller::getRandomString()
+{
+  uint32_t r = random::generateWord32();
+  std::stringstream ss;
+  {
+    using namespace CryptoPP;
+    StringSource(reinterpret_cast<uint8_t*>(&r), 4, true,
+                 new HexEncoder(new FileSink(ss), false));
+    
+  }
+  // for(int i = 0; i < 8; i++)
+  //   {
+  //     uint32_t t = r & mask;
+  //     if(t < 10)
+  //       ss << static_cast<char>(t + 0x30);
+  //     else
+  //       ss << static_cast<char>(t + 0x57);
+  //     r = r >> 4;
+  //   }
+
+  return ss.str();
+}
+
+ndn::Name
+Controller::getInvitationRoutingPrefix()
+{
+  return Name("/ndn/broadcast");
+}
+
+void
+Controller::addChatDialog(const QString& chatroomName, ChatDialog* chatDialog)
+{
+  m_chatDialogList[chatroomName.toStdString()] = chatDialog;
+  connect(chatDialog, SIGNAL(closeChatDialog(const QString&)),
+          this, SLOT(onRemoveChatDialog(const QString&)));
+  connect(chatDialog, SIGNAL(showChatMessage(const QString&, const QString&, const QString&)),
+          this, SLOT(onShowChatMessage(const QString&, const QString&, const QString&)));
+  connect(this, SIGNAL(localPrefixUpdated(const QString&)),
+          chatDialog, SLOT(onLocalPrefixUpdated(const QString&)));
+
+  QAction* chatAction = new QAction(chatroomName, this);
+  m_chatActionList[chatroomName.toStdString()] = chatAction;
+  connect(chatAction, SIGNAL(triggered()), chatDialog, SLOT(showNormal()));
+
+  QAction* closeAction = new QAction(chatroomName, this);
+  m_closeActionList[chatroomName.toStdString()] = closeAction;
+  connect(closeAction, SIGNAL(triggered()), chatDialog, SLOT(onClose()));
+
+  updateMenu();
+}
+
+// private slots:
+void
+Controller::onIdentityUpdated(const QString& identity)
+{
+  Name identityName(identity.toStdString());
+
+  while(!m_chatDialogList.empty())
+    {
+      ChatDialogList::const_iterator it = m_chatDialogList.begin();
+      onRemoveChatDialog(QString::fromStdString(it->first));
+    }
+
+  m_identity = identityName;
+  m_keyChain.createIdentity(m_identity);
+
+  emit closeDBModule();
+  
+  QTimer::singleShot(500, this, SLOT(onIdentityUpdatedContinued()));
+
+}
+
+void
+Controller::onIdentityUpdatedContinued()
+{
+  QString connection = m_db.connectionName();
+  // _LOG_DEBUG("connection name: " << connection.toStdString());
+  QSqlDatabase::removeDatabase(connection);
+  m_db.close();
+  
+  openDB();
+
+  emit identityUpdated(QString(m_identity.toUri().c_str()));
+}
+
+void
+Controller::onContactIdListReady(const QStringList& list)
+{
+  ContactList contactList;
+
+  m_contactManager.getContactList(contactList);
+  m_validator.cleanTrustAnchor();
+
+  ContactList::const_iterator it  = contactList.begin();
+  ContactList::const_iterator end = contactList.end();
+
+  for(; it != end; it++)
+    m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
+
+}
+
+void
+Controller::onNickUpdated(const QString& nick)
+{
+  m_nick = nick.toStdString();
+}
+
+void
+Controller::onLocalPrefixUpdated(const QString& localPrefix)
+{
+  m_localPrefix = Name(localPrefix.toStdString());
+}
+
+void
+Controller::onStartChatAction()
+{
+  std::string chatroom = "chatroom-" + getRandomString();
+
+  m_startChatDialog->setChatroom(chatroom);
+  m_startChatDialog->show();
+  m_startChatDialog->raise();
+}
+
+void
+Controller::onSettingsAction()
+{
+  m_settingDialog->setNick(QString(m_nick.c_str()));
+  m_settingDialog->show();
+  m_settingDialog->raise();
+}
+
+void
+Controller::onProfileEditorAction()
+{
+  m_profileEditor->show();
+  m_profileEditor->raise();
+}
+
+void
+Controller::onAddContactAction()
+{
+  emit refreshBrowseContact();
+  m_browseContactDialog->show();
+  m_browseContactDialog->raise();
+}
+
+void
+Controller::onContactListAction()
+{
+  m_contactPanel->show();
+  m_contactPanel->raise();
+}
+
+void
+Controller::onDirectAdd()
+{
+  m_addContactPanel->show();
+  m_addContactPanel->raise();
+}
+
+void
+Controller::onUpdateLocalPrefixAction()
+{
+  // Name interestName();
+  Interest interest("/local/ndn/prefix");
+  interest.setInterestLifetime(1000);
+  interest.setMustBeFresh(true);
+
+  m_face->expressInterest(interest, 
+                          bind(&Controller::onLocalPrefix, this, _1, _2), 
+                          bind(&Controller::onLocalPrefixTimeout, this, _1));  
+}
+
+void
+Controller::onMinimizeAction()
+{
+  m_settingDialog->hide();
+  m_startChatDialog->hide();
+  m_profileEditor->hide();
+  m_invitationDialog->hide();
+  m_addContactPanel->hide();
+
+  ChatDialogList::iterator it = m_chatDialogList.begin();
+  ChatDialogList::iterator end = m_chatDialogList.end();
+  for(; it != end; it++)
+    it->second->hide();
+}
+
+void
+Controller::onQuitAction()
+{
+  while(!m_chatDialogList.empty())
+    {
+      ChatDialogList::const_iterator it = m_chatDialogList.begin();
+      onRemoveChatDialog(QString::fromStdString(it->first));
+    }
+
+  delete m_settingDialog;
+  delete m_startChatDialog;
+  delete m_profileEditor;
+  delete m_invitationDialog;
+  delete m_addContactPanel;
+  // TODO: clean up all the dialog.
+
+  QApplication::quit();
+}
+
+void
+Controller::onStartChatroom(const QString& chatroomName, bool secured)
+{
+  Name chatroomPrefix;
+  chatroomPrefix.append("ndn")
+    .append("broadcast")
+    .append("ChronoChat")
+    .append(chatroomName.toStdString());
+
+  // check if the chatroom exists
+  if(m_chatDialogList.find(chatroomName.toStdString()) != m_chatDialogList.end())
+    {
+      QMessageBox::information(this, tr("ChronoChat"),
+                               tr("You are creating an existing chatroom."
+                                  "You can check it in the context memu."));
+      return;
+    }
+
+  // TODO: We should create a chatroom specific key/cert (which should be created in the first half of this method, but let's use the default one for now.
+  shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
+  ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, secured);
+
+  addChatDialog(chatroomName, chatDialog);  
+  chatDialog->show();
+}
+
+void
+Controller::onInvitationResponded(const Name& invitationName, bool accepted)
+{
+  Data response;
+  shared_ptr<IdentityCertificate> chatroomCert;
+
+  // generate reply;
+  if(accepted)
+    {
+      Name responseName = invitationName;
+      responseName.append(m_localPrefix.wireEncode());
+
+      response.setName(responseName);
+
+      // We should create a particular certificate for this chatroom, but let's use default one for now.
+      chatroomCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
+
+      response.setContent(chatroomCert->wireEncode());
+      response.setFreshnessPeriod(1000);      
+    }
+  else
+    {
+      response.setName(invitationName);
+      response.setFreshnessPeriod(1000);
+    }
+  
+  // Check if we need a wrapper
+  Name invitationRoutingPrefix = getInvitationRoutingPrefix();
+  if(invitationRoutingPrefix.isPrefixOf(m_identity))
+    {
+      m_keyChain.signByIdentity(response, m_identity);
+      m_face->put(response);
+    }
+  else
+    {
+      Name wrappedName;
+      wrappedName.append(invitationRoutingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
+      
+      Data wrappedData(wrappedName);
+      wrappedData.setContent(response.wireEncode());
+      wrappedData.setFreshnessPeriod(1000);
+
+      m_keyChain.signByIdentity(response, m_identity);
+      m_face->put(response);
+    }
+
+  // create chatroom
+  if(accepted)
+    {
+      Invitation invitation(invitationName);
+      Name chatroomPrefix;
+      chatroomPrefix.append("ndn")
+        .append("broadcast")
+        .append("ChronoChat")
+        .append(invitation.getChatroom());
+
+      //We should create a chatroom specific key/cert (which should be created in the first half of this method, but let's use the default one for now.
+      shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
+      ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, true);
+
+      addChatDialog(QString::fromStdString(invitation.getChatroom()), chatDialog);
+      chatDialog->addSyncAnchor(invitation);
+      chatDialog->show();
+    }
+}
+
+void
+Controller::onShowChatMessage(const QString& chatroomName, const QString& from, const QString& data)
+{
+  m_trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(chatroomName), 
+                          QString("<%1>: %2").arg(from).arg(data), 
+                          QSystemTrayIcon::Information, 20000);
+  m_trayIcon->setIcon(QIcon(":/images/note.png"));
+}
+
+void
+Controller::onRemoveChatDialog(const QString& chatroomName)
+{
+  ChatDialogList::iterator it = m_chatDialogList.find(chatroomName.toStdString());
+
+  if(it != m_chatDialogList.end())
+    {
+      ChatDialog* deletedChat = it->second;
+      if(deletedChat)
+        delete deletedChat;
+      m_chatDialogList.erase(it);
+
+      QAction* chatAction = m_chatActionList[chatroomName.toStdString()];
+      QAction* closeAction = m_closeActionList[chatroomName.toStdString()];
+      if(chatAction)
+        delete chatAction;
+      if(closeAction)
+        delete closeAction;
+      
+      m_chatActionList.erase(chatroomName.toStdString());
+      m_closeActionList.erase(chatroomName.toStdString());
+      
+      updateMenu();
+    }
+}
+
+void
+Controller::onWarning(const QString& msg)
+{
+  QMessageBox::information(this, tr("ChronoChat"), msg);
+}
+
+void
+Controller::onError(const QString& msg) 
+{
+  QMessageBox::critical(this, tr("ChronoChat"), msg, QMessageBox::Ok);
+  exit(1);
+}
+
+} // namespace chronos
+
+#if WAF
+#include "controller.moc"
+#include "controller.cpp.moc"
+#endif