blob: 7517a4e78ceefcc774638afef8bd5cbf9d5fd164 [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>
15#include "controller.h"
16
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 Yu348f5ea2014-03-01 14:47:25 -080021#include <cryptopp/sha.h>
22#include <cryptopp/hex.h>
23#include <cryptopp/files.h>
24#include <cryptopp/filters.h>
25#include "config.pb.h"
26#include "endorse-info.pb.h"
27#include "logging.h"
28#endif
29
30INIT_LOGGER("chronos.Controller");
31
32using namespace ndn;
33
34Q_DECLARE_METATYPE(ndn::Name)
35Q_DECLARE_METATYPE(ndn::IdentityCertificate)
36Q_DECLARE_METATYPE(chronos::EndorseInfo)
Yingdi Yu233a9722014-03-07 15:47:09 -080037Q_DECLARE_METATYPE(ndn::Interest)
38Q_DECLARE_METATYPE(size_t)
Yingdi Yu348f5ea2014-03-01 14:47:25 -080039
40namespace chronos {
41
Yingdi Yu17032f82014-03-25 15:48:23 -070042using ndn::shared_ptr;
43
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");
63 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&)));
113 connect(&m_contactManager, SIGNAL(contactEndorseInfoReady(const chronos::EndorseInfo&)),
114 m_addContactPanel, SLOT(onContactEndorseInfoReady(const chronos::EndorseInfo&)));
115
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&)));
154 connect(&m_contactManager, SIGNAL(contactInfoReady(const QString&, const QString&, const QString&, bool)),
155 m_contactPanel, SLOT(onContactInfoReady(const QString&, const QString&, const QString&, bool)));
156
157 initialize();
158
Yingdi Yu233a9722014-03-07 15:47:09 -0800159 createTrayIcon();
160
161 onUpdateLocalPrefixAction();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800162}
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700163
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800164Controller::~Controller()
165{
166 saveConf();
167}
168
169// public methods
170
171
172// private methods
173std::string
174Controller::getDBName()
175{
176 std::string dbName("chronos-");
177
178 std::stringstream ss;
179 {
180 using namespace CryptoPP;
181
182 SHA256 hash;
183 StringSource(m_identity.wireEncode().wire(), m_identity.wireEncode().size(), true,
184 new HashFilter(hash, new HexEncoder(new FileSink(ss), false)));
185 }
186 dbName.append(ss.str()).append(".db");
187
188 return dbName;
189}
190
191void
192Controller::openDB()
193{
194 m_db = QSqlDatabase::addDatabase("QSQLITE");
195 QString path = (QDir::home().path());
196 path.append(QDir::separator()).append(".chronos").append(QDir::separator()).append(getDBName().c_str());
197 m_db.setDatabaseName(path);
198 bool ok = m_db.open();
199
200 _LOG_DEBUG("DB opened: " << std::boolalpha << ok );
201}
202
203void
204Controller::initialize()
205{
206 loadConf();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700207
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800208 m_keyChain.createIdentity(m_identity);
209
210 openDB();
211
212 emit identityUpdated(QString(m_identity.toUri().c_str()));
213
214 setInvitationListener();
215}
216
217void
218Controller::setInvitationListener()
219{
220 if(m_invitationListenerId != 0)
221 m_face->unsetInterestFilter(m_invitationListenerId);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700222
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800223 Name invitationPrefix;
224 Name routingPrefix = getInvitationRoutingPrefix();
225 size_t offset = 0;
226 if(!routingPrefix.isPrefixOf(m_identity))
227 {
228 invitationPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
229 offset = routingPrefix.size() + 1;
230 }
231 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
232
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700233 m_invitationListenerId = m_face->setInterestFilter(invitationPrefix,
Yingdi Yu233a9722014-03-07 15:47:09 -0800234 bind(&Controller::onInvitationInterestWrapper, this, _1, _2, offset),
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800235 bind(&Controller::onInvitationRegisterFailed, this, _1, _2));
236}
237
238void
239Controller::loadConf()
240{
241 namespace fs = boost::filesystem;
242
243 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
244 fs::create_directories (chronosDir);
245
246 std::ifstream is((chronosDir / "config").c_str ());
247 ChronoChat::Conf conf;
248 if(conf.ParseFromIstream(&is))
249 {
250 m_identity.clear();
251 m_identity.append(conf.identity());
252 if(conf.has_nick())
253 m_nick = conf.nick();
254 else
255 m_nick = m_identity.get(-1).toUri();
256 }
257 else
258 {
259 m_identity.clear();
260 // TODO: change below to system default;
261 m_identity.append("chronochat-tmp-identity")
Yingdi Yua7876722014-03-25 14:46:55 -0700262 .append(getRandomString());
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700263
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800264 m_nick = m_identity.get(-1).toUri();
265 }
266}
267
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700268void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800269Controller::saveConf()
270{
271 namespace fs = boost::filesystem;
272
273 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
274 fs::create_directories (chronosDir);
275
276 std::ofstream os((chronosDir / "config").c_str ());
277 ChronoChat::Conf conf;
278 conf.set_identity(m_identity.toUri());
279 if(!m_nick.empty())
280 conf.set_nick(m_nick);
281 conf.SerializeToOstream(&os);
282
283 os.close();
284}
285
286void
287Controller::createActions()
288{
289 m_startChatroom = new QAction(tr("Start new chat"), this);
290 connect(m_startChatroom, SIGNAL(triggered()), this, SLOT(onStartChatAction()));
291
292 m_settingsAction = new QAction(tr("Settings"), this);
293 connect(m_settingsAction, SIGNAL(triggered()), this, SLOT(onSettingsAction()));
294
295 m_editProfileAction = new QAction(tr("Edit profile"), this);
296 connect(m_editProfileAction, SIGNAL(triggered()), this, SLOT(onProfileEditorAction()));
297
298 m_contactListAction = new QAction(tr("Contact List"), this);
299 connect(m_contactListAction, SIGNAL(triggered()), this, SLOT(onContactListAction()));
300
301 m_addContactAction = new QAction(tr("Add contact"), this);
302 connect(m_addContactAction, SIGNAL(triggered()), this, SLOT(onAddContactAction()));
303
304 m_updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
305 connect(m_updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(onUpdateLocalPrefixAction()));
306
307 m_minimizeAction = new QAction(tr("Mi&nimize"), this);
308 connect(m_minimizeAction, SIGNAL(triggered()), this, SLOT(onMinimizeAction()));
309
310 m_quitAction = new QAction(tr("Quit"), this);
311 connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuitAction()));
312}
313
314void
315Controller::createTrayIcon()
316{
317 createActions();
318
319 m_trayIconMenu = new QMenu(this);
320 m_trayIconMenu->addAction(m_startChatroom);
321 m_trayIconMenu->addSeparator();
322 m_trayIconMenu->addAction(m_settingsAction);
323 m_trayIconMenu->addAction(m_editProfileAction);
324 m_trayIconMenu->addSeparator();
325 m_trayIconMenu->addAction(m_contactListAction);
326 m_trayIconMenu->addAction(m_addContactAction);
327 m_trayIconMenu->addSeparator();
328 m_trayIconMenu->addAction(m_updateLocalPrefixAction);
329 m_trayIconMenu->addSeparator();
330 m_trayIconMenu->addAction(m_minimizeAction);
331 m_closeMenu = m_trayIconMenu->addMenu("Close chatroom");
332 m_closeMenu->setEnabled(false);
333 m_trayIconMenu->addSeparator();
334 m_trayIconMenu->addAction(m_quitAction);
335
336 m_trayIcon = new QSystemTrayIcon(this);
337 m_trayIcon->setContextMenu(m_trayIconMenu);
338
339 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
340 m_trayIcon->setToolTip("ChronoChat System Tray Icon");
341 m_trayIcon->setVisible(true);
342}
343
344void
345Controller::updateMenu()
346{
347 QMenu* menu = new QMenu(this);
348 QMenu* closeMenu = 0;
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700349
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800350 menu->addAction(m_startChatroom);
351 menu->addSeparator();
352 menu->addAction(m_settingsAction);
353 menu->addAction(m_editProfileAction);
354 menu->addSeparator();
Yingdi Yu233a9722014-03-07 15:47:09 -0800355 menu->addAction(m_contactListAction);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800356 menu->addAction(m_addContactAction);
357 menu->addSeparator();
358 {
359 ChatActionList::const_iterator it = m_chatActionList.begin();
360 ChatActionList::const_iterator end = m_chatActionList.end();
361 if(it != end)
362 {
363 for(; it != end; it++)
364 menu->addAction(it->second);
365 menu->addSeparator();
366 }
367 }
368 menu->addAction(m_updateLocalPrefixAction);
369 menu->addSeparator();
370 menu->addAction(m_minimizeAction);
371 closeMenu = menu->addMenu("Close chatroom");
372 {
373 ChatActionList::const_iterator it = m_closeActionList.begin();
374 ChatActionList::const_iterator end = m_closeActionList.end();
375 if(it == end)
376 {
377 closeMenu->setEnabled(false);
378 }
379 else
380 {
381 for(; it != end; it++)
382 closeMenu->addAction(it->second);
383 }
384 }
385 menu->addSeparator();
386 menu->addAction(m_quitAction);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700387
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800388 m_trayIcon->setContextMenu(menu);
389 delete m_trayIconMenu;
390 m_trayIconMenu = menu;
391 m_closeMenu = closeMenu;
392}
393
394void
395Controller::onLocalPrefix(const Interest& interest, Data& data)
396{
397 QString localPrefixStr = QString::fromUtf8
398 (reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size())
399 .trimmed();
400
401 Name localPrefix(localPrefixStr.toStdString());
402 if(m_localPrefix.empty() || m_localPrefix != localPrefix)
403 emit localPrefixUpdated(localPrefixStr);
404}
405
406void
407Controller::onLocalPrefixTimeout(const Interest& interest)
408{
409 QString localPrefixStr("/private/local");
410
411 Name localPrefix(localPrefixStr.toStdString());
412 if(m_localPrefix.empty() || m_localPrefix != localPrefix)
413 emit localPrefixUpdated(localPrefixStr);
414}
415
416void
Yingdi Yu233a9722014-03-07 15:47:09 -0800417Controller::onInvitationInterestWrapper(const Name& prefix, const Interest& interest, size_t routingPrefixOffset)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800418{
Yingdi Yu233a9722014-03-07 15:47:09 -0800419 emit invitationInterest(prefix, interest, routingPrefixOffset);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800420}
421
422void
423Controller::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
424{
Yingdi Yu233a9722014-03-07 15:47:09 -0800425 _LOG_DEBUG("Controller::onInvitationRegisterFailed: " << failInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800426}
427
428void
429Controller::onInvitationValidated(const shared_ptr<const Interest>& interest)
430{
431 Invitation invitation(interest->getName());
432 std::string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri(); // Should be obtained via a method of ContactManager.
433
434 m_invitationDialog->setInvitation(alias, invitation.getChatroom(), interest->getName());
435 m_invitationDialog->show();
436}
437
438void
439Controller::onInvitationValidationFailed(const shared_ptr<const Interest>& interest, std::string failureInfo)
440{
Yingdi Yu233a9722014-03-07 15:47:09 -0800441 _LOG_DEBUG("Invitation: " << interest->getName() << " cannot not be validated due to: " << failureInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800442}
443
444std::string
445Controller::getRandomString()
446{
447 uint32_t r = random::generateWord32();
448 std::stringstream ss;
449 {
450 using namespace CryptoPP;
451 StringSource(reinterpret_cast<uint8_t*>(&r), 4, true,
452 new HexEncoder(new FileSink(ss), false));
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700453
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800454 }
455 // for(int i = 0; i < 8; i++)
456 // {
457 // uint32_t t = r & mask;
458 // if(t < 10)
459 // ss << static_cast<char>(t + 0x30);
460 // else
461 // ss << static_cast<char>(t + 0x57);
462 // r = r >> 4;
463 // }
464
465 return ss.str();
466}
467
468ndn::Name
469Controller::getInvitationRoutingPrefix()
470{
471 return Name("/ndn/broadcast");
472}
473
474void
475Controller::addChatDialog(const QString& chatroomName, ChatDialog* chatDialog)
476{
477 m_chatDialogList[chatroomName.toStdString()] = chatDialog;
478 connect(chatDialog, SIGNAL(closeChatDialog(const QString&)),
479 this, SLOT(onRemoveChatDialog(const QString&)));
480 connect(chatDialog, SIGNAL(showChatMessage(const QString&, const QString&, const QString&)),
481 this, SLOT(onShowChatMessage(const QString&, const QString&, const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800482 connect(chatDialog, SIGNAL(resetIcon()),
483 this, SLOT(onResetIcon()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800484 connect(this, SIGNAL(localPrefixUpdated(const QString&)),
485 chatDialog, SLOT(onLocalPrefixUpdated(const QString&)));
486
487 QAction* chatAction = new QAction(chatroomName, this);
488 m_chatActionList[chatroomName.toStdString()] = chatAction;
Yingdi Yu46ebfc42014-03-10 15:50:27 -0700489 connect(chatAction, SIGNAL(triggered()), chatDialog, SLOT(raise()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800490
491 QAction* closeAction = new QAction(chatroomName, this);
492 m_closeActionList[chatroomName.toStdString()] = closeAction;
493 connect(closeAction, SIGNAL(triggered()), chatDialog, SLOT(onClose()));
494
495 updateMenu();
496}
497
498// private slots:
499void
500Controller::onIdentityUpdated(const QString& identity)
501{
502 Name identityName(identity.toStdString());
503
504 while(!m_chatDialogList.empty())
505 {
506 ChatDialogList::const_iterator it = m_chatDialogList.begin();
507 onRemoveChatDialog(QString::fromStdString(it->first));
508 }
509
510 m_identity = identityName;
511 m_keyChain.createIdentity(m_identity);
Yingdi Yu233a9722014-03-07 15:47:09 -0800512 setInvitationListener();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800513
514 emit closeDBModule();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700515
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800516 QTimer::singleShot(500, this, SLOT(onIdentityUpdatedContinued()));
517
518}
519
520void
521Controller::onIdentityUpdatedContinued()
522{
523 QString connection = m_db.connectionName();
524 // _LOG_DEBUG("connection name: " << connection.toStdString());
525 QSqlDatabase::removeDatabase(connection);
526 m_db.close();
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700527
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800528 openDB();
529
530 emit identityUpdated(QString(m_identity.toUri().c_str()));
531}
532
533void
534Controller::onContactIdListReady(const QStringList& list)
535{
536 ContactList contactList;
537
538 m_contactManager.getContactList(contactList);
539 m_validator.cleanTrustAnchor();
540
541 ContactList::const_iterator it = contactList.begin();
542 ContactList::const_iterator end = contactList.end();
543
544 for(; it != end; it++)
545 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{
564 std::string chatroom = "chatroom-" + getRandomString();
565
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();
632 for(; it != end; it++)
633 it->second->hide();
634}
635
636void
637Controller::onQuitAction()
638{
639 while(!m_chatDialogList.empty())
640 {
641 ChatDialogList::const_iterator it = m_chatDialogList.begin();
642 onRemoveChatDialog(QString::fromStdString(it->first));
643 }
644
Yingdi Yu233a9722014-03-07 15:47:09 -0800645 if(m_invitationListenerId != 0)
646 m_face->unsetInterestFilter(m_invitationListenerId);
647
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800648 delete m_settingDialog;
649 delete m_startChatDialog;
650 delete m_profileEditor;
651 delete m_invitationDialog;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700652 delete m_browseContactDialog;
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800653 delete m_addContactPanel;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700654
Yingdi Yua7876722014-03-25 14:46:55 -0700655 m_face->ioService()->stop();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800656
657 QApplication::quit();
658}
659
660void
661Controller::onStartChatroom(const QString& chatroomName, bool secured)
662{
663 Name chatroomPrefix;
664 chatroomPrefix.append("ndn")
665 .append("broadcast")
666 .append("ChronoChat")
667 .append(chatroomName.toStdString());
668
669 // check if the chatroom exists
670 if(m_chatDialogList.find(chatroomName.toStdString()) != m_chatDialogList.end())
671 {
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 }
677
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;
693 if(accepted)
694 {
695 Name responseName = invitationName;
696 responseName.append(m_localPrefix.wireEncode());
697
698 response.setName(responseName);
699
700 // We should create a particular certificate for this chatroom, but let's use default one for now.
701 chatroomCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
702
703 response.setContent(chatroomCert->wireEncode());
Yingdi Yua7876722014-03-25 14:46:55 -0700704 response.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800705 }
706 else
707 {
708 response.setName(invitationName);
Yingdi Yua7876722014-03-25 14:46:55 -0700709 response.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800710 }
Yingdi Yu233a9722014-03-07 15:47:09 -0800711 m_keyChain.signByIdentity(response, m_identity);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700712
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800713 // Check if we need a wrapper
714 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
715 if(invitationRoutingPrefix.isPrefixOf(m_identity))
716 {
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800717 m_face->put(response);
718 }
719 else
720 {
721 Name wrappedName;
Yingdi Yu233a9722014-03-07 15:47:09 -0800722 wrappedName.append(invitationRoutingPrefix)
723 .append(ROUTING_PREFIX_SEPARATOR, 2)
724 .append(response.getName());
725
726 _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700727
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800728 Data wrappedData(wrappedName);
729 wrappedData.setContent(response.wireEncode());
Yingdi Yua7876722014-03-25 14:46:55 -0700730 wrappedData.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800731
Yingdi Yu233a9722014-03-07 15:47:09 -0800732 m_keyChain.signByIdentity(wrappedData, m_identity);
733 m_face->put(wrappedData);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800734 }
735
736 // create chatroom
737 if(accepted)
738 {
739 Invitation invitation(invitationName);
740 Name chatroomPrefix;
741 chatroomPrefix.append("ndn")
742 .append("broadcast")
743 .append("ChronoChat")
744 .append(invitation.getChatroom());
745
746 //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.
747 shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
748 ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, true);
Yingdi Yu233a9722014-03-07 15:47:09 -0800749 chatDialog->addSyncAnchor(invitation);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800750
751 addChatDialog(QString::fromStdString(invitation.getChatroom()), chatDialog);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800752 chatDialog->show();
753 }
754}
755
756void
757Controller::onShowChatMessage(const QString& chatroomName, const QString& from, const QString& data)
758{
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700759 m_trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(chatroomName),
760 QString("<%1>: %2").arg(from).arg(data),
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800761 QSystemTrayIcon::Information, 20000);
762 m_trayIcon->setIcon(QIcon(":/images/note.png"));
763}
764
765void
Yingdi Yu233a9722014-03-07 15:47:09 -0800766Controller::onResetIcon()
767{
768 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
769}
770
771void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800772Controller::onRemoveChatDialog(const QString& chatroomName)
773{
774 ChatDialogList::iterator it = m_chatDialogList.find(chatroomName.toStdString());
775
776 if(it != m_chatDialogList.end())
777 {
778 ChatDialog* deletedChat = it->second;
779 if(deletedChat)
780 delete deletedChat;
781 m_chatDialogList.erase(it);
782
783 QAction* chatAction = m_chatActionList[chatroomName.toStdString()];
784 QAction* closeAction = m_closeActionList[chatroomName.toStdString()];
785 if(chatAction)
786 delete chatAction;
787 if(closeAction)
788 delete closeAction;
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700789
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800790 m_chatActionList.erase(chatroomName.toStdString());
791 m_closeActionList.erase(chatroomName.toStdString());
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700792
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800793 updateMenu();
794 }
795}
796
797void
798Controller::onWarning(const QString& msg)
799{
800 QMessageBox::information(this, tr("ChronoChat"), msg);
801}
802
803void
Yingdi Yufa0b6a02014-04-30 14:26:42 -0700804Controller::onError(const QString& msg)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800805{
806 QMessageBox::critical(this, tr("ChronoChat"), msg, QMessageBox::Ok);
807 exit(1);
808}
809
Yingdi Yu233a9722014-03-07 15:47:09 -0800810void
811Controller::onInvitationInterest(const Name& prefix, const Interest& interest, size_t routingPrefixOffset)
812{
813 _LOG_DEBUG("onInvitationInterest: " << interest.getName());
814 shared_ptr<Interest> invitationInterest = make_shared<Interest>(boost::cref(interest.getName().getSubName(routingPrefixOffset)));
815
816 // check if the chatroom already exists;
817 try
818 {
819 Invitation invitation(invitationInterest->getName());
820 if(m_chatDialogList.find(invitation.getChatroom()) != m_chatDialogList.end())
821 return;
822 }
823 catch(Invitation::Error& e)
824 {
825 // Cannot parse the invitation;
826 return;
827 }
828
829 OnInterestValidated onValidated = bind(&Controller::onInvitationValidated, this, _1);
830 OnInterestValidationFailed onValidationFailed = bind(&Controller::onInvitationValidationFailed, this, _1, _2);
831 m_validator.validate(*invitationInterest, onValidated, onValidationFailed);
832}
833
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800834} // namespace chronos
835
836#if WAF
837#include "controller.moc"
838#include "controller.cpp.moc"
839#endif