From 021c8896c89edf17b19b07b97cf409250efc52a7 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 15 Jun 2016 08:36:59 +0300
Subject: [PATCH] Fixed crash with forbidden megagroup in App::feedChats.
 ReplyMarkupClickHandler holds FullMsgId instead of HistoryItem*.

---
 Telegram/SourceFiles/app.cpp     |  7 +++--
 Telegram/SourceFiles/history.cpp | 47 ++++++++++++++++++++++++++------
 Telegram/SourceFiles/history.h   |  1 +
 3 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 3575ecbaa..c9155965c 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -685,7 +685,6 @@ namespace {
 					cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
 					cdata->access = d.vaccess_hash.v;
 					cdata->date = d.vdate.v;
-					cdata->flags = d.vflags.v;
 					if (cdata->version < d.vversion.v) {
 						cdata->version = d.vversion.v;
 					}
@@ -694,12 +693,14 @@ namespace {
 					} else {
 						cdata->setRestrictionReason(QString());
 					}
+					cdata->flags = d.vflags.v;
 				}
+				cdata->flagsUpdated();
+
 				QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString();
 				cdata->setName(qs(d.vtitle), uname);
 
 				cdata->isForbidden = false;
-				cdata->flagsUpdated();
 				cdata->setPhoto(d.vphoto);
 
 				if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
@@ -731,6 +732,7 @@ namespace {
 
 				auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
 				cdata->flags = (cdata->flags & ~mask) | (mtpCastFlags(d.vflags) & mask);
+				cdata->flagsUpdated();
 
 				cdata->setName(qs(d.vtitle), QString());
 
@@ -739,7 +741,6 @@ namespace {
 				cdata->date = 0;
 				cdata->setMembersCount(0);
 				cdata->isForbidden = true;
-				cdata->flagsUpdated();
 
 				if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
 				if (canEditPhoto != cdata->canEditPhoto()) update.flags |= UpdateFlag::ChannelCanEditPhoto;
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index a4bb1feaf..0dcb4a9f4 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -2180,7 +2180,7 @@ void HistoryBlock::removeItem(HistoryItem *item) {
 
 class ReplyMarkupClickHandler : public LeftButtonClickHandler {
 public:
-	ReplyMarkupClickHandler(const HistoryItem *item, int row, int col) : _item(item), _row(row), _col(col) {
+	ReplyMarkupClickHandler(const HistoryItem *item, int row, int col) : _itemId(item->fullId()), _row(row), _col(col) {
 	}
 
 	QString tooltip() const override {
@@ -2216,24 +2216,34 @@ public:
 	// Note: it is possible that we will point to the different button
 	// than the one was used when constructing the handler, but not a big deal.
 	const HistoryMessageReplyMarkup::Button *getButton() const {
-		if (auto markup = _item->Get<HistoryMessageReplyMarkup>()) {
-			if (_row < markup->rows.size()) {
-				const HistoryMessageReplyMarkup::ButtonRow &row(markup->rows.at(_row));
-				if (_col < row.size()) {
-					return &row.at(_col);
+		if (auto item = App::histItemById(_itemId)) {
+			if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
+				if (_row < markup->rows.size()) {
+					const HistoryMessageReplyMarkup::ButtonRow &row(markup->rows.at(_row));
+					if (_col < row.size()) {
+						return &row.at(_col);
+					}
 				}
 			}
 		}
 		return nullptr;
 	}
 
+	// We hold only FullMsgId, not HistoryItem*, because all click handlers
+	// are activated async and the item may be already destroyed.
+	void setMessageId(const FullMsgId &msgId) {
+		_itemId = msgId;
+	}
+
 protected:
 	void onClickImpl() const override {
-		App::activateBotCommand(_item, _row, _col);
+		if (auto item = App::histItemById(_itemId)) {
+			App::activateBotCommand(item, _row, _col);
+		}
 	}
 
 private:
-	const HistoryItem *_item = nullptr;
+	FullMsgId _itemId;
 	int _row, _col;
 	bool _fullDisplayed = true;
 
@@ -2270,6 +2280,16 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
 	}
 }
 
+void ReplyKeyboard::updateMessageId() {
+	auto msgId = _item->fullId();
+	for_const (auto &row, _rows) {
+		for_const (auto &button, row) {
+			button.link->setMessageId(msgId);
+		}
+	}
+
+}
+
 void ReplyKeyboard::resize(int width, int height) {
 	_width = width;
 
@@ -2775,6 +2795,15 @@ void HistoryItem::recountAttachToPrevious() {
 void HistoryItem::setId(MsgId newId) {
 	history()->changeMsgId(id, newId);
 	id = newId;
+
+	// We don't need to call Notify::replyMarkupUpdated(this) and update keyboard
+	// in history widget, because it can't exist for an outgoing message.
+	// Only inline keyboards can be in outgoing messages.
+	if (auto markup = inlineReplyMarkup()) {
+		if (markup->inlineKeyboard) {
+			markup->inlineKeyboard->updateMessageId();
+		}
+	}
 }
 
 bool HistoryItem::canEdit(const QDateTime &cur) const {
@@ -6816,7 +6845,7 @@ void HistoryMessage::initDimensions() {
 		}
 		if (replyw > _maxw) _maxw = replyw;
 	}
-	if (HistoryMessageReplyMarkup *markup = inlineReplyMarkup()) {
+	if (auto markup = inlineReplyMarkup()) {
 		if (!markup->inlineKeyboard) {
 			markup->inlineKeyboard.reset(new ReplyKeyboard(this, std_::make_unique<KeyboardStyle>(st::msgBotKbButton)));
 		}
diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h
index 4d170eb2f..b46e8d006 100644
--- a/Telegram/SourceFiles/history.h
+++ b/Telegram/SourceFiles/history.h
@@ -905,6 +905,7 @@ public:
 	void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed);
 
 	void clearSelection();
+	void updateMessageId();
 
 private:
 	const HistoryItem *_item;