blob: 10d97f28026a4c40d133f406c3e2bbc95bd5611a [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
21#include <ndn.cxx/security/identity/identity-manager.h>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070022#include <ndn.cxx/security/encryption/basic-encryption-manager.h>
Yingdi Yu42f66462013-10-31 17:38:22 -070023#include <sync-intro-certificate.h>
24#include <boost/random/random_device.hpp>
25#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070026#include "logging.h"
27#endif
28
Yingdi Yu04842232013-10-23 14:03:09 -070029using namespace std;
Yingdi Yu04842232013-10-23 14:03:09 -070030
Yingdi Yuc4d08d22013-10-23 23:07:29 -070031INIT_LOGGER("ChatDialog");
32
Yingdi Yu42f66462013-10-31 17:38:22 -070033static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
34
35Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
36Q_DECLARE_METATYPE(size_t)
37
38ChatDialog::ChatDialog(ndn::Ptr<ContactManager> contactManager,
39 const ndn::Name& chatroomPrefix,
40 const ndn::Name& localPrefix,
41 const ndn::Name& defaultIdentity,
Yingdi Yu42372442013-11-06 18:43:31 -080042 const std::string& nick,
43 bool trial,
Yingdi Yu04842232013-10-23 14:03:09 -070044 QWidget *parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070045: QDialog(parent)
46 , ui(new Ui::ChatDialog)
47 , m_contactManager(contactManager)
48 , m_chatroomPrefix(chatroomPrefix)
49 , m_localPrefix(localPrefix)
50 , m_defaultIdentity(defaultIdentity)
Yingdi Yub35b8652013-11-07 11:32:40 -080051 , m_invitationPolicyManager(ndn::Ptr<InvitationPolicyManager>(new InvitationPolicyManager(m_chatroomPrefix.get(-1).toUri(), m_defaultIdentity)))
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 Yu42f66462013-10-31 17:38:22 -070064 m_localChatPrefix = m_localPrefix;
Yingdi Yu42372442013-11-06 18:43:31 -080065 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -070066 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1));
67
68 m_session = time(NULL);
69 m_scene = new DigestTreeScene(this);
70
71 initializeSetting();
72 updateLabels();
73
74 ui->treeViewer->setScene(m_scene);
75 ui->treeViewer->hide();
76 m_scene->plot("Empty");
77 QRectF rect = m_scene->itemsBoundingRect();
78 m_scene->setSceneRect(rect);
79
80 m_rosterModel = new QStringListModel(this);
81 ui->listView->setModel(m_rosterModel);
82
Yingdi Yua0594092013-11-06 22:07:38 -080083 createActions();
84 createTrayIcon();
85
Yingdi Yu42f66462013-10-31 17:38:22 -070086 m_timer = new QTimer(this);
87
Yingdi Yu42372442013-11-06 18:43:31 -080088 setWrapper(trial);
Yingdi Yu42f66462013-10-31 17:38:22 -070089
90 connect(ui->inviteButton, SIGNAL(clicked()),
91 this, SLOT(openInviteListDialog()));
92 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
93 this, SLOT(sendInvitationWrapper(QString, bool)));
94 connect(ui->lineEdit, SIGNAL(returnPressed()),
95 this, SLOT(returnPressed()));
96 connect(ui->treeButton, SIGNAL(pressed()),
97 this, SLOT(treeButtonPressed()));
98 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
99 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
100 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
101 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
102 connect(m_timer, SIGNAL(timeout()),
103 this, SLOT(replot()));
104 connect(m_scene, SIGNAL(replot()),
105 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800106 connect(trayIcon, SIGNAL(messageClicked()),
107 this, SLOT(showNormal()));
108 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
109 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700110 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
111 this, SLOT(updateRosterList(QStringList)));
112
Yingdi Yu42f66462013-10-31 17:38:22 -0700113
114 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700115}
116
Yingdi Yu42f66462013-10-31 17:38:22 -0700117
Yingdi Yu04842232013-10-23 14:03:09 -0700118ChatDialog::~ChatDialog()
119{
Yingdi Yu42372442013-11-06 18:43:31 -0800120 if(m_sock != NULL)
121 {
122 sendLeave();
123 delete m_sock;
124 m_sock = NULL;
125 }
Yingdi Yua0594092013-11-06 22:07:38 -0800126 m_handler->shutdown();
Yingdi Yu04842232013-10-23 14:03:09 -0700127}
128
129void
Yingdi Yu42372442013-11-06 18:43:31 -0800130ChatDialog::setWrapper(bool trial)
Yingdi Yu04842232013-10-23 14:03:09 -0700131{
Yingdi Yu42f66462013-10-31 17:38:22 -0700132 m_identityManager = ndn::Ptr<ndn::security::IdentityManager>::Create();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700133
Yingdi Yu42f66462013-10-31 17:38:22 -0700134 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
135 m_syncPolicyManager = ndn::Ptr<SyncPolicyManager>(new SyncPolicyManager(m_defaultIdentity, certificateName, m_chatroomPrefix));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700136
Yingdi Yu42f66462013-10-31 17:38:22 -0700137 m_keychain = ndn::Ptr<ndn::security::Keychain>(new ndn::security::Keychain(m_identityManager, m_invitationPolicyManager, NULL));
138
Yingdi Yub29f78c2013-11-09 20:12:31 -0800139 try{
140 m_handler = ndn::Ptr<ndn::Wrapper>(new ndn::Wrapper(m_keychain));
141 }catch(ndn::Error::ndnOperation& e){
142 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
143 }
144
Yingdi Yu42372442013-11-06 18:43:31 -0800145
146 if(trial == true)
147 {
148 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(ndn::Name("/local/ndn/prefix")));
149 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onUnverified,
150 this,
151 _1),
152 boost::bind(&ChatDialog::onTimeout,
153 this,
154 _1,
155 _2),
156 boost::bind(&ChatDialog::onUnverified,
157 this,
158 _1)));
159
160 m_handler->sendInterest(interest, closure);
161 }
Yingdi Yu04842232013-10-23 14:03:09 -0700162}
163
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700164void
Yingdi Yu42f66462013-10-31 17:38:22 -0700165ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700166{
Yingdi Yu42372442013-11-06 18:43:31 -0800167 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu42f66462013-10-31 17:38:22 -0700168 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toUri()));
169 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
170 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
171 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
172}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700173
Yingdi Yu42f66462013-10-31 17:38:22 -0700174void
175ChatDialog::updateLabels()
176{
177 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
178 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
179 ui->infoLabel->setText(settingDisp);
180 QString prefixDisp;
181 if (m_user.getPrefix().startsWith("/private/local"))
182 {
183 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
184 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
185 }
186 else
187 {
188 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
189 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
190 }
191 ui->prefixLabel->setText(prefixDisp);
192}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700193
Yingdi Yu42f66462013-10-31 17:38:22 -0700194void
195ChatDialog::sendInvitation(ndn::Ptr<ContactItem> contact, bool isIntroducer)
196{
197 m_invitationPolicyManager->addTrustAnchor(contact->getSelfEndorseCertificate());
198
199 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
200
201 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700202 interestName.append(contact->getNameSpace());
203 interestName.append("chatroom");
204 interestName.append(m_chatroomPrefix.get(-1));
205 interestName.append("inviter-prefix");
206 interestName.append(m_localPrefix);
207 interestName.append("inviter");
208 interestName.append(certificateName);
209
210 string signedUri = interestName.toUri();
Yingdi Yu42f66462013-10-31 17:38:22 -0700211 ndn::Blob signedBlob(signedUri.c_str(), signedUri.size());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700212
Yingdi Yu42f66462013-10-31 17:38:22 -0700213 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = ndn::DynamicCast<const ndn::signature::Sha256WithRsa>(m_identityManager->signByCertificate(signedBlob, certificateName));
214 const ndn::Blob& sigBits = sha256sig->getSignatureBits();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700215
216 interestName.append(sigBits.buf(), sigBits.size());
Yingdi Yu42372442013-11-06 18:43:31 -0800217 interestName.appendVersion();
218
Yingdi Yu42f66462013-10-31 17:38:22 -0700219 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(interestName));
220 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onInviteReplyVerified,
221 this,
222 _1,
223 contact->getNameSpace(),
224 isIntroducer),
225 boost::bind(&ChatDialog::onInviteTimeout,
226 this,
227 _1,
228 _2,
229 contact->getNameSpace(),
230 7),
231 boost::bind(&ChatDialog::onUnverified,
232 this,
233 _1)));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700234
235 m_handler->sendInterest(interest, closure);
236}
237
238void
Yingdi Yu42f66462013-10-31 17:38:22 -0700239ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
240{ m_invitationPolicyManager->addTrustAnchor(selfEndorseCertificate); }
241
242void
243ChatDialog::addChatDataRule(const ndn::Name& prefix,
244 const ndn::security::IdentityCertificate& identityCertificate,
245 bool isIntroducer)
246{ m_syncPolicyManager->addChatDataRule(prefix, identityCertificate, isIntroducer); }
247
248void
Yingdi Yua0594092013-11-06 22:07:38 -0800249ChatDialog::publishIntroCert(const ndn::security::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700250{
Yingdi Yu42f66462013-10-31 17:38:22 -0700251 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
Yingdi Yua0594092013-11-06 22:07:38 -0800252 dskCertificate.getPublicKeyName(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700253 m_identityManager->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yua0594092013-11-06 22:07:38 -0800254 dskCertificate.getNotBefore(),
255 dskCertificate.getNotAfter(),
256 dskCertificate.getPublicKeyInfo(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700257 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
258 ndn::Name certName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800259 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yu42f66462013-10-31 17:38:22 -0700260 m_identityManager->signByCertificate(syncIntroCertificate, certName);
261 m_handler->putToNdnd(*syncIntroCertificate.encodeToWire());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700262}
263
264void
Yingdi Yu42f66462013-10-31 17:38:22 -0700265ChatDialog::invitationRejected(const ndn::Name& identity)
266{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800267 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700268}
269
270void
271ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::Ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700272{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800273 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700274 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = boost::dynamic_pointer_cast<const ndn::signature::Sha256WithRsa> (data->getSignature());
275 const ndn::Name & keyLocatorName = sha256sig->getKeyLocator().getKeyName();
276 ndn::Ptr<ndn::security::IdentityCertificate> dskCertificate = m_invitationPolicyManager->getValidatedDskCertificate(keyLocatorName);
Yingdi Yuc90deb12013-11-06 18:51:19 -0800277 m_syncPolicyManager->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800278 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700279}
280
281void
Yingdi Yu42f66462013-10-31 17:38:22 -0700282ChatDialog::onInviteReplyVerified(ndn::Ptr<ndn::Data> data, const ndn::Name& identity, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700283{
284 string content(data->content().buf(), data->content().size());
Yingdi Yu97936352013-11-08 14:13:42 -0800285 if(content == string("nack"))
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700286 invitationRejected(identity);
287 else
Yingdi Yu42f66462013-10-31 17:38:22 -0700288 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700289}
290
291void
Yingdi Yu42f66462013-10-31 17:38:22 -0700292ChatDialog::onInviteTimeout(ndn::Ptr<ndn::Closure> closure, ndn::Ptr<ndn::Interest> interest, const ndn::Name& identity, int retry)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700293{
294 if(retry > 0)
295 {
Yingdi Yu42f66462013-10-31 17:38:22 -0700296 ndn::Ptr<ndn::Closure> newClosure = ndn::Ptr<ndn::Closure>(new ndn::Closure(closure->m_dataCallback,
297 boost::bind(&ChatDialog::onInviteTimeout,
298 this,
299 _1,
300 _2,
301 identity,
302 retry - 1),
303 closure->m_unverifiedCallback,
304 closure->m_stepCount)
305 );
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700306 m_handler->sendInterest(interest, newClosure);
307 }
308 else
309 invitationRejected(identity);
310}
311
312void
Yingdi Yu42f66462013-10-31 17:38:22 -0700313ChatDialog::onUnverified(ndn::Ptr<ndn::Data> data)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700314{}
315
Yingdi Yu42f66462013-10-31 17:38:22 -0700316void
Yingdi Yu42372442013-11-06 18:43:31 -0800317ChatDialog::onTimeout(ndn::Ptr<ndn::Closure> closure,
318 ndn::Ptr<ndn::Interest> interest)
319{}
320
321void
Yingdi Yu42f66462013-10-31 17:38:22 -0700322ChatDialog::initializeSync()
323{
324
325 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
326 m_syncPolicyManager,
327 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
328 bind(&ChatDialog::processRemoveWrapper, this, _1));
329
330 usleep(100000);
331
332 QTimer::singleShot(600, this, SLOT(sendJoin()));
333 m_timer->start(FRESHNESS * 1000);
334 disableTreeDisplay();
335 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
336 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
337 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700338}
339
340void
341ChatDialog::returnPressed()
342{
343 QString text = ui->lineEdit->text();
344 if (text.isEmpty())
345 return;
346
347 ui->lineEdit->clear();
348
349 if (text.startsWith("boruoboluomi"))
350 {
351 summonReaper ();
352 // reapButton->show();
353 fitView();
354 return;
355 }
356
357 if (text.startsWith("minimanihong"))
358 {
359 // reapButton->hide();
360 fitView();
361 return;
362 }
363
364 SyncDemo::ChatMessage msg;
365 formChatMessage(text, msg);
366
367 appendMessage(msg);
368
369 sendMsg(msg);
370
371 fitView();
372}
373
374void
375ChatDialog::treeButtonPressed()
376{
377 if (ui->treeViewer->isVisible())
378 {
379 ui->treeViewer->hide();
380 ui->treeButton->setText("Show ChronoSync Tree");
381 }
382 else
383 {
384 ui->treeViewer->show();
385 ui->treeButton->setText("Hide ChronoSync Tree");
386 }
387
388 fitView();
389}
390
391void ChatDialog::disableTreeDisplay()
392{
393 ui->treeButton->setEnabled(false);
394 ui->treeViewer->hide();
395 fitView();
396}
397
398void ChatDialog::enableTreeDisplay()
399{
400 ui->treeButton->setEnabled(true);
401 // treeViewer->show();
402 // fitView();
403}
404
405void
406ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
407{
408 emit treeUpdated(v);
409 _LOG_DEBUG("<<< Tree update signal emitted");
410}
411
412void
413ChatDialog::processRemoveWrapper(std::string prefix)
414{
415 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
416}
417
418void
419ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
420{
421 _LOG_DEBUG("<<< processing Tree Update");
422
423 if (v.empty())
424 {
425 return;
426 }
427
428 // reflect the changes on digest tree
429 {
430 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
431 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
432 }
433
434 int n = v.size();
435 int totalMissingPackets = 0;
436 for (int i = 0; i < n; i++)
437 {
438 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
439 }
440
441 for (int i = 0; i < n; i++)
442 {
443 if (totalMissingPackets < 4)
444 {
445 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
446 {
447 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
448 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
449 }
450 }
451 else
452 {
453 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
454 }
455 }
456
457 // adjust the view
458 fitView();
459
460}
461
462void
463ChatDialog::processDataWrapper(ndn::Ptr<ndn::Data> data)
464{
465 string name = data->getName().toUri();
466 const char* buf = data->content().buf();
467 size_t len = data->content().size();
468
469 char *tempBuf = new char[len];
470 memcpy(tempBuf, buf, len);
471 emit dataReceived(name.c_str(), tempBuf, len, true, false);
472 _LOG_DEBUG("<<< " << name << " fetched");
473}
474
475void
476ChatDialog::processDataNoShowWrapper(ndn::Ptr<ndn::Data> data)
477{
478 string name = data->getName().toUri();
479 const char* buf = data->content().buf();
480 size_t len = data->content().size();
481
482 char *tempBuf = new char[len];
483 memcpy(tempBuf, buf, len);
484 emit dataReceived(name.c_str(), tempBuf, len, false, false);
485
486 // if (!m_historyInitialized)
487 // {
488 // fetchHistory(name);
489 // m_historyInitialized = true;
490 // }
491}
492
493// void
494// ChatDialog::fetchHistory(std::string name)
495// {
496
497// /****************************/
498// /* TODO: fix following part */
499// /****************************/
500// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
501// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
502// prefix += "/history";
503// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
504// // QString randomString = getRandomString();
505// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
506// // {
507// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
508// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
509// // }
510// }
511
512void
513ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
514{
515 SyncDemo::ChatMessage msg;
516 bool corrupted = false;
517 if (!msg.ParseFromArray(buf, len))
518 {
519 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
520 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
521 msg.set_from("inconnu");
522 msg.set_type(SyncDemo::ChatMessage::OTHER);
523 corrupted = true;
524 }
525
526 delete [] buf;
527 buf = NULL;
528
529 // display msg received from network
530 // we have to do so; this function is called by ccnd thread
531 // so if we call appendMsg directly
532 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
533 // the "cannonical" way to is use signal-slot
534 if (show && !corrupted)
535 {
536 appendMessage(msg, isHistory);
537 }
538
539 if (!isHistory)
540 {
541 // update the tree view
542 std::string stdStrName = name.toStdString();
543 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
544 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
545 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
546 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
547 {
548 processRemove(prefix.c_str());
549 }
550 else
551 {
552 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
553 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
554 }
555 }
556 fitView();
557}
558
559void
560ChatDialog::processRemove(QString prefix)
561{
562 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
563
564 bool removed = m_scene->removeNode(prefix);
565 if (removed)
566 {
567 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
568 m_scene->plot(m_sock->getRootDigest().c_str());
569 }
570}
571
572void
573ChatDialog::sendJoin()
574{
575 m_joined = true;
576 SyncDemo::ChatMessage msg;
577 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
578 sendMsg(msg);
579 boost::random::random_device rng;
580 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
581 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
582 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
583}
584
585void
586ChatDialog::sendHello()
587{
588 time_t now = time(NULL);
589 int elapsed = now - m_lastMsgTime;
590 if (elapsed >= m_randomizedInterval / 1000)
591 {
592 SyncDemo::ChatMessage msg;
593 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
594 sendMsg(msg);
595 boost::random::random_device rng;
596 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
597 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
598 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
599 }
600 else
601 {
602 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
603 }
604}
605
606void
607ChatDialog::sendLeave()
608{
609 SyncDemo::ChatMessage msg;
610 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
611 sendMsg(msg);
612 usleep(500000);
613 m_sock->remove(m_user.getPrefix().toStdString());
614 usleep(5000);
615 m_joined = false;
616 _LOG_DEBUG("Sync REMOVE signal sent");
617}
618
619void
620ChatDialog::replot()
621{
622 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
623 m_scene->plot(m_sock->getRootDigest().c_str());
624 fitView();
625}
626
627void
628ChatDialog::summonReaper()
629{
630 Sync::SyncLogic &logic = m_sock->getLogic ();
631 map<string, bool> branches = logic.getBranchPrefixes();
632 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
633
634 m_zombieList.clear();
635
636 QMapIterator<QString, DisplayUserPtr> it(roster);
637 map<string, bool>::iterator mapIt;
638 while(it.hasNext())
639 {
640 it.next();
641 DisplayUserPtr p = it.value();
642 if (p != DisplayUserNullPtr)
643 {
644 mapIt = branches.find(p->getPrefix().toStdString());
645 if (mapIt != branches.end())
646 {
647 mapIt->second = true;
648 }
649 }
650 }
651
652 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
653 {
654 // this is zombie. all active users should have been marked true
655 if (! mapIt->second)
656 {
657 m_zombieList.append(mapIt->first.c_str());
658 }
659 }
660
661 m_zombieIndex = 0;
662
663 // start reaping
664 reap();
665}
666
667void
668ChatDialog::reap()
669{
670 if (m_zombieIndex < m_zombieList.size())
671 {
672 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
673 m_sock->remove(prefix);
674 _LOG_DEBUG("Reaped: prefix = " << prefix);
675 m_zombieIndex++;
676 // reap again in 10 seconds
677 QTimer::singleShot(10000, this, SLOT(reap()));
678 }
679}
680
681void
682ChatDialog::updateRosterList(QStringList staleUserList)
683{
684 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
685 QStringList rosterList = m_scene->getRosterList();
686 m_rosterModel->setStringList(rosterList);
687 QString user;
688 QStringListIterator it(staleUserList);
689 while(it.hasNext())
690 {
691 std::string nick = it.next().toStdString();
692 if (nick.empty())
693 continue;
694
695 SyncDemo::ChatMessage msg;
696 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
697 msg.set_from(nick);
698 appendMessage(msg);
699 }
700}
701
702void
Yingdi Yua0594092013-11-06 22:07:38 -0800703ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
704{
705 switch (reason)
706 {
707 case QSystemTrayIcon::Trigger:
708 case QSystemTrayIcon::DoubleClick:
709 break;
710 case QSystemTrayIcon::MiddleClick:
711 // showMessage();
712 break;
713 default:;
714 }
715}
716
717
718void
719ChatDialog::messageClicked()
720{
721 this->showMaximized();
722}
723
724
725void
726ChatDialog::createActions()
727{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800728 minimizeAction = new QAction(tr("Mi&nimize"), this);
729 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800730
Yingdi Yu07b5b092013-11-07 17:00:54 -0800731 maximizeAction = new QAction(tr("Ma&ximize"), this);
732 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800733
Yingdi Yu07b5b092013-11-07 17:00:54 -0800734 restoreAction = new QAction(tr("&Restore"), this);
735 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800736
737 // settingsAction = new QAction(tr("Settings"), this);
738 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
739
740 // settingsAction->setMenuRole (QAction::PreferencesRole);
741
742 // updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
743 // connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
744
Yingdi Yu07b5b092013-11-07 17:00:54 -0800745 quitAction = new QAction(tr("Quit"), this);
746 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800747}
748
749void
750ChatDialog::createTrayIcon()
751{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800752 trayIconMenu = new QMenu(this);
753 trayIconMenu->addAction(minimizeAction);
754 trayIconMenu->addAction(maximizeAction);
755 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800756 // trayIconMenu->addSeparator();
757 // trayIconMenu->addAction(settingsAction);
758 // trayIconMenu->addSeparator();
759 // trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800760 trayIconMenu->addSeparator();
761 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800762
763 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800764 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800765
766 QIcon icon(":/images/icon_small.png");
767 trayIcon->setIcon(icon);
768 setWindowIcon(icon);
769 trayIcon->setToolTip("ChronoChat System Tray Icon");
770 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800771}
772
773
774void
Yingdi Yu42f66462013-10-31 17:38:22 -0700775ChatDialog::resizeEvent(QResizeEvent *e)
776{
777 fitView();
778}
779
780void
781ChatDialog::showEvent(QShowEvent *e)
782{
783 fitView();
784}
785
786void
787ChatDialog::fitView()
788{
789 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
790 QRectF rect = m_scene->itemsBoundingRect();
791 m_scene->setSceneRect(rect);
792 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
793}
794
795void
796ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
797 msg.set_from(m_user.getNick().toStdString());
798 msg.set_to(m_user.getChatroom().toStdString());
799 msg.set_data(text.toUtf8().constData());
800 time_t seconds = time(NULL);
801 msg.set_timestamp(seconds);
802 msg.set_type(SyncDemo::ChatMessage::CHAT);
803}
804
805void
806ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
807{
808 msg.set_from(m_user.getNick().toStdString());
809 msg.set_to(m_user.getChatroom().toStdString());
810 time_t seconds = time(NULL);
811 msg.set_timestamp(seconds);
812 msg.set_type(type);
813}
814
815void
Yingdi Yua0594092013-11-06 22:07:38 -0800816ChatDialog::changeEvent(QEvent *e)
817{
818 switch(e->type())
819 {
820 case QEvent::ActivationChange:
821 if (isActiveWindow())
822 {
823 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
824 }
825 break;
826 default:
827 break;
828 }
829}
830
831void
Yingdi Yu42f66462013-10-31 17:38:22 -0700832ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
833{
834 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
835
836 if (msg.type() == SyncDemo::ChatMessage::CHAT)
837 {
838
839 if (!msg.has_data())
840 {
841 return;
842 }
843
844 if (msg.from().empty() || msg.data().empty())
845 {
846 return;
847 }
848
849 if (!msg.has_timestamp())
850 {
851 return;
852 }
853
854 // if (m_history.size() == MAX_HISTORY_ENTRY)
855 // {
856 // m_history.dequeue();
857 // }
858
859 // m_history.enqueue(msg);
860
861 QTextCharFormat nickFormat;
862 nickFormat.setForeground(Qt::darkGreen);
863 nickFormat.setFontWeight(QFont::Bold);
864 nickFormat.setFontUnderline(true);
865 nickFormat.setUnderlineColor(Qt::gray);
866
867 QTextCursor cursor(ui->textEdit->textCursor());
868 cursor.movePosition(QTextCursor::End);
869 QTextTableFormat tableFormat;
870 tableFormat.setBorder(0);
871 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
872 QString from = QString("%1 ").arg(msg.from().c_str());
873 QTextTableCell fromCell = table->cellAt(0, 0);
874 fromCell.setFormat(nickFormat);
875 fromCell.firstCursorPosition().insertText(from);
876
877 time_t timestamp = msg.timestamp();
878 printTimeInCell(table, timestamp);
879
880 QTextCursor nextCursor(ui->textEdit->textCursor());
881 nextCursor.movePosition(QTextCursor::End);
882 table = nextCursor.insertTable(1, 1, tableFormat);
883 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
884 if (!isHistory)
885 {
886 showMessage(from, QString::fromUtf8(msg.data().c_str()));
887 }
888 }
889
890 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
891 {
892 QTextCharFormat nickFormat;
893 nickFormat.setForeground(Qt::gray);
894 nickFormat.setFontWeight(QFont::Bold);
895 nickFormat.setFontUnderline(true);
896 nickFormat.setUnderlineColor(Qt::gray);
897
898 QTextCursor cursor(ui->textEdit->textCursor());
899 cursor.movePosition(QTextCursor::End);
900 QTextTableFormat tableFormat;
901 tableFormat.setBorder(0);
902 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
903 QString action;
904 if (msg.type() == SyncDemo::ChatMessage::JOIN)
905 {
906 action = "enters room";
907 }
908 else
909 {
910 action = "leaves room";
911 }
912
913 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
914 QTextTableCell fromCell = table->cellAt(0, 0);
915 fromCell.setFormat(nickFormat);
916 fromCell.firstCursorPosition().insertText(from);
917
918 time_t timestamp = msg.timestamp();
919 printTimeInCell(table, timestamp);
920 }
921
922 QScrollBar *bar = ui->textEdit->verticalScrollBar();
923 bar->setValue(bar->maximum());
924}
925
926QString
927ChatDialog::formatTime(time_t timestamp)
928{
929 struct tm *tm_time = localtime(&timestamp);
930 int hour = tm_time->tm_hour;
931 QString amOrPM;
932 if (hour > 12)
933 {
934 hour -= 12;
935 amOrPM = "PM";
936 }
937 else
938 {
939 amOrPM = "AM";
940 if (hour == 0)
941 {
942 hour = 12;
943 }
944 }
945
946 char textTime[12];
947 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
948 return QString(textTime);
949}
950
951void
952ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
953{
954 QTextCharFormat timeFormat;
955 timeFormat.setForeground(Qt::gray);
956 timeFormat.setFontUnderline(true);
957 timeFormat.setUnderlineColor(Qt::gray);
958 QTextTableCell timeCell = table->cellAt(0, 1);
959 timeCell.setFormat(timeFormat);
960 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
961}
962
963void
964ChatDialog::showMessage(QString from, QString data)
965{
966 if (!isActiveWindow())
967 {
Yingdi Yua0594092013-11-06 22:07:38 -0800968 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
969 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -0700970 }
971}
972
973void
974ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
975{
976 // send msg
977 size_t size = msg.ByteSize();
978 char *buf = new char[size];
979 msg.SerializeToArray(buf, size);
980 if (!msg.IsInitialized())
981 {
982 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
983 abort();
984 }
985 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
986
987 delete buf;
988
989 m_lastMsgTime = time(NULL);
990
991 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
992 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
993 std::vector<Sync::MissingDataInfo> v;
994 v.push_back(mdi);
995 {
996 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
997 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
998 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
999 }
1000}
1001
1002void
1003ChatDialog::openInviteListDialog()
1004{
1005 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1006 m_inviteListDialog->show();
1007}
1008
1009void
1010ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1011{
1012 ndn::Name inviteeNamespace(invitee.toUtf8().constData());
1013 ndn::Ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
1014 sendInvitation(inviteeItem, isIntroducer);
1015}
1016
Yingdi Yu42372442013-11-06 18:43:31 -08001017void
1018ChatDialog::closeEvent(QCloseEvent *e)
1019{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001020 if (trayIcon->isVisible())
1021 {
1022 QMessageBox::information(this, tr("Chronos"),
1023 tr("The program will keep running in the "
1024 "system tray. To terminate the program"
1025 "choose <b>Quit</b> in the context memu"
1026 "of the system tray entry."));
1027 hide();
1028 e->ignore();
1029 }
1030}
1031
1032void
1033ChatDialog::quit()
1034{
Yingdi Yu42372442013-11-06 18:43:31 -08001035 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001036 emit closeChatDialog(m_chatroomPrefix);
1037}
1038
1039
1040
Yingdi Yu42f66462013-10-31 17:38:22 -07001041
Yingdi Yu04842232013-10-23 14:03:09 -07001042#if WAF
1043#include "chatdialog.moc"
1044#include "chatdialog.cpp.moc"
1045#endif