src: Add chatroom discovery
Change-Id: I45e17a8d8bbcdef6dc5f93c528cde91181f3b578
diff --git a/src/chat-dialog-backend.cpp b/src/chat-dialog-backend.cpp
index f175615..1ea25ac 100644
--- a/src/chat-dialog-backend.cpp
+++ b/src/chat-dialog-backend.cpp
@@ -26,6 +26,7 @@
static const time::milliseconds FRESHNESS_PERIOD(60000);
static const time::seconds HELLO_INTERVAL(60);
static const uint8_t ROUTING_HINT_SEPARATOR[2] = {0xF0, 0x2E}; // %F0.
+static const int IDENTITY_OFFSET = -3;
ChatDialogBackend::ChatDialogBackend(const Name& chatroomPrefix,
const Name& userChatPrefix,
@@ -264,6 +265,9 @@
// remove roster entry
m_roster.erase(remoteSessionPrefix);
+
+ emit eraseInRoster(remoteSessionPrefix.getPrefix(IDENTITY_OFFSET),
+ Name::Component(m_chatroomName));
}
}
else {
@@ -281,6 +285,9 @@
emit sessionAdded(QString::fromStdString(remoteSessionPrefix.toUri()),
QString::fromStdString(msg.from()),
msg.timestamp());
+
+ emit addInRoster(remoteSessionPrefix.getPrefix(IDENTITY_OFFSET),
+ Name::Component(m_chatroomName));
}
// If we get a new nick for an existing session, update it.
@@ -330,6 +337,9 @@
// remove roster entry
m_roster.erase(sessionPrefix);
+
+ emit eraseInRoster(sessionPrefix.getPrefix(IDENTITY_OFFSET),
+ Name::Component(m_chatroomName));
}
void
@@ -350,7 +360,8 @@
m_sock->publishData(os.buf()->buf(), os.buf()->size(), FRESHNESS_PERIOD);
std::vector<NodeInfo> nodeInfos;
- NodeInfo nodeInfo = {QString::fromStdString(m_routableUserChatPrefix.toUri()),
+ Name sessionName = m_sock->getLogic().getSessionName();
+ NodeInfo nodeInfo = {QString::fromStdString(sessionName.toUri()),
nextSequence};
nodeInfos.push_back(nodeInfo);
@@ -370,7 +381,8 @@
m_helloEventId = m_scheduler->scheduleEvent(HELLO_INTERVAL,
bind(&ChatDialogBackend::sendHello, this));
- emit sessionAdded(QString::fromStdString(m_routableUserChatPrefix.toUri()),
+ Name sessionName = m_sock->getLogic().getSessionName();
+ emit sessionAdded(QString::fromStdString(sessionName.toUri()),
QString::fromStdString(msg.from()),
msg.timestamp());
}
@@ -393,6 +405,10 @@
prepareControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
sendMsg(msg);
+ // get my own identity with routable prefix by getPrefix(-2)
+ emit eraseInRoster(m_routableUserChatPrefix.getPrefix(-2),
+ Name::Component(m_chatroomName));
+
usleep(5000);
m_joined = false;
}
diff --git a/src/chat-dialog-backend.hpp b/src/chat-dialog-backend.hpp
index f01c015..f8133f7 100644
--- a/src/chat-dialog-backend.hpp
+++ b/src/chat-dialog-backend.hpp
@@ -125,6 +125,12 @@
void
chatPrefixChanged(ndn::Name newChatPrefix);
+ void
+ eraseInRoster(ndn::Name sessionPrefix, ndn::Name::Component chatroomName);
+
+ void
+ addInRoster(ndn::Name sessionPrefix, ndn::Name::Component chatroomName);
+
public slots:
void
sendChatMessage(QString text, time_t timestamp);
diff --git a/src/chat-dialog.cpp b/src/chat-dialog.cpp
index 776e076..ad04d09 100644
--- a/src/chat-dialog.cpp
+++ b/src/chat-dialog.cpp
@@ -38,6 +38,7 @@
, ui(new Ui::ChatDialog)
, m_backend(chatroomPrefix, userChatPrefix, routingPrefix, chatroomName, nick, signingId)
, m_chatroomName(chatroomName)
+ , m_chatroomPrefix(chatroomPrefix)
, m_nick(nick.c_str())
, m_isSecured(isSecured)
{
@@ -172,24 +173,23 @@
fitView();
}
-shared_ptr<ChatroomInfo>
+ChatroomInfo
ChatDialog::getChatroomInfo()
{
- shared_ptr<ChatroomInfo> chatroomInfo = make_shared<ChatroomInfo>();
-
- chatroomInfo->setName(Name::Component(m_chatroomName));
-
+ ChatroomInfo chatroomInfo;
+ chatroomInfo.setName(Name::Component(m_chatroomName));
QStringList prefixList = m_scene->getRosterPrefixList();
for(QStringList::iterator it = prefixList.begin();
it != prefixList.end(); ++it ) {
Name participant = Name(it->toStdString()).getPrefix(-3);
- chatroomInfo->addParticipant(participant);
+ chatroomInfo.addParticipant(participant);
}
+ chatroomInfo.setSyncPrefix(m_chatroomPrefix);
if (m_isSecured)
- chatroomInfo->setTrustModel(ChatroomInfo::TRUST_MODEL_HIERARCHICAL);
+ chatroomInfo.setTrustModel(ChatroomInfo::TRUST_MODEL_HIERARCHICAL);
else
- chatroomInfo->setTrustModel(ChatroomInfo::TRUST_MODEL_NONE);
+ chatroomInfo.setTrustModel(ChatroomInfo::TRUST_MODEL_NONE);
return chatroomInfo;
}
diff --git a/src/chat-dialog.hpp b/src/chat-dialog.hpp
index 74dd76d..919c511 100644
--- a/src/chat-dialog.hpp
+++ b/src/chat-dialog.hpp
@@ -76,7 +76,7 @@
{
}
- shared_ptr<ChatroomInfo>
+ ChatroomInfo
getChatroomInfo();
private:
@@ -117,9 +117,6 @@
void
resetIcon();
- void
- rosterChanged(const chronochat::ChatroomInfo& info);
-
public slots:
void
onShow();
@@ -167,6 +164,7 @@
ChatDialogBackend m_backend;
std::string m_chatroomName;
+ Name m_chatroomPrefix;
QString m_nick;
bool m_isSecured;
diff --git a/src/chatroom-discovery-backend.cpp b/src/chatroom-discovery-backend.cpp
new file mode 100644
index 0000000..e7ed3ea
--- /dev/null
+++ b/src/chatroom-discovery-backend.cpp
@@ -0,0 +1,455 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
+ * Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "chatroom-discovery-backend.hpp"
+#include <QStringList>
+
+
+#ifndef Q_MOC_RUN
+
+#endif
+
+namespace chronochat {
+
+static const time::milliseconds FRESHNESS_PERIOD(60000);
+static const time::seconds REFRESH_INTERVAL(60);
+static const time::seconds HELLO_INTERVAL(60);
+static const uint8_t ROUTING_HINT_SEPARATOR[2] = {0xF0, 0x2E}; // %F0.
+// a count enforced when a manager himself find another one publish chatroom data
+static const int MAXIMUM_COUNT = 3;
+static const int IDENTITY_OFFSET = -1;
+
+ChatroomDiscoveryBackend::ChatroomDiscoveryBackend(const Name& routingPrefix,
+ const Name& identity,
+ QObject* parent)
+ : QThread(parent)
+ , m_routingPrefix(routingPrefix)
+ , m_identity(identity)
+ , m_randomGenerator(static_cast<unsigned int>(std::time(0)))
+ , m_rangeUniformRandom(m_randomGenerator, boost::uniform_int<>(500,2000))
+ , m_shouldResume(false)
+{
+ m_discoveryPrefix.append("ndn")
+ .append("broadcast")
+ .append("ChronoChat")
+ .append("Discovery");
+ m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
+ updatePrefixes();
+}
+
+ChatroomDiscoveryBackend::~ChatroomDiscoveryBackend()
+{
+}
+
+void
+ChatroomDiscoveryBackend::run()
+{
+ bool shouldResume = false;
+ do {
+ initializeSync();
+
+ if (m_face == nullptr)
+ break;
+
+ m_face->getIoService().run();
+
+ m_mutex.lock();
+ shouldResume = m_shouldResume;
+ m_shouldResume = false;
+ m_mutex.unlock();
+
+ } while (shouldResume);
+
+ std::cerr << "Bye!" << std::endl;
+}
+
+void
+ChatroomDiscoveryBackend::initializeSync()
+{
+ BOOST_ASSERT(m_sock == nullptr);
+
+ m_face = shared_ptr<ndn::Face>(new ndn::Face);
+ m_scheduler = unique_ptr<ndn::Scheduler>(new ndn::Scheduler(m_face->getIoService()));
+
+ m_sock = make_shared<chronosync::Socket>(m_discoveryPrefix,
+ Name(),
+ ref(*m_face),
+ bind(&ChatroomDiscoveryBackend::processSyncUpdate,
+ this, _1));
+
+ // add an timer to refresh front end
+ if (m_refreshPanelId != nullptr) {
+ m_scheduler->cancelEvent(m_refreshPanelId);
+ }
+ m_refreshPanelId = m_scheduler->scheduleEvent(REFRESH_INTERVAL,
+ [this] { sendChatroomList(); });
+}
+
+void
+ChatroomDiscoveryBackend::close()
+{
+ m_scheduler->cancelAllEvents();
+ m_refreshPanelId.reset();
+ m_chatroomList.clear();
+ m_sock.reset();
+}
+
+void
+ChatroomDiscoveryBackend::processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates)
+{
+ if (updates.empty()) {
+ return;
+ }
+ for (const auto& update : updates) {
+ m_sock->fetchData(update.session, update.high,
+ bind(&ChatroomDiscoveryBackend::processChatroomData, this, _1), 2);
+ }
+}
+
+void
+ChatroomDiscoveryBackend::processChatroomData(const ndn::shared_ptr<const ndn::Data>& data)
+{
+ // extract chatroom name by get(-3)
+ Name::Component chatroomName = data->getName().get(-3);
+ auto it = m_chatroomList.find(chatroomName);
+ if (it == m_chatroomList.end()) {
+ m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
+ m_chatroomList[chatroomName].count = 0;
+ m_chatroomList[chatroomName].isPrint = false;
+ m_chatroomList[chatroomName].isParticipant = false;
+ m_chatroomList[chatroomName].isManager = false;
+ it = m_chatroomList.find(chatroomName);
+ }
+ // If the user is the manager of this chatroom, he should not receive any data from this chatroom
+ if (it->second.isManager) {
+ if (it->second.count < MAXIMUM_COUNT) {
+ it->second.count++;
+ return;
+ }
+ else {
+ it->second.count = 0;
+ if (m_routableUserDiscoveryPrefix < data->getName()) {
+ // when two managers exist, the one with "smaller" name take the control
+ sendUpdate(chatroomName);
+ return;
+ }
+ else {
+ if (it->second.helloTimeoutEventId != nullptr) {
+ m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
+ }
+ it->second.helloTimeoutEventId = nullptr;
+ it->second.isManager = false;
+ }
+
+ }
+ }
+
+ else if (it->second.isParticipant) {
+ if (it->second.localChatroomTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.localChatroomTimeoutEventId);
+
+ // If a user start a random timer it means that he think his own chatroom is not alive
+ // But when he receive some packet, it means that this chatroom is alive, so he can
+ // cancel the timer
+ if (it->second.managerSelectionTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.managerSelectionTimeoutEventId);
+ it->second.managerSelectionTimeoutEventId = nullptr;
+
+ it->second.localChatroomTimeoutEventId =
+ m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
+ bind(&ChatroomDiscoveryBackend::localSessionTimeout,
+ this, chatroomName));
+ }
+ else {
+ if (!data->getContent().empty()) {
+ ChatroomInfo chatroom;
+ chatroom.wireDecode(data->getContent().blockFromValue());
+ it->second.info = chatroom;
+ }
+
+ if (it->second.remoteChatroomTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.remoteChatroomTimeoutEventId);
+
+ it->second.remoteChatroomTimeoutEventId =
+ m_scheduler->scheduleEvent(HELLO_INTERVAL * 5,
+ bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
+ this, chatroomName));
+ }
+ // if this is a chatroom that haven't been print on the discovery panel, print it.
+ if(!it->second.isPrint) {
+ sendChatroomList();
+ it->second.isPrint = true;
+ }
+}
+
+void
+ChatroomDiscoveryBackend::localSessionTimeout(const Name::Component& chatroomName)
+{
+ auto it = m_chatroomList.find(chatroomName);
+ if (it == m_chatroomList.end() || it->second.isParticipant == false)
+ return;
+ it->second.managerSelectionTimeoutEventId =
+ m_scheduler->scheduleEvent(time::milliseconds(m_rangeUniformRandom()),
+ bind(&ChatroomDiscoveryBackend::randomSessionTimeout,
+ this, chatroomName));
+}
+
+void
+ChatroomDiscoveryBackend::remoteSessionTimeout(const Name::Component& chatroomName)
+{
+ m_chatroomList.erase(chatroomName);
+}
+
+void
+ChatroomDiscoveryBackend::randomSessionTimeout(const Name::Component& chatroomName)
+{
+ Name prefix = m_routableUserDiscoveryPrefix;
+ prefix.append(chatroomName);
+ m_sock->addSyncNode(prefix);
+
+ emit chatroomInfoRequest(chatroomName.toUri(), true);
+}
+
+void
+ChatroomDiscoveryBackend::sendUpdate(const Name::Component& chatroomName)
+{
+ auto it = m_chatroomList.find(chatroomName);
+ if (it != m_chatroomList.end() && it->second.isManager) {
+ ndn::Block buf = it->second.info.wireEncode();
+
+ if (it->second.helloTimeoutEventId != nullptr) {
+ m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
+ }
+
+ m_sock->publishData(buf.wire(), buf.size(), FRESHNESS_PERIOD, it->second.chatroomPrefix);
+
+ it->second.helloTimeoutEventId =
+ m_scheduler->scheduleEvent(HELLO_INTERVAL,
+ bind(&ChatroomDiscoveryBackend::sendUpdate, this, chatroomName));
+ // if this is a chatroom that haven't been print on the discovery panel, print it.
+ if(!it->second.isPrint) {
+ sendChatroomList();
+ it->second.isPrint = true;
+ }
+ }
+}
+
+void
+ChatroomDiscoveryBackend::updatePrefixes()
+{
+ Name temp;
+ if (m_routingPrefix.isPrefixOf(m_userDiscoveryPrefix))
+ temp = m_userDiscoveryPrefix;
+ else
+ temp.append(m_routingPrefix)
+ .append(ROUTING_HINT_SEPARATOR, 2)
+ .append(m_userDiscoveryPrefix);
+
+ Name routableIdentity = m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET);
+ for (auto& chatroom : m_chatroomList) {
+ if (chatroom.second.isParticipant) {
+ chatroom.second.info.removeParticipant(routableIdentity);
+ chatroom.second.info.addParticipant(temp.getPrefix(IDENTITY_OFFSET));
+ }
+ }
+ m_routableUserDiscoveryPrefix = temp;
+}
+
+void
+ChatroomDiscoveryBackend::updateRoutingPrefix(const QString& routingPrefix)
+{
+ Name newRoutingPrefix(routingPrefix.toStdString());
+ if (!newRoutingPrefix.empty() && newRoutingPrefix != m_routingPrefix) {
+ // Update localPrefix
+ m_routingPrefix = newRoutingPrefix;
+
+ updatePrefixes();
+
+ m_mutex.lock();
+ m_shouldResume = true;
+ m_mutex.unlock();
+
+ close();
+
+ m_face->getIoService().stop();
+ }
+}
+
+void
+ChatroomDiscoveryBackend::onEraseInRoster(ndn::Name sessionPrefix,
+ ndn::Name::Component chatroomName)
+{
+ auto it = m_chatroomList.find(chatroomName);
+ if (it != m_chatroomList.end()) {
+ it->second.info.removeParticipant(sessionPrefix);
+ if (it->second.info.getParticipants().size() == 0) {
+ // Before deleting the chatroom, cancel the hello event timer if exist
+ if (it->second.helloTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
+
+ m_chatroomList.erase(chatroomName);
+ Name prefix = sessionPrefix;
+ prefix.append("CHRONOCHAT-DISCOVERYDATA").append(chatroomName);
+ m_sock->removeSyncNode(prefix);
+ sendChatroomList();
+ return;
+ }
+
+ if (sessionPrefix.isPrefixOf(m_routableUserDiscoveryPrefix)) {
+ it->second.isParticipant = false;
+ it->second.isManager = false;
+ it->second.isPrint = false;
+ it->second.count = 0;
+ if (it->second.helloTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.helloTimeoutEventId);
+ it->second.helloTimeoutEventId = nullptr;
+
+ if (it->second.localChatroomTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.localChatroomTimeoutEventId);
+ it->second.localChatroomTimeoutEventId = nullptr;
+
+ it->second.remoteChatroomTimeoutEventId =
+ m_scheduler->scheduleEvent(HELLO_INTERVAL * 5,
+ bind(&ChatroomDiscoveryBackend::remoteSessionTimeout,
+ this, chatroomName));
+ }
+
+ if (it->second.isManager) {
+ sendUpdate(chatroomName);
+ }
+ }
+}
+
+void
+ChatroomDiscoveryBackend::onAddInRoster(ndn::Name sessionPrefix,
+ ndn::Name::Component chatroomName)
+{
+ auto it = m_chatroomList.find(chatroomName);
+ if (it != m_chatroomList.end()) {
+ it->second.info.addParticipant(sessionPrefix);
+ if (it->second.isManager)
+ sendUpdate(chatroomName);
+ }
+ else {
+ m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
+ m_chatroomList[chatroomName].info.setName(chatroomName);
+ m_chatroomList[chatroomName].info.addParticipant(sessionPrefix);
+ }
+}
+
+void
+ChatroomDiscoveryBackend::onNewChatroomForDiscovery(Name::Component chatroomName)
+{
+ Name newPrefix = m_routableUserDiscoveryPrefix;
+ newPrefix.append(chatroomName);
+ auto it = m_chatroomList.find(chatroomName);
+ if (it == m_chatroomList.end()) {
+ m_chatroomList[chatroomName].chatroomPrefix = newPrefix;
+ m_chatroomList[chatroomName].isParticipant = true;
+ m_chatroomList[chatroomName].isManager = false;
+ m_chatroomList[chatroomName].count = 0;
+ m_chatroomList[chatroomName].isPrint = false;
+ m_scheduler->scheduleEvent(time::milliseconds(600),
+ bind(&ChatroomDiscoveryBackend::randomSessionTimeout, this,
+ chatroomName));
+ }
+ else {
+ // Entering an existing chatroom
+ it->second.isParticipant = true;
+ it->second.isManager = false;
+ it->second.chatroomPrefix = newPrefix;
+
+ if (it->second.remoteChatroomTimeoutEventId != nullptr)
+ m_scheduler->cancelEvent(it->second.remoteChatroomTimeoutEventId);
+ it->second.isPrint = false;
+ it->second.remoteChatroomTimeoutEventId = nullptr;
+
+ it->second.localChatroomTimeoutEventId =
+ m_scheduler->scheduleEvent(HELLO_INTERVAL * 3,
+ bind(&ChatroomDiscoveryBackend::localSessionTimeout,
+ this, chatroomName));
+ emit chatroomInfoRequest(chatroomName.toUri(), false);
+ }
+}
+
+void
+ChatroomDiscoveryBackend::onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager)
+{
+ if (isManager)
+ chatroomInfo.setManager(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET));
+ Name::Component chatroomName = chatroomInfo.getName();
+ m_chatroomList[chatroomName].chatroomName = chatroomName.toUri();
+ m_chatroomList[chatroomName].isManager = isManager;
+ m_chatroomList[chatroomName].count = 0;
+ m_chatroomList[chatroomName].info = chatroomInfo;
+ sendChatroomList();
+ onAddInRoster(m_routableUserDiscoveryPrefix.getPrefix(IDENTITY_OFFSET), chatroomName);
+}
+
+void
+ChatroomDiscoveryBackend::onIdentityUpdated(const QString& identity)
+{
+ m_chatroomList.clear();
+ m_identity = Name(identity.toStdString());
+ m_userDiscoveryPrefix.clear();
+ m_userDiscoveryPrefix.append(m_identity).append("CHRONOCHAT-DISCOVERYDATA");
+ updatePrefixes();
+
+ m_mutex.lock();
+ m_shouldResume = true;
+ m_mutex.unlock();
+
+ close();
+
+ m_face->getIoService().stop();
+}
+
+void
+ChatroomDiscoveryBackend::sendChatroomList()
+{
+ QStringList chatroomList;
+ for (const auto& chatroom : m_chatroomList) {
+ chatroomList << QString::fromStdString(chatroom.first.toUri());
+ }
+
+ emit chatroomListReady(chatroomList);
+ if (m_refreshPanelId != nullptr) {
+ m_scheduler->cancelEvent(m_refreshPanelId);
+ }
+ m_refreshPanelId = m_scheduler->scheduleEvent(REFRESH_INTERVAL,
+ [this] { sendChatroomList(); });
+}
+
+void
+ChatroomDiscoveryBackend::onWaitForChatroomInfo(const QString& chatroomName)
+{
+ auto chatroom = m_chatroomList.find(Name::Component(chatroomName.toStdString()));
+ if (chatroom != m_chatroomList.end())
+ emit chatroomInfoReady(chatroom->second.info);
+}
+
+void
+ChatroomDiscoveryBackend::shutdown()
+{
+ m_mutex.lock();
+ m_shouldResume = false;
+ m_mutex.unlock();
+
+ close();
+
+ m_face->getIoService().stop();
+}
+
+
+} // namespace chronochat
+
+#if WAF
+#include "chatroom-discovery-backend.moc"
+#endif
diff --git a/src/chatroom-discovery-backend.hpp b/src/chatroom-discovery-backend.hpp
new file mode 100644
index 0000000..fe5e211
--- /dev/null
+++ b/src/chatroom-discovery-backend.hpp
@@ -0,0 +1,233 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
+ * Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOCHAT_CHATROOM_DISCOVERY_BACKEND_HPP
+#define CHRONOCHAT_CHATROOM_DISCOVERY_BACKEND_HPP
+
+#include <ndn-cxx/util/scheduler.hpp>
+#include <boost/random.hpp>
+#include <QThread>
+#include <QMutex>
+
+#ifndef Q_MOC_RUN
+#include "common.hpp"
+#include "chatroom-info.hpp"
+#include <socket.hpp>
+#endif
+
+namespace chronochat {
+
+class ChatroomInfoBackend {
+public:
+ std::string chatroomName;
+ Name chatroomPrefix;
+ ChatroomInfo info;
+ // For a chatroom's user to check whether his own chatroom is alive
+ ndn::EventId localChatroomTimeoutEventId;
+ // If the manager no longer exist, set a random timer to compete for manager
+ ndn::EventId managerSelectionTimeoutEventId;
+ // For a user to check the status of the chatroom that he is not in.
+ ndn::EventId remoteChatroomTimeoutEventId;
+ // If the user is manager, he will need the helloEventId to keep track of hello message
+ ndn::EventId helloTimeoutEventId;
+ // To tell whether the user is in this chatroom
+ bool isParticipant;
+ // To tell whether the user is the manager
+ bool isManager;
+ // Variable to tell whether another manager exists
+ int count;
+ // Variable to tell whether to print on the panel
+ bool isPrint;
+
+};
+
+class ChatroomDiscoveryBackend : public QThread
+{
+ Q_OBJECT
+
+public:
+ ChatroomDiscoveryBackend(const Name& routingPrefix,
+ const Name& identity,
+ QObject* parent = 0);
+
+ ~ChatroomDiscoveryBackend();
+
+
+protected:
+ void
+ run();
+
+private:
+ void
+ initializeSync();
+
+ void
+ close();
+
+ void
+ processSyncUpdate(const std::vector<chronosync::MissingDataInfo>& updates);
+
+ void
+ processChatroomData(const ndn::shared_ptr<const ndn::Data>& data);
+
+ void
+ localSessionTimeout(const Name::Component& chatroomName);
+
+ void
+ remoteSessionTimeout(const Name::Component& chatroomName);
+
+ void
+ randomSessionTimeout(const Name::Component& chatroomName);
+
+ void
+ sendUpdate(const Name::Component& chatroomName);
+
+ void
+ updatePrefixes();
+
+ void
+ sendChatroomList();
+
+signals:
+ /**
+ * @brief request to get chatroom info
+ *
+ * This signal will be sent to controller to get chatroom info
+ *
+ * @param chatroomName specify which chatroom's info to request
+ * @param isManager if the user who send the signal is the manager of the chatroom
+ */
+ void
+ chatroomInfoRequest(std::string chatroomName, bool isManager);
+
+ /**
+ * @brief send chatroom list to front end
+ *
+ * @param chatroomList the list of chatrooms
+ */
+ void
+ chatroomListReady(const QStringList& chatroomList);
+
+ /**
+ * @brief send chatroom info to front end
+ *
+ * @param info the chatroom info request by front end
+ */
+ void
+ chatroomInfoReady(const ChatroomInfo& info);
+
+public slots:
+
+ /**
+ * @brief update routing prefix
+ *
+ * @param routingPrefix the new routing prefix
+ */
+ void
+ updateRoutingPrefix(const QString& routingPrefix);
+
+ /**
+ * @brief erase a participant in a chatroom's roster
+ *
+ * This slot is called by chat dialog will erase a participant from a chatroom.
+ * If the user is the manager of this chatroom, he will publish an update.
+ *
+ * @param sessionPrefix the prefix of the participant to erase
+ * @param chatroomName the name of the chatroom
+ */
+ void
+ onEraseInRoster(ndn::Name sessionPrefix, ndn::Name::Component chatroomName);
+
+ /**
+ * @brief add a participant in a chatroom's roster
+ *
+ * This slot is called by chat dialog and will add a participant from a chatroom.
+ * If the user is the manager of this chatroom, he will publish an update.
+ *
+ * @param sessionPrefix the prefix of the participant to erase
+ * @param chatroomName the name of the chatroom
+ */
+ void
+ onAddInRoster(ndn::Name sessionPrefix, ndn::Name::Component chatroomName);
+
+ /**
+ * @brief is called when user himself join a chatroom
+ *
+ * This slot is called by controller and will modify the chatroom list in discovery
+ *
+ * @param chatroomName the name of chatroom the user join
+ */
+ void
+ onNewChatroomForDiscovery(Name::Component chatroomName);
+
+ /**
+ * @brief get chatroom info from chat dialog
+ *
+ * This slot is called by controller. It get the chatroom info of a chatroom and
+ * if the user is the manager, he will publish an update
+ *
+ * @param chatroomInfo chatroom info
+ * @param isManager whether the user is the manager of the chatroom
+ */
+ void
+ onRespondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager);
+
+ /**
+ * @brief reset when the identity updates
+ *
+ * This slot is called when the identity is updated, it will update the identity and
+ * reset the socket
+ *
+ * @param identity new identity
+ */
+ void
+ onIdentityUpdated(const QString& identity);
+
+ /**
+ * @brief prepare chatroom info for discovery panel
+ *
+ * This slot is called by discovery panel when it wants to display the info of a chatroom
+ *
+ * @param chatroomName the name of chatroom the discovery panel requested
+ */
+ void
+ onWaitForChatroomInfo(const QString& chatroomName);
+
+ void
+ shutdown();
+
+private:
+
+ typedef std::map<ndn::Name::Component, ChatroomInfoBackend> ChatroomList;
+
+ Name m_discoveryPrefix;
+ Name m_routableUserDiscoveryPrefix;
+ Name m_routingPrefix;
+ Name m_userDiscoveryPrefix;
+ Name m_identity;
+
+ boost::mt19937 m_randomGenerator;
+ boost::variate_generator<boost::mt19937&, boost::uniform_int<> > m_rangeUniformRandom;
+
+ shared_ptr<ndn::Face> m_face;
+
+ unique_ptr<ndn::Scheduler> m_scheduler; // scheduler
+ ndn::EventId m_refreshPanelId;
+ shared_ptr<chronosync::Socket> m_sock; // SyncSocket
+
+ ChatroomList m_chatroomList;
+ QMutex m_mutex;
+
+ bool m_shouldResume;
+};
+
+} // namespace chronochat
+
+#endif // CHRONOCHAT_CHATROOM_DISCOVERY_BACKEND_HPP
diff --git a/src/chatroom-info.cpp b/src/chatroom-info.cpp
index a56856c..bd705da 100644
--- a/src/chatroom-info.cpp
+++ b/src/chatroom-info.cpp
@@ -137,11 +137,9 @@
// Chatroom Info
Block::element_const_iterator i = m_wire.elements_begin();
-
if (i == m_wire.elements_end() || i->type() != tlv::ChatroomName)
throw Error("Missing Chatroom Name Info");
m_chatroomName.wireDecode(i->blockFromValue());
-
++i;
// Trust Model
@@ -149,14 +147,12 @@
throw Error("Missing TrustModel");
m_trustModel =
static_cast<TrustModel>(readNonNegativeInteger(*i));
-
++i;
// Chatroom Sync Prefix
if (i == m_wire.elements_end() || i->type() != tlv::ChatroomPrefix)
throw Error("Missing Chatroom Prefix");
m_syncPrefix.wireDecode(i->blockFromValue());
-
++i;
// Manager Prefix
@@ -209,7 +205,10 @@
ChatroomInfo::addParticipant(const Name& participant)
{
m_wire.reset();
- m_participants.push_back(participant);
+ if (find(m_participants.begin(), m_participants.end(), participant) ==
+ m_participants.end()) {
+ m_participants.push_back(participant);
+ }
}
void
diff --git a/src/controller.cpp b/src/controller.cpp
index f716ada..dce0b03 100644
--- a/src/controller.cpp
+++ b/src/controller.cpp
@@ -13,7 +13,6 @@
#include <QDir>
#include <QTimer>
#include "controller.hpp"
-//#include "chatroom-discovery.h"
#ifndef Q_MOC_RUN
#include <boost/filesystem.hpp>
@@ -34,6 +33,8 @@
Q_DECLARE_METATYPE(size_t)
Q_DECLARE_METATYPE(chronochat::ChatroomInfo)
Q_DECLARE_METATYPE(chronochat::Invitation)
+Q_DECLARE_METATYPE(std::string)
+Q_DECLARE_METATYPE(ndn::Name::Component)
namespace chronochat {
@@ -43,21 +44,25 @@
Controller::Controller(QWidget* parent)
: QDialog(parent)
, m_localPrefixDetected(false)
- , 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)
+ , m_settingDialog(new SettingDialog(this))
+ , m_startChatDialog(new StartChatDialog(this))
+ , m_profileEditor(new ProfileEditor(this))
+ , m_invitationDialog(new InvitationDialog(this))
+ , m_contactPanel(new ContactPanel(this))
+ , m_browseContactDialog(new BrowseContactDialog(this))
+ , m_addContactPanel(new AddContactPanel(this))
+ , m_discoveryPanel(new DiscoveryPanel(this))
{
qRegisterMetaType<ndn::Name>("ndn.Name");
qRegisterMetaType<ndn::IdentityCertificate>("ndn.IdentityCertificate");
qRegisterMetaType<chronochat::EndorseInfo>("chronochat.EndorseInfo");
qRegisterMetaType<ndn::Interest>("ndn.Interest");
qRegisterMetaType<size_t>("size_t");
- qRegisterMetaType<chronochat::ChatroomInfo>("chronos.Chatroom");
- qRegisterMetaType<chronochat::Invitation>("chronos.Invitation");
+ qRegisterMetaType<chronochat::ChatroomInfo>("chronochat.Chatroom");
+ qRegisterMetaType<chronochat::Invitation>("chronochat.Invitation");
+ qRegisterMetaType<std::string>("std.string");
+ qRegisterMetaType<ndn::Name::Component>("ndn.Component");
+
// Connection to ContactManager
connect(m_backend.getContactManager(), SIGNAL(warning(const QString&)),
@@ -103,7 +108,6 @@
m_addContactPanel,
SLOT(onContactEndorseInfoReady(const chronochat::EndorseInfo&)));
-
// Connection to BrowseContactDialog
connect(m_browseContactDialog, SIGNAL(directAddClicked()),
this, SLOT(onDirectAdd()));
@@ -177,6 +181,43 @@
initialize();
+ m_chatroomDiscoveryBackend
+ = new ChatroomDiscoveryBackend(m_localPrefix,
+ m_identity,
+ this);
+
+ // connect to chatroom discovery back end
+ connect(&m_backend, SIGNAL(localPrefixUpdated(const QString&)),
+ m_chatroomDiscoveryBackend, SLOT(updateRoutingPrefix(const QString&)));
+ connect(this, SIGNAL(localPrefixConfigured(const QString&)),
+ m_chatroomDiscoveryBackend, SLOT(updateRoutingPrefix(const QString&)));
+ connect(this, SIGNAL(newChatroomForDiscovery(Name::Component)),
+ m_chatroomDiscoveryBackend, SLOT(onNewChatroomForDiscovery(Name::Component)));
+ connect(m_chatroomDiscoveryBackend, SIGNAL(chatroomInfoRequest(std::string, bool)),
+ this, SLOT(onChatroomInfoRequest(std::string, bool)));
+ connect(this, SIGNAL(respondChatroomInfoRequest(ChatroomInfo, bool)),
+ m_chatroomDiscoveryBackend, SLOT(onRespondChatroomInfoRequest(ChatroomInfo, bool)));
+ connect(this, SIGNAL(shutdownDiscoveryBackend()),
+ m_chatroomDiscoveryBackend, SLOT(shutdown()));
+ connect(this, SIGNAL(identityUpdated(const QString&)),
+ m_chatroomDiscoveryBackend, SLOT(onIdentityUpdated(const QString&)));
+
+ // connect chatroom discovery back end with front end
+ connect(m_discoveryPanel, SIGNAL(waitForChatroomInfo(const QString&)),
+ m_chatroomDiscoveryBackend, SLOT(onWaitForChatroomInfo(const QString&)));
+ connect(m_discoveryPanel, SIGNAL(warning(const QString&)),
+ this, SLOT(onWarning(const QString&)));
+ connect(this, SIGNAL(identityUpdated(const QString&)),
+ m_discoveryPanel, SLOT(onIdentityUpdated(const QString&)));
+ connect(m_chatroomDiscoveryBackend, SIGNAL(chatroomListReady(const QStringList&)),
+ m_discoveryPanel, SLOT(onChatroomListReady(const QStringList&)));
+ connect(m_chatroomDiscoveryBackend, SIGNAL(chatroomInfoReady(const ChatroomInfo&)),
+ m_discoveryPanel, SLOT(onChatroomInfoReady(const ChatroomInfo&)));
+ connect(m_discoveryPanel, SIGNAL(startChatroom(const QString&, bool)),
+ this, SLOT(onStartChatroom(const QString&, bool)));
+
+ m_chatroomDiscoveryBackend->start();
+
createTrayIcon();
emit updateLocalPrefix();
@@ -288,9 +329,6 @@
m_startChatroom = new QAction(tr("Start new chat"), this);
connect(m_startChatroom, SIGNAL(triggered()), this, SLOT(onStartChatAction()));
- m_discoveryAction = new QAction(tr("Ongoing Chatrooms"), this);
- connect(m_discoveryAction, SIGNAL(triggered()), this, SLOT(onDiscoveryAction()));
-
m_settingsAction = new QAction(tr("Settings"), this);
connect(m_settingsAction, SIGNAL(triggered()), this, SLOT(onSettingsAction()));
@@ -303,6 +341,9 @@
m_addContactAction = new QAction(tr("Add contact"), this);
connect(m_addContactAction, SIGNAL(triggered()), this, SLOT(onAddContactAction()));
+ m_chatroomDiscoveryAction = new QAction(tr("Chatroom Discovery"), this);
+ connect(m_chatroomDiscoveryAction, SIGNAL(triggered()), this, SLOT(onChatroomDiscoveryAction()));
+
m_updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
connect(m_updateLocalPrefixAction, SIGNAL(triggered()),
&m_backend, SLOT(onUpdateLocalPrefixAction()));
@@ -322,7 +363,7 @@
m_trayIconMenu = new QMenu(this);
m_trayIconMenu->addAction(m_startChatroom);
- // m_trayIconMenu->addAction(m_discoveryAction); // disable discovery temporarily
+ m_trayIconMenu->addAction(m_chatroomDiscoveryAction);
m_trayIconMenu->addSeparator();
m_trayIconMenu->addAction(m_settingsAction);
@@ -354,7 +395,7 @@
QMenu* closeMenu = 0;
menu->addAction(m_startChatroom);
- // menu->addAction(m_discoveryAction);
+ menu->addAction(m_chatroomDiscoveryAction);
menu->addSeparator();
menu->addAction(m_settingsAction);
@@ -428,13 +469,18 @@
this, SLOT(onShowChatMessage(const QString&, const QString&, const QString&)));
connect(chatDialog, SIGNAL(resetIcon()),
this, SLOT(onResetIcon()));
- connect(chatDialog, SIGNAL(rosterChanged(const chronochat::ChatroomInfo&)),
- this, SLOT(onRosterChanged(const chronochat::ChatroomInfo&)));
connect(&m_backend, SIGNAL(localPrefixUpdated(const QString&)),
chatDialog->getBackend(), SLOT(updateRoutingPrefix(const QString&)));
connect(this, SIGNAL(localPrefixConfigured(const QString&)),
chatDialog->getBackend(), SLOT(updateRoutingPrefix(const QString&)));
+ // connect chat dialog with discovery backend
+ connect(chatDialog->getBackend(), SIGNAL(addInRoster(ndn::Name, ndn::Name::Component)),
+ m_chatroomDiscoveryBackend, SLOT(onAddInRoster(ndn::Name, ndn::Name::Component)));
+ connect(chatDialog->getBackend(), SIGNAL(eraseInRoster(ndn::Name, ndn::Name::Component)),
+ m_chatroomDiscoveryBackend, SLOT(onEraseInRoster(ndn::Name, ndn::Name::Component)));
+
+
QAction* chatAction = new QAction(chatroomName, this);
m_chatActionList[chatroomName.toStdString()] = chatAction;
connect(chatAction, SIGNAL(triggered()),
@@ -446,6 +492,7 @@
chatDialog, SLOT(shutdown()));
updateMenu();
+ emit newChatroomForDiscovery(Name::Component(chatroomName.toStdString()));
}
void
@@ -522,12 +569,6 @@
}
void
-Controller::onDiscoveryAction()
-{
-}
-
-
-void
Controller::onSettingsAction()
{
m_settingDialog->setNick(QString(m_nick.c_str()));
@@ -558,6 +599,13 @@
}
void
+Controller::onChatroomDiscoveryAction()
+{
+ m_discoveryPanel->show();
+ m_discoveryPanel->raise();
+}
+
+void
Controller::onDirectAdd()
{
m_addContactPanel->show();
@@ -572,6 +620,7 @@
m_profileEditor->hide();
m_invitationDialog->hide();
m_addContactPanel->hide();
+ m_discoveryPanel->hide();
ChatDialogList::iterator it = m_chatDialogList.begin();
ChatDialogList::iterator end = m_chatDialogList.end();
@@ -593,6 +642,12 @@
delete m_invitationDialog;
delete m_browseContactDialog;
delete m_addContactPanel;
+ delete m_discoveryPanel;
+ if (m_chatroomDiscoveryBackend->isRunning()) {
+ emit shutdownDiscoveryBackend();
+ m_chatroomDiscoveryBackend->wait();
+ }
+ delete m_chatroomDiscoveryBackend;
if (m_backend.isRunning()) {
emit shutdownBackend();
@@ -609,6 +664,7 @@
chatroomPrefix.append("ndn")
.append("broadcast")
.append("ChronoChat")
+ .append("Chatroom")
.append(chatroomName.toStdString());
// check if the chatroom exists
@@ -623,7 +679,7 @@
//(which should be created in the first half of this method
//, but let's use the default one for now.
Name chatPrefix;
- chatPrefix.append(m_identity).append("CHRONOCHAT-DATA").append(chatroomName.toStdString());
+ chatPrefix.append(m_identity).append("CHRONOCHAT-CHATDATA").append(chatroomName.toStdString());
ChatDialog* chatDialog
= new ChatDialog(chatroomPrefix,
@@ -632,7 +688,8 @@
chatroomName.toStdString(),
m_nick,
true,
- m_identity);
+ m_identity,
+ this);
addChatDialog(chatroomName, chatDialog);
chatDialog->show();
@@ -672,11 +729,11 @@
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()];
@@ -707,9 +764,11 @@
}
void
-Controller::onRosterChanged(const chronochat::ChatroomInfo& info)
+Controller::onChatroomInfoRequest(std::string chatroomName, bool isManager)
{
-
+ auto it = m_chatDialogList.find(chatroomName);
+ if (it != m_chatDialogList.end())
+ emit respondChatroomInfoRequest(it->second->getChatroomInfo(), isManager);
}
} // namespace chronochat
diff --git a/src/controller.hpp b/src/controller.hpp
index 1c6ef43..2b80932 100644
--- a/src/controller.hpp
+++ b/src/controller.hpp
@@ -24,6 +24,8 @@
#include "browse-contact-dialog.hpp"
#include "add-contact-panel.hpp"
#include "chat-dialog.hpp"
+#include "chatroom-discovery-backend.hpp"
+#include "discovery-panel.hpp"
#ifndef Q_MOC_RUN
#include "common.hpp"
@@ -82,6 +84,9 @@
shutdownBackend();
void
+ shutdownDiscoveryBackend();
+
+ void
updateLocalPrefix();
void
@@ -112,6 +117,12 @@
void
removeChatroom(QString chatroomName);
+ void
+ newChatroomForDiscovery(Name::Component chatroomName);
+
+ void
+ respondChatroomInfoRequest(ChatroomInfo chatroomInfo, bool isManager);
+
private slots:
void
onIdentityUpdated(const QString& identity);
@@ -132,9 +143,6 @@
onStartChatAction();
void
- onDiscoveryAction();
-
- void
onSettingsAction();
void
@@ -156,6 +164,9 @@
onQuitAction();
void
+ onChatroomDiscoveryAction();
+
+ void
onStartChatroom(const QString& chatroom, bool secured);
void
@@ -177,7 +188,7 @@
onError(const QString& msg);
void
- onRosterChanged(const chronochat::ChatroomInfo& info);
+ onChatroomInfoRequest(std::string chatroomName, bool isManager);
private: // private member
typedef std::map<std::string, QAction*> ChatActionList;
@@ -189,7 +200,7 @@
// Tray
QAction* m_startChatroom;
- QAction* m_discoveryAction;
+ //QAction* m_discoveryAction;
QAction* m_minimizeAction;
QAction* m_settingsAction;
QAction* m_editProfileAction;
@@ -197,6 +208,7 @@
QAction* m_addContactAction;
QAction* m_updateLocalPrefixAction;
QAction* m_quitAction;
+ QAction* m_chatroomDiscoveryAction;
QMenu* m_trayIconMenu;
QMenu* m_closeMenu;
QSystemTrayIcon* m_trayIcon;
@@ -204,14 +216,16 @@
ChatActionList m_closeActionList;
// Dialogs
- SettingDialog* m_settingDialog;
- StartChatDialog* m_startChatDialog;
- ProfileEditor* m_profileEditor;
- InvitationDialog* m_invitationDialog;
- ContactPanel* m_contactPanel;
- BrowseContactDialog* m_browseContactDialog;
- AddContactPanel* m_addContactPanel;
- ChatDialogList m_chatDialogList;
+ SettingDialog* m_settingDialog;
+ StartChatDialog* m_startChatDialog;
+ ProfileEditor* m_profileEditor;
+ InvitationDialog* m_invitationDialog;
+ ContactPanel* m_contactPanel;
+ BrowseContactDialog* m_browseContactDialog;
+ AddContactPanel* m_addContactPanel;
+ ChatDialogList m_chatDialogList;
+ DiscoveryPanel* m_discoveryPanel;
+ ChatroomDiscoveryBackend* m_chatroomDiscoveryBackend;
// Conf
Name m_identity;
diff --git a/src/digest-tree-scene.cpp b/src/digest-tree-scene.cpp
index a68c63f..a38658d 100644
--- a/src/digest-tree-scene.cpp
+++ b/src/digest-tree-scene.cpp
@@ -154,7 +154,7 @@
it.next();
DisplayUserPtr p = it.value();
if (p != DisplayUserNullPtr) {
- prefixList << "- " + p->getPrefix();
+ prefixList << p->getPrefix();
}
}
return prefixList;
diff --git a/src/discovery-panel.cpp b/src/discovery-panel.cpp
new file mode 100644
index 0000000..cbc98e0
--- /dev/null
+++ b/src/discovery-panel.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
+ * Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#include "discovery-panel.hpp"
+#include "ui_discovery-panel.h"
+
+#include <QItemSelectionModel>
+#include <QModelIndex>
+
+#ifndef Q_MOC_RUN
+#endif
+
+
+namespace chronochat {
+
+static const time::seconds REFRESH_INTERVAL(60);
+static const uint8_t ROUTING_HINT_SEPARATOR[2] = {0xF0, 0x2E};
+
+DiscoveryPanel::DiscoveryPanel(QWidget *parent)
+ : QDialog(parent)
+ , ui(new Ui::DiscoveryPanel)
+ , m_chatroomListModel(new QStringListModel)
+ , m_rosterListModel(new QStringListModel)
+{
+ ui->setupUi(this);
+ ui->ChatroomList->setModel(m_chatroomListModel);
+ ui->RosterList->setModel(m_rosterListModel);
+
+ connect(ui->ChatroomList->selectionModel(),
+ SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ this,
+ SLOT(onSelectedChatroomChanged(const QItemSelection &, const QItemSelection &)));
+ connect(ui->RosterList->selectionModel(),
+ SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
+ this,
+ SLOT(onSelectedParticipantChanged(const QItemSelection &, const QItemSelection &)));
+ connect(ui->join, SIGNAL(clicked()),
+ this, SLOT(onJoinClicked()));
+}
+
+DiscoveryPanel::~DiscoveryPanel()
+{
+ if (m_chatroomListModel)
+ delete m_chatroomListModel;
+ if (m_rosterListModel)
+ delete m_rosterListModel;
+ delete ui;
+}
+
+//private methods
+void
+DiscoveryPanel::resetPanel()
+{
+ // Clean up General tag.
+ ui->NameData->clear();
+ ui->NameSpaceData->clear();
+ ui->TrustModelData->clear();
+
+ // Clean up Roster tag.
+ m_rosterList.clear();
+ m_participant.clear();
+ m_rosterListModel->setStringList(m_rosterList);
+
+ // Clean up chatroom list.
+ m_chatroomList.clear();
+ m_chatroom.clear();
+ m_chatroomListModel->setStringList(m_chatroomList);
+
+}
+
+// public slots
+void
+DiscoveryPanel::onIdentityUpdated(const QString& identity)
+{
+ resetPanel();
+}
+
+void
+DiscoveryPanel::onChatroomListReady(const QStringList& list)
+{
+ m_chatroomList = list;
+ m_chatroomListModel->setStringList(m_chatroomList);
+}
+
+void
+DiscoveryPanel::onChatroomInfoReady(const ChatroomInfo& info)
+{
+ ui->NameData->setText(QString::fromStdString(info.getName().toUri()));
+ ui->NameSpaceData->setText(QString::fromStdString(info.getSyncPrefix().toUri()));
+
+ switch(info.getTrustModel()) {
+ case 2:
+ {
+ ui->TrustModelData->setText(QString("Hierarchical"));
+ ui->join->setEnabled(false);
+ ui->requestInvitation->setEnabled(true);
+ break;
+ }
+ case 1:
+ {
+ ui->TrustModelData->setText(QString("Web Of Trust"));
+ ui->join->setEnabled(false);
+ ui->requestInvitation->setEnabled(true);
+ break;
+ }
+ case 0:
+ {
+ ui->TrustModelData->setText(QString("None"));
+ ui->join->setEnabled(true);
+ ui->requestInvitation->setEnabled(false);
+ break;
+ }
+ default:
+ {
+ ui->TrustModelData->setText(QString("Unrecognized"));
+ ui->join->setEnabled(false);
+ ui->requestInvitation->setEnabled(false);
+ }
+ }
+
+ std::list<Name>roster = info.getParticipants();
+ m_rosterList.clear();
+ Name::Component routingHint = Name::Component(ROUTING_HINT_SEPARATOR, 2);
+ for (const auto& participant : roster) {
+ size_t i;
+ for (i = 0; i < participant.size(); ++i) {
+ if (routingHint == participant.at(i))
+ break;
+ }
+ if (i == participant.size())
+ m_rosterList << QString::fromStdString(participant.toUri());
+ else
+ m_rosterList << QString::fromStdString(participant.getSubName(i + 1).toUri());
+ }
+ m_rosterListModel->setStringList(m_rosterList);
+ ui->RosterList->setModel(m_rosterListModel);
+}
+
+// private slots
+void
+DiscoveryPanel::onSelectedChatroomChanged(const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+ QModelIndexList items = selected.indexes();
+ QString chatroomName = m_chatroomListModel->data(items.first(), Qt::DisplayRole).toString();
+
+ bool chatroomFound = false;
+ for (int i = 0; i < m_chatroomList.size(); i++) {
+ if (chatroomName == m_chatroomList[i]) {
+ chatroomFound = true;
+ m_chatroom = m_chatroomList[i];
+ m_participant.clear();
+ break;
+ }
+ }
+
+ if (!chatroomFound) {
+ emit warning("This should not happen: DiscoveryPanel::onSelectedChatroomChanged");
+ return;
+ }
+
+ emit waitForChatroomInfo(m_chatroom);
+}
+
+void
+DiscoveryPanel::onSelectedParticipantChanged(const QItemSelection &selected,
+ const QItemSelection &deselected)
+{
+ QModelIndexList items = selected.indexes();
+ QString participant = m_rosterListModel->data(items.first(), Qt::DisplayRole).toString();
+
+ bool participantFound = false;
+ for (int i = 0; i < m_rosterList.size(); i++) {
+ if (participant == m_rosterList[i]) {
+ participantFound = true;
+ m_participant = m_rosterList[i];
+ break;
+ }
+ }
+ if (!participantFound) {
+ emit warning("This should not happen: DiscoveryPanel::onSelectedParticipantChangeds #1");
+ return;
+ }
+}
+
+void
+DiscoveryPanel::onJoinClicked()
+{
+ emit startChatroom(m_chatroom, false);
+}
+
+} // namespace chronochat
+
+#if WAF
+#include "discovery-panel.moc"
+// #include "discovery-panel.cpp.moc"
+#endif
diff --git a/src/discovery-panel.hpp b/src/discovery-panel.hpp
new file mode 100644
index 0000000..1776d37
--- /dev/null
+++ b/src/discovery-panel.hpp
@@ -0,0 +1,127 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Qiuhan Ding <qiuhanding@cs.ucla.edu>
+ * Yingdi Yu <yingdi@cs.ucla.edu>
+ */
+
+#ifndef CHRONOCHAT_DISCOVERY_PANEL_HPP
+#define CHRONOCHAT_DISCOVERY_PANEL_HPP
+
+#include <QDialog>
+#include <QStringListModel>
+
+#ifndef Q_MOC_RUN
+#include "chatroom-info.hpp"
+#endif
+
+namespace Ui {
+class DiscoveryPanel;
+}
+
+namespace chronochat {
+
+class DiscoveryPanel : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit
+ DiscoveryPanel(QWidget* parent = 0);
+
+ virtual
+ ~DiscoveryPanel();
+
+private:
+ void
+ resetPanel();
+
+ void
+ refreshPanel();
+
+signals:
+ /**
+ * @brief get chatroom info from discovery backend
+ *
+ * @param chatroomName the name of chatroom we want to get info from
+ */
+ void
+ waitForChatroomInfo(const QString& chatroomName);
+
+ /**
+ * @brief send warning if strange things happen
+ *
+ * @param msg the message that print in the warning
+ */
+ void
+ warning(const QString& msg);
+
+ /**
+ * @brief join the chatroom the user clicked
+ *
+ * This function will be called if the join button is clicked. The join button is enabled
+ * when there is no trust model for the chatroom.
+ * The user will join the chatroom he choose directly.
+ *
+ * @param chatroomName the chatroom to join
+ * @param secured if security is enabled in this chatroom
+ */
+ void
+ startChatroom(const QString& chatroomName, bool secured);
+
+public slots:
+ /**
+ * @brief reset the panel when identity is updated
+ *
+ */
+ void
+ onIdentityUpdated(const QString& identity);
+
+ /**
+ * @brief print the chatroom list on the panel
+ *
+ * @param list list of chatroom name get from discovery backend
+ */
+ void
+ onChatroomListReady(const QStringList& list);
+
+ /**
+ * @brief print the chatroom info on the panel
+ *
+ * @param info chatroom info get from discovery backend
+ */
+ void
+ onChatroomInfoReady(const ChatroomInfo& info);
+
+private slots:
+ void
+ onSelectedChatroomChanged(const QItemSelection& selected,
+ const QItemSelection& deselected);
+
+ void
+ onSelectedParticipantChanged(const QItemSelection& selected,
+ const QItemSelection& deselected);
+
+ void
+ onJoinClicked();
+
+private:
+ Ui::DiscoveryPanel* ui;
+
+ // Models.
+ QStringListModel* m_chatroomListModel;
+ QStringListModel* m_rosterListModel;
+
+ // Internal data structure.
+ QStringList m_chatroomList;
+ QStringList m_rosterList;
+ QString m_chatroom;
+ QString m_participant;
+};
+
+} // namespace chronochat
+
+#endif // CHRONOCHAT_DISCOVERY_PANEL_HPP
diff --git a/src/discovery-panel.ui b/src/discovery-panel.ui
new file mode 100644
index 0000000..def8138
--- /dev/null
+++ b/src/discovery-panel.ui
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DiscoveryPanel</class>
+ <widget class="QDialog" name="DiscoveryPanel">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>480</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>ChronoChat Discovery</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <item>
+ <layout class="QHBoxLayout" name="DiscoveryPanelLayout" stretch="3,0">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QListView" name="ChatroomList">
+ <property name="maximumSize">
+ <size>
+ <width>180</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="contextMenuPolicy">
+ <enum>Qt::PreventContextMenu</enum>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="ChatroomInfo">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="General">
+ <attribute name="title">
+ <string>General</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_7">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_6" stretch="2">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout" stretch="35,100">
+ <property name="spacing">
+ <number>-1</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="NameSpaceLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sync Prefix</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="NameSpaceData">
+ <property name="font">
+ <font>
+ <family>Lucida Grande</family>
+ </font>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="35,100">
+ <item>
+ <widget class="QLabel" name="NameLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="NameData">
+ <property name="font">
+ <font>
+ <family>Lucida Grande</family>
+ </font>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="35,100">
+ <item>
+ <widget class="QLabel" name="TrustModel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Trust Model</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="TrustModelData">
+ <property name="font">
+ <font>
+ <family>Lucida Grande</family>
+ </font>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QListView" name="RosterList">
+ <property name="contextMenuPolicy">
+ <enum>Qt::PreventContextMenu</enum>
+ </property>
+ <property name="acceptDrops">
+ <bool>false</bool>
+ </property>
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QPushButton" name="join">
+ <property name="text">
+ <string>Join</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="requestInvitation">
+ <property name="text">
+ <string>Request Invitation</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>