blob: 4d806045dde633befec38dca5cf0dd6ac205ff77 [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>
20#include <ndn-cpp-dev/util/random.hpp>
21#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
42static const uint8_t ROUTING_PREFIX_SEPARATOR[2] = {0xF0, 0x2E};
43
44// constructor & destructor
45Controller::Controller(shared_ptr<Face> face,
46 QWidget* parent)
47 : QDialog(parent)
48 , m_face(face)
49 , m_invitationListenerId(0)
50 , m_contactManager(m_face)
51 , m_settingDialog(new SettingDialog)
52 , m_startChatDialog(new StartChatDialog)
53 , m_profileEditor(new ProfileEditor)
54 , m_invitationDialog(new InvitationDialog)
55 , m_contactPanel(new ContactPanel)
56 , m_browseContactDialog(new BrowseContactDialog)
57 , m_addContactPanel(new AddContactPanel)
58{
59 qRegisterMetaType<ndn::Name>("ndn.Name");
60 qRegisterMetaType<ndn::IdentityCertificate>("ndn.IdentityCertificate");
61 qRegisterMetaType<chronos::EndorseInfo>("chronos.EndorseInfo");
Yingdi Yu233a9722014-03-07 15:47:09 -080062 qRegisterMetaType<ndn::Interest>("ndn.Interest");
63 qRegisterMetaType<size_t>("size_t");
Yingdi Yu348f5ea2014-03-01 14:47:25 -080064
65 connect(this, SIGNAL(localPrefixUpdated(const QString&)),
66 this, SLOT(onLocalPrefixUpdated(const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -080067 connect(this, SIGNAL(invitationInterest(const ndn::Name&, const ndn::Interest&, size_t)),
68 this, SLOT(onInvitationInterest(const ndn::Name&, const ndn::Interest&, size_t)));
Yingdi Yu348f5ea2014-03-01 14:47:25 -080069
70 // Connection to ContactManager
71 connect(this, SIGNAL(identityUpdated(const QString&)),
72 &m_contactManager, SLOT(onIdentityUpdated(const QString&)));
73 connect(&m_contactManager, SIGNAL(warning(const QString&)),
74 this, SLOT(onWarning(const QString&)));
75 connect(this, SIGNAL(refreshBrowseContact()),
76 &m_contactManager, SLOT(onRefreshBrowseContact()));
77 connect(&m_contactManager, SIGNAL(contactInfoFetchFailed(const QString&)),
78 this, SLOT(onWarning(const QString&)));
79 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
80 this, SLOT(onContactIdListReady(const QStringList&)));
81
82 // Connection to SettingDialog
83 connect(this, SIGNAL(identityUpdated(const QString&)),
84 m_settingDialog, SLOT(onIdentityUpdated(const QString&)));
85 connect(m_settingDialog, SIGNAL(identityUpdated(const QString&)),
86 this, SLOT(onIdentityUpdated(const QString&)));
87 connect(m_settingDialog, SIGNAL(nickUpdated(const QString&)),
88 this, SLOT(onNickUpdated(const QString&)));
89
90 // Connection to ProfileEditor
91 connect(this, SIGNAL(closeDBModule()),
92 m_profileEditor, SLOT(onCloseDBModule()));
93 connect(this, SIGNAL(identityUpdated(const QString&)),
94 m_profileEditor, SLOT(onIdentityUpdated(const QString&)));
95 connect(m_profileEditor, SIGNAL(updateProfile()),
96 &m_contactManager, SLOT(onUpdateProfile()));
97
98 // Connection to StartChatDialog
99 connect(m_startChatDialog, SIGNAL(startChatroom(const QString&, bool)),
100 this, SLOT(onStartChatroom(const QString&, bool)));
101
102 // Connection to InvitationDialog
103 connect(m_invitationDialog, SIGNAL(invitationResponded(const ndn::Name&, bool)),
104 this, SLOT(onInvitationResponded(const ndn::Name&, bool)));
105
106 // Connection to AddContactPanel
107 connect(m_addContactPanel, SIGNAL(fetchInfo(const QString&)),
108 &m_contactManager, SLOT(onFetchContactInfo(const QString&)));
109 connect(m_addContactPanel, SIGNAL(addContact(const QString&)),
110 &m_contactManager, SLOT(onAddFetchedContact(const QString&)));
111 connect(&m_contactManager, SIGNAL(contactEndorseInfoReady(const chronos::EndorseInfo&)),
112 m_addContactPanel, SLOT(onContactEndorseInfoReady(const chronos::EndorseInfo&)));
113
114
115 // Connection to BrowseContactDialog
116 connect(m_browseContactDialog, SIGNAL(directAddClicked()),
117 this, SLOT(onDirectAdd()));
118 connect(m_browseContactDialog, SIGNAL(fetchIdCert(const QString&)),
119 &m_contactManager, SLOT(onFetchIdCert(const QString&)));
120 connect(m_browseContactDialog, SIGNAL(addContact(const QString&)),
121 &m_contactManager, SLOT(onAddFetchedContactIdCert(const QString&)));
122 connect(&m_contactManager, SIGNAL(idCertNameListReady(const QStringList&)),
123 m_browseContactDialog, SLOT(onIdCertNameListReady(const QStringList&)));
124 connect(&m_contactManager, SIGNAL(nameListReady(const QStringList&)),
125 m_browseContactDialog, SLOT(onNameListReady(const QStringList&)));
126 connect(&m_contactManager, SIGNAL(idCertReady(const ndn::IdentityCertificate&)),
127 m_browseContactDialog, SLOT(onIdCertReady(const ndn::IdentityCertificate&)));
128
129 // Connection to ContactPanel
130 connect(m_contactPanel, SIGNAL(waitForContactList()),
131 &m_contactManager, SLOT(onWaitForContactList()));
132 connect(m_contactPanel, SIGNAL(waitForContactInfo(const QString&)),
133 &m_contactManager, SLOT(onWaitForContactInfo(const QString&)));
134 connect(m_contactPanel, SIGNAL(removeContact(const QString&)),
135 &m_contactManager, SLOT(onRemoveContact(const QString&)));
136 connect(m_contactPanel, SIGNAL(updateAlias(const QString&, const QString&)),
137 &m_contactManager, SLOT(onUpdateAlias(const QString&, const QString&)));
138 connect(m_contactPanel, SIGNAL(updateIsIntroducer(const QString&, bool)),
139 &m_contactManager, SLOT(onUpdateIsIntroducer(const QString&, bool)));
140 connect(m_contactPanel, SIGNAL(updateEndorseCertificate(const QString&)),
141 &m_contactManager, SLOT(onUpdateEndorseCertificate(const QString&)));
142 connect(m_contactPanel, SIGNAL(warning(const QString&)),
143 this, SLOT(onWarning(const QString&)));
144 connect(this, SIGNAL(closeDBModule()),
145 m_contactPanel, SLOT(onCloseDBModule()));
146 connect(this, SIGNAL(identityUpdated(const QString&)),
147 m_contactPanel, SLOT(onIdentityUpdated(const QString&)));
148 connect(&m_contactManager, SIGNAL(contactAliasListReady(const QStringList&)),
149 m_contactPanel, SLOT(onContactAliasListReady(const QStringList&)));
150 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
151 m_contactPanel, SLOT(onContactIdListReady(const QStringList&)));
152 connect(&m_contactManager, SIGNAL(contactInfoReady(const QString&, const QString&, const QString&, bool)),
153 m_contactPanel, SLOT(onContactInfoReady(const QString&, const QString&, const QString&, bool)));
154
155 initialize();
156
Yingdi Yu233a9722014-03-07 15:47:09 -0800157 createTrayIcon();
158
159 onUpdateLocalPrefixAction();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800160}
161
162Controller::~Controller()
163{
164 saveConf();
165}
166
167// public methods
168
169
170// private methods
171std::string
172Controller::getDBName()
173{
174 std::string dbName("chronos-");
175
176 std::stringstream ss;
177 {
178 using namespace CryptoPP;
179
180 SHA256 hash;
181 StringSource(m_identity.wireEncode().wire(), m_identity.wireEncode().size(), true,
182 new HashFilter(hash, new HexEncoder(new FileSink(ss), false)));
183 }
184 dbName.append(ss.str()).append(".db");
185
186 return dbName;
187}
188
189void
190Controller::openDB()
191{
192 m_db = QSqlDatabase::addDatabase("QSQLITE");
193 QString path = (QDir::home().path());
194 path.append(QDir::separator()).append(".chronos").append(QDir::separator()).append(getDBName().c_str());
195 m_db.setDatabaseName(path);
196 bool ok = m_db.open();
197
198 _LOG_DEBUG("DB opened: " << std::boolalpha << ok );
199}
200
201void
202Controller::initialize()
203{
204 loadConf();
205
206 m_keyChain.createIdentity(m_identity);
207
208 openDB();
209
210 emit identityUpdated(QString(m_identity.toUri().c_str()));
211
212 setInvitationListener();
213}
214
215void
216Controller::setInvitationListener()
217{
218 if(m_invitationListenerId != 0)
219 m_face->unsetInterestFilter(m_invitationListenerId);
220
221 Name invitationPrefix;
222 Name routingPrefix = getInvitationRoutingPrefix();
223 size_t offset = 0;
224 if(!routingPrefix.isPrefixOf(m_identity))
225 {
226 invitationPrefix.append(routingPrefix).append(ROUTING_PREFIX_SEPARATOR, 2);
227 offset = routingPrefix.size() + 1;
228 }
229 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
230
231 m_invitationListenerId = m_face->setInterestFilter(invitationPrefix,
Yingdi Yu233a9722014-03-07 15:47:09 -0800232 bind(&Controller::onInvitationInterestWrapper, this, _1, _2, offset),
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800233 bind(&Controller::onInvitationRegisterFailed, this, _1, _2));
234}
235
236void
237Controller::loadConf()
238{
239 namespace fs = boost::filesystem;
240
241 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
242 fs::create_directories (chronosDir);
243
244 std::ifstream is((chronosDir / "config").c_str ());
245 ChronoChat::Conf conf;
246 if(conf.ParseFromIstream(&is))
247 {
248 m_identity.clear();
249 m_identity.append(conf.identity());
250 if(conf.has_nick())
251 m_nick = conf.nick();
252 else
253 m_nick = m_identity.get(-1).toUri();
254 }
255 else
256 {
257 m_identity.clear();
258 // TODO: change below to system default;
259 m_identity.append("chronochat-tmp-identity")
Yingdi Yua7876722014-03-25 14:46:55 -0700260 .append(getRandomString());
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800261
262 m_nick = m_identity.get(-1).toUri();
263 }
264}
265
266void
267Controller::saveConf()
268{
269 namespace fs = boost::filesystem;
270
271 fs::path chronosDir = fs::path(getenv("HOME")) / ".chronos";
272 fs::create_directories (chronosDir);
273
274 std::ofstream os((chronosDir / "config").c_str ());
275 ChronoChat::Conf conf;
276 conf.set_identity(m_identity.toUri());
277 if(!m_nick.empty())
278 conf.set_nick(m_nick);
279 conf.SerializeToOstream(&os);
280
281 os.close();
282}
283
284void
285Controller::createActions()
286{
287 m_startChatroom = new QAction(tr("Start new chat"), this);
288 connect(m_startChatroom, SIGNAL(triggered()), this, SLOT(onStartChatAction()));
289
290 m_settingsAction = new QAction(tr("Settings"), this);
291 connect(m_settingsAction, SIGNAL(triggered()), this, SLOT(onSettingsAction()));
292
293 m_editProfileAction = new QAction(tr("Edit profile"), this);
294 connect(m_editProfileAction, SIGNAL(triggered()), this, SLOT(onProfileEditorAction()));
295
296 m_contactListAction = new QAction(tr("Contact List"), this);
297 connect(m_contactListAction, SIGNAL(triggered()), this, SLOT(onContactListAction()));
298
299 m_addContactAction = new QAction(tr("Add contact"), this);
300 connect(m_addContactAction, SIGNAL(triggered()), this, SLOT(onAddContactAction()));
301
302 m_updateLocalPrefixAction = new QAction(tr("Update local prefix"), this);
303 connect(m_updateLocalPrefixAction, SIGNAL(triggered()), this, SLOT(onUpdateLocalPrefixAction()));
304
305 m_minimizeAction = new QAction(tr("Mi&nimize"), this);
306 connect(m_minimizeAction, SIGNAL(triggered()), this, SLOT(onMinimizeAction()));
307
308 m_quitAction = new QAction(tr("Quit"), this);
309 connect(m_quitAction, SIGNAL(triggered()), this, SLOT(onQuitAction()));
310}
311
312void
313Controller::createTrayIcon()
314{
315 createActions();
316
317 m_trayIconMenu = new QMenu(this);
318 m_trayIconMenu->addAction(m_startChatroom);
319 m_trayIconMenu->addSeparator();
320 m_trayIconMenu->addAction(m_settingsAction);
321 m_trayIconMenu->addAction(m_editProfileAction);
322 m_trayIconMenu->addSeparator();
323 m_trayIconMenu->addAction(m_contactListAction);
324 m_trayIconMenu->addAction(m_addContactAction);
325 m_trayIconMenu->addSeparator();
326 m_trayIconMenu->addAction(m_updateLocalPrefixAction);
327 m_trayIconMenu->addSeparator();
328 m_trayIconMenu->addAction(m_minimizeAction);
329 m_closeMenu = m_trayIconMenu->addMenu("Close chatroom");
330 m_closeMenu->setEnabled(false);
331 m_trayIconMenu->addSeparator();
332 m_trayIconMenu->addAction(m_quitAction);
333
334 m_trayIcon = new QSystemTrayIcon(this);
335 m_trayIcon->setContextMenu(m_trayIconMenu);
336
337 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
338 m_trayIcon->setToolTip("ChronoChat System Tray Icon");
339 m_trayIcon->setVisible(true);
340}
341
342void
343Controller::updateMenu()
344{
345 QMenu* menu = new QMenu(this);
346 QMenu* closeMenu = 0;
347
348 menu->addAction(m_startChatroom);
349 menu->addSeparator();
350 menu->addAction(m_settingsAction);
351 menu->addAction(m_editProfileAction);
352 menu->addSeparator();
Yingdi Yu233a9722014-03-07 15:47:09 -0800353 menu->addAction(m_contactListAction);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800354 menu->addAction(m_addContactAction);
355 menu->addSeparator();
356 {
357 ChatActionList::const_iterator it = m_chatActionList.begin();
358 ChatActionList::const_iterator end = m_chatActionList.end();
359 if(it != end)
360 {
361 for(; it != end; it++)
362 menu->addAction(it->second);
363 menu->addSeparator();
364 }
365 }
366 menu->addAction(m_updateLocalPrefixAction);
367 menu->addSeparator();
368 menu->addAction(m_minimizeAction);
369 closeMenu = menu->addMenu("Close chatroom");
370 {
371 ChatActionList::const_iterator it = m_closeActionList.begin();
372 ChatActionList::const_iterator end = m_closeActionList.end();
373 if(it == end)
374 {
375 closeMenu->setEnabled(false);
376 }
377 else
378 {
379 for(; it != end; it++)
380 closeMenu->addAction(it->second);
381 }
382 }
383 menu->addSeparator();
384 menu->addAction(m_quitAction);
385
386 m_trayIcon->setContextMenu(menu);
387 delete m_trayIconMenu;
388 m_trayIconMenu = menu;
389 m_closeMenu = closeMenu;
390}
391
392void
393Controller::onLocalPrefix(const Interest& interest, Data& data)
394{
395 QString localPrefixStr = QString::fromUtf8
396 (reinterpret_cast<const char*>(data.getContent().value()), data.getContent().value_size())
397 .trimmed();
398
399 Name localPrefix(localPrefixStr.toStdString());
400 if(m_localPrefix.empty() || m_localPrefix != localPrefix)
401 emit localPrefixUpdated(localPrefixStr);
402}
403
404void
405Controller::onLocalPrefixTimeout(const Interest& interest)
406{
407 QString localPrefixStr("/private/local");
408
409 Name localPrefix(localPrefixStr.toStdString());
410 if(m_localPrefix.empty() || m_localPrefix != localPrefix)
411 emit localPrefixUpdated(localPrefixStr);
412}
413
414void
Yingdi Yu233a9722014-03-07 15:47:09 -0800415Controller::onInvitationInterestWrapper(const Name& prefix, const Interest& interest, size_t routingPrefixOffset)
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800416{
Yingdi Yu233a9722014-03-07 15:47:09 -0800417 emit invitationInterest(prefix, interest, routingPrefixOffset);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800418}
419
420void
421Controller::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
422{
Yingdi Yu233a9722014-03-07 15:47:09 -0800423 _LOG_DEBUG("Controller::onInvitationRegisterFailed: " << failInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800424}
425
426void
427Controller::onInvitationValidated(const shared_ptr<const Interest>& interest)
428{
429 Invitation invitation(interest->getName());
430 std::string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri(); // Should be obtained via a method of ContactManager.
431
432 m_invitationDialog->setInvitation(alias, invitation.getChatroom(), interest->getName());
433 m_invitationDialog->show();
434}
435
436void
437Controller::onInvitationValidationFailed(const shared_ptr<const Interest>& interest, std::string failureInfo)
438{
Yingdi Yu233a9722014-03-07 15:47:09 -0800439 _LOG_DEBUG("Invitation: " << interest->getName() << " cannot not be validated due to: " << failureInfo);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800440}
441
442std::string
443Controller::getRandomString()
444{
445 uint32_t r = random::generateWord32();
446 std::stringstream ss;
447 {
448 using namespace CryptoPP;
449 StringSource(reinterpret_cast<uint8_t*>(&r), 4, true,
450 new HexEncoder(new FileSink(ss), false));
451
452 }
453 // for(int i = 0; i < 8; i++)
454 // {
455 // uint32_t t = r & mask;
456 // if(t < 10)
457 // ss << static_cast<char>(t + 0x30);
458 // else
459 // ss << static_cast<char>(t + 0x57);
460 // r = r >> 4;
461 // }
462
463 return ss.str();
464}
465
466ndn::Name
467Controller::getInvitationRoutingPrefix()
468{
469 return Name("/ndn/broadcast");
470}
471
472void
473Controller::addChatDialog(const QString& chatroomName, ChatDialog* chatDialog)
474{
475 m_chatDialogList[chatroomName.toStdString()] = chatDialog;
476 connect(chatDialog, SIGNAL(closeChatDialog(const QString&)),
477 this, SLOT(onRemoveChatDialog(const QString&)));
478 connect(chatDialog, SIGNAL(showChatMessage(const QString&, const QString&, const QString&)),
479 this, SLOT(onShowChatMessage(const QString&, const QString&, const QString&)));
Yingdi Yu233a9722014-03-07 15:47:09 -0800480 connect(chatDialog, SIGNAL(resetIcon()),
481 this, SLOT(onResetIcon()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800482 connect(this, SIGNAL(localPrefixUpdated(const QString&)),
483 chatDialog, SLOT(onLocalPrefixUpdated(const QString&)));
484
485 QAction* chatAction = new QAction(chatroomName, this);
486 m_chatActionList[chatroomName.toStdString()] = chatAction;
Yingdi Yu46ebfc42014-03-10 15:50:27 -0700487 connect(chatAction, SIGNAL(triggered()), chatDialog, SLOT(raise()));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800488
489 QAction* closeAction = new QAction(chatroomName, this);
490 m_closeActionList[chatroomName.toStdString()] = closeAction;
491 connect(closeAction, SIGNAL(triggered()), chatDialog, SLOT(onClose()));
492
493 updateMenu();
494}
495
496// private slots:
497void
498Controller::onIdentityUpdated(const QString& identity)
499{
500 Name identityName(identity.toStdString());
501
502 while(!m_chatDialogList.empty())
503 {
504 ChatDialogList::const_iterator it = m_chatDialogList.begin();
505 onRemoveChatDialog(QString::fromStdString(it->first));
506 }
507
508 m_identity = identityName;
509 m_keyChain.createIdentity(m_identity);
Yingdi Yu233a9722014-03-07 15:47:09 -0800510 setInvitationListener();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800511
512 emit closeDBModule();
513
514 QTimer::singleShot(500, this, SLOT(onIdentityUpdatedContinued()));
515
516}
517
518void
519Controller::onIdentityUpdatedContinued()
520{
521 QString connection = m_db.connectionName();
522 // _LOG_DEBUG("connection name: " << connection.toStdString());
523 QSqlDatabase::removeDatabase(connection);
524 m_db.close();
525
526 openDB();
527
528 emit identityUpdated(QString(m_identity.toUri().c_str()));
529}
530
531void
532Controller::onContactIdListReady(const QStringList& list)
533{
534 ContactList contactList;
535
536 m_contactManager.getContactList(contactList);
537 m_validator.cleanTrustAnchor();
538
539 ContactList::const_iterator it = contactList.begin();
540 ContactList::const_iterator end = contactList.end();
541
542 for(; it != end; it++)
543 m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
544
545}
546
547void
548Controller::onNickUpdated(const QString& nick)
549{
550 m_nick = nick.toStdString();
551}
552
553void
554Controller::onLocalPrefixUpdated(const QString& localPrefix)
555{
556 m_localPrefix = Name(localPrefix.toStdString());
557}
558
559void
560Controller::onStartChatAction()
561{
562 std::string chatroom = "chatroom-" + getRandomString();
563
564 m_startChatDialog->setChatroom(chatroom);
565 m_startChatDialog->show();
566 m_startChatDialog->raise();
567}
568
569void
570Controller::onSettingsAction()
571{
572 m_settingDialog->setNick(QString(m_nick.c_str()));
573 m_settingDialog->show();
574 m_settingDialog->raise();
575}
576
577void
578Controller::onProfileEditorAction()
579{
580 m_profileEditor->show();
581 m_profileEditor->raise();
582}
583
584void
585Controller::onAddContactAction()
586{
587 emit refreshBrowseContact();
588 m_browseContactDialog->show();
589 m_browseContactDialog->raise();
590}
591
592void
593Controller::onContactListAction()
594{
595 m_contactPanel->show();
596 m_contactPanel->raise();
597}
598
599void
600Controller::onDirectAdd()
601{
602 m_addContactPanel->show();
603 m_addContactPanel->raise();
604}
605
606void
607Controller::onUpdateLocalPrefixAction()
608{
609 // Name interestName();
610 Interest interest("/local/ndn/prefix");
Yingdi Yua7876722014-03-25 14:46:55 -0700611 interest.setInterestLifetime(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800612 interest.setMustBeFresh(true);
613
614 m_face->expressInterest(interest,
615 bind(&Controller::onLocalPrefix, this, _1, _2),
616 bind(&Controller::onLocalPrefixTimeout, this, _1));
617}
618
619void
620Controller::onMinimizeAction()
621{
622 m_settingDialog->hide();
623 m_startChatDialog->hide();
624 m_profileEditor->hide();
625 m_invitationDialog->hide();
626 m_addContactPanel->hide();
627
628 ChatDialogList::iterator it = m_chatDialogList.begin();
629 ChatDialogList::iterator end = m_chatDialogList.end();
630 for(; it != end; it++)
631 it->second->hide();
632}
633
634void
635Controller::onQuitAction()
636{
637 while(!m_chatDialogList.empty())
638 {
639 ChatDialogList::const_iterator it = m_chatDialogList.begin();
640 onRemoveChatDialog(QString::fromStdString(it->first));
641 }
642
Yingdi Yu233a9722014-03-07 15:47:09 -0800643 if(m_invitationListenerId != 0)
644 m_face->unsetInterestFilter(m_invitationListenerId);
645
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800646 delete m_settingDialog;
647 delete m_startChatDialog;
648 delete m_profileEditor;
649 delete m_invitationDialog;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700650 delete m_browseContactDialog;
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800651 delete m_addContactPanel;
Yingdi Yuf4aaa8b2014-03-10 11:24:31 -0700652
Yingdi Yua7876722014-03-25 14:46:55 -0700653 m_face->ioService()->stop();
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800654
655 QApplication::quit();
656}
657
658void
659Controller::onStartChatroom(const QString& chatroomName, bool secured)
660{
661 Name chatroomPrefix;
662 chatroomPrefix.append("ndn")
663 .append("broadcast")
664 .append("ChronoChat")
665 .append(chatroomName.toStdString());
666
667 // check if the chatroom exists
668 if(m_chatDialogList.find(chatroomName.toStdString()) != m_chatDialogList.end())
669 {
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 }
675
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
680 addChatDialog(chatroomName, chatDialog);
681 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;
691 if(accepted)
692 {
693 Name responseName = invitationName;
694 responseName.append(m_localPrefix.wireEncode());
695
696 response.setName(responseName);
697
698 // We should create a particular certificate for this chatroom, but let's use default one for now.
699 chatroomCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
700
701 response.setContent(chatroomCert->wireEncode());
Yingdi Yua7876722014-03-25 14:46:55 -0700702 response.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800703 }
704 else
705 {
706 response.setName(invitationName);
Yingdi Yua7876722014-03-25 14:46:55 -0700707 response.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800708 }
Yingdi Yu233a9722014-03-07 15:47:09 -0800709 m_keyChain.signByIdentity(response, m_identity);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800710
711 // Check if we need a wrapper
712 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
713 if(invitationRoutingPrefix.isPrefixOf(m_identity))
714 {
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800715 m_face->put(response);
716 }
717 else
718 {
719 Name wrappedName;
Yingdi Yu233a9722014-03-07 15:47:09 -0800720 wrappedName.append(invitationRoutingPrefix)
721 .append(ROUTING_PREFIX_SEPARATOR, 2)
722 .append(response.getName());
723
724 _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800725
726 Data wrappedData(wrappedName);
727 wrappedData.setContent(response.wireEncode());
Yingdi Yua7876722014-03-25 14:46:55 -0700728 wrappedData.setFreshnessPeriod(time::milliseconds(1000));
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800729
Yingdi Yu233a9722014-03-07 15:47:09 -0800730 m_keyChain.signByIdentity(wrappedData, m_identity);
731 m_face->put(wrappedData);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800732 }
733
734 // create chatroom
735 if(accepted)
736 {
737 Invitation invitation(invitationName);
738 Name chatroomPrefix;
739 chatroomPrefix.append("ndn")
740 .append("broadcast")
741 .append("ChronoChat")
742 .append(invitation.getChatroom());
743
744 //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.
745 shared_ptr<IdentityCertificate> idCert = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
746 ChatDialog* chatDialog = new ChatDialog(&m_contactManager, m_face, *idCert, chatroomPrefix, m_localPrefix, m_nick, true);
Yingdi Yu233a9722014-03-07 15:47:09 -0800747 chatDialog->addSyncAnchor(invitation);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800748
749 addChatDialog(QString::fromStdString(invitation.getChatroom()), chatDialog);
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800750 chatDialog->show();
751 }
752}
753
754void
755Controller::onShowChatMessage(const QString& chatroomName, const QString& from, const QString& data)
756{
757 m_trayIcon->showMessage(QString("Chatroom %1 has a new message").arg(chatroomName),
758 QString("<%1>: %2").arg(from).arg(data),
759 QSystemTrayIcon::Information, 20000);
760 m_trayIcon->setIcon(QIcon(":/images/note.png"));
761}
762
763void
Yingdi Yu233a9722014-03-07 15:47:09 -0800764Controller::onResetIcon()
765{
766 m_trayIcon->setIcon(QIcon(":/images/icon_small.png"));
767}
768
769void
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800770Controller::onRemoveChatDialog(const QString& chatroomName)
771{
772 ChatDialogList::iterator it = m_chatDialogList.find(chatroomName.toStdString());
773
774 if(it != m_chatDialogList.end())
775 {
776 ChatDialog* deletedChat = it->second;
777 if(deletedChat)
778 delete deletedChat;
779 m_chatDialogList.erase(it);
780
781 QAction* chatAction = m_chatActionList[chatroomName.toStdString()];
782 QAction* closeAction = m_closeActionList[chatroomName.toStdString()];
783 if(chatAction)
784 delete chatAction;
785 if(closeAction)
786 delete closeAction;
787
788 m_chatActionList.erase(chatroomName.toStdString());
789 m_closeActionList.erase(chatroomName.toStdString());
790
791 updateMenu();
792 }
793}
794
795void
796Controller::onWarning(const QString& msg)
797{
798 QMessageBox::information(this, tr("ChronoChat"), msg);
799}
800
801void
802Controller::onError(const QString& msg)
803{
804 QMessageBox::critical(this, tr("ChronoChat"), msg, QMessageBox::Ok);
805 exit(1);
806}
807
Yingdi Yu233a9722014-03-07 15:47:09 -0800808void
809Controller::onInvitationInterest(const Name& prefix, const Interest& interest, size_t routingPrefixOffset)
810{
811 _LOG_DEBUG("onInvitationInterest: " << interest.getName());
812 shared_ptr<Interest> invitationInterest = make_shared<Interest>(boost::cref(interest.getName().getSubName(routingPrefixOffset)));
813
814 // check if the chatroom already exists;
815 try
816 {
817 Invitation invitation(invitationInterest->getName());
818 if(m_chatDialogList.find(invitation.getChatroom()) != m_chatDialogList.end())
819 return;
820 }
821 catch(Invitation::Error& e)
822 {
823 // Cannot parse the invitation;
824 return;
825 }
826
827 OnInterestValidated onValidated = bind(&Controller::onInvitationValidated, this, _1);
828 OnInterestValidationFailed onValidationFailed = bind(&Controller::onInvitationValidationFailed, this, _1, _2);
829 m_validator.validate(*invitationInterest, onValidated, onValidationFailed);
830}
831
Yingdi Yu348f5ea2014-03-01 14:47:25 -0800832} // namespace chronos
833
834#if WAF
835#include "controller.moc"
836#include "controller.cpp.moc"
837#endif