diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index f4d6141ef..eecbc9d64 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -1362,7 +1362,7 @@ btnInfoClose: flatButton(aboutCloseButton) { emojiTextFont: font(16px); emojiReplaceWidth: 56px; emojiReplaceHeight: 56px; -emojiReplaceInnerHeight: 38px; +emojiReplaceInnerHeight: 42px; connectingBG: #fffe; connectingColor: #777; @@ -1459,40 +1459,37 @@ emojiScroll: flatScroll(scrollDef) { topsh: 0px; bottomsh: 0px; } -emojiRecent: sprite(6px, 197px, 20px, 20px); -emojiRecentOver: sprite(290px, 221px, 20px, 20px); -emojiRecentActive: sprite(290px, 242px, 20px, 20px); -emojiPeople: sprite(27px, 197px, 20px, 20px); -emojiPeopleOver: sprite(311px, 221px, 20px, 20px); -emojiPeopleActive: sprite(311px, 242px, 20px, 20px); -emojiNature: sprite(48px, 197px, 20px, 20px); -emojiNatureOver: sprite(245px, 266px, 20px, 20px); -emojiNatureActive: sprite(245px, 287px, 20px, 20px); -emojiFood: sprite(69px, 197px, 20px, 20px); -emojiFoodOver: sprite(266px, 266px, 20px, 20px); -emojiFoodActive: sprite(266px, 287px, 20px, 20px); -emojiCelebration: sprite(90px, 197px, 20px, 20px); -emojiCelebrationOver: sprite(290px, 266px, 20px, 20px); -emojiCelebrationActive: sprite(290px, 287px, 20px, 20px); -emojiActivity: sprite(111px, 197px, 20px, 20px); -emojiActivityOver: sprite(311px, 266px, 20px, 20px); -emojiActivityActive: sprite(311px, 287px, 20px, 20px); -emojiTravel: sprite(132px, 197px, 20px, 20px); -emojiTravelOver: sprite(321px, 344px, 20px, 20px); -emojiTravelActive: sprite(321px, 365px, 20px, 20px); -emojiObjects: sprite(153px, 197px, 20px, 20px); -emojiObjectsOver: sprite(342px, 344px, 20px, 20px); -emojiObjectsActive: sprite(342px, 365px, 20px, 20px); -emojiStickers: sprite(174px, 197px, 20px, 20px); -emojiStickersOver: sprite(363px, 344px, 20px, 20px); -emojiStickersActive: sprite(363px, 365px, 20px, 20px); +emojiRecent: sprite(0px, 196px, 21px, 22px); +emojiRecentOver: sprite(287px, 220px, 21px, 22px); +emojiRecentActive: sprite(287px, 242px, 21px, 22px); +emojiPeople: sprite(21px, 196px, 21px, 22px); +emojiPeopleOver: sprite(298px, 220px, 21px, 22px); +emojiPeopleActive: sprite(298px, 242px, 21px, 22px); +emojiNature: sprite(42px, 196px, 21px, 22px); +emojiNatureOver: sprite(245px, 264px, 21px, 22px); +emojiNatureActive: sprite(245px, 286px, 21px, 22px); +emojiFood: sprite(63px, 196px, 21px, 22px); +emojiFoodOver: sprite(266px, 264px, 21px, 22px); +emojiFoodActive: sprite(266px, 286px, 21px, 22px); +emojiCelebration: sprite(84px, 196px, 21px, 22px); +emojiCelebrationOver: sprite(287px, 264px, 21px, 22px); +emojiCelebrationActive: sprite(287px, 286px, 21px, 22px); +emojiActivity: sprite(105px, 196px, 21px, 22px); +emojiActivityOver: sprite(308px, 264px, 21px, 22px); +emojiActivityActive: sprite(308px, 286px, 21px, 22px); +emojiTravel: sprite(126px, 196px, 21px, 22px); +emojiTravelOver: sprite(321px, 344px, 21px, 22px); +emojiTravelActive: sprite(321px, 366px, 21px, 22px); +emojiObjects: sprite(147px, 196px, 21px, 22px); +emojiObjectsOver: sprite(342px, 344px, 21px, 22px); +emojiObjectsActive: sprite(342px, 366px, 21px, 22px); rbEmoji: flatCheckbox { textColor: transparent; bgColor: transparent; disColor: transparent; - width: 28px; - height: 36px; + width: 36px; + height: 46px; textTop: 0px; textLeft: 0px; @@ -1502,7 +1499,7 @@ rbEmoji: flatCheckbox { cursor: cursor(pointer); disabledCursor: cursor(default); - imagePos: point(5px, 8px); + imagePos: point(8px, 12px); } rbEmojiRecent: flatCheckbox(rbEmoji) { imageRect: emojiRecent; @@ -1568,16 +1565,8 @@ rbEmojiObjects: flatCheckbox(rbEmoji) { disImageRect: emojiObjects; chkDisImageRect: emojiObjectsActive; } -rbEmojiStickers: flatCheckbox(rbEmojiRecent) { - imageRect: emojiStickers; - chkImageRect: emojiStickersActive; - overImageRect: emojiStickersOver; - chkOverImageRect: emojiStickersActive; - disImageRect: emojiStickers; - chkDisImageRect: emojiStickersActive; -} emojiPanPadding: margins(5px, 0px, 0px, 5px); -emojiPanSize: size(35px, 35px); +emojiPanSize: size(39px, 35px); emojiPanDuration: 200; emojiPanHover: #f0f0f0; emojiPanRound: 2px; @@ -1593,6 +1582,8 @@ emojiColorsPadding: 5px; emojiColorsSep: 1px; emojiColorsSepColor: #d5d5d5; +toStickersImg: sprite(); +toEmojiImg: sprite(); stickerPanRound: 3px; stickerPanPadding: 2px; stickerPanDelete: sprite(123px, 132px, 12px, 12px); diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 39ef10656..824f52389 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 5f8e42002..dd6970979 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/boxes/emojibox.cpp b/Telegram/SourceFiles/boxes/emojibox.cpp index 747c4b135..13d894e91 100644 --- a/Telegram/SourceFiles/boxes/emojibox.cpp +++ b/Telegram/SourceFiles/boxes/emojibox.cpp @@ -68,12 +68,12 @@ namespace { const uint32 replacesCount = sizeof(replaces) / sizeof(EmojiReplace), replacesInRow = 7; } -EmojiBox::EmojiBox() : _done(this, lang(lng_about_done), st::aboutCloseButton) { +EmojiBox::EmojiBox() : _esize(EmojiSizes[EIndex + 1]), _done(this, lang(lng_about_done), st::aboutCloseButton) { fillBlocks(); _blockHeight = st::emojiReplaceInnerHeight; - resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - st::emojiSize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height()); + resizeMaxHeight(_blocks[0].size() * st::emojiReplaceWidth + (st::emojiReplaceWidth - _esize), st::boxPadding.top() + st::boxFont->height + _blocks.size() * st::emojiReplaceHeight + (st::emojiReplaceHeight - _blockHeight) + _done.height()); connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); @@ -139,7 +139,7 @@ void EmojiBox::paintEvent(QPaintEvent *e) { int32 rowSize = i->size(), left = (width() - rowSize * st::emojiReplaceWidth) / 2; for (BlockRow::const_iterator j = i->cbegin(), en = i->cend(); j != en; ++j) { if (j->emoji) { - emojiDraw(p, j->emoji, left + (st::emojiReplaceWidth - st::emojiSize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2); + p.drawPixmap(QPoint(left + (st::emojiReplaceWidth - _esize) / 2, top + (st::emojiReplaceHeight - _blockHeight) / 2), App::emojisLarge(), QRect(j->emoji->x * _esize, j->emoji->y * _esize, _esize, _esize)); } QRect trect(left, top + (st::emojiReplaceHeight + _blockHeight) / 2 - st::emojiTextFont->height, st::emojiReplaceWidth, st::emojiTextFont->height); p.drawText(trect, j->text, QTextOption(Qt::AlignHCenter | Qt::AlignTop)); diff --git a/Telegram/SourceFiles/boxes/emojibox.h b/Telegram/SourceFiles/boxes/emojibox.h index f3c4f90b0..a20a4ac32 100644 --- a/Telegram/SourceFiles/boxes/emojibox.h +++ b/Telegram/SourceFiles/boxes/emojibox.h @@ -38,6 +38,7 @@ private: void fillBlocks(); + int32 _esize; BottomButton _done; int32 _blockHeight; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 9d31b53aa..c10cea049 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -4879,7 +4879,7 @@ void HistoryMessage::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) lnk = _from->lnk; return; } -// width -= st::msgPhotoSkip; + // width -= st::msgPhotoSkip; left += st::msgPhotoSkip; } if (width < 1) return; @@ -4900,6 +4900,10 @@ void HistoryMessage::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) } r.setTop(r.top() + st::msgNameFont->height); } + return getStateFromMessageText(lnk, inText, x, y, r); +} + +void HistoryMessage::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const { QRect trect(r.marginsAdded(-st::msgPadding)); TextLinkPtr medialnk; if (_media) { @@ -5056,12 +5060,10 @@ void HistoryForwarded::drawForwardedFrom(QPainter &p, int32 x, int32 y, int32 w, } void HistoryForwarded::drawMessageText(QPainter &p, const QRect &trect, uint32 selection) const { - int32 h = st::msgServiceNameFont->height; - drawForwardedFrom(p, trect.x(), trect.y(), trect.width(), (selection == FullItemSel)); QRect realtrect(trect); - realtrect.setY(trect.y() + h); + realtrect.setY(trect.y() + st::msgServiceNameFont->height); HistoryMessage::drawMessageText(p, realtrect, selection); } @@ -5139,6 +5141,12 @@ void HistoryForwarded::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y return HistoryMessage::getState(lnk, inText, x, y); } +void HistoryForwarded::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const { + QRect realr(r); + realr.setHeight(r.height() - st::msgServiceNameFont->height); + HistoryMessage::getStateFromMessageText(lnk, inText, x, y, realr); +} + void HistoryForwarded::getForwardedState(TextLinkPtr &lnk, bool &inText, int32 x, int32 w) const { inText = false; if (x >= fromWidth && x < w && x < fromWidth + fwdFromName.maxWidth()) { @@ -5445,6 +5453,14 @@ void HistoryReply::getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) co return HistoryMessage::getState(lnk, inText, x, y); } +void HistoryReply::getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const { + int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); + + QRect realr(r); + realr.setHeight(r.height() - h); + HistoryMessage::getStateFromMessageText(lnk, inText, x, y, realr); +} + void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const { symbol = 0; after = false; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 33049b826..e82d93c47 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -1238,7 +1238,10 @@ public: int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0); bool hasPoint(int32 x, int32 y) const; + void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const; + virtual void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const; + void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const; uint32 adjustSelection(uint16 from, uint16 to, TextSelectType type) const { return _text.adjustSelection(from, to, type); @@ -1311,6 +1314,7 @@ public: int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0); bool hasPoint(int32 x, int32 y) const; void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const; + void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const; void getForwardedState(TextLinkPtr &lnk, bool &inText, int32 x, int32 w) const; void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const; @@ -1363,6 +1367,7 @@ public: int32 resize(int32 width, bool dontRecountText = false, const HistoryItem *parent = 0); bool hasPoint(int32 x, int32 y) const; void getState(TextLinkPtr &lnk, bool &inText, int32 x, int32 y) const; + void getStateFromMessageText(TextLinkPtr &lnk, bool &inText, int32 x, int32 y, const QRect &r) const; void getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, int32 y) const; UserData *replyTo() const { diff --git a/Telegram/SourceFiles/intro/introcode.cpp b/Telegram/SourceFiles/intro/introcode.cpp index 4f64a2c4b..f34631f9c 100644 --- a/Telegram/SourceFiles/intro/introcode.cpp +++ b/Telegram/SourceFiles/intro/introcode.cpp @@ -153,6 +153,7 @@ void IntroCode::activate() { callTimer.start(1000); error = ""; errorAlpha = anim::fvalue(0); + sentCode = QString(); show(); code.setDisabled(false); code.setFocus(); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index b11f1166c..55cd0751e 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -1077,10 +1077,17 @@ namespace { LOG(("App Info: reading old user config..")); qint32 version = 0; - mtpDcOptions dcOpts(cDcOptions()); + mtpDcOptions dcOpts; + { + QReadLocker lock(MTP::dcOptionsMutex()); + dcOpts = cDcOptions(); + } _dcOpts = &dcOpts; _readOldUserSettingsFields(&file, version); - cSetDcOptions(dcOpts); + { + QWriteLocker lock(MTP::dcOptionsMutex()); + cSetDcOptions(dcOpts); + } file.close(); result = true; @@ -1157,10 +1164,17 @@ namespace { LOG(("App Info: reading old keys..")); qint32 version = 0; - mtpDcOptions dcOpts(cDcOptions()); + mtpDcOptions dcOpts; + { + QReadLocker lock(MTP::dcOptionsMutex()); + dcOpts = cDcOptions(); + } _dcOpts = &dcOpts; _readOldMtpDataFields(&file, version); - cSetDcOptions(dcOpts); + { + QWriteLocker lock(MTP::dcOptionsMutex()); + cSetDcOptions(dcOpts); + } file.close(); result = true; @@ -1648,7 +1662,11 @@ namespace Local { LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode..")); return writeSettings(); } - mtpDcOptions dcOpts(cDcOptions()); + mtpDcOptions dcOpts; + { + QReadLocker lock(MTP::dcOptionsMutex()); + dcOpts = cDcOptions(); + } _dcOpts = &dcOpts; LOG(("App Info: reading encrypted settings..")); while (!settings.stream.atEnd()) { @@ -1669,7 +1687,10 @@ namespace Local { DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); } } - cSetDcOptions(dcOpts); + { + QWriteLocker lock(MTP::dcOptionsMutex()); + cSetDcOptions(dcOpts); + } _settingsSalt = salt; } @@ -1690,13 +1711,19 @@ namespace Local { } settings.writeData(_settingsSalt); - mtpDcOptions dcOpts(cDcOptions()); + mtpDcOptions dcOpts; + { + QReadLocker lock(MTP::dcOptionsMutex()); + dcOpts = cDcOptions(); + } if (dcOpts.isEmpty()) { const BuiltInDc *bdcs = builtInDcs(); for (int i = 0, l = builtInDcsCount(); i < l; ++i) { dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port)); DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); } + + QWriteLocker lock(MTP::dcOptionsMutex()); cSetDcOptions(dcOpts); } diff --git a/Telegram/SourceFiles/mtproto/mtp.cpp b/Telegram/SourceFiles/mtproto/mtp.cpp index d2553c4f9..4f4be3b1c 100644 --- a/Telegram/SourceFiles/mtproto/mtp.cpp +++ b/Telegram/SourceFiles/mtproto/mtp.cpp @@ -23,13 +23,14 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org namespace { typedef QMap Sessions; Sessions sessions; + QVector sessionsToKill; MTProtoSessionPtr mainSession; - typedef QMap RequestsByDC; // holds dc for request to this dc or -dc for request to main dc + typedef QMap RequestsByDC; // holds dcWithShift for request to this dc or -dc for request to main dc RequestsByDC requestsByDC; QMutex requestByDCLock; - typedef QMap AuthExportRequests; // holds target dc for auth export request + typedef QMap AuthExportRequests; // holds target dcWithShift for auth export request AuthExportRequests authExportRequests; bool _started = false; @@ -55,7 +56,7 @@ namespace { BadGuestDCRequests badGuestDCRequests; typedef QVector DCAuthWaiters; - typedef QMap AuthWaiters; + typedef QMap AuthWaiters; // holds request ids waiting for auth import to specific dc AuthWaiters authWaiters; QMutex toClearLock; @@ -76,12 +77,11 @@ namespace { if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc return; } - int32 newdc = i.value(); + int32 newdc = i.value() % _mtp_internal::dcShift; DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc)); DCAuthWaiters &waiters(authWaiters[newdc]); - MTProtoSessionPtr session(_mtp_internal::getSession(newdc)); if (waiters.size()) { QReadLocker locker(&requestMapLock); for (DCAuthWaiters::iterator i = waiters.begin(), e = waiters.end(); i != e; ++i) { @@ -91,6 +91,7 @@ namespace { LOG(("MTP Error: could not find request %1 for resending").arg(requestId)); continue; } + int32 dcWithShift = newdc; { RequestsByDC::iterator k = requestsByDC.find(requestId); if (k == requestsByDC.cend()) { @@ -101,11 +102,15 @@ namespace { MTP::setdc(newdc); k.value() = -newdc; } else { - k.value() = k.value() - (k.value() % _mtp_internal::dcShift) + newdc; + int32 shift = k.value() - (k.value() % _mtp_internal::dcShift); + dcWithShift += shift; + k.value() = dcWithShift; } DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(requestId).arg(k.value())); } - session->sendPrepared(j.value()); + if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift)) { + session->sendPrepared(j.value()); + } } waiters.clear(); } @@ -121,8 +126,8 @@ namespace { void exportDone(const MTPauth_ExportedAuthorization &result, mtpRequestId req) { AuthExportRequests::const_iterator i = authExportRequests.constFind(req); if (i == authExportRequests.cend()) { - LOG(("MTP Error: auth export request target dc not found, requestId: %1").arg(req)); - RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dc, request %1").arg(req))); + LOG(("MTP Error: auth export request target dcWithShift not found, requestId: %1").arg(req)); + RPCError error(rpcClientError("AUTH_IMPORT_FAIL", QString("did not find target dcWithShift, request %1").arg(req))); if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc return; } @@ -137,7 +142,7 @@ namespace { AuthExportRequests::const_iterator i = authExportRequests.constFind(req); if (i != authExportRequests.cend()) { - authWaiters[i.value()].clear(); + authWaiters[i.value() % _mtp_internal::dcShift].clear(); } if (globalHandler.onFail && MTP::authedId()) (*globalHandler.onFail)(req, error); // auth failed in main dc return true; @@ -151,31 +156,34 @@ namespace { if ((m = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$").match(err)).hasMatch()) { if (!requestId) return false; - int32 dc = 0, newdc = m.captured(2).toInt(); + int32 dcWithShift = 0, newdcWithShift = m.captured(2).toInt(); { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId); if (i == requestsByDC.end()) { - LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdc)); + LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdcWithShift)); } else { - dc = i.value(); + dcWithShift = i.value(); } } - if (!dc || !newdc) return false; + if (!dcWithShift || !newdcWithShift) return false; - DEBUG_LOG(("MTP Info: changing request %1 dc%2 to %3").arg(requestId).arg((dc > 0) ? "" : " and main dc").arg(newdc)); - if (dc < 0) { - if (MTP::authedId() && !authExportRequests.contains(requestId)) { // import auth, set dc and resend - DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdc)); - DCAuthWaiters &waiters(authWaiters[newdc]); + DEBUG_LOG(("MTP Info: changing request %1 from dcWithShift%2 to dc%3").arg(requestId).arg(dcWithShift).arg(newdcWithShift)); + if (dcWithShift < 0) { // newdc shift = 0 + if (false && MTP::authedId() && !authExportRequests.contains(requestId)) { // migrate not supported at this moment + DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdcWithShift)); + DCAuthWaiters &waiters(authWaiters[newdcWithShift]); if (!waiters.size()) { - authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc); + authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdcWithShift)), rpcDone(exportDone), rpcFail(exportFail)), newdcWithShift); } waiters.push_back(requestId); return true; } else { - MTP::setdc(newdc); + MTP::setdc(newdcWithShift); } + } else { + int32 shift = dcWithShift - (dcWithShift % _mtp_internal::dcShift); + newdcWithShift += shift; } mtpRequest req; @@ -188,8 +196,10 @@ namespace { } req = i.value(); } - _mtp_internal::registerRequest(requestId, (dc < 0) ? -newdc : newdc); - _mtp_internal::getSession(newdc)->sendPrepared(req); + if (MTProtoSessionPtr session = _mtp_internal::getSession(newdcWithShift)) { + _mtp_internal::registerRequest(requestId, (dcWithShift < 0) ? -newdcWithShift : newdcWithShift); + session->sendPrepared(req); + } return true; } else if (code < 0 || code >= 500 || (m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) { if (!requestId) return false; @@ -218,26 +228,26 @@ namespace { return true; } else if (code == 401 || (badGuestDC && badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend())) { - int32 dc = 0; + int32 dcWithShift = 0; { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId); if (i != requestsByDC.end()) { - dc = i.value(); + dcWithShift = i.value(); } else { LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId)); } } - int32 newdc = abs(dc) % _mtp_internal::dcShift; + int32 newdc = abs(dcWithShift) % _mtp_internal::dcShift; if (!newdc || newdc == mtpMainDC() || !MTP::authedId()) { if (!badGuestDC && globalHandler.onFail) (*globalHandler.onFail)(requestId, error); // auth failed in main dc return false; } - DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(dc)); + DEBUG_LOG(("MTP Info: importing auth to dcWithShift %1").arg(dcWithShift)); DCAuthWaiters &waiters(authWaiters[newdc]); if (!waiters.size()) { - authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), newdc); + authExportRequests.insert(MTP::send(MTPauth_ExportAuthorization(MTP_int(newdc)), rpcDone(exportDone), rpcFail(exportFail)), abs(dcWithShift)); } waiters.push_back(requestId); if (badGuestDC) badGuestDCRequests.insert(requestId); @@ -253,20 +263,22 @@ namespace { } req = i.value(); } - int32 dc = 0; + int32 dcWithShift = 0; { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId); if (i == requestsByDC.end()) { LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId)); } else { - dc = i.value(); + dcWithShift = i.value(); } } - if (!dc) return false; + if (!dcWithShift) return false; - req->needsLayer = true; - _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req); + if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) { + req->needsLayer = true; + session->sendPrepared(req); + } return true; } else if (err == qsl("MSG_WAIT_FAILED")) { mtpRequest req; @@ -283,7 +295,7 @@ namespace { LOG(("MTP Error: wait failed for not dependent request %1").arg(requestId)); return false; } - int32 dc = 0; + int32 dcWithShift = 0; { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId), j = requestsByDC.find(req->after->requestId); @@ -292,19 +304,21 @@ namespace { } else if (j == requestsByDC.end()) { LOG(("MTP Error: could not find dependent request %1 by dc").arg(req->after->requestId)); } else { - dc = i.value(); + dcWithShift = i.value(); if (i.value() != j.value()) { req->after = mtpRequest(); } } } - if (!dc) return false; + if (!dcWithShift) return false; if (!req->after) { - req->needsLayer = true; - _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req); + if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) { + req->needsLayer = true; + session->sendPrepared(req); + } } else { - int32 newdc = abs(dc) % _mtp_internal::dcShift; + int32 newdc = abs(dcWithShift) % _mtp_internal::dcShift; DCAuthWaiters &waiters(authWaiters[newdc]); if (waiters.indexOf(req->after->requestId) >= 0) { if (waiters.indexOf(requestId) < 0) { @@ -338,27 +352,27 @@ namespace { } namespace _mtp_internal { - MTProtoSessionPtr getSession(int32 dc) { + MTProtoSessionPtr getSession(int32 dcWithShift) { if (!_started) return MTProtoSessionPtr(); - if (!dc) return mainSession; - if (!(dc % _mtp_internal::dcShift)) { - dc += mainSession->getDC(); + if (!dcWithShift) return mainSession; + if (!(dcWithShift % _mtp_internal::dcShift)) { + dcWithShift += (mainSession->getDcWithShift() % _mtp_internal::dcShift); } - Sessions::const_iterator i = sessions.constFind(dc); + Sessions::const_iterator i = sessions.constFind(dcWithShift); if (i != sessions.cend()) return *i; MTProtoSessionPtr result(new MTProtoSession()); - result->start(dc); + result->start(dcWithShift); - sessions.insert(dc, result); + sessions.insert(dcWithShift, result); return result; } - void registerRequest(mtpRequestId requestId, int32 dc) { + void registerRequest(mtpRequestId requestId, int32 dcWithShift) { { QMutexLocker locker(&requestByDCLock); - requestsByDC.insert(requestId, dc); + requestsByDC.insert(requestId, dcWithShift); } _mtp_internal::performDelayedClear(); // need to do it somewhere.. } @@ -530,12 +544,12 @@ namespace _mtp_internal { if (globalHandler.onDone) (*globalHandler.onDone)(0, from, end); // some updates were received } - void onStateChange(int32 dc, int32 state) { - if (stateChangedHandler) stateChangedHandler(dc, state); + void onStateChange(int32 dcWithShift, int32 state) { + if (stateChangedHandler) stateChangedHandler(dcWithShift, state); } - void onSessionReset(int32 dc) { - if (sessionResetHandler) sessionResetHandler(dc); + void onSessionReset(int32 dcWithShift) { + if (sessionResetHandler) sessionResetHandler(dcWithShift); } bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data @@ -561,12 +575,12 @@ namespace _mtp_internal { mtpRequestId requestId = delayedRequests.front().first; delayedRequests.pop_front(); - int32 dc = 0; + int32 dcWithShift = 0; { QMutexLocker locker(&requestByDCLock); RequestsByDC::const_iterator i = requestsByDC.constFind(requestId); if (i != requestsByDC.cend()) { - dc = i.value(); + dcWithShift = i.value(); } else { LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId)); continue; @@ -583,7 +597,9 @@ namespace _mtp_internal { } req = j.value(); } - _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req); + if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) { + session->sendPrepared(req); + } } if (!delayedRequests.isEmpty()) { @@ -595,13 +611,15 @@ namespace _mtp_internal { namespace MTP { void start() { + if (started()) return; + unixtimeInit(); MTProtoDCMap &dcs(mtpDCMap()); mainSession = MTProtoSessionPtr(new MTProtoSession()); mainSession->start(mtpMainDC()); - sessions[mainSession->getDC()] = mainSession; + sessions[mainSession->getDcWithShift()] = mainSession; _started = true; resender = new _mtp_internal::RequestResender(); @@ -625,8 +643,9 @@ namespace MTP { void restart(int32 dcMask) { if (!_started) return; + dcMask %= _mtp_internal::dcShift; for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { - if ((*i)->getDC() % _mtp_internal::dcShift == dcMask % _mtp_internal::dcShift) { + if (((*i)->getDcWithShift() % _mtp_internal::dcShift) == dcMask) { (*i)->restart(); } } @@ -641,8 +660,9 @@ namespace MTP { void setdc(int32 dc, bool fromZeroOnly) { if (!dc || !_started) return; mtpSetDC(dc, fromZeroOnly); - if (maindc() != mainSession->getDC()) { - mainSession = _mtp_internal::getSession(maindc()); + int32 oldMainDc = mainSession->getDcWithShift(); + if (maindc() != oldMainDc) { + killSession(oldMainDc); } Local::writeMtpData(); } @@ -656,7 +676,7 @@ namespace MTP { if (!dc) return mainSession->getState(); if (!(dc % _mtp_internal::dcShift)) { - dc += mainSession->getDC(); + dc += (mainSession->getDcWithShift() % _mtp_internal::dcShift); } Sessions::const_iterator i = sessions.constFind(dc); @@ -670,7 +690,7 @@ namespace MTP { if (!dc) return mainSession->transport(); if (!(dc % _mtp_internal::dcShift)) { - dc += mainSession->getDC(); + dc += (mainSession->getDcWithShift() % _mtp_internal::dcShift); } Sessions::const_iterator i = sessions.constFind(dc); @@ -679,16 +699,10 @@ namespace MTP { return QString(); } - void initdc(int32 dc) { - if (!_started) return; - _mtp_internal::getSession(dc); - } - void ping() { - MTProtoSessionPtr session = _mtp_internal::getSession(0); - if (!session) return; - - return session->ping(); + if (MTProtoSessionPtr session = _mtp_internal::getSession(0)) { + session->ping(); + } } void cancel(mtpRequestId requestId) { @@ -706,25 +720,35 @@ namespace MTP { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId); if (i != requestsByDC.end()) { - _mtp_internal::getSession(abs(i.value()))->cancel(requestId, msgId); + if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) { + session->cancel(requestId, msgId); + } requestsByDC.erase(i); } } _mtp_internal::clearCallbacks(requestId); } + void killSessionsDelayed() { + if (!sessionsToKill.isEmpty()) { + sessionsToKill.clear(); + } + } + void killSession(int32 dc) { Sessions::iterator i = sessions.find(dc); if (i != sessions.end()) { bool wasMain = (i.value() == mainSession); - i.value()->stop(); + i.value()->kill(); + if (sessionsToKill.isEmpty()) QTimer::singleShot(0, killSessionsDelayed); + sessionsToKill.push_back(i.value()); sessions.erase(i); if (wasMain) { mainSession = MTProtoSessionPtr(new MTProtoSession()); mainSession->start(mtpMainDC()); - sessions[mainSession->getDC()] = mainSession; + sessions[mainSession->getDcWithShift()] = mainSession; } } } @@ -743,22 +767,30 @@ namespace MTP { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId); if (i != requestsByDC.end()) { - return _mtp_internal::getSession(abs(i.value()))->requestState(requestId); + if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) { + return session->requestState(requestId); + } + return MTP::RequestConnecting; } return MTP::RequestSent; } - return _mtp_internal::getSession(-requestId)->requestState(0); + if (MTProtoSessionPtr session = _mtp_internal::getSession(-requestId)) { + return session->requestState(0); + } + return MTP::RequestConnecting; } void stop() { for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) { - i.value()->stop(); + i.value()->kill(); } sessions.clear(); mainSession = MTProtoSessionPtr(); delete resender; resender = 0; mtpDestroyConfigLoader(); + + _started = false; } void authed(int32 uid) { @@ -810,4 +842,8 @@ namespace MTP { return mtpSetKey(dc, key); } + QReadWriteLock *dcOptionsMutex() { + return mtpDcOptionsMutex(); + } + }; diff --git a/Telegram/SourceFiles/mtproto/mtp.h b/Telegram/SourceFiles/mtproto/mtp.h index 1224b7ea9..bb9344657 100644 --- a/Telegram/SourceFiles/mtproto/mtp.h +++ b/Telegram/SourceFiles/mtproto/mtp.h @@ -37,8 +37,8 @@ namespace _mtp_internal { void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end); bool hasCallbacks(mtpRequestId requestId); void globalCallback(const mtpPrime *from, const mtpPrime *end); - void onStateChange(int32 dc, int32 state); - void onSessionReset(int32 dc); + void onStateChange(int32 dcWithShift, int32 state); + void onSessionReset(int32 dcWithShift); bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); // return true if need to clean request data inline bool rpcErrorOccured(mtpRequestId requestId, const RPCResponseHandler &handler, const RPCError &err) { return rpcErrorOccured(requestId, handler.onFail, err); @@ -64,6 +64,7 @@ namespace _mtp_internal { namespace MTP { static const uint32 cfg = 1 * _mtp_internal::dcShift; // send(MTPhelp_GetConfig(), MTP::cfg + dc) - for dc enum + static const uint32 lgt = 2 * _mtp_internal::dcShift; // send(MTPauth_LogOut(), MTP::lgt + dc) - for logout of guest dcs enum static const uint32 dld[MTPDownloadSessionsCount] = { // send(req, callbacks, MTP::dld[i] + dc) - for download 0x10 * _mtp_internal::dcShift, 0x11 * _mtp_internal::dcShift, @@ -89,13 +90,13 @@ namespace MTP { int32 dcstate(int32 dc = 0); QString dctransport(int32 dc = 0); - void initdc(int32 dc); + template inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) { - MTProtoSessionPtr session = _mtp_internal::getSession(dc); - if (!session) return 0; - - return session->send(request, callbacks, msCanWait, true, !dc, after); + if (MTProtoSessionPtr session = _mtp_internal::getSession(dc)) { + return session->send(request, callbacks, msCanWait, true, !dc, after); + } + return 0; } template inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) { @@ -139,6 +140,8 @@ namespace MTP { mtpKeysMap getKeys(); void setKey(int32 dc, mtpAuthKeyPtr key); + QReadWriteLock *dcOptionsMutex(); + }; #include "mtproto/mtpSessionImpl.h" diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.cpp b/Telegram/SourceFiles/mtproto/mtpConnection.cpp index e5c9910c1..57abdfa3d 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.cpp +++ b/Telegram/SourceFiles/mtproto/mtpConnection.cpp @@ -1114,6 +1114,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne // createConn(); if (!dc) { + QReadLocker lock(mtpDcOptionsMutex()); const mtpDcOptions &options(cDcOptions()); if (options.isEmpty()) { LOG(("MTP Error: connect failed, no DCs")); @@ -1713,6 +1714,8 @@ void MTProtoConnectionPrivate::retryByTimer() { } if (keyId == mtpAuthKey::RecreateKeyId) { if (sessionData->getKey()) { + unlockKey(); + QWriteLocker lock(sessionData->keyMutex()); sessionData->owner()->destroyKey(); } @@ -1738,31 +1741,34 @@ void MTProtoConnectionPrivate::socketStart(bool afterConfig) { _pingId = _pingMsgId = _pingIdToSend = _pingSendAt = 0; _pingSender.stop(); - const mtpDcOption *dcOption = 0; - const mtpDcOptions &options(cDcOptions()); - mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift); - DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc)); - if (dcIndex == options.cend()) { + std::string ip; + uint32 port = 0; + { + QReadLocker lock(mtpDcOptionsMutex()); + const mtpDcOptions &options(cDcOptions()); + mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift); + DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc)); + if (dcIndex != options.cend()) { + ip = dcIndex->ip; + port = dcIndex->port; + } + } + if (!port || ip.empty()) { if (afterConfig) { LOG(("MTP Error: DC %1 options not found right after config load!").arg(dc)); return restart(); - } else { - DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc)); - connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded())); - mtpConfigLoader()->load(); - return; } + DEBUG_LOG(("MTP Info: DC %1 options not found, waiting for config").arg(dc)); + connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded())); + mtpConfigLoader()->load(); + return; } - dcOption = &dcIndex.value(); - - const char *ip(dcOption->ip.c_str()); - uint32 port(dcOption->port); - DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip).arg(port)); + DEBUG_LOG(("MTP Info: socket connection to %1:%2..").arg(ip.c_str()).arg(port)); connect(conn, SIGNAL(connected()), this, SLOT(onConnected())); connect(conn, SIGNAL(disconnected()), this, SLOT(restart())); - conn->connectToServer(ip, port); + conn->connectToServer(ip.c_str(), port); } void MTProtoConnectionPrivate::restart(bool maybeBadKey) { @@ -2824,12 +2830,36 @@ void MTProtoConnectionPrivate::onConnected() { TCP_LOG(("Connection Info: connection succeed.")); - if (updateAuthKey()) { - DEBUG_LOG(("MTP Info: returning from socketConnected..")); - return; + updateAuthKey(); +} + +void MTProtoConnectionPrivate::updateAuthKey() { + QReadLocker lockFinished(&sessionDataMutex); + if (!sessionData || !conn) return; + + DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc)); + uint64 newKeyId = 0; + { + ReadLockerAttempt lock(sessionData->keyMutex()); + if (!lock) { + DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit")); + clearMessages(); + keyId = newKeyId; + return; // some other connection is getting key + } + const mtpAuthKeyPtr &key(sessionData->getKey()); + newKeyId = key ? key->keyId() : 0; + } + if (keyId != newKeyId) { + clearMessages(); + keyId = newKeyId; + } + DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str())); + if (keyId) { + return authKeyCreated(); } - DEBUG_LOG(("MTP Info: will be creating auth_key")); + DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key")); lockKey(); const mtpAuthKeyPtr &key(sessionData->getKey()); @@ -2854,36 +2884,6 @@ void MTProtoConnectionPrivate::onConnected() { sendRequestNotSecure(req_pq); } -bool MTProtoConnectionPrivate::updateAuthKey() { - QReadLocker lockFinished(&sessionDataMutex); - if (!sessionData || !conn) return false; - - DEBUG_LOG(("AuthKey Info: MTProtoConnection updating key from MTProtoSession, dc %1").arg(dc)); - uint64 newKeyId = 0; - { - ReadLockerAttempt lock(sessionData->keyMutex()); - if (!lock) { - DEBUG_LOG(("MTP Info: could not lock auth_key for read, waiting signal emit")); - clearMessages(); - keyId = newKeyId; - return true; // some other connection is getting key - } - const mtpAuthKeyPtr &key(sessionData->getKey()); - newKeyId = key ? key->keyId() : 0; - } - if (keyId != newKeyId) { - clearMessages(); - keyId = newKeyId; - } - DEBUG_LOG(("AuthKey Info: MTProtoConnection update key from MTProtoSession, dc %1 result: %2").arg(dc).arg(mb(&keyId, sizeof(keyId)).str())); - if (keyId) { - authKeyCreated(); - return true; - } - DEBUG_LOG(("AuthKey Info: Key update failed")); - return false; -} - void MTProtoConnectionPrivate::clearMessages() { if (keyId && keyId != mtpAuthKey::RecreateKeyId && conn) { conn->received().clear(); @@ -3481,7 +3481,14 @@ MTProtoConnectionPrivate::~MTProtoConnectionPrivate() { void MTProtoConnectionPrivate::stop() { QWriteLocker lockFinished(&sessionDataMutex); - sessionData = 0; + if (sessionData) { + if (myKeyLock) { + sessionData->owner()->notifyKeyCreated(mtpAuthKeyPtr()); // release key lock, let someone else create it + sessionData->keyMutex()->unlock(); + myKeyLock = false; + } + sessionData = 0; + } } MTProtoConnection::~MTProtoConnection() { diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.h b/Telegram/SourceFiles/mtproto/mtpConnection.h index 6402277b0..7708b771f 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.h +++ b/Telegram/SourceFiles/mtproto/mtpConnection.h @@ -366,7 +366,7 @@ public slots: // Sessions signals, when we need to send something void tryToSend(); - bool updateAuthKey(); + void updateAuthKey(); void onConfigLoaded(); diff --git a/Telegram/SourceFiles/mtproto/mtpDC.cpp b/Telegram/SourceFiles/mtproto/mtpDC.cpp index 5d9635a7b..bbd5b28f8 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.cpp +++ b/Telegram/SourceFiles/mtproto/mtpDC.cpp @@ -56,6 +56,20 @@ int32 mtpMainDC() { return mainDC; } +namespace { + QMap logoutGuestMap; // dcWithShift to logout request id + bool logoutDone(mtpRequestId req) { + for (QMap::iterator i = logoutGuestMap.begin(); i != logoutGuestMap.end(); ++i) { + if (i.value() == req) { + MTP::killSession(i.key()); + logoutGuestMap.erase(i); + return true; + } + } + return false; + } +} + void mtpLogoutOtherDCs() { QList dcs; { @@ -64,7 +78,7 @@ void mtpLogoutOtherDCs() { } for (int32 i = 0, cnt = dcs.size(); i != cnt; ++i) { if (dcs[i] != MTP::maindc()) { - MTP::send(MTPauth_LogOut(), RPCResponseHandler(), dcs[i]); + logoutGuestMap.insert(MTP::lgt + dcs[i], MTP::send(MTPauth_LogOut(), rpcDone(&logoutDone), rpcFail(&logoutDone), MTP::lgt + dcs[i])); } } } @@ -154,7 +168,11 @@ namespace { void mtpUpdateDcOptions(const QVector &options) { QSet already, restart; { - mtpDcOptions opts(cDcOptions()); + mtpDcOptions opts; + { + QReadLocker lock(mtpDcOptionsMutex()); + opts = cDcOptions(); + } for (QVector::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { const MTPDdcOption &optData(i->c_dcOption()); if (already.constFind(optData.vid.v) == already.cend()) { @@ -168,16 +186,26 @@ void mtpUpdateDcOptions(const QVector &options) { opts.insert(optData.vid.v, mtpDcOption(optData.vid.v, optData.vhostname.c_string().v, optData.vip_address.c_string().v, optData.vport.v)); } } - cSetDcOptions(opts); + { + QWriteLocker lock(mtpDcOptionsMutex()); + cSetDcOptions(opts); + } } for (QSet::const_iterator i = restart.cbegin(), e = restart.cend(); i != e; ++i) { MTP::restart(*i); } } +namespace { + QReadWriteLock _dcOptionsMutex; +} + +QReadWriteLock *mtpDcOptionsMutex() { + return &_dcOptionsMutex; +} + MTProtoConfigLoader::MTProtoConfigLoader() : _enumCurrent(0), _enumRequest(0) { connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC())); - connect(this, SIGNAL(killCurrentSession(qint32,qint32)), this, SLOT(onKillCurrentSession(qint32,qint32)), Qt::QueuedConnection); } void MTProtoConfigLoader::load() { @@ -189,23 +217,15 @@ void MTProtoConfigLoader::load() { _enumDCTimer.start(MTPEnumDCTimeout); } -void MTProtoConfigLoader::onKillCurrentSession(qint32 request, qint32 current) { - if (request == _enumRequest && current == _enumCurrent) { - if (_enumRequest) { - MTP::cancel(_enumRequest); - _enumRequest = 0; - } - if (_enumCurrent) { - MTP::killSession(MTP::cfg + _enumCurrent); - _enumCurrent = 0; - } - } -} - void MTProtoConfigLoader::done() { _enumDCTimer.stop(); - if (_enumRequest || _enumCurrent) { - emit killCurrentSession(_enumRequest, _enumCurrent); + if (_enumRequest) { + MTP::cancel(_enumRequest); + _enumRequest = 0; + } + if (_enumCurrent) { + MTP::killSession(MTP::cfg + _enumCurrent); + _enumCurrent = 0; } emit loaded(); } @@ -220,14 +240,16 @@ void MTProtoConfigLoader::enumDC() { } else { MTP::killSession(MTP::cfg + _enumCurrent); } - const mtpDcOptions &options(cDcOptions()); - for (mtpDcOptions::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { - if (i.key() == _enumCurrent) { - _enumCurrent = (++i == e) ? options.cbegin().key() : i.key(); - break; + { + QReadLocker lock(mtpDcOptionsMutex()); + const mtpDcOptions &options(cDcOptions()); + for (mtpDcOptions::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { + if (i.key() == _enumCurrent) { + _enumCurrent = (++i == e) ? options.cbegin().key() : i.key(); + break; + } } } - _enumRequest = MTP::send(MTPhelp_GetConfig(), rpcDone(configLoaded), rpcFail(configFailed), MTP::cfg + _enumCurrent); _enumDCTimer.start(MTPEnumDCTimeout); diff --git a/Telegram/SourceFiles/mtproto/mtpDC.h b/Telegram/SourceFiles/mtproto/mtpDC.h index 4a5bd2c0d..403dc8615 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.h +++ b/Telegram/SourceFiles/mtproto/mtpDC.h @@ -72,12 +72,10 @@ public: public slots: void enumDC(); - void onKillCurrentSession(qint32 request, qint32 session); signals: void loaded(); - void killCurrentSession(qint32 request, qint32 session); private: @@ -104,3 +102,4 @@ mtpKeysMap mtpGetKeys(); void mtpSetKey(int32 dc, mtpAuthKeyPtr key); void mtpUpdateDcOptions(const QVector &options); +QReadWriteLock *mtpDcOptionsMutex(); diff --git a/Telegram/SourceFiles/mtproto/mtpSession.cpp b/Telegram/SourceFiles/mtproto/mtpSession.cpp index a38e01273..89c3fa623 100644 --- a/Telegram/SourceFiles/mtproto/mtpSession.cpp +++ b/Telegram/SourceFiles/mtproto/mtpSession.cpp @@ -63,11 +63,15 @@ void MTPSessionData::clear() { } -MTProtoSession::MTProtoSession() : data(this), dcId(0), dc(0), msSendCall(0), msWait(0), _ping(false) { +MTProtoSession::MTProtoSession() : _killed(false), data(this), dcWithShift(0), dc(0), msSendCall(0), msWait(0), _ping(false) { } void MTProtoSession::start(int32 dcenter) { - if (dcId) { + if (_killed) { + DEBUG_LOG(("Session Error: can't start a killed session")); + return; + } + if (dcWithShift) { DEBUG_LOG(("Session Info: MTProtoSession::start called on already started session")); return; } @@ -84,8 +88,8 @@ void MTProtoSession::start(int32 dcenter) { connections.reserve(cConnectionsInSession()); for (uint32 i = 0; i < cConnectionsInSession(); ++i) { connections.push_back(new MTProtoConnection()); - dcId = connections.back()->start(&data, dcenter); - if (!dcId) { + dcWithShift = connections.back()->start(&data, dcenter); + if (!dcWithShift) { for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) { delete *j; } @@ -94,11 +98,12 @@ void MTProtoSession::start(int32 dcenter) { return; } if (!dc) { - dcenter = dcId; - MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId % _mtp_internal::dcShift); + dcenter = dcWithShift; + int32 dcId = dcWithShift % _mtp_internal::dcShift; + MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId); if (dcIndex == dcs.cend()) { - dc = MTProtoDCPtr(new MTProtoDC(dcId % _mtp_internal::dcShift, mtpAuthKeyPtr())); - dcs.insert(dcId % _mtp_internal::dcShift, dc); + dc = MTProtoDCPtr(new MTProtoDC(dcId, mtpAuthKeyPtr())); + dcs.insert(dcWithShift % _mtp_internal::dcShift, dc); } else { dc = dcIndex.value(); } @@ -115,18 +120,32 @@ void MTProtoSession::start(int32 dcenter) { } void MTProtoSession::restart() { + if (_killed) { + DEBUG_LOG(("Session Error: can't restart a killed session")); + return; + } emit needToRestart(); } void MTProtoSession::stop() { - DEBUG_LOG(("Session Info: stopping session %1").arg(dcId)); + DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(dcWithShift)); while (!connections.isEmpty()) { connections.back()->stop(); connections.pop_back(); } } +void MTProtoSession::kill() { + stop(); + _killed = true; + DEBUG_LOG(("Session Info: marked session dcWithShift %1 as killed").arg(dcWithShift)); +} + void MTProtoSession::sendAnything(quint64 msCanWait) { + if (_killed) { + DEBUG_LOG(("Session Error: can't send anything in a killed session")); + return; + } uint64 ms = getms(true); if (msSendCall) { if (ms > msSendCall + msWait) { @@ -141,11 +160,11 @@ void MTProtoSession::sendAnything(quint64 msCanWait) { msWait = msCanWait; } if (msWait) { - DEBUG_LOG(("MTP Info: dc %1 can wait for %2ms from current %3").arg(dcId).arg(msWait).arg(msSendCall)); + DEBUG_LOG(("MTP Info: dcWithShift %1 can wait for %2ms from current %3").arg(dcWithShift).arg(msWait).arg(msSendCall)); msSendCall = ms; sender.start(msWait); } else { - DEBUG_LOG(("MTP Info: dc %1 stopped send timer, can wait for %2ms from current %3").arg(dcId).arg(msWait).arg(msSendCall)); + DEBUG_LOG(("MTP Info: dcWithShift %1 stopped send timer, can wait for %2ms from current %3").arg(dcWithShift).arg(msWait).arg(msSendCall)); sender.stop(); msSendCall = 0; needToResumeAndSend(); @@ -153,20 +172,24 @@ void MTProtoSession::sendAnything(quint64 msCanWait) { } void MTProtoSession::needToResumeAndSend() { + if (_killed) { + DEBUG_LOG(("Session Info: can't resume a killed session")); + return; + } if (connections.isEmpty()) { - DEBUG_LOG(("Session Info: resuming session %1").arg(dcId)); + DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(dcWithShift)); MTProtoDCMap &dcs(mtpDCMap()); connections.reserve(cConnectionsInSession()); for (uint32 i = 0; i < cConnectionsInSession(); ++i) { connections.push_back(new MTProtoConnection()); - if (!connections.back()->start(&data, dcId)) { + if (!connections.back()->start(&data, dcWithShift)) { for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) { delete *j; } connections.clear(); - DEBUG_LOG(("Session Info: could not start connection %1 to dc %2").arg(i).arg(dcId)); - dcId = 0; + DEBUG_LOG(("Session Info: could not start connection %1 to dcWithShift %2").arg(i).arg(dcWithShift)); + dcWithShift = 0; return; } } @@ -259,11 +282,11 @@ void MTProtoSession::checkRequestsByTimer() { } void MTProtoSession::onConnectionStateChange(qint32 newState) { - _mtp_internal::onStateChange(dcId, newState); + _mtp_internal::onStateChange(dcWithShift, newState); } void MTProtoSession::onResetDone() { - _mtp_internal::onSessionReset(dcId); + _mtp_internal::onSessionReset(dcWithShift); } void MTProtoSession::cancel(mtpRequestId requestId, mtpMsgId msgId) { @@ -428,23 +451,23 @@ QReadWriteLock *MTProtoSession::keyMutex() const { } void MTProtoSession::authKeyCreatedForDC() { - DEBUG_LOG(("AuthKey Info: MTProtoSession::authKeyCreatedForDC slot, emitting authKeyCreated(), dc %1").arg(dcId)); + DEBUG_LOG(("AuthKey Info: MTProtoSession::authKeyCreatedForDC slot, emitting authKeyCreated(), dcWithShift %1").arg(dcWithShift)); data.setKey(dc->getKey()); emit authKeyCreated(); } void MTProtoSession::notifyKeyCreated(const mtpAuthKeyPtr &key) { - DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dc %1").arg(dcId)); + DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dcWithShift %1").arg(dcWithShift)); dc->setKey(key); } void MTProtoSession::layerWasInitedForDC(bool wasInited) { - DEBUG_LOG(("MTP Info: MTProtoSession::layerWasInitedForDC slot, dc %1").arg(dcId)); + DEBUG_LOG(("MTP Info: MTProtoSession::layerWasInitedForDC slot, dcWithShift %1").arg(dcWithShift)); data.setLayerWasInited(wasInited); } void MTProtoSession::notifyLayerInited(bool wasInited) { - DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dc %2").arg(logBool(wasInited)).arg(dcId)); + DEBUG_LOG(("MTP Info: emitting MTProtoDC::layerWasInited(%1), dcWithShift %2").arg(logBool(wasInited)).arg(dcWithShift)); dc->setConnectionInited(wasInited); emit dc->layerWasInited(wasInited); } @@ -453,7 +476,7 @@ void MTProtoSession::destroyKey() { if (!dc) return; if (data.getKey()) { - DEBUG_LOG(("MTP Info: destroying auth_key for dc %1").arg(dcId)); + DEBUG_LOG(("MTP Info: destroying auth_key for dcWithShift %1").arg(dcWithShift)); if (data.getKey() == dc->getKey()) { dc->destroyKey(); } @@ -461,8 +484,8 @@ void MTProtoSession::destroyKey() { } } -int32 MTProtoSession::getDC() const { - return dcId; +int32 MTProtoSession::getDcWithShift() const { + return dcWithShift; } void MTProtoSession::tryToReceive() { @@ -481,7 +504,7 @@ void MTProtoSession::tryToReceive() { responses.erase(i); } if (requestId <= 0) { - if (dcId < int(_mtp_internal::dcShift)) { // call globalCallback only in main session + if (dcWithShift < int(_mtp_internal::dcShift)) { // call globalCallback only in main session _mtp_internal::globalCallback(response.constData(), response.constData() + response.size()); } } else { diff --git a/Telegram/SourceFiles/mtproto/mtpSession.h b/Telegram/SourceFiles/mtproto/mtpSession.h index 7e0145d71..3743f247d 100644 --- a/Telegram/SourceFiles/mtproto/mtpSession.h +++ b/Telegram/SourceFiles/mtproto/mtpSession.h @@ -224,8 +224,9 @@ public: void start(int32 dcenter = 0); void restart(); void stop(); + void kill(); - int32 getDC() const; + int32 getDcWithShift() const; ~MTProtoSession(); QReadWriteLock *keyMutex() const; @@ -275,10 +276,12 @@ private: typedef QList MTProtoConnections; MTProtoConnections connections; + + bool _killed; MTPSessionData data; - int32 dcId; + int32 dcWithShift; MTProtoDCPtr dc; uint64 msSendCall, msWait; diff --git a/Telegram/SourceFiles/mtproto/mtpSessionImpl.h b/Telegram/SourceFiles/mtproto/mtpSessionImpl.h index 79e519e15..033cab32d 100644 --- a/Telegram/SourceFiles/mtproto/mtpSessionImpl.h +++ b/Telegram/SourceFiles/mtproto/mtpSessionImpl.h @@ -37,6 +37,6 @@ mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler ca requestId = 0; _mtp_internal::rpcErrorOccured(requestId, callbacks, rpcClientError("NO_REQUEST_ID", QString("send() failed to queue request, exception: %1").arg(e.what()))); } - if (requestId) _mtp_internal::registerRequest(requestId, toMainDC ? -getDC() : getDC()); + if (requestId) _mtp_internal::registerRequest(requestId, toMainDC ? -getDcWithShift() : getDcWithShift()); return requestId; }