blob: 2ecae8702bf34310962d00c3e6d57d6891631c1e [file] [log] [blame]
Yingdi Yu348f5ea2014-03-01 14:47:25 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013, Regents of the University of California
4 * Yingdi Yu
5 *
6 * BSD license, See the LICENSE file for more information
7 *
8 * Author: Yingdi Yu <yingdi@cs.ucla.edu>
9 */
10
11#include <QApplication>
12#include <QMessageBox>
13#include <QDir>
14#include <QTimer>
Yingdi Yu0b0a7362014-08-05 16:31:30 -070015#include "controller.hpp"
Yingdi Yu348f5ea2014-03-01 14:47:25 -080016
17#ifndef Q_MOC_RUN
18#include <boost/filesystem.hpp>
19#include <boost/lexical_cast.hpp>
Yingdi Yufa0b6a02014-04-30 14:26:42 -070020#include <ndn-cxx/util/random.hpp>
Yingdi Yu0b0a7362014-08-05 16:31:30 -070021#include "cryptopp.hpp"
Yingdi Yu348f5ea2014-03-01 14:47:25 -080022#include "config.pb.h"
23#include "endorse-info.pb.h"
24#include "logging.h"
25#endif
26
Yingdi Yu0b0a7362014-08-05 16:31:30 -070027// INIT_LOGGER("chronos.Controller");
Yingdi Yu348f5ea2014-03-01 14:47:25 -080028
29Q_DECLARE_METATYPE(ndn::Name)
30Q_DECLARE_METATYPE(ndn::IdentityCertificate)
Yingdi Yu0b0a7362014-08-05 16:31:30 -070031Q_DECLARE_METATYPE(Chronos::EndorseInfo)
Yingdi Yu233a9722014-03-07 15:47:09 -080032Q_DECLARE_METATYPE(ndn::Interest)
33Q_DECLARE_METATYPE(size_t)
Yingdi Yu348f5ea2014-03-01 14:47:25 -080034
35namespace chronos {
36
Yingdi Yu0b0a7362014-08-05 16:31:30 -070037using std::string;
38
39using ndn::Face;
40using ndn::IdentityCertificate;
41using ndn::OnInterestValidated;
42using ndn::OnInterestValidationFailed;
Yingdi Yu17032f82014-03-25 15:48:23 -070043
Yingdi Yu348f5ea2014-03-01 14:47:25 -080044static const uint8_t ROUTING_PREFIX_SEPARATOR[2] = {0xF0, 0x2E};
45
46// constructor & destructor
47Controller::Controller(shared_ptr<Face> face,
Yingdi Yufa0b6a02014-04-30 14:26:42 -070048 QWidget* parent)
Yingdi Yu348f5ea2014-03-01 14:47:25 -080049 : QDialog(parent)
50 , m_face(face)
51 , m_invitationListenerId(0)
52 , m_contactManager(m_face)
53 , m_settingDialog(new SettingDialog)
54 , m_startChatDialog(new StartChatDialog)
55 , m_profileEditor(new ProfileEditor)
56 , m_invitationDialog(new InvitationDialog)
57 , m_contactPanel(new ContactPanel)
58 , m_browseContactDialog(new BrowseContactDialog)
59 , m_addContactPanel(new AddContactPanel)
60{
61 qRegisterMetaType<ndn::Name>("ndn.Name");
62 qRegisterMetaType<ndn::IdentityCertificate>("ndn.IdentityCertificate");
Yingdi Yu0b0a7362014-08-05 16:31:30 -070063 qRegisterMetaType<Chronos::EndorseInfo>("Chronos.EndorseInfo");
Yingdi Yu233a9722014-03-07 15:47:09 -080064 qRegisterMetaType<ndn::Interest>("ndn.Interest");
65 qRegisterMetaType<size_t>("size_t");
Yingdi Yu348f5ea2014-03-01 14:47:25 -080066
67 connect(this, SIGNAL(localPrefixUpdated(const QString&)),
68 this, SLOT(onLocalPrefixUpdated(const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -080069 connect(this, SIGNAL(invitationInterest(const ndn::Name&, const ndn::Interest&, size_t)),
70 this, SLOT(onInvitationInterest(const ndn::Name&, const ndn::Interest&, size_t)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -080071
72 // Connection to ContactManager
73 connect(this, SIGNAL(identityUpdated(const QString&)),
74 &m_contactManager, SLOT(onIdentityUpdated(const QString&)));
75 connect(&m_contactManager, SIGNAL(warning(const QString&)),
76 this, SLOT(onWarning(const QString&)));
77 connect(this, SIGNAL(refreshBrowseContact()),
78 &m_contactManager, SLOT(onRefreshBrowseContact()));
79 connect(&m_contactManager, SIGNAL(contactInfoFetchFailed(const QString&)),
80 this, SLOT(onWarning(const QString&)));
81 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
82 this, SLOT(onContactIdListReady(const QStringList&)));
83
84 // Connection to SettingDialog
85 connect(this, SIGNAL(identityUpdated(const QString&)),
86 m_settingDialog, SLOT(onIdentityUpdated(const QString&)));
87 connect(m_settingDialog, SIGNAL(identityUpdated(const QString&)),
88 this, SLOT(onIdentityUpdated(const QString&)));
89 connect(m_settingDialog, SIGNAL(nickUpdated(const QString&)),
90 this, SLOT(onNickUpdated(const QString&)));
91
92 // Connection to ProfileEditor
93 connect(this, SIGNAL(closeDBModule()),
94 m_profileEditor, SLOT(onCloseDBModule()));
95 connect(this, SIGNAL(identityUpdated(const QString&)),
96 m_profileEditor, SLOT(onIdentityUpdated(const QString&)));
97 connect(m_profileEditor, SIGNAL(updateProfile()),
98 &m_contactManager, SLOT(onUpdateProfile()));
99
100 // Connection to StartChatDialog
101 connect(m_startChatDialog, SIGNAL(startChatroom(const QString&, bool)),
102 this, SLOT(onStartChatroom(const QString&, bool)));
103
104 // Connection to InvitationDialog
105 connect(m_invitationDialog, SIGNAL(invitationResponded(const ndn::Name&, bool)),
106 this, SLOT(onInvitationResponded(const ndn::Name&, bool)));
107
108 // Connection to AddContactPanel
109 connect(m_addContactPanel, SIGNAL(fetchInfo(const QString&)),
110 &m_contactManager, SLOT(onFetchContactInfo(const QString&)));
111 connect(m_addContactPanel, SIGNAL(addContact(const QString&)),
112 &m_contactManager, SLOT(onAddFetchedContact(const QString&)));
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700113 connect(&m_contactManager, SIGNAL(contactEndorseInfoReady(const Chronos::EndorseInfo&)),
114 m_addContactPanel, SLOT(onContactEndorseInfoReady(const Chronos::EndorseInfo&)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800115
116
117 // Connection to BrowseContactDialog
118 connect(m_browseContactDialog, SIGNAL(directAddClicked()),
119 this, SLOT(onDirectAdd()));
120 connect(m_browseContactDialog, SIGNAL(fetchIdCert(const QString&)),
121 &m_contactManager, SLOT(onFetchIdCert(const QString&)));
122 connect(m_browseContactDialog, SIGNAL(addContact(const QString&)),
123 &m_contactManager, SLOT(onAddFetchedContactIdCert(const QString&)));
124 connect(&m_contactManager, SIGNAL(idCertNameListReady(const QStringList&)),
125 m_browseContactDialog, SLOT(onIdCertNameListReady(const QStringList&)));
126 connect(&m_contactManager, SIGNAL(nameListReady(const QStringList&)),
127 m_browseContactDialog, SLOT(onNameListReady(const QStringList&)));
128 connect(&m_contactManager, SIGNAL(idCertReady(const ndn::IdentityCertificate&)),
129 m_browseContactDialog, SLOT(onIdCertReady(const ndn::IdentityCertificate&)));
130
131 // Connection to ContactPanel
132 connect(m_contactPanel, SIGNAL(waitForContactList()),
133 &m_contactManager, SLOT(onWaitForContactList()));
134 connect(m_contactPanel, SIGNAL(waitForContactInfo(const QString&)),
135 &m_contactManager, SLOT(onWaitForContactInfo(const QString&)));
136 connect(m_contactPanel, SIGNAL(removeContact(const QString&)),
137 &m_contactManager, SLOT(onRemoveContact(const QString&)));
138 connect(m_contactPanel, SIGNAL(updateAlias(const QString&, const QString&)),
139 &m_contactManager, SLOT(onUpdateAlias(const QString&, const QString&)));
140 connect(m_contactPanel, SIGNAL(updateIsIntroducer(const QString&, bool)),
141 &m_contactManager, SLOT(onUpdateIsIntroducer(const QString&, bool)));
142 connect(m_contactPanel, SIGNAL(updateEndorseCertificate(const QString&)),
143 &m_contactManager, SLOT(onUpdateEndorseCertificate(const QString&)));
144 connect(m_contactPanel, SIGNAL(warning(const QString&)),
145 this, SLOT(onWarning(const QString&)));
146 connect(this, SIGNAL(closeDBModule()),
147 m_contactPanel, SLOT(onCloseDBModule()));
148 connect(this, SIGNAL(identityUpdated(const QString&)),
149 m_contactPanel, SLOT(onIdentityUpdated(const QString&)));
150 connect(&m_contactManager, SIGNAL(contactAliasListReady(const QStringList&)),
151 m_contactPanel, SLOT(onContactAliasListReady(const QStringList&)));
152 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
153 m_contactPanel, SLOT(onContactIdListReady(const QStringList&)));
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700154 connect(&m_contactManager, SIGNAL(contactInfoReady(const QString&, const QString&,
155 const QString&, bool)),
156 m_contactPanel, SLOT(onContactInfoReady(const QString&, const QString&,
157 const QString&, bool)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800158
159 initialize();
160
Yingdi Yu233a9722014-03-07 15:47:09 -0800161 createTrayIcon();
162
163 onUpdateLocalPrefixAction();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800164}
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700165
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800166Controller::~Controller()
167{
168 saveConf();
169}
170
171// public methods
172
173
174// private methods
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700175string
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800176Controller::getDBName()
177{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700178 string dbName("chronos-");
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800179
180 std::stringstream ss;
181 {
182 using namespace CryptoPP;
183
184 SHA256 hash;
185 StringSource(m_identity.wireEncode().wire(), m_identity.wireEncode().size(), true,
186 new HashFilter(hash, new HexEncoder(new FileSink(ss), false)));
187 }
188 dbName.append(ss.str()).append(".db");
189
190 return dbName;
191}
192
193void
194Controller::openDB()
195{
196 m_db = QSqlDatabase::addDatabase("QSQLITE");
197 QString path = (QDir::home().path());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700198 path.append(QDir::separator())
199 .append(".chronos")
200 .append(QDir::separator())
201 .append(getDBName().c_str());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800202 m_db.setDatabaseName(path);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800203
Yingdi Yu42125862014-08-07 17:04:28 -0700204 m_db.open();
205
206 // bool ok = m_db.open();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700207 // _LOG_DEBUG("DB opened: " << std::boolalpha << ok );
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800208}
209
210void
211Controller::initialize()
212{
213 loadConf();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700214
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800215 m_keyChain.createIdentity(m_identity);
216
217 openDB();
218
219 emit identityUpdated(QString(m_identity.toUri().c_str()));
220
221 setInvitationListener();
222}
223
224void
225Controller::setInvitationListener()
226{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700227 if (m_invitationListenerId != 0)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800228 m_face->unsetInterestFilter(m_invitationListenerId);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700229
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800230 Name invitationPrefix;
231 Name routingPrefix = getInvitationRoutingPrefix();
232 size_t offset = 0;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700233 if (!routingPrefix.isPrefixOf(m_identity)) {
234 invitationPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
235 offset = routingPrefix.size() + 1;
236 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800237 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
238
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700239 m_invitationListenerId = m_face->setInterestFilter(invitationPrefix,
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700240 bind(&Controller::onInvitationInterestWrapper,
241 this, _1, _2, offset),
242 bind(&Controller::onInvitationRegisterFailed,
243 this, _1, _2));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800244}
245
246void
247Controller::loadConf()
248{
249 namespace fs = boost::filesystem;
250
251 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
252 fs::create_directories (chronosDir);
253
254 std::ifstream is((chronosDir / "config").c_str ());
255 ChronoChat::Conf conf;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700256 if (conf.ParseFromIstream(&is)) {
257 m_identity.clear();
258 m_identity.append(conf.identity());
259 if (conf.has_nick())
260 m_nick = conf.nick();
261 else
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800262 m_nick = m_identity.get(-1).toUri();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700263 }
264 else {
265 m_identity.clear();
266 // TODO: change below to system default;
267 m_identity.append("chronochat-tmp-identity")
268 .append(getRandomString());
269
270 m_nick = m_identity.get(-1).toUri();
271 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800272}
273
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700274void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800275Controller::saveConf()
276{
277 namespace fs = boost::filesystem;
278
279 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
280 fs::create_directories (chronosDir);
281
282 std::ofstream os((chronosDir / "config").c_str ());
283 ChronoChat::Conf conf;
284 conf.set_identity(m_identity.toUri());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700285 if (!m_nick.empty())
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800286 conf.set_nick(m_nick);
287 conf.SerializeToOstream(&os);
288
289 os.close();
290}
291
292void
293Controller::createActions()
294{
295 m_startChatroom = new QAction(tr("Start new chat"), this);
296 connect(m_startChatroom, SIGNAL(triggered()), this, SLOT(onStartChatAction()));
297
298 m_settingsAction = new QAction(tr("Settings"), this);
299 connect(m_settingsAction, SIGNAL(triggered()), this, SLOT(onSettingsAction()));
300
301 m_editProfileAction = new QAction(tr("Edit profile"), this);
302 connect(m_editProfileAction, SIGNAL(triggered()), this, SLOT(onProfileEditorAction()));
303
304 m_contactListAction = new QAction(tr("Contact List"), this);
305 connect(m_contactListAction, SIGNAL(triggered()), this, SLOT(onContactListAction()));
306
307 m_addContactAction = new QAction(tr("Add contact"), this);
308 connect(m_addContactAction, SIGNAL(triggered()), this, SLOT(onAddContactAction()));
309
310 m_updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
311 connect(m_updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(onUpdateLocalPrefixAction()));
312
313 m_minimizeAction = new QAction(tr("Mi&nimize"), this);
314 connect(m_minimizeAction, SIGNAL(triggered()), this, SLOT(onMinimizeAction()));
315
316 m_quitAction = new QAction(tr("Quit"), this);
317 connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuitAction()));
318}
319
320void
321Controller::createTrayIcon()
322{
323 createActions();
324
325 m_trayIconMenu = new QMenu(this);
326 m_trayIconMenu->addAction(m_startChatroom);
327 m_trayIconMenu->addSeparator();
328 m_trayIconMenu->addAction(m_settingsAction);
329 m_trayIconMenu->addAction(m_editProfileAction);
330 m_trayIconMenu->addSeparator();
331 m_trayIconMenu->addAction(m_contactListAction);
332 m_trayIconMenu->addAction(m_addContactAction);
333 m_trayIconMenu->addSeparator();
334 m_trayIconMenu->addAction(m_updateLocalPrefixAction);
335 m_trayIconMenu->addSeparator();
336 m_trayIconMenu->addAction(m_minimizeAction);
337 m_closeMenu = m_trayIconMenu->addMenu("Close chatroom");
338 m_closeMenu->setEnabled(false);
339 m_trayIconMenu->addSeparator();
340 m_trayIconMenu->addAction(m_quitAction);
341
342 m_trayIcon = new QSystemTrayIcon(this);
343 m_trayIcon->setContextMenu(m_trayIconMenu);
344
345 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
346 m_trayIcon->setToolTip("ChronoChat System Tray Icon");
347 m_trayIcon->setVisible(true);
348}
349
350void
351Controller::updateMenu()
352{
353 QMenu* menu = new QMenu(this);
354 QMenu* closeMenu = 0;
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700355
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800356 menu->addAction(m_startChatroom);
357 menu->addSeparator();
358 menu->addAction(m_settingsAction);
359 menu->addAction(m_editProfileAction);
360 menu->addSeparator();
Yingdi Yu233a9722014-03-07 15:47:09 -0800361 menu->addAction(m_contactListAction);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800362 menu->addAction(m_addContactAction);
363 menu->addSeparator();
364 {
365 ChatActionList::const_iterator it = m_chatActionList.begin();
366 ChatActionList::const_iterator end = m_chatActionList.end();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700367 if (it != end) {
368 for (; it != end; it++)
369 menu->addAction(it->second);
370 menu->addSeparator();
371 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800372 }
373 menu->addAction(m_updateLocalPrefixAction);
374 menu->addSeparator();
375 menu->addAction(m_minimizeAction);
376 closeMenu = menu->addMenu("Close chatroom");
377 {
378 ChatActionList::const_iterator it = m_closeActionList.begin();
379 ChatActionList::const_iterator end = m_closeActionList.end();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700380 if (it == end)
381 closeMenu->setEnabled(false);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800382 else
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700383 for (; it != end; it++)
384 closeMenu->addAction(it->second);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800385 }
386 menu->addSeparator();
387 menu->addAction(m_quitAction);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700388
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800389 m_trayIcon->setContextMenu(menu);
390 delete m_trayIconMenu;
391 m_trayIconMenu = menu;
392 m_closeMenu = closeMenu;
393}
394
395void
396Controller::onLocalPrefix(const Interest& interest, Data& data)
397{
398 QString localPrefixStr = QString::fromUtf8
399 (reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size())
400 .trimmed();
401
402 Name localPrefix(localPrefixStr.toStdString());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700403 if (m_localPrefix.empty() || m_localPrefix != localPrefix)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800404 emit localPrefixUpdated(localPrefixStr);
405}
406
407void
408Controller::onLocalPrefixTimeout(const Interest& interest)
409{
410 QString localPrefixStr("/private/local");
411
412 Name localPrefix(localPrefixStr.toStdString());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700413 if (m_localPrefix.empty() || m_localPrefix != localPrefix)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800414 emit localPrefixUpdated(localPrefixStr);
415}
416
417void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700418Controller::onInvitationInterestWrapper(const Name& prefix,
419 const Interest& interest,
420 size_t routingPrefixOffset)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800421{
Yingdi Yu233a9722014-03-07 15:47:09 -0800422 emit invitationInterest(prefix, interest, routingPrefixOffset);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800423}
424
425void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700426Controller::onInvitationRegisterFailed(const Name& prefix, const string& failInfo)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800427{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700428 // _LOG_DEBUG("Controller::onInvitationRegisterFailed: " << failInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800429}
430
431void
432Controller::onInvitationValidated(const shared_ptr<const Interest>& interest)
433{
434 Invitation invitation(interest->getName());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700435 // Should be obtained via a method of ContactManager.
436 string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800437
438 m_invitationDialog->setInvitation(alias, invitation.getChatroom(), interest->getName());
439 m_invitationDialog->show();
440}
441
442void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700443Controller::onInvitationValidationFailed(const shared_ptr<const Interest>& interest,
444 string failureInfo)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800445{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700446 // _LOG_DEBUG("Invitation: " << interest->getName() <<
447 // " cannot not be validated due to: " << failureInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800448}
449
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700450string
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800451Controller::getRandomString()
452{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700453 uint32_t r = ndn::random::generateWord32();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800454 std::stringstream ss;
455 {
456 using namespace CryptoPP;
457 StringSource(reinterpret_cast<uint8_t*>(&r), 4, true,
458 new HexEncoder(new FileSink(ss), false));
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700459
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800460 }
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700461 // for (int i = 0; i < 8; i++)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800462 // {
463 // uint32_t t = r & mask;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700464 // if (t < 10)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800465 // ss << static_cast<char>(t + 0x30);
466 // else
467 // ss << static_cast<char>(t + 0x57);
468 // r = r >> 4;
469 // }
470
471 return ss.str();
472}
473
474ndn::Name
475Controller::getInvitationRoutingPrefix()
476{
477 return Name("/ndn/broadcast");
478}
479
480void
481Controller::addChatDialog(const QString& chatroomName, ChatDialog* chatDialog)
482{
483 m_chatDialogList[chatroomName.toStdString()] = chatDialog;
484 connect(chatDialog, SIGNAL(closeChatDialog(const QString&)),
485 this, SLOT(onRemoveChatDialog(const QString&)));
486 connect(chatDialog, SIGNAL(showChatMessage(const QString&, const QString&, const QString&)),
487 this, SLOT(onShowChatMessage(const QString&, const QString&, const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800488 connect(chatDialog, SIGNAL(resetIcon()),
489 this, SLOT(onResetIcon()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800490 connect(this, SIGNAL(localPrefixUpdated(const QString&)),
491 chatDialog, SLOT(onLocalPrefixUpdated(const QString&)));
492
493 QAction* chatAction = new QAction(chatroomName, this);
494 m_chatActionList[chatroomName.toStdString()] = chatAction;
Yingdi Yu46ebfc42014-03-10 15:50:27 -0700495 connect(chatAction, SIGNAL(triggered()), chatDialog, SLOT(raise()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800496
497 QAction* closeAction = new QAction(chatroomName, this);
498 m_closeActionList[chatroomName.toStdString()] = closeAction;
499 connect(closeAction, SIGNAL(triggered()), chatDialog, SLOT(onClose()));
500
501 updateMenu();
502}
503
504// private slots:
505void
506Controller::onIdentityUpdated(const QString& identity)
507{
508 Name identityName(identity.toStdString());
509
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700510 while (!m_chatDialogList.empty()) {
511 ChatDialogList::const_iterator it = m_chatDialogList.begin();
512 onRemoveChatDialog(QString::fromStdString(it->first));
513 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800514
515 m_identity = identityName;
516 m_keyChain.createIdentity(m_identity);
Yingdi Yu233a9722014-03-07 15:47:09 -0800517 setInvitationListener();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800518
519 emit closeDBModule();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700520
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800521 QTimer::singleShot(500, this, SLOT(onIdentityUpdatedContinued()));
522
523}
524
525void
526Controller::onIdentityUpdatedContinued()
527{
528 QString connection = m_db.connectionName();
529 // _LOG_DEBUG("connection name: " << connection.toStdString());
530 QSqlDatabase::removeDatabase(connection);
531 m_db.close();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700532
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800533 openDB();
534
535 emit identityUpdated(QString(m_identity.toUri().c_str()));
536}
537
538void
539Controller::onContactIdListReady(const QStringList& list)
540{
541 ContactList contactList;
542
543 m_contactManager.getContactList(contactList);
544 m_validator.cleanTrustAnchor();
545
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700546 for (ContactList::const_iterator it = contactList.begin(); it != contactList.end(); it++)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800547 m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
548
549}
550
551void
552Controller::onNickUpdated(const QString& nick)
553{
554 m_nick = nick.toStdString();
555}
556
557void
558Controller::onLocalPrefixUpdated(const QString& localPrefix)
559{
560 m_localPrefix = Name(localPrefix.toStdString());
561}
562
563void
564Controller::onStartChatAction()
565{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700566 string chatroom = "chatroom-" + getRandomString();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800567
568 m_startChatDialog->setChatroom(chatroom);
569 m_startChatDialog->show();
570 m_startChatDialog->raise();
571}
572
573void
574Controller::onSettingsAction()
575{
576 m_settingDialog->setNick(QString(m_nick.c_str()));
577 m_settingDialog->show();
578 m_settingDialog->raise();
579}
580
581void
582Controller::onProfileEditorAction()
583{
584 m_profileEditor->show();
585 m_profileEditor->raise();
586}
587
588void
589Controller::onAddContactAction()
590{
591 emit refreshBrowseContact();
592 m_browseContactDialog->show();
593 m_browseContactDialog->raise();
594}
595
596void
597Controller::onContactListAction()
598{
599 m_contactPanel->show();
600 m_contactPanel->raise();
601}
602
603void
604Controller::onDirectAdd()
605{
606 m_addContactPanel->show();
607 m_addContactPanel->raise();
608}
609
610void
611Controller::onUpdateLocalPrefixAction()
612{
613 // Name interestName();
614 Interest interest("/local/ndn/prefix");
Yingdi Yua7876722014-03-25 14:46:55 -0700615 interest.setInterestLifetime(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800616 interest.setMustBeFresh(true);
617
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700618 m_face->expressInterest(interest,
619 bind(&Controller::onLocalPrefix, this, _1, _2),
620 bind(&Controller::onLocalPrefixTimeout, this, _1));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800621}
622
623void
624Controller::onMinimizeAction()
625{
626 m_settingDialog->hide();
627 m_startChatDialog->hide();
628 m_profileEditor->hide();
629 m_invitationDialog->hide();
630 m_addContactPanel->hide();
631
632 ChatDialogList::iterator it = m_chatDialogList.begin();
633 ChatDialogList::iterator end = m_chatDialogList.end();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700634 for (; it != end; it++)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800635 it->second->hide();
636}
637
638void
639Controller::onQuitAction()
640{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700641 while (!m_chatDialogList.empty()) {
642 ChatDialogList::const_iterator it = m_chatDialogList.begin();
643 onRemoveChatDialog(QString::fromStdString(it->first));
644 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800645
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700646 if (m_invitationListenerId != 0)
Yingdi Yu233a9722014-03-07 15:47:09 -0800647 m_face->unsetInterestFilter(m_invitationListenerId);
648
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800649 delete m_settingDialog;
650 delete m_startChatDialog;
651 delete m_profileEditor;
652 delete m_invitationDialog;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700653 delete m_browseContactDialog;
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800654 delete m_addContactPanel;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700655
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700656 m_face->getIoService().stop();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800657
658 QApplication::quit();
659}
660
661void
662Controller::onStartChatroom(const QString& chatroomName, bool secured)
663{
664 Name chatroomPrefix;
665 chatroomPrefix.append("ndn")
666 .append("broadcast")
667 .append("ChronoChat")
668 .append(chatroomName.toStdString());
669
670 // check if the chatroom exists
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700671 if (m_chatDialogList.find(chatroomName.toStdString()) != m_chatDialogList.end()) {
672 QMessageBox::information(this, tr("ChronoChat"),
673 tr("You are creating an existing chatroom."
674 "You can check it in the context memu."));
675 return;
676 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800677
678 // TODO: We should create a chatroom specific key/cert (which should be created in the first half of this method, but let's use the default one for now.
679 shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
680 ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, secured);
681
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700682 addChatDialog(chatroomName, chatDialog);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800683 chatDialog->show();
684}
685
686void
687Controller::onInvitationResponded(const Name& invitationName, bool accepted)
688{
689 Data response;
690 shared_ptr<IdentityCertificate> chatroomCert;
691
692 // generate reply;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700693 if (accepted) {
694 Name responseName = invitationName;
695 responseName.append(m_localPrefix.wireEncode());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800696
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700697 response.setName(responseName);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800698
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700699 // We should create a particular certificate for this chatroom, but let's use default one for now.
700 chatroomCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800701
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700702 response.setContent(chatroomCert->wireEncode());
703 response.setFreshnessPeriod(time::milliseconds(1000));
704 }
705 else {
706 response.setName(invitationName);
707 response.setFreshnessPeriod(time::milliseconds(1000));
708 }
709
Yingdi Yu233a9722014-03-07 15:47:09 -0800710 m_keyChain.signByIdentity(response, m_identity);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700711
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800712 // Check if we need a wrapper
713 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700714 if (invitationRoutingPrefix.isPrefixOf(m_identity))
715 m_face->put(response);
716 else {
717 Name wrappedName;
718 wrappedName.append(invitationRoutingPrefix)
719 .append(ROUTING_PREFIX_SEPARATOR, 2)
720 .append(response.getName());
Yingdi Yu233a9722014-03-07 15:47:09 -0800721
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700722 // _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700723
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700724 Data wrappedData(wrappedName);
725 wrappedData.setContent(response.wireEncode());
726 wrappedData.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800727
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700728 m_keyChain.signByIdentity(wrappedData, m_identity);
729 m_face->put(wrappedData);
730 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800731
732 // create chatroom
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700733 if (accepted) {
734 Invitation invitation(invitationName);
735 Name chatroomPrefix;
736 chatroomPrefix.append("ndn")
737 .append("broadcast")
738 .append("ChronoChat")
739 .append(invitation.getChatroom());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800740
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700741 //We should create a chatroom specific key/cert (which should be created in the first half of this method, but let's use the default one for now.
742 shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
743 ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, true);
744 chatDialog->addSyncAnchor(invitation);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800745
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700746 addChatDialog(QString::fromStdString(invitation.getChatroom()), chatDialog);
747 chatDialog->show();
748 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800749}
750
751void
752Controller::onShowChatMessage(const QString& chatroomName, const QString& from, const QString& data)
753{
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700754 m_trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(chatroomName),
755 QString("<%1>: %2").arg(from).arg(data),
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800756 QSystemTrayIcon::Information, 20000);
757 m_trayIcon->setIcon(QIcon(":/images/note.png"));
758}
759
760void
Yingdi Yu233a9722014-03-07 15:47:09 -0800761Controller::onResetIcon()
762{
763 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
764}
765
766void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800767Controller::onRemoveChatDialog(const QString& chatroomName)
768{
769 ChatDialogList::iterator it = m_chatDialogList.find(chatroomName.toStdString());
770
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700771 if (it != m_chatDialogList.end()) {
772 ChatDialog* deletedChat = it->second;
773 if (deletedChat)
774 delete deletedChat;
775 m_chatDialogList.erase(it);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800776
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700777 QAction* chatAction = m_chatActionList[chatroomName.toStdString()];
778 QAction* closeAction = m_closeActionList[chatroomName.toStdString()];
779 if (chatAction)
780 delete chatAction;
781 if (closeAction)
782 delete closeAction;
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700783
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700784 m_chatActionList.erase(chatroomName.toStdString());
785 m_closeActionList.erase(chatroomName.toStdString());
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700786
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700787 updateMenu();
788 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800789}
790
791void
792Controller::onWarning(const QString& msg)
793{
794 QMessageBox::information(this, tr("ChronoChat"), msg);
795}
796
797void
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700798Controller::onError(const QString& msg)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800799{
800 QMessageBox::critical(this, tr("ChronoChat"), msg, QMessageBox::Ok);
801 exit(1);
802}
803
Yingdi Yu233a9722014-03-07 15:47:09 -0800804void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700805Controller::onInvitationInterest(const Name& prefix,
806 const Interest& interest,
807 size_t routingPrefixOffset)
Yingdi Yu233a9722014-03-07 15:47:09 -0800808{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700809 // _LOG_DEBUG("onInvitationInterest: " << interest.getName());
810 shared_ptr<Interest> invitationInterest =
811 make_shared<Interest>(boost::cref(interest.getName().getSubName(routingPrefixOffset)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800812
813 // check if the chatroom already exists;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700814 try {
Yingdi Yu233a9722014-03-07 15:47:09 -0800815 Invitation invitation(invitationInterest->getName());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700816 if (m_chatDialogList.find(invitation.getChatroom()) != m_chatDialogList.end())
Yingdi Yu233a9722014-03-07 15:47:09 -0800817 return;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700818 }
819 catch (Invitation::Error& e) {
820 // Cannot parse the invitation;
821 return;
822 }
Yingdi Yu233a9722014-03-07 15:47:09 -0800823
824 OnInterestValidated onValidated = bind(&Controller::onInvitationValidated, this, _1);
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700825 OnInterestValidationFailed onValidationFailed = bind(&Controller::onInvitationValidationFailed,
826 this, _1, _2);
Yingdi Yu233a9722014-03-07 15:47:09 -0800827 m_validator.validate(*invitationInterest, onValidated, onValidationFailed);
828}
829
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800830} // namespace chronos
831
832#if WAF
833#include "controller.moc"
Yingdi Yu42125862014-08-07 17:04:28 -0700834// #include "controller.cpp.moc"
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800835#endif