blob: dad2b6b5ab18729dfad4a7b420f45feee75fc0b8 [file] [log] [blame]
Yingdi Yu04842232013-10-23 14:03:09 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013, Regents of the University of California
4 * Yingdi Yu
5 *
6 * BSD license, See the LICENSE file for more information
7 *
Yingdi Yu42f66462013-10-31 17:38:22 -07008 * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
9 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
10 * Yingdi Yu <yingdi@cs.ucla.edu>
Yingdi Yu04842232013-10-23 14:03:09 -070011 */
12
13#include "chatdialog.h"
14#include "ui_chatdialog.h"
15
Yingdi Yu42f66462013-10-31 17:38:22 -070016#include <QScrollBar>
Yingdi Yu07b5b092013-11-07 17:00:54 -080017#include <QMessageBox>
18#include <QCloseEvent>
Yingdi Yu42f66462013-10-31 17:38:22 -070019
Yingdi Yuc4d08d22013-10-23 23:07:29 -070020#ifndef Q_MOC_RUN
Yingdi Yu42f66462013-10-31 17:38:22 -070021#include <sync-intro-certificate.h>
22#include <boost/random/random_device.hpp>
23#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yueaa84e22014-01-16 10:30:26 -080024#include <ndn-cpp/security/signature-sha256-with-rsa.hpp>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070025#include "logging.h"
26#endif
27
Yingdi Yu04842232013-10-23 14:03:09 -070028using namespace std;
Yingdi Yu04842232013-10-23 14:03:09 -070029
Yingdi Yuc4d08d22013-10-23 23:07:29 -070030INIT_LOGGER("ChatDialog");
31
Yingdi Yu42f66462013-10-31 17:38:22 -070032static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
33
34Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
35Q_DECLARE_METATYPE(size_t)
36
Yingdi Yu64206112013-12-24 11:16:32 +080037ChatDialog::ChatDialog(ndn::ptr_lib::shared_ptr<ContactManager> contactManager,
Yingdi Yu42f66462013-10-31 17:38:22 -070038 const ndn::Name& chatroomPrefix,
39 const ndn::Name& localPrefix,
40 const ndn::Name& defaultIdentity,
Yingdi Yu42372442013-11-06 18:43:31 -080041 const std::string& nick,
42 bool trial,
Yingdi Yu04842232013-10-23 14:03:09 -070043 QWidget *parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070044: QDialog(parent)
45 , ui(new Ui::ChatDialog)
46 , m_contactManager(contactManager)
47 , m_chatroomPrefix(chatroomPrefix)
48 , m_localPrefix(localPrefix)
49 , m_defaultIdentity(defaultIdentity)
Yingdi Yueaa84e22014-01-16 10:30:26 -080050 , m_invitationPolicy(new SecPolicyChronoChatInvitation(m_chatroomPrefix.get(-1).toEscapedString(), m_defaultIdentity))
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -080051 , m_keyChain(new ndn::KeyChain())
Yingdi Yu42372442013-11-06 18:43:31 -080052 , m_nick(nick)
Yingdi Yu42f66462013-10-31 17:38:22 -070053 , m_sock(NULL)
54 , m_lastMsgTime(0)
55 // , m_historyInitialized(false)
56 , m_joined(false)
57 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Yingdi Yu04842232013-10-23 14:03:09 -070058{
Yingdi Yu42f66462013-10-31 17:38:22 -070059 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
60 qRegisterMetaType<size_t>("size_t");
61
Yingdi Yuc4d08d22013-10-23 23:07:29 -070062 ui->setupUi(this);
63
Yingdi Yu2ab22e72013-11-10 01:38:21 -080064 QString randString = getRandomString();
Yingdi Yu42f66462013-10-31 17:38:22 -070065 m_localChatPrefix = m_localPrefix;
Yingdi Yu42372442013-11-06 18:43:31 -080066 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu2ab22e72013-11-10 01:38:21 -080067 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
Yingdi Yu42f66462013-10-31 17:38:22 -070068
69 m_session = time(NULL);
70 m_scene = new DigestTreeScene(this);
71
72 initializeSetting();
73 updateLabels();
74
75 ui->treeViewer->setScene(m_scene);
76 ui->treeViewer->hide();
77 m_scene->plot("Empty");
78 QRectF rect = m_scene->itemsBoundingRect();
79 m_scene->setSceneRect(rect);
80
81 m_rosterModel = new QStringListModel(this);
82 ui->listView->setModel(m_rosterModel);
83
Yingdi Yua0594092013-11-06 22:07:38 -080084 createActions();
85 createTrayIcon();
86
Yingdi Yu42f66462013-10-31 17:38:22 -070087 m_timer = new QTimer(this);
88
Yingdi Yuaccbda92013-12-27 08:44:12 +080089 startFace();
Yingdi Yu7630f642014-01-16 19:13:03 -080090 m_verifier = ndn::ptr_lib::make_shared<ndn::Verifier>(m_invitationPolicy);
91 m_verifier->setFace(m_face);
Yingdi Yuaccbda92013-12-27 08:44:12 +080092
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -080093 ndn::Name certificateName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yueaa84e22014-01-16 10:30:26 -080094 m_syncPolicy = ndn::ptr_lib::make_shared<SecPolicySync>(m_defaultIdentity, certificateName, m_chatroomPrefix, m_face);
Yingdi Yu42f66462013-10-31 17:38:22 -070095
96 connect(ui->inviteButton, SIGNAL(clicked()),
97 this, SLOT(openInviteListDialog()));
98 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
99 this, SLOT(sendInvitationWrapper(QString, bool)));
100 connect(ui->lineEdit, SIGNAL(returnPressed()),
101 this, SLOT(returnPressed()));
102 connect(ui->treeButton, SIGNAL(pressed()),
103 this, SLOT(treeButtonPressed()));
104 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
105 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
106 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
107 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
108 connect(m_timer, SIGNAL(timeout()),
109 this, SLOT(replot()));
110 connect(m_scene, SIGNAL(replot()),
111 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800112 connect(trayIcon, SIGNAL(messageClicked()),
113 this, SLOT(showNormal()));
114 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
115 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700116 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
117 this, SLOT(updateRosterList(QStringList)));
118
Yingdi Yu42f66462013-10-31 17:38:22 -0700119
120 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700121}
122
Yingdi Yu42f66462013-10-31 17:38:22 -0700123
Yingdi Yu04842232013-10-23 14:03:09 -0700124ChatDialog::~ChatDialog()
125{
Yingdi Yu42372442013-11-06 18:43:31 -0800126 if(m_sock != NULL)
127 {
128 sendLeave();
129 delete m_sock;
130 m_sock = NULL;
131 }
Yingdi Yuaccbda92013-12-27 08:44:12 +0800132 shutdownFace();
133}
134
135void
136ChatDialog::startFace()
137{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800138 m_face = ndn::ptr_lib::make_shared<ndn::Face>();
Yingdi Yuaccbda92013-12-27 08:44:12 +0800139
140 connectToDaemon();
141
142 m_running = true;
143 m_thread = boost::thread (&ChatDialog::eventLoop, this);
144}
145
146void
147ChatDialog::shutdownFace()
148{
149 {
150 boost::unique_lock<boost::recursive_mutex> lock(m_mutex);
151 m_running = false;
152 }
153
154 m_thread.join();
Yingdi Yu64206112013-12-24 11:16:32 +0800155 m_face->shutdown();
Yingdi Yu04842232013-10-23 14:03:09 -0700156}
157
158void
Yingdi Yuaccbda92013-12-27 08:44:12 +0800159ChatDialog::eventLoop()
160{
161 while (m_running)
162 {
163 try{
164 m_face->processEvents();
165 usleep(1000);
166 }catch(std::exception& e){
167 _LOG_DEBUG(" " << e.what() );
168 }
169 }
170}
171
172void
Yingdi Yu64206112013-12-24 11:16:32 +0800173ChatDialog::connectToDaemon()
Yingdi Yu04842232013-10-23 14:03:09 -0700174{
Yingdi Yu64206112013-12-24 11:16:32 +0800175 //Hack! transport does not connect to daemon unless an interest is expressed.
176 ndn::Name name("/ndn");
177 ndn::ptr_lib::shared_ptr<ndn::Interest> interest = ndn::ptr_lib::make_shared<ndn::Interest>(name);
178 m_face->expressInterest(*interest,
179 boost::bind(&ChatDialog::onConnectionData, this, _1, _2),
180 boost::bind(&ChatDialog::onConnectionDataTimeout, this, _1));
Yingdi Yu04842232013-10-23 14:03:09 -0700181}
182
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700183void
Yingdi Yu64206112013-12-24 11:16:32 +0800184ChatDialog::onConnectionData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
185 const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
186{ _LOG_DEBUG("onConnectionData"); }
187
188void
189ChatDialog::onConnectionDataTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest)
190{ _LOG_DEBUG("onConnectionDataTimeout"); }
191
192void
Yingdi Yu42f66462013-10-31 17:38:22 -0700193ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700194{
Yingdi Yu42372442013-11-06 18:43:31 -0800195 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu64206112013-12-24 11:16:32 +0800196 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toEscapedString()));
Yingdi Yu42f66462013-10-31 17:38:22 -0700197 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
198 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
199 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
200}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700201
Yingdi Yu42f66462013-10-31 17:38:22 -0700202void
203ChatDialog::updateLabels()
204{
205 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
206 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
207 ui->infoLabel->setText(settingDisp);
208 QString prefixDisp;
209 if (m_user.getPrefix().startsWith("/private/local"))
210 {
211 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
212 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
213 }
214 else
215 {
216 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
217 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
218 }
219 ui->prefixLabel->setText(prefixDisp);
220}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700221
Yingdi Yu42f66462013-10-31 17:38:22 -0700222void
Yingdi Yu64206112013-12-24 11:16:32 +0800223ChatDialog::sendInterest(const ndn::Interest& interest,
224 const ndn::OnVerified& onVerified,
225 const ndn::OnVerifyFailed& onVerifyFailed,
226 const OnEventualTimeout& timeoutNotify,
Yingdi Yu7630f642014-01-16 19:13:03 -0800227 int retry /* = 1 */)
Yingdi Yu64206112013-12-24 11:16:32 +0800228{
229 m_face->expressInterest(interest,
230 boost::bind(&ChatDialog::onTargetData,
231 this,
232 _1,
233 _2,
Yingdi Yu64206112013-12-24 11:16:32 +0800234 onVerified,
Yingdi Yu7630f642014-01-16 19:13:03 -0800235 onVerifyFailed),
Yingdi Yu64206112013-12-24 11:16:32 +0800236 boost::bind(&ChatDialog::onTargetTimeout,
237 this,
238 _1,
239 retry,
Yingdi Yu64206112013-12-24 11:16:32 +0800240 onVerified,
241 onVerifyFailed,
Yingdi Yu7630f642014-01-16 19:13:03 -0800242 timeoutNotify));
Yingdi Yu64206112013-12-24 11:16:32 +0800243}
244
245void
246ChatDialog::onTargetData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
247 const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
Yingdi Yu64206112013-12-24 11:16:32 +0800248 const ndn::OnVerified& onVerified,
Yingdi Yu7630f642014-01-16 19:13:03 -0800249 const ndn::OnVerifyFailed& onVerifyFailed)
Yingdi Yu64206112013-12-24 11:16:32 +0800250{
Yingdi Yu7630f642014-01-16 19:13:03 -0800251 m_verifier->verifyData(data, onVerified, onVerifyFailed);
Yingdi Yu64206112013-12-24 11:16:32 +0800252}
253
254void
255ChatDialog::onTargetTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
256 int retry,
Yingdi Yu64206112013-12-24 11:16:32 +0800257 const ndn::OnVerified& onVerified,
258 const ndn::OnVerifyFailed& onVerifyFailed,
Yingdi Yu7630f642014-01-16 19:13:03 -0800259 const OnEventualTimeout& timeoutNotify)
Yingdi Yu64206112013-12-24 11:16:32 +0800260{
261 if(retry > 0)
Yingdi Yu7630f642014-01-16 19:13:03 -0800262 sendInterest(*interest, onVerified, onVerifyFailed, timeoutNotify, retry-1);
Yingdi Yu64206112013-12-24 11:16:32 +0800263 else
264 {
265 _LOG_DEBUG("Interest: " << interest->getName().toUri() << " eventually times out!");
266 timeoutNotify();
267 }
268}
269
270void
Yingdi Yu64206112013-12-24 11:16:32 +0800271ChatDialog::sendInvitation(ndn::ptr_lib::shared_ptr<ContactItem> contact, bool isIntroducer)
Yingdi Yu42f66462013-10-31 17:38:22 -0700272{
Yingdi Yueaa84e22014-01-16 10:30:26 -0800273 m_invitationPolicy->addTrustAnchor(contact->getSelfEndorseCertificate());
Yingdi Yu42f66462013-10-31 17:38:22 -0700274
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800275 ndn::Name certificateName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -0700276
277 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700278 interestName.append(contact->getNameSpace());
279 interestName.append("chatroom");
280 interestName.append(m_chatroomPrefix.get(-1));
281 interestName.append("inviter-prefix");
282 interestName.append(m_localPrefix);
283 interestName.append("inviter");
284 interestName.append(certificateName);
285
286 string signedUri = interestName.toUri();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700287
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800288 ndn::Signature sig = m_keyChain->sign(reinterpret_cast<const uint8_t*>(signedUri.c_str()), signedUri.size(), certificateName);
289 const ndn::Block& sigValue = sig.getValue();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700290
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800291 interestName.append(sigValue);
Yingdi Yuaccbda92013-12-27 08:44:12 +0800292
Yingdi Yu64206112013-12-24 11:16:32 +0800293 //TODO... remove version from invitation interest
294 // interestName.appendVersion();
Yingdi Yu42372442013-11-06 18:43:31 -0800295
Yingdi Yu64206112013-12-24 11:16:32 +0800296 ndn::Interest interest(interestName);
297 ndn::OnVerified onVerified = boost::bind(&ChatDialog::onInviteReplyVerified,
298 this,
299 _1,
300 contact->getNameSpace(),
301 isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700302
Yingdi Yu64206112013-12-24 11:16:32 +0800303 ndn::OnVerifyFailed onVerifyFailed = boost::bind(&ChatDialog::onInviteReplyVerifyFailed,
304 this,
305 _1,
306 contact->getNameSpace());
307
308 OnEventualTimeout timeoutNotify = boost::bind(&ChatDialog::onInviteReplyTimeout,
309 this,
310 contact->getNameSpace());
311
312
Yingdi Yu7630f642014-01-16 19:13:03 -0800313 sendInterest(interest, onVerified, onVerifyFailed, timeoutNotify);
Yingdi Yu64206112013-12-24 11:16:32 +0800314}
315
316void
317ChatDialog::onInviteReplyVerified(const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
318 const ndn::Name& identity,
319 bool isIntroducer)
320{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800321 string content(reinterpret_cast<const char*>(data->getContent().value()), data->getContent().value_size());
Yingdi Yu64206112013-12-24 11:16:32 +0800322 if(content == string("nack"))
323 invitationRejected(identity);
324 else
325 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700326}
327
328void
Yingdi Yu64206112013-12-24 11:16:32 +0800329ChatDialog::onInviteReplyVerifyFailed(const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
330 const ndn::Name& identity)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700331{
Yingdi Yu64206112013-12-24 11:16:32 +0800332 _LOG_DEBUG("Reply from " << identity.toUri() << " cannot be verified!");
333 QString msg = QString::fromUtf8("Reply from ") + QString::fromStdString(identity.toUri()) + " cannot be verified!";
334 emit inivationRejection(msg);
335}
336
337
338void
339ChatDialog::onInviteReplyTimeout(const ndn::Name& identity)
340{
341 _LOG_DEBUG("Your invitation to " << identity.toUri() << " times out!");
342 QString msg = QString::fromUtf8("Your invitation to ") + QString::fromStdString(identity.toUri()) + " times out!";
343 emit inivationRejection(msg);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700344}
345
346void
Yingdi Yu42f66462013-10-31 17:38:22 -0700347ChatDialog::invitationRejected(const ndn::Name& identity)
348{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800349 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu3e87bd82013-11-10 10:47:44 -0800350 QString msg = QString::fromStdString(identity.toUri()) + " Rejected your invitation!";
351 emit inivationRejection(msg);
Yingdi Yu42f66462013-10-31 17:38:22 -0700352}
353
354void
Yingdi Yu64206112013-12-24 11:16:32 +0800355ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::ptr_lib::shared_ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700356{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800357 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800358 ndn::SignatureSha256WithRsa sig(data->getSignature());
359 const ndn::Name & keyLocatorName = sig.getKeyLocator().getName();
Yingdi Yueaa84e22014-01-16 10:30:26 -0800360 ndn::ptr_lib::shared_ptr<ndn::IdentityCertificate> dskCertificate = m_invitationPolicy->getValidatedDskCertificate(keyLocatorName);
361 m_syncPolicy->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800362 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700363}
364
Yingdi Yu64206112013-12-24 11:16:32 +0800365void
366ChatDialog::publishIntroCert(const ndn::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700367{
Yingdi Yu64206112013-12-24 11:16:32 +0800368 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
369 dskCertificate.getPublicKeyName(),
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800370 m_keyChain->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yu64206112013-12-24 11:16:32 +0800371 dskCertificate.getNotBefore(),
372 dskCertificate.getNotAfter(),
373 dskCertificate.getPublicKeyInfo(),
374 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800375 ndn::Name certName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu64206112013-12-24 11:16:32 +0800376 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800377 m_keyChain->sign(syncIntroCertificate, certName);
378 m_face->put(syncIntroCertificate);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700379}
380
Yingdi Yu64206112013-12-24 11:16:32 +0800381void
382ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
Yingdi Yueaa84e22014-01-16 10:30:26 -0800383{ m_invitationPolicy->addTrustAnchor(selfEndorseCertificate); }
Yingdi Yu64206112013-12-24 11:16:32 +0800384
385void
386ChatDialog::addChatDataRule(const ndn::Name& prefix,
387 const ndn::IdentityCertificate& identityCertificate,
388 bool isIntroducer)
Yingdi Yueaa84e22014-01-16 10:30:26 -0800389{ m_syncPolicy->addChatDataRule(prefix, identityCertificate, isIntroducer); }
Yingdi Yu64206112013-12-24 11:16:32 +0800390
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700391
Yingdi Yu42372442013-11-06 18:43:31 -0800392
393void
Yingdi Yu42f66462013-10-31 17:38:22 -0700394ChatDialog::initializeSync()
395{
396
397 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
Yingdi Yueaa84e22014-01-16 10:30:26 -0800398 m_syncPolicy,
Yingdi Yuaccbda92013-12-27 08:44:12 +0800399 m_face,
Yingdi Yu64206112013-12-24 11:16:32 +0800400 boost::bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
401 boost::bind(&ChatDialog::processRemoveWrapper, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700402
403 usleep(100000);
404
405 QTimer::singleShot(600, this, SLOT(sendJoin()));
406 m_timer->start(FRESHNESS * 1000);
407 disableTreeDisplay();
408 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
409 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
410 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700411}
412
413void
414ChatDialog::returnPressed()
415{
416 QString text = ui->lineEdit->text();
417 if (text.isEmpty())
418 return;
419
420 ui->lineEdit->clear();
421
422 if (text.startsWith("boruoboluomi"))
423 {
424 summonReaper ();
425 // reapButton->show();
426 fitView();
427 return;
428 }
429
430 if (text.startsWith("minimanihong"))
431 {
432 // reapButton->hide();
433 fitView();
434 return;
435 }
436
437 SyncDemo::ChatMessage msg;
438 formChatMessage(text, msg);
439
440 appendMessage(msg);
441
442 sendMsg(msg);
443
444 fitView();
445}
446
447void
448ChatDialog::treeButtonPressed()
449{
450 if (ui->treeViewer->isVisible())
451 {
452 ui->treeViewer->hide();
453 ui->treeButton->setText("Show ChronoSync Tree");
454 }
455 else
456 {
457 ui->treeViewer->show();
458 ui->treeButton->setText("Hide ChronoSync Tree");
459 }
460
461 fitView();
462}
463
464void ChatDialog::disableTreeDisplay()
465{
466 ui->treeButton->setEnabled(false);
467 ui->treeViewer->hide();
468 fitView();
469}
470
471void ChatDialog::enableTreeDisplay()
472{
473 ui->treeButton->setEnabled(true);
474 // treeViewer->show();
475 // fitView();
476}
477
478void
479ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
480{
481 emit treeUpdated(v);
482 _LOG_DEBUG("<<< Tree update signal emitted");
483}
484
485void
486ChatDialog::processRemoveWrapper(std::string prefix)
487{
488 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
489}
490
491void
492ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
493{
494 _LOG_DEBUG("<<< processing Tree Update");
495
496 if (v.empty())
497 {
498 return;
499 }
500
501 // reflect the changes on digest tree
502 {
503 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
504 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
505 }
506
507 int n = v.size();
508 int totalMissingPackets = 0;
509 for (int i = 0; i < n; i++)
510 {
511 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
512 }
513
514 for (int i = 0; i < n; i++)
515 {
516 if (totalMissingPackets < 4)
517 {
518 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
519 {
520 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
521 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
522 }
523 }
524 else
525 {
526 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
527 }
528 }
529
530 // adjust the view
531 fitView();
532
533}
534
535void
Yingdi Yu64206112013-12-24 11:16:32 +0800536ChatDialog::processDataWrapper(const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700537{
538 string name = data->getName().toUri();
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800539 const char* buf = reinterpret_cast<const char*>(data->getContent().value());
540 size_t len = data->getContent().value_size();
Yingdi Yu42f66462013-10-31 17:38:22 -0700541
542 char *tempBuf = new char[len];
543 memcpy(tempBuf, buf, len);
544 emit dataReceived(name.c_str(), tempBuf, len, true, false);
545 _LOG_DEBUG("<<< " << name << " fetched");
546}
547
548void
Yingdi Yu64206112013-12-24 11:16:32 +0800549ChatDialog::processDataNoShowWrapper(const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700550{
551 string name = data->getName().toUri();
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800552 const char* buf = reinterpret_cast<const char*>(data->getContent().value());
553 size_t len = data->getContent().value_size();
Yingdi Yu42f66462013-10-31 17:38:22 -0700554
555 char *tempBuf = new char[len];
556 memcpy(tempBuf, buf, len);
557 emit dataReceived(name.c_str(), tempBuf, len, false, false);
558
559 // if (!m_historyInitialized)
560 // {
561 // fetchHistory(name);
562 // m_historyInitialized = true;
563 // }
564}
565
566// void
567// ChatDialog::fetchHistory(std::string name)
568// {
569
570// /****************************/
571// /* TODO: fix following part */
572// /****************************/
573// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
574// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
575// prefix += "/history";
576// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
577// // QString randomString = getRandomString();
578// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
579// // {
580// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
581// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
582// // }
583// }
584
585void
586ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
587{
588 SyncDemo::ChatMessage msg;
589 bool corrupted = false;
590 if (!msg.ParseFromArray(buf, len))
591 {
592 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
593 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
594 msg.set_from("inconnu");
595 msg.set_type(SyncDemo::ChatMessage::OTHER);
596 corrupted = true;
597 }
598
599 delete [] buf;
600 buf = NULL;
601
602 // display msg received from network
603 // we have to do so; this function is called by ccnd thread
604 // so if we call appendMsg directly
605 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
606 // the "cannonical" way to is use signal-slot
607 if (show && !corrupted)
608 {
609 appendMessage(msg, isHistory);
610 }
611
612 if (!isHistory)
613 {
614 // update the tree view
615 std::string stdStrName = name.toStdString();
616 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
617 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
618 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
619 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
620 {
621 processRemove(prefix.c_str());
622 }
623 else
624 {
625 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
626 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
627 }
628 }
629 fitView();
630}
631
632void
633ChatDialog::processRemove(QString prefix)
634{
635 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
636
637 bool removed = m_scene->removeNode(prefix);
638 if (removed)
639 {
640 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
641 m_scene->plot(m_sock->getRootDigest().c_str());
642 }
643}
644
645void
646ChatDialog::sendJoin()
647{
648 m_joined = true;
649 SyncDemo::ChatMessage msg;
650 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
651 sendMsg(msg);
652 boost::random::random_device rng;
653 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
654 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
655 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
656}
657
658void
659ChatDialog::sendHello()
660{
661 time_t now = time(NULL);
662 int elapsed = now - m_lastMsgTime;
663 if (elapsed >= m_randomizedInterval / 1000)
664 {
665 SyncDemo::ChatMessage msg;
666 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
667 sendMsg(msg);
668 boost::random::random_device rng;
669 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
670 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
671 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
672 }
673 else
674 {
675 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
676 }
677}
678
679void
680ChatDialog::sendLeave()
681{
682 SyncDemo::ChatMessage msg;
683 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
684 sendMsg(msg);
685 usleep(500000);
686 m_sock->remove(m_user.getPrefix().toStdString());
687 usleep(5000);
688 m_joined = false;
689 _LOG_DEBUG("Sync REMOVE signal sent");
690}
691
692void
693ChatDialog::replot()
694{
695 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
696 m_scene->plot(m_sock->getRootDigest().c_str());
697 fitView();
698}
699
700void
701ChatDialog::summonReaper()
702{
703 Sync::SyncLogic &logic = m_sock->getLogic ();
704 map<string, bool> branches = logic.getBranchPrefixes();
705 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
706
707 m_zombieList.clear();
708
709 QMapIterator<QString, DisplayUserPtr> it(roster);
710 map<string, bool>::iterator mapIt;
711 while(it.hasNext())
712 {
713 it.next();
714 DisplayUserPtr p = it.value();
715 if (p != DisplayUserNullPtr)
716 {
717 mapIt = branches.find(p->getPrefix().toStdString());
718 if (mapIt != branches.end())
719 {
720 mapIt->second = true;
721 }
722 }
723 }
724
725 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
726 {
727 // this is zombie. all active users should have been marked true
728 if (! mapIt->second)
729 {
730 m_zombieList.append(mapIt->first.c_str());
731 }
732 }
733
734 m_zombieIndex = 0;
735
736 // start reaping
737 reap();
738}
739
740void
741ChatDialog::reap()
742{
743 if (m_zombieIndex < m_zombieList.size())
744 {
745 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
746 m_sock->remove(prefix);
747 _LOG_DEBUG("Reaped: prefix = " << prefix);
748 m_zombieIndex++;
749 // reap again in 10 seconds
750 QTimer::singleShot(10000, this, SLOT(reap()));
751 }
752}
753
754void
755ChatDialog::updateRosterList(QStringList staleUserList)
756{
757 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
758 QStringList rosterList = m_scene->getRosterList();
759 m_rosterModel->setStringList(rosterList);
760 QString user;
761 QStringListIterator it(staleUserList);
762 while(it.hasNext())
763 {
764 std::string nick = it.next().toStdString();
765 if (nick.empty())
766 continue;
767
768 SyncDemo::ChatMessage msg;
769 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
770 msg.set_from(nick);
771 appendMessage(msg);
772 }
773}
774
775void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800776ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
777{
Yingdi Yu9f657242013-11-10 12:25:43 -0800778 _LOG_DEBUG("called");
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800779 QString randString = getRandomString();
780 bool needWrite = false;
781 bool needFresh = false;
782
783 QString oldPrefix = m_user.getPrefix();
784 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
785 m_user.setOriginPrefix(originPrefix);
786
787 m_localPrefix = ndn::Name(originPrefix.toStdString());
788 m_localChatPrefix = m_localPrefix;
789 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
790 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
791 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
792 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
793 needWrite = true;
794 needFresh = true;
795 }
796
797 if (needWrite) {
798 updateLabels();
799 }
800
801 if (needFresh && m_sock != NULL)
802 {
803
804 {
805 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
806 m_scene->clearAll();
807 m_scene->plot("Empty");
808 }
809
Yingdi Yu0a953c32013-11-10 10:32:18 -0800810 ui->textEdit->clear();
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800811
812 // keep the new prefix
813 QString newPrefix = m_user.getPrefix();
814 // send leave for the old
815 m_user.setPrefix(oldPrefix);
816 // there is no point to send leave if we haven't joined yet
817 if (m_joined)
818 {
819 sendLeave();
820 }
821 // resume new prefix
822 m_user.setPrefix(newPrefix);
823 // Sync::CcnxWrapperPtr handle = Sync::CcnxWrapper::Create();
824 // handle->clearInterestFilter(oldPrefix.toStdString());
825 delete m_sock;
826 m_sock = NULL;
827
828 try
829 {
830 usleep(100000);
831 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
Yingdi Yueaa84e22014-01-16 10:30:26 -0800832 m_syncPolicy,
Yingdi Yuaccbda92013-12-27 08:44:12 +0800833 m_face,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800834 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
835 bind(&ChatDialog::processRemoveWrapper, this, _1));
836 usleep(100000);
837 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
838 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu0a953c32013-11-10 10:32:18 -0800839 QTimer::singleShot(600, this, SLOT(sendJoin()));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800840 m_timer->start(FRESHNESS * 1000);
841 disableTreeDisplay();
Yingdi Yu0a953c32013-11-10 10:32:18 -0800842 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
Yingdi Yu64206112013-12-24 11:16:32 +0800843 }catch(std::exception& e){
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800844 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
845 }
846 }
847 else if (needFresh && m_sock == NULL)
848 {
849 initializeSync();
850 }
851 else if (m_sock == NULL)
852 {
853 initializeSync();
854 }
855 else
856 {
857// #ifdef __DEBUG
858// std::cout << "Just changing nicks, we're good. " << std::endl;
859// #endif
860 }
861
862 fitView();
863}
864
865void
Yingdi Yua0594092013-11-06 22:07:38 -0800866ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
867{
868 switch (reason)
869 {
870 case QSystemTrayIcon::Trigger:
871 case QSystemTrayIcon::DoubleClick:
872 break;
873 case QSystemTrayIcon::MiddleClick:
874 // showMessage();
875 break;
876 default:;
877 }
878}
879
880
881void
882ChatDialog::messageClicked()
883{
884 this->showMaximized();
885}
886
887
888void
889ChatDialog::createActions()
890{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800891 minimizeAction = new QAction(tr("Mi&nimize"), this);
892 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800893
Yingdi Yu07b5b092013-11-07 17:00:54 -0800894 maximizeAction = new QAction(tr("Ma&ximize"), this);
895 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800896
Yingdi Yu07b5b092013-11-07 17:00:54 -0800897 restoreAction = new QAction(tr("&Restore"), this);
898 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800899
900 // settingsAction = new QAction(tr("Settings"), this);
901 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
902
903 // settingsAction->setMenuRole (QAction::PreferencesRole);
904
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800905 updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
906 connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
Yingdi Yua0594092013-11-06 22:07:38 -0800907
Yingdi Yu07b5b092013-11-07 17:00:54 -0800908 quitAction = new QAction(tr("Quit"), this);
909 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800910}
911
912void
913ChatDialog::createTrayIcon()
914{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800915 trayIconMenu = new QMenu(this);
916 trayIconMenu->addAction(minimizeAction);
917 trayIconMenu->addAction(maximizeAction);
918 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800919 // trayIconMenu->addSeparator();
920 // trayIconMenu->addAction(settingsAction);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800921 trayIconMenu->addSeparator();
922 trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800923 trayIconMenu->addSeparator();
924 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800925
926 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800927 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800928
929 QIcon icon(":/images/icon_small.png");
930 trayIcon->setIcon(icon);
931 setWindowIcon(icon);
932 trayIcon->setToolTip("ChronoChat System Tray Icon");
933 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800934}
935
936
937void
Yingdi Yu42f66462013-10-31 17:38:22 -0700938ChatDialog::resizeEvent(QResizeEvent *e)
939{
940 fitView();
941}
942
943void
944ChatDialog::showEvent(QShowEvent *e)
945{
946 fitView();
947}
948
949void
950ChatDialog::fitView()
951{
952 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
953 QRectF rect = m_scene->itemsBoundingRect();
954 m_scene->setSceneRect(rect);
955 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
956}
957
958void
959ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
960 msg.set_from(m_user.getNick().toStdString());
961 msg.set_to(m_user.getChatroom().toStdString());
962 msg.set_data(text.toUtf8().constData());
963 time_t seconds = time(NULL);
964 msg.set_timestamp(seconds);
965 msg.set_type(SyncDemo::ChatMessage::CHAT);
966}
967
968void
969ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
970{
971 msg.set_from(m_user.getNick().toStdString());
972 msg.set_to(m_user.getChatroom().toStdString());
973 time_t seconds = time(NULL);
974 msg.set_timestamp(seconds);
975 msg.set_type(type);
976}
977
978void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800979ChatDialog::updateLocalPrefix()
980{
Yingdi Yu0a953c32013-11-10 10:32:18 -0800981 m_newLocalPrefixReady = false;
Yingdi Yu64206112013-12-24 11:16:32 +0800982 ndn::Name interestName("/local/ndn/prefix");
983 ndn::Interest interest(interestName);
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800984 interest.setInterestLifetime(1000);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800985
Yingdi Yu64206112013-12-24 11:16:32 +0800986 m_face->expressInterest(interest,
987 bind(&ChatDialog::onLocalPrefix, this, _1, _2),
988 bind(&ChatDialog::onLocalPrefixTimeout, this, _1));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800989
Yingdi Yu0a953c32013-11-10 10:32:18 -0800990 while(m_newLocalPrefixReady == false)
991 {
992#if BOOST_VERSION >= 1050000
993 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
994#else
995 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
996#endif
997 }
Yingdi Yu9f657242013-11-10 12:25:43 -0800998 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
Yingdi Yu6b56f092013-11-10 11:54:02 -0800999 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
Yingdi Yu0a953c32013-11-10 10:32:18 -08001000 QString originPrefix = QString::fromStdString(m_newLocalPrefix.toUri());
1001
1002 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
1003 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001004}
1005
Yingdi Yu64206112013-12-24 11:16:32 +08001006
1007void
1008ChatDialog::onLocalPrefix(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
1009 const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
1010{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -08001011 string dataString(reinterpret_cast<const char*>(data->getContent().value()), data->getContent().value_size());
Yingdi Yu64206112013-12-24 11:16:32 +08001012 QString originPrefix = QString::fromStdString (dataString).trimmed ();
1013 string trimmedString = originPrefix.toStdString();
1014 m_newLocalPrefix = ndn::Name(trimmedString);
1015 m_newLocalPrefixReady = true;
1016}
1017
1018void
1019ChatDialog::onLocalPrefixTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest)
1020{
1021 m_newLocalPrefix = m_localPrefix;
1022 m_newLocalPrefixReady = true;
1023}
1024
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001025static std::string chars2("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
1026
1027QString
1028ChatDialog::getRandomString()
1029{
1030 std::string randStr;
1031 boost::random::random_device rng;
1032 boost::random::uniform_int_distribution<> index_dist(0, chars2.size() - 1);
1033 for (int i = 0; i < 10; i ++)
1034 {
1035 randStr += chars2[index_dist(rng)];
1036 }
1037 return randStr.c_str();
1038}
1039
1040void
Yingdi Yua0594092013-11-06 22:07:38 -08001041ChatDialog::changeEvent(QEvent *e)
1042{
1043 switch(e->type())
1044 {
1045 case QEvent::ActivationChange:
1046 if (isActiveWindow())
1047 {
1048 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
1049 }
1050 break;
1051 default:
1052 break;
1053 }
1054}
1055
1056void
Yingdi Yu42f66462013-10-31 17:38:22 -07001057ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
1058{
1059 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
1060
1061 if (msg.type() == SyncDemo::ChatMessage::CHAT)
1062 {
1063
1064 if (!msg.has_data())
1065 {
1066 return;
1067 }
1068
1069 if (msg.from().empty() || msg.data().empty())
1070 {
1071 return;
1072 }
1073
1074 if (!msg.has_timestamp())
1075 {
1076 return;
1077 }
1078
1079 // if (m_history.size() == MAX_HISTORY_ENTRY)
1080 // {
1081 // m_history.dequeue();
1082 // }
1083
1084 // m_history.enqueue(msg);
1085
1086 QTextCharFormat nickFormat;
1087 nickFormat.setForeground(Qt::darkGreen);
1088 nickFormat.setFontWeight(QFont::Bold);
1089 nickFormat.setFontUnderline(true);
1090 nickFormat.setUnderlineColor(Qt::gray);
1091
1092 QTextCursor cursor(ui->textEdit->textCursor());
1093 cursor.movePosition(QTextCursor::End);
1094 QTextTableFormat tableFormat;
1095 tableFormat.setBorder(0);
1096 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1097 QString from = QString("%1 ").arg(msg.from().c_str());
1098 QTextTableCell fromCell = table->cellAt(0, 0);
1099 fromCell.setFormat(nickFormat);
1100 fromCell.firstCursorPosition().insertText(from);
1101
1102 time_t timestamp = msg.timestamp();
1103 printTimeInCell(table, timestamp);
1104
1105 QTextCursor nextCursor(ui->textEdit->textCursor());
1106 nextCursor.movePosition(QTextCursor::End);
1107 table = nextCursor.insertTable(1, 1, tableFormat);
1108 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
1109 if (!isHistory)
1110 {
1111 showMessage(from, QString::fromUtf8(msg.data().c_str()));
1112 }
1113 }
1114
1115 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
1116 {
1117 QTextCharFormat nickFormat;
1118 nickFormat.setForeground(Qt::gray);
1119 nickFormat.setFontWeight(QFont::Bold);
1120 nickFormat.setFontUnderline(true);
1121 nickFormat.setUnderlineColor(Qt::gray);
1122
1123 QTextCursor cursor(ui->textEdit->textCursor());
1124 cursor.movePosition(QTextCursor::End);
1125 QTextTableFormat tableFormat;
1126 tableFormat.setBorder(0);
1127 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1128 QString action;
1129 if (msg.type() == SyncDemo::ChatMessage::JOIN)
1130 {
1131 action = "enters room";
1132 }
1133 else
1134 {
1135 action = "leaves room";
1136 }
1137
1138 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
1139 QTextTableCell fromCell = table->cellAt(0, 0);
1140 fromCell.setFormat(nickFormat);
1141 fromCell.firstCursorPosition().insertText(from);
1142
1143 time_t timestamp = msg.timestamp();
1144 printTimeInCell(table, timestamp);
1145 }
1146
1147 QScrollBar *bar = ui->textEdit->verticalScrollBar();
1148 bar->setValue(bar->maximum());
1149}
1150
1151QString
1152ChatDialog::formatTime(time_t timestamp)
1153{
1154 struct tm *tm_time = localtime(&timestamp);
1155 int hour = tm_time->tm_hour;
1156 QString amOrPM;
1157 if (hour > 12)
1158 {
1159 hour -= 12;
1160 amOrPM = "PM";
1161 }
1162 else
1163 {
1164 amOrPM = "AM";
1165 if (hour == 0)
1166 {
1167 hour = 12;
1168 }
1169 }
1170
1171 char textTime[12];
1172 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
1173 return QString(textTime);
1174}
1175
1176void
1177ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
1178{
1179 QTextCharFormat timeFormat;
1180 timeFormat.setForeground(Qt::gray);
1181 timeFormat.setFontUnderline(true);
1182 timeFormat.setUnderlineColor(Qt::gray);
1183 QTextTableCell timeCell = table->cellAt(0, 1);
1184 timeCell.setFormat(timeFormat);
1185 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
1186}
1187
1188void
1189ChatDialog::showMessage(QString from, QString data)
1190{
1191 if (!isActiveWindow())
1192 {
Yingdi Yua0594092013-11-06 22:07:38 -08001193 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
1194 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -07001195 }
1196}
1197
1198void
1199ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
1200{
1201 // send msg
1202 size_t size = msg.ByteSize();
1203 char *buf = new char[size];
1204 msg.SerializeToArray(buf, size);
1205 if (!msg.IsInitialized())
1206 {
1207 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
1208 abort();
1209 }
1210 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
1211
1212 delete buf;
1213
1214 m_lastMsgTime = time(NULL);
1215
1216 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
1217 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
1218 std::vector<Sync::MissingDataInfo> v;
1219 v.push_back(mdi);
1220 {
1221 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1222 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
1223 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
1224 }
1225}
1226
1227void
1228ChatDialog::openInviteListDialog()
1229{
1230 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1231 m_inviteListDialog->show();
1232}
1233
1234void
1235ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1236{
Yingdi Yu64206112013-12-24 11:16:32 +08001237 ndn::Name inviteeNamespace(invitee.toStdString());
1238 ndn::ptr_lib::shared_ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
Yingdi Yu42f66462013-10-31 17:38:22 -07001239 sendInvitation(inviteeItem, isIntroducer);
1240}
1241
Yingdi Yu42372442013-11-06 18:43:31 -08001242void
1243ChatDialog::closeEvent(QCloseEvent *e)
1244{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001245 if (trayIcon->isVisible())
1246 {
1247 QMessageBox::information(this, tr("Chronos"),
1248 tr("The program will keep running in the "
1249 "system tray. To terminate the program"
1250 "choose <b>Quit</b> in the context memu"
1251 "of the system tray entry."));
1252 hide();
1253 e->ignore();
1254 }
1255}
1256
1257void
1258ChatDialog::quit()
1259{
Yingdi Yu42372442013-11-06 18:43:31 -08001260 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001261 emit closeChatDialog(m_chatroomPrefix);
1262}
1263
1264
1265
Yingdi Yu42f66462013-10-31 17:38:22 -07001266
Yingdi Yu04842232013-10-23 14:03:09 -07001267#if WAF
1268#include "chatdialog.moc"
1269#include "chatdialog.cpp.moc"
1270#endif