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