blob: 17ec238a6b7c080c912b8aafe6c2b0deff285518 [file] [log] [blame]
Alexander Afanasyevb4b92292013-07-09 13:54:59 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013, Regents of the University of California
Yingdi Yu5b989132013-10-23 14:03:09 -07004 * Yingdi Yu
Alexander Afanasyevb4b92292013-07-09 13:54:59 -07005 *
Yingdi Yu5b989132013-10-23 14:03:09 -07006 * BSD license, See the LICENSE file for more information
Alexander Afanasyevb4b92292013-07-09 13:54:59 -07007 *
Yingdi Yu7989eb22013-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>
Alexander Afanasyevb4b92292013-07-09 13:54:59 -070011 */
12
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070013#include "chatdialog.h"
Yingdi Yu5b989132013-10-23 14:03:09 -070014#include "ui_chatdialog.h"
Alexander Afanasyevf829f4d2013-05-07 15:59:36 -070015
Yingdi Yu7989eb22013-10-31 17:38:22 -070016#include <QScrollBar>
17
Yingdi Yueda39aa2013-10-23 23:07:29 -070018#ifndef Q_MOC_RUN
19#include <ndn.cxx/security/identity/identity-manager.h>
Yingdi Yueda39aa2013-10-23 23:07:29 -070020#include <ndn.cxx/security/encryption/basic-encryption-manager.h>
Yingdi Yu7989eb22013-10-31 17:38:22 -070021#include <sync-intro-certificate.h>
22#include <boost/random/random_device.hpp>
23#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yueda39aa2013-10-23 23:07:29 -070024#include "logging.h"
25#endif
26
Yingdi Yu5b989132013-10-23 14:03:09 -070027using namespace std;
Alexander Afanasyevf829f4d2013-05-07 15:59:36 -070028
Yingdi Yueda39aa2013-10-23 23:07:29 -070029INIT_LOGGER("ChatDialog");
30
Yingdi Yu7989eb22013-10-31 17:38:22 -070031static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
32
33Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
34Q_DECLARE_METATYPE(size_t)
35
36ChatDialog::ChatDialog(ndn::Ptr<ContactManager> contactManager,
37 const ndn::Name& chatroomPrefix,
38 const ndn::Name& localPrefix,
39 const ndn::Name& defaultIdentity,
Yingdi Yu46948282013-11-06 18:43:31 -080040 const std::string& nick,
41 bool trial,
Yingdi Yu5b989132013-10-23 14:03:09 -070042 QWidget *parent)
Yingdi Yu7989eb22013-10-31 17:38:22 -070043: QDialog(parent)
44 , ui(new Ui::ChatDialog)
45 , m_contactManager(contactManager)
46 , m_chatroomPrefix(chatroomPrefix)
47 , m_localPrefix(localPrefix)
48 , m_defaultIdentity(defaultIdentity)
49 , m_invitationPolicyManager(ndn::Ptr<InvitationPolicyManager>(new InvitationPolicyManager(m_chatroomPrefix.get(-1).toUri())))
Yingdi Yu46948282013-11-06 18:43:31 -080050 , m_nick(nick)
Yingdi Yu7989eb22013-10-31 17:38:22 -070051 , m_sock(NULL)
52 , m_lastMsgTime(0)
53 // , m_historyInitialized(false)
54 , m_joined(false)
55 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Zhenkai Zhu6d589aa2012-05-29 17:34:35 -070056{
Yingdi Yu7989eb22013-10-31 17:38:22 -070057 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
58 qRegisterMetaType<size_t>("size_t");
59
Yingdi Yueda39aa2013-10-23 23:07:29 -070060 ui->setupUi(this);
61
Yingdi Yu7989eb22013-10-31 17:38:22 -070062 m_localChatPrefix = m_localPrefix;
Yingdi Yu46948282013-11-06 18:43:31 -080063 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu7989eb22013-10-31 17:38:22 -070064 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1));
65
66 m_session = time(NULL);
67 m_scene = new DigestTreeScene(this);
68
69 initializeSetting();
70 updateLabels();
71
72 ui->treeViewer->setScene(m_scene);
73 ui->treeViewer->hide();
74 m_scene->plot("Empty");
75 QRectF rect = m_scene->itemsBoundingRect();
76 m_scene->setSceneRect(rect);
77
78 m_rosterModel = new QStringListModel(this);
79 ui->listView->setModel(m_rosterModel);
80
81 m_timer = new QTimer(this);
82
Yingdi Yu46948282013-11-06 18:43:31 -080083 setWrapper(trial);
Yingdi Yu7989eb22013-10-31 17:38:22 -070084
85 connect(ui->inviteButton, SIGNAL(clicked()),
86 this, SLOT(openInviteListDialog()));
87 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
88 this, SLOT(sendInvitationWrapper(QString, bool)));
89 connect(ui->lineEdit, SIGNAL(returnPressed()),
90 this, SLOT(returnPressed()));
91 connect(ui->treeButton, SIGNAL(pressed()),
92 this, SLOT(treeButtonPressed()));
93 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
94 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
95 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
96 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
97 connect(m_timer, SIGNAL(timeout()),
98 this, SLOT(replot()));
99 connect(m_scene, SIGNAL(replot()),
100 this, SLOT(replot()));
101 // TODO: TrayIcon
102 // connect(trayIcon, SIGNAL(messageClicked()),
103 // this, SLOT(showNormal()));
104 // connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
105 // this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
106 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
107 this, SLOT(updateRosterList(QStringList)));
108
109 // m_identityManager = ndn::Ptr<ndn::security::IdentityManager>::Create();
110 // // ndn::Ptr<ndn::security::EncryptionManager> encryptionManager = ndn::Ptr<ndn::security::EncryptionManager>(new ndn::security::BasicEncryptionManager(privateStorage, "/tmp/encryption.db"));
111
112 // ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
113 // m_syncPolicyManager = ndn::Ptr<SyncPolicyManager>(new SyncPolicyManager(m_defaultIdentity, certificateName, m_chatroomPrefix));
114
115 initializeSync();
Zhenkai Zhu5a8d5aa2012-05-30 21:25:23 -0700116}
117
Yingdi Yu7989eb22013-10-31 17:38:22 -0700118// ChatDialog::ChatDialog(const ndn::Name& chatroomPrefix,
119// const ndn::Name& localPrefix,
120// const ndn::Name& defaultIdentity,
121// const ndn::security::IdentityCertificate& identityCertificate,
122// QWidget *parent)
123// : QDialog(parent)
124// , ui(new Ui::ChatDialog)
125// , m_chatroomPrefix(chatroomPrefix)
126// , m_localPrefix(localPrefix)
127// , m_defaultIdentity(defaultIdentity)
128// , m_invitationPolicyManager(ndn::Ptr<InvitationPolicyManager>(new InvitationPolicyManager(m_chatroomPrefix.get(-1).toUri())))
129
130// , m_sock(NULL)
131// , m_lastMsgTime(0)
132// // , m_historyInitialized(false)
133// , m_joined(false)
134// {
135// qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
136// qRegisterMetaType<size_t>("size_t");
137
138// ui->setupUi(this);
139
140// m_localChatPrefix = m_localPrefix;
141// m_localChatPrefix.append("FH").append(m_defaultIdentity);
142// m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1));
143
144// m_session = time(NULL);
145// m_scene = new DigestTreeScene(this);
146
147// initializeSetting();
148// updateLabels();
149
150// ui->treeViewer->setScene(m_scene);
151// ui->treeViewer->hide();
152// m_scene->plot("Empty");
153// QRectF rect = m_scene->itemsBoundingRect();
154// m_scene->setSceneRect(rect);
155
156// m_rosterModel = new QStringListModel(this);
157// ui->listView->setModel(m_rosterModel);
158
159// m_timer = new QTimer(this);
160
161// setWrapper();
162
163// connect(ui->lineEdit, SIGNAL(returnPressed()),
164// this, SLOT(returnPressed()));
165// connect(ui->treeButton, SIGNAL(pressed()),
166// this, SLOT(treeButtonPressed()));
167// connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
168// this, SLOT(processData(QString, const char *, size_t, bool, bool)));
169// connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
170// this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
171// connect(m_timer, SIGNAL(timeout()),
172// this, SLOT(replot()));
173// connect(m_scene, SIGNAL(replot()),
174// this, SLOT(replot()));
175// // TODO: TrayIcon
176// // connect(trayIcon, SIGNAL(messageClicked()),
177// // this, SLOT(showNormal()));
178// // connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
179// // this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
180// connect(m_scene, SIGNAL(rosterChanged(QStringList)),
181// this, SLOT(updateRosterList(QStringList)));
182
183// // m_identityManager = ndn::Ptr<ndn::security::IdentityManager>::Create();
184// // // ndn::Ptr<ndn::security::EncryptionManager> encryptionManager = ndn::Ptr<ndn::security::EncryptionManager>(new ndn::security::BasicEncryptionManager(privateStorage, "/tmp/encryption.db"));
185
186// // ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
187// // m_syncPolicyManager = ndn::Ptr<SyncPolicyManager>(new SyncPolicyManager(m_defaultIdentity, certificateName, m_chatroomPrefix));
188
189// initializeSync();
190// }
191
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700192ChatDialog::~ChatDialog()
193{
Yingdi Yu46948282013-11-06 18:43:31 -0800194 _LOG_DEBUG("about to leave 4!");
195 if(m_sock != NULL)
196 {
197 sendLeave();
198 delete m_sock;
199 m_sock = NULL;
200 }
201 // m_handler->shutdown();
Zhenkai Zhu82a62752012-06-04 17:11:04 -0700202}
203
Zhenkai Zhu86df7412012-09-27 16:30:20 -0700204void
Yingdi Yu46948282013-11-06 18:43:31 -0800205ChatDialog::setWrapper(bool trial)
Zhenkai Zhucf024442012-10-05 10:33:08 -0700206{
Yingdi Yu7989eb22013-10-31 17:38:22 -0700207 m_identityManager = ndn::Ptr<ndn::security::IdentityManager>::Create();
208 // ndn::Ptr<ndn::security::EncryptionManager> encryptionManager = ndn::Ptr<ndn::security::EncryptionManager>(new ndn::security::BasicEncryptionManager(privateStorage, "/tmp/encryption.db"));
Yingdi Yueda39aa2013-10-23 23:07:29 -0700209
Yingdi Yu7989eb22013-10-31 17:38:22 -0700210 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
211 m_syncPolicyManager = ndn::Ptr<SyncPolicyManager>(new SyncPolicyManager(m_defaultIdentity, certificateName, m_chatroomPrefix));
Yingdi Yueda39aa2013-10-23 23:07:29 -0700212
Yingdi Yu7989eb22013-10-31 17:38:22 -0700213 m_keychain = ndn::Ptr<ndn::security::Keychain>(new ndn::security::Keychain(m_identityManager, m_invitationPolicyManager, NULL));
214
215 m_handler = ndn::Ptr<ndn::Wrapper>(new ndn::Wrapper(m_keychain));
Yingdi Yu46948282013-11-06 18:43:31 -0800216
217 if(trial == true)
218 {
219 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(ndn::Name("/local/ndn/prefix")));
220 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onUnverified,
221 this,
222 _1),
223 boost::bind(&ChatDialog::onTimeout,
224 this,
225 _1,
226 _2),
227 boost::bind(&ChatDialog::onUnverified,
228 this,
229 _1)));
230
231 m_handler->sendInterest(interest, closure);
232 }
Zhenkai Zhud13acd02012-06-04 15:25:20 -0700233}
Alexander Afanasyevb4b92292013-07-09 13:54:59 -0700234
Yingdi Yueda39aa2013-10-23 23:07:29 -0700235void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700236ChatDialog::initializeSetting()
Yingdi Yueda39aa2013-10-23 23:07:29 -0700237{
Yingdi Yu7989eb22013-10-31 17:38:22 -0700238 // TODO: nick name may be changed.
Yingdi Yu46948282013-11-06 18:43:31 -0800239 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu7989eb22013-10-31 17:38:22 -0700240 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toUri()));
241 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
242 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
243 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
244}
Yingdi Yueda39aa2013-10-23 23:07:29 -0700245
Yingdi Yu7989eb22013-10-31 17:38:22 -0700246void
247ChatDialog::updateLabels()
248{
249 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
250 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
251 ui->infoLabel->setText(settingDisp);
252 QString prefixDisp;
253 if (m_user.getPrefix().startsWith("/private/local"))
254 {
255 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
256 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
257 }
258 else
259 {
260 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
261 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
262 }
263 ui->prefixLabel->setText(prefixDisp);
264}
Yingdi Yueda39aa2013-10-23 23:07:29 -0700265
Yingdi Yu7989eb22013-10-31 17:38:22 -0700266void
267ChatDialog::sendInvitation(ndn::Ptr<ContactItem> contact, bool isIntroducer)
268{
269 m_invitationPolicyManager->addTrustAnchor(contact->getSelfEndorseCertificate());
270
271 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
272
273 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yueda39aa2013-10-23 23:07:29 -0700274 interestName.append(contact->getNameSpace());
275 interestName.append("chatroom");
276 interestName.append(m_chatroomPrefix.get(-1));
277 interestName.append("inviter-prefix");
278 interestName.append(m_localPrefix);
279 interestName.append("inviter");
280 interestName.append(certificateName);
281
282 string signedUri = interestName.toUri();
Yingdi Yu7989eb22013-10-31 17:38:22 -0700283 ndn::Blob signedBlob(signedUri.c_str(), signedUri.size());
Yingdi Yueda39aa2013-10-23 23:07:29 -0700284
Yingdi Yu7989eb22013-10-31 17:38:22 -0700285 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = ndn::DynamicCast<const ndn::signature::Sha256WithRsa>(m_identityManager->signByCertificate(signedBlob, certificateName));
286 const ndn::Blob& sigBits = sha256sig->getSignatureBits();
Yingdi Yueda39aa2013-10-23 23:07:29 -0700287
288 interestName.append(sigBits.buf(), sigBits.size());
Yingdi Yu46948282013-11-06 18:43:31 -0800289 interestName.appendVersion();
290
Yingdi Yueda39aa2013-10-23 23:07:29 -0700291
Yingdi Yu7989eb22013-10-31 17:38:22 -0700292 ndn::Ptr<ndn::Interest> interest = ndn::Ptr<ndn::Interest>(new ndn::Interest(interestName));
293 ndn::Ptr<ndn::Closure> closure = ndn::Ptr<ndn::Closure>(new ndn::Closure(boost::bind(&ChatDialog::onInviteReplyVerified,
294 this,
295 _1,
296 contact->getNameSpace(),
297 isIntroducer),
298 boost::bind(&ChatDialog::onInviteTimeout,
299 this,
300 _1,
301 _2,
302 contact->getNameSpace(),
303 7),
304 boost::bind(&ChatDialog::onUnverified,
305 this,
306 _1)));
Yingdi Yueda39aa2013-10-23 23:07:29 -0700307
308 m_handler->sendInterest(interest, closure);
309}
310
311void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700312ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
313{ m_invitationPolicyManager->addTrustAnchor(selfEndorseCertificate); }
314
315void
316ChatDialog::addChatDataRule(const ndn::Name& prefix,
317 const ndn::security::IdentityCertificate& identityCertificate,
318 bool isIntroducer)
319{ m_syncPolicyManager->addChatDataRule(prefix, identityCertificate, isIntroducer); }
320
321void
322ChatDialog::publishIntroCert(ndn::Ptr<ndn::security::IdentityCertificate> dskCertificate, bool isIntroducer)
Yingdi Yueda39aa2013-10-23 23:07:29 -0700323{
Yingdi Yu7989eb22013-10-31 17:38:22 -0700324 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
325 dskCertificate->getPublicKeyName(),
326 m_identityManager->getDefaultKeyNameForIdentity(m_defaultIdentity),
327 dskCertificate->getNotBefore(),
328 dskCertificate->getNotAfter(),
329 dskCertificate->getPublicKeyInfo(),
330 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
331 ndn::Name certName = m_identityManager->getDefaultCertificateNameByIdentity(m_defaultIdentity);
Yingdi Yu9b34b1f2013-11-01 17:37:51 -0700332 _LOG_DEBUG("publishIntroCert: " << syncIntroCertificate.getName());
Yingdi Yu7989eb22013-10-31 17:38:22 -0700333 m_identityManager->signByCertificate(syncIntroCertificate, certName);
334 m_handler->putToNdnd(*syncIntroCertificate.encodeToWire());
Yingdi Yueda39aa2013-10-23 23:07:29 -0700335}
336
337void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700338ChatDialog::invitationRejected(const ndn::Name& identity)
339{
340 _LOG_DEBUG(" " << identity.toUri() << " rejected your invitation!");
341 //TODO:
342}
343
344void
345ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::Ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yueda39aa2013-10-23 23:07:29 -0700346{
347 _LOG_DEBUG(" " << identity.toUri() << " accepted your invitation!");
Yingdi Yu7989eb22013-10-31 17:38:22 -0700348 ndn::Ptr<const ndn::signature::Sha256WithRsa> sha256sig = boost::dynamic_pointer_cast<const ndn::signature::Sha256WithRsa> (data->getSignature());
349 const ndn::Name & keyLocatorName = sha256sig->getKeyLocator().getKeyName();
350 ndn::Ptr<ndn::security::IdentityCertificate> dskCertificate = m_invitationPolicyManager->getValidatedDskCertificate(keyLocatorName);
Yingdi Yu46948282013-11-06 18:43:31 -0800351 ndn::Name tmpPrefix(inviteePrefix);
352 ndn::Name prefix = tmpPrefix.getPrefix(tmpPrefix.size()-1);
353 m_syncPolicyManager->addChatDataRule(prefix, *dskCertificate, isIntroducer);
354 // m_syncPolicyManager->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yu7989eb22013-10-31 17:38:22 -0700355 publishIntroCert(dskCertificate, isIntroducer);
Yingdi Yueda39aa2013-10-23 23:07:29 -0700356}
357
358void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700359ChatDialog::onInviteReplyVerified(ndn::Ptr<ndn::Data> data, const ndn::Name& identity, bool isIntroducer)
Yingdi Yueda39aa2013-10-23 23:07:29 -0700360{
361 string content(data->content().buf(), data->content().size());
362 if(content.empty())
363 invitationRejected(identity);
364 else
Yingdi Yu7989eb22013-10-31 17:38:22 -0700365 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yueda39aa2013-10-23 23:07:29 -0700366}
367
368void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700369ChatDialog::onInviteTimeout(ndn::Ptr<ndn::Closure> closure, ndn::Ptr<ndn::Interest> interest, const ndn::Name& identity, int retry)
Yingdi Yueda39aa2013-10-23 23:07:29 -0700370{
371 if(retry > 0)
372 {
Yingdi Yu7989eb22013-10-31 17:38:22 -0700373 ndn::Ptr<ndn::Closure> newClosure = ndn::Ptr<ndn::Closure>(new ndn::Closure(closure->m_dataCallback,
374 boost::bind(&ChatDialog::onInviteTimeout,
375 this,
376 _1,
377 _2,
378 identity,
379 retry - 1),
380 closure->m_unverifiedCallback,
381 closure->m_stepCount)
382 );
Yingdi Yueda39aa2013-10-23 23:07:29 -0700383 m_handler->sendInterest(interest, newClosure);
384 }
385 else
386 invitationRejected(identity);
387}
388
389void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700390ChatDialog::onUnverified(ndn::Ptr<ndn::Data> data)
Yingdi Yueda39aa2013-10-23 23:07:29 -0700391{}
392
Yingdi Yu7989eb22013-10-31 17:38:22 -0700393void
Yingdi Yu46948282013-11-06 18:43:31 -0800394ChatDialog::onTimeout(ndn::Ptr<ndn::Closure> closure,
395 ndn::Ptr<ndn::Interest> interest)
396{}
397
398void
Yingdi Yu7989eb22013-10-31 17:38:22 -0700399ChatDialog::initializeSync()
400{
401
402 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
403 m_syncPolicyManager,
404 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
405 bind(&ChatDialog::processRemoveWrapper, this, _1));
406
407 usleep(100000);
408
409 QTimer::singleShot(600, this, SLOT(sendJoin()));
410 m_timer->start(FRESHNESS * 1000);
411 disableTreeDisplay();
412 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
413 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
414 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
415 // _LOG_DEBUG("initializeSync is done!");
416}
417
418void
419ChatDialog::returnPressed()
420{
421 QString text = ui->lineEdit->text();
422 if (text.isEmpty())
423 return;
424
425 ui->lineEdit->clear();
426
427 if (text.startsWith("boruoboluomi"))
428 {
429 summonReaper ();
430 // reapButton->show();
431 fitView();
432 return;
433 }
434
435 if (text.startsWith("minimanihong"))
436 {
437 // reapButton->hide();
438 fitView();
439 return;
440 }
441
442 SyncDemo::ChatMessage msg;
443 formChatMessage(text, msg);
444
445 appendMessage(msg);
446
447 sendMsg(msg);
448
449 fitView();
450}
451
452void
453ChatDialog::treeButtonPressed()
454{
455 if (ui->treeViewer->isVisible())
456 {
457 ui->treeViewer->hide();
458 ui->treeButton->setText("Show ChronoSync Tree");
459 }
460 else
461 {
462 ui->treeViewer->show();
463 ui->treeButton->setText("Hide ChronoSync Tree");
464 }
465
466 fitView();
467}
468
469void ChatDialog::disableTreeDisplay()
470{
471 ui->treeButton->setEnabled(false);
472 ui->treeViewer->hide();
473 fitView();
474}
475
476void ChatDialog::enableTreeDisplay()
477{
478 ui->treeButton->setEnabled(true);
479 // treeViewer->show();
480 // fitView();
481}
482
483void
484ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
485{
486 emit treeUpdated(v);
487 _LOG_DEBUG("<<< Tree update signal emitted");
488}
489
490void
491ChatDialog::processRemoveWrapper(std::string prefix)
492{
493 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
494}
495
496void
497ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
498{
499 _LOG_DEBUG("<<< processing Tree Update");
500
501 if (v.empty())
502 {
503 return;
504 }
505
506 // reflect the changes on digest tree
507 {
508 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
509 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
510 }
511
512 int n = v.size();
513 int totalMissingPackets = 0;
514 for (int i = 0; i < n; i++)
515 {
516 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
517 }
518
519 for (int i = 0; i < n; i++)
520 {
521 if (totalMissingPackets < 4)
522 {
523 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
524 {
525 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
526 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
527 }
528 }
529 else
530 {
531 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
532 }
533 }
534
535 // adjust the view
536 fitView();
537
538}
539
540void
541ChatDialog::processDataWrapper(ndn::Ptr<ndn::Data> data)
542{
543 string name = data->getName().toUri();
544 const char* buf = data->content().buf();
545 size_t len = data->content().size();
546
547 char *tempBuf = new char[len];
548 memcpy(tempBuf, buf, len);
549 emit dataReceived(name.c_str(), tempBuf, len, true, false);
550 _LOG_DEBUG("<<< " << name << " fetched");
551}
552
553void
554ChatDialog::processDataNoShowWrapper(ndn::Ptr<ndn::Data> data)
555{
556 string name = data->getName().toUri();
557 const char* buf = data->content().buf();
558 size_t len = data->content().size();
559
560 char *tempBuf = new char[len];
561 memcpy(tempBuf, buf, len);
562 emit dataReceived(name.c_str(), tempBuf, len, false, false);
563
564 // if (!m_historyInitialized)
565 // {
566 // fetchHistory(name);
567 // m_historyInitialized = true;
568 // }
569}
570
571// void
572// ChatDialog::fetchHistory(std::string name)
573// {
574
575// /****************************/
576// /* TODO: fix following part */
577// /****************************/
578// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
579// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
580// prefix += "/history";
581// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
582// // QString randomString = getRandomString();
583// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
584// // {
585// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
586// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
587// // }
588// }
589
590void
591ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
592{
593 SyncDemo::ChatMessage msg;
594 bool corrupted = false;
595 if (!msg.ParseFromArray(buf, len))
596 {
597 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
598 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
599 msg.set_from("inconnu");
600 msg.set_type(SyncDemo::ChatMessage::OTHER);
601 corrupted = true;
602 }
603
604 delete [] buf;
605 buf = NULL;
606
607 // display msg received from network
608 // we have to do so; this function is called by ccnd thread
609 // so if we call appendMsg directly
610 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
611 // the "cannonical" way to is use signal-slot
612 if (show && !corrupted)
613 {
614 appendMessage(msg, isHistory);
615 }
616
617 if (!isHistory)
618 {
619 // update the tree view
620 std::string stdStrName = name.toStdString();
621 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
622 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
623 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
624 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
625 {
626 processRemove(prefix.c_str());
627 }
628 else
629 {
630 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
631 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
632 }
633 }
634 fitView();
635}
636
637void
638ChatDialog::processRemove(QString prefix)
639{
640 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
641
642 bool removed = m_scene->removeNode(prefix);
643 if (removed)
644 {
645 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
646 m_scene->plot(m_sock->getRootDigest().c_str());
647 }
648}
649
650void
651ChatDialog::sendJoin()
652{
653 m_joined = true;
654 SyncDemo::ChatMessage msg;
655 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
656 sendMsg(msg);
657 boost::random::random_device rng;
658 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
659 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
660 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
661}
662
663void
664ChatDialog::sendHello()
665{
666 time_t now = time(NULL);
667 int elapsed = now - m_lastMsgTime;
668 if (elapsed >= m_randomizedInterval / 1000)
669 {
670 SyncDemo::ChatMessage msg;
671 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
672 sendMsg(msg);
673 boost::random::random_device rng;
674 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
675 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
676 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
677 }
678 else
679 {
680 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
681 }
682}
683
684void
685ChatDialog::sendLeave()
686{
687 SyncDemo::ChatMessage msg;
688 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
689 sendMsg(msg);
690 usleep(500000);
691 m_sock->remove(m_user.getPrefix().toStdString());
692 usleep(5000);
693 m_joined = false;
694 _LOG_DEBUG("Sync REMOVE signal sent");
695}
696
697void
698ChatDialog::replot()
699{
700 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
701 m_scene->plot(m_sock->getRootDigest().c_str());
702 fitView();
703}
704
705void
706ChatDialog::summonReaper()
707{
708 Sync::SyncLogic &logic = m_sock->getLogic ();
709 map<string, bool> branches = logic.getBranchPrefixes();
710 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
711
712 m_zombieList.clear();
713
714 QMapIterator<QString, DisplayUserPtr> it(roster);
715 map<string, bool>::iterator mapIt;
716 while(it.hasNext())
717 {
718 it.next();
719 DisplayUserPtr p = it.value();
720 if (p != DisplayUserNullPtr)
721 {
722 mapIt = branches.find(p->getPrefix().toStdString());
723 if (mapIt != branches.end())
724 {
725 mapIt->second = true;
726 }
727 }
728 }
729
730 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
731 {
732 // this is zombie. all active users should have been marked true
733 if (! mapIt->second)
734 {
735 m_zombieList.append(mapIt->first.c_str());
736 }
737 }
738
739 m_zombieIndex = 0;
740
741 // start reaping
742 reap();
743}
744
745void
746ChatDialog::reap()
747{
748 if (m_zombieIndex < m_zombieList.size())
749 {
750 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
751 m_sock->remove(prefix);
752 _LOG_DEBUG("Reaped: prefix = " << prefix);
753 m_zombieIndex++;
754 // reap again in 10 seconds
755 QTimer::singleShot(10000, this, SLOT(reap()));
756 }
757}
758
759void
760ChatDialog::updateRosterList(QStringList staleUserList)
761{
762 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
763 QStringList rosterList = m_scene->getRosterList();
764 m_rosterModel->setStringList(rosterList);
765 QString user;
766 QStringListIterator it(staleUserList);
767 while(it.hasNext())
768 {
769 std::string nick = it.next().toStdString();
770 if (nick.empty())
771 continue;
772
773 SyncDemo::ChatMessage msg;
774 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
775 msg.set_from(nick);
776 appendMessage(msg);
777 }
778}
779
780void
781ChatDialog::resizeEvent(QResizeEvent *e)
782{
783 fitView();
784}
785
786void
787ChatDialog::showEvent(QShowEvent *e)
788{
789 fitView();
790}
791
792void
793ChatDialog::fitView()
794{
795 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
796 QRectF rect = m_scene->itemsBoundingRect();
797 m_scene->setSceneRect(rect);
798 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
799}
800
801void
802ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
803 msg.set_from(m_user.getNick().toStdString());
804 msg.set_to(m_user.getChatroom().toStdString());
805 msg.set_data(text.toUtf8().constData());
806 time_t seconds = time(NULL);
807 msg.set_timestamp(seconds);
808 msg.set_type(SyncDemo::ChatMessage::CHAT);
809}
810
811void
812ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
813{
814 msg.set_from(m_user.getNick().toStdString());
815 msg.set_to(m_user.getChatroom().toStdString());
816 time_t seconds = time(NULL);
817 msg.set_timestamp(seconds);
818 msg.set_type(type);
819}
820
821void
822ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
823{
824 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
825
826 if (msg.type() == SyncDemo::ChatMessage::CHAT)
827 {
828
829 if (!msg.has_data())
830 {
831 return;
832 }
833
834 if (msg.from().empty() || msg.data().empty())
835 {
836 return;
837 }
838
839 if (!msg.has_timestamp())
840 {
841 return;
842 }
843
844 // if (m_history.size() == MAX_HISTORY_ENTRY)
845 // {
846 // m_history.dequeue();
847 // }
848
849 // m_history.enqueue(msg);
850
851 QTextCharFormat nickFormat;
852 nickFormat.setForeground(Qt::darkGreen);
853 nickFormat.setFontWeight(QFont::Bold);
854 nickFormat.setFontUnderline(true);
855 nickFormat.setUnderlineColor(Qt::gray);
856
857 QTextCursor cursor(ui->textEdit->textCursor());
858 cursor.movePosition(QTextCursor::End);
859 QTextTableFormat tableFormat;
860 tableFormat.setBorder(0);
861 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
862 QString from = QString("%1 ").arg(msg.from().c_str());
863 QTextTableCell fromCell = table->cellAt(0, 0);
864 fromCell.setFormat(nickFormat);
865 fromCell.firstCursorPosition().insertText(from);
866
867 time_t timestamp = msg.timestamp();
868 printTimeInCell(table, timestamp);
869
870 QTextCursor nextCursor(ui->textEdit->textCursor());
871 nextCursor.movePosition(QTextCursor::End);
872 table = nextCursor.insertTable(1, 1, tableFormat);
873 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
874 if (!isHistory)
875 {
876 showMessage(from, QString::fromUtf8(msg.data().c_str()));
877 }
878 }
879
880 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
881 {
882 QTextCharFormat nickFormat;
883 nickFormat.setForeground(Qt::gray);
884 nickFormat.setFontWeight(QFont::Bold);
885 nickFormat.setFontUnderline(true);
886 nickFormat.setUnderlineColor(Qt::gray);
887
888 QTextCursor cursor(ui->textEdit->textCursor());
889 cursor.movePosition(QTextCursor::End);
890 QTextTableFormat tableFormat;
891 tableFormat.setBorder(0);
892 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
893 QString action;
894 if (msg.type() == SyncDemo::ChatMessage::JOIN)
895 {
896 action = "enters room";
897 }
898 else
899 {
900 action = "leaves room";
901 }
902
903 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
904 QTextTableCell fromCell = table->cellAt(0, 0);
905 fromCell.setFormat(nickFormat);
906 fromCell.firstCursorPosition().insertText(from);
907
908 time_t timestamp = msg.timestamp();
909 printTimeInCell(table, timestamp);
910 }
911
912 QScrollBar *bar = ui->textEdit->verticalScrollBar();
913 bar->setValue(bar->maximum());
914}
915
916QString
917ChatDialog::formatTime(time_t timestamp)
918{
919 struct tm *tm_time = localtime(&timestamp);
920 int hour = tm_time->tm_hour;
921 QString amOrPM;
922 if (hour > 12)
923 {
924 hour -= 12;
925 amOrPM = "PM";
926 }
927 else
928 {
929 amOrPM = "AM";
930 if (hour == 0)
931 {
932 hour = 12;
933 }
934 }
935
936 char textTime[12];
937 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
938 return QString(textTime);
939}
940
941void
942ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
943{
944 QTextCharFormat timeFormat;
945 timeFormat.setForeground(Qt::gray);
946 timeFormat.setFontUnderline(true);
947 timeFormat.setUnderlineColor(Qt::gray);
948 QTextTableCell timeCell = table->cellAt(0, 1);
949 timeCell.setFormat(timeFormat);
950 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
951}
952
953void
954ChatDialog::showMessage(QString from, QString data)
955{
956 if (!isActiveWindow())
957 {
958 //TODO: Notification to be done
959 // trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
960 // trayIcon->setIcon(QIcon(":/images/note.png"));
961 }
962}
963
964void
965ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
966{
967 // send msg
968 size_t size = msg.ByteSize();
969 char *buf = new char[size];
970 msg.SerializeToArray(buf, size);
971 if (!msg.IsInitialized())
972 {
973 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
974 abort();
975 }
976 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
977
978 delete buf;
979
980 m_lastMsgTime = time(NULL);
981
982 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
983 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
984 std::vector<Sync::MissingDataInfo> v;
985 v.push_back(mdi);
986 {
987 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
988 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
989 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
990 }
991}
992
993void
994ChatDialog::openInviteListDialog()
995{
996 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
997 m_inviteListDialog->show();
998}
999
1000void
1001ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1002{
1003 ndn::Name inviteeNamespace(invitee.toUtf8().constData());
1004 ndn::Ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
1005 sendInvitation(inviteeItem, isIntroducer);
1006}
1007
Yingdi Yu46948282013-11-06 18:43:31 -08001008void
1009ChatDialog::closeEvent(QCloseEvent *e)
1010{
1011 hide();
1012 _LOG_DEBUG("about to leave 1");
1013 emit closeChatDialog(m_chatroomPrefix);
1014}
1015
1016
1017
Yingdi Yu7989eb22013-10-31 17:38:22 -07001018
Alexander Afanasyevb4b92292013-07-09 13:54:59 -07001019#if WAF
1020#include "chatdialog.moc"
1021#include "chatdialog.cpp.moc"
1022#endif