1 /***************************************************************************
2 * Copyright (C) 2014 by Marcin Ziemiński <zieminn@gmail.com> *
3 * *
4 * This library is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
8 * *
9 * This library is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with this library; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA*
17 ***************************************************************************/
18
19 #include "otr-proxy-channel-adaptee.h"
20 #include "otr-proxy-channel.h"
21 #include "proxy-service.h"
22 #include "otr-constants.h"
23 #include "otr-manager.h"
24 #include "otr-utils.h"
25 #include "pending-curry-operation.h"
26 #include "ktp-proxy-debug.h"
27
28 #include "KTp/OTR/constants.h"
29
30 #include <TelepathyQt/DBusObject>
31 #include <TelepathyQt/TextChannel>
32 #include <TelepathyQt/Contact>
33
34 #include <QDateTime>
35
36 class PendingSendMessageResult : public PendingCurryOperation
37 {
38 public:
PendingSendMessageResult(Tp::PendingOperation * op,const Tp::TextChannelPtr & chan,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr & context,const Tp::MessagePartList & message,uint flags,bool isOTRmessage=false)39 PendingSendMessageResult(Tp::PendingOperation *op,
40 const Tp::TextChannelPtr &chan,
41 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr &context,
42 const Tp::MessagePartList &message,
43 uint flags,
44 bool isOTRmessage = false)
45 : PendingCurryOperation(op, Tp::SharedPtr<Tp::RefCounted>::dynamicCast(chan)),
46 token(QString::fromLatin1("")),
47 context(context),
48 message(message),
49 flags(flags),
50 isOTRmessage(isOTRmessage)
51 { }
52
getContext()53 Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr getContext()
54 {
55 return context;
56 }
57
getFlags()58 uint getFlags()
59 {
60 return flags;
61 }
62
getMessage()63 Tp::MessagePartList getMessage()
64 {
65 return message;
66 }
67
getToken() const68 const QString& getToken() const
69 {
70 return token;
71 }
72
extract(Tp::PendingOperation * op)73 void extract(Tp::PendingOperation *op) override
74 {
75 token = dynamic_cast<Tp::PendingSendMessage*>(op)->sentMessageToken();
76 }
77
78 private:
79 QString token;
80 Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr context;
81 Tp::MessagePartList message;
82 uint flags;
83 public:
84 const bool isOTRmessage;
85 };
86
87
Adaptee(OtrProxyChannel * pc,const QDBusConnection & dbusConnection,const Tp::TextChannelPtr & channel,const OTR::SessionContext & context,ProxyService * ps)88 OtrProxyChannel::Adaptee::Adaptee(OtrProxyChannel *pc,
89 const QDBusConnection &dbusConnection,
90 const Tp::TextChannelPtr &channel,
91 const OTR::SessionContext &context,
92 ProxyService *ps)
93 : QObject(pc),
94 adaptor(new Tp::Service::ChannelProxyInterfaceOTRAdaptor(dbusConnection, this, pc->dbusObject())),
95 pc(pc),
96 chan(channel),
97 isConnected(false),
98 otrSes(this, context, ps->managerOTR()),
99 ps(ps),
100 isGenerating(false),
101 aboutToInit(false)
102 {
103 qCDebug(KTP_PROXY) << "Created OTR session for context: "
104 << "Account id: " << context.accountId
105 << " Account name: " << context.accountName
106 << " recipient name: " << context.recipientName
107 << " protocol: " << context.protocol;
108 connect(chan.data(), SIGNAL(invalidated(Tp::DBusProxy*,const QString&,const QString&)), SLOT(onChannelClosed()));
109 connect(&otrSes, SIGNAL(trustLevelChanged(TrustLevel)), SLOT(onTrustLevelChanged(TrustLevel)));
110 connect(&otrSes, SIGNAL(sessionRefreshed()), SIGNAL(sessionRefreshed()));
111 connect(&otrSes, SIGNAL(authenticationRequested(const QString&)), SIGNAL(peerAuthenticationRequested(const QString&)));
112 connect(&otrSes, SIGNAL(authenticationInProgress()), SIGNAL(peerAuthenticationInProgress()));
113 connect(&otrSes, SIGNAL(authenticationConcluded(bool)), SIGNAL(peerAuthenticationConcluded(bool)));
114 connect(&otrSes, SIGNAL(authenticationAborted()), SIGNAL(peerAuthenticationAborted()));
115 connect(&otrSes, SIGNAL(authenticationError()), SIGNAL(peerAuthenticationError()));
116 connect(&otrSes, SIGNAL(authenticationCheated()), SIGNAL(peerAuthenticationCheated()));
117
118 sender = channel->connection()->selfHandle();
119 }
120
wrappedChannel() const121 QDBusObjectPath OtrProxyChannel::Adaptee::wrappedChannel() const
122 {
123 return QDBusObjectPath(chan->objectPath());
124 }
125
connected() const126 bool OtrProxyChannel::Adaptee::connected() const
127 {
128 return isConnected;
129 }
130
pendingMessages() const131 Tp::MessagePartListList OtrProxyChannel::Adaptee::pendingMessages() const
132 {
133 Tp::MessagePartListList pending;
134 for(const auto& mp: messages) {
135 pending << mp.parts();
136 }
137
138 return pending;
139 }
140
trustLevel() const141 uint OtrProxyChannel::Adaptee::trustLevel() const
142 {
143 return static_cast<uint>(otrSes.trustLevel());
144 }
145
localFingerprint() const146 QString OtrProxyChannel::Adaptee::localFingerprint() const
147 {
148 return otrSes.localFingerprint();
149 }
150
remoteFingerprint() const151 QString OtrProxyChannel::Adaptee::remoteFingerprint() const
152 {
153 return otrSes.remoteFingerprint();
154 }
155
channel() const156 Tp::TextChannelPtr OtrProxyChannel::Adaptee::channel() const
157 {
158 return chan;
159 }
160
connectProxy(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::ConnectProxyContextPtr & context)161 void OtrProxyChannel::Adaptee::connectProxy(
162 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::ConnectProxyContextPtr &context)
163 {
164 qCDebug(KTP_PROXY) << "Connecting proxy: " << pc->objectPath();
165
166 connect(chan.data(),
167 SIGNAL(messageReceived(const Tp::ReceivedMessage&)),
168 SLOT(onMessageReceived(const Tp::ReceivedMessage&)));
169
170 connect(chan.data(),
171 SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)),
172 SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&)));
173
174 connect(ps, SIGNAL(keyGenerationStarted(const QString&)), SLOT(onKeyGenerationStarted(const QString&)));
175 connect(ps, SIGNAL(keyGenerationFinished(const QString&, bool)), SLOT(onKeyGenerationFinished(const QString&, bool)));
176
177 for(const Tp::ReceivedMessage &m: chan->messageQueue()) {
178 onMessageReceived(m);
179 }
180
181 isConnected = true;
182 context->setFinished();
183 }
disconnectProxy(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::DisconnectProxyContextPtr & context)184 void OtrProxyChannel::Adaptee::disconnectProxy(
185 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::DisconnectProxyContextPtr &context)
186 {
187 if(otrSes.trustLevel() != OTR::TrustLevel::NOT_PRIVATE) {
188 otrSes.stopSession();
189 }
190 qCDebug(KTP_PROXY) << "Disconnecting proxy: " << pc->objectPath();
191 disconnect(chan.data(), SIGNAL(messageReceived(const Tp::ReceivedMessage&)),
192 this, SLOT(onMessageReceived(const Tp::ReceivedMessage&)));
193
194 disconnect(chan.data(), SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)),
195 this, SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&)));
196
197 disconnect(ps, SIGNAL(keyGenerationStarted(const QString&)),
198 this, SLOT(onKeyGenerationStarted(const QString&)));
199 disconnect(ps, SIGNAL(keyGenerationFinished(const QString&, bool)), this,
200 SLOT(onKeyGenerationFinished(const QString&, bool)));
201
202 isConnected = false;
203 messages.clear();
204 context->setFinished();
205 }
206
processOTRmessage(const OTR::Message & message)207 void OtrProxyChannel::Adaptee::processOTRmessage(const OTR::Message &message)
208 {
209 qCDebug(KTP_PROXY);
210 switch(message.direction()) {
211 case OTR::MessageDirection::INTERNAL:
212 case OTR::MessageDirection::FROM_PEER:
213 Q_EMIT messageReceived(message.parts());
214 return;
215 case OTR::MessageDirection::TO_PEER:
216 sendOTRmessage(message);
217 return;
218 }
219 }
220
sendOTRmessage(const OTR::Message & message)221 void OtrProxyChannel::Adaptee::sendOTRmessage(const OTR::Message &message)
222 {
223 qCDebug(KTP_PROXY);
224 uint flags = 0;
225 PendingSendMessageResult *pending = new PendingSendMessageResult(
226 chan->send(message.parts(), (Tp::MessageSendingFlags) flags),
227 chan,
228 Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr(),
229 message.parts(),
230 flags,
231 true);
232
233 connect(pending,
234 SIGNAL(finished(Tp::PendingOperation*)),
235 SLOT(onPendingSendFinished(Tp::PendingOperation*)));
236 }
237
sendMessage(const Tp::MessagePartList & message,uint flags,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr & context)238 void OtrProxyChannel::Adaptee::sendMessage(const Tp::MessagePartList &message, uint flags,
239 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr &context)
240 {
241 qCDebug(KTP_PROXY);
242
243 if(!connected()) {
244 context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
245 return;
246 }
247
248 OTR::Message otrMessage(message);
249 const OTR::CryptResult cres = otrSes.encrypt(otrMessage);
250 if(cres == OTR::CryptResult::ERROR) {
251 qCDebug(KTP_PROXY) << "Sending error";
252 context->setFinishedWithError(KTP_PROXY_ERROR_ENCRYPTION_ERROR,
253 QLatin1String("Message could not be encrypted with OTR"));
254 return;
255 }
256
257 // we are starting an AKE - do not show it to the user
258 // policy is probably set to ALWAYS in this case
259 if(otrl_proto_message_type(otrMessage.text().toLocal8Bit()) == OTRL_MSGTYPE_QUERY) {
260 sendOTRmessage(otrMessage);
261 } else {
262 PendingSendMessageResult *pending = new PendingSendMessageResult(
263 chan->send(otrMessage.parts(), (Tp::MessageSendingFlags) flags),
264 chan,
265 context,
266 message,
267 flags);
268
269 connect(pending,
270 SIGNAL(finished(Tp::PendingOperation*)),
271 SLOT(onPendingSendFinished(Tp::PendingOperation*)));
272 }
273 }
274
acknowledgePendingMessages(const Tp::UIntList & ids,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AcknowledgePendingMessagesContextPtr & context)275 void OtrProxyChannel::Adaptee::acknowledgePendingMessages(const Tp::UIntList &ids,
276 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AcknowledgePendingMessagesContextPtr &context)
277 {
278 if(!connected()) {
279 context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
280 return;
281 }
282
283 qCDebug(KTP_PROXY) << "Message queue size: " << messages.size();
284 QList<Tp::ReceivedMessage> toAcknowledge;
285 for(uint id: ids) {
286 auto found = messages.find(id);
287 if(found == messages.end()) {
288 qCDebug(KTP_PROXY) << "Client trying to acknowledge non existing message with id" << id;
289 context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
290 QLatin1String("Message with given ID is not present in the message queue"));
291 return;
292 } else {
293 toAcknowledge << *found;
294 }
295 }
296
297 qCDebug(KTP_PROXY) << "Acknowledging " << toAcknowledge.count() << " messages";
298 chan->acknowledge(toAcknowledge);
299 context->setFinished();
300 }
301
initialize(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::InitializeContextPtr & context)302 void OtrProxyChannel::Adaptee::initialize(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::InitializeContextPtr &context)
303 {
304 qCDebug(KTP_PROXY);
305 if(!connected()) {
306 context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
307 return;
308 }
309
310 // create private key if necessary - to avoid blocking
311 if(otrSes.localFingerprint().isEmpty() && ps->getPolicy() != OTRL_POLICY_NEVER) {
312 aboutToInit = true;
313 acquirePrivateKey();
314 } else if(isGenerating) {
315 aboutToInit = true;
316 } else {
317 sendOTRmessage(otrSes.startSession());
318 }
319
320 context->setFinished();
321 }
322
stop(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StopContextPtr & context)323 void OtrProxyChannel::Adaptee::stop(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StopContextPtr &context)
324 {
325 if(!connected()) {
326 context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
327 return;
328 }
329 otrSes.stopSession();
330 context->setFinished();
331 }
332
trustFingerprint(const QString & fingerprint,bool trust,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::TrustFingerprintContextPtr & context)333 void OtrProxyChannel::Adaptee::trustFingerprint(const QString& fingerprint, bool trust,
334 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::TrustFingerprintContextPtr &context)
335 {
336 if(!connected()) {
337 context->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
338 return;
339 }
340
341 qCDebug(KTP_PROXY) << "TrustFingeprint - " << trust << ": " << fingerprint << " when remote is: " << otrSes.remoteFingerprint();
342
343 if(otrSes.remoteFingerprint().isEmpty() || fingerprint != otrSes.remoteFingerprint()) {
344 context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
345 QLatin1String("No such fingerprint currently in use by remote contact"));
346 return;
347 }
348
349 OTR::TrustFpResult fpRes = otrSes.trustFingerprint(trust);
350 if(fpRes != OTR::TrustFpResult::OK) {
351 // should not happend, TODO clarify
352 qCDebug(KTP_PROXY) << "Trust error";
353 context->setFinishedWithError(TP_QT_ERROR_INVALID_ARGUMENT,
354 QLatin1String("No such fingerprint currently in use by remote contact"));
355 return;
356 }
357 context->setFinished();
358 }
359
startPeerAuthentication(const QString & question,const QString & secret,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StartPeerAuthenticationContextPtr & ctx)360 void OtrProxyChannel::Adaptee::startPeerAuthentication(const QString &question, const QString &secret,
361 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::StartPeerAuthenticationContextPtr &ctx)
362 {
363 if(!connected()) {
364 ctx->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
365 return;
366 }
367
368 if(question.isEmpty()) {
369 otrSes.initSMPSecret(secret);
370 } else {
371 otrSes.initSMPQuery(question, secret);
372 }
373 ctx->setFinished();
374 }
375
respondPeerAuthentication(const QString & secret,const Tp::Service::ChannelProxyInterfaceOTRAdaptor::RespondPeerAuthenticationContextPtr & ctx)376 void OtrProxyChannel::Adaptee::respondPeerAuthentication(const QString &secret,
377 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::RespondPeerAuthenticationContextPtr &ctx)
378 {
379 if(!connected()) {
380 ctx->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
381 return;
382 }
383
384 otrSes.respondSMPAuthentication(secret);
385 ctx->setFinished();
386 }
387
abortPeerAuthentication(const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AbortPeerAuthenticationContextPtr & ctx)388 void OtrProxyChannel::Adaptee::abortPeerAuthentication(
389 const Tp::Service::ChannelProxyInterfaceOTRAdaptor::AbortPeerAuthenticationContextPtr &ctx)
390 {
391 if(!connected()) {
392 ctx->setFinishedWithError(KTP_PROXY_ERROR_NOT_CONNECTED, QString::fromLatin1("Proxy is not connected"));
393 return;
394 }
395
396 otrSes.abortSMPAuthentiaction();
397 ctx->setFinished();
398 }
399
onMessageReceived(const Tp::ReceivedMessage & receivedMessage)400 void OtrProxyChannel::Adaptee::onMessageReceived(const Tp::ReceivedMessage &receivedMessage)
401 {
402 const uint id = receivedMessage.header()[QLatin1String("pending-message-id")].variant().toUInt(nullptr);
403 OTR::Message otrMsg(receivedMessage.parts());
404 // no private key - should generate on now
405 if(otrMsg.isOTRmessage() && otrSes.localFingerprint().isEmpty() && ps->getPolicy() != OTRL_POLICY_NEVER) {
406 enqueuedMessages << receivedMessage;
407 acquirePrivateKey();
408 return;
409 // private key is currently being generated
410 } else if(isGenerating) {
411 enqueuedMessages << receivedMessage;
412 return;
413 }
414
415 qCDebug(KTP_PROXY) << "Received message with id: " << id;
416 const OTR::CryptResult cres = otrSes.decrypt(otrMsg);
417
418 if(cres == OTR::CryptResult::CHANGED || cres == OTR::CryptResult::UNCHANGED) {
419 messages.insert(id, receivedMessage);
420
421 Q_EMIT messageReceived(otrMsg.parts());
422 } else {
423 // Error or OTR message - acknowledge right now
424 if(cres == OTR::CryptResult::ERROR) {
425 qCWarning(KTP_PROXY) << "Decryption error of the message: " << otrMsg.text();
426 }
427 chan->acknowledge(QList<Tp::ReceivedMessage>() << receivedMessage);
428 }
429 }
430
onPendingMessageRemoved(const Tp::ReceivedMessage & receivedMessage)431 void OtrProxyChannel::Adaptee::onPendingMessageRemoved(const Tp::ReceivedMessage &receivedMessage)
432 {
433 const uint id = receivedMessage.header().value(QLatin1String("pending-message-id")).variant().toUInt(nullptr);
434 if(messages.remove(id)) {
435 Q_EMIT pendingMessagesRemoved(Tp::UIntList() << id);
436 } else {
437 qCDebug(KTP_PROXY) << "Text channel removed missing pending message with id or an OTR message: " << id;
438 }
439 }
440
onPendingSendFinished(Tp::PendingOperation * op)441 void OtrProxyChannel::Adaptee::onPendingSendFinished(Tp::PendingOperation *op)
442 {
443 PendingSendMessageResult *sendResult = dynamic_cast<PendingSendMessageResult*>(op);
444
445 if(sendResult->isOTRmessage) {
446 if(sendResult->isError()) {
447 // TODO check the message and provide information to the user
448 }
449 } else {
450 Tp::Service::ChannelProxyInterfaceOTRAdaptor::SendMessageContextPtr ctx = sendResult->getContext();
451
452 if(sendResult->isError()) {
453 ctx->setFinishedWithError(sendResult->errorName(), sendResult->errorMessage());
454 } else {
455 ctx->setFinished(sendResult->getToken());
456
457 OTR::Message message = sendResult->getMessage();
458 message.setToken(sendResult->getToken());
459 message.setTimestamp(QDateTime::currentDateTime().toTime_t());
460 message.setSender(sender);
461 message.setSenderId(otrSes.context().accountName);
462 if(!otrSes.remoteFingerprint().isEmpty()) {
463 message.setOTRheader(OTR_REMOTE_FINGERPRINT_HEADER, otrSes.remoteFingerprint());
464 }
465
466 Q_EMIT messageSent(message.parts(), sendResult->getFlags(), sendResult->getToken());
467 }
468 }
469 }
470
onTrustLevelChanged(TrustLevel trustLevel)471 void OtrProxyChannel::Adaptee::onTrustLevelChanged(TrustLevel trustLevel)
472 {
473 Q_EMIT trustLevelChanged(static_cast<uint>(trustLevel));
474 }
475
acquirePrivateKey()476 void OtrProxyChannel::Adaptee::acquirePrivateKey()
477 {
478 if(!ps->createNewPrivateKey(otrSes.context().accountId, otrSes.context().accountName)) {
479 qCDebug(KTP_PROXY) << "Probably ongoing key generation for another session";
480 }
481 }
482
onKeyGenerationStarted(const QString & accountId)483 void OtrProxyChannel::Adaptee::onKeyGenerationStarted(const QString &accountId)
484 {
485 if(accountId != otrSes.context().accountId) {
486 return;
487 }
488 isGenerating = true;
489 }
490
onKeyGenerationFinished(const QString & accountId,bool error)491 void OtrProxyChannel::Adaptee::onKeyGenerationFinished(const QString &accountId, bool error)
492 {
493 if(accountId != otrSes.context().accountId) {
494 return;
495 }
496 qCDebug(KTP_PROXY) << "Finished key generation for: " << accountId;
497 isGenerating = false;
498
499 if(error) {
500 qCWarning(KTP_PROXY) << "Could not generate private key for " << accountId;
501 return;
502 }
503 if(!enqueuedMessages.isEmpty()) {
504 for(auto &&mes: enqueuedMessages) {
505 onMessageReceived(mes);
506 }
507 enqueuedMessages.clear();
508 } else if(aboutToInit) {
509 sendOTRmessage(otrSes.startSession());
510 }
511
512 aboutToInit = false;
513 }
514
onChannelClosed()515 void OtrProxyChannel::Adaptee::onChannelClosed()
516 {
517 qCDebug(KTP_PROXY);
518 // we will not be able to send disconnect message so we just finish our own OTR session
519 if(isConnected) {
520 otrSes.forceUnencrypted();
521 }
522 Q_EMIT closed();
523 }
524