diff --git a/Telegram/DeployLinux.sh b/Telegram/DeployLinux.sh index 71f022d5dcf7d4..2defda27576d89 100755 --- a/Telegram/DeployLinux.sh +++ b/Telegram/DeployLinux.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tlinuxupd$AppVersion" ]; then echo "tlinuxupd$AppVersion not found!"; diff --git a/Telegram/DeployLinux32.sh b/Telegram/DeployLinux32.sh index 50d729aca92241..a0a10014780d01 100755 --- a/Telegram/DeployLinux32.sh +++ b/Telegram/DeployLinux32.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ ! -f "./../Linux/Release/deploy/$AppVersionStr/tlinux32upd$AppVersion" ]; then echo "tlinux32upd$AppVersion not found!" diff --git a/Telegram/DeployMacWin.sh b/Telegram/DeployMacWin.sh index d67ee3e37c4634..d9f95e8a45c08b 100755 --- a/Telegram/DeployMacWin.sh +++ b/Telegram/DeployMacWin.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ ! -f "./../Mac/Release/deploy/$AppVersionStr/tmacupd$AppVersion" ]; then echo "tmacupd$AppVersion not found!" diff --git a/Telegram/DeployWin.sh b/Telegram/DeployWin.sh index 400bb3946ae438..1c7c7b4b98cc72 100644 --- a/Telegram/DeployWin.sh +++ b/Telegram/DeployWin.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ ! -f "./../Win32/Deploy/deploy/$AppVersionStr/tupdate$AppVersion" ]; then echo "tupdate$AppVersion not found!" diff --git a/Telegram/PrepareLinux.sh b/Telegram/PrepareLinux.sh index 7455a72043f54d..4b40df398c7ee8 100755 --- a/Telegram/PrepareLinux.sh +++ b/Telegram/PrepareLinux.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then echo "Deploy folder for version $AppVersionStr already exists!" diff --git a/Telegram/PrepareLinux32.sh b/Telegram/PrepareLinux32.sh index 9f367ebfb1cbc0..b2fefd1c38c704 100755 --- a/Telegram/PrepareLinux32.sh +++ b/Telegram/PrepareLinux32.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ -d "./../Linux/Release/deploy/$AppVersionStr" ]; then echo "Deploy folder for version $AppVersionStr already exists!" diff --git a/Telegram/PrepareMac.sh b/Telegram/PrepareMac.sh index 25fbfbf325fefb..cf358d6b35d036 100755 --- a/Telegram/PrepareMac.sh +++ b/Telegram/PrepareMac.sh @@ -1,5 +1,5 @@ -AppVersionStr=0.6.6 -AppVersion=6006 +AppVersionStr=0.6.7 +AppVersion=6007 if [ -d "./../Mac/Release/deploy/$AppVersionStr" ]; then echo "Deploy folder for version $AppVersionStr already exists!" diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index b3ced200fccb16..c0951b661cd91b 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,6 +1,6 @@ cd ..\Win32\Deploy -call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.6.exe +call ..\..\..\TelegramPrivate\Sign.bat tsetup.0.6.7.exe call Prepare.exe -path Telegram.exe -path Updater.exe -mkdir deploy\0.6.6\Telegram -move deploy\0.6.6\Telegram.exe deploy\0.6.6\Telegram\ +mkdir deploy\0.6.7\Telegram +move deploy\0.6.7\Telegram.exe deploy\0.6.7\Telegram\ cd ..\..\Telegram diff --git a/Telegram/Setup.iss b/Telegram/Setup.iss index 3b0b69a3d8c2e5..1dfc7eaafc50ce 100644 --- a/Telegram/Setup.iss +++ b/Telegram/Setup.iss @@ -3,9 +3,9 @@ #define MyAppShortName "Telegram" #define MyAppName "Telegram Desktop" -#define MyAppVersion "0.6.6" -#define MyAppVersionZero "0.6.6" -#define MyAppFullVersion "0.6.6.0" +#define MyAppVersion "0.6.7" +#define MyAppVersionZero "0.6.7" +#define MyAppFullVersion "0.6.7.0" #define MyAppPublisher "Telegram Messenger LLP" #define MyAppURL "https://tdesktop.com" #define MyAppExeName "Telegram.exe" diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index fb470b0cbc2095..26b6a219a48940 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -574,7 +574,7 @@ namespace App { const QVector &v(links.c_vector().v); for (QVector::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) { const MTPDcontacts_link &dv(i->c_contacts_link()); - feedUsers(MTP_vector(QVector(1, dv.vuser))); + feedUsers(MTP_vector(1, dv.vuser)); MTPint userId(MTP_int(0)); switch (dv.vuser.type()) { case mtpc_userEmpty: userId = dv.vuser.c_userEmpty().vid; break; diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index db03180124b6a2..56ad3bc4bad29b 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -331,10 +331,10 @@ void Application::writeUserConfigIn(uint64 ms) { void Application::killDownloadSessionsStart(int32 dc) { if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) { - killDownloadSessionTimes.insert(dc, getms() + MTPKillFileSessionTimeout); + killDownloadSessionTimes.insert(dc, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout); } if (!killDownloadSessionsTimer.isActive()) { - killDownloadSessionsTimer.start(MTPKillFileSessionTimeout + 5); + killDownloadSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout + 5); } } @@ -350,7 +350,7 @@ void Application::onWriteUserConfig() { } void Application::killDownloadSessions() { - uint64 ms = getms(), left = MTPKillFileSessionTimeout; + uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout; for (QMap::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) { if (i.value() <= ms) { for (int j = 1; j < MTPDownloadSessionsCount; ++j) { @@ -378,7 +378,8 @@ void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) { if (peer == App::self()->id) { MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&Application::selfPhotoDone), rpcFail(&Application::peerPhotoFail, peer)); } else { - MTP::send(MTPmessages_EditChatPhoto(MTP_int(peer & 0xFFFFFFFF), MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&Application::chatPhotoDone, peer), rpcFail(&Application::peerPhotoFail, peer)); + History *hist = App::history(peer); + hist->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(MTP_int(peer & 0xFFFFFFFF), MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&Application::chatPhotoDone, peer), rpcFail(&Application::peerPhotoFail, peer), 0, 0, hist->sendRequestId); } } } diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index b767cee1b4fdc6..5f7228714ab1d5 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -251,7 +251,7 @@ void AddContactBox::onSend() { } void AddContactBox::onSaveSelfDone(const MTPUser &user) { - App::feedUsers(MTP_vector(QVector(1, user))); + App::feedUsers(MTP_vector(1, user)); emit closed(); } diff --git a/Telegram/SourceFiles/boxes/usernamebox.cpp b/Telegram/SourceFiles/boxes/usernamebox.cpp index 2900ef97213801..e3274849a9956b 100644 --- a/Telegram/SourceFiles/boxes/usernamebox.cpp +++ b/Telegram/SourceFiles/boxes/usernamebox.cpp @@ -201,7 +201,7 @@ void UsernameBox::onChanged() { } void UsernameBox::onUpdateDone(const MTPUser &user) { - App::feedUsers(MTP_vector(QVector(1, user))); + App::feedUsers(MTP_vector(1, user)); emit closed(); } diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 4c85b9c3716796..59fd9015c049b3 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://tdesktop.com */ #pragma once -static const int32 AppVersion = 6006; -static const wchar_t *AppVersionStr = L"0.6.6"; +static const int32 AppVersion = 6007; +static const wchar_t *AppVersionStr = L"0.6.7"; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppName = L"Telegram Desktop"; @@ -34,6 +34,7 @@ enum { MTPIdsBufferSize = 400, // received msgIds and wereAcked msgIds count stored MTPCheckResendTimeout = 10000, // how much time passed from send till we resend request or check it's state, in ms MTPCheckResendWaiting = 1000, // how much time to wait for some more requests, when resending request or checking it's state, in ms + MTPAckSendWaiting = 10000, // how much time to wait for some more requests, when sending msg acks MTPResendThreshold = 1, // how much ints should message contain for us not to resend, but to check it's state MTPContainerLives = 600, // container lives 10 minutes in haveSent map MTPMaxReceiveDelay = 64000, // 64 seconds @@ -97,6 +98,8 @@ enum { MinUsernameLength = 5, MaxUsernameLength = 32, UsernameCheckTimeout = 200, + + MaxMessageSize = 4096, }; #ifdef Q_OS_WIN @@ -232,6 +235,7 @@ enum { MaxPhotosInMemory = 50, // try to clear some memory after 50 photos are created NoUpdatesTimeout = 180 * 1000, // if nothing is received in 3 min we reconnect + WaitForSeqTimeout = 1000, // 1s wait for skipped seq in updates MemoryForImageCache = 64 * 1024 * 1024, // after 64mb of unpacked images we try to clear some memory NotifyWindowsCount = 3, // 3 desktop notifies at the same time diff --git a/Telegram/SourceFiles/fileuploader.cpp b/Telegram/SourceFiles/fileuploader.cpp index bda9d1d3c0a65c..251deae309b640 100644 --- a/Telegram/SourceFiles/fileuploader.cpp +++ b/Telegram/SourceFiles/fileuploader.cpp @@ -85,7 +85,7 @@ void FileUploader::sendNext() { bool killing = killSessionsTimer.isActive(); if (queue.isEmpty()) { if (!killing) { - killSessionsTimer.start(MTPKillFileSessionTimeout); + killSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout); } return; } diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index 0df125f80e0eba..78e0a9437ea330 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -101,6 +101,28 @@ namespace { } return false; } + inline bool chIsSentenceEnd(QChar ch) { + switch (ch.unicode()) { + case '.': + case '?': + case '!': + return true; + default: + break; + } + return false; + } + inline bool chIsSentencePartEnd(QChar ch) { + switch (ch.unicode()) { + case ',': + case ':': + case ';': + return true; + default: + break; + } + return false; + } inline bool chIsParagraphSeparator(QChar ch) { switch (ch.unicode()) { case QChar::LineFeed: @@ -3978,6 +4000,74 @@ QString textSearchKey(const QString &text) { return textAccentFold(text.trimmed().toLower()); } +bool textSplit(QString &sendingText, QString &leftText, int32 limit) { + if (leftText.isEmpty() || !limit) return false; + + LinkRanges lnkRanges = textParseLinks(leftText); + int32 currentLink = 0, lnkCount = lnkRanges.size(); + + int32 s = 0, half = limit / 2, goodLevel = 0; + for (const QChar *start = leftText.constData(), *ch = start, *end = leftText.constEnd(), *good = ch; ch != end; ++ch, ++s) { + while (currentLink < lnkCount && ch >= lnkRanges[currentLink].from + lnkRanges[currentLink].len) { + ++currentLink; + } + + bool inLink = (currentLink < lnkCount) && (ch > lnkRanges[currentLink].from) && (ch < lnkRanges[currentLink].from + lnkRanges[currentLink].len); + if (s > half) { + if (inLink) { + if (!goodLevel) good = ch; + } else { + if (chIsNewline(*ch)) { + if (ch + 1 < end && chIsNewline(*(ch + 1)) && goodLevel <= 7) { + goodLevel = 7; + good = ch; + } else if (goodLevel <= 6) { + goodLevel = 6; + good = ch; + } + } else if (chIsSpace(*ch)) { + if (chIsSentenceEnd(*(ch - 1)) && goodLevel <= 5) { + goodLevel = 5; + good = ch; + } else if (chIsSentencePartEnd(*(ch - 1)) && goodLevel <= 4) { + goodLevel = 4; + good = ch; + } else if (goodLevel <= 3) { + goodLevel = 3; + good = ch; + } + } else if (chIsWordSeparator(*(ch - 1)) && goodLevel <= 2) { + goodLevel = 2; + good = ch; + } else if (goodLevel <= 1) { + goodLevel = 1; + good = ch; + } + } + } + if (ch->isHighSurrogate()) { + if (ch + 1 < end && (ch + 1)->isLowSurrogate()) { + ++ch; + } + } else { + if (ch + 1 < end && ((((ch->unicode() >= 48 && ch->unicode() < 58) || ch->unicode() == 35) && (ch + 1)->unicode() == 0x20E3) || (ch + 1)->unicode() == 0xFE0F)) { + if (getEmoji((ch->unicode() << 16) | (ch + 1)->unicode())) { + ++ch; + ++s; + } + } + } + if (s >= limit) { + sendingText = leftText.mid(0, good - start); + leftText = leftText.mid(good - start); + return true; + } + } + sendingText = leftText; + leftText = QString(); + return true; +} + LinkRanges textParseLinks(const QString &text, bool rich) { LinkRanges lnkRanges; diff --git a/Telegram/SourceFiles/gui/text.h b/Telegram/SourceFiles/gui/text.h index a566b2605a2a1b..a11d7990de1160 100644 --- a/Telegram/SourceFiles/gui/text.h +++ b/Telegram/SourceFiles/gui/text.h @@ -23,6 +23,7 @@ QString textRichPrepare(const QString &text); QString textOneLine(const QString &text, bool trim = true, bool rich = false); QString textAccentFold(const QString &text); QString textSearchKey(const QString &text); +bool textSplit(QString &sendingText, QString &leftText, int32 limit); struct LinkRange { LinkRange() : from(0), len(0) { diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 64c5e496fbb766..25dc1f746ce498 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -874,6 +874,7 @@ History::History(const PeerId &peerId) : width(0), height(0) , lastWidth(0) , lastScrollTop(History::ScrollMax) , mute(isNotifyMuted(peer->notify)) +, sendRequestId(0) , textCachedFor(0) , lastItemTextCache(st::dlgRichMinWidth) , posInDialogs(0) @@ -1337,7 +1338,6 @@ void History::newItemAdded(HistoryItem *item) { unregTyping(item->from()); } if (item->out()) { -// inboxRead(false); if (unreadBar) unreadBar->destroy(); } else if (item->unread()) { notifies.push_back(item); @@ -1529,13 +1529,13 @@ void History::addToBack(const QVector &slice) { } } -void History::inboxRead(bool byThisInstance) { +void History::inboxRead(HistoryItem *wasRead) { if (unreadCount) { - if (!byThisInstance && loadedAtBottom()) App::main()->historyToDown(this); + if (wasRead && loadedAtBottom()) App::main()->historyToDown(this); setUnreadCount(0); } if (!isEmpty()) { - int32 till = back()->back()->id; + int32 till = (wasRead ? wasRead : back()->back())->id; if (inboxReadTill < till) inboxReadTill = till; } if (!dialogs.isEmpty()) { @@ -1545,9 +1545,9 @@ void History::inboxRead(bool byThisInstance) { clearNotifications(); } -void History::outboxRead() { +void History::outboxRead(HistoryItem *wasRead) { if (!isEmpty()) { - int32 till = back()->back()->id; + int32 till = wasRead->id; if (outboxReadTill < till) outboxReadTill = till; } } @@ -1978,9 +1978,9 @@ HistoryItem::HistoryItem(History *history, HistoryBlock *block, MsgId msgId, boo void HistoryItem::markRead() { if (_unread) { if (_out) { - _history->outboxRead(); + _history->outboxRead(this); } else { - _history->inboxRead(); + _history->inboxRead(this); } App::main()->msgUpdated(_history->peer->id, this); _unread = false; diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 05cff10efff4fb..5de26a58f28914 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -657,8 +657,8 @@ struct History : public QList { void newItemAdded(HistoryItem *item); void unregTyping(UserData *from); - void inboxRead(bool byThisInstance = false); - void outboxRead(); + void inboxRead(HistoryItem *wasRead); + void outboxRead(HistoryItem *wasRead); void setUnreadCount(int32 newUnreadCount, bool psUpdate = true); void setMsgCount(int32 newMsgCount); @@ -732,6 +732,8 @@ struct History : public QList { int32 lastWidth, lastScrollTop; bool mute; + mtpRequestId sendRequestId; + // for dialog drawing Text nameText; void updateNameText(); diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index fc4af7946421f4..ee732961eeb3a9 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -1536,7 +1536,8 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent) , titlePeerTextWidth(0) , bg(st::msgBG) , hiderOffered(false) - , _scrollDelta(0) { + , _scrollDelta(0) + , _typingRequest(0) { _scroll.setFocusPolicy(Qt::NoFocus); setAcceptDrops(true); @@ -1557,9 +1558,12 @@ HistoryWidget::HistoryWidget(QWidget *parent) : QWidget(parent) connect(App::wnd()->windowHandle(), SIGNAL(visibleChanged(bool)), this, SLOT(onVisibleChanged())); connect(&_scrollTimer, SIGNAL(timeout()), this, SLOT(onScrollTimer())); connect(&_emojiPan, SIGNAL(emojiSelected(EmojiPtr)), &_field, SLOT(onEmojiInsert(EmojiPtr))); + connect(&_typingStopTimer, SIGNAL(timeout()), this, SLOT(cancelTyping())); _scrollTimer.setSingleShot(false); + _typingStopTimer.setSingleShot(true); + _animActiveTimer.setSingleShot(false); connect(&_animActiveTimer, SIGNAL(timeout()), this, SLOT(onAnimActiveStep())); @@ -1595,12 +1599,29 @@ void HistoryWidget::onTextChange() { updateTyping(); } +void HistoryWidget::cancelTyping() { + if (_typingRequest) { + MTP::cancel(_typingRequest); + _typingRequest = 0; + } +} + void HistoryWidget::updateTyping(bool typing) { uint64 ms = getms() + 10000; if (noTypingUpdate || !hist || (typing && (hist->myTyping + 5000 > ms)) || (!typing && (hist->myTyping + 5000 <= ms))) return; hist->myTyping = typing ? ms : 0; - if (typing) MTP::send(MTPmessages_SetTyping(histPeer->input, typing ? MTP_sendMessageTypingAction() : MTP_sendMessageCancelAction())); + cancelTyping(); + if (typing) { + _typingRequest = MTP::send(MTPmessages_SetTyping(histPeer->input, typing ? MTP_sendMessageTypingAction() : MTP_sendMessageCancelAction()), rpcDone(&HistoryWidget::typingDone)); + _typingStopTimer.start(5000); + } +} + +void HistoryWidget::typingDone(const MTPBool &result, mtpRequestId req) { + if (_typingRequest == req) { + _typingRequest = 0; + } } void HistoryWidget::activate() { @@ -1778,7 +1799,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l App::main()->peerUpdated(histPeer); noTypingUpdate = true; - _field.setPlainText(hist->draft); + setFieldText(hist->draft); _field.setFocus(); if (!hist->draft.isEmpty()) { _field.setTextCursor(hist->draftCur); @@ -2192,26 +2213,15 @@ void HistoryWidget::onSend(bool ctrlShiftEnter) { QString text = prepareMessage(_field.getText()); if (!text.isEmpty()) { App::main()->readServerHistory(hist, false); - - MsgId newId = clientMsgId(); - uint64 randomId = MTP::nonce(); - - App::historyRegRandom(randomId, newId); - hist->loadAround(0); - MTPstring msgText(MTP_string(text)); - int32 flags = 0x01 | 0x02; // unread, out - hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty())); - App::main()->historyToDown(hist); - App::main()->dialogsToUp(); - peerMessagesUpdated(); + App::main()->sendPreparedText(hist, prepareMessage(_field.getText())); - MTP::send(MTPmessages_SendMessage(histInputPeer, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId)); - _field.setPlainText(""); + setFieldText(QString()); if (!_attachType.isHidden()) _attachType.hideStart(); if (!_emojiPan.isHidden()) _emojiPan.hideStart(); } + _field.setFocus(); } @@ -2237,10 +2247,10 @@ mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForw newId = clientMsgId(); hist->addToBackForwarded(newId, msg); - MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId)); + hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } else if (srv || (msg && msg->selectedText(FullItemSel).isEmpty())) { // newId = clientMsgId(); - // MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId)); + // hist->sendRequestId = MTP::send(MTPmessages_ForwardMessage(histPeer->input, MTP_int(item->id), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } else if (msg) { App::main()->readServerHistory(hist, false); @@ -2250,7 +2260,7 @@ mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForw int32 flags = 0x01 | 0x02; // unread, out hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(histPeer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty())); - MTP::send(MTPmessages_SendMessage(histPeer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId)); + hist->sendRequestId = MTP::send(MTPmessages_SendMessage(histPeer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } if (newId) { App::historyRegRandom(randomId, newId); @@ -2265,14 +2275,16 @@ mtpRequestId HistoryWidget::onForward(const PeerId &peer, SelectedItemSet toForw PeerData *toPeer = App::peerLoaded(peer); if (!toPeer) return 0; - App::main()->readServerHistory(App::history(toPeer->id), false); + History *hist = App::history(peer); + App::main()->readServerHistory(hist, false); QVector ids; ids.reserve(toForward.size()); for (SelectedItemSet::const_iterator i = toForward.cbegin(), e = toForward.cend(); i != e; ++i) { ids.push_back(MTP_int(i.value()->id)); } - return MTP::send(MTPmessages_ForwardMessages(toPeer->input, MTP_vector(ids)), App::main()->rpcDone(&MainWidget::forwardDone, peer)); + hist->sendRequestId = MTP::send(MTPmessages_ForwardMessages(toPeer->input, MTP_vector(ids)), App::main()->rpcDone(&MainWidget::forwardDone, peer), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); + return hist->sendRequestId; } void HistoryWidget::onShareContact(const PeerId &peer, UserData *contact) { @@ -2296,7 +2308,7 @@ void HistoryWidget::shareContact(const PeerId &peer, const QString &phone, const int32 flags = 0x01 | 0x02; // unread, out h->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(peer), MTP_int(unixtime()), MTP_string(""), MTP_messageMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname), MTP_int(userId)))); - MTP::send(MTPmessages_SendMedia(App::peer(peer)->input, MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId)); + h->sendRequestId = MTP::send(MTPmessages_SendMedia(App::peer(peer)->input, MTP_inputMediaContact(MTP_string(phone), MTP_string(fname), MTP_string(lname)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); App::historyRegRandom(randomId, newId); if (hist && histPeer && peer == histPeer->id) { @@ -2838,7 +2850,7 @@ void HistoryWidget::confirmSendImage(const ReadyLocalMedia &img) { } void HistoryWidget::cancelSendImage() { - if (confirmImageId && confirmWithText) _field.setPlainText(QString()); + if (confirmImageId && confirmWithText) setFieldText(QString()); confirmImageId = 0; confirmWithText = false; confirmImage = QImage(); @@ -2852,7 +2864,8 @@ void HistoryWidget::onPhotoUploaded(MsgId newId, const MTPInputFile &file) { uint64 randomId = MTP::nonce(); App::historyRegRandom(randomId, newId); - MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId)); + History *hist = item->history(); + hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedPhoto(file), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } } @@ -2867,7 +2880,8 @@ void HistoryWidget::onDocumentUploaded(MsgId newId, const MTPInputFile &file) { uint64 randomId = MTP::nonce(); App::historyRegRandom(randomId, newId); DocumentData *document = media->document(); - MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedDocument(file, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId)); + History *hist = item->history(); + hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedDocument(file, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } } } @@ -2883,7 +2897,8 @@ void HistoryWidget::onThumbDocumentUploaded(MsgId newId, const MTPInputFile &fil uint64 randomId = MTP::nonce(); App::historyRegRandom(randomId, newId); DocumentData *document = media->document(); - MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId)); + History *hist = item->history(); + hist->sendRequestId = MTP::send(MTPmessages_SendMedia(item->history()->peer->input, MTP_inputMediaUploadedThumbDocument(file, thumb, MTP_string(document->name), MTP_string(document->mime)), MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentFullDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); } } } @@ -3114,11 +3129,11 @@ void HistoryWidget::onFieldTabbed() { if (isImg) { QImage img(cWorkingDir() + fname); if (!img.isNull()) { - _field.setPlainText(text); + setFieldText(text); uploadImage(img, !text.isEmpty()); } } else { - _field.setPlainText(text); + setFieldText(text); uploadFile(cWorkingDir() + fname, !text.isEmpty()); } } else if (isContact) { @@ -3130,13 +3145,12 @@ void HistoryWidget::onFieldTabbed() { } QStringList data = contact.split(QChar(' ')); if (data.size() > 1) { - _field.setPlainText(text); - + setFieldText(text); QString phone = data.at(0).trimmed(), fname = data.at(1).trimmed(), lname = (data.size() > 2) ? static_cast(data.mid(2)).join(QChar(' ')).trimmed() : QString(); shareContactConfirmation(phone, fname, lname, !text.isEmpty()); } } else { - _field.setPlainText(t); + setFieldText(t); QTextCursor c = _field.textCursor(); c.movePosition(QTextCursor::End); _field.setTextCursor(c); @@ -3144,6 +3158,12 @@ void HistoryWidget::onFieldTabbed() { } } +void HistoryWidget::setFieldText(const QString &text) { + noTypingUpdate = true; + _field.setPlainText(text); + noTypingUpdate = false; +} + void HistoryWidget::peerUpdated(PeerData *data) { if (data && data == histPeer) { updateListSize(); @@ -3203,7 +3223,7 @@ void HistoryWidget::onDeleteContextSure() { } if (item->id > 0) { - MTP::send(MTPmessages_DeleteMessages(MTP_vector(QVector(1, MTP_int(item->id))))); + MTP::send(MTPmessages_DeleteMessages(MTP_vector(1, MTP_int(item->id)))); } item->destroy(); App::wnd()->hideLayer(); diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index a8bb8450336a22..4085fea564aec3 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -283,6 +283,7 @@ class HistoryWidget : public QWidget, public RPCSender, public Animated { QRect historyRect() const; void updateTyping(bool typing = true); + void typingDone(const MTPBool &result, mtpRequestId req); void destroyData(); void uploadImage(const QImage &img, bool withText = false); @@ -344,6 +345,8 @@ public slots: void peerUpdated(PeerData *data); + void cancelTyping(); + void onPhotoUploaded(MsgId msgId, const MTPInputFile &file); void onDocumentUploaded(MsgId msgId, const MTPInputFile &file); void onThumbDocumentUploaded(MsgId msgId, const MTPInputFile &file, const MTPInputFile &thumb); @@ -396,6 +399,8 @@ public slots: void addMessagesToBack(const QVector &messages); void chatLoaded(const MTPmessages_ChatFull &res); + void setFieldText(const QString &text); + QStringList getMediasFromMime(const QMimeData *d); DragState getDragState(const QMimeData *d); @@ -457,5 +462,8 @@ public slots: QTimer _animActiveTimer; float64 _animActiveStart; + mtpRequestId _typingRequest; + QTimer _typingStopTimer; + }; diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp index 0d82f9826c9d0f..229afd516a72f1 100644 --- a/Telegram/SourceFiles/logs.cpp +++ b/Telegram/SourceFiles/logs.cpp @@ -344,3 +344,12 @@ QString logVectorLong(const QVector &ids) { } return idsStr + "]"; } + +QString logVectorLong(const QVector &ids) { + if (!ids.size()) return "[void list]"; + QString idsStr = QString("[%1").arg(*ids.cbegin()); + for (QVector::const_iterator i = ids.cbegin() + 1, e = ids.cend(); i != e; ++i) { + idsStr += QString(", %2").arg(*i); + } + return idsStr + "]"; +} diff --git a/Telegram/SourceFiles/logs.h b/Telegram/SourceFiles/logs.h index 38f87770c888e2..c9e78e7a755be2 100644 --- a/Telegram/SourceFiles/logs.h +++ b/Telegram/SourceFiles/logs.h @@ -72,6 +72,7 @@ inline const char *logBool(bool v) { class MTPlong; QString logVectorLong(const QVector &ids); +QString logVectorLong(const QVector &ids); #define LOG(msg) (logWrite(QString msg)) //usage LOG(("log: %1 %2").arg(1).arg(2)) diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 0f4b8dc47f3c77..d82e583fb24894 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1,4 +1,4 @@ - /* +/* This file is part of Telegram Desktop, an unofficial desktop messaging app, see https://telegram.org @@ -275,7 +275,7 @@ MainWidget *TopBarWidget::main() { MainWidget::MainWidget(Window *window) : QWidget(window), failedObjId(0), _dialogsWidth(st::dlgMinWidth), dialogs(this), history(this), profile(0), overview(0), _topBar(this), hider(0), _mediaType(this), _mediaTypeMask(0), - updPts(0), updDate(0), updQts(0), updSeq(0), updInited(false), onlineRequest(0) { +updPts(0), updDate(0), updQts(-1), updSeq(0), updInited(false), onlineRequest(0), _failDifferenceTimeout(1) { setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); connect(window, SIGNAL(resized(const QSize &)), this, SLOT(onParentResize(const QSize &))); @@ -285,6 +285,8 @@ dialogs(this), history(this), profile(0), overview(0), _topBar(this), hider(0), connect(&noUpdatesTimer, SIGNAL(timeout()), this, SLOT(getDifference())); connect(&onlineTimer, SIGNAL(timeout()), this, SLOT(setOnline())); connect(&onlineUpdater, SIGNAL(timeout()), this, SLOT(updateOnlineDisplay())); + connect(&_bySeqTimer, SIGNAL(timeout()), this, SLOT(getDifference())); + connect(&_failDifferenceTimer, SIGNAL(timeout()), this, SLOT(getDifferenceForce())); connect(this, SIGNAL(peerUpdated(PeerData*)), &history, SLOT(peerUpdated(PeerData*))); connect(&_topBar, SIGNAL(clicked()), this, SLOT(onTopBarClick())); connect(&history, SIGNAL(peerShown(PeerData*)), this, SLOT(onPeerShown(PeerData*))); @@ -298,6 +300,8 @@ dialogs(this), history(this), profile(0), overview(0), _topBar(this), hider(0), onlineTimer.setSingleShot(true); onlineUpdater.setSingleShot(true); updateNotifySettingTimer.setSingleShot(true); + _bySeqTimer.setSingleShot(true); + _failDifferenceTimer.setSingleShot(true); dialogs.show(); history.show(); @@ -413,7 +417,7 @@ void MainWidget::deleteHistory(PeerData *peer, const MTPmessages_StatedMessage & void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHistory &result) { const MTPDmessages_affectedHistory &d(result.c_messages_affectedHistory()); - App::main()->updUpdated(d.vpts.v, 0, 0, d.vseq.v); + App::main()->updUpdated(d.vpts.v, d.vseq.v); int32 offset = d.voffset.v; if (!MTP::authedId() || offset <= 0) return; @@ -423,13 +427,13 @@ void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHis void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) { const MTPDcontacts_link &d(result.c_contacts_link()); - App::feedUsers(MTP_vector(QVector(1, d.vuser))); + App::feedUsers(MTP_vector(1, d.vuser)); App::feedUserLink(MTP_int(user->id & 0xFFFFFFFF), d.vmy_link, d.vforeign_link); } void MainWidget::deleteHistoryAndContact(UserData *user, const MTPcontacts_Link &result) { const MTPDcontacts_link &d(result.c_contacts_link()); - App::feedUsers(MTP_vector(QVector(1, d.vuser))); + App::feedUsers(MTP_vector(1, d.vuser)); App::feedUserLink(MTP_int(user->id & 0xFFFFFFFF), d.vmy_link, d.vforeign_link); if ((profile && profile->peer() == user) || (overview && overview->peer() == user) || _stack.contains(user) || history.peer() == user) { @@ -546,27 +550,30 @@ DialogsIndexed &MainWidget::contactsList() { return dialogs.contactsList(); } -void MainWidget::sendMessage(History *hist, const QString &text) { - readServerHistory(hist, false); - QString msg = history.prepareMessage(text); - if (!msg.isEmpty()) { +void MainWidget::sendPreparedText(History *hist, const QString &text) { + QString sendingText, leftText = text; + while (textSplit(sendingText, leftText, MaxMessageSize)) { MsgId newId = clientMsgId(); uint64 randomId = MTP::nonce(); - + App::historyRegRandom(randomId, newId); - - hist->loadAround(0); - MTPstring msgText(MTP_string(msg)); + MTPstring msgText(MTP_string(sendingText)); int32 flags = 0x01 | 0x02; // unread, out hist->addToBack(MTP_message(MTP_int(flags), MTP_int(newId), MTP_int(MTP::authedId()), App::peerToMTP(hist->peer->id), MTP_int(unixtime()), msgText, MTP_messageMediaEmpty())); - historyToDown(hist); - if (history.peer() == hist->peer) { - history.peerMessagesUpdated(); - } - - MTP::send(MTPmessages_SendMessage(hist->peer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId)); - } + hist->sendRequestId = MTP::send(MTPmessages_SendMessage(hist->peer->input, msgText, MTP_long(randomId)), App::main()->rpcDone(&MainWidget::sentDataReceived, randomId), RPCFailHandlerPtr(), 0, 0, hist->sendRequestId); + } + + historyToDown(hist); + if (history.peer() == hist->peer) { + history.peerMessagesUpdated(); + } +} + +void MainWidget::sendMessage(History *hist, const QString &text) { + readServerHistory(hist, false); + hist->loadAround(0); + sendPreparedText(hist, history.prepareMessage(text)); } void MainWidget::readServerHistory(History *hist, bool force) { @@ -574,7 +581,7 @@ void MainWidget::readServerHistory(History *hist, bool force) { ReadRequests::const_iterator i = _readRequests.constFind(hist->peer); if (i == _readRequests.cend()) { - hist->inboxRead(true); + hist->inboxRead(0); _readRequests.insert(hist->peer, MTP::send(MTPmessages_ReadHistory(hist->peer->input, MTP_int(0), MTP_int(0), MTP_bool(true)), rpcDone(&MainWidget::partWasRead, hist->peer))); } } @@ -823,7 +830,7 @@ void MainWidget::photosLoaded(History *h, const MTPmessages_Messages &msgs, mtpR void MainWidget::partWasRead(PeerData *peer, const MTPmessages_AffectedHistory &result) { const MTPDmessages_affectedHistory &d(result.c_messages_affectedHistory()); - App::main()->updUpdated(d.vpts.v, 0, 0, d.vseq.v); + App::main()->updUpdated(d.vpts.v, d.vseq.v); int32 offset = d.voffset.v; if (!MTP::authedId() || offset <= 0) { @@ -1270,12 +1277,15 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage case mtpc_messages_sentMessage: { const MTPDmessages_sentMessage &d(result.c_messages_sentMessage()); + if (randomId) feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date + if (updInited && d.vseq.v) { if (d.vseq.v <= updSeq) return; - if (d.vseq.v > updSeq + 1) return getDifference(); + if (d.vseq.v > updSeq + 1) { + _bySeqSentMessage.insert(d.vseq.v, result); + return _bySeqTimer.start(WaitForSeqTimeout); + } } - - feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date if (updInited) { updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v); } @@ -1284,12 +1294,15 @@ void MainWidget::sentDataReceived(uint64 randomId, const MTPmessages_SentMessage case mtpc_messages_sentMessageLink: { const MTPDmessages_sentMessageLink &d(result.c_messages_sentMessageLink()); + if (randomId) feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date + if (updInited && d.vseq.v) { if (d.vseq.v <= updSeq) return; - if (d.vseq.v > updSeq + 1) return getDifference(); + if (d.vseq.v > updSeq + 1) { + _bySeqSentMessage.insert(d.vseq.v, result); + return _bySeqTimer.start(WaitForSeqTimeout); + } } - - feedUpdate(MTP_updateMessageID(d.vid, MTP_long(randomId))); // ignore real date if (updInited) { updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v); } @@ -1330,7 +1343,11 @@ void MainWidget::sentFullDataReceived(uint64 randomId, const MTPmessages_StatedM App::feedMessageMedia(msgId, *msg); } if (updInited && d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqStatedMessage.insert(d.vseq.v, result); + return _bySeqTimer.start(WaitForSeqTimeout); + } } if (!randomId) { feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts)); @@ -1349,7 +1366,11 @@ void MainWidget::sentFullDataReceived(uint64 randomId, const MTPmessages_StatedM App::feedMessageMedia(msgId, *msg); } if (updInited && d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqStatedMessage.insert(d.vseq.v, result); + return _bySeqTimer.start(WaitForSeqTimeout); + } } if (!randomId) { feedUpdate(MTP_updateNewMessage(d.vmessage, d.vpts)); @@ -1368,7 +1389,11 @@ void MainWidget::sentFullDatasReceived(const MTPmessages_StatedMessages &result) case mtpc_messages_statedMessages: { const MTPDmessages_statedMessages &d(result.c_messages_statedMessages()); if (updInited && d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqStatedMessages.insert(d.vseq.v, result); + return _bySeqTimer.start(WaitForSeqTimeout); + } } App::feedUsers(d.vusers); @@ -1385,7 +1410,11 @@ void MainWidget::sentFullDatasReceived(const MTPmessages_StatedMessages &result) const MTPDmessages_statedMessagesLinks &d(result.c_messages_statedMessagesLinks()); if (updInited && d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqStatedMessages.insert(d.vseq.v, result); + return _bySeqTimer.start(WaitForSeqTimeout); + } } App::feedUsers(d.vusers); @@ -1620,8 +1649,78 @@ bool MainWidget::updateFail(const RPCError &e) { void MainWidget::updSetState(int32 pts, int32 date, int32 qts, int32 seq) { if (updPts < pts) updPts = pts; if (updDate < date) updDate = date; - if (updQts < qts) updQts = qts; - if (seq) updSeq = seq; + if (qts && updQts < qts) { + updQts = qts; + } + if (seq && seq != updSeq) { + updSeq = seq; + if (_bySeqTimer.isActive()) _bySeqTimer.stop(); + for (QMap::iterator i = _bySeqUpdates.begin(); i != _bySeqUpdates.end();) { + int32 s = i.key(); + if (s <= seq + 1) { + MTPUpdates v = i.value(); + i = _bySeqUpdates.erase(i); + if (s == seq + 1) { + return handleUpdates(v); + } + } else { + if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout); + break; + } + } + for (QMap::iterator i = _bySeqSentMessage.begin(); i != _bySeqSentMessage.end();) { + int32 s = i.key(); + if (s <= seq + 1) { + MTPmessages_SentMessage v = i.value(); + i = _bySeqSentMessage.erase(i); + if (s == seq + 1) { + return sentDataReceived(0, v); + } + } else { + if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout); + break; + } + } + for (QMap::iterator i = _bySeqStatedMessage.begin(); i != _bySeqStatedMessage.end();) { + int32 s = i.key(); + if (s <= seq + 1) { + MTPmessages_StatedMessage v = i.value(); + i = _bySeqStatedMessage.erase(i); + if (s == seq + 1) { + return sentFullDataReceived(0, v); + } + } else { + if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout); + break; + } + } + for (QMap::iterator i = _bySeqStatedMessages.begin(); i != _bySeqStatedMessages.end();) { + int32 s = i.key(); + if (s <= seq + 1) { + MTPmessages_StatedMessages v = i.value(); + i = _bySeqStatedMessages.erase(i); + if (s == seq + 1) { + return sentFullDatasReceived(v); + } + } else { + if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout); + break; + } + } + for (QMap::iterator i = _bySeqPart.begin(); i != _bySeqPart.end();) { + int32 s = i.key(); + if (s <= seq + 1) { + int32 v = i.value(); + i = _bySeqPart.erase(i); + if (s == seq + 1) { + return updUpdated(v, s); + } + } else { + if (!_bySeqTimer.isActive()) _bySeqTimer.start(WaitForSeqTimeout); + break; + } + } + } } void MainWidget::gotState(const MTPupdates_State &state) { @@ -1637,6 +1736,8 @@ void MainWidget::gotState(const MTPupdates_State &state) { } void MainWidget::gotDifference(const MTPupdates_Difference &diff) { + _failDifferenceTimeout = 1; + switch (diff.type()) { case mtpc_updates_differenceEmpty: { const MTPDupdates_differenceEmpty &d(diff.c_updates_differenceEmpty()); @@ -1666,10 +1767,13 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) { }; } -void MainWidget::updUpdated(int32 pts, int32 date, int32 qts, int32 seq) { +void MainWidget::updUpdated(int32 pts, int32 seq) { if (!updInited) return; - if (seq && (seq < updSeq || seq > updSeq + 1)) return getDifference(); - updSetState(pts, date, qts, seq); + if (seq && (seq < updSeq || seq > updSeq + 1)) { + _bySeqPart.insert(seq, pts); + return _bySeqTimer.start(); + } + updSetState(pts, 0, 0, seq); } void MainWidget::feedDifference(const MTPVector &users, const MTPVector &chats, const MTPVector &msgs, const MTPVector &other) { @@ -1683,15 +1787,31 @@ void MainWidget::feedDifference(const MTPVector &users, const MTPVector bool MainWidget::failDifference(const RPCError &e) { LOG(("RPC Error: %1 %2: %3").arg(e.code()).arg(e.type()).arg(e.description())); + _failDifferenceTimer.start(_failDifferenceTimeout * 1000); + if (_failDifferenceTimeout < 64) _failDifferenceTimeout *= 2; + return true; +} + +void MainWidget::getDifferenceForce() { if (MTP::authedId()) { updInited = true; getDifference(); } - return true; } void MainWidget::getDifference() { if (!updInited) return; + + _bySeqUpdates.clear(); + _bySeqSentMessage.clear(); + _bySeqStatedMessage.clear(); + _bySeqStatedMessages.clear(); + _bySeqPart.clear(); + _bySeqTimer.stop(); + + noUpdatesTimer.stop(); + _failDifferenceTimer.stop(); + updInited = false; MTP::setGlobalDoneHandler(RPCDoneHandlerPtr(0)); MTP::send(MTPupdates_GetDifference(MTP_int(updPts), MTP_int(updDate), MTP_int(updQts)), rpcDone(&MainWidget::gotDifference), rpcFail(&MainWidget::failDifference)); @@ -1700,7 +1820,7 @@ void MainWidget::getDifference() { void MainWidget::start(const MTPUser &user) { MTP::authed(user.c_userSelf().vid.v); App::initMedia(); - App::feedUsers(MTP_vector(QVector(1, user))); + App::feedUsers(MTP_vector(1, user)); App::app()->startUpdateCheck(); MTP::send(MTPupdates_GetState(), rpcDone(&MainWidget::gotState)); update(); @@ -1913,82 +2033,101 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) { noUpdatesTimer.start(NoUpdatesTimeout); - switch (updates.type()) { - case mtpc_updates: { - const MTPDupdates &d(updates.c_updates()); - if (d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); - } + handleUpdates(updates); + } catch(mtpErrorUnexpected &e) { // just some other type + } + } + update(); +} - App::feedChats(d.vchats); - App::feedUsers(d.vusers); - feedUpdates(d.vupdates); - - updSetState(updPts, d.vdate.v, updQts, d.vseq.v); - } break; - - case mtpc_updatesCombined: { - const MTPDupdatesCombined &d(updates.c_updatesCombined()); - if (d.vseq.v) { - if (d.vseq_start.v <= updSeq || d.vseq_start.v > updSeq + 1) return getDifference(); - } +void MainWidget::handleUpdates(const MTPUpdates &updates) { + switch (updates.type()) { + case mtpc_updates: { + const MTPDupdates &d(updates.c_updates()); + if (d.vseq.v) { + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqUpdates.insert(d.vseq.v, updates); + return _bySeqTimer.start(WaitForSeqTimeout); + } + } - App::feedChats(d.vchats); - App::feedUsers(d.vusers); - feedUpdates(d.vupdates); + App::feedChats(d.vchats); + App::feedUsers(d.vusers); + feedUpdates(d.vupdates); - updSetState(updPts, d.vdate.v, updQts, d.vseq.v); - } break; + updSetState(updPts, d.vdate.v, updQts, d.vseq.v); + } break; - case mtpc_updateShort: { - const MTPDupdateShort &d(updates.c_updateShort()); - - feedUpdate(d.vupdate); + case mtpc_updatesCombined: { + const MTPDupdatesCombined &d(updates.c_updatesCombined()); + if (d.vseq_start.v) { + if (d.vseq_start.v <= updSeq) return; + if (d.vseq_start.v > updSeq + 1) { + _bySeqUpdates.insert(d.vseq_start.v, updates); + return _bySeqTimer.start(WaitForSeqTimeout); + } + } - updSetState(updPts, d.vdate.v, updQts, updSeq); - } break; + App::feedChats(d.vchats); + App::feedUsers(d.vusers); + feedUpdates(d.vupdates); - case mtpc_updateShortMessage: { - const MTPDupdateShortMessage &d(updates.c_updateShortMessage()); - if (d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); - } + updSetState(updPts, d.vdate.v, updQts, d.vseq.v); + } break; - if (!App::userLoaded(d.vfrom_id.v)) return getDifference(); - int32 flags = 0x01; // unread - HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerUser(MTP_int(MTP::authedId())), d.vdate, d.vmessage, MTP_messageMediaEmpty())); - if (item) { - history.peerMessagesUpdated(item->history()->peer->id); - } + case mtpc_updateShort: { + const MTPDupdateShort &d(updates.c_updateShort()); - updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v); - } break; + feedUpdate(d.vupdate); - case mtpc_updateShortChatMessage: { - const MTPDupdateShortChatMessage &d(updates.c_updateShortChatMessage()); - if (d.vseq.v) { - if (d.vseq.v <= updSeq || d.vseq.v > updSeq + 1) return getDifference(); - } + updSetState(updPts, d.vdate.v, updQts, updSeq); + } break; - if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v)) return getDifference(); - int32 flags = 0x01; // unread - HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vdate, d.vmessage, MTP_messageMediaEmpty())); - if (item) { - history.peerMessagesUpdated(item->history()->peer->id); - } + case mtpc_updateShortMessage: { + const MTPDupdateShortMessage &d(updates.c_updateShortMessage()); + if (d.vseq.v) { + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqUpdates.insert(d.vseq.v, updates); + return _bySeqTimer.start(WaitForSeqTimeout); + } + } + + if (!App::userLoaded(d.vfrom_id.v)) return getDifference(); + int32 flags = 0x01; // unread + HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerUser(MTP_int(MTP::authedId())), d.vdate, d.vmessage, MTP_messageMediaEmpty())); + if (item) { + history.peerMessagesUpdated(item->history()->peer->id); + } - updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v); - } break; + updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v); + } break; - case mtpc_updatesTooLong: { - return getDifference(); - } break; + case mtpc_updateShortChatMessage: { + const MTPDupdateShortChatMessage &d(updates.c_updateShortChatMessage()); + if (d.vseq.v) { + if (d.vseq.v <= updSeq) return; + if (d.vseq.v > updSeq + 1) { + _bySeqUpdates.insert(d.vseq.v, updates); + return _bySeqTimer.start(WaitForSeqTimeout); } - } catch(mtpErrorUnexpected &e) { // just some other type } + + if (!App::chatLoaded(d.vchat_id.v) || !App::userLoaded(d.vfrom_id.v)) return getDifference(); + int32 flags = 0x01; // unread + HistoryItem *item = App::histories().addToBack(MTP_message(MTP_int(flags), d.vid, d.vfrom_id, MTP_peerChat(d.vchat_id), d.vdate, d.vmessage, MTP_messageMediaEmpty())); + if (item) { + history.peerMessagesUpdated(item->history()->peer->id); + } + + updSetState(d.vpts.v, d.vdate.v, updQts, d.vseq.v); + } break; + + case mtpc_updatesTooLong: { + return getDifference(); + } break; } - update(); -/**/ } void MainWidget::feedUpdate(const MTPUpdate &update) { @@ -2176,7 +2315,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateNewEncryptedMessage: { const MTPDupdateNewEncryptedMessage &d(update.c_updateNewEncryptedMessage()); - if (updQts < d.vqts.v) updQts = d.vqts.v; +// if (d.vqts.v && updQts < d.vqts.v) updQts = d.vqts.v; } break; case mtpc_updateEncryptedChatTyping: { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index ae80fb00c3b3c5..58572ab2fa1bbe 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -197,7 +197,7 @@ class MainWidget : public QWidget, public Animated, public RPCSender { void historyToDown(History *hist); void dialogsToUp(); void newUnreadMsg(History *history, MsgId msgId); - void updUpdated(int32 pts, int32 date, int32 qts, int32 seq); + void updUpdated(int32 pts, int32 seq); void historyWasRead(); void peerBefore(const PeerData *inPeer, MsgId inMsg, PeerData *&outPeer, MsgId &outMsg); @@ -266,6 +266,7 @@ class MainWidget : public QWidget, public Animated, public RPCSender { DialogsIndexed &contactsList(); void sendMessage(History *history, const QString &text); + void sendPreparedText(History *hist, const QString &text); void readServerHistory(History *history, bool force = true); @@ -312,6 +313,7 @@ public slots: void onParentResize(const QSize &newSize); void getDifference(); + void getDifferenceForce(); void setOnline(int windowState = -1); void mainStateChanged(Qt::WindowState state); @@ -348,6 +350,7 @@ public slots: void feedUpdate(const MTPUpdate &update); void updateReceived(const mtpPrime *from, const mtpPrime *end); + void handleUpdates(const MTPUpdates &updates); bool updateFail(const RPCError &e); void hideAll(); @@ -391,4 +394,14 @@ public slots: typedef QMap OverviewsPreload; OverviewsPreload _overviewPreload[OverviewCount], _overviewLoad[OverviewCount]; + + QMap _bySeqUpdates; + QMap _bySeqSentMessage; + QMap _bySeqStatedMessage; + QMap _bySeqStatedMessages; + QMap _bySeqPart; + QTimer _bySeqTimer; + + int32 _failDifferenceTimeout; // growing timeout for getDifference calls, if it fails + QTimer _failDifferenceTimer; }; diff --git a/Telegram/SourceFiles/mtproto/mtp.cpp b/Telegram/SourceFiles/mtproto/mtp.cpp index 7af8738c44ef72..81909b8ffc7dd9 100644 --- a/Telegram/SourceFiles/mtproto/mtp.cpp +++ b/Telegram/SourceFiles/mtproto/mtp.cpp @@ -40,6 +40,7 @@ namespace { typedef QMap RequestMap; RequestMap requestMap; + QReadWriteLock requestMapLock; typedef QPair DelayedRequest; typedef QList DelayedRequestsList; @@ -63,7 +64,8 @@ namespace { mtpAuthKey _localKey; void importDone(const MTPauth_Authorization &result, mtpRequestId req) { - QMutexLocker locker(&requestByDCLock); + QMutexLocker locker1(&requestByDCLock); + RequestsByDC::iterator i = requestsByDC.find(req); if (i == requestsByDC.end()) { LOG(("MTP Error: auth import request not found in requestsByDC, requestId: %1").arg(req)); @@ -78,6 +80,7 @@ namespace { 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) { mtpRequestId requestId = *i; RequestMap::const_iterator j = requestMap.constFind(requestId); @@ -167,13 +170,18 @@ namespace { } } - RequestMap::const_iterator i = requestMap.constFind(requestId); - if (i == requestMap.cend()) { - LOG(("MTP Error: could not find request %1").arg(requestId)); - return false; + mtpRequest req; + { + QReadLocker locker(&requestMapLock); + RequestMap::const_iterator i = requestMap.constFind(requestId); + if (i == requestMap.cend()) { + LOG(("MTP Error: could not find request %1").arg(requestId)); + return false; + } + req = i.value(); } _mtp_internal::registerRequest(requestId, (dc < 0) ? -newdc : newdc); - _mtp_internal::getSession(newdc)->sendPrepared(i.value()); + _mtp_internal::getSession(newdc)->sendPrepared(req); return true; } else if ((m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) { if (!requestId) return false; @@ -216,10 +224,15 @@ namespace { if (badGuestDC) badGuestDCRequests.insert(requestId); return true; } else if (err == qsl("CONNECTION_NOT_INITED") || err == qsl("CONNECTION_LAYER_INVALID")) { - RequestMap::const_iterator i = requestMap.constFind(requestId); - if (i == requestMap.cend()) { - LOG(("MTP Error: could not find request %1").arg(requestId)); - return false; + mtpRequest req; + { + QReadLocker locker(&requestMapLock); + RequestMap::const_iterator i = requestMap.constFind(requestId); + if (i == requestMap.cend()) { + LOG(("MTP Error: could not find request %1").arg(requestId)); + return false; + } + req = i.value(); } int32 dc = 0; { @@ -233,7 +246,66 @@ namespace { } if (!dc) return false; - _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(i.value()); + _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(req); + return true; + } else if (err == qsl("MSG_WAIT_FAILED")) { + mtpRequest req; + { + QReadLocker locker(&requestMapLock); + RequestMap::const_iterator i = requestMap.constFind(requestId); + if (i == requestMap.cend()) { + LOG(("MTP Error: could not find request %1").arg(requestId)); + return false; + } + req = i.value(); + } + if (!req->after) { + LOG(("MTP Error: wait failed for not dependent request %1").arg(requestId)); + return false; + } + int32 dc = 0; + { + QMutexLocker locker(&requestByDCLock); + RequestsByDC::iterator i = requestsByDC.find(requestId), j = requestsByDC.find(req->after->requestId); + if (i == requestsByDC.end()) { + LOG(("MTP Error: could not find request %1 by dc").arg(requestId)); + } else if (j == requestsByDC.end()) { + LOG(("MTP Error: could not find dependent request %1 by dc").arg(req->after->requestId)); + } else { + dc = i.value(); + if (i.value() != j.value()) { + req->after = mtpRequest(); + } + } + } + if (!dc) return false; + + if (!req->after) { + _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPreparedWithInit(req); + } else { + int32 newdc = abs(dc) % _mtp_internal::dcShift; + DCAuthWaiters &waiters(authWaiters[newdc]); + if (waiters.indexOf(req->after->requestId) >= 0) { + if (waiters.indexOf(requestId) < 0) { + waiters.push_back(requestId); + } + if (badGuestDCRequests.constFind(req->after->requestId) != badGuestDCRequests.cend()) { + if (badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend()) { + badGuestDCRequests.insert(requestId); + } + } + } else { + uint64 at = 0; + DelayedRequestsList::iterator i = delayedRequests.begin(), e = delayedRequests.end(); + for (; i != e; ++i) { + if (i->first == requestId) return true; + if (i->first == req->after->requestId) break; + } + if (i != e) { + delayedRequests.insert(i, DelayedRequest(requestId, i->second)); + } + } + } return true; } if (badGuestDC) badGuestDCRequests.remove(requestId); @@ -269,13 +341,13 @@ namespace _mtp_internal { } void unregisterRequest(mtpRequestId requestId) { - requestMap.remove(requestId); + { + QWriteLocker locker(&requestMapLock); + requestMap.remove(requestId); + } QMutexLocker locker(&requestByDCLock); - RequestsByDC::iterator i = requestsByDC.find(requestId); - if (i != requestsByDC.end()) { - requestsByDC.erase(i); - } + requestsByDC.remove(requestId); } uint32 getLayer() { @@ -289,15 +361,39 @@ namespace _mtp_internal { QMutexLocker locker(&parserMapLock); parserMap.insert(res, parser); } - requestMap.insert(res, request); + { + QWriteLocker locker(&requestMapLock); + requestMap.insert(res, request); + } return res; } - void replaceRequest(mtpRequest &newRequest, const mtpRequest &oldRequest) { - newRequest->requestId = oldRequest->requestId; - RequestMap::iterator i = requestMap.find(oldRequest->requestId); - if (i != requestMap.cend()) { - i.value() = newRequest; + mtpRequest getRequest(mtpRequestId reqId) { + static mtpRequest zero; + mtpRequest req; + { + QReadLocker locker(&requestMapLock); + RequestMap::const_iterator i = requestMap.constFind(reqId); + req = (i == requestMap.cend()) ? zero : i.value(); + } + return req; + } + + void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent) { + mtpMsgId afterId(*(mtpMsgId*)(from->after->data() + 4)); + mtpRequestMap::const_iterator i = afterId ? haveSent.constFind(afterId) : haveSent.cend(); + int32 size = to->size(), len = (*from)[7] >> 2, headlen = 4, fulllen = headlen + len; + if (i == haveSent.constEnd()) { // no invoke after or such msg was not sent or was completed recently + to->resize(size + fulllen); + memcpy(to->data() + size, from->constData() + 4, fulllen * sizeof(mtpPrime)); + } else { + to->resize(size + fulllen + 3); + memcpy(to->data() + size, from->constData() + 4, headlen * sizeof(mtpPrime)); + (*to)[size + 3] += 3 * sizeof(mtpPrime); + *((mtpTypeId*)&((*to)[size + headlen])) = mtpc_invokeAfterMsg; + memcpy(to->data() + size + headlen + 1, &afterId, 2 * sizeof(mtpPrime)); + memcpy(to->data() + size + headlen + 3, from->constData() + 4 + headlen, len * sizeof(mtpPrime)); + if (size + 3 != 7) (*to)[7] += 3 * sizeof(mtpPrime); } } @@ -317,7 +413,6 @@ namespace _mtp_internal { if (errorCode && found) { rpcErrorOccured(requestId, h, rpcClientError("CLEAR_CALLBACK", QString("did not handle request %1, error code %2").arg(requestId).arg(errorCode))); } - _mtp_internal::unregisterRequest(requestId); } void clearCallbacksDelayed(const RPCCallbackClears &requestIds) { @@ -353,6 +448,7 @@ namespace _mtp_internal { } } clearCallbacks(i->requestId, i->errorCode); + _mtp_internal::unregisterRequest(i->requestId); } toClear.clear(); } @@ -443,12 +539,17 @@ namespace _mtp_internal { } } - RequestMap::const_iterator j = requestMap.constFind(requestId); - if (j == requestMap.cend()) { - DEBUG_LOG(("MTP Error: could not find request %1").arg(requestId)); - continue; + mtpRequest req; + { + QReadLocker locker(&requestMapLock); + RequestMap::const_iterator j = requestMap.constFind(requestId); + if (j == requestMap.cend()) { + DEBUG_LOG(("MTP Error: could not find request %1").arg(requestId)); + continue; + } + req = j.value(); } - _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(j.value(), 0, false); + _mtp_internal::getSession(dc < 0 ? (-dc) : dc)->sendPrepared(req, 0, false); } if (!delayedRequests.isEmpty()) { @@ -577,11 +678,21 @@ namespace MTP { } void cancel(mtpRequestId requestId) { + mtpMsgId msgId = 0; + { + QWriteLocker locker(&requestMapLock); + RequestMap::iterator i = requestMap.find(requestId); + if (i != requestMap.end()) { + msgId = *(mtpMsgId*)(i.value()->constData() + 4); + requestMap.erase(i); + } + } { QMutexLocker locker(&requestByDCLock); RequestsByDC::iterator i = requestsByDC.find(requestId); if (i != requestsByDC.end()) { - _mtp_internal::getSession(abs(i.value()))->cancel(requestId); + _mtp_internal::getSession(abs(i.value()))->cancel(requestId, msgId); + requestsByDC.erase(i); } } _mtp_internal::clearCallbacks(requestId); diff --git a/Telegram/SourceFiles/mtproto/mtp.h b/Telegram/SourceFiles/mtproto/mtp.h index a18d8c0f100bc8..4d1f712f6b09ad 100644 --- a/Telegram/SourceFiles/mtproto/mtp.h +++ b/Telegram/SourceFiles/mtproto/mtp.h @@ -31,7 +31,8 @@ namespace _mtp_internal { static const uint32 dcShift = 10000; mtpRequestId storeRequest(mtpRequest &request, const RPCResponseHandler &parser); - void replaceRequest(mtpRequest &newRequest, const mtpRequest &oldRequest); + mtpRequest getRequest(mtpRequestId req); + void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent); void clearCallbacks(mtpRequestId requestId, int32 errorCode = RPCError::NoError); // 0 - do not toggle onError callback void clearCallbacksDelayed(const RPCCallbackClears &requestIds); void performDelayedClear(); @@ -85,12 +86,12 @@ namespace MTP { 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) { - return _mtp_internal::getSession(dc)->send(request, callbacks, msCanWait, _mtp_internal::getLayer(), !dc); + inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) { + return _mtp_internal::getSession(dc)->send(request, callbacks, msCanWait, _mtp_internal::getLayer(), !dc, after); } template - inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0) { - return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait); + inline mtpRequestId send(const TRequest &request, RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail = RPCFailHandlerPtr(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) { + return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait, after); } void cancel(mtpRequestId req); void killSession(int32 dc); diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.cpp b/Telegram/SourceFiles/mtproto/mtpConnection.cpp index dc678f483d61c5..c2ffe7bc0f2205 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.cpp +++ b/Telegram/SourceFiles/mtproto/mtpConnection.cpp @@ -1051,7 +1051,6 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne , oldConnection(true) , receiveDelay(MinReceiveDelay) , firstSentAt(-1) - , ackRequest(MTP_msgs_ack(MTPVector())) , pingId(0) , toSendPingId(0) , pingMsgId(0) @@ -1059,9 +1058,8 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne , keyId(0) , sessionData(data) , myKeyLock(false) - , authKeyData(0) { - - ackRequestData = &ackRequest._msgs_ack().vmsg_ids._vector().v; + , authKeyData(0) + , authKeyStrings(0) { oldConnectionTimer.moveToThread(thread); connCheckTimer.moveToThread(thread); @@ -1258,6 +1256,13 @@ void MTProtoConnectionPrivate::resetSession() { // recreate all msg_id and msg_s } } + ackRequestData.clear(); + resendRequestData.clear(); + { + QWriteLocker locker5(sessionData->stateRequestMutex()); + sessionData->stateRequestMap().clear(); + } + emit sessionResetDone(); } @@ -1341,31 +1346,36 @@ mtpMsgId MTProtoConnectionPrivate::replaceMsgId(mtpRequest &request, mtpMsgId ne return newId; } +mtpMsgId MTProtoConnectionPrivate::placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req) { + mtpMsgId msgId = prepareToSend(req, bigMsgId); + if (msgId > bigMsgId) msgId = replaceMsgId(req, bigMsgId); + if (msgId >= bigMsgId) bigMsgId = msgid(); + *(haveSentArr++) = msgId; + + uint32 from = toSendRequest->size(), len = mtpRequestData::messageSize(req); + toSendRequest->resize(from + len); + memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime)); + + return msgId; +} + void MTProtoConnectionPrivate::tryToSend() { if (!conn) return; - bool prependOnly = false, havePrepend = false; - mtpRequest prepend; + bool prependOnly = false; + mtpRequest pingRequest; if (toSendPingId) { -/* - MTPPing_delay_disconnect ping; - ping.ping_id = MTP_long(toSendPingId); - ping.disconnect_delay.v = 45; - - DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, delay: %1s, ping_id: %2").arg(ping.disconnect_delay.v).arg(ping.ping_id.v)); -/**/ MTPPing ping(MTPping(MTP_long(toSendPingId))); prependOnly = (getState() != MTProtoConnection::Connected); DEBUG_LOG(("MTP Info: sending ping, ping_id: %1, prepend_only: %2").arg(ping.vping_id.v).arg(prependOnly ? "[TRUE]" : "[FALSE]")); uint32 pingSize = ping.size() >> 2; // copy from MTProtoSession::send - prepend = mtpRequestData::prepare(pingSize); - ping.write(*prepend); + pingRequest = mtpRequestData::prepare(pingSize); + ping.write(*pingRequest); - prepend->msDate = getms(); // > 0 - can send without container - prepend->requestId = 0; // dont add to haveSent / wereAcked maps - havePrepend = true; + pingRequest->msDate = getms(); // > 0 - can send without container + pingRequest->requestId = 0; // dont add to haveSent / wereAcked maps pingId = toSendPingId; toSendPingId = 0; @@ -1377,6 +1387,53 @@ void MTProtoConnectionPrivate::tryToSend() { } } + mtpRequest ackRequest, resendRequest, stateRequest; + if (!prependOnly && !ackRequestData.isEmpty()) { + MTPMsgsAck ack(MTP_msgs_ack(MTP_vector(ackRequestData))); + + ackRequest = mtpRequestData::prepare(ack.size() >> 2); + ack.write(*ackRequest); + + ackRequest->msDate = getms(); // > 0 - can send without container + ackRequest->requestId = 0; // dont add to haveSent / wereAcked maps + + ackRequestData.clear(); + } + if (!prependOnly && !resendRequestData.isEmpty()) { + MTPMsgResendReq resend(MTP_msg_resend_req(MTP_vector(resendRequestData))); + + resendRequest = mtpRequestData::prepare(resend.size() >> 2); + resend.write(*resendRequest); + + resendRequest->msDate = getms(); // > 0 - can send without container + resendRequest->requestId = 0; // dont add to haveSent / wereAcked maps + + resendRequestData.clear(); + } + if (!prependOnly) { + QVector stateReq; + { + QWriteLocker locker(sessionData->stateRequestMutex()); + mtpMsgIdsSet &ids(sessionData->stateRequestMap()); + if (!ids.isEmpty()) { + stateReq.reserve(ids.size()); + for (mtpMsgIdsSet::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { + stateReq.push_back(MTP_long(i.key())); + } + } + ids.clear(); + } + if (!stateReq.isEmpty()) { + MTPMsgsStateReq req(MTP_msgs_state_req(MTP_vector(stateReq))); + + stateRequest = mtpRequestData::prepare(req.size() >> 2); + req.write(*stateRequest); + + stateRequest->msDate = getms(); // > 0 - can send without container + stateRequest->requestId = reqid();// add to haveSent / wereAcked maps, but don't add to requestMap + } + } + bool needAnyResponse = false; mtpRequest toSendRequest; { @@ -1386,11 +1443,14 @@ void MTProtoConnectionPrivate::tryToSend() { if (prependOnly) locker1.unlock(); uint32 toSendCount = toSend.size(); - if (havePrepend) ++toSendCount; + if (pingRequest) ++toSendCount; + if (ackRequest) ++toSendCount; + if (resendRequest) ++toSendCount; + if (stateRequest) ++toSendCount; if (!toSendCount) return; // nothing to send - mtpRequest first = havePrepend ? prepend : toSend.cbegin().value(); + mtpRequest first = pingRequest ? pingRequest : (ackRequest ? ackRequest : (resendRequest ? resendRequest : (stateRequest ? stateRequest : toSend.cbegin().value()))); if (toSendCount == 1 && first->msDate > 0) { // if can send without container toSendRequest = first; if (!prependOnly) { @@ -1399,14 +1459,28 @@ void MTProtoConnectionPrivate::tryToSend() { } mtpMsgId msgId = prepareToSend(toSendRequest, msgid()); - if (havePrepend) pingMsgId = msgId; + if (pingRequest) { + pingMsgId = msgId; + needAnyResponse = true; + } else if (resendRequest || stateRequest) { + needAnyResponse = true; + } if (toSendRequest->requestId) { if (mtpRequestData::needAck(toSendRequest)) { toSendRequest->msDate = mtpRequestData::isStateRequest(toSendRequest) ? 0 : getms(); QWriteLocker locker2(sessionData->haveSentMutex()); - sessionData->haveSentMap().insert(msgId, toSendRequest); + mtpRequestMap &haveSent(sessionData->haveSentMap()); + haveSent.insert(msgId, toSendRequest); + if (toSendRequest->after) { + int32 toSendSize = toSendRequest->at(7) >> 2; + mtpRequest wrappedRequest(mtpRequestData::prepare(toSendSize, toSendSize + 3)); // cons + msg_id + wrappedRequest->resize(4); + memcpy(wrappedRequest->data(), toSendRequest->constData(), 4 * sizeof(mtpPrime)); + _mtp_internal::wrapInvokeAfter(wrappedRequest, toSendRequest, haveSent); + toSendRequest = wrappedRequest; + } needAnyResponse = true; } else { @@ -1416,11 +1490,14 @@ void MTProtoConnectionPrivate::tryToSend() { } } else { // send in container uint32 containerSize = 1 + 1, idsWrapSize = (toSendCount << 1); // cons + vector size, idsWrapSize - size of "request-like" wrap for msgId vector - if (havePrepend) containerSize += mtpRequestData::messageSize(prepend); + if (pingRequest) containerSize += mtpRequestData::messageSize(pingRequest); + if (ackRequest) containerSize += mtpRequestData::messageSize(ackRequest); + if (resendRequest) containerSize += mtpRequestData::messageSize(resendRequest); + if (stateRequest) containerSize += mtpRequestData::messageSize(stateRequest); for (mtpPreRequestMap::iterator i = toSend.begin(), e = toSend.end(); i != e; ++i) { containerSize += mtpRequestData::messageSize(i.value()); } - toSendRequest = mtpRequestData::prepare(containerSize); // prepare container + toSendRequest = mtpRequestData::prepare(containerSize, containerSize + 3 * toSend.size()); // prepare container + each in invoke after toSendRequest->push_back(mtpc_msg_container); toSendRequest->push_back(toSendCount); @@ -1437,17 +1514,10 @@ void MTProtoConnectionPrivate::tryToSend() { haveSentIdsWrap->resize(haveSentIdsWrap->size() + idsWrapSize); mtpMsgId *haveSentArr = (mtpMsgId*)(haveSentIdsWrap->data() + 8); - if (havePrepend) { - mtpMsgId msgId = prepareToSend(prepend, bigMsgId); - if (msgId > bigMsgId) msgId = replaceMsgId(prepend, bigMsgId); - if (msgId >= bigMsgId) bigMsgId = msgid(); - *(haveSentArr++) = msgId; - if (havePrepend) pingMsgId = msgId; - - uint32 from = toSendRequest->size(), len = mtpRequestData::messageSize(prepend); - toSendRequest->resize(from + len); - memcpy(toSendRequest->data() + from, prepend->constData() + 4, len * sizeof(mtpPrime)); - + if (pingRequest) { + pingMsgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, pingRequest); + needAnyResponse = true; + } else if (resendRequest || stateRequest) { needAnyResponse = true; } for (mtpPreRequestMap::iterator i = toSend.begin(), e = toSend.end(); i != e; ++i) { @@ -1456,10 +1526,14 @@ void MTProtoConnectionPrivate::tryToSend() { if (msgId > bigMsgId) msgId = replaceMsgId(req, bigMsgId); if (msgId >= bigMsgId) bigMsgId = msgid(); *(haveSentArr++) = msgId; - + bool added = false; if (req->requestId) { if (mtpRequestData::needAck(req)) { req->msDate = mtpRequestData::isStateRequest(req) ? 0 : getms(); + if (req->after) { + _mtp_internal::wrapInvokeAfter(toSendRequest, req, haveSent); + added = true; + } haveSent.insert(msgId, req); needAnyResponse = true; @@ -1467,10 +1541,19 @@ void MTProtoConnectionPrivate::tryToSend() { wereAcked.insert(msgId, req->requestId); } } - uint32 from = toSendRequest->size(), len = mtpRequestData::messageSize(req); - toSendRequest->resize(from + len); - memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime)); + if (!added) { + uint32 from = toSendRequest->size(), len = mtpRequestData::messageSize(req); + toSendRequest->resize(from + len); + memcpy(toSendRequest->data() + from, req->constData() + 4, len * sizeof(mtpPrime)); + } + } + if (stateRequest) { + mtpMsgId msgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, stateRequest); + stateRequest->msDate = 0; // 0 for state request, do not request state of it + haveSent.insert(msgId, stateRequest); } + if (resendRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, resendRequest); + if (ackRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, ackRequest); mtpMsgId contMsgId = prepareToSend(toSendRequest, bigMsgId); *(mtpMsgId*)(haveSentIdsWrap->data() + 4) = contMsgId; @@ -1760,7 +1843,7 @@ void MTProtoConnectionPrivate::handleReceived() { serverSalt = 0; // dont pass to handle method, so not to lock in setSalt() } - if (needAck) ackRequestData->push_back(MTP_long(msgId)); + if (needAck) ackRequestData.push_back(MTP_long(msgId)); int32 res = 1; // if no need to handle, then succeed end = data + 8 + (msgLen >> 2); @@ -1770,7 +1853,7 @@ void MTProtoConnectionPrivate::handleReceived() { bool needToHandle = false; { QWriteLocker lock(sessionData->receivedIdsMutex()); - mtpMsgIdsSet &receivedIds(sessionData->receivedIdsSet()); + mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); needToHandle = receivedIds.insert(msgId, needAck); } if (needToHandle) { @@ -1778,7 +1861,7 @@ void MTProtoConnectionPrivate::handleReceived() { } { QWriteLocker lock(sessionData->receivedIdsMutex()); - mtpMsgIdsSet &receivedIds(sessionData->receivedIdsSet()); + mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); uint32 receivedIdsSize = receivedIds.size(); while (receivedIdsSize-- > MTPIdsBufferSize) { receivedIds.erase(receivedIds.begin()); @@ -1786,11 +1869,10 @@ void MTProtoConnectionPrivate::handleReceived() { } // send acks - uint32 toAckSize = ackRequestData->size(); + uint32 toAckSize = ackRequestData.size(); if (toAckSize) { - DEBUG_LOG(("MTP Info: sending %1 acks, ids: %2").arg(toAckSize).arg(logVectorLong(*ackRequestData))); - sessionData->owner()->send(ackRequest, RPCResponseHandler(), 10000); - ackRequestData->clear(); + DEBUG_LOG(("MTP Info: will send %1 acks, ids: %2").arg(toAckSize).arg(logVectorLong(ackRequestData))); + sessionData->owner()->sendAnything(MTPAckSendWaiting); } bool emitSignal = false; @@ -1867,7 +1949,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt } bool needAck = (inSeqNo.v & 0x01); - if (needAck) ackRequestData->push_back(inMsgId); + if (needAck) ackRequestData.push_back(inMsgId); DEBUG_LOG(("Message Info: message from container, msg_id: %1, needAck: %2").arg(inMsgId.v).arg(logBool(needAck))); @@ -1877,7 +1959,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt bool needToHandle = false; { QWriteLocker lock(sessionData->receivedIdsMutex()); - mtpMsgIdsSet &receivedIds(sessionData->receivedIdsSet()); + mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); needToHandle = receivedIds.insert(inMsgId.v, needAck); } int32 res = 1; // if no need to handle, then succeed @@ -1916,6 +1998,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt const MTPDbad_msg_notification &data(msg.c_bad_msg_notification()); LOG(("Message Info: bad message notification received (error_code %3) for msg_id = %1, seq_no = %2").arg(data.vbad_msg_id.v).arg(data.vbad_msg_seqno.v).arg(data.verror_code.v)); + mtpMsgId resendId = data.vbad_msg_id.v; int32 errorCode = data.verror_code.v; if (errorCode == 16 || errorCode == 17 || errorCode == 32 || errorCode == 33 || errorCode == 64) { // can handle bool needResend = (errorCode == 16 || errorCode == 17); // bad msg_id @@ -1927,12 +2010,12 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt QWriteLocker locker(sessionData->haveSentMutex()); mtpRequestMap &haveSent(sessionData->haveSentMap()); - mtpRequestMap::iterator i = haveSent.find(msgId); - if (i == haveSent.end()) { + mtpRequestMap::const_iterator i = haveSent.constFind(resendId); + if (i == haveSent.cend()) { LOG(("Message Error: Container not found!")); + } else { + request = i.value(); } - - request = i.value(); } if (request) { if (mtpRequestData::isSentContainer(request)) { @@ -1949,7 +2032,6 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt } } - mtpMsgId resendId = data.vbad_msg_id.v; if (!wasSent(resendId)) { DEBUG_LOG(("Message Error: such message was not sent recently %1").arg(resendId)); return (badTime ? 0 : 1); @@ -2029,8 +2111,8 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt { QReadLocker lock(sessionData->receivedIdsMutex()); - const mtpMsgIdsSet &receivedIds(sessionData->receivedIdsSet()); - mtpMsgIdsSet::const_iterator receivedIdsEnd(receivedIds.cend()); + const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); + mtpMsgIdsMap::const_iterator receivedIdsEnd(receivedIds.cend()); uint64 minRecv = receivedIds.min(), maxRecv = receivedIds.max(); QReadLocker locker(sessionData->wereAckedMutex()); @@ -2045,7 +2127,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt } else if (reqMsgId > maxRecv) { state |= 0x03; } else { - mtpMsgIdsSet::const_iterator recv = receivedIds.constFind(reqMsgId); + mtpMsgIdsMap::const_iterator recv = receivedIds.constFind(reqMsgId); if (recv == receivedIdsEnd) { state |= 0x02; } else { @@ -2074,7 +2156,7 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt uint64 reqMsgId = data.vreq_msg_id.v; const string &states(data.vinfo.c_string().v); - DEBUG_LOG(("Message Info: msg state received, msgId %1, reqMsgId: %2, states %3").arg(msgId).arg(reqMsgId).arg(mb(states.data(), states.length()).str())); + DEBUG_LOG(("Message Info: msg state received, msgId %1, reqMsgId: %2, HEX states %3").arg(msgId).arg(reqMsgId).arg(mb(states.data(), states.length()).str())); mtpRequest requestBuffer; { // find this request in session-shared sent requests map QReadLocker locker(sessionData->haveSentMutex()); @@ -2158,14 +2240,14 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt MTPlong resMsgId = data.vanswer_msg_id; { QReadLocker lock(sessionData->receivedIdsMutex()); - const mtpMsgIdsSet &receivedIds(sessionData->receivedIdsSet()); + const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); received = (receivedIds.find(resMsgId.v) != receivedIds.cend()) && (receivedIds.min() < resMsgId.v); } - if (!received) { + if (received) { + ackRequestData.push_back(resMsgId); + } else { DEBUG_LOG(("Message Info: answer message %1 was not received, requesting..").arg(resMsgId.v)); - MTPMsgResendReq resendRequest(MTP_msg_resend_req(MTPVector(1))); - resendRequest._msg_resend_req().vmsg_ids._vector().v.push_back(resMsgId); - sessionData->owner()->send(resendRequest); + resendRequestData.push_back(resMsgId); } } return 1; @@ -2183,14 +2265,14 @@ int32 MTProtoConnectionPrivate::handleOneReceived(const mtpPrime *from, const mt MTPlong resMsgId = data.vanswer_msg_id; { QReadLocker lock(sessionData->receivedIdsMutex()); - const mtpMsgIdsSet &receivedIds(sessionData->receivedIdsSet()); + const mtpMsgIdsMap &receivedIds(sessionData->receivedIdsSet()); received = (receivedIds.find(resMsgId.v) != receivedIds.cend()) && (receivedIds.min() < resMsgId.v); } - if (!received) { + if (received) { + ackRequestData.push_back(resMsgId); + } else { DEBUG_LOG(("Message Info: answer message %1 was not received, requesting..").arg(resMsgId.v)); - MTPMsgResendReq resendRequest(MTP_msg_resend_req(MTPVector(1))); - resendRequest._msg_resend_req().vmsg_ids._vector().v.push_back(resMsgId); - sessionData->owner()->send(resendRequest); + resendRequestData.push_back(resMsgId); } } return 1; @@ -2577,6 +2659,7 @@ void MTProtoConnectionPrivate::onConnected() { } authKeyData = new MTProtoConnectionPrivate::AuthKeyCreateData(); + authKeyStrings = new MTProtoConnectionPrivate::AuthKeyCreateStrings(); authKeyData->req_num = 0; authKeyData->nonce = MTP::nonce(); @@ -2815,9 +2898,9 @@ void MTProtoConnectionPrivate::dhParamsAnswered() { return restart(); } - authKeyData->dh_prime = dhPrime; + authKeyStrings->dh_prime = QByteArray(dhPrime.data(), dhPrime.size()); authKeyData->g = dh_inner_data.vg.v; - authKeyData->g_a = g_a; + authKeyStrings->g_a = QByteArray(g_a.data(), g_a.size()); authKeyData->retry_id = MTP_long(0); authKeyData->retries = 0; } return dhClientParamsSend(); @@ -2867,7 +2950,7 @@ void MTProtoConnectionPrivate::dhClientParamsSend() { // count g_b and auth_key using openssl BIGNUM methods _BigNumCounter bnCounter; - if (!bnCounter.count(b, &authKeyData->dh_prime[0], authKeyData->g, g_b, &authKeyData->g_a[0], authKeyData->auth_key)) { + if (!bnCounter.count(b, authKeyStrings->dh_prime.constData(), authKeyData->g, g_b, authKeyStrings->g_a.constData(), authKeyData->auth_key)) { return dhClientParamsSend(); } @@ -3027,17 +3110,23 @@ void MTProtoConnectionPrivate::authKeyCreated() { void MTProtoConnectionPrivate::clearAuthKeyData() { if (authKeyData) { #ifdef Q_OS_WIN // TODO -// SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData)); + SecureZeroMemory(authKeyData, sizeof(AuthKeyCreateData)); + if (!authKeyStrings->dh_prime.isEmpty()) SecureZeroMemory(authKeyStrings->dh_prime.data(), authKeyStrings->dh_prime.size()); + if (!authKeyStrings->g_a.isEmpty()) SecureZeroMemory(authKeyStrings->g_a.data(), authKeyStrings->g_a.size()); #else -// memset(authKeyData, 0, sizeof(AuthKeyCreateData)); + memset(authKeyData, 0, sizeof(AuthKeyCreateData)); + if (!authKeyStrings->dh_prime.isEmpty()) memset(authKeyStrings->dh_prime.data(), 0, authKeyStrings->dh_prime.size()); + if (!authKeyStrings->g_a.isEmpty()) memset(authKeyStrings->g_a.data(), 0, authKeyStrings->g_a.size()); #endif delete authKeyData; authKeyData = 0; + delete authKeyStrings; + authKeyStrings = 0; } } void MTProtoConnectionPrivate::sendPing() { - sessionData->owner()->send(MTPPing(MTPping(MTP::nonce()))); + sessionData->owner()->send(MTPPing(MTP::nonce())); } void MTProtoConnectionPrivate::onError(bool mayBeBadKey) { diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.h b/Telegram/SourceFiles/mtproto/mtpConnection.h index c76411c29093b2..f5b629f9a2c7c5 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.h +++ b/Telegram/SourceFiles/mtproto/mtpConnection.h @@ -333,6 +333,7 @@ public slots: void createConn(); + mtpMsgId placeToContainer(mtpRequest &toSendRequest, mtpMsgId &bigMsgId, mtpMsgId *&haveSentArr, mtpRequest &req); mtpMsgId prepareToSend(mtpRequest &request, mtpMsgId currentLastId); mtpMsgId replaceMsgId(mtpRequest &request, mtpMsgId newId); @@ -367,8 +368,7 @@ public slots: uint32 receiveDelay; int64 firstSentAt; - MTPMsgsAck ackRequest; - QVector *ackRequestData; + QVector ackRequestData, resendRequestData; // if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found bool requestsFixTimeSalt(const QVector &ids, int32 serverTime, uint64 serverSalt); @@ -417,10 +417,8 @@ public slots: uint32 retries; MTPlong retry_id; - string dh_prime; int32 g; - string g_a; - + uchar aesKey[32], aesIV[32]; uint32 auth_key[64]; MTPlong auth_key_hash; @@ -428,7 +426,13 @@ public slots: uint32 req_num; // sent not encrypted request number uint32 msgs_sent; }; + struct AuthKeyCreateStrings { + QByteArray dh_prime; + QByteArray g_a; + }; AuthKeyCreateData *authKeyData; + AuthKeyCreateStrings *authKeyStrings; + void dhClientParamsSend(); void authKeyCreated(); void clearAuthKeyData(); diff --git a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h index 973bc6cfab4c16..116a8be4f4e615 100644 --- a/Telegram/SourceFiles/mtproto/mtpCoreTypes.h +++ b/Telegram/SourceFiles/mtproto/mtpCoreTypes.h @@ -75,13 +75,15 @@ class mtpRequestData : public mtpBuffer { // in haveSent: = 0 - container with msgIds, > 0 - when was sent uint64 msDate; mtpRequestId requestId; + mtpRequest after; - mtpRequestData(bool/* sure*/) : msDate(0) { + mtpRequestData(bool/* sure*/) : msDate(0), requestId(0) { } - static mtpRequest prepare(uint32 requestSize) { + static mtpRequest prepare(uint32 requestSize, uint32 maxSize = 0) { + if (!maxSize) maxSize = requestSize; mtpRequest result(new mtpRequestData(true)); - result->reserve(8 + requestSize + _padding(requestSize)); // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length + result->reserve(8 + maxSize + _padding(maxSize)); // 2: salt, 2: session_id, 2: msg_id, 1: seq_no, 1: message_length result->resize(7); result->push_back(requestSize << 2); return result; @@ -150,8 +152,8 @@ class mtpResponse : public mtpBuffer { typedef QMap mtpPreRequestMap; typedef QMap mtpRequestMap; - -class mtpMsgIdsSet : public QMap { +typedef QMap mtpMsgIdsSet; +class mtpMsgIdsMap : public QMap { public: typedef QMap ParentType; @@ -172,12 +174,12 @@ class mtpMsgIdsSet : public QMap { } mtpMsgId min() const { - return size() ? cbegin().key() : 0; + return isEmpty() ? 0 : cbegin().key(); } mtpMsgId max() const { ParentType::const_iterator e(cend()); - return size() ? (--e).key() : 0; + return isEmpty() ? 0 : (--e).key(); } }; @@ -815,6 +817,8 @@ class MTPDvector : public mtpDataImpl > { } MTPDvector(uint32 count) : v(count) { } + MTPDvector(uint32 count, const T &value) : v(count, value) { + } MTPDvector(const QVector &vec) : v(vec) { } @@ -829,6 +833,9 @@ class MTPvector; template MTPvector MTP_vector(uint32 count); +template +MTPvector MTP_vector(uint32 count, const T &value); + template MTPvector MTP_vector(const QVector &v); @@ -886,6 +893,7 @@ class MTPvector : private mtpDataOwner { } friend MTPvector MTP_vector(uint32 count); + friend MTPvector MTP_vector(uint32 count, const T &value); friend MTPvector MTP_vector(const QVector &v); typedef typename MTPDvector::VType VType; }; @@ -894,6 +902,10 @@ inline MTPvector MTP_vector(uint32 count) { return MTPvector(new MTPDvector(count)); } template +inline MTPvector MTP_vector(uint32 count, const T &value) { + return MTPvector(new MTPDvector(count, value)); +} +template inline MTPvector MTP_vector(const QVector &v) { return MTPvector(new MTPDvector(v)); } @@ -904,6 +916,8 @@ class MTPVector : public MTPBoxed > { } MTPVector(uint32 count) : MTPBoxed >(MTP_vector(count)) { } + MTPVector(uint32 count, const T &value) : MTPBoxed >(MTP_vector(count, value)) { + } MTPVector(const MTPvector &v) : MTPBoxed >(v) { } MTPVector(const mtpPrime *&from, const mtpPrime *end, mtpTypeId cons = 0) : MTPBoxed >(from, end, cons) { diff --git a/Telegram/SourceFiles/mtproto/mtpSession.cpp b/Telegram/SourceFiles/mtproto/mtpSession.cpp index 0b650ad0bc0c97..6b446d0fe9c98a 100644 --- a/Telegram/SourceFiles/mtproto/mtpSession.cpp +++ b/Telegram/SourceFiles/mtproto/mtpSession.cpp @@ -133,12 +133,35 @@ void MTProtoSession::stop() { } } -void MTProtoSession::checkRequestsByTimer() { - MTPMsgsStateReq stateRequest(MTP_msgs_state_req(MTP_vector(0))); - QVector &stateRequestIds(stateRequest._msgs_state_req().vmsg_ids._vector().v); +void MTProtoSession::sendAnything(uint64 msCanWait) { + uint64 ms = getms(); + if (msSendCall) { + if (ms > msSendCall + msWait) { + msWait = 0; + } else { + msWait = (msSendCall + msWait) - ms; + if (msWait > msCanWait) { + msWait = msCanWait; + } + } + } else { + msWait = msCanWait; + } + if (msWait) { + msSendCall = ms; + emit startSendTimer(msWait); + DEBUG_LOG(("MTP Info: can wait for %1ms from current %2").arg(msWait).arg(msSendCall)); + } else { + emit stopSendTimer(); + msSendCall = 0; + emit needToSendAsync(); + } +} +void MTProtoSession::checkRequestsByTimer() { QVector resendingIds; QVector removingIds; // remove very old (10 minutes) containers and resend requests + QVector stateRequestIds; { QReadLocker locker(data.haveSentMutex()); @@ -155,7 +178,7 @@ void MTProtoSession::checkRequestsByTimer() { } else { req->msDate = ms; stateRequestIds.reserve(haveSentCount); - stateRequestIds.push_back(MTP_long(i.key())); + stateRequestIds.push_back(i.key()); } } } else if (unixtime() > (int32)(i.key() >> 32) + MTPContainerLives) { @@ -167,19 +190,26 @@ void MTProtoSession::checkRequestsByTimer() { if (stateRequestIds.size()) { DEBUG_LOG(("MTP Info: requesting state of msgs: %1").arg(logVectorLong(stateRequestIds))); - send(stateRequest, RPCResponseHandler(), MTPCheckResendWaiting); + { + QWriteLocker locker(data.stateRequestMutex()); + for (uint32 i = 0, l = stateRequestIds.size(); i < l; ++i) { + data.stateRequestMap().insert(stateRequestIds[i], true); + } + } + sendAnything(MTPCheckResendWaiting); } - for (uint32 i = 0, l = resendingIds.size(); i < l; ++i) { - DEBUG_LOG(("MTP Info: resending request %1").arg(resendingIds[i])); - resend(resendingIds[i], MTPCheckResendWaiting); + if (!resendingIds.isEmpty()) { + for (uint32 i = 0, l = resendingIds.size(); i < l; ++i) { + DEBUG_LOG(("MTP Info: resending request %1").arg(resendingIds[i])); + resend(resendingIds[i], MTPCheckResendWaiting); + } } - uint32 removingIdsCount = removingIds.size(); - if (removingIdsCount) { + if (!removingIds.isEmpty()) { RPCCallbackClears clearCallbacks; { QWriteLocker locker(data.haveSentMutex()); mtpRequestMap &haveSent(data.haveSentMap()); - for (uint32 i = 0; i < removingIdsCount; ++i) { + for (uint32 i = 0, l = removingIds.size(); i < l; ++i) { mtpRequestMap::iterator j = haveSent.find(removingIds[i]); if (j != haveSent.cend()) { if (j.value()->requestId) { @@ -201,11 +231,15 @@ void MTProtoSession::onResetDone() { _mtp_internal::onSessionReset(dcId); } -void MTProtoSession::cancel(mtpRequestId requestId) { - QWriteLocker locker(data.toSendMutex()); - mtpPreRequestMap &toSend(data.toSendMap()); - mtpPreRequestMap::iterator i = toSend.find(requestId); - if (i != toSend.end()) toSend.erase(i); +void MTProtoSession::cancel(mtpRequestId requestId, mtpMsgId msgId) { + if (requestId) { + QWriteLocker locker(data.toSendMutex()); + data.toSendMap().remove(requestId); + } + if (msgId) { + QWriteLocker locker(data.haveSentMutex()); + data.haveSentMap().remove(msgId); + } } int32 MTProtoSession::requestState(mtpRequestId requestId) const { @@ -340,28 +374,7 @@ void MTProtoSession::sendPrepared(const mtpRequest &request, uint64 msCanWait, b DEBUG_LOG(("MTP Info: added, requestId %1").arg(request->requestId)); - uint64 ms = getms(); - if (msSendCall) { - if (ms > msSendCall + msWait) { - msWait = 0; - } else { - msWait = (msSendCall + msWait) - ms; - if (msWait > msCanWait) { - msWait = msCanWait; - } - } - } else { - msWait = msCanWait; - } - if (msWait) { - msSendCall = ms; - emit startSendTimer(msWait); - DEBUG_LOG(("MTP Info: can wait for %1ms from current %2").arg(msWait).arg(msSendCall)); - } else { - emit stopSendTimer(); - msSendCall = 0; - emit needToSendAsync(); - } + sendAnything(msCanWait); } void MTProtoSession::sendPreparedWithInit(const mtpRequest &request, uint64 msCanWait) { // returns true, if emit of needToSend() is needed @@ -369,14 +382,16 @@ void MTProtoSession::sendPreparedWithInit(const mtpRequest &request, uint64 msCa sendPrepared(request, msCanWait, false); return; } - MTPInitConnection requestWrap(MTPinitConnection(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request)); - uint32 requestSize = requestWrap.size() >> 2; - mtpRequest reqSerialized(mtpRequestData::prepare(requestSize)); - requestWrap.write(*reqSerialized); - - reqSerialized->msDate = getms(); // > 0 - can send without container - _mtp_internal::replaceRequest(reqSerialized, request); - sendPrepared(reqSerialized, msCanWait); + { + MTPInitConnection requestWrap(MTPinitConnection(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request)); + uint32 requestSize = requestWrap.size() >> 2; + mtpRequest reqSerialized(mtpRequestData::prepare(requestSize)); + requestWrap.write(*reqSerialized); + request->resize(reqSerialized->size()); + memcpy(request->data(), reqSerialized->constData(), reqSerialized->size()); + } + request->msDate = getms(); // > 0 - can send without container + sendPrepared(request, msCanWait); } QReadWriteLock *MTProtoSession::keyMutex() const { diff --git a/Telegram/SourceFiles/mtproto/mtpSession.h b/Telegram/SourceFiles/mtproto/mtpSession.h index 2ba95cb33d2deb..3977f7efe54935 100644 --- a/Telegram/SourceFiles/mtproto/mtpSession.h +++ b/Telegram/SourceFiles/mtproto/mtpSession.h @@ -98,6 +98,9 @@ class MTPSessionData { QReadWriteLock *haveReceivedMutex() const { return &haveReceivedLock; } + QReadWriteLock *stateRequestMutex() const { + return &stateRequestLock; + } mtpPreRequestMap &toSendMap() { return toSend; @@ -117,10 +120,10 @@ class MTPSessionData { const mtpRequestIdsMap &toResendMap() const { return toResend; } - mtpMsgIdsSet &receivedIdsSet() { + mtpMsgIdsMap &receivedIdsSet() { return receivedIds; } - const mtpMsgIdsSet &receivedIdsSet() const { + const mtpMsgIdsMap &receivedIdsSet() const { return receivedIds; } mtpRequestIdsMap &wereAckedMap() { @@ -135,6 +138,12 @@ class MTPSessionData { const mtpResponseMap &haveReceivedMap() const { return haveReceived; } + mtpMsgIdsSet &stateRequestMap() { + return stateRequest; + } + const mtpMsgIdsSet &stateRequestMap() const { + return stateRequest; + } mtpRequestId nextFakeRequestId() { // must be locked by haveReceivedMutex() if (haveReceived.isEmpty() || haveReceived.cbegin().key() > 0) { @@ -175,9 +184,10 @@ class MTPSessionData { mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent mtpRequestMap haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers mtpRequestIdsMap toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent - mtpMsgIdsSet receivedIds; // set of received msg_id's, for checking new msg_ids + mtpMsgIdsMap receivedIds; // set of received msg_id's, for checking new msg_ids mtpRequestIdsMap wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread + mtpMsgIdsSet stateRequest; // set of msg_id's, whose state should be requested // mutexes mutable QReadWriteLock lock; @@ -187,6 +197,7 @@ class MTPSessionData { mutable QReadWriteLock receivedIdsLock; mutable QReadWriteLock wereAckedLock; mutable QReadWriteLock haveReceivedLock; + mutable QReadWriteLock stateRequestLock; }; @@ -209,9 +220,10 @@ class MTProtoSession : public QObject { void destroyKey(); template - mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false); // send mtp request + mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false, mtpRequestId after = 0); // send mtp request + void sendAnything(uint64 msCanWait); - void cancel(mtpRequestId requestId); + void cancel(mtpRequestId requestId, mtpMsgId msgId); int32 requestState(mtpRequestId requestId) const; int32 getState() const; QString transport() const; @@ -244,7 +256,7 @@ public slots: private: template - mtpRequestId sendFirst(const MTPInitConnection &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false); // send first mtp request + mtpRequestId sendFirst(const MTPInitConnection &request, RPCResponseHandler callbacks = RPCResponseHandler(), uint64 msCanWait = 0, uint32 layer = 0, bool toMainDC = false, mtpRequestId after = 0); // send first mtp request typedef QList MTProtoConnections; MTProtoConnections connections; diff --git a/Telegram/SourceFiles/mtproto/mtpSessionImpl.h b/Telegram/SourceFiles/mtproto/mtpSessionImpl.h index 1f5e4b8805057c..fb504a7ddb7116 100644 --- a/Telegram/SourceFiles/mtproto/mtpSessionImpl.h +++ b/Telegram/SourceFiles/mtproto/mtpSessionImpl.h @@ -18,11 +18,11 @@ Copyright (c) 2014 John Preston, https://tdesktop.com #pragma once template -mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC) { +mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC, mtpRequestId after) { mtpRequestId requestId = 0; if (layer && dc->needConnectionInit()) { MTPInitConnection requestWrap(MTPinitConnection(MTP_int(ApiId), MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(ApiLang), request)); - return sendFirst(requestWrap, callbacks, msCanWait, layer, toMainDC); + return sendFirst(requestWrap, callbacks, msCanWait, layer, toMainDC, after); } try { uint32 requestSize = request.size() >> 2; @@ -34,7 +34,8 @@ mtpRequestId MTProtoSession::send(const TRequest &request, RPCResponseHandler ca DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1").arg(msCanWait)); reqSerialized->msDate = getms(); // > 0 - can send without container - requestId = _mtp_internal::storeRequest(reqSerialized, callbacks); + if (after) reqSerialized->after = _mtp_internal::getRequest(after); + requestId = _mtp_internal::storeRequest(reqSerialized, callbacks); sendPrepared(reqSerialized, msCanWait); } catch (Exception &e) { @@ -61,7 +62,7 @@ class RPCWrappedDcDoneHandler : public RPCAbstractDoneHandler { }; template -mtpRequestId MTProtoSession::sendFirst(const MTPInitConnection &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC) { +mtpRequestId MTProtoSession::sendFirst(const MTPInitConnection &request, RPCResponseHandler callbacks, uint64 msCanWait, uint32 layer, bool toMainDC, mtpRequestId after) { mtpRequestId requestId = 0; try { uint32 requestSize = request.size() >> 2; @@ -72,7 +73,8 @@ mtpRequestId MTProtoSession::sendFirst(const MTPInitConnection &reques DEBUG_LOG(("MTP Info: adding wrapped to init connection request to toSendMap, msCanWait %1").arg(msCanWait)); callbacks.onDone = RPCDoneHandlerPtr(new RPCWrappedDcDoneHandler(dc, callbacks.onDone)); reqSerialized->msDate = getms(); // > 0 - can send without container - requestId = _mtp_internal::storeRequest(reqSerialized, callbacks); + if (after) reqSerialized->after = _mtp_internal::getRequest(after); + requestId = _mtp_internal::storeRequest(reqSerialized, callbacks); sendPrepared(reqSerialized, msCanWait); } catch (Exception &e) { diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index e4c46149fd2701..6d5daac32f1ceb 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -1848,7 +1848,7 @@ void OverviewWidget::onDeleteContextSure() { } if (item->id > 0) { - MTP::send(MTPmessages_DeleteMessages(MTP_vector(QVector(1, MTP_int(item->id))))); + MTP::send(MTPmessages_DeleteMessages(MTP_vector(1, MTP_int(item->id)))); } item->destroy(); App::wnd()->hideLayer(); diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp index bfbfe8361cd980..7befcb821f0775 100644 --- a/Telegram/SourceFiles/profilewidget.cpp +++ b/Telegram/SourceFiles/profilewidget.cpp @@ -252,7 +252,7 @@ void ProfileInner::gotFullUser(const MTPUserFull &user) { _loadingId = 0; const MTPDuserFull &d(user.c_userFull()); App::feedPhoto(d.vprofile_photo); - App::feedUsers(MTP_vector(QVector(1, d.vuser))); + App::feedUsers(MTP_vector(1, d.vuser)); PhotoData *userPhoto = _peerUser->photoId ? App::photo(_peerUser->photoId) : 0; if (userPhoto && userPhoto->date) { _photoLink = TextLinkPtr(new PhotoLink(userPhoto, _peer)); diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index fecfd6cd76e678..9b182286af36af 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -642,7 +642,7 @@ void SettingsInner::updateConnectionType() { void SettingsInner::gotFullSelf(const MTPUserFull &selfFull) { if (!self()) return; App::feedPhoto(selfFull.c_userFull().vprofile_photo); - App::feedUsers(MTP_vector(QVector(1, selfFull.c_userFull().vuser))); + App::feedUsers(MTP_vector(1, selfFull.c_userFull().vuser)); PhotoData *selfPhoto = self()->photoId ? App::photo(self()->photoId) : 0; if (selfPhoto && selfPhoto->date) { _photoLink = TextLinkPtr(new PhotoLink(selfPhoto, self())); diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 13c79e36c57259..52aa54928d02c7 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -469,7 +469,7 @@ void Window::setupMain(bool anim) { if (anim) { main->animShow(bg); } else { - MTP::send(MTPusers_GetUsers(MTP_vector(QVector(1, MTP_inputUserSelf()))), main->rpcDone(&MainWidget::startFull)); + MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), main->rpcDone(&MainWidget::startFull)); main->activate(); } diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index 8b8072950b6781..645ca6f11ffb50 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.6 + 0.6.7 CFBundleSignature ???? NOTE diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 2e7db65e68889d..fecbdd96d7eac1 100644 Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 4f21906d79ef80..788c1ccefc2395 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1515,7 +1515,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.6; + CURRENT_PROJECT_VERSION = 0.6.7; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1533,7 +1533,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.6.6; + CURRENT_PROJECT_VERSION = 0.6.7; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1559,10 +1559,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.6; + CURRENT_PROJECT_VERSION = 0.6.7; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.6; - DYLIB_CURRENT_VERSION = 0.6.6; + DYLIB_CURRENT_VERSION = 0.6.7; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1701,10 +1701,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.6.6; + CURRENT_PROJECT_VERSION = 0.6.7; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.6; - DYLIB_CURRENT_VERSION = 0.6.6; + DYLIB_CURRENT_VERSION = 0.6.7; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES;