blob: 9046f2cfaffa5bb3468c83d4d49151b3425a4c8a [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
139 m_handler = ndn::Ptr<ndn::Wrapper>(new ndn::Wrapper(m_keychain));
Yingdi Yu42372442013-11-06 18:43:31 -0800140
141 if(trial == true)
142 {
143 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(ndn::Name("/local/ndn/prefix")));
144 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onUnverified,
145 this,
146 _1),
147 boost::bind(&ChatDialog::onTimeout,
148 this,
149 _1,
150 _2),
151 boost::bind(&ChatDialog::onUnverified,
152 this,
153 _1)));
154
155 m_handler->sendInterest(interest, closure);
156 }
Yingdi Yu04842232013-10-23 14:03:09 -0700157}
158
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700159void
Yingdi Yu42f66462013-10-31 17:38:22 -0700160ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700161{
Yingdi Yu42372442013-11-06 18:43:31 -0800162 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu42f66462013-10-31 17:38:22 -0700163 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toUri()));
164 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
165 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
166 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
167}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700168
Yingdi Yu42f66462013-10-31 17:38:22 -0700169void
170ChatDialog::updateLabels()
171{
172 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
173 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
174 ui->infoLabel->setText(settingDisp);
175 QString prefixDisp;
176 if (m_user.getPrefix().startsWith("/private/local"))
177 {
178 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
179 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
180 }
181 else
182 {
183 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
184 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
185 }
186 ui->prefixLabel->setText(prefixDisp);
187}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700188
Yingdi Yu42f66462013-10-31 17:38:22 -0700189void
190ChatDialog::sendInvitation(ndn::Ptr<ContactItem> contact, bool isIntroducer)
191{
192 m_invitationPolicyManager->addTrustAnchor(contact->getSelfEndorseCertificate());
193
194 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
195
196 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700197 interestName.append(contact->getNameSpace());
198 interestName.append("chatroom");
199 interestName.append(m_chatroomPrefix.get(-1));
200 interestName.append("inviter-prefix");
201 interestName.append(m_localPrefix);
202 interestName.append("inviter");
203 interestName.append(certificateName);
204
205 string signedUri = interestName.toUri();
Yingdi Yu42f66462013-10-31 17:38:22 -0700206 ndn::Blob signedBlob(signedUri.c_str(), signedUri.size());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700207
Yingdi Yu42f66462013-10-31 17:38:22 -0700208 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = ndn::DynamicCast<const ndn::signature::Sha256WithRsa>(m_identityManager->signByCertificate(signedBlob, certificateName));
209 const ndn::Blob& sigBits = sha256sig->getSignatureBits();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700210
211 interestName.append(sigBits.buf(), sigBits.size());
Yingdi Yu42372442013-11-06 18:43:31 -0800212 interestName.appendVersion();
213
Yingdi Yu42f66462013-10-31 17:38:22 -0700214 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(interestName));
215 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onInviteReplyVerified,
216 this,
217 _1,
218 contact->getNameSpace(),
219 isIntroducer),
220 boost::bind(&ChatDialog::onInviteTimeout,
221 this,
222 _1,
223 _2,
224 contact->getNameSpace(),
225 7),
226 boost::bind(&ChatDialog::onUnverified,
227 this,
228 _1)));
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700229
230 m_handler->sendInterest(interest, closure);
231}
232
233void
Yingdi Yu42f66462013-10-31 17:38:22 -0700234ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
235{ m_invitationPolicyManager->addTrustAnchor(selfEndorseCertificate); }
236
237void
238ChatDialog::addChatDataRule(const ndn::Name& prefix,
239 const ndn::security::IdentityCertificate& identityCertificate,
240 bool isIntroducer)
241{ m_syncPolicyManager->addChatDataRule(prefix, identityCertificate, isIntroducer); }
242
243void
Yingdi Yua0594092013-11-06 22:07:38 -0800244ChatDialog::publishIntroCert(const ndn::security::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700245{
Yingdi Yu42f66462013-10-31 17:38:22 -0700246 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
Yingdi Yua0594092013-11-06 22:07:38 -0800247 dskCertificate.getPublicKeyName(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700248 m_identityManager->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yua0594092013-11-06 22:07:38 -0800249 dskCertificate.getNotBefore(),
250 dskCertificate.getNotAfter(),
251 dskCertificate.getPublicKeyInfo(),
Yingdi Yu42f66462013-10-31 17:38:22 -0700252 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
253 ndn::Name certName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800254 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yu42f66462013-10-31 17:38:22 -0700255 m_identityManager->signByCertificate(syncIntroCertificate, certName);
256 m_handler->putToNdnd(*syncIntroCertificate.encodeToWire());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700257}
258
259void
Yingdi Yu42f66462013-10-31 17:38:22 -0700260ChatDialog::invitationRejected(const ndn::Name& identity)
261{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800262 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700263}
264
265void
266ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::Ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700267{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800268 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yu42f66462013-10-31 17:38:22 -0700269 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = boost::dynamic_pointer_cast<const ndn::signature::Sha256WithRsa> (data->getSignature());
270 const ndn::Name & keyLocatorName = sha256sig->getKeyLocator().getKeyName();
271 ndn::Ptr<ndn::security::IdentityCertificate> dskCertificate = m_invitationPolicyManager->getValidatedDskCertificate(keyLocatorName);
Yingdi Yuc90deb12013-11-06 18:51:19 -0800272 m_syncPolicyManager->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800273 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700274}
275
276void
Yingdi Yu42f66462013-10-31 17:38:22 -0700277ChatDialog::onInviteReplyVerified(ndn::Ptr<ndn::Data> data, const ndn::Name& identity, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700278{
279 string content(data->content().buf(), data->content().size());
Yingdi Yu97936352013-11-08 14:13:42 -0800280 if(content == string("nack"))
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700281 invitationRejected(identity);
282 else
Yingdi Yu42f66462013-10-31 17:38:22 -0700283 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700284}
285
286void
Yingdi Yu42f66462013-10-31 17:38:22 -0700287ChatDialog::onInviteTimeout(ndn::Ptr<ndn::Closure> closure, ndn::Ptr<ndn::Interest> interest, const ndn::Name& identity, int retry)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700288{
289 if(retry > 0)
290 {
Yingdi Yu42f66462013-10-31 17:38:22 -0700291 ndn::Ptr<ndn::Closure> newClosure = ndn::Ptr<ndn::Closure>(new ndn::Closure(closure->m_dataCallback,
292 boost::bind(&ChatDialog::onInviteTimeout,
293 this,
294 _1,
295 _2,
296 identity,
297 retry - 1),
298 closure->m_unverifiedCallback,
299 closure->m_stepCount)
300 );
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700301 m_handler->sendInterest(interest, newClosure);
302 }
303 else
304 invitationRejected(identity);
305}
306
307void
Yingdi Yu42f66462013-10-31 17:38:22 -0700308ChatDialog::onUnverified(ndn::Ptr<ndn::Data> data)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700309{}
310
Yingdi Yu42f66462013-10-31 17:38:22 -0700311void
Yingdi Yu42372442013-11-06 18:43:31 -0800312ChatDialog::onTimeout(ndn::Ptr<ndn::Closure> closure,
313 ndn::Ptr<ndn::Interest> interest)
314{}
315
316void
Yingdi Yu42f66462013-10-31 17:38:22 -0700317ChatDialog::initializeSync()
318{
319
320 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
321 m_syncPolicyManager,
322 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
323 bind(&ChatDialog::processRemoveWrapper, this, _1));
324
325 usleep(100000);
326
327 QTimer::singleShot(600, this, SLOT(sendJoin()));
328 m_timer->start(FRESHNESS * 1000);
329 disableTreeDisplay();
330 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
331 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
332 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700333}
334
335void
336ChatDialog::returnPressed()
337{
338 QString text = ui->lineEdit->text();
339 if (text.isEmpty())
340 return;
341
342 ui->lineEdit->clear();
343
344 if (text.startsWith("boruoboluomi"))
345 {
346 summonReaper ();
347 // reapButton->show();
348 fitView();
349 return;
350 }
351
352 if (text.startsWith("minimanihong"))
353 {
354 // reapButton->hide();
355 fitView();
356 return;
357 }
358
359 SyncDemo::ChatMessage msg;
360 formChatMessage(text, msg);
361
362 appendMessage(msg);
363
364 sendMsg(msg);
365
366 fitView();
367}
368
369void
370ChatDialog::treeButtonPressed()
371{
372 if (ui->treeViewer->isVisible())
373 {
374 ui->treeViewer->hide();
375 ui->treeButton->setText("Show ChronoSync Tree");
376 }
377 else
378 {
379 ui->treeViewer->show();
380 ui->treeButton->setText("Hide ChronoSync Tree");
381 }
382
383 fitView();
384}
385
386void ChatDialog::disableTreeDisplay()
387{
388 ui->treeButton->setEnabled(false);
389 ui->treeViewer->hide();
390 fitView();
391}
392
393void ChatDialog::enableTreeDisplay()
394{
395 ui->treeButton->setEnabled(true);
396 // treeViewer->show();
397 // fitView();
398}
399
400void
401ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
402{
403 emit treeUpdated(v);
404 _LOG_DEBUG("<<< Tree update signal emitted");
405}
406
407void
408ChatDialog::processRemoveWrapper(std::string prefix)
409{
410 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
411}
412
413void
414ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
415{
416 _LOG_DEBUG("<<< processing Tree Update");
417
418 if (v.empty())
419 {
420 return;
421 }
422
423 // reflect the changes on digest tree
424 {
425 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
426 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
427 }
428
429 int n = v.size();
430 int totalMissingPackets = 0;
431 for (int i = 0; i < n; i++)
432 {
433 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
434 }
435
436 for (int i = 0; i < n; i++)
437 {
438 if (totalMissingPackets < 4)
439 {
440 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
441 {
442 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
443 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
444 }
445 }
446 else
447 {
448 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
449 }
450 }
451
452 // adjust the view
453 fitView();
454
455}
456
457void
458ChatDialog::processDataWrapper(ndn::Ptr<ndn::Data> data)
459{
460 string name = data->getName().toUri();
461 const char* buf = data->content().buf();
462 size_t len = data->content().size();
463
464 char *tempBuf = new char[len];
465 memcpy(tempBuf, buf, len);
466 emit dataReceived(name.c_str(), tempBuf, len, true, false);
467 _LOG_DEBUG("<<< " << name << " fetched");
468}
469
470void
471ChatDialog::processDataNoShowWrapper(ndn::Ptr<ndn::Data> data)
472{
473 string name = data->getName().toUri();
474 const char* buf = data->content().buf();
475 size_t len = data->content().size();
476
477 char *tempBuf = new char[len];
478 memcpy(tempBuf, buf, len);
479 emit dataReceived(name.c_str(), tempBuf, len, false, false);
480
481 // if (!m_historyInitialized)
482 // {
483 // fetchHistory(name);
484 // m_historyInitialized = true;
485 // }
486}
487
488// void
489// ChatDialog::fetchHistory(std::string name)
490// {
491
492// /****************************/
493// /* TODO: fix following part */
494// /****************************/
495// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
496// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
497// prefix += "/history";
498// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
499// // QString randomString = getRandomString();
500// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
501// // {
502// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
503// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
504// // }
505// }
506
507void
508ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
509{
510 SyncDemo::ChatMessage msg;
511 bool corrupted = false;
512 if (!msg.ParseFromArray(buf, len))
513 {
514 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
515 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
516 msg.set_from("inconnu");
517 msg.set_type(SyncDemo::ChatMessage::OTHER);
518 corrupted = true;
519 }
520
521 delete [] buf;
522 buf = NULL;
523
524 // display msg received from network
525 // we have to do so; this function is called by ccnd thread
526 // so if we call appendMsg directly
527 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
528 // the "cannonical" way to is use signal-slot
529 if (show && !corrupted)
530 {
531 appendMessage(msg, isHistory);
532 }
533
534 if (!isHistory)
535 {
536 // update the tree view
537 std::string stdStrName = name.toStdString();
538 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
539 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
540 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
541 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
542 {
543 processRemove(prefix.c_str());
544 }
545 else
546 {
547 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
548 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
549 }
550 }
551 fitView();
552}
553
554void
555ChatDialog::processRemove(QString prefix)
556{
557 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
558
559 bool removed = m_scene->removeNode(prefix);
560 if (removed)
561 {
562 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
563 m_scene->plot(m_sock->getRootDigest().c_str());
564 }
565}
566
567void
568ChatDialog::sendJoin()
569{
570 m_joined = true;
571 SyncDemo::ChatMessage msg;
572 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
573 sendMsg(msg);
574 boost::random::random_device rng;
575 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
576 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
577 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
578}
579
580void
581ChatDialog::sendHello()
582{
583 time_t now = time(NULL);
584 int elapsed = now - m_lastMsgTime;
585 if (elapsed >= m_randomizedInterval / 1000)
586 {
587 SyncDemo::ChatMessage msg;
588 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
589 sendMsg(msg);
590 boost::random::random_device rng;
591 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
592 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
593 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
594 }
595 else
596 {
597 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
598 }
599}
600
601void
602ChatDialog::sendLeave()
603{
604 SyncDemo::ChatMessage msg;
605 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
606 sendMsg(msg);
607 usleep(500000);
608 m_sock->remove(m_user.getPrefix().toStdString());
609 usleep(5000);
610 m_joined = false;
611 _LOG_DEBUG("Sync REMOVE signal sent");
612}
613
614void
615ChatDialog::replot()
616{
617 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
618 m_scene->plot(m_sock->getRootDigest().c_str());
619 fitView();
620}
621
622void
623ChatDialog::summonReaper()
624{
625 Sync::SyncLogic &logic = m_sock->getLogic ();
626 map<string, bool> branches = logic.getBranchPrefixes();
627 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
628
629 m_zombieList.clear();
630
631 QMapIterator<QString, DisplayUserPtr> it(roster);
632 map<string, bool>::iterator mapIt;
633 while(it.hasNext())
634 {
635 it.next();
636 DisplayUserPtr p = it.value();
637 if (p != DisplayUserNullPtr)
638 {
639 mapIt = branches.find(p->getPrefix().toStdString());
640 if (mapIt != branches.end())
641 {
642 mapIt->second = true;
643 }
644 }
645 }
646
647 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
648 {
649 // this is zombie. all active users should have been marked true
650 if (! mapIt->second)
651 {
652 m_zombieList.append(mapIt->first.c_str());
653 }
654 }
655
656 m_zombieIndex = 0;
657
658 // start reaping
659 reap();
660}
661
662void
663ChatDialog::reap()
664{
665 if (m_zombieIndex < m_zombieList.size())
666 {
667 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
668 m_sock->remove(prefix);
669 _LOG_DEBUG("Reaped: prefix = " << prefix);
670 m_zombieIndex++;
671 // reap again in 10 seconds
672 QTimer::singleShot(10000, this, SLOT(reap()));
673 }
674}
675
676void
677ChatDialog::updateRosterList(QStringList staleUserList)
678{
679 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
680 QStringList rosterList = m_scene->getRosterList();
681 m_rosterModel->setStringList(rosterList);
682 QString user;
683 QStringListIterator it(staleUserList);
684 while(it.hasNext())
685 {
686 std::string nick = it.next().toStdString();
687 if (nick.empty())
688 continue;
689
690 SyncDemo::ChatMessage msg;
691 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
692 msg.set_from(nick);
693 appendMessage(msg);
694 }
695}
696
697void
Yingdi Yua0594092013-11-06 22:07:38 -0800698ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
699{
700 switch (reason)
701 {
702 case QSystemTrayIcon::Trigger:
703 case QSystemTrayIcon::DoubleClick:
704 break;
705 case QSystemTrayIcon::MiddleClick:
706 // showMessage();
707 break;
708 default:;
709 }
710}
711
712
713void
714ChatDialog::messageClicked()
715{
716 this->showMaximized();
717}
718
719
720void
721ChatDialog::createActions()
722{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800723 minimizeAction = new QAction(tr("Mi&nimize"), this);
724 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800725
Yingdi Yu07b5b092013-11-07 17:00:54 -0800726 maximizeAction = new QAction(tr("Ma&ximize"), this);
727 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800728
Yingdi Yu07b5b092013-11-07 17:00:54 -0800729 restoreAction = new QAction(tr("&Restore"), this);
730 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800731
732 // settingsAction = new QAction(tr("Settings"), this);
733 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
734
735 // settingsAction->setMenuRole (QAction::PreferencesRole);
736
737 // updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
738 // connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
739
Yingdi Yu07b5b092013-11-07 17:00:54 -0800740 quitAction = new QAction(tr("Quit"), this);
741 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800742}
743
744void
745ChatDialog::createTrayIcon()
746{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800747 trayIconMenu = new QMenu(this);
748 trayIconMenu->addAction(minimizeAction);
749 trayIconMenu->addAction(maximizeAction);
750 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800751 // trayIconMenu->addSeparator();
752 // trayIconMenu->addAction(settingsAction);
753 // trayIconMenu->addSeparator();
754 // trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800755 trayIconMenu->addSeparator();
756 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800757
758 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800759 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800760
761 QIcon icon(":/images/icon_small.png");
762 trayIcon->setIcon(icon);
763 setWindowIcon(icon);
764 trayIcon->setToolTip("ChronoChat System Tray Icon");
765 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800766}
767
768
769void
Yingdi Yu42f66462013-10-31 17:38:22 -0700770ChatDialog::resizeEvent(QResizeEvent *e)
771{
772 fitView();
773}
774
775void
776ChatDialog::showEvent(QShowEvent *e)
777{
778 fitView();
779}
780
781void
782ChatDialog::fitView()
783{
784 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
785 QRectF rect = m_scene->itemsBoundingRect();
786 m_scene->setSceneRect(rect);
787 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
788}
789
790void
791ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
792 msg.set_from(m_user.getNick().toStdString());
793 msg.set_to(m_user.getChatroom().toStdString());
794 msg.set_data(text.toUtf8().constData());
795 time_t seconds = time(NULL);
796 msg.set_timestamp(seconds);
797 msg.set_type(SyncDemo::ChatMessage::CHAT);
798}
799
800void
801ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
802{
803 msg.set_from(m_user.getNick().toStdString());
804 msg.set_to(m_user.getChatroom().toStdString());
805 time_t seconds = time(NULL);
806 msg.set_timestamp(seconds);
807 msg.set_type(type);
808}
809
810void
Yingdi Yua0594092013-11-06 22:07:38 -0800811ChatDialog::changeEvent(QEvent *e)
812{
813 switch(e->type())
814 {
815 case QEvent::ActivationChange:
816 if (isActiveWindow())
817 {
818 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
819 }
820 break;
821 default:
822 break;
823 }
824}
825
826void
Yingdi Yu42f66462013-10-31 17:38:22 -0700827ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
828{
829 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
830
831 if (msg.type() == SyncDemo::ChatMessage::CHAT)
832 {
833
834 if (!msg.has_data())
835 {
836 return;
837 }
838
839 if (msg.from().empty() || msg.data().empty())
840 {
841 return;
842 }
843
844 if (!msg.has_timestamp())
845 {
846 return;
847 }
848
849 // if (m_history.size() == MAX_HISTORY_ENTRY)
850 // {
851 // m_history.dequeue();
852 // }
853
854 // m_history.enqueue(msg);
855
856 QTextCharFormat nickFormat;
857 nickFormat.setForeground(Qt::darkGreen);
858 nickFormat.setFontWeight(QFont::Bold);
859 nickFormat.setFontUnderline(true);
860 nickFormat.setUnderlineColor(Qt::gray);
861
862 QTextCursor cursor(ui->textEdit->textCursor());
863 cursor.movePosition(QTextCursor::End);
864 QTextTableFormat tableFormat;
865 tableFormat.setBorder(0);
866 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
867 QString from = QString("%1 ").arg(msg.from().c_str());
868 QTextTableCell fromCell = table->cellAt(0, 0);
869 fromCell.setFormat(nickFormat);
870 fromCell.firstCursorPosition().insertText(from);
871
872 time_t timestamp = msg.timestamp();
873 printTimeInCell(table, timestamp);
874
875 QTextCursor nextCursor(ui->textEdit->textCursor());
876 nextCursor.movePosition(QTextCursor::End);
877 table = nextCursor.insertTable(1, 1, tableFormat);
878 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
879 if (!isHistory)
880 {
881 showMessage(from, QString::fromUtf8(msg.data().c_str()));
882 }
883 }
884
885 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
886 {
887 QTextCharFormat nickFormat;
888 nickFormat.setForeground(Qt::gray);
889 nickFormat.setFontWeight(QFont::Bold);
890 nickFormat.setFontUnderline(true);
891 nickFormat.setUnderlineColor(Qt::gray);
892
893 QTextCursor cursor(ui->textEdit->textCursor());
894 cursor.movePosition(QTextCursor::End);
895 QTextTableFormat tableFormat;
896 tableFormat.setBorder(0);
897 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
898 QString action;
899 if (msg.type() == SyncDemo::ChatMessage::JOIN)
900 {
901 action = "enters room";
902 }
903 else
904 {
905 action = "leaves room";
906 }
907
908 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
909 QTextTableCell fromCell = table->cellAt(0, 0);
910 fromCell.setFormat(nickFormat);
911 fromCell.firstCursorPosition().insertText(from);
912
913 time_t timestamp = msg.timestamp();
914 printTimeInCell(table, timestamp);
915 }
916
917 QScrollBar *bar = ui->textEdit->verticalScrollBar();
918 bar->setValue(bar->maximum());
919}
920
921QString
922ChatDialog::formatTime(time_t timestamp)
923{
924 struct tm *tm_time = localtime(&timestamp);
925 int hour = tm_time->tm_hour;
926 QString amOrPM;
927 if (hour > 12)
928 {
929 hour -= 12;
930 amOrPM = "PM";
931 }
932 else
933 {
934 amOrPM = "AM";
935 if (hour == 0)
936 {
937 hour = 12;
938 }
939 }
940
941 char textTime[12];
942 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
943 return QString(textTime);
944}
945
946void
947ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
948{
949 QTextCharFormat timeFormat;
950 timeFormat.setForeground(Qt::gray);
951 timeFormat.setFontUnderline(true);
952 timeFormat.setUnderlineColor(Qt::gray);
953 QTextTableCell timeCell = table->cellAt(0, 1);
954 timeCell.setFormat(timeFormat);
955 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
956}
957
958void
959ChatDialog::showMessage(QString from, QString data)
960{
961 if (!isActiveWindow())
962 {
Yingdi Yua0594092013-11-06 22:07:38 -0800963 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
964 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -0700965 }
966}
967
968void
969ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
970{
971 // send msg
972 size_t size = msg.ByteSize();
973 char *buf = new char[size];
974 msg.SerializeToArray(buf, size);
975 if (!msg.IsInitialized())
976 {
977 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
978 abort();
979 }
980 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
981
982 delete buf;
983
984 m_lastMsgTime = time(NULL);
985
986 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
987 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
988 std::vector<Sync::MissingDataInfo> v;
989 v.push_back(mdi);
990 {
991 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
992 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
993 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
994 }
995}
996
997void
998ChatDialog::openInviteListDialog()
999{
1000 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1001 m_inviteListDialog->show();
1002}
1003
1004void
1005ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1006{
1007 ndn::Name inviteeNamespace(invitee.toUtf8().constData());
1008 ndn::Ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
1009 sendInvitation(inviteeItem, isIntroducer);
1010}
1011
Yingdi Yu42372442013-11-06 18:43:31 -08001012void
1013ChatDialog::closeEvent(QCloseEvent *e)
1014{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001015 if (trayIcon->isVisible())
1016 {
1017 QMessageBox::information(this, tr("Chronos"),
1018 tr("The program will keep running in the "
1019 "system tray. To terminate the program"
1020 "choose <b>Quit</b> in the context memu"
1021 "of the system tray entry."));
1022 hide();
1023 e->ignore();
1024 }
1025}
1026
1027void
1028ChatDialog::quit()
1029{
Yingdi Yu42372442013-11-06 18:43:31 -08001030 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001031 emit closeChatDialog(m_chatroomPrefix);
1032}
1033
1034
1035
Yingdi Yu42f66462013-10-31 17:38:22 -07001036
Yingdi Yu04842232013-10-23 14:03:09 -07001037#if WAF
1038#include "chatdialog.moc"
1039#include "chatdialog.cpp.moc"
1040#endif