1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "mtproto/session.h"
9
10 #include "mtproto/details/mtproto_dcenter.h"
11 #include "mtproto/session_private.h"
12 #include "mtproto/mtproto_auth_key.h"
13 #include "core/application.h"
14 #include "core/core_settings.h"
15 #include "base/unixtime.h"
16
17 namespace MTP {
18 namespace details {
19
SessionOptions(const QString & systemLangCode,const QString & cloudLangCode,const QString & langPackName,const ProxyData & proxy,bool useIPv4,bool useIPv6,bool useHttp,bool useTcp)20 SessionOptions::SessionOptions(
21 const QString &systemLangCode,
22 const QString &cloudLangCode,
23 const QString &langPackName,
24 const ProxyData &proxy,
25 bool useIPv4,
26 bool useIPv6,
27 bool useHttp,
28 bool useTcp)
29 : systemLangCode(systemLangCode)
30 , cloudLangCode(cloudLangCode)
31 , langPackName(langPackName)
32 , proxy(proxy)
33 , useIPv4(useIPv4)
34 , useIPv6(useIPv6)
35 , useHttp(useHttp)
36 , useTcp(useTcp) {
37 }
38
39 template <typename Callback>
withSession(Callback && callback)40 void SessionData::withSession(Callback &&callback) {
41 QMutexLocker lock(&_ownerMutex);
42 if (const auto session = _owner) {
43 InvokeQueued(session, [
44 session,
45 callback = std::forward<Callback>(callback)
46 ] {
47 callback(session);
48 });
49 }
50 }
51
notifyConnectionInited(const SessionOptions & options)52 void SessionData::notifyConnectionInited(const SessionOptions &options) {
53 // #TODO race
54 const auto current = this->options();
55 if (current.cloudLangCode == _options.cloudLangCode
56 && current.systemLangCode == _options.systemLangCode
57 && current.langPackName == _options.langPackName
58 && current.proxy == _options.proxy) {
59 QMutexLocker lock(&_ownerMutex);
60 if (_owner) {
61 _owner->notifyDcConnectionInited();
62 }
63 }
64 }
65
queueTryToReceive()66 void SessionData::queueTryToReceive() {
67 withSession([](not_null<Session*> session) {
68 session->tryToReceive();
69 });
70 }
71
queueNeedToResumeAndSend()72 void SessionData::queueNeedToResumeAndSend() {
73 withSession([](not_null<Session*> session) {
74 session->needToResumeAndSend();
75 });
76 }
77
queueConnectionStateChange(int newState)78 void SessionData::queueConnectionStateChange(int newState) {
79 withSession([=](not_null<Session*> session) {
80 session->connectionStateChange(newState);
81 });
82 }
83
queueResetDone()84 void SessionData::queueResetDone() {
85 withSession([](not_null<Session*> session) {
86 session->resetDone();
87 });
88 }
89
queueSendAnything(crl::time msCanWait)90 void SessionData::queueSendAnything(crl::time msCanWait) {
91 withSession([=](not_null<Session*> session) {
92 session->sendAnything(msCanWait);
93 });
94 }
95
connectionInited() const96 bool SessionData::connectionInited() const {
97 QMutexLocker lock(&_ownerMutex);
98 return _owner ? _owner->connectionInited() : false;
99 }
100
getTemporaryKey(TemporaryKeyType type) const101 AuthKeyPtr SessionData::getTemporaryKey(TemporaryKeyType type) const {
102 QMutexLocker lock(&_ownerMutex);
103 return _owner ? _owner->getTemporaryKey(type) : nullptr;
104 }
105
getPersistentKey() const106 AuthKeyPtr SessionData::getPersistentKey() const {
107 QMutexLocker lock(&_ownerMutex);
108 return _owner ? _owner->getPersistentKey() : nullptr;
109 }
110
acquireKeyCreation(DcType type)111 CreatingKeyType SessionData::acquireKeyCreation(DcType type) {
112 QMutexLocker lock(&_ownerMutex);
113 return _owner ? _owner->acquireKeyCreation(type) : CreatingKeyType::None;
114 }
115
releaseKeyCreationOnDone(const AuthKeyPtr & temporaryKey,const AuthKeyPtr & persistentKeyUsedForBind)116 bool SessionData::releaseKeyCreationOnDone(
117 const AuthKeyPtr &temporaryKey,
118 const AuthKeyPtr &persistentKeyUsedForBind) {
119 QMutexLocker lock(&_ownerMutex);
120 return _owner
121 ? _owner->releaseKeyCreationOnDone(
122 temporaryKey,
123 persistentKeyUsedForBind)
124 : false;
125 }
126
releaseCdnKeyCreationOnDone(const AuthKeyPtr & temporaryKey)127 bool SessionData::releaseCdnKeyCreationOnDone(
128 const AuthKeyPtr &temporaryKey) {
129 QMutexLocker lock(&_ownerMutex);
130 return _owner
131 ? _owner->releaseCdnKeyCreationOnDone(temporaryKey)
132 : false;
133 }
134
releaseKeyCreationOnFail()135 void SessionData::releaseKeyCreationOnFail() {
136 QMutexLocker lock(&_ownerMutex);
137 if (_owner) {
138 _owner->releaseKeyCreationOnFail();
139 }
140 }
141
destroyTemporaryKey(uint64 keyId)142 void SessionData::destroyTemporaryKey(uint64 keyId) {
143 QMutexLocker lock(&_ownerMutex);
144 if (_owner) {
145 _owner->destroyTemporaryKey(keyId);
146 }
147 }
148
detach()149 void SessionData::detach() {
150 QMutexLocker lock(&_ownerMutex);
151 _owner = nullptr;
152 }
153
Session(not_null<Instance * > instance,not_null<QThread * > thread,ShiftedDcId shiftedDcId,not_null<Dcenter * > dc)154 Session::Session(
155 not_null<Instance*> instance,
156 not_null<QThread*> thread,
157 ShiftedDcId shiftedDcId,
158 not_null<Dcenter*> dc)
159 : _instance(instance)
160 , _shiftedDcId(shiftedDcId)
161 , _dc(dc)
162 , _data(std::make_shared<SessionData>(this))
163 , _thread(thread)
164 , _sender([=] { needToResumeAndSend(); }) {
165 refreshOptions();
166 watchDcKeyChanges();
167 watchDcOptionsChanges();
168 start();
169 }
170
~Session()171 Session::~Session() {
172 Expects(!_private);
173
174 if (_myKeyCreation != CreatingKeyType::None) {
175 releaseKeyCreationOnFail();
176 }
177 }
178
watchDcKeyChanges()179 void Session::watchDcKeyChanges() {
180 _instance->dcTemporaryKeyChanged(
181 ) | rpl::filter([=](DcId dcId) {
182 return (dcId == _shiftedDcId) || (dcId == BareDcId(_shiftedDcId));
183 }) | rpl::start_with_next([=] {
184 DEBUG_LOG(("AuthKey Info: dcTemporaryKeyChanged in Session %1"
185 ).arg(_shiftedDcId));
186 if (const auto captured = _private) {
187 InvokeQueued(captured, [=] {
188 DEBUG_LOG(("AuthKey Info: calling Connection::updateAuthKey in Session %1"
189 ).arg(_shiftedDcId));
190 captured->updateAuthKey();
191 });
192 }
193 }, _lifetime);
194 }
195
watchDcOptionsChanges()196 void Session::watchDcOptionsChanges() {
197 _instance->dcOptions().changed(
198 ) | rpl::filter([=](DcId dcId) {
199 return (BareDcId(_shiftedDcId) == dcId) && (_private != nullptr);
200 }) | rpl::start_with_next([=] {
201 InvokeQueued(_private, [captured = _private] {
202 captured->dcOptionsChanged();
203 });
204 }, _lifetime);
205
206 _instance->dcOptions().cdnConfigChanged(
207 ) | rpl::filter([=] {
208 return (_private != nullptr)
209 && (_instance->dcOptions().dcType(_shiftedDcId) == DcType::Cdn);
210 }) | rpl::start_with_next([=] {
211 InvokeQueued(_private, [captured = _private] {
212 captured->cdnConfigChanged();
213 });
214 }, _lifetime);
215 }
216
start()217 void Session::start() {
218 killConnection();
219 _private = new SessionPrivate(
220 _instance,
221 _thread.get(),
222 _data,
223 _shiftedDcId);
224 }
225
restart()226 void Session::restart() {
227 if (_killed) {
228 DEBUG_LOG(("Session Error: can't restart a killed session"));
229 return;
230 }
231 refreshOptions();
232 if (const auto captured = _private) {
233 InvokeQueued(captured, [=] {
234 captured->restartNow();
235 });
236 }
237 }
238
refreshOptions()239 void Session::refreshOptions() {
240 auto &settings = Core::App().settings().proxy();
241 const auto &proxy = settings.selected();
242 const auto isEnabled = settings.isEnabled();
243 const auto proxyType = (isEnabled ? proxy.type : ProxyData::Type::None);
244 const auto useTcp = (proxyType != ProxyData::Type::Http);
245 const auto useHttp = (proxyType != ProxyData::Type::Mtproto);
246 const auto useIPv4 = true;
247 const auto useIPv6 = settings.tryIPv6();
248 _data->setOptions(SessionOptions(
249 _instance->systemLangCode(),
250 _instance->cloudLangCode(),
251 _instance->langPackName(),
252 (isEnabled ? proxy : ProxyData()),
253 useIPv4,
254 useIPv6,
255 useHttp,
256 useTcp));
257 }
258
reInitConnection()259 void Session::reInitConnection() {
260 _dc->setConnectionInited(false);
261 restart();
262 }
263
stop()264 void Session::stop() {
265 if (_killed) {
266 DEBUG_LOG(("Session Error: can't stop a killed session"));
267 return;
268 }
269 DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(_shiftedDcId));
270 killConnection();
271 }
272
kill()273 void Session::kill() {
274 stop();
275 _killed = true;
276 _data->detach();
277 DEBUG_LOG(("Session Info: marked session dcWithShift %1 as killed").arg(_shiftedDcId));
278 }
279
unpaused()280 void Session::unpaused() {
281 if (_needToReceive) {
282 _needToReceive = false;
283 InvokeQueued(this, [=] {
284 tryToReceive();
285 });
286 }
287 }
288
sendAnything(crl::time msCanWait)289 void Session::sendAnything(crl::time msCanWait) {
290 if (_killed) {
291 DEBUG_LOG(("Session Error: can't send anything in a killed session"));
292 return;
293 }
294 const auto ms = crl::now();
295 if (_msSendCall) {
296 if (ms > _msSendCall + _msWait) {
297 _msWait = 0;
298 } else {
299 _msWait = (_msSendCall + _msWait) - ms;
300 if (_msWait > msCanWait) {
301 _msWait = msCanWait;
302 }
303 }
304 } else {
305 _msWait = msCanWait;
306 }
307 if (_msWait) {
308 DEBUG_LOG(("MTP Info: dcWithShift %1 can wait for %2ms from current %3").arg(_shiftedDcId).arg(_msWait).arg(_msSendCall));
309 _msSendCall = ms;
310 _sender.callOnce(_msWait);
311 } else {
312 DEBUG_LOG(("MTP Info: dcWithShift %1 stopped send timer, can wait for %2ms from current %3").arg(_shiftedDcId).arg(_msWait).arg(_msSendCall));
313 _sender.cancel();
314 _msSendCall = 0;
315 needToResumeAndSend();
316 }
317 }
318
needToResumeAndSend()319 void Session::needToResumeAndSend() {
320 if (_killed) {
321 DEBUG_LOG(("Session Info: can't resume a killed session"));
322 return;
323 }
324 if (!_private) {
325 DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(_shiftedDcId));
326 start();
327 }
328 const auto captured = _private;
329 const auto ping = base::take(_ping);
330 InvokeQueued(captured, [=] {
331 if (ping) {
332 captured->sendPingForce();
333 } else {
334 captured->tryToSend();
335 }
336 });
337 }
338
connectionStateChange(int newState)339 void Session::connectionStateChange(int newState) {
340 _instance->onStateChange(_shiftedDcId, newState);
341 }
342
resetDone()343 void Session::resetDone() {
344 _instance->onSessionReset(_shiftedDcId);
345 }
346
cancel(mtpRequestId requestId,mtpMsgId msgId)347 void Session::cancel(mtpRequestId requestId, mtpMsgId msgId) {
348 if (requestId) {
349 QWriteLocker locker(_data->toSendMutex());
350 _data->toSendMap().remove(requestId);
351 }
352 if (msgId) {
353 QWriteLocker locker(_data->haveSentMutex());
354 _data->haveSentMap().remove(msgId);
355 }
356 }
357
ping()358 void Session::ping() {
359 _ping = true;
360 sendAnything();
361 }
362
requestState(mtpRequestId requestId) const363 int32 Session::requestState(mtpRequestId requestId) const {
364 int32 result = MTP::RequestSent;
365
366 bool connected = false;
367 if (_private) {
368 const auto s = _private->getState();
369 if (s == ConnectedState) {
370 connected = true;
371 } else if (s == ConnectingState || s == DisconnectedState) {
372 if (result < 0 || result == MTP::RequestSent) {
373 result = MTP::RequestConnecting;
374 }
375 } else if (s < 0) {
376 if ((result < 0 && s > result) || result == MTP::RequestSent) {
377 result = s;
378 }
379 }
380 }
381 if (!connected) {
382 return result;
383 } else if (!requestId) {
384 return MTP::RequestSent;
385 }
386
387 QWriteLocker locker(_data->toSendMutex());
388 return _data->toSendMap().contains(requestId)
389 ? MTP::RequestSending
390 : MTP::RequestSent;
391 }
392
getState() const393 int32 Session::getState() const {
394 int32 result = -86400000;
395
396 if (_private) {
397 const auto s = _private->getState();
398 if (s == ConnectedState) {
399 return s;
400 } else if (s == ConnectingState || s == DisconnectedState) {
401 if (result < 0) {
402 return s;
403 }
404 } else if (s < 0) {
405 if (result < 0 && s > result) {
406 result = s;
407 }
408 }
409 }
410 if (result == -86400000) {
411 result = DisconnectedState;
412 }
413 return result;
414 }
415
transport() const416 QString Session::transport() const {
417 return _private ? _private->transport() : QString();
418 }
419
sendPrepared(const SerializedRequest & request,crl::time msCanWait)420 void Session::sendPrepared(
421 const SerializedRequest &request,
422 crl::time msCanWait) {
423 DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1"
424 ).arg(msCanWait));
425 {
426 QWriteLocker locker(_data->toSendMutex());
427 _data->toSendMap().emplace(request->requestId, request);
428 *(mtpMsgId*)(request->data() + 4) = 0;
429 *(request->data() + 6) = 0;
430 }
431
432 DEBUG_LOG(("MTP Info: added, requestId %1").arg(request->requestId));
433 if (msCanWait >= 0) {
434 InvokeQueued(this, [=] {
435 sendAnything(msCanWait);
436 });
437 }
438 }
439
acquireKeyCreation(DcType type)440 CreatingKeyType Session::acquireKeyCreation(DcType type) {
441 Expects(_myKeyCreation == CreatingKeyType::None);
442
443 _myKeyCreation = _dc->acquireKeyCreation(type);
444 return _myKeyCreation;
445 }
446
releaseKeyCreationOnDone(const AuthKeyPtr & temporaryKey,const AuthKeyPtr & persistentKeyUsedForBind)447 bool Session::releaseKeyCreationOnDone(
448 const AuthKeyPtr &temporaryKey,
449 const AuthKeyPtr &persistentKeyUsedForBind) {
450 Expects(_myKeyCreation != CreatingKeyType::None);
451 Expects(persistentKeyUsedForBind != nullptr);
452
453 return releaseGenericKeyCreationOnDone(
454 temporaryKey,
455 persistentKeyUsedForBind);
456 }
457
releaseCdnKeyCreationOnDone(const AuthKeyPtr & temporaryKey)458 bool Session::releaseCdnKeyCreationOnDone(
459 const AuthKeyPtr &temporaryKey) {
460 Expects(_myKeyCreation == CreatingKeyType::TemporaryRegular);
461
462 return releaseGenericKeyCreationOnDone(temporaryKey, nullptr);
463 }
464
releaseGenericKeyCreationOnDone(const AuthKeyPtr & temporaryKey,const AuthKeyPtr & persistentKeyUsedForBind)465 bool Session::releaseGenericKeyCreationOnDone(
466 const AuthKeyPtr &temporaryKey,
467 const AuthKeyPtr &persistentKeyUsedForBind) {
468 const auto wasKeyCreation = std::exchange(
469 _myKeyCreation,
470 CreatingKeyType::None);
471 const auto result = _dc->releaseKeyCreationOnDone(
472 wasKeyCreation,
473 temporaryKey,
474 persistentKeyUsedForBind);
475
476 if (!result) {
477 DEBUG_LOG(("AuthKey Info: Persistent key changed "
478 "while binding temporary, dcWithShift %1"
479 ).arg(_shiftedDcId));
480 return false;
481 }
482
483 DEBUG_LOG(("AuthKey Info: Session key bound, setting, dcWithShift %1"
484 ).arg(_shiftedDcId));
485
486 const auto dcId = _dc->id();
487 const auto instance = _instance;
488 InvokeQueued(instance, [=] {
489 if (wasKeyCreation == CreatingKeyType::Persistent) {
490 instance->dcPersistentKeyChanged(dcId, persistentKeyUsedForBind);
491 } else {
492 instance->dcTemporaryKeyChanged(dcId);
493 }
494 });
495 return true;
496 }
497
releaseKeyCreationOnFail()498 void Session::releaseKeyCreationOnFail() {
499 Expects(_myKeyCreation != CreatingKeyType::None);
500
501 const auto wasKeyCreation = std::exchange(
502 _myKeyCreation,
503 CreatingKeyType::None);
504 _dc->releaseKeyCreationOnFail(wasKeyCreation);
505 }
506
notifyDcConnectionInited()507 void Session::notifyDcConnectionInited() {
508 DEBUG_LOG(("MTP Info: MTProtoDC::connectionWasInited(), dcWithShift %1"
509 ).arg(_shiftedDcId));
510 _dc->setConnectionInited();
511 }
512
destroyTemporaryKey(uint64 keyId)513 void Session::destroyTemporaryKey(uint64 keyId) {
514 if (!_dc->destroyTemporaryKey(keyId)) {
515 return;
516 }
517 const auto dcId = _dc->id();
518 const auto instance = _instance;
519 InvokeQueued(instance, [=] {
520 instance->dcTemporaryKeyChanged(dcId);
521 });
522 }
523
getDcWithShift() const524 int32 Session::getDcWithShift() const {
525 return _shiftedDcId;
526 }
527
getTemporaryKey(TemporaryKeyType type) const528 AuthKeyPtr Session::getTemporaryKey(TemporaryKeyType type) const {
529 return _dc->getTemporaryKey(type);
530 }
531
getPersistentKey() const532 AuthKeyPtr Session::getPersistentKey() const {
533 return _dc->getPersistentKey();
534 }
535
connectionInited() const536 bool Session::connectionInited() const {
537 return _dc->connectionInited();
538 }
539
tryToReceive()540 void Session::tryToReceive() {
541 if (_killed) {
542 DEBUG_LOG(("Session Error: can't receive in a killed session"));
543 return;
544 }
545 if (paused()) {
546 _needToReceive = true;
547 return;
548 }
549 while (true) {
550 auto lock = QWriteLocker(_data->haveReceivedMutex());
551 const auto messages = base::take(_data->haveReceivedMessages());
552 lock.unlock();
553 if (messages.empty()) {
554 break;
555 }
556 for (const auto &message : messages) {
557 if (message.requestId) {
558 _instance->processCallback(message);
559 } else if (_shiftedDcId == BareDcId(_shiftedDcId)) {
560 // Process updates only in main session.
561 _instance->processUpdate(message);
562 }
563 }
564 }
565 }
566
killConnection()567 void Session::killConnection() {
568 if (!_private) {
569 return;
570 }
571
572 base::take(_private)->deleteLater();
573
574 Ensures(_private == nullptr);
575 }
576
577 } // namespace details
578 } // namespace MTP
579