blob: fd3179cfe5d3c8176e31c885fa05f66d03176b97 [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>
17
Yingdi Yuc4d08d22013-10-23 23:07:29 -070018#ifndef Q_MOC_RUN
19#include <ndn.cxx/security/identity/identity-manager.h>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070020#include <ndn.cxx/security/encryption/basic-encryption-manager.h>
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 Yuc4d08d22013-10-23 23:07:29 -070024#include "logging.h"
25#endif
26
Yingdi Yu04842232013-10-23 14:03:09 -070027using namespace std;
Yingdi Yu04842232013-10-23 14:03:09 -070028
Yingdi Yuc4d08d22013-10-23 23:07:29 -070029INIT_LOGGER("ChatDialog");
30
Yingdi Yu42f66462013-10-31 17:38:22 -070031static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
32
33Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
34Q_DECLARE_METATYPE(size_t)
35
36ChatDialog::ChatDialog(ndn::Ptr<ContactManager> contactManager,
37 const ndn::Name& chatroomPrefix,
38 const ndn::Name& localPrefix,
39 const ndn::Name& defaultIdentity,
Yingdi Yu42372442013-11-06 18:43:31 -080040 const std::string& nick,
41 bool trial,
Yingdi Yu04842232013-10-23 14:03:09 -070042 QWidget *parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070043: QDialog(parent)
44 , ui(new Ui::ChatDialog)
45 , m_contactManager(contactManager)
46 , m_chatroomPrefix(chatroomPrefix)
47 , m_localPrefix(localPrefix)
48 , m_defaultIdentity(defaultIdentity)
Yingdi Yub35b8652013-11-07 11:32:40 -080049 , m_invitationPolicyManager(ndn::Ptr<InvitationPolicyManager>(new InvitationPolicyManager(m_chatroomPrefix.get(-1).toUri(), m_defaultIdentity)))
Yingdi Yu42372442013-11-06 18:43:31 -080050 , m_nick(nick)
Yingdi Yu42f66462013-10-31 17:38:22 -070051 , m_sock(NULL)
52 , m_lastMsgTime(0)
53 // , m_historyInitialized(false)
54 , m_joined(false)
55 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Yingdi Yu04842232013-10-23 14:03:09 -070056{
Yingdi Yu42f66462013-10-31 17:38:22 -070057 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
58 qRegisterMetaType<size_t>("size_t");
59
Yingdi Yuc4d08d22013-10-23 23:07:29 -070060 ui->setupUi(this);
61
Yingdi Yu42f66462013-10-31 17:38:22 -070062 m_localChatPrefix = m_localPrefix;
Yingdi Yu42372442013-11-06 18:43:31 -080063 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -070064 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1));
65
66 m_session = time(NULL);
67 m_scene = new DigestTreeScene(this);
68
69 initializeSetting();
70 updateLabels();
71
72 ui->treeViewer->setScene(m_scene);
73 ui->treeViewer->hide();
74 m_scene->plot("Empty");
75 QRectF rect = m_scene->itemsBoundingRect();
76 m_scene->setSceneRect(rect);
77
78 m_rosterModel = new QStringListModel(this);
79 ui->listView->setModel(m_rosterModel);
80
Yingdi Yua0594092013-11-06 22:07:38 -080081 createActions();
82 createTrayIcon();
83
Yingdi Yu42f66462013-10-31 17:38:22 -070084 m_timer = new QTimer(this);
85
Yingdi Yu42372442013-11-06 18:43:31 -080086 setWrapper(trial);
Yingdi Yu42f66462013-10-31 17:38:22 -070087
88 connect(ui->inviteButton, SIGNAL(clicked()),
89 this, SLOT(openInviteListDialog()));
90 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
91 this, SLOT(sendInvitationWrapper(QString, bool)));
92 connect(ui->lineEdit, SIGNAL(returnPressed()),
93 this, SLOT(returnPressed()));
94 connect(ui->treeButton, SIGNAL(pressed()),
95 this, SLOT(treeButtonPressed()));
96 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
97 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
98 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
99 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
100 connect(m_timer, SIGNAL(timeout()),
101 this, SLOT(replot()));
102 connect(m_scene, SIGNAL(replot()),
103 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800104 connect(trayIcon, SIGNAL(messageClicked()),
105 this, SLOT(showNormal()));
106 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
107 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700108 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
109 this, SLOT(updateRosterList(QStringList)));
110
Yingdi Yu42f66462013-10-31 17:38:22 -0700111
112 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700113}
114
Yingdi Yu42f66462013-10-31 17:38:22 -0700115
Yingdi Yu04842232013-10-23 14:03:09 -0700116ChatDialog::~ChatDialog()
117{
Yingdi Yu42372442013-11-06 18:43:31 -0800118 if(m_sock != NULL)
119 {
120 sendLeave();
121 delete m_sock;
122 m_sock = NULL;
123 }
Yingdi Yua0594092013-11-06 22:07:38 -0800124 m_handler->shutdown();
Yingdi Yu04842232013-10-23 14:03:09 -0700125}
126
127void
Yingdi Yu42372442013-11-06 18:43:31 -0800128ChatDialog::setWrapper(bool trial)
Yingdi Yu04842232013-10-23 14:03:09 -0700129{
Yingdi Yu42f66462013-10-31 17:38:22 -0700130 m_identityManager = ndn::Ptr<ndn::security::IdentityManager>::Create();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700131
Yingdi Yu42f66462013-10-31 17:38:22 -0700132 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
133 m_syncPolicyManager = ndn::Ptr<SyncPolicyManager>(new SyncPolicyManager(m_defaultIdentity, certificateName, m_chatroomPrefix));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700134
Yingdi Yu42f66462013-10-31 17:38:22 -0700135 m_keychain = ndn::Ptr<ndn::security::Keychain>(new ndn::security::Keychain(m_identityManager, m_invitationPolicyManager, NULL));
136
137 m_handler = ndn::Ptr<ndn::Wrapper>(new ndn::Wrapper(m_keychain));
Yingdi Yu42372442013-11-06 18:43:31 -0800138
139 if(trial == true)
140 {
141 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(ndn::Name("/local/ndn/prefix")));
142 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onUnverified,
143 this,
144 _1),
145 boost::bind(&ChatDialog::onTimeout,
146 this,
147 _1,
148 _2),
149 boost::bind(&ChatDialog::onUnverified,
150 this,
151 _1)));
152
153 m_handler->sendInterest(interest, closure);
154 }
Yingdi Yu04842232013-10-23 14:03:09 -0700155}
156
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700157void
Yingdi Yu42f66462013-10-31 17:38:22 -0700158ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700159{
Yingdi Yu42372442013-11-06 18:43:31 -0800160 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu42f66462013-10-31 17:38:22 -0700161 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toUri()));
162 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
163 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
164 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
165}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700166
Yingdi Yu42f66462013-10-31 17:38:22 -0700167void
168ChatDialog::updateLabels()
169{
170 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
171 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
172 ui->infoLabel->setText(settingDisp);
173 QString prefixDisp;
174 if (m_user.getPrefix().startsWith("/private/local"))
175 {
176 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
177 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
178 }
179 else
180 {
181 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
182 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
183 }
184 ui->prefixLabel->setText(prefixDisp);
185}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700186
Yingdi Yu42f66462013-10-31 17:38:22 -0700187void
188ChatDialog::sendInvitation(ndn::Ptr<ContactItem> contact, bool isIntroducer)
189{
190 m_invitationPolicyManager->addTrustAnchor(contact->getSelfEndorseCertificate());
191
192 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
193
194 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700195 interestName.append(contact->getNameSpace());
196 interestName.append("chatroom");
197 interestName.append(m_chatroomPrefix.get(-1));
198 interestName.append("inviter-prefix");
199 interestName.append(m_localPrefix);
200 interestName.append("inviter");
201 interestName.append(certificateName);
202
203 string signedUri = interestName.toUri();
Yingdi Yu42f66462013-10-31 17:38:22 -0700204 ndn::Blob signedBlob(signedUri.c_str(), signedUri.size());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700205
Yingdi Yu42f66462013-10-31 17:38:22 -0700206 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = ndn::DynamicCast<const ndn::signature::Sha256WithRsa>(m_identityManager->signByCertificate(signedBlob, certificateName));
207 const ndn::Blob& sigBits = sha256sig->getSignatureBits();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700208
209 interestName.append(sigBits.buf(), sigBits.size());
Yingdi Yu42372442013-11-06 18:43:31 -0800210 interestName.appendVersion();
211
Yingdi Yu42f66462013-10-31 17:38:22 -0700212 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(interestName));
213 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onInviteReplyVerified,
214 this,
215 _1,
216 contact->getNameSpace(),
217 isIntroducer),
218 boost::bind(&ChatDialog::onInviteTimeout,
219 this,
220 _1,
221 _2,
222 contact->getNameSpace(),
223 7),
224 boost::bind(&ChatDialog::onUnverified,
225 this,
226 _1)));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700227
228 m_handler->sendInterest(interest, closure);
229}
230
231void
Yingdi Yu42f66462013-10-31 17:38:22 -0700232ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
233{ m_invitationPolicyManager->addTrustAnchor(selfEndorseCertificate); }
234
235void
236ChatDialog::addChatDataRule(const ndn::Name& prefix,
237 const ndn::security::IdentityCertificate& identityCertificate,
238 bool isIntroducer)
239{ m_syncPolicyManager->addChatDataRule(prefix, identityCertificate, isIntroducer); }
240
241void
Yingdi Yua0594092013-11-06 22:07:38 -0800242ChatDialog::publishIntroCert(const ndn::security::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700243{
Yingdi Yu42f66462013-10-31 17:38:22 -0700244 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
Yingdi Yua0594092013-11-06 22:07:38 -0800245 dskCertificate.getPublicKeyName(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700246 m_identityManager->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yua0594092013-11-06 22:07:38 -0800247 dskCertificate.getNotBefore(),
248 dskCertificate.getNotAfter(),
249 dskCertificate.getPublicKeyInfo(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700250 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
251 ndn::Name certName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800252 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yu42f66462013-10-31 17:38:22 -0700253 m_identityManager->signByCertificate(syncIntroCertificate, certName);
254 m_handler->putToNdnd(*syncIntroCertificate.encodeToWire());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700255}
256
257void
Yingdi Yu42f66462013-10-31 17:38:22 -0700258ChatDialog::invitationRejected(const ndn::Name& identity)
259{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800260 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700261}
262
263void
264ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::Ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700265{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800266 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700267 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = boost::dynamic_pointer_cast<const ndn::signature::Sha256WithRsa> (data->getSignature());
268 const ndn::Name & keyLocatorName = sha256sig->getKeyLocator().getKeyName();
269 ndn::Ptr<ndn::security::IdentityCertificate> dskCertificate = m_invitationPolicyManager->getValidatedDskCertificate(keyLocatorName);
Yingdi Yuc90deb12013-11-06 18:51:19 -0800270 m_syncPolicyManager->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800271 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700272}
273
274void
Yingdi Yu42f66462013-10-31 17:38:22 -0700275ChatDialog::onInviteReplyVerified(ndn::Ptr<ndn::Data> data, const ndn::Name& identity, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700276{
277 string content(data->content().buf(), data->content().size());
278 if(content.empty())
279 invitationRejected(identity);
280 else
Yingdi Yu42f66462013-10-31 17:38:22 -0700281 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700282}
283
284void
Yingdi Yu42f66462013-10-31 17:38:22 -0700285ChatDialog::onInviteTimeout(ndn::Ptr<ndn::Closure> closure, ndn::Ptr<ndn::Interest> interest, const ndn::Name& identity, int retry)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700286{
287 if(retry > 0)
288 {
Yingdi Yu42f66462013-10-31 17:38:22 -0700289 ndn::Ptr<ndn::Closure> newClosure = ndn::Ptr<ndn::Closure>(new ndn::Closure(closure->m_dataCallback,
290 boost::bind(&ChatDialog::onInviteTimeout,
291 this,
292 _1,
293 _2,
294 identity,
295 retry - 1),
296 closure->m_unverifiedCallback,
297 closure->m_stepCount)
298 );
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700299 m_handler->sendInterest(interest, newClosure);
300 }
301 else
302 invitationRejected(identity);
303}
304
305void
Yingdi Yu42f66462013-10-31 17:38:22 -0700306ChatDialog::onUnverified(ndn::Ptr<ndn::Data> data)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700307{}
308
Yingdi Yu42f66462013-10-31 17:38:22 -0700309void
Yingdi Yu42372442013-11-06 18:43:31 -0800310ChatDialog::onTimeout(ndn::Ptr<ndn::Closure> closure,
311 ndn::Ptr<ndn::Interest> interest)
312{}
313
314void
Yingdi Yu42f66462013-10-31 17:38:22 -0700315ChatDialog::initializeSync()
316{
317
318 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
319 m_syncPolicyManager,
320 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
321 bind(&ChatDialog::processRemoveWrapper, this, _1));
322
323 usleep(100000);
324
325 QTimer::singleShot(600, this, SLOT(sendJoin()));
326 m_timer->start(FRESHNESS * 1000);
327 disableTreeDisplay();
328 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
329 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
330 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700331}
332
333void
334ChatDialog::returnPressed()
335{
336 QString text = ui->lineEdit->text();
337 if (text.isEmpty())
338 return;
339
340 ui->lineEdit->clear();
341
342 if (text.startsWith("boruoboluomi"))
343 {
344 summonReaper ();
345 // reapButton->show();
346 fitView();
347 return;
348 }
349
350 if (text.startsWith("minimanihong"))
351 {
352 // reapButton->hide();
353 fitView();
354 return;
355 }
356
357 SyncDemo::ChatMessage msg;
358 formChatMessage(text, msg);
359
360 appendMessage(msg);
361
362 sendMsg(msg);
363
364 fitView();
365}
366
367void
368ChatDialog::treeButtonPressed()
369{
370 if (ui->treeViewer->isVisible())
371 {
372 ui->treeViewer->hide();
373 ui->treeButton->setText("Show ChronoSync Tree");
374 }
375 else
376 {
377 ui->treeViewer->show();
378 ui->treeButton->setText("Hide ChronoSync Tree");
379 }
380
381 fitView();
382}
383
384void ChatDialog::disableTreeDisplay()
385{
386 ui->treeButton->setEnabled(false);
387 ui->treeViewer->hide();
388 fitView();
389}
390
391void ChatDialog::enableTreeDisplay()
392{
393 ui->treeButton->setEnabled(true);
394 // treeViewer->show();
395 // fitView();
396}
397
398void
399ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
400{
401 emit treeUpdated(v);
402 _LOG_DEBUG("<<< Tree update signal emitted");
403}
404
405void
406ChatDialog::processRemoveWrapper(std::string prefix)
407{
408 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
409}
410
411void
412ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
413{
414 _LOG_DEBUG("<<< processing Tree Update");
415
416 if (v.empty())
417 {
418 return;
419 }
420
421 // reflect the changes on digest tree
422 {
423 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
424 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
425 }
426
427 int n = v.size();
428 int totalMissingPackets = 0;
429 for (int i = 0; i < n; i++)
430 {
431 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
432 }
433
434 for (int i = 0; i < n; i++)
435 {
436 if (totalMissingPackets < 4)
437 {
438 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
439 {
440 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
441 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
442 }
443 }
444 else
445 {
446 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
447 }
448 }
449
450 // adjust the view
451 fitView();
452
453}
454
455void
456ChatDialog::processDataWrapper(ndn::Ptr<ndn::Data> data)
457{
458 string name = data->getName().toUri();
459 const char* buf = data->content().buf();
460 size_t len = data->content().size();
461
462 char *tempBuf = new char[len];
463 memcpy(tempBuf, buf, len);
464 emit dataReceived(name.c_str(), tempBuf, len, true, false);
465 _LOG_DEBUG("<<< " << name << " fetched");
466}
467
468void
469ChatDialog::processDataNoShowWrapper(ndn::Ptr<ndn::Data> data)
470{
471 string name = data->getName().toUri();
472 const char* buf = data->content().buf();
473 size_t len = data->content().size();
474
475 char *tempBuf = new char[len];
476 memcpy(tempBuf, buf, len);
477 emit dataReceived(name.c_str(), tempBuf, len, false, false);
478
479 // if (!m_historyInitialized)
480 // {
481 // fetchHistory(name);
482 // m_historyInitialized = true;
483 // }
484}
485
486// void
487// ChatDialog::fetchHistory(std::string name)
488// {
489
490// /****************************/
491// /* TODO: fix following part */
492// /****************************/
493// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
494// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
495// prefix += "/history";
496// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
497// // QString randomString = getRandomString();
498// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
499// // {
500// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
501// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
502// // }
503// }
504
505void
506ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
507{
508 SyncDemo::ChatMessage msg;
509 bool corrupted = false;
510 if (!msg.ParseFromArray(buf, len))
511 {
512 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
513 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
514 msg.set_from("inconnu");
515 msg.set_type(SyncDemo::ChatMessage::OTHER);
516 corrupted = true;
517 }
518
519 delete [] buf;
520 buf = NULL;
521
522 // display msg received from network
523 // we have to do so; this function is called by ccnd thread
524 // so if we call appendMsg directly
525 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
526 // the "cannonical" way to is use signal-slot
527 if (show && !corrupted)
528 {
529 appendMessage(msg, isHistory);
530 }
531
532 if (!isHistory)
533 {
534 // update the tree view
535 std::string stdStrName = name.toStdString();
536 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
537 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
538 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
539 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
540 {
541 processRemove(prefix.c_str());
542 }
543 else
544 {
545 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
546 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
547 }
548 }
549 fitView();
550}
551
552void
553ChatDialog::processRemove(QString prefix)
554{
555 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
556
557 bool removed = m_scene->removeNode(prefix);
558 if (removed)
559 {
560 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
561 m_scene->plot(m_sock->getRootDigest().c_str());
562 }
563}
564
565void
566ChatDialog::sendJoin()
567{
568 m_joined = true;
569 SyncDemo::ChatMessage msg;
570 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
571 sendMsg(msg);
572 boost::random::random_device rng;
573 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
574 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
575 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
576}
577
578void
579ChatDialog::sendHello()
580{
581 time_t now = time(NULL);
582 int elapsed = now - m_lastMsgTime;
583 if (elapsed >= m_randomizedInterval / 1000)
584 {
585 SyncDemo::ChatMessage msg;
586 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
587 sendMsg(msg);
588 boost::random::random_device rng;
589 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
590 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
591 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
592 }
593 else
594 {
595 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
596 }
597}
598
599void
600ChatDialog::sendLeave()
601{
602 SyncDemo::ChatMessage msg;
603 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
604 sendMsg(msg);
605 usleep(500000);
606 m_sock->remove(m_user.getPrefix().toStdString());
607 usleep(5000);
608 m_joined = false;
609 _LOG_DEBUG("Sync REMOVE signal sent");
610}
611
612void
613ChatDialog::replot()
614{
615 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
616 m_scene->plot(m_sock->getRootDigest().c_str());
617 fitView();
618}
619
620void
621ChatDialog::summonReaper()
622{
623 Sync::SyncLogic &logic = m_sock->getLogic ();
624 map<string, bool> branches = logic.getBranchPrefixes();
625 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
626
627 m_zombieList.clear();
628
629 QMapIterator<QString, DisplayUserPtr> it(roster);
630 map<string, bool>::iterator mapIt;
631 while(it.hasNext())
632 {
633 it.next();
634 DisplayUserPtr p = it.value();
635 if (p != DisplayUserNullPtr)
636 {
637 mapIt = branches.find(p->getPrefix().toStdString());
638 if (mapIt != branches.end())
639 {
640 mapIt->second = true;
641 }
642 }
643 }
644
645 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
646 {
647 // this is zombie. all active users should have been marked true
648 if (! mapIt->second)
649 {
650 m_zombieList.append(mapIt->first.c_str());
651 }
652 }
653
654 m_zombieIndex = 0;
655
656 // start reaping
657 reap();
658}
659
660void
661ChatDialog::reap()
662{
663 if (m_zombieIndex < m_zombieList.size())
664 {
665 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
666 m_sock->remove(prefix);
667 _LOG_DEBUG("Reaped: prefix = " << prefix);
668 m_zombieIndex++;
669 // reap again in 10 seconds
670 QTimer::singleShot(10000, this, SLOT(reap()));
671 }
672}
673
674void
675ChatDialog::updateRosterList(QStringList staleUserList)
676{
677 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
678 QStringList rosterList = m_scene->getRosterList();
679 m_rosterModel->setStringList(rosterList);
680 QString user;
681 QStringListIterator it(staleUserList);
682 while(it.hasNext())
683 {
684 std::string nick = it.next().toStdString();
685 if (nick.empty())
686 continue;
687
688 SyncDemo::ChatMessage msg;
689 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
690 msg.set_from(nick);
691 appendMessage(msg);
692 }
693}
694
695void
Yingdi Yua0594092013-11-06 22:07:38 -0800696ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
697{
698 switch (reason)
699 {
700 case QSystemTrayIcon::Trigger:
701 case QSystemTrayIcon::DoubleClick:
702 break;
703 case QSystemTrayIcon::MiddleClick:
704 // showMessage();
705 break;
706 default:;
707 }
708}
709
710
711void
712ChatDialog::messageClicked()
713{
714 this->showMaximized();
715}
716
717
718void
719ChatDialog::createActions()
720{
721 // minimizeAction = new QAction(tr("Mi&nimize"), this);
722 // connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
723
724 // maximizeAction = new QAction(tr("Ma&ximize"), this);
725 // connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
726
727 // restoreAction = new QAction(tr("&Restore"), this);
728 // connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
729
730 // settingsAction = new QAction(tr("Settings"), this);
731 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
732
733 // settingsAction->setMenuRole (QAction::PreferencesRole);
734
735 // updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
736 // connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
737
738 // quitAction = new QAction(tr("Quit"), this);
739 // connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
740}
741
742void
743ChatDialog::createTrayIcon()
744{
745 // trayIconMenu = new QMenu(this);
746 // trayIconMenu->addAction(minimizeAction);
747 // trayIconMenu->addAction(maximizeAction);
748 // trayIconMenu->addAction(restoreAction);
749 // trayIconMenu->addSeparator();
750 // trayIconMenu->addAction(settingsAction);
751 // trayIconMenu->addSeparator();
752 // trayIconMenu->addAction(updateLocalPrefixAction);
753 // trayIconMenu->addSeparator();
754 // trayIconMenu->addAction(quitAction);
755
756 trayIcon = new QSystemTrayIcon(this);
757 // trayIcon->setContextMenu(trayIconMenu);
758
759 QIcon icon(":/images/icon_small.png");
760 trayIcon->setIcon(icon);
761 setWindowIcon(icon);
762 trayIcon->setToolTip("ChronoChat System Tray Icon");
763 trayIcon->setVisible(true);
764
765 // // QApplication::getMenu ()->addMenu (trayIconMenu);
766 // QMenuBar *bar = new QMenuBar ();
767 // bar->setMenu (trayIconMenu);
768 // setMenuBar (bar);
769}
770
771
772void
Yingdi Yu42f66462013-10-31 17:38:22 -0700773ChatDialog::resizeEvent(QResizeEvent *e)
774{
775 fitView();
776}
777
778void
779ChatDialog::showEvent(QShowEvent *e)
780{
781 fitView();
782}
783
784void
785ChatDialog::fitView()
786{
787 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
788 QRectF rect = m_scene->itemsBoundingRect();
789 m_scene->setSceneRect(rect);
790 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
791}
792
793void
794ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
795 msg.set_from(m_user.getNick().toStdString());
796 msg.set_to(m_user.getChatroom().toStdString());
797 msg.set_data(text.toUtf8().constData());
798 time_t seconds = time(NULL);
799 msg.set_timestamp(seconds);
800 msg.set_type(SyncDemo::ChatMessage::CHAT);
801}
802
803void
804ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
805{
806 msg.set_from(m_user.getNick().toStdString());
807 msg.set_to(m_user.getChatroom().toStdString());
808 time_t seconds = time(NULL);
809 msg.set_timestamp(seconds);
810 msg.set_type(type);
811}
812
813void
Yingdi Yua0594092013-11-06 22:07:38 -0800814ChatDialog::changeEvent(QEvent *e)
815{
816 switch(e->type())
817 {
818 case QEvent::ActivationChange:
819 if (isActiveWindow())
820 {
821 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
822 }
823 break;
824 default:
825 break;
826 }
827}
828
829void
Yingdi Yu42f66462013-10-31 17:38:22 -0700830ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
831{
832 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
833
834 if (msg.type() == SyncDemo::ChatMessage::CHAT)
835 {
836
837 if (!msg.has_data())
838 {
839 return;
840 }
841
842 if (msg.from().empty() || msg.data().empty())
843 {
844 return;
845 }
846
847 if (!msg.has_timestamp())
848 {
849 return;
850 }
851
852 // if (m_history.size() == MAX_HISTORY_ENTRY)
853 // {
854 // m_history.dequeue();
855 // }
856
857 // m_history.enqueue(msg);
858
859 QTextCharFormat nickFormat;
860 nickFormat.setForeground(Qt::darkGreen);
861 nickFormat.setFontWeight(QFont::Bold);
862 nickFormat.setFontUnderline(true);
863 nickFormat.setUnderlineColor(Qt::gray);
864
865 QTextCursor cursor(ui->textEdit->textCursor());
866 cursor.movePosition(QTextCursor::End);
867 QTextTableFormat tableFormat;
868 tableFormat.setBorder(0);
869 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
870 QString from = QString("%1 ").arg(msg.from().c_str());
871 QTextTableCell fromCell = table->cellAt(0, 0);
872 fromCell.setFormat(nickFormat);
873 fromCell.firstCursorPosition().insertText(from);
874
875 time_t timestamp = msg.timestamp();
876 printTimeInCell(table, timestamp);
877
878 QTextCursor nextCursor(ui->textEdit->textCursor());
879 nextCursor.movePosition(QTextCursor::End);
880 table = nextCursor.insertTable(1, 1, tableFormat);
881 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
882 if (!isHistory)
883 {
884 showMessage(from, QString::fromUtf8(msg.data().c_str()));
885 }
886 }
887
888 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
889 {
890 QTextCharFormat nickFormat;
891 nickFormat.setForeground(Qt::gray);
892 nickFormat.setFontWeight(QFont::Bold);
893 nickFormat.setFontUnderline(true);
894 nickFormat.setUnderlineColor(Qt::gray);
895
896 QTextCursor cursor(ui->textEdit->textCursor());
897 cursor.movePosition(QTextCursor::End);
898 QTextTableFormat tableFormat;
899 tableFormat.setBorder(0);
900 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
901 QString action;
902 if (msg.type() == SyncDemo::ChatMessage::JOIN)
903 {
904 action = "enters room";
905 }
906 else
907 {
908 action = "leaves room";
909 }
910
911 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
912 QTextTableCell fromCell = table->cellAt(0, 0);
913 fromCell.setFormat(nickFormat);
914 fromCell.firstCursorPosition().insertText(from);
915
916 time_t timestamp = msg.timestamp();
917 printTimeInCell(table, timestamp);
918 }
919
920 QScrollBar *bar = ui->textEdit->verticalScrollBar();
921 bar->setValue(bar->maximum());
922}
923
924QString
925ChatDialog::formatTime(time_t timestamp)
926{
927 struct tm *tm_time = localtime(&timestamp);
928 int hour = tm_time->tm_hour;
929 QString amOrPM;
930 if (hour > 12)
931 {
932 hour -= 12;
933 amOrPM = "PM";
934 }
935 else
936 {
937 amOrPM = "AM";
938 if (hour == 0)
939 {
940 hour = 12;
941 }
942 }
943
944 char textTime[12];
945 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
946 return QString(textTime);
947}
948
949void
950ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
951{
952 QTextCharFormat timeFormat;
953 timeFormat.setForeground(Qt::gray);
954 timeFormat.setFontUnderline(true);
955 timeFormat.setUnderlineColor(Qt::gray);
956 QTextTableCell timeCell = table->cellAt(0, 1);
957 timeCell.setFormat(timeFormat);
958 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
959}
960
961void
962ChatDialog::showMessage(QString from, QString data)
963{
964 if (!isActiveWindow())
965 {
Yingdi Yua0594092013-11-06 22:07:38 -0800966 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
967 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -0700968 }
969}
970
971void
972ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
973{
974 // send msg
975 size_t size = msg.ByteSize();
976 char *buf = new char[size];
977 msg.SerializeToArray(buf, size);
978 if (!msg.IsInitialized())
979 {
980 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
981 abort();
982 }
983 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
984
985 delete buf;
986
987 m_lastMsgTime = time(NULL);
988
989 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
990 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
991 std::vector<Sync::MissingDataInfo> v;
992 v.push_back(mdi);
993 {
994 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
995 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
996 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
997 }
998}
999
1000void
1001ChatDialog::openInviteListDialog()
1002{
1003 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1004 m_inviteListDialog->show();
1005}
1006
1007void
1008ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1009{
1010 ndn::Name inviteeNamespace(invitee.toUtf8().constData());
1011 ndn::Ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
1012 sendInvitation(inviteeItem, isIntroducer);
1013}
1014
Yingdi Yu42372442013-11-06 18:43:31 -08001015void
1016ChatDialog::closeEvent(QCloseEvent *e)
1017{
1018 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001019 emit closeChatDialog(m_chatroomPrefix);
1020}
1021
1022
1023
Yingdi Yu42f66462013-10-31 17:38:22 -07001024
Yingdi Yu04842232013-10-23 14:03:09 -07001025#if WAF
1026#include "chatdialog.moc"
1027#include "chatdialog.cpp.moc"
1028#endif