blob: baa9f78456d0571f5022fc40f010f71bd556d172 [file] [log] [blame]
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001/* -*- 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 *
8 * Author: Zhenkai Zhu <zhenkai@cs.ucla.edu>
9 * Alexander Afanasyev <alexander.afanasyev@ucla.edu>
10 * Yingdi Yu <yingdi@cs.ucla.edu>
11 */
12
13#include "chat-dialog.h"
14#include "ui_chat-dialog.h"
15
16#include <QScrollBar>
17#include <QMessageBox>
18#include <QCloseEvent>
19
20#ifndef Q_MOC_RUN
21#include <sync-intro-certificate.h>
22#include <boost/random/random_device.hpp>
23#include <boost/random/uniform_int_distribution.hpp>
24#include <ndn-cpp-dev/util/random.hpp>
25#include <cryptopp/hex.h>
26#include <cryptopp/files.h>
27#include "logging.h"
28#endif
29
30using namespace std;
31using namespace ndn;
32using namespace chronos;
33
34INIT_LOGGER("ChatDialog");
35
36static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
37static const uint8_t CHRONOS_RP_SEPARATOR[2] = {0xF0, 0x2E}; // %F0.
38
39Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
Yingdi Yu233a9722014-03-07 15:47:09 -080040Q_DECLARE_METATYPE(ndn::shared_ptr<const ndn::Data>)
Yingdi Yu348f5ea2014-03-01 14:47:25 -080041Q_DECLARE_METATYPE(ndn::Interest)
42Q_DECLARE_METATYPE(size_t)
43
44ChatDialog::ChatDialog(ContactManager* contactManager,
45 shared_ptr<Face> face,
46 const IdentityCertificate& myCertificate,
47 const Name& chatroomPrefix,
48 const Name& localPrefix,
49 const string& nick,
50 bool withSecurity,
51 QWidget* parent)
52 : QDialog(parent)
53 , ui(new Ui::ChatDialog)
54 , m_contactManager(contactManager)
55 , m_face(face)
56 , m_myCertificate(myCertificate)
57 , m_chatroomName(chatroomPrefix.get(-1).toEscapedString())
58 , m_chatroomPrefix(chatroomPrefix)
59 , m_localPrefix(localPrefix)
60 , m_useRoutablePrefix(false)
61 , m_nick(nick)
62 , m_lastMsgTime(time::now())
63 , m_joined(false)
64 , m_sock(NULL)
65 , m_session(static_cast<uint64_t>(time::now()))
66 , m_inviteListDialog(new InviteListDialog)
67{
68 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
Yingdi Yu233a9722014-03-07 15:47:09 -080069 qRegisterMetaType<ndn::shared_ptr<const ndn::Data> >("ndn.DataPtr");
Yingdi Yu348f5ea2014-03-01 14:47:25 -080070 qRegisterMetaType<ndn::Interest>("ndn.Interest");
71 qRegisterMetaType<size_t>("size_t");
72
73 m_scene = new DigestTreeScene(this);
74 m_rosterModel = new QStringListModel(this);
75 m_timer = new QTimer(this);
76
77 ui->setupUi(this);
78 ui->treeViewer->setScene(m_scene);
79 m_scene->setSceneRect(m_scene->itemsBoundingRect());
80 ui->treeViewer->hide();
81 ui->listView->setModel(m_rosterModel);
82
83 m_identity = IdentityCertificate::certificateNameToPublicKeyName(m_myCertificate.getName()).getPrefix(-1);
84 updatePrefix();
85 updateLabels();
86
87 m_scene->setCurrentPrefix(QString(m_localChatPrefix.toUri().c_str()));
88 m_scene->plot("Empty");
89
90 connect(ui->lineEdit, SIGNAL(returnPressed()),
91 this, SLOT(onReturnPressed()));
92 connect(ui->treeButton, SIGNAL(pressed()),
93 this, SLOT(onTreeButtonPressed()));
94 connect(m_scene, SIGNAL(replot()),
95 this, SLOT(onReplot()));
96 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
97 this, SLOT(onRosterChanged(QStringList)));
98 connect(m_timer, SIGNAL(timeout()),
99 this, SLOT(onReplot()));
100
Yingdi Yu233a9722014-03-07 15:47:09 -0800101 connect(this, SIGNAL(processData(const ndn::shared_ptr<const ndn::Data>&, bool, bool)),
102 this, SLOT(onProcessData(const ndn::shared_ptr<const ndn::Data>&, bool, bool)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800103 connect(this, SIGNAL(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)),
104 this, SLOT(onProcessTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800105 connect(this, SIGNAL(reply(const ndn::Interest&, const ndn::shared_ptr<const ndn::Data>&, size_t, bool)),
106 this, SLOT(onReply(const ndn::Interest&, const ndn::shared_ptr<const ndn::Data>&, size_t, bool)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800107 connect(this, SIGNAL(replyTimeout(const ndn::Interest&, size_t)),
108 this, SLOT(onReplyTimeout(const ndn::Interest&, size_t)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800109 connect(this, SIGNAL(introCert(const ndn::Interest&, const ndn::shared_ptr<const ndn::Data>&)),
110 this, SLOT(onIntroCert(const ndn::Interest&, const ndn::shared_ptr<const ndn::Data>&)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800111 connect(this, SIGNAL(introCertTimeout(const ndn::Interest&, int, const QString&)),
112 this, SLOT(onIntroCertTimeout(const ndn::Interest&, int, const QString&)));
113
114 if(withSecurity)
115 {
116
117 m_invitationValidator = make_shared<chronos::ValidatorInvitation>();
118 m_dataRule = make_shared<SecRuleRelative>("([^<CHRONOCHAT-DATA>]*)<CHRONOCHAT-DATA><>",
119 "^([^<KEY>]*)<KEY>(<>*)<><ID-CERT>$",
120 "==", "\\1", "\\1", true);
121
122 ui->inviteButton->setEnabled(true);
123 connect(ui->inviteButton, SIGNAL(clicked()),
124 this, SLOT(onInviteListDialogRequested()));
125 connect(m_inviteListDialog, SIGNAL(sendInvitation(const QString&)),
126 this, SLOT(onSendInvitation(const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800127 connect(this, SIGNAL(waitForContactList()),
128 m_contactManager, SLOT(onWaitForContactList()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800129 connect(m_contactManager, SIGNAL(contactAliasListReady(const QStringList&)),
130 m_inviteListDialog, SLOT(onContactAliasListReady(const QStringList&)));
131 connect(m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
132 m_inviteListDialog, SLOT(onContactIdListReady(const QStringList&)));
133 }
134
135 initializeSync();
136}
137
138
139ChatDialog::~ChatDialog()
140{
141 if(m_certListPrefixId)
142 m_face->unsetInterestFilter(m_certListPrefixId);
143
144 if(m_certSinglePrefixId)
145 m_face->unsetInterestFilter(m_certSinglePrefixId);
146
147 if(m_sock != NULL)
148 {
149 sendLeave();
150 delete m_sock;
151 m_sock = NULL;
152 }
153}
154
155// public methods:
156void
157ChatDialog::addSyncAnchor(const Invitation& invitation)
158{
Yingdi Yu233a9722014-03-07 15:47:09 -0800159 _LOG_DEBUG("Add sync anchor from invation");
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800160 // Add inviter certificate as trust anchor.
161 m_sock->addParticipant(invitation.getInviterCertificate());
162
163 // Ask inviter for IntroCertificate
164 Name inviterNameSpace = IdentityCertificate::certificateNameToPublicKeyName(invitation.getInviterCertificate().getName()).getPrefix(-1);
165 fetchIntroCert(inviterNameSpace, invitation.getInviterRoutingPrefix());
166}
167
168void
169ChatDialog::processTreeUpdateWrapper(const vector<Sync::MissingDataInfo>& v, Sync::SyncSocket *sock)
170{
171 emit processTreeUpdate(v);
172 _LOG_DEBUG("<<< Tree update signal emitted");
173}
174
175void
176ChatDialog::processDataWrapper(const shared_ptr<const Data>& data)
177{
Yingdi Yu233a9722014-03-07 15:47:09 -0800178 emit processData(data, true, false);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800179 _LOG_DEBUG("<<< " << data->getName() << " fetched");
180}
181
182void
183ChatDialog::processDataNoShowWrapper(const shared_ptr<const Data>& data)
184{
Yingdi Yu233a9722014-03-07 15:47:09 -0800185 emit processData(data, false, false);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800186}
187
188void
189ChatDialog::processRemoveWrapper(string prefix)
190{
191 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
192}
193
194// protected methods:
195void
196ChatDialog::closeEvent(QCloseEvent *e)
197{
198 QMessageBox::information(this, tr("ChronoChat"),
199 tr("The chatroom will keep running in the "
200 "system tray. To close the chatroom, "
201 "choose <b>Close chatroom</b> in the "
202 "context memu of the system tray entry."));
203 hide();
204 e->ignore();
205}
206
207void
Yingdi Yu233a9722014-03-07 15:47:09 -0800208ChatDialog::changeEvent(QEvent *e)
209{
210 switch(e->type())
211 {
212 case QEvent::ActivationChange:
213 if (isActiveWindow())
214 {
215 emit resetIcon();
216 }
217 break;
218 default:
219 break;
220 }
221}
222
223void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800224ChatDialog::resizeEvent(QResizeEvent *e)
225{
226 fitView();
227}
228
229void
230ChatDialog::showEvent(QShowEvent *e)
231{
232 fitView();
233}
234
235// private methods:
236void
237ChatDialog::updatePrefix()
238{
239 m_certListPrefix.clear();
240 m_certSinglePrefix.clear();
241 m_localChatPrefix.clear();
242 m_chatPrefix.clear();
243 m_chatPrefix.append(m_identity).append("CHRONOCHAT-DATA").append(m_chatroomName).append(getRandomString());
244 if(!m_localPrefix.isPrefixOf(m_identity))
245 {
246 m_useRoutablePrefix = true;
247 m_certListPrefix.append(m_localPrefix).append(CHRONOS_RP_SEPARATOR, 2);
248 m_certSinglePrefix.append(m_localPrefix).append(CHRONOS_RP_SEPARATOR, 2);
249 m_localChatPrefix.append(m_localPrefix).append(CHRONOS_RP_SEPARATOR, 2);
250 }
251 m_certListPrefix.append(m_identity).append("CHRONOCHAT-CERT-LIST").append(m_chatroomName);
252 m_certSinglePrefix.append(m_identity).append("CHRONOCHAT-CERT-SINGLE").append(m_chatroomName);
253 m_localChatPrefix.append(m_chatPrefix);
254
255 if(m_certListPrefixId)
256 m_face->unsetInterestFilter(m_certListPrefixId);
257 m_certListPrefixId = m_face->setInterestFilter (m_certListPrefix,
258 bind(&ChatDialog::onCertListInterest, this, _1, _2),
259 bind(&ChatDialog::onCertListRegisterFailed, this, _1, _2));
260
261 if(m_certSinglePrefixId)
262 m_face->unsetInterestFilter(m_certSinglePrefixId);
263 m_certSinglePrefixId = m_face->setInterestFilter (m_certSinglePrefix,
264 bind(&ChatDialog::onCertSingleInterest, this, _1, _2),
265 bind(&ChatDialog::onCertSingleRegisterFailed, this, _1, _2));
266}
267
268void
269ChatDialog::updateLabels()
270{
271 QString settingDisp = QString("Chatroom: %1").arg(QString::fromStdString(m_chatroomName));
272 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
273 ui->infoLabel->setText(settingDisp);
274 QString prefixDisp;
275 Name privatePrefix("/private/local");
276 if(privatePrefix.isPrefixOf(m_localChatPrefix))
277 {
278 prefixDisp =
279 QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>")
280 .arg(QString::fromStdString(m_localChatPrefix.toUri()));
281 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
282 }
283 else
284 {
285 prefixDisp = QString("<Prefix = %1>")
286 .arg(QString::fromStdString(m_localChatPrefix.toUri()));
287 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
288 }
289 ui->prefixLabel->setText(prefixDisp);
290}
291
292void
293ChatDialog::initializeSync()
294{
295
296 m_sock = new Sync::SyncSocket(m_chatroomPrefix,
297 m_chatPrefix,
298 m_session,
299 m_useRoutablePrefix,
300 m_localPrefix,
301 m_face,
302 m_myCertificate,
303 m_dataRule,
304 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
305 bind(&ChatDialog::processRemoveWrapper, this, _1));
306
307 usleep(100000);
308
309 QTimer::singleShot(600, this, SLOT(sendJoin()));
310 m_timer->start(FRESHNESS * 1000);
311 disableTreeDisplay();
312 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
313}
314
315void
316ChatDialog::sendInvitation(shared_ptr<Contact> contact, bool isIntroducer)
317{
318 // Add invitee as a trust anchor.
319 m_invitationValidator->addTrustAnchor(contact->getPublicKeyName(),
320 contact->getPublicKey());
321
322 // Prepared an invitation interest without routable prefix.
323 Invitation invitation(contact->getNameSpace(),
324 m_chatroomName,
325 m_localPrefix,
326 m_myCertificate);
327 Interest tmpInterest(invitation.getUnsignedInterestName());
328 m_keyChain.sign(tmpInterest, m_myCertificate.getName());
329
330 // Get invitee's routable prefix (ideally it will do some DNS lookup, but we assume everyone use /ndn/broadcast
331 Name routablePrefix = getInviteeRoutablePrefix(contact->getNameSpace());
332
333 // Check if we need to prepend the routable prefix to the interest name.
334 bool requireRoutablePrefix = false;
335 Name interestName;
336 size_t routablePrefixOffset = 0;
337 if(!routablePrefix.isPrefixOf(tmpInterest.getName()))
338 {
339 interestName.append(routablePrefix).append(CHRONOS_RP_SEPARATOR, 2);
340 requireRoutablePrefix = true;
341 routablePrefixOffset = routablePrefix.size() + 1;
342 }
343 interestName.append(tmpInterest.getName());
344
345 // Send the invitation out
346 Interest interest(interestName);
347 interest.setMustBeFresh(true);
Yingdi Yu233a9722014-03-07 15:47:09 -0800348 _LOG_DEBUG("sendInvitation: " << interest.getName());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800349 m_face->expressInterest(interest,
350 bind(&ChatDialog::replyWrapper, this, _1, _2, routablePrefixOffset, isIntroducer),
351 bind(&ChatDialog::replyTimeoutWrapper, this, _1, routablePrefixOffset));
352}
353
354void
355ChatDialog::replyWrapper(const Interest& interest,
356 Data& data,
357 size_t routablePrefixOffset,
358 bool isIntroducer)
359{
Yingdi Yu233a9722014-03-07 15:47:09 -0800360 _LOG_DEBUG("ChatDialog::replyWrapper");
361 emit reply(interest, data.shared_from_this(), routablePrefixOffset, isIntroducer);
362 _LOG_DEBUG("OK?");
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800363}
364
365void
366ChatDialog::replyTimeoutWrapper(const Interest& interest,
367 size_t routablePrefixOffset)
368{
Yingdi Yu233a9722014-03-07 15:47:09 -0800369 _LOG_DEBUG("ChatDialog::replyTimeoutWrapper");
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800370 emit replyTimeout(interest, routablePrefixOffset);
371}
372
373void
374ChatDialog::onReplyValidated(const shared_ptr<const Data>& data,
375 size_t inviteeRoutablePrefixOffset,
376 bool isIntroducer)
377{
378 if(data->getName().size() <= inviteeRoutablePrefixOffset)
379 {
380 Invitation invitation(data->getName());
381 invitationRejected(invitation.getInviteeNameSpace());
382 }
383 else
384 {
385 Name inviteePrefix;
386 inviteePrefix.wireDecode(data->getName().get(inviteeRoutablePrefixOffset).blockFromValue());
387 IdentityCertificate inviteeCert;
388 inviteeCert.wireDecode(data->getContent().blockFromValue());
389 invitationAccepted(inviteeCert, inviteePrefix, isIntroducer);
390 }
391}
392
393void
394ChatDialog::onReplyValidationFailed(const shared_ptr<const Data>& data,
395 const string& failureInfo)
396{
397 _LOG_DEBUG("Invitation reply cannot be validated: " + failureInfo + " ==> " + data->getName().toUri());
398}
399
400void
401ChatDialog::invitationRejected(const Name& identity)
402{
403 QString msg = QString::fromStdString(identity.toUri()) + " rejected your invitation!";
404 emit inivationRejection(msg);
405}
406
407void
408ChatDialog::invitationAccepted(const IdentityCertificate& inviteeCert,
409 const Name& inviteePrefix,
410 bool isIntroducer)
411{
412 // Add invitee certificate as trust anchor.
413 m_sock->addParticipant(inviteeCert);
414
415 // Ask invitee for IntroCertificate.
416 Name inviteeNameSpace = IdentityCertificate::certificateNameToPublicKeyName(inviteeCert.getName()).getPrefix(-1);
417 fetchIntroCert(inviteeNameSpace, inviteePrefix);
418}
419
420void
421ChatDialog::fetchIntroCert(const Name& identity, const Name& prefix)
422{
423 Name interestName;
424
425 if(!prefix.isPrefixOf(identity))
426 interestName.append(prefix).append(CHRONOS_RP_SEPARATOR, 2);
427
428 interestName.append(identity)
429 .append("CHRONOCHAT-CERT-LIST")
430 .append(m_chatroomName)
431 .appendVersion();
432
433 Interest interest(interestName);
434 interest.setMustBeFresh(true);
435
436 m_face->expressInterest(interest,
437 bind(&ChatDialog::onIntroCertList, this, _1, _2),
438 bind(&ChatDialog::onIntroCertListTimeout, this, _1, 1,
439 "IntroCertList: " + interestName.toUri()));
440}
441
442void
443ChatDialog::onIntroCertList(const Interest& interest, const Data& data)
444{
445 Chronos::IntroCertListMsg introCertList;
446 if(!introCertList.ParseFromArray(data.getContent().value(), data.getContent().value_size()))
447 return;
448
449 for(int i = 0; i < introCertList.certname_size(); i++)
450 {
451 Name certName(introCertList.certname(i));
452 Interest interest(certName);
453 interest.setMustBeFresh(true);
454
Yingdi Yu233a9722014-03-07 15:47:09 -0800455 _LOG_DEBUG("onIntroCertList: to fetch " << certName);
456
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800457 m_face->expressInterest(interest,
458 bind(&ChatDialog::introCertWrapper, this, _1, _2),
459 bind(&ChatDialog::introCertTimeoutWrapper, this, _1, 0,
460 QString("IntroCert: %1").arg(introCertList.certname(i).c_str())));
461 }
462}
463
464void
465ChatDialog::onIntroCertListTimeout(const Interest& interest, int retry, const string& msg)
466{
467 if(retry > 0)
468 m_face->expressInterest(interest,
469 bind(&ChatDialog::onIntroCertList, this, _1, _2),
470 bind(&ChatDialog::onIntroCertListTimeout, this, _1, retry - 1, msg));
471 else
472 _LOG_DEBUG(msg << " TIMEOUT!");
473}
474
475void
476ChatDialog::introCertWrapper(const Interest& interest, Data& data)
477{
Yingdi Yu233a9722014-03-07 15:47:09 -0800478 emit introCert(interest, data.shared_from_this());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800479}
480
481void
482ChatDialog::introCertTimeoutWrapper(const Interest& interest, int retry, const QString& msg)
483{
484 emit introCertTimeout(interest, retry, msg);
485}
486
487void
488ChatDialog::onCertListInterest(const Name& prefix, const ndn::Interest& interest)
489{
490 vector<Name> certNameList;
491 m_sock->getIntroCertNames(certNameList);
492
493 Chronos::IntroCertListMsg msg;
494
495 vector<Name>::const_iterator it = certNameList.begin();
496 vector<Name>::const_iterator end = certNameList.end();
497 for(; it != end; it++)
498 {
499 Name certName;
500 certName.append(m_certSinglePrefix).append(*it);
501 msg.add_certname(certName.toUri());
502 }
503 OBufferStream os;
504 msg.SerializeToOstream(&os);
505
506 Data data(interest.getName());
507 data.setContent(os.buf());
508 m_keyChain.sign(data, m_myCertificate.getName());
509
510 m_face->put(data);
511}
512
513void
514ChatDialog::onCertListRegisterFailed(const Name& prefix, const string& msg)
515{
516 _LOG_DEBUG("ChatDialog::onCertListRegisterFailed failed: " + msg);
517}
518
519void
520ChatDialog::onCertSingleInterest(const Name& prefix, const ndn::Interest& interest)
521{
522 try
523 {
524 Name certName = interest.getName().getSubName(prefix.size());
525 const Sync::IntroCertificate& introCert = m_sock->getIntroCertificate(certName);
526 Data data(interest.getName());
527 data.setContent(introCert.wireEncode());
Yingdi Yu233a9722014-03-07 15:47:09 -0800528 m_keyChain.sign(data, m_myCertificate.getName());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800529 m_face->put(data);
530 }
531 catch(Sync::SyncSocket::Error& e)
532 {
533 return;
534 }
535}
536
537void
538ChatDialog::onCertSingleRegisterFailed(const Name& prefix, const string& msg)
539{
540 _LOG_DEBUG("ChatDialog::onCertListRegisterFailed failed: " + msg);
541}
542
543void
544ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
545{
546 // send msg
547 OBufferStream os;
548 msg.SerializeToOstream(&os);
549
550 if (!msg.IsInitialized())
551 {
552 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
553 abort();
554 }
Yingdi Yu233a9722014-03-07 15:47:09 -0800555 uint64_t nextSequence = m_sock->getNextSeq();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800556 m_sock->publishData(os.buf()->buf(), os.buf()->size(), FRESHNESS);
557
558 m_lastMsgTime = time::now();
559
Yingdi Yu233a9722014-03-07 15:47:09 -0800560 Sync::MissingDataInfo mdi = {m_localChatPrefix.toUri(), Sync::SeqNo(0), Sync::SeqNo(nextSequence)};
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800561 std::vector<Sync::MissingDataInfo> v;
562 v.push_back(mdi);
563 {
564 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
565 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
566 m_scene->msgReceived(QString::fromStdString(m_localChatPrefix.toUri()),
567 QString::fromStdString(m_nick));
568 }
569}
570
571void ChatDialog::disableTreeDisplay()
572{
573 ui->treeButton->setEnabled(false);
574 ui->treeViewer->hide();
575 fitView();
576}
577
578void
579ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
580{
581 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
582
583 if (msg.type() == SyncDemo::ChatMessage::CHAT)
584 {
585
586 if (!msg.has_data())
587 {
588 return;
589 }
590
591 if (msg.from().empty() || msg.data().empty())
592 {
593 return;
594 }
595
596 if (!msg.has_timestamp())
597 {
598 return;
599 }
600
601 // if (m_history.size() == MAX_HISTORY_ENTRY)
602 // {
603 // m_history.dequeue();
604 // }
605
606 // m_history.enqueue(msg);
607
608 QTextCharFormat nickFormat;
609 nickFormat.setForeground(Qt::darkGreen);
610 nickFormat.setFontWeight(QFont::Bold);
611 nickFormat.setFontUnderline(true);
612 nickFormat.setUnderlineColor(Qt::gray);
613
614 QTextCursor cursor(ui->textEdit->textCursor());
615 cursor.movePosition(QTextCursor::End);
616 QTextTableFormat tableFormat;
617 tableFormat.setBorder(0);
618 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
619 QString from = QString("%1 ").arg(msg.from().c_str());
620 QTextTableCell fromCell = table->cellAt(0, 0);
621 fromCell.setFormat(nickFormat);
622 fromCell.firstCursorPosition().insertText(from);
623
624 time_t timestamp = msg.timestamp();
625 printTimeInCell(table, timestamp);
626
627 QTextCursor nextCursor(ui->textEdit->textCursor());
628 nextCursor.movePosition(QTextCursor::End);
629 table = nextCursor.insertTable(1, 1, tableFormat);
630 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
631 if (!isHistory)
632 {
633 showMessage(from, QString::fromUtf8(msg.data().c_str()));
634 }
635 }
636
637 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
638 {
639 QTextCharFormat nickFormat;
640 nickFormat.setForeground(Qt::gray);
641 nickFormat.setFontWeight(QFont::Bold);
642 nickFormat.setFontUnderline(true);
643 nickFormat.setUnderlineColor(Qt::gray);
644
645 QTextCursor cursor(ui->textEdit->textCursor());
646 cursor.movePosition(QTextCursor::End);
647 QTextTableFormat tableFormat;
648 tableFormat.setBorder(0);
649 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
650 QString action;
651 if (msg.type() == SyncDemo::ChatMessage::JOIN)
652 {
653 action = "enters room";
654 }
655 else
656 {
657 action = "leaves room";
658 }
659
660 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
661 QTextTableCell fromCell = table->cellAt(0, 0);
662 fromCell.setFormat(nickFormat);
663 fromCell.firstCursorPosition().insertText(from);
664
665 time_t timestamp = msg.timestamp();
666 printTimeInCell(table, timestamp);
667 }
668
669 QScrollBar *bar = ui->textEdit->verticalScrollBar();
670 bar->setValue(bar->maximum());
671}
672
673void
674ChatDialog::processRemove(QString prefix)
675{
676 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
677
678 bool removed = m_scene->removeNode(prefix);
679 if (removed)
680 {
681 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
682 m_scene->plot(m_sock->getRootDigest().c_str());
683 }
684}
685
686Name
687ChatDialog::getInviteeRoutablePrefix(const Name& invitee)
688{
689 return ndn::Name("/ndn/broadcast");
690}
691
692void
693ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
694 msg.set_from(m_nick);
695 msg.set_to(m_chatroomName);
696 msg.set_data(text.toStdString());
697 int32_t seconds = static_cast<int32_t>(time::now()/1000000000);
698 msg.set_timestamp(seconds);
699 msg.set_type(SyncDemo::ChatMessage::CHAT);
700}
701
702void
703ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
704{
705 msg.set_from(m_nick);
706 msg.set_to(m_chatroomName);
707 int32_t seconds = static_cast<int32_t>(time::now()/1000000000);
708 msg.set_timestamp(seconds);
709 msg.set_type(type);
710}
711
712QString
713ChatDialog::formatTime(time_t timestamp)
714{
715 struct tm *tm_time = localtime(&timestamp);
716 int hour = tm_time->tm_hour;
717 QString amOrPM;
718 if (hour > 12)
719 {
720 hour -= 12;
721 amOrPM = "PM";
722 }
723 else
724 {
725 amOrPM = "AM";
726 if (hour == 0)
727 {
728 hour = 12;
729 }
730 }
731
732 char textTime[12];
733 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
734 return QString(textTime);
735}
736
737void
738ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
739{
740 QTextCharFormat timeFormat;
741 timeFormat.setForeground(Qt::gray);
742 timeFormat.setFontUnderline(true);
743 timeFormat.setUnderlineColor(Qt::gray);
744 QTextTableCell timeCell = table->cellAt(0, 1);
745 timeCell.setFormat(timeFormat);
746 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
747}
748
749string
750ChatDialog::getRandomString()
751{
752 uint32_t r = random::generateWord32();
753 stringstream ss;
754 {
755 using namespace CryptoPP;
756 StringSource(reinterpret_cast<uint8_t*>(&r), 4, true,
757 new HexEncoder(new FileSink(ss), false));
758
759 }
760
761 return ss.str();
762}
763
764void
765ChatDialog::showMessage(const QString& from, const QString& data)
766{
767 if (!isActiveWindow())
768 emit showChatMessage(QString::fromStdString(m_chatroomName),
769 from, data);
770}
771
772void
773ChatDialog::fitView()
774{
775 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
776 QRectF rect = m_scene->itemsBoundingRect();
777 m_scene->setSceneRect(rect);
778 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
779}
780
781void
782ChatDialog::summonReaper()
783{
784 Sync::SyncLogic &logic = m_sock->getLogic ();
785 map<string, bool> branches = logic.getBranchPrefixes();
786 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
787
788 m_zombieList.clear();
789
790 QMapIterator<QString, DisplayUserPtr> it(roster);
791 map<string, bool>::iterator mapIt;
792 while(it.hasNext())
793 {
794 it.next();
795 DisplayUserPtr p = it.value();
796 if (p != DisplayUserNullPtr)
797 {
798 mapIt = branches.find(p->getPrefix().toStdString());
799 if (mapIt != branches.end())
800 {
801 mapIt->second = true;
802 }
803 }
804 }
805
806 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
807 {
808 // this is zombie. all active users should have been marked true
809 if (! mapIt->second)
810 {
811 m_zombieList.append(mapIt->first.c_str());
812 }
813 }
814
815 m_zombieIndex = 0;
816
817 // start reaping
818 reap();
819}
820
821
822// public slots:
823void
824ChatDialog::onLocalPrefixUpdated(const QString& localPrefix)
825{
826 Name newLocalPrefix(localPrefix.toStdString());
827 if(!newLocalPrefix.empty() && newLocalPrefix != m_localPrefix)
828 {
829 // Update localPrefix
830 m_localPrefix = newLocalPrefix;
831
832 updatePrefix();
833 updateLabels();
834 m_scene->setCurrentPrefix(QString(m_localChatPrefix.toUri().c_str()));
835
836 if(m_sock != NULL)
837 {
838 {
839 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
840 m_scene->clearAll();
841 m_scene->plot("Empty");
842 }
843
844 ui->textEdit->clear();
845
846 if (m_joined)
847 {
848 sendLeave();
849 }
850
851 delete m_sock;
852 m_sock = NULL;
853
854 usleep(100000);
855 m_sock = new Sync::SyncSocket(m_chatroomPrefix,
856 m_chatPrefix,
857 m_session,
858 m_useRoutablePrefix,
859 m_localPrefix,
860 m_face,
861 m_myCertificate,
862 m_dataRule,
863 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
864 bind(&ChatDialog::processRemoveWrapper, this, _1));
865 usleep(100000);
866 QTimer::singleShot(600, this, SLOT(sendJoin()));
867 m_timer->start(FRESHNESS * 1000);
868 disableTreeDisplay();
869 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
870 }
871 else
872 initializeSync();
873 }
874 else
875 if (m_sock == NULL)
876 initializeSync();
877
878 fitView();
879}
880
881void
882ChatDialog::onClose()
883{
884 hide();
885 emit closeChatDialog(QString::fromStdString(m_chatroomName));
886}
887
888
889// private slots:
890void
891ChatDialog::onReturnPressed()
892{
893 QString text = ui->lineEdit->text();
894 if (text.isEmpty())
895 return;
896
897 ui->lineEdit->clear();
898
899 if (text.startsWith("boruoboluomi"))
900 {
901 summonReaper ();
902 // reapButton->show();
903 fitView();
904 return;
905 }
906
907 if (text.startsWith("minimanihong"))
908 {
909 // reapButton->hide();
910 fitView();
911 return;
912 }
913
914 SyncDemo::ChatMessage msg;
915 formChatMessage(text, msg);
916
917 appendMessage(msg);
918
919 sendMsg(msg);
920
921 fitView();
922}
923
924void
925ChatDialog::onTreeButtonPressed()
926{
927 if (ui->treeViewer->isVisible())
928 {
929 ui->treeViewer->hide();
930 ui->treeButton->setText("Show ChronoSync Tree");
931 }
932 else
933 {
934 ui->treeViewer->show();
935 ui->treeButton->setText("Hide ChronoSync Tree");
936 }
937
938 fitView();
939}
940
941void
Yingdi Yu233a9722014-03-07 15:47:09 -0800942ChatDialog::onProcessData(const shared_ptr<const Data>& data, bool show, bool isHistory)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800943{
944 SyncDemo::ChatMessage msg;
945 bool corrupted = false;
Yingdi Yu233a9722014-03-07 15:47:09 -0800946 if (!msg.ParseFromArray(data->getContent().value(), data->getContent().value_size()))
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800947 {
Yingdi Yu233a9722014-03-07 15:47:09 -0800948 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << data->getName() << ". what is happening?");
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800949 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
950 msg.set_from("inconnu");
951 msg.set_type(SyncDemo::ChatMessage::OTHER);
952 corrupted = true;
953 }
954
955 // display msg received from network
956 // we have to do so; this function is called by ccnd thread
957 // so if we call appendMsg directly
958 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
959 // the "cannonical" way to is use signal-slot
960 if (show && !corrupted)
961 {
962 appendMessage(msg, isHistory);
963 }
964
965 if (!isHistory)
966 {
967 // update the tree view
Yingdi Yu233a9722014-03-07 15:47:09 -0800968 std::string stdStrName = data->getName().toUri();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800969 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
970 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
971 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
972 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
973 {
974 processRemove(prefix.c_str());
975 }
976 else
977 {
978 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
979 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
980 }
981 }
982 fitView();
983}
984
985void
986ChatDialog::onProcessTreeUpdate(const vector<Sync::MissingDataInfo>& v)
987{
988 _LOG_DEBUG("<<< processing Tree Update");
989
990 if (v.empty())
991 {
992 return;
993 }
994
995 // reflect the changes on digest tree
996 {
997 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
998 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
999 }
1000
1001 int n = v.size();
1002 int totalMissingPackets = 0;
1003 for (int i = 0; i < n; i++)
1004 {
1005 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
1006 }
1007
1008 for (int i = 0; i < n; i++)
1009 {
1010 if (totalMissingPackets < 4)
1011 {
1012 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
1013 {
1014 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
1015 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
1016 }
1017 }
1018 else
1019 {
1020 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
1021 }
1022 }
1023 // adjust the view
1024 fitView();
1025}
1026
1027void
1028ChatDialog::onReplot()
1029{
1030 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1031 m_scene->plot(m_sock->getRootDigest().c_str());
1032 fitView();
1033}
1034
1035void
1036ChatDialog::onRosterChanged(QStringList staleUserList)
1037{
1038 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1039 QStringList rosterList = m_scene->getRosterList();
1040 m_rosterModel->setStringList(rosterList);
1041 QString user;
1042 QStringListIterator it(staleUserList);
1043 while(it.hasNext())
1044 {
1045 std::string nick = it.next().toStdString();
1046 if (nick.empty())
1047 continue;
1048
1049 SyncDemo::ChatMessage msg;
1050 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
1051 msg.set_from(nick);
1052 appendMessage(msg);
1053 }
1054}
1055
1056void
1057ChatDialog::onInviteListDialogRequested()
1058{
Yingdi Yu233a9722014-03-07 15:47:09 -08001059 emit waitForContactList();
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001060 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1061 m_inviteListDialog->show();
1062}
1063
1064void
1065ChatDialog::sendJoin()
1066{
1067 m_joined = true;
1068 SyncDemo::ChatMessage msg;
1069 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
1070 sendMsg(msg);
1071 boost::random::random_device rng;
1072 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
1073 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
1074 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
1075}
1076
1077void
1078ChatDialog::sendHello()
1079{
1080 int64_t now = time::now();
1081 int elapsed = (now - m_lastMsgTime) / 1000000000;
1082 if (elapsed >= m_randomizedInterval / 1000)
1083 {
1084 SyncDemo::ChatMessage msg;
1085 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
1086 sendMsg(msg);
1087 boost::random::random_device rng;
1088 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
1089 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
1090 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
1091 }
1092 else
1093 {
1094 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
1095 }
1096}
1097
1098void
1099ChatDialog::sendLeave()
1100{
1101 SyncDemo::ChatMessage msg;
1102 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
1103 sendMsg(msg);
1104 usleep(500000);
1105 m_sock->leave();
1106 usleep(5000);
1107 m_joined = false;
1108 _LOG_DEBUG("Sync REMOVE signal sent");
1109}
1110
1111void ChatDialog::enableTreeDisplay()
1112{
1113 ui->treeButton->setEnabled(true);
1114 // treeViewer->show();
1115 // fitView();
1116}
1117
1118void
1119ChatDialog::reap()
1120{
1121 if (m_zombieIndex < m_zombieList.size())
1122 {
1123 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
1124 m_sock->remove(prefix);
1125 _LOG_DEBUG("Reaped: prefix = " << prefix);
1126 m_zombieIndex++;
1127 // reap again in 10 seconds
1128 QTimer::singleShot(10000, this, SLOT(reap()));
1129 }
1130}
1131
1132void
1133ChatDialog::onSendInvitation(QString invitee)
1134{
1135 Name inviteeNamespace(invitee.toStdString());
1136 shared_ptr<Contact> inviteeItem = m_contactManager->getContact(inviteeNamespace);
1137 sendInvitation(inviteeItem, true);
1138}
1139
1140void
1141ChatDialog::onReply(const Interest& interest,
Yingdi Yu233a9722014-03-07 15:47:09 -08001142 const shared_ptr<const Data>& data,
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001143 size_t routablePrefixOffset,
1144 bool isIntroducer)
1145{
1146 OnDataValidated onValidated = bind(&ChatDialog::onReplyValidated,
1147 this, _1,
1148 interest.getName().size()-routablePrefixOffset, //RoutablePrefix will be removed before data is passed to the validator
1149 isIntroducer);
1150
1151 OnDataValidationFailed onFailed = bind(&ChatDialog::onReplyValidationFailed,
1152 this, _1, _2);
1153
1154 if(routablePrefixOffset > 0)
1155 {
1156 // It is an encapsulated packet, we only validate the inner packet.
Yingdi Yu233a9722014-03-07 15:47:09 -08001157 shared_ptr<Data> innerData = make_shared<Data>();
1158 innerData->wireDecode(data->getContent().blockFromValue());
1159 m_invitationValidator->validate(*innerData, onValidated, onFailed);
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001160 }
1161 else
Yingdi Yu233a9722014-03-07 15:47:09 -08001162 m_invitationValidator->validate(*data, onValidated, onFailed);
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001163}
1164
1165void
1166ChatDialog::onReplyTimeout(const Interest& interest,
1167 size_t routablePrefixOffset)
1168{
1169 Name interestName;
1170 if(routablePrefixOffset > 0)
1171 interestName = interest.getName().getSubName(routablePrefixOffset);
1172 else
1173 interestName = interest.getName();
1174
1175 Invitation invitation(interestName);
1176
1177 QString msg = QString::fromUtf8("Your invitation to ") + QString::fromStdString(invitation.getInviteeNameSpace().toUri()) + " times out!";
1178 emit inivationRejection(msg);
1179}
1180
1181void
Yingdi Yu233a9722014-03-07 15:47:09 -08001182ChatDialog::onIntroCert(const Interest& interest, const shared_ptr<const Data>& data)
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001183{
1184 Data innerData;
Yingdi Yu233a9722014-03-07 15:47:09 -08001185 innerData.wireDecode(data->getContent().blockFromValue());
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001186 Sync::IntroCertificate introCert(innerData);
1187 m_sock->addParticipant(introCert);
1188}
1189
1190void
Yingdi Yu233a9722014-03-07 15:47:09 -08001191ChatDialog::onIntroCertTimeout(const Interest& interest, int retry, const QString& msg)
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001192{
1193 _LOG_DEBUG("onIntroCertTimeout: " << msg.toStdString());
1194}
1195
1196
1197
1198#if WAF
1199#include "chat-dialog.moc"
1200#include "chat-dialog.cpp.moc"
1201#endif