blob: 915e3d865d19439fdd527dfd5c1cb2235c7b7010 [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);
203 bool ok = m_db.open();
204
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700205 // _LOG_DEBUG("DB opened: " << std::boolalpha << ok );
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800206}
207
208void
209Controller::initialize()
210{
211 loadConf();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700212
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800213 m_keyChain.createIdentity(m_identity);
214
215 openDB();
216
217 emit identityUpdated(QString(m_identity.toUri().c_str()));
218
219 setInvitationListener();
220}
221
222void
223Controller::setInvitationListener()
224{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700225 if (m_invitationListenerId != 0)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800226 m_face->unsetInterestFilter(m_invitationListenerId);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700227
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800228 Name invitationPrefix;
229 Name routingPrefix = getInvitationRoutingPrefix();
230 size_t offset = 0;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700231 if (!routingPrefix.isPrefixOf(m_identity)) {
232 invitationPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
233 offset = routingPrefix.size() + 1;
234 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800235 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
236
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700237 m_invitationListenerId = m_face->setInterestFilter(invitationPrefix,
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700238 bind(&Controller::onInvitationInterestWrapper,
239 this, _1, _2, offset),
240 bind(&Controller::onInvitationRegisterFailed,
241 this, _1, _2));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800242}
243
244void
245Controller::loadConf()
246{
247 namespace fs = boost::filesystem;
248
249 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
250 fs::create_directories (chronosDir);
251
252 std::ifstream is((chronosDir / "config").c_str ());
253 ChronoChat::Conf conf;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700254 if (conf.ParseFromIstream(&is)) {
255 m_identity.clear();
256 m_identity.append(conf.identity());
257 if (conf.has_nick())
258 m_nick = conf.nick();
259 else
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800260 m_nick = m_identity.get(-1).toUri();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700261 }
262 else {
263 m_identity.clear();
264 // TODO: change below to system default;
265 m_identity.append("chronochat-tmp-identity")
266 .append(getRandomString());
267
268 m_nick = m_identity.get(-1).toUri();
269 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800270}
271
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700272void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800273Controller::saveConf()
274{
275 namespace fs = boost::filesystem;
276
277 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
278 fs::create_directories (chronosDir);
279
280 std::ofstream os((chronosDir / "config").c_str ());
281 ChronoChat::Conf conf;
282 conf.set_identity(m_identity.toUri());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700283 if (!m_nick.empty())
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800284 conf.set_nick(m_nick);
285 conf.SerializeToOstream(&os);
286
287 os.close();
288}
289
290void
291Controller::createActions()
292{
293 m_startChatroom = new QAction(tr("Start new chat"), this);
294 connect(m_startChatroom, SIGNAL(triggered()), this, SLOT(onStartChatAction()));
295
296 m_settingsAction = new QAction(tr("Settings"), this);
297 connect(m_settingsAction, SIGNAL(triggered()), this, SLOT(onSettingsAction()));
298
299 m_editProfileAction = new QAction(tr("Edit profile"), this);
300 connect(m_editProfileAction, SIGNAL(triggered()), this, SLOT(onProfileEditorAction()));
301
302 m_contactListAction = new QAction(tr("Contact List"), this);
303 connect(m_contactListAction, SIGNAL(triggered()), this, SLOT(onContactListAction()));
304
305 m_addContactAction = new QAction(tr("Add contact"), this);
306 connect(m_addContactAction, SIGNAL(triggered()), this, SLOT(onAddContactAction()));
307
308 m_updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
309 connect(m_updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(onUpdateLocalPrefixAction()));
310
311 m_minimizeAction = new QAction(tr("Mi&nimize"), this);
312 connect(m_minimizeAction, SIGNAL(triggered()), this, SLOT(onMinimizeAction()));
313
314 m_quitAction = new QAction(tr("Quit"), this);
315 connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuitAction()));
316}
317
318void
319Controller::createTrayIcon()
320{
321 createActions();
322
323 m_trayIconMenu = new QMenu(this);
324 m_trayIconMenu->addAction(m_startChatroom);
325 m_trayIconMenu->addSeparator();
326 m_trayIconMenu->addAction(m_settingsAction);
327 m_trayIconMenu->addAction(m_editProfileAction);
328 m_trayIconMenu->addSeparator();
329 m_trayIconMenu->addAction(m_contactListAction);
330 m_trayIconMenu->addAction(m_addContactAction);
331 m_trayIconMenu->addSeparator();
332 m_trayIconMenu->addAction(m_updateLocalPrefixAction);
333 m_trayIconMenu->addSeparator();
334 m_trayIconMenu->addAction(m_minimizeAction);
335 m_closeMenu = m_trayIconMenu->addMenu("Close chatroom");
336 m_closeMenu->setEnabled(false);
337 m_trayIconMenu->addSeparator();
338 m_trayIconMenu->addAction(m_quitAction);
339
340 m_trayIcon = new QSystemTrayIcon(this);
341 m_trayIcon->setContextMenu(m_trayIconMenu);
342
343 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
344 m_trayIcon->setToolTip("ChronoChat System Tray Icon");
345 m_trayIcon->setVisible(true);
346}
347
348void
349Controller::updateMenu()
350{
351 QMenu* menu = new QMenu(this);
352 QMenu* closeMenu = 0;
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700353
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800354 menu->addAction(m_startChatroom);
355 menu->addSeparator();
356 menu->addAction(m_settingsAction);
357 menu->addAction(m_editProfileAction);
358 menu->addSeparator();
Yingdi Yu233a9722014-03-07 15:47:09 -0800359 menu->addAction(m_contactListAction);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800360 menu->addAction(m_addContactAction);
361 menu->addSeparator();
362 {
363 ChatActionList::const_iterator it = m_chatActionList.begin();
364 ChatActionList::const_iterator end = m_chatActionList.end();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700365 if (it != end) {
366 for (; it != end; it++)
367 menu->addAction(it->second);
368 menu->addSeparator();
369 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800370 }
371 menu->addAction(m_updateLocalPrefixAction);
372 menu->addSeparator();
373 menu->addAction(m_minimizeAction);
374 closeMenu = menu->addMenu("Close chatroom");
375 {
376 ChatActionList::const_iterator it = m_closeActionList.begin();
377 ChatActionList::const_iterator end = m_closeActionList.end();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700378 if (it == end)
379 closeMenu->setEnabled(false);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800380 else
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700381 for (; it != end; it++)
382 closeMenu->addAction(it->second);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800383 }
384 menu->addSeparator();
385 menu->addAction(m_quitAction);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700386
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800387 m_trayIcon->setContextMenu(menu);
388 delete m_trayIconMenu;
389 m_trayIconMenu = menu;
390 m_closeMenu = closeMenu;
391}
392
393void
394Controller::onLocalPrefix(const Interest& interest, Data& data)
395{
396 QString localPrefixStr = QString::fromUtf8
397 (reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size())
398 .trimmed();
399
400 Name localPrefix(localPrefixStr.toStdString());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700401 if (m_localPrefix.empty() || m_localPrefix != localPrefix)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800402 emit localPrefixUpdated(localPrefixStr);
403}
404
405void
406Controller::onLocalPrefixTimeout(const Interest& interest)
407{
408 QString localPrefixStr("/private/local");
409
410 Name localPrefix(localPrefixStr.toStdString());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700411 if (m_localPrefix.empty() || m_localPrefix != localPrefix)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800412 emit localPrefixUpdated(localPrefixStr);
413}
414
415void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700416Controller::onInvitationInterestWrapper(const Name& prefix,
417 const Interest& interest,
418 size_t routingPrefixOffset)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800419{
Yingdi Yu233a9722014-03-07 15:47:09 -0800420 emit invitationInterest(prefix, interest, routingPrefixOffset);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800421}
422
423void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700424Controller::onInvitationRegisterFailed(const Name& prefix, const string& failInfo)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800425{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700426 // _LOG_DEBUG("Controller::onInvitationRegisterFailed: " << failInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800427}
428
429void
430Controller::onInvitationValidated(const shared_ptr<const Interest>& interest)
431{
432 Invitation invitation(interest->getName());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700433 // Should be obtained via a method of ContactManager.
434 string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800435
436 m_invitationDialog->setInvitation(alias, invitation.getChatroom(), interest->getName());
437 m_invitationDialog->show();
438}
439
440void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700441Controller::onInvitationValidationFailed(const shared_ptr<const Interest>& interest,
442 string failureInfo)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800443{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700444 // _LOG_DEBUG("Invitation: " << interest->getName() <<
445 // " cannot not be validated due to: " << failureInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800446}
447
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700448string
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800449Controller::getRandomString()
450{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700451 uint32_t r = ndn::random::generateWord32();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800452 std::stringstream ss;
453 {
454 using namespace CryptoPP;
455 StringSource(reinterpret_cast<uint8_t*>(&r), 4, true,
456 new HexEncoder(new FileSink(ss), false));
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700457
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800458 }
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700459 // for (int i = 0; i < 8; i++)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800460 // {
461 // uint32_t t = r & mask;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700462 // if (t < 10)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800463 // ss << static_cast<char>(t + 0x30);
464 // else
465 // ss << static_cast<char>(t + 0x57);
466 // r = r >> 4;
467 // }
468
469 return ss.str();
470}
471
472ndn::Name
473Controller::getInvitationRoutingPrefix()
474{
475 return Name("/ndn/broadcast");
476}
477
478void
479Controller::addChatDialog(const QString& chatroomName, ChatDialog* chatDialog)
480{
481 m_chatDialogList[chatroomName.toStdString()] = chatDialog;
482 connect(chatDialog, SIGNAL(closeChatDialog(const QString&)),
483 this, SLOT(onRemoveChatDialog(const QString&)));
484 connect(chatDialog, SIGNAL(showChatMessage(const QString&, const QString&, const QString&)),
485 this, SLOT(onShowChatMessage(const QString&, const QString&, const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800486 connect(chatDialog, SIGNAL(resetIcon()),
487 this, SLOT(onResetIcon()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800488 connect(this, SIGNAL(localPrefixUpdated(const QString&)),
489 chatDialog, SLOT(onLocalPrefixUpdated(const QString&)));
490
491 QAction* chatAction = new QAction(chatroomName, this);
492 m_chatActionList[chatroomName.toStdString()] = chatAction;
Yingdi Yu46ebfc42014-03-10 15:50:27 -0700493 connect(chatAction, SIGNAL(triggered()), chatDialog, SLOT(raise()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800494
495 QAction* closeAction = new QAction(chatroomName, this);
496 m_closeActionList[chatroomName.toStdString()] = closeAction;
497 connect(closeAction, SIGNAL(triggered()), chatDialog, SLOT(onClose()));
498
499 updateMenu();
500}
501
502// private slots:
503void
504Controller::onIdentityUpdated(const QString& identity)
505{
506 Name identityName(identity.toStdString());
507
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700508 while (!m_chatDialogList.empty()) {
509 ChatDialogList::const_iterator it = m_chatDialogList.begin();
510 onRemoveChatDialog(QString::fromStdString(it->first));
511 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800512
513 m_identity = identityName;
514 m_keyChain.createIdentity(m_identity);
Yingdi Yu233a9722014-03-07 15:47:09 -0800515 setInvitationListener();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800516
517 emit closeDBModule();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700518
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800519 QTimer::singleShot(500, this, SLOT(onIdentityUpdatedContinued()));
520
521}
522
523void
524Controller::onIdentityUpdatedContinued()
525{
526 QString connection = m_db.connectionName();
527 // _LOG_DEBUG("connection name: " << connection.toStdString());
528 QSqlDatabase::removeDatabase(connection);
529 m_db.close();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700530
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800531 openDB();
532
533 emit identityUpdated(QString(m_identity.toUri().c_str()));
534}
535
536void
537Controller::onContactIdListReady(const QStringList& list)
538{
539 ContactList contactList;
540
541 m_contactManager.getContactList(contactList);
542 m_validator.cleanTrustAnchor();
543
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700544 for (ContactList::const_iterator it = contactList.begin(); it != contactList.end(); it++)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800545 m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
546
547}
548
549void
550Controller::onNickUpdated(const QString& nick)
551{
552 m_nick = nick.toStdString();
553}
554
555void
556Controller::onLocalPrefixUpdated(const QString& localPrefix)
557{
558 m_localPrefix = Name(localPrefix.toStdString());
559}
560
561void
562Controller::onStartChatAction()
563{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700564 string chatroom = "chatroom-" + getRandomString();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800565
566 m_startChatDialog->setChatroom(chatroom);
567 m_startChatDialog->show();
568 m_startChatDialog->raise();
569}
570
571void
572Controller::onSettingsAction()
573{
574 m_settingDialog->setNick(QString(m_nick.c_str()));
575 m_settingDialog->show();
576 m_settingDialog->raise();
577}
578
579void
580Controller::onProfileEditorAction()
581{
582 m_profileEditor->show();
583 m_profileEditor->raise();
584}
585
586void
587Controller::onAddContactAction()
588{
589 emit refreshBrowseContact();
590 m_browseContactDialog->show();
591 m_browseContactDialog->raise();
592}
593
594void
595Controller::onContactListAction()
596{
597 m_contactPanel->show();
598 m_contactPanel->raise();
599}
600
601void
602Controller::onDirectAdd()
603{
604 m_addContactPanel->show();
605 m_addContactPanel->raise();
606}
607
608void
609Controller::onUpdateLocalPrefixAction()
610{
611 // Name interestName();
612 Interest interest("/local/ndn/prefix");
Yingdi Yua7876722014-03-25 14:46:55 -0700613 interest.setInterestLifetime(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800614 interest.setMustBeFresh(true);
615
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700616 m_face->expressInterest(interest,
617 bind(&Controller::onLocalPrefix, this, _1, _2),
618 bind(&Controller::onLocalPrefixTimeout, this, _1));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800619}
620
621void
622Controller::onMinimizeAction()
623{
624 m_settingDialog->hide();
625 m_startChatDialog->hide();
626 m_profileEditor->hide();
627 m_invitationDialog->hide();
628 m_addContactPanel->hide();
629
630 ChatDialogList::iterator it = m_chatDialogList.begin();
631 ChatDialogList::iterator end = m_chatDialogList.end();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700632 for (; it != end; it++)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800633 it->second->hide();
634}
635
636void
637Controller::onQuitAction()
638{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700639 while (!m_chatDialogList.empty()) {
640 ChatDialogList::const_iterator it = m_chatDialogList.begin();
641 onRemoveChatDialog(QString::fromStdString(it->first));
642 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800643
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700644 if (m_invitationListenerId != 0)
Yingdi Yu233a9722014-03-07 15:47:09 -0800645 m_face->unsetInterestFilter(m_invitationListenerId);
646
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800647 delete m_settingDialog;
648 delete m_startChatDialog;
649 delete m_profileEditor;
650 delete m_invitationDialog;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700651 delete m_browseContactDialog;
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800652 delete m_addContactPanel;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700653
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700654 m_face->getIoService().stop();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800655
656 QApplication::quit();
657}
658
659void
660Controller::onStartChatroom(const QString& chatroomName, bool secured)
661{
662 Name chatroomPrefix;
663 chatroomPrefix.append("ndn")
664 .append("broadcast")
665 .append("ChronoChat")
666 .append(chatroomName.toStdString());
667
668 // check if the chatroom exists
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700669 if (m_chatDialogList.find(chatroomName.toStdString()) != m_chatDialogList.end()) {
670 QMessageBox::information(this, tr("ChronoChat"),
671 tr("You are creating an existing chatroom."
672 "You can check it in the context memu."));
673 return;
674 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800675
676 // 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.
677 shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
678 ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, secured);
679
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700680 addChatDialog(chatroomName, chatDialog);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800681 chatDialog->show();
682}
683
684void
685Controller::onInvitationResponded(const Name& invitationName, bool accepted)
686{
687 Data response;
688 shared_ptr<IdentityCertificate> chatroomCert;
689
690 // generate reply;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700691 if (accepted) {
692 Name responseName = invitationName;
693 responseName.append(m_localPrefix.wireEncode());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800694
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700695 response.setName(responseName);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800696
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700697 // We should create a particular certificate for this chatroom, but let's use default one for now.
698 chatroomCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800699
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700700 response.setContent(chatroomCert->wireEncode());
701 response.setFreshnessPeriod(time::milliseconds(1000));
702 }
703 else {
704 response.setName(invitationName);
705 response.setFreshnessPeriod(time::milliseconds(1000));
706 }
707
Yingdi Yu233a9722014-03-07 15:47:09 -0800708 m_keyChain.signByIdentity(response, m_identity);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700709
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800710 // Check if we need a wrapper
711 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700712 if (invitationRoutingPrefix.isPrefixOf(m_identity))
713 m_face->put(response);
714 else {
715 Name wrappedName;
716 wrappedName.append(invitationRoutingPrefix)
717 .append(ROUTING_PREFIX_SEPARATOR, 2)
718 .append(response.getName());
Yingdi Yu233a9722014-03-07 15:47:09 -0800719
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700720 // _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700721
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700722 Data wrappedData(wrappedName);
723 wrappedData.setContent(response.wireEncode());
724 wrappedData.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800725
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700726 m_keyChain.signByIdentity(wrappedData, m_identity);
727 m_face->put(wrappedData);
728 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800729
730 // create chatroom
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700731 if (accepted) {
732 Invitation invitation(invitationName);
733 Name chatroomPrefix;
734 chatroomPrefix.append("ndn")
735 .append("broadcast")
736 .append("ChronoChat")
737 .append(invitation.getChatroom());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800738
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700739 //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.
740 shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
741 ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, true);
742 chatDialog->addSyncAnchor(invitation);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800743
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700744 addChatDialog(QString::fromStdString(invitation.getChatroom()), chatDialog);
745 chatDialog->show();
746 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800747}
748
749void
750Controller::onShowChatMessage(const QString& chatroomName, const QString& from, const QString& data)
751{
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700752 m_trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(chatroomName),
753 QString("<%1>: %2").arg(from).arg(data),
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800754 QSystemTrayIcon::Information, 20000);
755 m_trayIcon->setIcon(QIcon(":/images/note.png"));
756}
757
758void
Yingdi Yu233a9722014-03-07 15:47:09 -0800759Controller::onResetIcon()
760{
761 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
762}
763
764void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800765Controller::onRemoveChatDialog(const QString& chatroomName)
766{
767 ChatDialogList::iterator it = m_chatDialogList.find(chatroomName.toStdString());
768
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700769 if (it != m_chatDialogList.end()) {
770 ChatDialog* deletedChat = it->second;
771 if (deletedChat)
772 delete deletedChat;
773 m_chatDialogList.erase(it);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800774
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700775 QAction* chatAction = m_chatActionList[chatroomName.toStdString()];
776 QAction* closeAction = m_closeActionList[chatroomName.toStdString()];
777 if (chatAction)
778 delete chatAction;
779 if (closeAction)
780 delete closeAction;
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700781
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700782 m_chatActionList.erase(chatroomName.toStdString());
783 m_closeActionList.erase(chatroomName.toStdString());
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700784
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700785 updateMenu();
786 }
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800787}
788
789void
790Controller::onWarning(const QString& msg)
791{
792 QMessageBox::information(this, tr("ChronoChat"), msg);
793}
794
795void
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700796Controller::onError(const QString& msg)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800797{
798 QMessageBox::critical(this, tr("ChronoChat"), msg, QMessageBox::Ok);
799 exit(1);
800}
801
Yingdi Yu233a9722014-03-07 15:47:09 -0800802void
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700803Controller::onInvitationInterest(const Name& prefix,
804 const Interest& interest,
805 size_t routingPrefixOffset)
Yingdi Yu233a9722014-03-07 15:47:09 -0800806{
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700807 // _LOG_DEBUG("onInvitationInterest: " << interest.getName());
808 shared_ptr<Interest> invitationInterest =
809 make_shared<Interest>(boost::cref(interest.getName().getSubName(routingPrefixOffset)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800810
811 // check if the chatroom already exists;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700812 try {
Yingdi Yu233a9722014-03-07 15:47:09 -0800813 Invitation invitation(invitationInterest->getName());
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700814 if (m_chatDialogList.find(invitation.getChatroom()) != m_chatDialogList.end())
Yingdi Yu233a9722014-03-07 15:47:09 -0800815 return;
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700816 }
817 catch (Invitation::Error& e) {
818 // Cannot parse the invitation;
819 return;
820 }
Yingdi Yu233a9722014-03-07 15:47:09 -0800821
822 OnInterestValidated onValidated = bind(&Controller::onInvitationValidated, this, _1);
Yingdi Yu0b0a7362014-08-05 16:31:30 -0700823 OnInterestValidationFailed onValidationFailed = bind(&Controller::onInvitationValidationFailed,
824 this, _1, _2);
Yingdi Yu233a9722014-03-07 15:47:09 -0800825 m_validator.validate(*invitationInterest, onValidated, onValidationFailed);
826}
827
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800828} // namespace chronos
829
830#if WAF
831#include "controller.moc"
832#include "controller.cpp.moc"
833#endif