blob: 14dd7a5529c240d15dd06e9f71284a6d799be383 [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>
Yingdi Yu6df61252014-01-21 11:05:11 -080022#include "chronos-invitation.h"
Yingdi Yu42f66462013-10-31 17:38:22 -070023#include <boost/random/random_device.hpp>
24#include <boost/random/uniform_int_distribution.hpp>
Yingdi Yu6df61252014-01-21 11:05:11 -080025#include <ndn-cpp-dev/security/signature-sha256-with-rsa.hpp>
Yingdi Yuc4d08d22013-10-23 23:07:29 -070026#include "logging.h"
27#endif
28
Yingdi Yu04842232013-10-23 14:03:09 -070029using namespace std;
Yingdi Yu04842232013-10-23 14:03:09 -070030
Yingdi Yuc4d08d22013-10-23 23:07:29 -070031INIT_LOGGER("ChatDialog");
32
Yingdi Yu42f66462013-10-31 17:38:22 -070033static const int HELLO_INTERVAL = FRESHNESS * 3 / 4;
34
35Q_DECLARE_METATYPE(std::vector<Sync::MissingDataInfo> )
36Q_DECLARE_METATYPE(size_t)
37
Yingdi Yu64206112013-12-24 11:16:32 +080038ChatDialog::ChatDialog(ndn::ptr_lib::shared_ptr<ContactManager> contactManager,
Yingdi Yu42f66462013-10-31 17:38:22 -070039 const ndn::Name& chatroomPrefix,
40 const ndn::Name& localPrefix,
41 const ndn::Name& defaultIdentity,
Yingdi Yu42372442013-11-06 18:43:31 -080042 const std::string& nick,
43 bool trial,
Yingdi Yu04842232013-10-23 14:03:09 -070044 QWidget *parent)
Yingdi Yu42f66462013-10-31 17:38:22 -070045: QDialog(parent)
46 , ui(new Ui::ChatDialog)
47 , m_contactManager(contactManager)
48 , m_chatroomPrefix(chatroomPrefix)
49 , m_localPrefix(localPrefix)
50 , m_defaultIdentity(defaultIdentity)
Yingdi Yueaa84e22014-01-16 10:30:26 -080051 , m_invitationPolicy(new SecPolicyChronoChatInvitation(m_chatroomPrefix.get(-1).toEscapedString(), m_defaultIdentity))
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -080052 , m_keyChain(new ndn::KeyChain())
Yingdi Yu42372442013-11-06 18:43:31 -080053 , m_nick(nick)
Yingdi Yu42f66462013-10-31 17:38:22 -070054 , m_sock(NULL)
55 , m_lastMsgTime(0)
56 // , m_historyInitialized(false)
57 , m_joined(false)
58 , m_inviteListDialog(new InviteListDialog(m_contactManager))
Yingdi Yu04842232013-10-23 14:03:09 -070059{
Yingdi Yu42f66462013-10-31 17:38:22 -070060 qRegisterMetaType<std::vector<Sync::MissingDataInfo> >("std::vector<Sync::MissingDataInfo>");
61 qRegisterMetaType<size_t>("size_t");
62
Yingdi Yuc4d08d22013-10-23 23:07:29 -070063 ui->setupUi(this);
64
Yingdi Yu2ab22e72013-11-10 01:38:21 -080065 QString randString = getRandomString();
Yingdi Yu42f66462013-10-31 17:38:22 -070066 m_localChatPrefix = m_localPrefix;
Yingdi Yu42372442013-11-06 18:43:31 -080067 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
Yingdi Yu2ab22e72013-11-10 01:38:21 -080068 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
Yingdi Yu42f66462013-10-31 17:38:22 -070069
70 m_session = time(NULL);
71 m_scene = new DigestTreeScene(this);
72
73 initializeSetting();
74 updateLabels();
75
76 ui->treeViewer->setScene(m_scene);
77 ui->treeViewer->hide();
78 m_scene->plot("Empty");
79 QRectF rect = m_scene->itemsBoundingRect();
80 m_scene->setSceneRect(rect);
81
82 m_rosterModel = new QStringListModel(this);
83 ui->listView->setModel(m_rosterModel);
84
Yingdi Yua0594092013-11-06 22:07:38 -080085 createActions();
86 createTrayIcon();
87
Yingdi Yu42f66462013-10-31 17:38:22 -070088 m_timer = new QTimer(this);
89
Yingdi Yuaccbda92013-12-27 08:44:12 +080090 startFace();
Yingdi Yu7630f642014-01-16 19:13:03 -080091 m_verifier = ndn::ptr_lib::make_shared<ndn::Verifier>(m_invitationPolicy);
92 m_verifier->setFace(m_face);
Yingdi Yuaccbda92013-12-27 08:44:12 +080093
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -080094 ndn::Name certificateName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yueaa84e22014-01-16 10:30:26 -080095 m_syncPolicy = ndn::ptr_lib::make_shared<SecPolicySync>(m_defaultIdentity, certificateName, m_chatroomPrefix, m_face);
Yingdi Yu42f66462013-10-31 17:38:22 -070096
97 connect(ui->inviteButton, SIGNAL(clicked()),
98 this, SLOT(openInviteListDialog()));
99 connect(m_inviteListDialog, SIGNAL(invitionDetermined(QString, bool)),
100 this, SLOT(sendInvitationWrapper(QString, bool)));
101 connect(ui->lineEdit, SIGNAL(returnPressed()),
102 this, SLOT(returnPressed()));
103 connect(ui->treeButton, SIGNAL(pressed()),
104 this, SLOT(treeButtonPressed()));
105 connect(this, SIGNAL(dataReceived(QString, const char *, size_t, bool, bool)),
106 this, SLOT(processData(QString, const char *, size_t, bool, bool)));
107 connect(this, SIGNAL(treeUpdated(const std::vector<Sync::MissingDataInfo>)),
108 this, SLOT(processTreeUpdate(const std::vector<Sync::MissingDataInfo>)));
109 connect(m_timer, SIGNAL(timeout()),
110 this, SLOT(replot()));
111 connect(m_scene, SIGNAL(replot()),
112 this, SLOT(replot()));
Yingdi Yua0594092013-11-06 22:07:38 -0800113 connect(trayIcon, SIGNAL(messageClicked()),
114 this, SLOT(showNormal()));
115 connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
116 this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
Yingdi Yu42f66462013-10-31 17:38:22 -0700117 connect(m_scene, SIGNAL(rosterChanged(QStringList)),
118 this, SLOT(updateRosterList(QStringList)));
119
Yingdi Yu42f66462013-10-31 17:38:22 -0700120
121 initializeSync();
Yingdi Yu04842232013-10-23 14:03:09 -0700122}
123
Yingdi Yu42f66462013-10-31 17:38:22 -0700124
Yingdi Yu04842232013-10-23 14:03:09 -0700125ChatDialog::~ChatDialog()
126{
Yingdi Yu42372442013-11-06 18:43:31 -0800127 if(m_sock != NULL)
128 {
129 sendLeave();
130 delete m_sock;
131 m_sock = NULL;
132 }
Yingdi Yuaccbda92013-12-27 08:44:12 +0800133 shutdownFace();
134}
135
136void
137ChatDialog::startFace()
138{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800139 m_face = ndn::ptr_lib::make_shared<ndn::Face>();
Yingdi Yuaccbda92013-12-27 08:44:12 +0800140
141 connectToDaemon();
142
143 m_running = true;
144 m_thread = boost::thread (&ChatDialog::eventLoop, this);
145}
146
147void
148ChatDialog::shutdownFace()
149{
150 {
151 boost::unique_lock<boost::recursive_mutex> lock(m_mutex);
152 m_running = false;
153 }
154
155 m_thread.join();
Yingdi Yu64206112013-12-24 11:16:32 +0800156 m_face->shutdown();
Yingdi Yu04842232013-10-23 14:03:09 -0700157}
158
159void
Yingdi Yuaccbda92013-12-27 08:44:12 +0800160ChatDialog::eventLoop()
161{
162 while (m_running)
163 {
164 try{
165 m_face->processEvents();
166 usleep(1000);
167 }catch(std::exception& e){
168 _LOG_DEBUG(" " << e.what() );
169 }
170 }
171}
172
173void
Yingdi Yu64206112013-12-24 11:16:32 +0800174ChatDialog::connectToDaemon()
Yingdi Yu04842232013-10-23 14:03:09 -0700175{
Yingdi Yu64206112013-12-24 11:16:32 +0800176 //Hack! transport does not connect to daemon unless an interest is expressed.
177 ndn::Name name("/ndn");
178 ndn::ptr_lib::shared_ptr<ndn::Interest> interest = ndn::ptr_lib::make_shared<ndn::Interest>(name);
179 m_face->expressInterest(*interest,
180 boost::bind(&ChatDialog::onConnectionData, this, _1, _2),
181 boost::bind(&ChatDialog::onConnectionDataTimeout, this, _1));
Yingdi Yu04842232013-10-23 14:03:09 -0700182}
183
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700184void
Yingdi Yu64206112013-12-24 11:16:32 +0800185ChatDialog::onConnectionData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
186 const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
187{ _LOG_DEBUG("onConnectionData"); }
188
189void
190ChatDialog::onConnectionDataTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest)
191{ _LOG_DEBUG("onConnectionDataTimeout"); }
192
193void
Yingdi Yu42f66462013-10-31 17:38:22 -0700194ChatDialog::initializeSetting()
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700195{
Yingdi Yu42372442013-11-06 18:43:31 -0800196 m_user.setNick(QString::fromStdString(m_nick));
Yingdi Yu64206112013-12-24 11:16:32 +0800197 m_user.setChatroom(QString::fromStdString(m_chatroomPrefix.get(-1).toEscapedString()));
Yingdi Yu42f66462013-10-31 17:38:22 -0700198 m_user.setOriginPrefix(QString::fromStdString(m_localPrefix.toUri()));
199 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
200 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
201}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700202
Yingdi Yu42f66462013-10-31 17:38:22 -0700203void
204ChatDialog::updateLabels()
205{
206 QString settingDisp = QString("Chatroom: %1").arg(m_user.getChatroom());
207 ui->infoLabel->setStyleSheet("QLabel {color: #630; font-size: 16px; font: bold \"Verdana\";}");
208 ui->infoLabel->setText(settingDisp);
209 QString prefixDisp;
210 if (m_user.getPrefix().startsWith("/private/local"))
211 {
212 prefixDisp = QString("<Warning: no connection to hub or hub does not support prefix autoconfig.>\n <Prefix = %1>").arg(m_user.getPrefix());
213 ui->prefixLabel->setStyleSheet("QLabel {color: red; font-size: 12px; font: bold \"Verdana\";}");
214 }
215 else
216 {
217 prefixDisp = QString("<Prefix = %1>").arg(m_user.getPrefix());
218 ui->prefixLabel->setStyleSheet("QLabel {color: Green; font-size: 12px; font: bold \"Verdana\";}");
219 }
220 ui->prefixLabel->setText(prefixDisp);
221}
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700222
Yingdi Yu42f66462013-10-31 17:38:22 -0700223void
Yingdi Yu64206112013-12-24 11:16:32 +0800224ChatDialog::sendInterest(const ndn::Interest& interest,
225 const ndn::OnVerified& onVerified,
226 const ndn::OnVerifyFailed& onVerifyFailed,
227 const OnEventualTimeout& timeoutNotify,
Yingdi Yu7630f642014-01-16 19:13:03 -0800228 int retry /* = 1 */)
Yingdi Yu64206112013-12-24 11:16:32 +0800229{
230 m_face->expressInterest(interest,
231 boost::bind(&ChatDialog::onTargetData,
232 this,
233 _1,
234 _2,
Yingdi Yu64206112013-12-24 11:16:32 +0800235 onVerified,
Yingdi Yu7630f642014-01-16 19:13:03 -0800236 onVerifyFailed),
Yingdi Yu64206112013-12-24 11:16:32 +0800237 boost::bind(&ChatDialog::onTargetTimeout,
238 this,
239 _1,
240 retry,
Yingdi Yu64206112013-12-24 11:16:32 +0800241 onVerified,
242 onVerifyFailed,
Yingdi Yu7630f642014-01-16 19:13:03 -0800243 timeoutNotify));
Yingdi Yu64206112013-12-24 11:16:32 +0800244}
245
246void
247ChatDialog::onTargetData(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
248 const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
Yingdi Yu64206112013-12-24 11:16:32 +0800249 const ndn::OnVerified& onVerified,
Yingdi Yu7630f642014-01-16 19:13:03 -0800250 const ndn::OnVerifyFailed& onVerifyFailed)
Yingdi Yu64206112013-12-24 11:16:32 +0800251{
Yingdi Yu7630f642014-01-16 19:13:03 -0800252 m_verifier->verifyData(data, onVerified, onVerifyFailed);
Yingdi Yu64206112013-12-24 11:16:32 +0800253}
254
255void
256ChatDialog::onTargetTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
257 int retry,
Yingdi Yu64206112013-12-24 11:16:32 +0800258 const ndn::OnVerified& onVerified,
259 const ndn::OnVerifyFailed& onVerifyFailed,
Yingdi Yu7630f642014-01-16 19:13:03 -0800260 const OnEventualTimeout& timeoutNotify)
Yingdi Yu64206112013-12-24 11:16:32 +0800261{
262 if(retry > 0)
Yingdi Yu7630f642014-01-16 19:13:03 -0800263 sendInterest(*interest, onVerified, onVerifyFailed, timeoutNotify, retry-1);
Yingdi Yu64206112013-12-24 11:16:32 +0800264 else
265 {
266 _LOG_DEBUG("Interest: " << interest->getName().toUri() << " eventually times out!");
267 timeoutNotify();
268 }
269}
270
271void
Yingdi Yu64206112013-12-24 11:16:32 +0800272ChatDialog::sendInvitation(ndn::ptr_lib::shared_ptr<ContactItem> contact, bool isIntroducer)
Yingdi Yu42f66462013-10-31 17:38:22 -0700273{
Yingdi Yueaa84e22014-01-16 10:30:26 -0800274 m_invitationPolicy->addTrustAnchor(contact->getSelfEndorseCertificate());
Yingdi Yu42f66462013-10-31 17:38:22 -0700275
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800276 ndn::Name certificateName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu42f66462013-10-31 17:38:22 -0700277
Yingdi Yu6df61252014-01-21 11:05:11 -0800278 ChronosInvitation invitation(contact->getNameSpace(),
279 m_chatroomPrefix.getSubName(m_chatroomPrefix.size()-1, 1), //!!Should be changed!
280 m_localPrefix,
281 certificateName);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700282
Yingdi Yu6df61252014-01-21 11:05:11 -0800283 const ndn::Buffer &signedBlob = invitation.getSignedBlob();
284 ndn::Signature sig = m_keyChain->sign(signedBlob.buf(), signedBlob.size(), certificateName);
285 invitation.setSignatureValue(sig.getValue());
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700286
Yingdi Yu6df61252014-01-21 11:05:11 -0800287 ndn::Interest interest(invitation.getInterestName());
Yingdi Yu64206112013-12-24 11:16:32 +0800288 ndn::OnVerified onVerified = boost::bind(&ChatDialog::onInviteReplyVerified,
289 this,
290 _1,
291 contact->getNameSpace(),
292 isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700293
Yingdi Yu64206112013-12-24 11:16:32 +0800294 ndn::OnVerifyFailed onVerifyFailed = boost::bind(&ChatDialog::onInviteReplyVerifyFailed,
295 this,
296 _1,
297 contact->getNameSpace());
298
299 OnEventualTimeout timeoutNotify = boost::bind(&ChatDialog::onInviteReplyTimeout,
300 this,
301 contact->getNameSpace());
302
303
Yingdi Yu7630f642014-01-16 19:13:03 -0800304 sendInterest(interest, onVerified, onVerifyFailed, timeoutNotify);
Yingdi Yu64206112013-12-24 11:16:32 +0800305}
306
307void
308ChatDialog::onInviteReplyVerified(const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
309 const ndn::Name& identity,
310 bool isIntroducer)
311{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800312 string content(reinterpret_cast<const char*>(data->getContent().value()), data->getContent().value_size());
Yingdi Yu64206112013-12-24 11:16:32 +0800313 if(content == string("nack"))
314 invitationRejected(identity);
315 else
316 invitationAccepted(identity, data, content, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700317}
318
319void
Yingdi Yu64206112013-12-24 11:16:32 +0800320ChatDialog::onInviteReplyVerifyFailed(const ndn::ptr_lib::shared_ptr<ndn::Data>& data,
321 const ndn::Name& identity)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700322{
Yingdi Yu64206112013-12-24 11:16:32 +0800323 _LOG_DEBUG("Reply from " << identity.toUri() << " cannot be verified!");
324 QString msg = QString::fromUtf8("Reply from ") + QString::fromStdString(identity.toUri()) + " cannot be verified!";
325 emit inivationRejection(msg);
326}
327
328
329void
330ChatDialog::onInviteReplyTimeout(const ndn::Name& identity)
331{
332 _LOG_DEBUG("Your invitation to " << identity.toUri() << " times out!");
333 QString msg = QString::fromUtf8("Your invitation to ") + QString::fromStdString(identity.toUri()) + " times out!";
334 emit inivationRejection(msg);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700335}
336
337void
Yingdi Yu42f66462013-10-31 17:38:22 -0700338ChatDialog::invitationRejected(const ndn::Name& identity)
339{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800340 _LOG_DEBUG(" " << identity.toUri() << " Rejected your invitation!");
Yingdi Yu3e87bd82013-11-10 10:47:44 -0800341 QString msg = QString::fromStdString(identity.toUri()) + " Rejected your invitation!";
342 emit inivationRejection(msg);
Yingdi Yu42f66462013-10-31 17:38:22 -0700343}
344
345void
Yingdi Yu64206112013-12-24 11:16:32 +0800346ChatDialog::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 -0700347{
Yingdi Yu6a5b9f62013-11-06 23:00:21 -0800348 _LOG_DEBUG(" " << identity.toUri() << " Accepted your invitation!");
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800349 ndn::SignatureSha256WithRsa sig(data->getSignature());
350 const ndn::Name & keyLocatorName = sig.getKeyLocator().getName();
Yingdi Yueaa84e22014-01-16 10:30:26 -0800351 ndn::ptr_lib::shared_ptr<ndn::IdentityCertificate> dskCertificate = m_invitationPolicy->getValidatedDskCertificate(keyLocatorName);
Yingdi Yu6df61252014-01-21 11:05:11 -0800352 m_syncPolicy->addSyncDataRule(inviteePrefix, *dskCertificate, isIntroducer);
Yingdi Yua0594092013-11-06 22:07:38 -0800353 publishIntroCert(*dskCertificate, isIntroducer);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700354}
355
Yingdi Yu64206112013-12-24 11:16:32 +0800356void
357ChatDialog::publishIntroCert(const ndn::IdentityCertificate& dskCertificate, bool isIntroducer)
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700358{
Yingdi Yu64206112013-12-24 11:16:32 +0800359 SyncIntroCertificate syncIntroCertificate(m_chatroomPrefix,
360 dskCertificate.getPublicKeyName(),
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800361 m_keyChain->getDefaultKeyNameForIdentity(m_defaultIdentity),
Yingdi Yu64206112013-12-24 11:16:32 +0800362 dskCertificate.getNotBefore(),
363 dskCertificate.getNotAfter(),
364 dskCertificate.getPublicKeyInfo(),
365 (isIntroducer ? SyncIntroCertificate::INTRODUCER : SyncIntroCertificate::PRODUCER));
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800366 ndn::Name certName = m_keyChain->getDefaultCertificateNameForIdentity(m_defaultIdentity);
Yingdi Yu64206112013-12-24 11:16:32 +0800367 _LOG_DEBUG("Publish Intro Certificate: " << syncIntroCertificate.getName());
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800368 m_keyChain->sign(syncIntroCertificate, certName);
369 m_face->put(syncIntroCertificate);
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700370}
371
Yingdi Yu64206112013-12-24 11:16:32 +0800372void
373ChatDialog::addTrustAnchor(const EndorseCertificate& selfEndorseCertificate)
Yingdi Yueaa84e22014-01-16 10:30:26 -0800374{ m_invitationPolicy->addTrustAnchor(selfEndorseCertificate); }
Yingdi Yu64206112013-12-24 11:16:32 +0800375
376void
377ChatDialog::addChatDataRule(const ndn::Name& prefix,
378 const ndn::IdentityCertificate& identityCertificate,
379 bool isIntroducer)
Yingdi Yu6df61252014-01-21 11:05:11 -0800380{ m_syncPolicy->addSyncDataRule(prefix, identityCertificate, isIntroducer); }
Yingdi Yu64206112013-12-24 11:16:32 +0800381
Yingdi Yuc4d08d22013-10-23 23:07:29 -0700382
Yingdi Yu42372442013-11-06 18:43:31 -0800383
384void
Yingdi Yu42f66462013-10-31 17:38:22 -0700385ChatDialog::initializeSync()
386{
387
388 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
Yingdi Yueaa84e22014-01-16 10:30:26 -0800389 m_syncPolicy,
Yingdi Yuaccbda92013-12-27 08:44:12 +0800390 m_face,
Yingdi Yu64206112013-12-24 11:16:32 +0800391 boost::bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
392 boost::bind(&ChatDialog::processRemoveWrapper, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700393
394 usleep(100000);
395
396 QTimer::singleShot(600, this, SLOT(sendJoin()));
397 m_timer->start(FRESHNESS * 1000);
398 disableTreeDisplay();
399 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
400 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
401 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu42f66462013-10-31 17:38:22 -0700402}
403
404void
405ChatDialog::returnPressed()
406{
407 QString text = ui->lineEdit->text();
408 if (text.isEmpty())
409 return;
410
411 ui->lineEdit->clear();
412
413 if (text.startsWith("boruoboluomi"))
414 {
415 summonReaper ();
416 // reapButton->show();
417 fitView();
418 return;
419 }
420
421 if (text.startsWith("minimanihong"))
422 {
423 // reapButton->hide();
424 fitView();
425 return;
426 }
427
428 SyncDemo::ChatMessage msg;
429 formChatMessage(text, msg);
430
431 appendMessage(msg);
432
433 sendMsg(msg);
434
435 fitView();
436}
437
438void
439ChatDialog::treeButtonPressed()
440{
441 if (ui->treeViewer->isVisible())
442 {
443 ui->treeViewer->hide();
444 ui->treeButton->setText("Show ChronoSync Tree");
445 }
446 else
447 {
448 ui->treeViewer->show();
449 ui->treeButton->setText("Hide ChronoSync Tree");
450 }
451
452 fitView();
453}
454
455void ChatDialog::disableTreeDisplay()
456{
457 ui->treeButton->setEnabled(false);
458 ui->treeViewer->hide();
459 fitView();
460}
461
462void ChatDialog::enableTreeDisplay()
463{
464 ui->treeButton->setEnabled(true);
465 // treeViewer->show();
466 // fitView();
467}
468
469void
470ChatDialog::processTreeUpdateWrapper(const std::vector<Sync::MissingDataInfo> v, Sync::SyncSocket *sock)
471{
472 emit treeUpdated(v);
473 _LOG_DEBUG("<<< Tree update signal emitted");
474}
475
476void
477ChatDialog::processRemoveWrapper(std::string prefix)
478{
479 _LOG_DEBUG("Sync REMOVE signal received for prefix: " << prefix);
480}
481
482void
483ChatDialog::processTreeUpdate(const std::vector<Sync::MissingDataInfo> v)
484{
485 _LOG_DEBUG("<<< processing Tree Update");
486
487 if (v.empty())
488 {
489 return;
490 }
491
492 // reflect the changes on digest tree
493 {
494 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
495 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
496 }
497
498 int n = v.size();
499 int totalMissingPackets = 0;
500 for (int i = 0; i < n; i++)
501 {
502 totalMissingPackets += v[i].high.getSeq() - v[i].low.getSeq() + 1;
503 }
504
505 for (int i = 0; i < n; i++)
506 {
507 if (totalMissingPackets < 4)
508 {
509 for (Sync::SeqNo seq = v[i].low; seq <= v[i].high; ++seq)
510 {
511 m_sock->fetchData(v[i].prefix, seq, bind(&ChatDialog::processDataWrapper, this, _1), 2);
512 _LOG_DEBUG("<<< Fetching " << v[i].prefix << "/" <<seq.getSession() <<"/" << seq.getSeq());
513 }
514 }
515 else
516 {
517 m_sock->fetchData(v[i].prefix, v[i].high, bind(&ChatDialog::processDataNoShowWrapper, this, _1), 2);
518 }
519 }
520
521 // adjust the view
522 fitView();
523
524}
525
526void
Yingdi Yu64206112013-12-24 11:16:32 +0800527ChatDialog::processDataWrapper(const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700528{
529 string name = data->getName().toUri();
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800530 const char* buf = reinterpret_cast<const char*>(data->getContent().value());
531 size_t len = data->getContent().value_size();
Yingdi Yu42f66462013-10-31 17:38:22 -0700532
533 char *tempBuf = new char[len];
534 memcpy(tempBuf, buf, len);
535 emit dataReceived(name.c_str(), tempBuf, len, true, false);
536 _LOG_DEBUG("<<< " << name << " fetched");
537}
538
539void
Yingdi Yu64206112013-12-24 11:16:32 +0800540ChatDialog::processDataNoShowWrapper(const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
Yingdi Yu42f66462013-10-31 17:38:22 -0700541{
542 string name = data->getName().toUri();
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800543 const char* buf = reinterpret_cast<const char*>(data->getContent().value());
544 size_t len = data->getContent().value_size();
Yingdi Yu42f66462013-10-31 17:38:22 -0700545
546 char *tempBuf = new char[len];
547 memcpy(tempBuf, buf, len);
548 emit dataReceived(name.c_str(), tempBuf, len, false, false);
549
550 // if (!m_historyInitialized)
551 // {
552 // fetchHistory(name);
553 // m_historyInitialized = true;
554 // }
555}
556
557// void
558// ChatDialog::fetchHistory(std::string name)
559// {
560
561// /****************************/
562// /* TODO: fix following part */
563// /****************************/
564// string nameWithoutSeq = name.substr(0, name.find_last_of('/'));
565// string prefix = nameWithoutSeq.substr(0, nameWithoutSeq.find_last_of('/'));
566// prefix += "/history";
567// // Ptr<Wrapper>CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
568// // QString randomString = getRandomString();
569// // for (int i = 0; i < MAX_HISTORY_ENTRY; i++)
570// // {
571// // QString interest = QString("%1/%2/%3").arg(prefix.c_str()).arg(randomString).arg(i);
572// // handle->sendInterest(interest.toStdString(), bind(&ChatDialog::processDataHistoryWrapper, this, _1, _2, _3));
573// // }
574// }
575
576void
577ChatDialog::processData(QString name, const char *buf, size_t len, bool show, bool isHistory)
578{
579 SyncDemo::ChatMessage msg;
580 bool corrupted = false;
581 if (!msg.ParseFromArray(buf, len))
582 {
583 _LOG_DEBUG("Errrrr.. Can not parse msg with name: " << name.toStdString() << ". what is happening?");
584 // nasty stuff: as a remedy, we'll form some standard msg for inparsable msgs
585 msg.set_from("inconnu");
586 msg.set_type(SyncDemo::ChatMessage::OTHER);
587 corrupted = true;
588 }
589
590 delete [] buf;
591 buf = NULL;
592
593 // display msg received from network
594 // we have to do so; this function is called by ccnd thread
595 // so if we call appendMsg directly
596 // Qt crash as "QObject: Cannot create children for a parent that is in a different thread"
597 // the "cannonical" way to is use signal-slot
598 if (show && !corrupted)
599 {
600 appendMessage(msg, isHistory);
601 }
602
603 if (!isHistory)
604 {
605 // update the tree view
606 std::string stdStrName = name.toStdString();
607 std::string stdStrNameWithoutSeq = stdStrName.substr(0, stdStrName.find_last_of('/'));
608 std::string prefix = stdStrNameWithoutSeq.substr(0, stdStrNameWithoutSeq.find_last_of('/'));
609 _LOG_DEBUG("<<< updating scene for" << prefix << ": " << msg.from());
610 if (msg.type() == SyncDemo::ChatMessage::LEAVE)
611 {
612 processRemove(prefix.c_str());
613 }
614 else
615 {
616 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
617 m_scene->msgReceived(prefix.c_str(), msg.from().c_str());
618 }
619 }
620 fitView();
621}
622
623void
624ChatDialog::processRemove(QString prefix)
625{
626 _LOG_DEBUG("<<< remove node for prefix" << prefix.toStdString());
627
628 bool removed = m_scene->removeNode(prefix);
629 if (removed)
630 {
631 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
632 m_scene->plot(m_sock->getRootDigest().c_str());
633 }
634}
635
636void
637ChatDialog::sendJoin()
638{
639 m_joined = true;
640 SyncDemo::ChatMessage msg;
641 formControlMessage(msg, SyncDemo::ChatMessage::JOIN);
642 sendMsg(msg);
643 boost::random::random_device rng;
644 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
645 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
646 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
647}
648
649void
650ChatDialog::sendHello()
651{
652 time_t now = time(NULL);
653 int elapsed = now - m_lastMsgTime;
654 if (elapsed >= m_randomizedInterval / 1000)
655 {
656 SyncDemo::ChatMessage msg;
657 formControlMessage(msg, SyncDemo::ChatMessage::HELLO);
658 sendMsg(msg);
659 boost::random::random_device rng;
660 boost::random::uniform_int_distribution<> uniform(1, FRESHNESS / 5 * 1000);
661 m_randomizedInterval = HELLO_INTERVAL * 1000 + uniform(rng);
662 QTimer::singleShot(m_randomizedInterval, this, SLOT(sendHello()));
663 }
664 else
665 {
666 QTimer::singleShot((m_randomizedInterval - elapsed * 1000), this, SLOT(sendHello()));
667 }
668}
669
670void
671ChatDialog::sendLeave()
672{
673 SyncDemo::ChatMessage msg;
674 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
675 sendMsg(msg);
676 usleep(500000);
677 m_sock->remove(m_user.getPrefix().toStdString());
678 usleep(5000);
679 m_joined = false;
680 _LOG_DEBUG("Sync REMOVE signal sent");
681}
682
683void
684ChatDialog::replot()
685{
686 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
687 m_scene->plot(m_sock->getRootDigest().c_str());
688 fitView();
689}
690
691void
692ChatDialog::summonReaper()
693{
694 Sync::SyncLogic &logic = m_sock->getLogic ();
695 map<string, bool> branches = logic.getBranchPrefixes();
696 QMap<QString, DisplayUserPtr> roster = m_scene->getRosterFull();
697
698 m_zombieList.clear();
699
700 QMapIterator<QString, DisplayUserPtr> it(roster);
701 map<string, bool>::iterator mapIt;
702 while(it.hasNext())
703 {
704 it.next();
705 DisplayUserPtr p = it.value();
706 if (p != DisplayUserNullPtr)
707 {
708 mapIt = branches.find(p->getPrefix().toStdString());
709 if (mapIt != branches.end())
710 {
711 mapIt->second = true;
712 }
713 }
714 }
715
716 for (mapIt = branches.begin(); mapIt != branches.end(); ++mapIt)
717 {
718 // this is zombie. all active users should have been marked true
719 if (! mapIt->second)
720 {
721 m_zombieList.append(mapIt->first.c_str());
722 }
723 }
724
725 m_zombieIndex = 0;
726
727 // start reaping
728 reap();
729}
730
731void
732ChatDialog::reap()
733{
734 if (m_zombieIndex < m_zombieList.size())
735 {
736 string prefix = m_zombieList.at(m_zombieIndex).toStdString();
737 m_sock->remove(prefix);
738 _LOG_DEBUG("Reaped: prefix = " << prefix);
739 m_zombieIndex++;
740 // reap again in 10 seconds
741 QTimer::singleShot(10000, this, SLOT(reap()));
742 }
743}
744
745void
746ChatDialog::updateRosterList(QStringList staleUserList)
747{
748 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
749 QStringList rosterList = m_scene->getRosterList();
750 m_rosterModel->setStringList(rosterList);
751 QString user;
752 QStringListIterator it(staleUserList);
753 while(it.hasNext())
754 {
755 std::string nick = it.next().toStdString();
756 if (nick.empty())
757 continue;
758
759 SyncDemo::ChatMessage msg;
760 formControlMessage(msg, SyncDemo::ChatMessage::LEAVE);
761 msg.set_from(nick);
762 appendMessage(msg);
763 }
764}
765
766void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800767ChatDialog::settingUpdated(QString nick, QString chatroom, QString originPrefix)
768{
Yingdi Yu9f657242013-11-10 12:25:43 -0800769 _LOG_DEBUG("called");
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800770 QString randString = getRandomString();
771 bool needWrite = false;
772 bool needFresh = false;
773
774 QString oldPrefix = m_user.getPrefix();
775 if (!originPrefix.isEmpty() && originPrefix != m_user.getOriginPrefix()) {
776 m_user.setOriginPrefix(originPrefix);
777
778 m_localPrefix = ndn::Name(originPrefix.toStdString());
779 m_localChatPrefix = m_localPrefix;
780 m_localChatPrefix.append("%F0.").append(m_defaultIdentity);
781 m_localChatPrefix.append("chronos").append(m_chatroomPrefix.get(-1)).append(randString.toStdString());
782 m_user.setPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
783 m_scene->setCurrentPrefix(QString::fromStdString(m_localChatPrefix.toUri()));
784 needWrite = true;
785 needFresh = true;
786 }
787
788 if (needWrite) {
789 updateLabels();
790 }
791
792 if (needFresh && m_sock != NULL)
793 {
794
795 {
796 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
797 m_scene->clearAll();
798 m_scene->plot("Empty");
799 }
800
Yingdi Yu0a953c32013-11-10 10:32:18 -0800801 ui->textEdit->clear();
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800802
803 // keep the new prefix
804 QString newPrefix = m_user.getPrefix();
805 // send leave for the old
806 m_user.setPrefix(oldPrefix);
807 // there is no point to send leave if we haven't joined yet
808 if (m_joined)
809 {
810 sendLeave();
811 }
812 // resume new prefix
813 m_user.setPrefix(newPrefix);
814 // Sync::CcnxWrapperPtr handle = Sync::CcnxWrapper::Create();
815 // handle->clearInterestFilter(oldPrefix.toStdString());
816 delete m_sock;
817 m_sock = NULL;
818
819 try
820 {
821 usleep(100000);
822 m_sock = new Sync::SyncSocket(m_chatroomPrefix.toUri(),
Yingdi Yueaa84e22014-01-16 10:30:26 -0800823 m_syncPolicy,
Yingdi Yuaccbda92013-12-27 08:44:12 +0800824 m_face,
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800825 bind(&ChatDialog::processTreeUpdateWrapper, this, _1, _2),
826 bind(&ChatDialog::processRemoveWrapper, this, _1));
827 usleep(100000);
828 // Sync::CcnxWrapperPtr handle = boost::make_shared<Sync::CcnxWrapper> ();
829 // handle->setInterestFilter(m_user.getPrefix().toStdString(), bind(&ChatDialog::respondHistoryRequest, this, _1));
Yingdi Yu0a953c32013-11-10 10:32:18 -0800830 QTimer::singleShot(600, this, SLOT(sendJoin()));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800831 m_timer->start(FRESHNESS * 1000);
832 disableTreeDisplay();
Yingdi Yu0a953c32013-11-10 10:32:18 -0800833 QTimer::singleShot(2200, this, SLOT(enableTreeDisplay()));
Yingdi Yu64206112013-12-24 11:16:32 +0800834 }catch(std::exception& e){
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800835 emit noNdnConnection(QString::fromStdString("Cannot conect to ndnd!\n Have you started your ndnd?"));
836 }
837 }
838 else if (needFresh && m_sock == NULL)
839 {
840 initializeSync();
841 }
842 else if (m_sock == NULL)
843 {
844 initializeSync();
845 }
846 else
847 {
848// #ifdef __DEBUG
849// std::cout << "Just changing nicks, we're good. " << std::endl;
850// #endif
851 }
852
853 fitView();
854}
855
856void
Yingdi Yua0594092013-11-06 22:07:38 -0800857ChatDialog::iconActivated(QSystemTrayIcon::ActivationReason reason)
858{
859 switch (reason)
860 {
861 case QSystemTrayIcon::Trigger:
862 case QSystemTrayIcon::DoubleClick:
863 break;
864 case QSystemTrayIcon::MiddleClick:
865 // showMessage();
866 break;
867 default:;
868 }
869}
870
871
872void
873ChatDialog::messageClicked()
874{
875 this->showMaximized();
876}
877
878
879void
880ChatDialog::createActions()
881{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800882 minimizeAction = new QAction(tr("Mi&nimize"), this);
883 connect(minimizeAction, SIGNAL(triggered()), this, SLOT(hide()));
Yingdi Yua0594092013-11-06 22:07:38 -0800884
Yingdi Yu07b5b092013-11-07 17:00:54 -0800885 maximizeAction = new QAction(tr("Ma&ximize"), this);
886 connect(maximizeAction, SIGNAL(triggered()), this, SLOT(showMaximized()));
Yingdi Yua0594092013-11-06 22:07:38 -0800887
Yingdi Yu07b5b092013-11-07 17:00:54 -0800888 restoreAction = new QAction(tr("&Restore"), this);
889 connect(restoreAction, SIGNAL(triggered()), this, SLOT(showNormal()));
Yingdi Yua0594092013-11-06 22:07:38 -0800890
891 // settingsAction = new QAction(tr("Settings"), this);
892 // connect (settingsAction, SIGNAL(triggered()), this, SLOT(buttonPressed()));
893
894 // settingsAction->setMenuRole (QAction::PreferencesRole);
895
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800896 updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
897 connect (updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(updateLocalPrefix()));
Yingdi Yua0594092013-11-06 22:07:38 -0800898
Yingdi Yu07b5b092013-11-07 17:00:54 -0800899 quitAction = new QAction(tr("Quit"), this);
900 connect(quitAction, SIGNAL(triggered()), this, SLOT(quit()));
Yingdi Yua0594092013-11-06 22:07:38 -0800901}
902
903void
904ChatDialog::createTrayIcon()
905{
Yingdi Yu07b5b092013-11-07 17:00:54 -0800906 trayIconMenu = new QMenu(this);
907 trayIconMenu->addAction(minimizeAction);
908 trayIconMenu->addAction(maximizeAction);
909 trayIconMenu->addAction(restoreAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800910 // trayIconMenu->addSeparator();
911 // trayIconMenu->addAction(settingsAction);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800912 trayIconMenu->addSeparator();
913 trayIconMenu->addAction(updateLocalPrefixAction);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800914 trayIconMenu->addSeparator();
915 trayIconMenu->addAction(quitAction);
Yingdi Yua0594092013-11-06 22:07:38 -0800916
917 trayIcon = new QSystemTrayIcon(this);
Yingdi Yu07b5b092013-11-07 17:00:54 -0800918 trayIcon->setContextMenu(trayIconMenu);
Yingdi Yua0594092013-11-06 22:07:38 -0800919
920 QIcon icon(":/images/icon_small.png");
921 trayIcon->setIcon(icon);
922 setWindowIcon(icon);
923 trayIcon->setToolTip("ChronoChat System Tray Icon");
924 trayIcon->setVisible(true);
Yingdi Yua0594092013-11-06 22:07:38 -0800925}
926
927
928void
Yingdi Yu42f66462013-10-31 17:38:22 -0700929ChatDialog::resizeEvent(QResizeEvent *e)
930{
931 fitView();
932}
933
934void
935ChatDialog::showEvent(QShowEvent *e)
936{
937 fitView();
938}
939
940void
941ChatDialog::fitView()
942{
943 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
944 QRectF rect = m_scene->itemsBoundingRect();
945 m_scene->setSceneRect(rect);
946 ui->treeViewer->fitInView(m_scene->itemsBoundingRect(), Qt::KeepAspectRatio);
947}
948
949void
950ChatDialog::formChatMessage(const QString &text, SyncDemo::ChatMessage &msg) {
951 msg.set_from(m_user.getNick().toStdString());
952 msg.set_to(m_user.getChatroom().toStdString());
953 msg.set_data(text.toUtf8().constData());
954 time_t seconds = time(NULL);
955 msg.set_timestamp(seconds);
956 msg.set_type(SyncDemo::ChatMessage::CHAT);
957}
958
959void
960ChatDialog::formControlMessage(SyncDemo::ChatMessage &msg, SyncDemo::ChatMessage::ChatMessageType type)
961{
962 msg.set_from(m_user.getNick().toStdString());
963 msg.set_to(m_user.getChatroom().toStdString());
964 time_t seconds = time(NULL);
965 msg.set_timestamp(seconds);
966 msg.set_type(type);
967}
968
969void
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800970ChatDialog::updateLocalPrefix()
971{
Yingdi Yu0a953c32013-11-10 10:32:18 -0800972 m_newLocalPrefixReady = false;
Yingdi Yu64206112013-12-24 11:16:32 +0800973 ndn::Name interestName("/local/ndn/prefix");
974 ndn::Interest interest(interestName);
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -0800975 interest.setInterestLifetime(1000);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800976
Yingdi Yu64206112013-12-24 11:16:32 +0800977 m_face->expressInterest(interest,
978 bind(&ChatDialog::onLocalPrefix, this, _1, _2),
979 bind(&ChatDialog::onLocalPrefixTimeout, this, _1));
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800980
Yingdi Yu0a953c32013-11-10 10:32:18 -0800981 while(m_newLocalPrefixReady == false)
982 {
983#if BOOST_VERSION >= 1050000
984 boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
985#else
986 boost::this_thread::sleep(boost::posix_time::milliseconds(100));
987#endif
988 }
Yingdi Yu9f657242013-11-10 12:25:43 -0800989 _LOG_DEBUG("now the prefix is " << m_newLocalPrefix.toUri());
Yingdi Yu6b56f092013-11-10 11:54:02 -0800990 _LOG_DEBUG("in use prefix is " << m_user.getOriginPrefix().toStdString());
Yingdi Yu0a953c32013-11-10 10:32:18 -0800991 QString originPrefix = QString::fromStdString(m_newLocalPrefix.toUri());
992
993 if (originPrefix != "" && m_user.getOriginPrefix () != originPrefix)
994 emit settingUpdated(m_user.getNick (), m_user.getChatroom (), originPrefix);
Yingdi Yu2ab22e72013-11-10 01:38:21 -0800995}
996
Yingdi Yu64206112013-12-24 11:16:32 +0800997
998void
999ChatDialog::onLocalPrefix(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest,
1000 const ndn::ptr_lib::shared_ptr<ndn::Data>& data)
1001{
Yingdi Yuc9ffa9f2014-01-13 11:19:47 -08001002 string dataString(reinterpret_cast<const char*>(data->getContent().value()), data->getContent().value_size());
Yingdi Yu64206112013-12-24 11:16:32 +08001003 QString originPrefix = QString::fromStdString (dataString).trimmed ();
1004 string trimmedString = originPrefix.toStdString();
1005 m_newLocalPrefix = ndn::Name(trimmedString);
1006 m_newLocalPrefixReady = true;
1007}
1008
1009void
1010ChatDialog::onLocalPrefixTimeout(const ndn::ptr_lib::shared_ptr<const ndn::Interest>& interest)
1011{
1012 m_newLocalPrefix = m_localPrefix;
1013 m_newLocalPrefixReady = true;
1014}
1015
Yingdi Yu2ab22e72013-11-10 01:38:21 -08001016static std::string chars2("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789");
1017
1018QString
1019ChatDialog::getRandomString()
1020{
1021 std::string randStr;
1022 boost::random::random_device rng;
1023 boost::random::uniform_int_distribution<> index_dist(0, chars2.size() - 1);
1024 for (int i = 0; i < 10; i ++)
1025 {
1026 randStr += chars2[index_dist(rng)];
1027 }
1028 return randStr.c_str();
1029}
1030
1031void
Yingdi Yua0594092013-11-06 22:07:38 -08001032ChatDialog::changeEvent(QEvent *e)
1033{
1034 switch(e->type())
1035 {
1036 case QEvent::ActivationChange:
1037 if (isActiveWindow())
1038 {
1039 trayIcon->setIcon(QIcon(":/images/icon_small.png"));
1040 }
1041 break;
1042 default:
1043 break;
1044 }
1045}
1046
1047void
Yingdi Yu42f66462013-10-31 17:38:22 -07001048ChatDialog::appendMessage(const SyncDemo::ChatMessage msg, bool isHistory)
1049{
1050 boost::recursive_mutex::scoped_lock lock(m_msgMutex);
1051
1052 if (msg.type() == SyncDemo::ChatMessage::CHAT)
1053 {
1054
1055 if (!msg.has_data())
1056 {
1057 return;
1058 }
1059
1060 if (msg.from().empty() || msg.data().empty())
1061 {
1062 return;
1063 }
1064
1065 if (!msg.has_timestamp())
1066 {
1067 return;
1068 }
1069
1070 // if (m_history.size() == MAX_HISTORY_ENTRY)
1071 // {
1072 // m_history.dequeue();
1073 // }
1074
1075 // m_history.enqueue(msg);
1076
1077 QTextCharFormat nickFormat;
1078 nickFormat.setForeground(Qt::darkGreen);
1079 nickFormat.setFontWeight(QFont::Bold);
1080 nickFormat.setFontUnderline(true);
1081 nickFormat.setUnderlineColor(Qt::gray);
1082
1083 QTextCursor cursor(ui->textEdit->textCursor());
1084 cursor.movePosition(QTextCursor::End);
1085 QTextTableFormat tableFormat;
1086 tableFormat.setBorder(0);
1087 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1088 QString from = QString("%1 ").arg(msg.from().c_str());
1089 QTextTableCell fromCell = table->cellAt(0, 0);
1090 fromCell.setFormat(nickFormat);
1091 fromCell.firstCursorPosition().insertText(from);
1092
1093 time_t timestamp = msg.timestamp();
1094 printTimeInCell(table, timestamp);
1095
1096 QTextCursor nextCursor(ui->textEdit->textCursor());
1097 nextCursor.movePosition(QTextCursor::End);
1098 table = nextCursor.insertTable(1, 1, tableFormat);
1099 table->cellAt(0, 0).firstCursorPosition().insertText(QString::fromUtf8(msg.data().c_str()));
1100 if (!isHistory)
1101 {
1102 showMessage(from, QString::fromUtf8(msg.data().c_str()));
1103 }
1104 }
1105
1106 if (msg.type() == SyncDemo::ChatMessage::JOIN || msg.type() == SyncDemo::ChatMessage::LEAVE)
1107 {
1108 QTextCharFormat nickFormat;
1109 nickFormat.setForeground(Qt::gray);
1110 nickFormat.setFontWeight(QFont::Bold);
1111 nickFormat.setFontUnderline(true);
1112 nickFormat.setUnderlineColor(Qt::gray);
1113
1114 QTextCursor cursor(ui->textEdit->textCursor());
1115 cursor.movePosition(QTextCursor::End);
1116 QTextTableFormat tableFormat;
1117 tableFormat.setBorder(0);
1118 QTextTable *table = cursor.insertTable(1, 2, tableFormat);
1119 QString action;
1120 if (msg.type() == SyncDemo::ChatMessage::JOIN)
1121 {
1122 action = "enters room";
1123 }
1124 else
1125 {
1126 action = "leaves room";
1127 }
1128
1129 QString from = QString("%1 %2 ").arg(msg.from().c_str()).arg(action);
1130 QTextTableCell fromCell = table->cellAt(0, 0);
1131 fromCell.setFormat(nickFormat);
1132 fromCell.firstCursorPosition().insertText(from);
1133
1134 time_t timestamp = msg.timestamp();
1135 printTimeInCell(table, timestamp);
1136 }
1137
1138 QScrollBar *bar = ui->textEdit->verticalScrollBar();
1139 bar->setValue(bar->maximum());
1140}
1141
1142QString
1143ChatDialog::formatTime(time_t timestamp)
1144{
1145 struct tm *tm_time = localtime(&timestamp);
1146 int hour = tm_time->tm_hour;
1147 QString amOrPM;
1148 if (hour > 12)
1149 {
1150 hour -= 12;
1151 amOrPM = "PM";
1152 }
1153 else
1154 {
1155 amOrPM = "AM";
1156 if (hour == 0)
1157 {
1158 hour = 12;
1159 }
1160 }
1161
1162 char textTime[12];
1163 sprintf(textTime, "%d:%02d:%02d %s", hour, tm_time->tm_min, tm_time->tm_sec, amOrPM.toStdString().c_str());
1164 return QString(textTime);
1165}
1166
1167void
1168ChatDialog::printTimeInCell(QTextTable *table, time_t timestamp)
1169{
1170 QTextCharFormat timeFormat;
1171 timeFormat.setForeground(Qt::gray);
1172 timeFormat.setFontUnderline(true);
1173 timeFormat.setUnderlineColor(Qt::gray);
1174 QTextTableCell timeCell = table->cellAt(0, 1);
1175 timeCell.setFormat(timeFormat);
1176 timeCell.firstCursorPosition().insertText(formatTime(timestamp));
1177}
1178
1179void
1180ChatDialog::showMessage(QString from, QString data)
1181{
1182 if (!isActiveWindow())
1183 {
Yingdi Yua0594092013-11-06 22:07:38 -08001184 trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(m_user.getChatroom()), QString("<%1>: %2").arg(from).arg(data), QSystemTrayIcon::Information, 20000);
1185 trayIcon->setIcon(QIcon(":/images/note.png"));
Yingdi Yu42f66462013-10-31 17:38:22 -07001186 }
1187}
1188
1189void
1190ChatDialog::sendMsg(SyncDemo::ChatMessage &msg)
1191{
1192 // send msg
1193 size_t size = msg.ByteSize();
1194 char *buf = new char[size];
1195 msg.SerializeToArray(buf, size);
1196 if (!msg.IsInitialized())
1197 {
1198 _LOG_DEBUG("Errrrr.. msg was not probally initialized "<<__FILE__ <<":"<<__LINE__<<". what is happening?");
1199 abort();
1200 }
1201 m_sock->publishData(m_user.getPrefix().toStdString(), m_session, buf, size, FRESHNESS);
1202
1203 delete buf;
1204
1205 m_lastMsgTime = time(NULL);
1206
1207 int nextSequence = m_sock->getNextSeq(m_user.getPrefix().toStdString(), m_session);
1208 Sync::MissingDataInfo mdi = {m_user.getPrefix().toStdString(), Sync::SeqNo(0), Sync::SeqNo(nextSequence - 1)};
1209 std::vector<Sync::MissingDataInfo> v;
1210 v.push_back(mdi);
1211 {
1212 boost::recursive_mutex::scoped_lock lock(m_sceneMutex);
1213 m_scene->processUpdate(v, m_sock->getRootDigest().c_str());
1214 m_scene->msgReceived(m_user.getPrefix(), m_user.getNick());
1215 }
1216}
1217
1218void
1219ChatDialog::openInviteListDialog()
1220{
1221 m_inviteListDialog->setInviteLabel(m_chatroomPrefix.toUri());
1222 m_inviteListDialog->show();
1223}
1224
1225void
1226ChatDialog::sendInvitationWrapper(QString invitee, bool isIntroducer)
1227{
Yingdi Yu64206112013-12-24 11:16:32 +08001228 ndn::Name inviteeNamespace(invitee.toStdString());
1229 ndn::ptr_lib::shared_ptr<ContactItem> inviteeItem = m_contactManager->getContact(inviteeNamespace);
Yingdi Yu42f66462013-10-31 17:38:22 -07001230 sendInvitation(inviteeItem, isIntroducer);
1231}
1232
Yingdi Yu42372442013-11-06 18:43:31 -08001233void
1234ChatDialog::closeEvent(QCloseEvent *e)
1235{
Yingdi Yu07b5b092013-11-07 17:00:54 -08001236 if (trayIcon->isVisible())
1237 {
1238 QMessageBox::information(this, tr("Chronos"),
1239 tr("The program will keep running in the "
1240 "system tray. To terminate the program"
1241 "choose <b>Quit</b> in the context memu"
1242 "of the system tray entry."));
1243 hide();
1244 e->ignore();
1245 }
1246}
1247
1248void
1249ChatDialog::quit()
1250{
Yingdi Yu42372442013-11-06 18:43:31 -08001251 hide();
Yingdi Yu42372442013-11-06 18:43:31 -08001252 emit closeChatDialog(m_chatroomPrefix);
1253}
1254
1255
1256
Yingdi Yu42f66462013-10-31 17:38:22 -07001257
Yingdi Yu04842232013-10-23 14:03:09 -07001258#if WAF
1259#include "chatdialog.moc"
1260#include "chatdialog.cpp.moc"
1261#endif