blob: 5a6d4838a0c321cc850f255d84e898833bccc49b [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
Yingdi Yu42f66462013-10-31 17:38:22 -070021#include <sync-intro-certificate.h>
22#include <boost/random/random_device.hpp>
23#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yu64206112013-12-24 11:16:32 +080024#include <ndn-cpp/sha256-with-rsa-signature.hpp>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070025#include "logging.h"
26#endif
27
Yingdi Yu04842232013-10-23 14:03:09 -070028using namespace std;
Yingdi Yu04842232013-10-23 14:03:09 -070029
Yingdi Yuc4d08d22013-10-23 23:07:29 -070030INIT_LOGGER("ChatDialog");
31
Yingdi Yu42f66462013-10-31 17:38:22 -070032static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
33
34Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
35Q_DECLARE_METATYPE(size_t)
36
Yingdi Yu64206112013-12-24 11:16:32 +080037ChatDialog::ChatDialog(ndn::ptr_lib::shared_ptr<ContactManager> contactManager,
Yingdi Yu42f66462013-10-31 17:38:22 -070038 const ndn::Name& chatroomPrefix,
39 const ndn::Name& localPrefix,
40 const ndn::Name& defaultIdentity,
Yingdi Yu42372442013-11-06 18:43:31 -080041 const std::string& nick,
42 bool trial,
Yingdi Yu04842232013-10-23 14:03:09 -070043 QWidget *parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070044: QDialog(parent)
45 , ui(new Ui::ChatDialog)
46 , m_contactManager(contactManager)
47 , m_chatroomPrefix(chatroomPrefix)
48 , m_localPrefix(localPrefix)
49 , m_defaultIdentity(defaultIdentity)
Yingdi Yu64206112013-12-24 11:16:32 +080050 , m_invitationPolicyManager(new InvitationPolicyManager(m_chatroomPrefix.get(-1).toEscapedString(), m_defaultIdentity))
Yingdi Yu42372442013-11-06 18:43:31 -080051 , m_nick(nick)
Yingdi Yu42f66462013-10-31 17:38:22 -070052 , m_sock(NULL)
53 , m_lastMsgTime(0)
54 // , m_historyInitialized(false)
55 , m_joined(false)
56 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Yingdi Yu04842232013-10-23 14:03:09 -070057{
Yingdi Yu42f66462013-10-31 17:38:22 -070058 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
59 qRegisterMetaType<size_t>("size_t");
60
Yingdi Yuc4d08d22013-10-23 23:07:29 -070061 ui->setupUi(this);
62
Yingdi Yu2ab22e72013-11-10 01:38:21 -080063 QString randString = getRandomString();
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 Yu2ab22e72013-11-10 01:38:21 -080066 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
Yingdi Yu42f66462013-10-31 17:38:22 -070067
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 Yu64206112013-12-24 11:16:32 +080088 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameForIdentity(m_defaultIdentity);
89 m_syncPolicyManager = ndn::ptr_lib::make_shared<SyncPolicyManager>(m_defaultIdentity, certificateName, m_chatroomPrefix);
90
91 m_transport = ndn::ptr_lib::make_shared<ndn::TcpTransport>();
92 m_face = ndn::ptr_lib::make_shared<ndn::Face>(m_transport, ndn::ptr_lib::make_shared<ndn::TcpTransport::ConnectionInfo>("localhost"));
93
94 connectToDaemon();
Yingdi Yu42f66462013-10-31 17:38:22 -070095
96 connect(ui->inviteButton, SIGNAL(clicked()),
97 this, SLOT(openInviteListDialog()));
98 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
99 this, SLOT(sendInvitationWrapper(QString, bool)));
100 connect(ui->lineEdit, SIGNAL(returnPressed()),
101 this, SLOT(returnPressed()));
102 connect(ui->treeButton, SIGNAL(pressed()),
103 this, SLOT(treeButtonPressed()));
104 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
105 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
106 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
107 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
108 connect(m_timer, SIGNAL(timeout()),
109 this, SLOT(replot()));
110 connect(m_scene, SIGNAL(replot()),
111 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800112 connect(trayIcon, SIGNAL(messageClicked()),
113 this, SLOT(showNormal()));
114 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
115 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700116 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
117 this, SLOT(updateRosterList(QStringList)));
118
Yingdi Yu42f66462013-10-31 17:38:22 -0700119
120 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700121}
122
Yingdi Yu42f66462013-10-31 17:38:22 -0700123
Yingdi Yu04842232013-10-23 14:03:09 -0700124ChatDialog::~ChatDialog()
125{
Yingdi Yu42372442013-11-06 18:43:31 -0800126 if(m_sock != NULL)
127 {
128 sendLeave();
129 delete m_sock;
130 m_sock = NULL;
131 }
Yingdi Yu64206112013-12-24 11:16:32 +0800132 m_face->shutdown();
Yingdi Yu04842232013-10-23 14:03:09 -0700133}
134
135void
Yingdi Yu64206112013-12-24 11:16:32 +0800136ChatDialog::connectToDaemon()
Yingdi Yu04842232013-10-23 14:03:09 -0700137{
Yingdi Yu64206112013-12-24 11:16:32 +0800138 //Hack! transport does not connect to daemon unless an interest is expressed.
139 ndn::Name name("/ndn");
140 ndn::ptr_lib::shared_ptr<ndn::Interest> interest = ndn::ptr_lib::make_shared<ndn::Interest>(name);
141 m_face->expressInterest(*interest,
142 boost::bind(&ChatDialog::onConnectionData, this, _1, _2),
143 boost::bind(&ChatDialog::onConnectionDataTimeout, this, _1));
Yingdi Yu04842232013-10-23 14:03:09 -0700144}
145
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700146void
Yingdi Yu64206112013-12-24 11:16:32 +0800147ChatDialog::onConnectionData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
148 const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
149{ _LOG_DEBUG("onConnectionData"); }
150
151void
152ChatDialog::onConnectionDataTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest)
153{ _LOG_DEBUG("onConnectionDataTimeout"); }
154
155void
Yingdi Yu42f66462013-10-31 17:38:22 -0700156ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700157{
Yingdi Yu42372442013-11-06 18:43:31 -0800158 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu64206112013-12-24 11:16:32 +0800159 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toEscapedString()));
Yingdi Yu42f66462013-10-31 17:38:22 -0700160 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
161 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
162 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
163}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700164
Yingdi Yu42f66462013-10-31 17:38:22 -0700165void
166ChatDialog::updateLabels()
167{
168 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
169 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
170 ui->infoLabel->setText(settingDisp);
171 QString prefixDisp;
172 if (m_user.getPrefix().startsWith("/private/local"))
173 {
174 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
175 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
176 }
177 else
178 {
179 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
180 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
181 }
182 ui->prefixLabel->setText(prefixDisp);
183}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700184
Yingdi Yu42f66462013-10-31 17:38:22 -0700185void
Yingdi Yu64206112013-12-24 11:16:32 +0800186ChatDialog::sendInterest(const ndn::Interest& interest,
187 const ndn::OnVerified& onVerified,
188 const ndn::OnVerifyFailed& onVerifyFailed,
189 const OnEventualTimeout& timeoutNotify,
190 const ndn::ptr_lib::shared_ptr<ndn::PolicyManager>& policyManager,
191 int retry /* = 1 */,
192 int stepCount /* = 0 */)
193{
194 m_face->expressInterest(interest,
195 boost::bind(&ChatDialog::onTargetData,
196 this,
197 _1,
198 _2,
199 stepCount,
200 onVerified,
201 onVerifyFailed,
202 timeoutNotify,
203 policyManager),
204 boost::bind(&ChatDialog::onTargetTimeout,
205 this,
206 _1,
207 retry,
208 stepCount,
209 onVerified,
210 onVerifyFailed,
211 timeoutNotify,
212 policyManager));
213}
214
215void
216ChatDialog::onTargetData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
217 const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
218 int stepCount,
219 const ndn::OnVerified& onVerified,
220 const ndn::OnVerifyFailed& onVerifyFailed,
221 const OnEventualTimeout& timeoutNotify,
222 const ndn::ptr_lib::shared_ptr<ndn::PolicyManager>& policyManager)
223{
224 ndn::ptr_lib::shared_ptr<ndn::ValidationRequest> nextStep = policyManager->checkVerificationPolicy(data, stepCount, onVerified, onVerifyFailed);
225
226 if (nextStep)
227 m_face->expressInterest
228 (*nextStep->interest_,
229 boost::bind(&ChatDialog::onCertData, this, _1, _2, nextStep, policyManager),
230 boost::bind(&ChatDialog::onCertTimeout, this, _1, onVerifyFailed, data, nextStep, policyManager));
231}
232
233void
234ChatDialog::onTargetTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
235 int retry,
236 int stepCount,
237 const ndn::OnVerified& onVerified,
238 const ndn::OnVerifyFailed& onVerifyFailed,
239 const OnEventualTimeout& timeoutNotify,
240 const ndn::ptr_lib::shared_ptr<ndn::PolicyManager>& policyManager)
241{
242 if(retry > 0)
243 sendInterest(*interest, onVerified, onVerifyFailed, timeoutNotify, policyManager, retry-1, stepCount);
244 else
245 {
246 _LOG_DEBUG("Interest: " << interest->getName().toUri() << " eventually times out!");
247 timeoutNotify();
248 }
249}
250
251void
252ChatDialog::onCertData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
253 const ndn::ptr_lib::shared_ptr<ndn::Data>& cert,
254 ndn::ptr_lib::shared_ptr<ndn::ValidationRequest> previousStep,
255 const ndn::ptr_lib::shared_ptr<ndn::PolicyManager>& policyManager)
256{
257 ndn::ptr_lib::shared_ptr<ndn::ValidationRequest> nextStep = policyManager->checkVerificationPolicy(cert,
258 previousStep->stepCount_,
259 previousStep->onVerified_,
260 previousStep->onVerifyFailed_);
261
262 if (nextStep)
263 m_face->expressInterest
264 (*nextStep->interest_,
265 boost::bind(&ChatDialog::onCertData, this, _1, _2, nextStep, policyManager),
266 boost::bind(&ChatDialog::onCertTimeout, this, _1, previousStep->onVerifyFailed_, cert, nextStep, policyManager));
267}
268
269void
270ChatDialog::onCertTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
271 const ndn::OnVerifyFailed& onVerifyFailed,
272 const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
273 ndn::ptr_lib::shared_ptr<ndn::ValidationRequest> nextStep,
274 const ndn::ptr_lib::shared_ptr<ndn::PolicyManager>& policyManager)
275{
276 if(nextStep->retry_ > 0)
277 m_face->expressInterest(*interest,
278 boost::bind(&ChatDialog::onCertData,
279 this,
280 _1,
281 _2,
282 nextStep,
283 policyManager),
284 boost::bind(&ChatDialog::onCertTimeout,
285 this,
286 _1,
287 onVerifyFailed,
288 data,
289 nextStep,
290 policyManager));
291 else
292 onVerifyFailed(data);
293}
294
295void
296ChatDialog::sendInvitation(ndn::ptr_lib::shared_ptr<ContactItem> contact, bool isIntroducer)
Yingdi Yu42f66462013-10-31 17:38:22 -0700297{
298 m_invitationPolicyManager->addTrustAnchor(contact->getSelfEndorseCertificate());
299
Yingdi Yu64206112013-12-24 11:16:32 +0800300 ndn::Name certificateName = m_identityManager->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -0700301
302 ndn::Name interestName("/ndn/broadcast/chronos/invitation");
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700303 interestName.append(contact->getNameSpace());
304 interestName.append("chatroom");
305 interestName.append(m_chatroomPrefix.get(-1));
306 interestName.append("inviter-prefix");
307 interestName.append(m_localPrefix);
308 interestName.append("inviter");
309 interestName.append(certificateName);
310
311 string signedUri = interestName.toUri();
Yingdi Yu64206112013-12-24 11:16:32 +0800312 ndn::Blob signedBlob((const uint8_t*)signedUri.c_str(), signedUri.size());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700313
Yingdi Yu64206112013-12-24 11:16:32 +0800314 ndn::ptr_lib::shared_ptr<const ndn::Sha256WithRsaSignature> sha256sig = ndn::ptr_lib::dynamic_pointer_cast<const ndn::Sha256WithRsaSignature>(m_identityManager->signByCertificate(signedBlob.buf(), signedBlob.size(), certificateName));
315 const ndn::Blob& sigBits = sha256sig->getSignature();
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700316
Yingdi Yu64206112013-12-24 11:16:32 +0800317 interestName.append(sigBits);
318 //TODO... remove version from invitation interest
319 // interestName.appendVersion();
Yingdi Yu42372442013-11-06 18:43:31 -0800320
Yingdi Yu64206112013-12-24 11:16:32 +0800321 ndn::Interest interest(interestName);
322 ndn::OnVerified onVerified = boost::bind(&ChatDialog::onInviteReplyVerified,
323 this,
324 _1,
325 contact->getNameSpace(),
326 isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700327
Yingdi Yu64206112013-12-24 11:16:32 +0800328 ndn::OnVerifyFailed onVerifyFailed = boost::bind(&ChatDialog::onInviteReplyVerifyFailed,
329 this,
330 _1,
331 contact->getNameSpace());
332
333 OnEventualTimeout timeoutNotify = boost::bind(&ChatDialog::onInviteReplyTimeout,
334 this,
335 contact->getNameSpace());
336
337
338 sendInterest(interest, onVerified, onVerifyFailed, timeoutNotify, m_invitationPolicyManager);
339}
340
341void
342ChatDialog::onInviteReplyVerified(const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
343 const ndn::Name& identity,
344 bool isIntroducer)
345{
346 string content((const char*)data->getContent().buf(), data->getContent().size());
347 if(content == string("nack"))
348 invitationRejected(identity);
349 else
350 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700351}
352
353void
Yingdi Yu64206112013-12-24 11:16:32 +0800354ChatDialog::onInviteReplyVerifyFailed(const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
355 const ndn::Name& identity)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700356{
Yingdi Yu64206112013-12-24 11:16:32 +0800357 _LOG_DEBUG("Reply from " << identity.toUri() << " cannot be verified!");
358 QString msg = QString::fromUtf8("Reply from ") + QString::fromStdString(identity.toUri()) + " cannot be verified!";
359 emit inivationRejection(msg);
360}
361
362
363void
364ChatDialog::onInviteReplyTimeout(const ndn::Name& identity)
365{
366 _LOG_DEBUG("Your invitation to " << identity.toUri() << " times out!");
367 QString msg = QString::fromUtf8("Your invitation to ") + QString::fromStdString(identity.toUri()) + " times out!";
368 emit inivationRejection(msg);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700369}
370
371void
Yingdi Yu42f66462013-10-31 17:38:22 -0700372ChatDialog::invitationRejected(const ndn::Name& identity)
373{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800374 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu3e87bd82013-11-10 10:47:44 -0800375 QString msg = QString::fromStdString(identity.toUri()) + " Rejected your invitation!";
376 emit inivationRejection(msg);
Yingdi Yu42f66462013-10-31 17:38:22 -0700377}
378
379void
Yingdi Yu64206112013-12-24 11:16:32 +0800380ChatDialog::invitationAccepted(const ndn::Name& identity, ndn::ptr_lib::shared_ptr<ndn::Data> data, const string& inviteePrefix, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700381{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800382 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yu64206112013-12-24 11:16:32 +0800383 const ndn::Sha256WithRsaSignature* sha256sig = dynamic_cast<const ndn::Sha256WithRsaSignature*>(data->getSignature());
Yingdi Yu42f66462013-10-31 17:38:22 -0700384 const ndn::Name & keyLocatorName = sha256sig->getKeyLocator().getKeyName();
Yingdi Yu64206112013-12-24 11:16:32 +0800385 ndn::ptr_lib::shared_ptr<ndn::IdentityCertificate> dskCertificate = m_invitationPolicyManager->getValidatedDskCertificate(keyLocatorName);
Yingdi Yuc90deb12013-11-06 18:51:19 -0800386 m_syncPolicyManager->addChatDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800387 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700388}
389
Yingdi Yu64206112013-12-24 11:16:32 +0800390void
391ChatDialog::publishIntroCert(const ndn::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700392{
Yingdi Yu64206112013-12-24 11:16:32 +0800393 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
394 dskCertificate.getPublicKeyName(),
395 m_identityManager->getDefaultKeyNameForIdentity(m_defaultIdentity),
396 dskCertificate.getNotBefore(),
397 dskCertificate.getNotAfter(),
398 dskCertificate.getPublicKeyInfo(),
399 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
400 ndn::Name certName = m_identityManager->getDefaultCertificateNameForIdentity(m_defaultIdentity);
401 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
402 m_identityManager->signByCertificate(syncIntroCertificate, certName);
403 m_transport->send(*syncIntroCertificate.wireEncode());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700404}
405
Yingdi Yu64206112013-12-24 11:16:32 +0800406void
407ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
408{ m_invitationPolicyManager->addTrustAnchor(selfEndorseCertificate); }
409
410void
411ChatDialog::addChatDataRule(const ndn::Name& prefix,
412 const ndn::IdentityCertificate& identityCertificate,
413 bool isIntroducer)
414{ m_syncPolicyManager->addChatDataRule(prefix, identityCertificate, isIntroducer); }
415
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700416
Yingdi Yu42372442013-11-06 18:43:31 -0800417
418void
Yingdi Yu42f66462013-10-31 17:38:22 -0700419ChatDialog::initializeSync()
420{
421
422 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
423 m_syncPolicyManager,
Yingdi Yu64206112013-12-24 11:16:32 +0800424 boost::bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
425 boost::bind(&ChatDialog::processRemoveWrapper, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700426
427 usleep(100000);
428
429 QTimer::singleShot(600, this, SLOT(sendJoin()));
430 m_timer->start(FRESHNESS * 1000);
431 disableTreeDisplay();
432 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
433 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
434 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700435}
436
437void
438ChatDialog::returnPressed()
439{
440 QString text = ui->lineEdit->text();
441 if (text.isEmpty())
442 return;
443
444 ui->lineEdit->clear();
445
446 if (text.startsWith("boruoboluomi"))
447 {
448 summonReaper ();
449 // reapButton->show();
450 fitView();
451 return;
452 }
453
454 if (text.startsWith("minimanihong"))
455 {
456 // reapButton->hide();
457 fitView();
458 return;
459 }
460
461 SyncDemo::ChatMessage msg;
462 formChatMessage(text, msg);
463
464 appendMessage(msg);
465
466 sendMsg(msg);
467
468 fitView();
469}
470
471void
472ChatDialog::treeButtonPressed()
473{
474 if (ui->treeViewer->isVisible())
475 {
476 ui->treeViewer->hide();
477 ui->treeButton->setText("Show ChronoSync Tree");
478 }
479 else
480 {
481 ui->treeViewer->show();
482 ui->treeButton->setText("Hide ChronoSync Tree");
483 }
484
485 fitView();
486}
487
488void ChatDialog::disableTreeDisplay()
489{
490 ui->treeButton->setEnabled(false);
491 ui->treeViewer->hide();
492 fitView();
493}
494
495void ChatDialog::enableTreeDisplay()
496{
497 ui->treeButton->setEnabled(true);
498 // treeViewer->show();
499 // fitView();
500}
501
502void
503ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
504{
505 emit treeUpdated(v);
506 _LOG_DEBUG("<<< Tree update signal emitted");
507}
508
509void
510ChatDialog::processRemoveWrapper(std::string prefix)
511{
512 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
513}
514
515void
516ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
517{
518 _LOG_DEBUG("<<< processing Tree Update");
519
520 if (v.empty())
521 {
522 return;
523 }
524
525 // reflect the changes on digest tree
526 {
527 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
528 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
529 }
530
531 int n = v.size();
532 int totalMissingPackets = 0;
533 for (int i = 0; i < n; i++)
534 {
535 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
536 }
537
538 for (int i = 0; i < n; i++)
539 {
540 if (totalMissingPackets < 4)
541 {
542 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
543 {
544 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
545 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
546 }
547 }
548 else
549 {
550 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
551 }
552 }
553
554 // adjust the view
555 fitView();
556
557}
558
559void
Yingdi Yu64206112013-12-24 11:16:32 +0800560ChatDialog::processDataWrapper(const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700561{
562 string name = data->getName().toUri();
Yingdi Yu64206112013-12-24 11:16:32 +0800563 const char* buf = (const char*)data->getContent().buf();
564 size_t len = data->getContent().size();
Yingdi Yu42f66462013-10-31 17:38:22 -0700565
566 char *tempBuf = new char[len];
567 memcpy(tempBuf, buf, len);
568 emit dataReceived(name.c_str(), tempBuf, len, true, false);
569 _LOG_DEBUG("<<< " << name << " fetched");
570}
571
572void
Yingdi Yu64206112013-12-24 11:16:32 +0800573ChatDialog::processDataNoShowWrapper(const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700574{
575 string name = data->getName().toUri();
Yingdi Yu64206112013-12-24 11:16:32 +0800576 const char* buf = (const char*)data->getContent().buf();
577 size_t len = data->getContent().size();
Yingdi Yu42f66462013-10-31 17:38:22 -0700578
579 char *tempBuf = new char[len];
580 memcpy(tempBuf, buf, len);
581 emit dataReceived(name.c_str(), tempBuf, len, false, false);
582
583 // if (!m_historyInitialized)
584 // {
585 // fetchHistory(name);
586 // m_historyInitialized = true;
587 // }
588}
589
590// void
591// ChatDialog::fetchHistory(std::string name)
592// {
593
594// /****************************/
595// /* TODO: fix following part */
596// /****************************/
597// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
598// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
599// prefix += "/history";
600// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
601// // QString randomString = getRandomString();
602// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
603// // {
604// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
605// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
606// // }
607// }
608
609void
610ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
611{
612 SyncDemo::ChatMessage msg;
613 bool corrupted = false;
614 if (!msg.ParseFromArray(buf, len))
615 {
616 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
617 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
618 msg.set_from("inconnu");
619 msg.set_type(SyncDemo::ChatMessage::OTHER);
620 corrupted = true;
621 }
622
623 delete [] buf;
624 buf = NULL;
625
626 // display msg received from network
627 // we have to do so; this function is called by ccnd thread
628 // so if we call appendMsg directly
629 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
630 // the "cannonical" way to is use signal-slot
631 if (show && !corrupted)
632 {
633 appendMessage(msg, isHistory);
634 }
635
636 if (!isHistory)
637 {
638 // update the tree view
639 std::string stdStrName = name.toStdString();
640 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
641 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
642 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
643 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
644 {
645 processRemove(prefix.c_str());
646 }
647 else
648 {
649 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
650 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
651 }
652 }
653 fitView();
654}
655
656void
657ChatDialog::processRemove(QString prefix)
658{
659 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
660
661 bool removed = m_scene->removeNode(prefix);
662 if (removed)
663 {
664 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
665 m_scene->plot(m_sock->getRootDigest().c_str());
666 }
667}
668
669void
670ChatDialog::sendJoin()
671{
672 m_joined = true;
673 SyncDemo::ChatMessage msg;
674 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
675 sendMsg(msg);
676 boost::random::random_device rng;
677 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
678 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
679 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
680}
681
682void
683ChatDialog::sendHello()
684{
685 time_t now = time(NULL);
686 int elapsed = now - m_lastMsgTime;
687 if (elapsed >= m_randomizedInterval / 1000)
688 {
689 SyncDemo::ChatMessage msg;
690 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
691 sendMsg(msg);
692 boost::random::random_device rng;
693 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
694 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
695 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
696 }
697 else
698 {
699 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
700 }
701}
702
703void
704ChatDialog::sendLeave()
705{
706 SyncDemo::ChatMessage msg;
707 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
708 sendMsg(msg);
709 usleep(500000);
710 m_sock->remove(m_user.getPrefix().toStdString());
711 usleep(5000);
712 m_joined = false;
713 _LOG_DEBUG("Sync REMOVE signal sent");
714}
715
716void
717ChatDialog::replot()
718{
719 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
720 m_scene->plot(m_sock->getRootDigest().c_str());
721 fitView();
722}
723
724void
725ChatDialog::summonReaper()
726{
727 Sync::SyncLogic &logic = m_sock->getLogic ();
728 map<string, bool> branches = logic.getBranchPrefixes();
729 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
730
731 m_zombieList.clear();
732
733 QMapIterator<QString, DisplayUserPtr> it(roster);
734 map<string, bool>::iterator mapIt;
735 while(it.hasNext())
736 {
737 it.next();
738 DisplayUserPtr p = it.value();
739 if (p != DisplayUserNullPtr)
740 {
741 mapIt = branches.find(p->getPrefix().toStdString());
742 if (mapIt != branches.end())
743 {
744 mapIt->second = true;
745 }
746 }
747 }
748
749 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
750 {
751 // this is zombie. all active users should have been marked true
752 if (! mapIt->second)
753 {
754 m_zombieList.append(mapIt->first.c_str());
755 }
756 }
757
758 m_zombieIndex = 0;
759
760 // start reaping
761 reap();
762}
763
764void
765ChatDialog::reap()
766{
767 if (m_zombieIndex < m_zombieList.size())
768 {
769 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
770 m_sock->remove(prefix);
771 _LOG_DEBUG("Reaped: prefix = " << prefix);
772 m_zombieIndex++;
773 // reap again in 10 seconds
774 QTimer::singleShot(10000, this, SLOT(reap()));
775 }
776}
777
778void
779ChatDialog::updateRosterList(QStringList staleUserList)
780{
781 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
782 QStringList rosterList = m_scene->getRosterList();
783 m_rosterModel->setStringList(rosterList);
784 QString user;
785 QStringListIterator it(staleUserList);
786 while(it.hasNext())
787 {
788 std::string nick = it.next().toStdString();
789 if (nick.empty())
790 continue;
791
792 SyncDemo::ChatMessage msg;
793 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
794 msg.set_from(nick);
795 appendMessage(msg);
796 }
797}
798
799void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800800ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
801{
Yingdi Yu9f657242013-11-10 12:25:43 -0800802 _LOG_DEBUG("called");
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800803 QString randString = getRandomString();
804 bool needWrite = false;
805 bool needFresh = false;
806
807 QString oldPrefix = m_user.getPrefix();
808 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
809 m_user.setOriginPrefix(originPrefix);
810
811 m_localPrefix = ndn::Name(originPrefix.toStdString());
812 m_localChatPrefix = m_localPrefix;
813 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
814 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
815 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
816 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
817 needWrite = true;
818 needFresh = true;
819 }
820
821 if (needWrite) {
822 updateLabels();
823 }
824
825 if (needFresh && m_sock != NULL)
826 {
827
828 {
829 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
830 m_scene->clearAll();
831 m_scene->plot("Empty");
832 }
833
Yingdi Yu0a953c32013-11-10 10:32:18 -0800834 ui->textEdit->clear();
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800835
836 // keep the new prefix
837 QString newPrefix = m_user.getPrefix();
838 // send leave for the old
839 m_user.setPrefix(oldPrefix);
840 // there is no point to send leave if we haven't joined yet
841 if (m_joined)
842 {
843 sendLeave();
844 }
845 // resume new prefix
846 m_user.setPrefix(newPrefix);
847 // Sync::CcnxWrapperPtr handle = Sync::CcnxWrapper::Create();
848 // handle->clearInterestFilter(oldPrefix.toStdString());
849 delete m_sock;
850 m_sock = NULL;
851
852 try
853 {
854 usleep(100000);
855 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
856 m_syncPolicyManager,
857 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
858 bind(&ChatDialog::processRemoveWrapper, this, _1));
859 usleep(100000);
860 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
861 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu0a953c32013-11-10 10:32:18 -0800862 QTimer::singleShot(600, this, SLOT(sendJoin()));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800863 m_timer->start(FRESHNESS * 1000);
864 disableTreeDisplay();
Yingdi Yu0a953c32013-11-10 10:32:18 -0800865 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
Yingdi Yu64206112013-12-24 11:16:32 +0800866 }catch(std::exception& e){
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800867 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
868 }
869 }
870 else if (needFresh && m_sock == NULL)
871 {
872 initializeSync();
873 }
874 else if (m_sock == NULL)
875 {
876 initializeSync();
877 }
878 else
879 {
880// #ifdef __DEBUG
881// std::cout << "Just changing nicks, we're good. " << std::endl;
882// #endif
883 }
884
885 fitView();
886}
887
888void
Yingdi Yua0594092013-11-06 22:07:38 -0800889ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
890{
891 switch (reason)
892 {
893 case QSystemTrayIcon::Trigger:
894 case QSystemTrayIcon::DoubleClick:
895 break;
896 case QSystemTrayIcon::MiddleClick:
897 // showMessage();
898 break;
899 default:;
900 }
901}
902
903
904void
905ChatDialog::messageClicked()
906{
907 this->showMaximized();
908}
909
910
911void
912ChatDialog::createActions()
913{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800914 minimizeAction = new QAction(tr("Mi&nimize"), this);
915 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800916
Yingdi Yu07b5b092013-11-07 17:00:54 -0800917 maximizeAction = new QAction(tr("Ma&ximize"), this);
918 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800919
Yingdi Yu07b5b092013-11-07 17:00:54 -0800920 restoreAction = new QAction(tr("&Restore"), this);
921 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800922
923 // settingsAction = new QAction(tr("Settings"), this);
924 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
925
926 // settingsAction->setMenuRole (QAction::PreferencesRole);
927
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800928 updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
929 connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
Yingdi Yua0594092013-11-06 22:07:38 -0800930
Yingdi Yu07b5b092013-11-07 17:00:54 -0800931 quitAction = new QAction(tr("Quit"), this);
932 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800933}
934
935void
936ChatDialog::createTrayIcon()
937{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800938 trayIconMenu = new QMenu(this);
939 trayIconMenu->addAction(minimizeAction);
940 trayIconMenu->addAction(maximizeAction);
941 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800942 // trayIconMenu->addSeparator();
943 // trayIconMenu->addAction(settingsAction);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800944 trayIconMenu->addSeparator();
945 trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800946 trayIconMenu->addSeparator();
947 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800948
949 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800950 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800951
952 QIcon icon(":/images/icon_small.png");
953 trayIcon->setIcon(icon);
954 setWindowIcon(icon);
955 trayIcon->setToolTip("ChronoChat System Tray Icon");
956 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800957}
958
959
960void
Yingdi Yu42f66462013-10-31 17:38:22 -0700961ChatDialog::resizeEvent(QResizeEvent *e)
962{
963 fitView();
964}
965
966void
967ChatDialog::showEvent(QShowEvent *e)
968{
969 fitView();
970}
971
972void
973ChatDialog::fitView()
974{
975 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
976 QRectF rect = m_scene->itemsBoundingRect();
977 m_scene->setSceneRect(rect);
978 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
979}
980
981void
982ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
983 msg.set_from(m_user.getNick().toStdString());
984 msg.set_to(m_user.getChatroom().toStdString());
985 msg.set_data(text.toUtf8().constData());
986 time_t seconds = time(NULL);
987 msg.set_timestamp(seconds);
988 msg.set_type(SyncDemo::ChatMessage::CHAT);
989}
990
991void
992ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
993{
994 msg.set_from(m_user.getNick().toStdString());
995 msg.set_to(m_user.getChatroom().toStdString());
996 time_t seconds = time(NULL);
997 msg.set_timestamp(seconds);
998 msg.set_type(type);
999}
1000
1001void
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001002ChatDialog::updateLocalPrefix()
1003{
Yingdi Yu0a953c32013-11-10 10:32:18 -08001004 m_newLocalPrefixReady = false;
Yingdi Yu64206112013-12-24 11:16:32 +08001005 ndn::Name interestName("/local/ndn/prefix");
1006 ndn::Interest interest(interestName);
1007 interest.setChildSelector(ndn_Interest_CHILD_SELECTOR_RIGHT);
1008 interest.setInterestLifetimeMilliseconds(1000);
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001009
Yingdi Yu64206112013-12-24 11:16:32 +08001010 m_face->expressInterest(interest,
1011 bind(&ChatDialog::onLocalPrefix, this, _1, _2),
1012 bind(&ChatDialog::onLocalPrefixTimeout, this, _1));
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001013
Yingdi Yu0a953c32013-11-10 10:32:18 -08001014 while(m_newLocalPrefixReady == false)
1015 {
1016#if BOOST_VERSION >= 1050000
1017 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
1018#else
1019 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
1020#endif
1021 }
Yingdi Yu9f657242013-11-10 12:25:43 -08001022 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
Yingdi Yu6b56f092013-11-10 11:54:02 -08001023 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
Yingdi Yu0a953c32013-11-10 10:32:18 -08001024 QString originPrefix = QString::fromStdString(m_newLocalPrefix.toUri());
1025
1026 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
1027 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001028}
1029
Yingdi Yu64206112013-12-24 11:16:32 +08001030
1031void
1032ChatDialog::onLocalPrefix(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
1033 const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
1034{
1035 string dataString((const char*)data->getContent().buf(), data->getContent().size());
1036 QString originPrefix = QString::fromStdString (dataString).trimmed ();
1037 string trimmedString = originPrefix.toStdString();
1038 m_newLocalPrefix = ndn::Name(trimmedString);
1039 m_newLocalPrefixReady = true;
1040}
1041
1042void
1043ChatDialog::onLocalPrefixTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest)
1044{
1045 m_newLocalPrefix = m_localPrefix;
1046 m_newLocalPrefixReady = true;
1047}
1048
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001049static std::string chars2("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
1050
1051QString
1052ChatDialog::getRandomString()
1053{
1054 std::string randStr;
1055 boost::random::random_device rng;
1056 boost::random::uniform_int_distribution<> index_dist(0, chars2.size() - 1);
1057 for (int i = 0; i < 10; i ++)
1058 {
1059 randStr += chars2[index_dist(rng)];
1060 }
1061 return randStr.c_str();
1062}
1063
1064void
Yingdi Yua0594092013-11-06 22:07:38 -08001065ChatDialog::changeEvent(QEvent *e)
1066{
1067 switch(e->type())
1068 {
1069 case QEvent::ActivationChange:
1070 if (isActiveWindow())
1071 {
1072 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
1073 }
1074 break;
1075 default:
1076 break;
1077 }
1078}
1079
1080void
Yingdi Yu42f66462013-10-31 17:38:22 -07001081ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
1082{
1083 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
1084
1085 if (msg.type() == SyncDemo::ChatMessage::CHAT)
1086 {
1087
1088 if (!msg.has_data())
1089 {
1090 return;
1091 }
1092
1093 if (msg.from().empty() || msg.data().empty())
1094 {
1095 return;
1096 }
1097
1098 if (!msg.has_timestamp())
1099 {
1100 return;
1101 }
1102
1103 // if (m_history.size() == MAX_HISTORY_ENTRY)
1104 // {
1105 // m_history.dequeue();
1106 // }
1107
1108 // m_history.enqueue(msg);
1109
1110 QTextCharFormat nickFormat;
1111 nickFormat.setForeground(Qt::darkGreen);
1112 nickFormat.setFontWeight(QFont::Bold);
1113 nickFormat.setFontUnderline(true);
1114 nickFormat.setUnderlineColor(Qt::gray);
1115
1116 QTextCursor cursor(ui->textEdit->textCursor());
1117 cursor.movePosition(QTextCursor::End);
1118 QTextTableFormat tableFormat;
1119 tableFormat.setBorder(0);
1120 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1121 QString from = QString("%1 ").arg(msg.from().c_str());
1122 QTextTableCell fromCell = table->cellAt(0, 0);
1123 fromCell.setFormat(nickFormat);
1124 fromCell.firstCursorPosition().insertText(from);
1125
1126 time_t timestamp = msg.timestamp();
1127 printTimeInCell(table, timestamp);
1128
1129 QTextCursor nextCursor(ui->textEdit->textCursor());
1130 nextCursor.movePosition(QTextCursor::End);
1131 table = nextCursor.insertTable(1, 1, tableFormat);
1132 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
1133 if (!isHistory)
1134 {
1135 showMessage(from, QString::fromUtf8(msg.data().c_str()));
1136 }
1137 }
1138
1139 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
1140 {
1141 QTextCharFormat nickFormat;
1142 nickFormat.setForeground(Qt::gray);
1143 nickFormat.setFontWeight(QFont::Bold);
1144 nickFormat.setFontUnderline(true);
1145 nickFormat.setUnderlineColor(Qt::gray);
1146
1147 QTextCursor cursor(ui->textEdit->textCursor());
1148 cursor.movePosition(QTextCursor::End);
1149 QTextTableFormat tableFormat;
1150 tableFormat.setBorder(0);
1151 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1152 QString action;
1153 if (msg.type() == SyncDemo::ChatMessage::JOIN)
1154 {
1155 action = "enters room";
1156 }
1157 else
1158 {
1159 action = "leaves room";
1160 }
1161
1162 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
1163 QTextTableCell fromCell = table->cellAt(0, 0);
1164 fromCell.setFormat(nickFormat);
1165 fromCell.firstCursorPosition().insertText(from);
1166
1167 time_t timestamp = msg.timestamp();
1168 printTimeInCell(table, timestamp);
1169 }
1170
1171 QScrollBar *bar = ui->textEdit->verticalScrollBar();
1172 bar->setValue(bar->maximum());
1173}
1174
1175QString
1176ChatDialog::formatTime(time_t timestamp)
1177{
1178 struct tm *tm_time = localtime(&timestamp);
1179 int hour = tm_time->tm_hour;
1180 QString amOrPM;
1181 if (hour > 12)
1182 {
1183 hour -= 12;
1184 amOrPM = "PM";
1185 }
1186 else
1187 {
1188 amOrPM = "AM";
1189 if (hour == 0)
1190 {
1191 hour = 12;
1192 }
1193 }
1194
1195 char textTime[12];
1196 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
1197 return QString(textTime);
1198}
1199
1200void
1201ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
1202{
1203 QTextCharFormat timeFormat;
1204 timeFormat.setForeground(Qt::gray);
1205 timeFormat.setFontUnderline(true);
1206 timeFormat.setUnderlineColor(Qt::gray);
1207 QTextTableCell timeCell = table->cellAt(0, 1);
1208 timeCell.setFormat(timeFormat);
1209 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
1210}
1211
1212void
1213ChatDialog::showMessage(QString from, QString data)
1214{
1215 if (!isActiveWindow())
1216 {
Yingdi Yua0594092013-11-06 22:07:38 -08001217 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
1218 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -07001219 }
1220}
1221
1222void
1223ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
1224{
1225 // send msg
1226 size_t size = msg.ByteSize();
1227 char *buf = new char[size];
1228 msg.SerializeToArray(buf, size);
1229 if (!msg.IsInitialized())
1230 {
1231 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
1232 abort();
1233 }
1234 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
1235
1236 delete buf;
1237
1238 m_lastMsgTime = time(NULL);
1239
1240 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
1241 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
1242 std::vector<Sync::MissingDataInfo> v;
1243 v.push_back(mdi);
1244 {
1245 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1246 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
1247 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
1248 }
1249}
1250
1251void
1252ChatDialog::openInviteListDialog()
1253{
1254 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1255 m_inviteListDialog->show();
1256}
1257
1258void
1259ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1260{
Yingdi Yu64206112013-12-24 11:16:32 +08001261 ndn::Name inviteeNamespace(invitee.toStdString());
1262 ndn::ptr_lib::shared_ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
Yingdi Yu42f66462013-10-31 17:38:22 -07001263 sendInvitation(inviteeItem, isIntroducer);
1264}
1265
Yingdi Yu42372442013-11-06 18:43:31 -08001266void
1267ChatDialog::closeEvent(QCloseEvent *e)
1268{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001269 if (trayIcon->isVisible())
1270 {
1271 QMessageBox::information(this, tr("Chronos"),
1272 tr("The program will keep running in the "
1273 "system tray. To terminate the program"
1274 "choose <b>Quit</b> in the context memu"
1275 "of the system tray entry."));
1276 hide();
1277 e->ignore();
1278 }
1279}
1280
1281void
1282ChatDialog::quit()
1283{
Yingdi Yu42372442013-11-06 18:43:31 -08001284 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001285 emit closeChatDialog(m_chatroomPrefix);
1286}
1287
1288
1289
Yingdi Yu42f66462013-10-31 17:38:22 -07001290
Yingdi Yu04842232013-10-23 14:03:09 -07001291#if WAF
1292#include "chatdialog.moc"
1293#include "chatdialog.cpp.moc"
1294#endif