discovery: Add hierarchical model for chatroom invitation

Change-Id: I19e74745a5998fe075a373357df542fef317ae5f
diff --git a/src/controller-backend.cpp b/src/controller-backend.cpp
index 804716b..217228e 100644
--- a/src/controller-backend.cpp
+++ b/src/controller-backend.cpp
@@ -28,7 +28,9 @@
 using ndn::OnInterestValidationFailed;
 
 
-static const uint8_t ROUTING_PREFIX_SEPARATOR[2] = {0xF0, 0x2E};
+static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
+  ndn::name::Component::fromEscapedString("%F0%2E");
+static const int MAXIMUM_REQUEST = 3;
 
 ControllerBackend::ControllerBackend(QObject* parent)
   : QThread(parent)
@@ -92,13 +94,16 @@
   QMutexLocker locker(&m_mutex);
 
   Name invitationPrefix;
+  Name requestPrefix;
   Name routingPrefix = getInvitationRoutingPrefix();
   size_t offset = 0;
   if (!routingPrefix.isPrefixOf(m_identity)) {
-    invitationPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
+    invitationPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
+    requestPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
     offset = routingPrefix.size() + 1;
   }
   invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
+  requestPrefix.append(m_identity).append("CHRONOCHAT-INVITATION-REQUEST");
 
   const ndn::RegisteredPrefixId* invitationListenerId =
     m_face.setInterestFilter(invitationPrefix,
@@ -115,6 +120,19 @@
 
   m_invitationListenerId = invitationListenerId;
 
+  const ndn::RegisteredPrefixId* requestListenerId =
+    m_face.setInterestFilter(requestPrefix,
+                             bind(&ControllerBackend::onInvitationRequestInterest,
+                                  this, _1, _2, offset),
+                             [] (const Name& prefix, const std::string& failInfo) {});
+
+  if (m_requestListenerId != 0) {
+    m_face.unregisterPrefix(m_requestListenerId,
+                            []{},
+                            [] (const std::string& failInfo) {});
+  }
+
+  m_requestListenerId = requestListenerId;
 }
 
 ndn::Name
@@ -164,21 +182,45 @@
 }
 
 void
-ControllerBackend::onInvitationRegisterFailed(const Name& prefix, const string& failInfo)
+ControllerBackend::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
 {
   // _LOG_DEBUG("ControllerBackend::onInvitationRegisterFailed: " << failInfo);
 }
 
 void
+ControllerBackend::onInvitationRequestInterest(const ndn::Name& prefix,
+                                               const ndn::Interest& interest,
+                                               size_t routingPrefixOffset)
+{
+  shared_ptr<const Data> data = m_ims.find(interest);
+  if (data != nullptr) {
+    m_face.put(*data);
+    return;
+  }
+  Name interestName = interest.getName();
+  size_t i;
+  for (i = 0; i < interestName.size(); i++)
+    if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
+      break;
+  if (i < interestName.size()) {
+    string chatroom = interestName.at(i+1).toUri();
+    string alias = interestName.getSubName(i+2).getPrefix(-1).toUri();
+    emit invitationRequestReceived(QString::fromStdString(alias),
+                                   QString::fromStdString(chatroom),
+                                   interestName);
+  }
+}
+
+void
 ControllerBackend::onInvitationValidated(const shared_ptr<const Interest>& interest)
 {
   Invitation invitation(interest->getName());
   // Should be obtained via a method of ContactManager.
   string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri();
 
-  emit invitaionValidated(QString::fromStdString(alias),
-                          QString::fromStdString(invitation.getChatroom()),
-                          interest->getName());
+  emit invitationValidated(QString::fromStdString(alias),
+                           QString::fromStdString(invitation.getChatroom()),
+                           interest->getName());
 }
 
 void
@@ -238,6 +280,36 @@
   }
 }
 
+void
+ControllerBackend::onRequestResponse(const Interest& interest, Data& data)
+{
+  size_t i;
+  Name interestName = interest.getName();
+  for (i = 0; i < interestName.size(); i++) {
+    if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
+      break;
+  }
+  Name::Component chatroomName = interestName.at(i+1);
+  Block contentBlock = data.getContent();
+  int res = ndn::readNonNegativeInteger(contentBlock);
+  // if data is true,
+  if (res == 1)
+    emit startChatroom(QString::fromStdString(chatroomName.toUri()), false);
+  else
+    emit invitationRequestResult("You are rejected to enter chatroom: " + chatroomName.toUri());
+}
+
+void
+ControllerBackend::onRequestTimeout(const Interest& interest, int& resendTimes)
+{
+  if (resendTimes < MAXIMUM_REQUEST)
+    m_face.expressInterest(interest,
+                           bind(&ControllerBackend::onRequestResponse, this, _1, _2),
+                           bind(&ControllerBackend::onRequestTimeout, this, _1, resendTimes + 1));
+  else
+    emit invitationRequestResult("Invitation request times out.");
+}
+
 // public slots:
 void
 ControllerBackend::shutdown()
@@ -321,7 +393,7 @@
   else {
     Name wrappedName;
     wrappedName.append(invitationRoutingPrefix)
-      .append(ROUTING_PREFIX_SEPARATOR, 2)
+      .append(ROUTING_HINT_SEPARATOR)
       .append(response->getName());
 
     // _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
@@ -339,6 +411,41 @@
 }
 
 void
+ControllerBackend::onInvitationRequestResponded(const ndn::Name& invitationResponseName,
+                                                bool accepted)
+{
+  shared_ptr<Data> response = make_shared<Data>(invitationResponseName);
+  if (accepted)
+    response->setContent(ndn::nonNegativeIntegerBlock(tlv::Content, 1));
+  else
+    response->setContent(ndn::nonNegativeIntegerBlock(tlv::Content, 0));
+
+  m_keyChain.signByIdentity(*response, m_identity);
+  m_ims.insert(*response);
+  m_face.put(*response);
+}
+
+void
+ControllerBackend::onSendInvitationRequest(const QString& chatroomName, const QString& prefix)
+{
+  if (prefix.length() == 0)
+    return;
+  Name interestName = getInvitationRoutingPrefix();
+  interestName.append(ROUTING_HINT_SEPARATOR).append(prefix.toStdString());
+  interestName.append("CHRONOCHAT-INVITATION-REQUEST");
+  interestName.append(chatroomName.toStdString());
+  interestName.append(m_identity);
+  interestName.appendTimestamp();
+  Interest interest(interestName);
+  interest.setInterestLifetime(time::milliseconds(10000));
+  interest.setMustBeFresh(true);
+  interest.getNonce();
+  m_face.expressInterest(interest,
+                         bind(&ControllerBackend::onRequestResponse, this, _1, _2),
+                         bind(&ControllerBackend::onRequestTimeout, this, _1, 0));
+}
+
+void
 ControllerBackend::onContactIdListReady(const QStringList& list)
 {
   ContactList contactList;