blob: df341c9492a09a4e8686eea00bc4f3cd9706d376 [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>
9 */
10
11#include "controller-backend.hpp"
12
13#ifndef Q_MOC_RUN
Yingdi Yu7ff31f02015-02-05 11:21:07 -080014#include <ndn-cxx/util/segment-fetcher.hpp>
Yingdi Yu2c9e7712014-10-20 11:55:05 -070015#include "invitation.hpp"
16#include "logging.h"
17#endif
18
19
20INIT_LOGGER("ControllerBackend");
21
Yingdi Yueb692ac2015-02-10 18:46:18 -080022namespace chronochat {
Yingdi Yu2c9e7712014-10-20 11:55:05 -070023
24using std::string;
25
26using ndn::Face;
27using ndn::IdentityCertificate;
28using ndn::OnInterestValidated;
29using ndn::OnInterestValidationFailed;
30
31
Qiuhan Dingba3e57a2015-01-08 19:07:39 -080032static const ndn::Name::Component ROUTING_HINT_SEPARATOR =
33 ndn::name::Component::fromEscapedString("%F0%2E");
34static const int MAXIMUM_REQUEST = 3;
Yingdi Yu2c9e7712014-10-20 11:55:05 -070035
36ControllerBackend::ControllerBackend(QObject* parent)
37 : QThread(parent)
38 , m_contactManager(m_face)
39 , m_invitationListenerId(0)
40{
41 // connection to contact manager
42 connect(this, SIGNAL(identityUpdated(const QString&)),
43 &m_contactManager, SLOT(onIdentityUpdated(const QString&)));
44
45 connect(&m_contactManager, SIGNAL(contactIdListReady(const QStringList&)),
46 this, SLOT(onContactIdListReady(const QStringList&)));
47
48}
49
50ControllerBackend::~ControllerBackend()
51{
52}
53
54void
55ControllerBackend::run()
56{
57 setInvitationListener();
58
59 m_face.processEvents();
60
61 std::cerr << "Bye!" << std::endl;
62}
63
64// private methods:
65
66void
67tmpOnInvitationInterest(const ndn::Name& prefix,
68 const ndn::Interest& interest)
69{
70 std::cerr << "tmpOnInvitationInterest" << std::endl;
71}
72
73void
74tmpOnInvitationRegisterFailed(const Name& prefix,
75 const string& failInfo)
76{
77 std::cerr << "tmpOnInvitationRegisterFailed" << std::endl;
78}
79
80void
81tmpUnregisterPrefixSuccessCallback()
82{
83 std::cerr << "tmpUnregisterPrefixSuccessCallback" << std::endl;
84}
85
86void
87tmpUnregisterPrefixFailureCallback(const string& failInfo)
88{
89 std::cerr << "tmpUnregisterPrefixSuccessCallback" << std::endl;
90}
91
92void
93ControllerBackend::setInvitationListener()
94{
95 QMutexLocker locker(&m_mutex);
96
97 Name invitationPrefix;
Qiuhan Dingba3e57a2015-01-08 19:07:39 -080098 Name requestPrefix;
Yingdi Yu2c9e7712014-10-20 11:55:05 -070099 Name routingPrefix = getInvitationRoutingPrefix();
100 size_t offset = 0;
101 if (!routingPrefix.isPrefixOf(m_identity)) {
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800102 invitationPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
103 requestPrefix.append(routingPrefix).append(ROUTING_HINT_SEPARATOR);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700104 offset = routingPrefix.size() + 1;
105 }
106 invitationPrefix.append(m_identity).append("CHRONOCHAT-INVITATION");
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800107 requestPrefix.append(m_identity).append("CHRONOCHAT-INVITATION-REQUEST");
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700108
109 const ndn::RegisteredPrefixId* invitationListenerId =
110 m_face.setInterestFilter(invitationPrefix,
111 bind(&ControllerBackend::onInvitationInterest,
112 this, _1, _2, offset),
113 bind(&ControllerBackend::onInvitationRegisterFailed,
114 this, _1, _2));
115
116 if (m_invitationListenerId != 0) {
117 m_face.unregisterPrefix(m_invitationListenerId,
118 bind(&ControllerBackend::onInvitationPrefixReset, this),
119 bind(&ControllerBackend::onInvitationPrefixResetFailed, this, _1));
120 }
121
122 m_invitationListenerId = invitationListenerId;
123
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800124 const ndn::RegisteredPrefixId* requestListenerId =
125 m_face.setInterestFilter(requestPrefix,
126 bind(&ControllerBackend::onInvitationRequestInterest,
127 this, _1, _2, offset),
128 [] (const Name& prefix, const std::string& failInfo) {});
129
130 if (m_requestListenerId != 0) {
131 m_face.unregisterPrefix(m_requestListenerId,
132 []{},
133 [] (const std::string& failInfo) {});
134 }
135
136 m_requestListenerId = requestListenerId;
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700137}
138
139ndn::Name
140ControllerBackend::getInvitationRoutingPrefix()
141{
142 return Name("/ndn/broadcast");
143}
144
145void
146ControllerBackend::onInvitationPrefixReset()
147{
148 // _LOG_DEBUG("ControllerBackend::onInvitationPrefixReset");
149}
150
151void
152ControllerBackend::onInvitationPrefixResetFailed(const std::string& failInfo)
153{
154 // _LOG_DEBUG("ControllerBackend::onInvitationPrefixResetFailed: " << failInfo);
155}
156
157
158void
159ControllerBackend::onInvitationInterest(const ndn::Name& prefix,
160 const ndn::Interest& interest,
161 size_t routingPrefixOffset)
162{
163 // _LOG_DEBUG("onInvitationInterest: " << interest.getName());
164 shared_ptr<Interest> invitationInterest =
Yingdi Yu6a614442014-10-31 17:42:43 -0700165 make_shared<Interest>(interest.getName().getSubName(routingPrefixOffset));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700166
167 // check if the chatroom already exists;
168 try {
169 Invitation invitation(invitationInterest->getName());
170 if (m_chatDialogList.contains(QString::fromStdString(invitation.getChatroom())))
171 return;
172 }
173 catch (Invitation::Error& e) {
174 // Cannot parse the invitation;
175 return;
176 }
177
178 OnInterestValidated onValidated = bind(&ControllerBackend::onInvitationValidated, this, _1);
179 OnInterestValidationFailed onFailed = bind(&ControllerBackend::onInvitationValidationFailed,
180 this, _1, _2);
181
182 m_validator.validate(*invitationInterest, onValidated, onFailed);
183}
184
185void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800186ControllerBackend::onInvitationRegisterFailed(const Name& prefix, const std::string& failInfo)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700187{
188 // _LOG_DEBUG("ControllerBackend::onInvitationRegisterFailed: " << failInfo);
189}
190
191void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800192ControllerBackend::onInvitationRequestInterest(const ndn::Name& prefix,
193 const ndn::Interest& interest,
194 size_t routingPrefixOffset)
195{
196 shared_ptr<const Data> data = m_ims.find(interest);
197 if (data != nullptr) {
198 m_face.put(*data);
199 return;
200 }
201 Name interestName = interest.getName();
202 size_t i;
203 for (i = 0; i < interestName.size(); i++)
204 if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
205 break;
206 if (i < interestName.size()) {
207 string chatroom = interestName.at(i+1).toUri();
208 string alias = interestName.getSubName(i+2).getPrefix(-1).toUri();
209 emit invitationRequestReceived(QString::fromStdString(alias),
210 QString::fromStdString(chatroom),
211 interestName);
212 }
213}
214
215void
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700216ControllerBackend::onInvitationValidated(const shared_ptr<const Interest>& interest)
217{
218 Invitation invitation(interest->getName());
219 // Should be obtained via a method of ContactManager.
220 string alias = invitation.getInviterCertificate().getPublicKeyName().getPrefix(-1).toUri();
221
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800222 emit invitationValidated(QString::fromStdString(alias),
223 QString::fromStdString(invitation.getChatroom()),
224 interest->getName());
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700225}
226
227void
228ControllerBackend::onInvitationValidationFailed(const shared_ptr<const Interest>& interest,
229 string failureInfo)
230{
231 // _LOG_DEBUG("Invitation: " << interest->getName() <<
232 // " cannot not be validated due to: " << failureInfo);
233}
234
235void
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800236ControllerBackend::onLocalPrefix(const ndn::ConstBufferPtr& data)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700237{
238 Name prefix;
239
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800240 Block contentBlock(tlv::Content, data);
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700241 try {
242 contentBlock.parse();
243
244 for (Block::element_const_iterator it = contentBlock.elements_begin();
245 it != contentBlock.elements_end(); it++) {
246 Name candidate;
247 candidate.wireDecode(*it);
248 if (candidate.isPrefixOf(m_identity)) {
249 prefix = candidate;
250 break;
251 }
252 }
253
254 if (prefix.empty()) {
255 if (contentBlock.elements_begin() != contentBlock.elements_end())
256 prefix.wireDecode(*contentBlock.elements_begin());
257 else
258 prefix = Name("/private/local");
259 }
260 }
261 catch (Block::Error& e) {
262 prefix = Name("/private/local");
263 }
264
265 updateLocalPrefix(prefix);
266}
267
268void
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800269ControllerBackend::onLocalPrefixError(uint32_t code, const std::string& msg)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700270{
271 Name localPrefix("/private/local");
272 updateLocalPrefix(localPrefix);
273}
274
275void
276ControllerBackend::updateLocalPrefix(const Name& localPrefix)
277{
278 if (m_localPrefix.empty() || m_localPrefix != localPrefix) {
279 m_localPrefix = localPrefix;
280 emit localPrefixUpdated(QString::fromStdString(localPrefix.toUri()));
281 }
282}
283
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800284void
285ControllerBackend::onRequestResponse(const Interest& interest, Data& data)
286{
287 size_t i;
288 Name interestName = interest.getName();
289 for (i = 0; i < interestName.size(); i++) {
290 if (interestName.at(i) == Name::Component("CHRONOCHAT-INVITATION-REQUEST"))
291 break;
292 }
293 Name::Component chatroomName = interestName.at(i+1);
294 Block contentBlock = data.getContent();
295 int res = ndn::readNonNegativeInteger(contentBlock);
Qiuhan Ding59c45432015-04-01 18:15:03 -0700296 if (m_chatDialogList.contains(QString::fromStdString(chatroomName.toUri())))
297 return;
298
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800299 // if data is true,
300 if (res == 1)
301 emit startChatroom(QString::fromStdString(chatroomName.toUri()), false);
302 else
303 emit invitationRequestResult("You are rejected to enter chatroom: " + chatroomName.toUri());
304}
305
306void
307ControllerBackend::onRequestTimeout(const Interest& interest, int& resendTimes)
308{
309 if (resendTimes < MAXIMUM_REQUEST)
310 m_face.expressInterest(interest,
311 bind(&ControllerBackend::onRequestResponse, this, _1, _2),
312 bind(&ControllerBackend::onRequestTimeout, this, _1, resendTimes + 1));
313 else
314 emit invitationRequestResult("Invitation request times out.");
315}
316
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700317// public slots:
318void
319ControllerBackend::shutdown()
320{
321 m_face.getIoService().stop();
322}
323
324void
325ControllerBackend::addChatroom(QString chatroom)
326{
327 m_chatDialogList.append(chatroom);
328}
329
330void
331ControllerBackend::removeChatroom(QString chatroom)
332{
333 m_chatDialogList.removeAll(chatroom);
334}
335
336void
337ControllerBackend::onUpdateLocalPrefixAction()
338{
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800339 Interest interest("/localhop/nfd/rib/routable-prefixes");
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700340 interest.setInterestLifetime(time::milliseconds(1000));
341 interest.setMustBeFresh(true);
342
Yingdi Yu7ff31f02015-02-05 11:21:07 -0800343 ndn::util::SegmentFetcher::fetch(m_face,
344 interest,
345 ndn::util::DontVerifySegment(),
346 bind(&ControllerBackend::onLocalPrefix, this, _1),
347 bind(&ControllerBackend::onLocalPrefixError, this, _1, _2));
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700348}
349
350void
351ControllerBackend::onIdentityChanged(const QString& identity)
352{
353 m_chatDialogList.clear();
354
355 m_identity = Name(identity.toStdString());
356
357 std::cerr << "ControllerBackend::onIdentityChanged: " << m_identity << std::endl;
358
359 m_keyChain.createIdentity(m_identity);
360
361 setInvitationListener();
362
363 emit identityUpdated(identity);
364}
365
366void
367ControllerBackend::onInvitationResponded(const ndn::Name& invitationName, bool accepted)
368{
369 shared_ptr<Data> response = make_shared<Data>();
370 shared_ptr<IdentityCertificate> chatroomCert;
371
372 // generate reply;
373 if (accepted) {
374 Name responseName = invitationName;
375 responseName.append(m_localPrefix.wireEncode());
376
377 response->setName(responseName);
378
379 // We should create a particular certificate for this chatroom,
380 //but let's use default one for now.
381 chatroomCert
382 = m_keyChain.getCertificate(m_keyChain.getDefaultCertificateNameForIdentity(m_identity));
383
384 response->setContent(chatroomCert->wireEncode());
385 response->setFreshnessPeriod(time::milliseconds(1000));
386 }
387 else {
388 response->setName(invitationName);
389 response->setFreshnessPeriod(time::milliseconds(1000));
390 }
391
392 m_keyChain.signByIdentity(*response, m_identity);
393
394 // Check if we need a wrapper
395 Name invitationRoutingPrefix = getInvitationRoutingPrefix();
396 if (invitationRoutingPrefix.isPrefixOf(m_identity))
397 m_face.put(*response);
398 else {
399 Name wrappedName;
400 wrappedName.append(invitationRoutingPrefix)
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800401 .append(ROUTING_HINT_SEPARATOR)
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700402 .append(response->getName());
403
404 // _LOG_DEBUG("onInvitationResponded: prepare reply " << wrappedName);
405
406 shared_ptr<Data> wrappedData = make_shared<Data>(wrappedName);
407 wrappedData->setContent(response->wireEncode());
408 wrappedData->setFreshnessPeriod(time::milliseconds(1000));
409
410 m_keyChain.signByIdentity(*wrappedData, m_identity);
411 m_face.put(*wrappedData);
412 }
413
414 Invitation invitation(invitationName);
415 emit startChatroomOnInvitation(invitation, true);
416}
417
418void
Qiuhan Dingba3e57a2015-01-08 19:07:39 -0800419ControllerBackend::onInvitationRequestResponded(const ndn::Name& invitationResponseName,
420 bool accepted)
421{
422 shared_ptr<Data> response = make_shared<Data>(invitationResponseName);
423 if (accepted)
424 response->setContent(ndn::nonNegativeIntegerBlock(tlv::Content, 1));
425 else
426 response->setContent(ndn::nonNegativeIntegerBlock(tlv::Content, 0));
427
428 m_keyChain.signByIdentity(*response, m_identity);
429 m_ims.insert(*response);
430 m_face.put(*response);
431}
432
433void
434ControllerBackend::onSendInvitationRequest(const QString& chatroomName, const QString& prefix)
435{
436 if (prefix.length() == 0)
437 return;
438 Name interestName = getInvitationRoutingPrefix();
439 interestName.append(ROUTING_HINT_SEPARATOR).append(prefix.toStdString());
440 interestName.append("CHRONOCHAT-INVITATION-REQUEST");
441 interestName.append(chatroomName.toStdString());
442 interestName.append(m_identity);
443 interestName.appendTimestamp();
444 Interest interest(interestName);
445 interest.setInterestLifetime(time::milliseconds(10000));
446 interest.setMustBeFresh(true);
447 interest.getNonce();
448 m_face.expressInterest(interest,
449 bind(&ControllerBackend::onRequestResponse, this, _1, _2),
450 bind(&ControllerBackend::onRequestTimeout, this, _1, 0));
451}
452
453void
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700454ControllerBackend::onContactIdListReady(const QStringList& list)
455{
456 ContactList contactList;
457
458 m_contactManager.getContactList(contactList);
459 m_validator.cleanTrustAnchor();
460
461 for (ContactList::const_iterator it = contactList.begin(); it != contactList.end(); it++)
462 m_validator.addTrustAnchor((*it)->getPublicKeyName(), (*it)->getPublicKey());
463
464}
465
466
Yingdi Yueb692ac2015-02-10 18:46:18 -0800467} // namespace chronochat
Yingdi Yu2c9e7712014-10-20 11:55:05 -0700468
469#if WAF
470#include "controller-backend.moc"
471// #include "controller-backend.cpp.moc"
472#endif