blob: 496cd9ae7503ce237c5078aab3839562a7e5e4f6 [file] [log] [blame]
Yingdi Yu2c9e7712014-10-20 11:55:05 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013, Regents of the University of California
4 * Yingdi Yu
5 *
6 * BSD license, See the LICENSE file for more information
7 *
8 * Author: Yingdi Yu <yingdi@cs.ucla.edu>
Yingdi Yuf3401182015-02-02 20:21:07 -08009 * Qiuhan Ding <qiuhanding@cs.ucla.edu>
Yingdi Yu2c9e7712014-10-20 11:55:05 -070010 */
11
12#include "controller-backend.hpp"
13
14#ifndef Q_MOC_RUN
Yingdi Yu7ff31f02015-02-05 11:21:07 -080015#include <ndn-cxx/util/segment-fetcher.hpp>
Yingdi Yu2c9e7712014-10-20 11:55:05 -070016#include "invitation.hpp"
17#include "logging.h"
18#endif
19
20
21INIT_LOGGER("ControllerBackend");
22
Yingdi Yueb692ac2015-02-10 18:46:18 -080023namespace chronochat {
Yingdi Yu2c9e7712014-10-20 11:55:05 -070024
25using std::string;
26
27using ndn::Face;
28using ndn::IdentityCertificate;
29using ndn::OnInterestValidated;
30using ndn::OnInterestValidationFailed;
31
32
Qiuhan Dingba3e57a2015-01-08 19:07:39 -080033static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
34 ndn::name::Component::fromEscapedString("%F0%2E");
35static const int MAXIMUM_REQUEST = 3;
Yingdi Yuf3401182015-02-02 20:21:07 -080036static const int CONNECTION_RETRY_TIMER = 3;
Yingdi Yu2c9e7712014-10-20 11:55:05 -070037
38ControllerBackend::ControllerBackend(QObject* parent)
39 : QThread(parent)
Yingdi Yuf3401182015-02-02 20:21:07 -080040 , m_shouldResume(false)
Yingdi Yu2c9e7712014-10-20 11:55:05 -070041 , m_contactManager(m_face)
42 , m_invitationListenerId(0)
43{
44 // connection to contact manager
45 connect(this, SIGNAL(identityUpdated(const QString&)),
46 &m_contactManager, SLOT(onIdentityUpdated(const QString&)));
47
48 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
49 this, SLOT(onContactIdListReady(const QStringList&)));
50
51}
52
53ControllerBackend::~ControllerBackend()
54{
55}
56
57void
58ControllerBackend::run()
59{
Yingdi Yuf3401182015-02-02 20:21:07 -080060 bool shouldResume = false;
61 do {
62 try {
63 setInvitationListener();
64 m_face.processEvents();
65 }
66 catch (std::runtime_error& e) {
67 {
68 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
69 m_isNfdConnected = false;
70 }
71 emit nfdError();
72 {
73 std::lock_guard<std::mutex>lock(m_resumeMutex);
74 m_shouldResume = true;
75 }
76#ifdef BOOST_THREAD_USES_CHRONO
77 time::seconds reconnectTimer = time::seconds(CONNECTION_RETRY_TIMER);
78#else
79 boost::posix_time::time_duration reconnectTimer;
80 reconnectTimer = boost::posix_time::seconds(CONNECTION_RETRY_TIMER);
81#endif
82 while (!m_isNfdConnected) {
83#ifdef BOOST_THREAD_USES_CHRONO
84 boost::this_thread::sleep_for(reconnectTimer);
85#else
86 boost::this_thread::sleep(reconnectTimer);
87#endif
88 }
89 }
90 {
91 std::lock_guard<std::mutex>lock(m_resumeMutex);
92 shouldResume = m_shouldResume;
93 m_shouldResume = false;
94 }
95 } while (shouldResume);
Yingdi Yu2c9e7712014-10-20 11:55:05 -070096 std::cerr << "Bye!" << std::endl;
97}
98
99// private methods:
100
101void
102tmpOnInvitationInterest(const ndn::Name& prefix,
103 const ndn::Interest& interest)
104{
105 std::cerr << "tmpOnInvitationInterest" << std::endl;
106}
107
108void
109tmpOnInvitationRegisterFailed(const Name& prefix,
110 const string& failInfo)
111{
112 std::cerr << "tmpOnInvitationRegisterFailed" << std::endl;
113}
114
115void
116tmpUnregisterPrefixSuccessCallback()
117{
118 std::cerr << "tmpUnregisterPrefixSuccessCallback" << std::endl;
119}
120
121void
122tmpUnregisterPrefixFailureCallback(const string& failInfo)
123{
124 std::cerr << "tmpUnregisterPrefixSuccessCallback" << std::endl;
125}
126
127void
128ControllerBackend::setInvitationListener()
129{
130 QMutexLocker locker(&m_mutex);
131
132 Name invitationPrefix;
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800133 Name requestPrefix;
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700134 Name routingPrefix = getInvitationRoutingPrefix();
135 size_t offset = 0;
136 if (!routingPrefix.isPrefixOf(m_identity)) {
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800137 invitationPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
138 requestPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700139 offset = routingPrefix.size() + 1;
140 }
141 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800142 requestPrefix.append(m_identity).append("CHRONOCHAT-INVITATION-REQUEST");
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700143
144 const ndn::RegisteredPrefixId* invitationListenerId =
145 m_face.setInterestFilter(invitationPrefix,
146 bind(&ControllerBackend::onInvitationInterest,
147 this, _1, _2, offset),
148 bind(&ControllerBackend::onInvitationRegisterFailed,
149 this, _1, _2));
150
151 if (m_invitationListenerId != 0) {
152 m_face.unregisterPrefix(m_invitationListenerId,
153 bind(&ControllerBackend::onInvitationPrefixReset, this),
154 bind(&ControllerBackend::onInvitationPrefixResetFailed, this, _1));
155 }
156
157 m_invitationListenerId = invitationListenerId;
158
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800159 const ndn::RegisteredPrefixId* requestListenerId =
160 m_face.setInterestFilter(requestPrefix,
161 bind(&ControllerBackend::onInvitationRequestInterest,
162 this, _1, _2, offset),
163 [] (const Name& prefix, const std::string& failInfo) {});
164
165 if (m_requestListenerId != 0) {
166 m_face.unregisterPrefix(m_requestListenerId,
167 []{},
168 [] (const std::string& failInfo) {});
169 }
170
171 m_requestListenerId = requestListenerId;
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700172}
173
174ndn::Name
175ControllerBackend::getInvitationRoutingPrefix()
176{
177 return Name("/ndn/broadcast");
178}
179
180void
181ControllerBackend::onInvitationPrefixReset()
182{
183 // _LOG_DEBUG("ControllerBackend::onInvitationPrefixReset");
184}
185
186void
187ControllerBackend::onInvitationPrefixResetFailed(const std::string& failInfo)
188{
189 // _LOG_DEBUG("ControllerBackend::onInvitationPrefixResetFailed: " << failInfo);
190}
191
192
193void
194ControllerBackend::onInvitationInterest(const ndn::Name& prefix,
195 const ndn::Interest& interest,
196 size_t routingPrefixOffset)
197{
198 // _LOG_DEBUG("onInvitationInterest: " << interest.getName());
199 shared_ptr<Interest> invitationInterest =
Yingdi Yu6a614442014-10-31 17:42:43 -0700200 make_shared<Interest>(interest.getName().getSubName(routingPrefixOffset));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700201
202 // check if the chatroom already exists;
203 try {
204 Invitation invitation(invitationInterest->getName());
205 if (m_chatDialogList.contains(QString::fromStdString(invitation.getChatroom())))
206 return;
207 }
208 catch (Invitation::Error& e) {
209 // Cannot parse the invitation;
210 return;
211 }
212
213 OnInterestValidated onValidated = bind(&ControllerBackend::onInvitationValidated, this, _1);
214 OnInterestValidationFailed onFailed = bind(&ControllerBackend::onInvitationValidationFailed,
215 this, _1, _2);
216
217 m_validator.validate(*invitationInterest, onValidated, onFailed);
218}
219
220void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800221ControllerBackend::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700222{
223 // _LOG_DEBUG("ControllerBackend::onInvitationRegisterFailed: " << failInfo);
224}
225
226void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800227ControllerBackend::onInvitationRequestInterest(const ndn::Name& prefix,
228 const ndn::Interest& interest,
229 size_t routingPrefixOffset)
230{
231 shared_ptr<const Data> data = m_ims.find(interest);
232 if (data != nullptr) {
233 m_face.put(*data);
234 return;
235 }
236 Name interestName = interest.getName();
237 size_t i;
238 for (i = 0; i < interestName.size(); i++)
239 if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
240 break;
241 if (i < interestName.size()) {
242 string chatroom = interestName.at(i+1).toUri();
243 string alias = interestName.getSubName(i+2).getPrefix(-1).toUri();
244 emit invitationRequestReceived(QString::fromStdString(alias),
245 QString::fromStdString(chatroom),
246 interestName);
247 }
248}
249
250void
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700251ControllerBackend::onInvitationValidated(const shared_ptr<const Interest>& interest)
252{
253 Invitation invitation(interest->getName());
254 // Should be obtained via a method of ContactManager.
255 string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri();
256
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800257 emit invitationValidated(QString::fromStdString(alias),
258 QString::fromStdString(invitation.getChatroom()),
259 interest->getName());
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700260}
261
262void
263ControllerBackend::onInvitationValidationFailed(const shared_ptr<const Interest>& interest,
264 string failureInfo)
265{
266 // _LOG_DEBUG("Invitation: " << interest->getName() <<
267 // " cannot not be validated due to: " << failureInfo);
268}
269
270void
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800271ControllerBackend::onLocalPrefix(const ndn::ConstBufferPtr& data)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700272{
273 Name prefix;
274
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800275 Block contentBlock(tlv::Content, data);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700276 try {
277 contentBlock.parse();
278
279 for (Block::element_const_iterator it = contentBlock.elements_begin();
280 it != contentBlock.elements_end(); it++) {
281 Name candidate;
282 candidate.wireDecode(*it);
283 if (candidate.isPrefixOf(m_identity)) {
284 prefix = candidate;
285 break;
286 }
287 }
288
289 if (prefix.empty()) {
290 if (contentBlock.elements_begin() != contentBlock.elements_end())
291 prefix.wireDecode(*contentBlock.elements_begin());
292 else
293 prefix = Name("/private/local");
294 }
295 }
296 catch (Block::Error& e) {
297 prefix = Name("/private/local");
298 }
299
300 updateLocalPrefix(prefix);
301}
302
303void
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800304ControllerBackend::onLocalPrefixError(uint32_t code, const std::string& msg)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700305{
306 Name localPrefix("/private/local");
307 updateLocalPrefix(localPrefix);
308}
309
310void
311ControllerBackend::updateLocalPrefix(const Name& localPrefix)
312{
313 if (m_localPrefix.empty() || m_localPrefix != localPrefix) {
314 m_localPrefix = localPrefix;
315 emit localPrefixUpdated(QString::fromStdString(localPrefix.toUri()));
316 }
317}
318
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800319void
320ControllerBackend::onRequestResponse(const Interest& interest, Data& data)
321{
322 size_t i;
323 Name interestName = interest.getName();
324 for (i = 0; i < interestName.size(); i++) {
325 if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
326 break;
327 }
328 Name::Component chatroomName = interestName.at(i+1);
329 Block contentBlock = data.getContent();
330 int res = ndn::readNonNegativeInteger(contentBlock);
Qiuhan Ding59c45432015-04-01 18:15:03 -0700331 if (m_chatDialogList.contains(QString::fromStdString(chatroomName.toUri())))
332 return;
333
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800334 // if data is true,
335 if (res == 1)
336 emit startChatroom(QString::fromStdString(chatroomName.toUri()), false);
337 else
338 emit invitationRequestResult("You are rejected to enter chatroom: " + chatroomName.toUri());
339}
340
341void
342ControllerBackend::onRequestTimeout(const Interest& interest, int& resendTimes)
343{
344 if (resendTimes < MAXIMUM_REQUEST)
345 m_face.expressInterest(interest,
346 bind(&ControllerBackend::onRequestResponse, this, _1, _2),
347 bind(&ControllerBackend::onRequestTimeout, this, _1, resendTimes + 1));
348 else
349 emit invitationRequestResult("Invitation request times out.");
350}
351
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700352// public slots:
353void
354ControllerBackend::shutdown()
355{
Yingdi Yuf3401182015-02-02 20:21:07 -0800356 {
357 std::lock_guard<std::mutex>lock(m_resumeMutex);
358 m_shouldResume = false;
359 }
360 {
361 // In this case, we just stop checking the nfd connection and exit
362 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
363 m_isNfdConnected = true;
364 }
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700365 m_face.getIoService().stop();
366}
367
368void
369ControllerBackend::addChatroom(QString chatroom)
370{
371 m_chatDialogList.append(chatroom);
372}
373
374void
375ControllerBackend::removeChatroom(QString chatroom)
376{
377 m_chatDialogList.removeAll(chatroom);
378}
379
380void
381ControllerBackend::onUpdateLocalPrefixAction()
382{
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800383 Interest interest("/localhop/nfd/rib/routable-prefixes");
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700384 interest.setInterestLifetime(time::milliseconds(1000));
385 interest.setMustBeFresh(true);
386
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800387 ndn::util::SegmentFetcher::fetch(m_face,
388 interest,
389 ndn::util::DontVerifySegment(),
390 bind(&ControllerBackend::onLocalPrefix, this, _1),
391 bind(&ControllerBackend::onLocalPrefixError, this, _1, _2));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700392}
393
394void
395ControllerBackend::onIdentityChanged(const QString& identity)
396{
397 m_chatDialogList.clear();
398
399 m_identity = Name(identity.toStdString());
400
401 std::cerr << "ControllerBackend::onIdentityChanged: " << m_identity << std::endl;
402
403 m_keyChain.createIdentity(m_identity);
404
405 setInvitationListener();
406
407 emit identityUpdated(identity);
408}
409
410void
411ControllerBackend::onInvitationResponded(const ndn::Name& invitationName, bool accepted)
412{
413 shared_ptr<Data> response = make_shared<Data>();
414 shared_ptr<IdentityCertificate> chatroomCert;
415
416 // generate reply;
417 if (accepted) {
418 Name responseName = invitationName;
419 responseName.append(m_localPrefix.wireEncode());
420
421 response->setName(responseName);
422
423 // We should create a particular certificate for this chatroom,
424 //but let's use default one for now.
425 chatroomCert
426 = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
427
428 response->setContent(chatroomCert->wireEncode());
429 response->setFreshnessPeriod(time::milliseconds(1000));
430 }
431 else {
432 response->setName(invitationName);
433 response->setFreshnessPeriod(time::milliseconds(1000));
434 }
435
436 m_keyChain.signByIdentity(*response, m_identity);
437
438 // Check if we need a wrapper
439 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
440 if (invitationRoutingPrefix.isPrefixOf(m_identity))
441 m_face.put(*response);
442 else {
443 Name wrappedName;
444 wrappedName.append(invitationRoutingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800445 .append(ROUTING_HINT_SEPARATOR)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700446 .append(response->getName());
447
448 // _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
449
450 shared_ptr<Data> wrappedData = make_shared<Data>(wrappedName);
451 wrappedData->setContent(response->wireEncode());
452 wrappedData->setFreshnessPeriod(time::milliseconds(1000));
453
454 m_keyChain.signByIdentity(*wrappedData, m_identity);
455 m_face.put(*wrappedData);
456 }
457
458 Invitation invitation(invitationName);
459 emit startChatroomOnInvitation(invitation, true);
460}
461
462void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800463ControllerBackend::onInvitationRequestResponded(const ndn::Name& invitationResponseName,
464 bool accepted)
465{
466 shared_ptr<Data> response = make_shared<Data>(invitationResponseName);
467 if (accepted)
468 response->setContent(ndn::nonNegativeIntegerBlock(tlv::Content, 1));
469 else
470 response->setContent(ndn::nonNegativeIntegerBlock(tlv::Content, 0));
471
472 m_keyChain.signByIdentity(*response, m_identity);
473 m_ims.insert(*response);
474 m_face.put(*response);
475}
476
477void
478ControllerBackend::onSendInvitationRequest(const QString& chatroomName, const QString& prefix)
479{
480 if (prefix.length() == 0)
481 return;
482 Name interestName = getInvitationRoutingPrefix();
483 interestName.append(ROUTING_HINT_SEPARATOR).append(prefix.toStdString());
484 interestName.append("CHRONOCHAT-INVITATION-REQUEST");
485 interestName.append(chatroomName.toStdString());
486 interestName.append(m_identity);
487 interestName.appendTimestamp();
488 Interest interest(interestName);
489 interest.setInterestLifetime(time::milliseconds(10000));
490 interest.setMustBeFresh(true);
491 interest.getNonce();
492 m_face.expressInterest(interest,
493 bind(&ControllerBackend::onRequestResponse, this, _1, _2),
494 bind(&ControllerBackend::onRequestTimeout, this, _1, 0));
495}
496
497void
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700498ControllerBackend::onContactIdListReady(const QStringList& list)
499{
500 ContactList contactList;
501
502 m_contactManager.getContactList(contactList);
503 m_validator.cleanTrustAnchor();
504
505 for (ContactList::const_iterator it = contactList.begin(); it != contactList.end(); it++)
506 m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
507
508}
509
Yingdi Yuf3401182015-02-02 20:21:07 -0800510void
511ControllerBackend::onNfdReconnect()
512{
513 std::lock_guard<std::mutex>lock(m_nfdConnectionMutex);
514 m_isNfdConnected = true;
515}
516
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700517
Yingdi Yueb692ac2015-02-10 18:46:18 -0800518} // namespace chronochat
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700519
520#if WAF
521#include "controller-backend.moc"
522// #include "controller-backend.cpp.moc"
523#endif