From 557d363d029a6760b17db2e3bfdc4bbc4d12420e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Sun, 15 Jul 2018 19:36:19 +0300
Subject: [PATCH] Refresh file references when downloading files.

---
 Telegram/SourceFiles/apiwrap.cpp              | 127 ++++++++++
 Telegram/SourceFiles/apiwrap.h                |  26 +-
 .../SourceFiles/data/data_file_origin.cpp     | 237 ++++++++++++++++++
 Telegram/SourceFiles/data/data_file_origin.h  |  47 ++++
 .../SourceFiles/storage/file_download.cpp     |  40 ++-
 Telegram/SourceFiles/storage/file_download.h  |   8 +-
 Telegram/SourceFiles/ui/images.cpp            |   4 +-
 Telegram/SourceFiles/ui/images.h              |   3 +
 Telegram/gyp/telegram_sources.txt             |   1 +
 9 files changed, 487 insertions(+), 6 deletions(-)
 create mode 100644 Telegram/SourceFiles/data/data_file_origin.cpp

diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 1bed0a207..90365476a 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "chat_helpers/stickers.h"
 #include "ui/text_options.h"
 #include "storage/localimageloader.h"
+#include "storage/file_download.h"
 #include "storage/storage_facade.h"
 #include "storage/storage_shared_media.h"
 #include "storage/storage_user_photos.h"
@@ -67,6 +68,11 @@ constexpr auto kFeedReadTimeout = TimeMs(1000);
 constexpr auto kStickersByEmojiInvalidateTimeout = TimeMs(60 * 60 * 1000);
 constexpr auto kNotifySettingSaveTimeout = TimeMs(1000);
 
+using SimpleFileLocationId = Data::SimpleFileLocationId;
+using DocumentFileLocationId = Data::DocumentFileLocationId;
+using FileLocationId = Data::FileLocationId;
+using UpdatedFileReferences = Data::UpdatedFileReferences;
+
 bool IsSilentPost(not_null<HistoryItem*> item, bool silent) {
 	const auto history = item->history();
 	return silent
@@ -2368,6 +2374,127 @@ void ApiWrap::channelRangeDifferenceDone(
 	}
 }
 
+template <typename Request>
+void ApiWrap::requestFileReference(
+		Data::FileOrigin origin,
+		not_null<mtpFileLoader*> loader,
+		int requestId,
+		const QByteArray &current,
+		Request &&data) {
+	auto handler = crl::guard(loader, [=](
+			const Data::UpdatedFileReferences &data) {
+		loader->refreshFileReferenceFrom(data, requestId, current);
+	});
+	const auto i = _fileReferenceHandlers.find(origin);
+	if (i != end(_fileReferenceHandlers)) {
+		i->second.push_back(std::move(handler));
+		return;
+	}
+	auto handlers = std::vector<FileReferencesHandler>();
+	handlers.push_back(std::move(handler));
+	_fileReferenceHandlers.emplace(origin, std::move(handlers));
+
+	request(std::move(data)).done([=](const auto &result) {
+		const auto parsed = Data::GetFileReferences(result);
+		const auto i = _fileReferenceHandlers.find(origin);
+		Assert(i != end(_fileReferenceHandlers));
+		auto handlers = std::move(i->second);
+		_fileReferenceHandlers.erase(i);
+		for (auto &handler : handlers) {
+			handler(parsed);
+		}
+	}).fail([=](const RPCError &error) {
+		const auto i = _fileReferenceHandlers.find(origin);
+		Assert(i != end(_fileReferenceHandlers));
+		auto handlers = std::move(i->second);
+		_fileReferenceHandlers.erase(i);
+		for (auto &handler : handlers) {
+			handler(Data::UpdatedFileReferences());
+		}
+	}).send();
+}
+
+void ApiWrap::refreshFileReference(
+		Data::FileOrigin origin,
+		not_null<mtpFileLoader*> loader,
+		int requestId,
+		const QByteArray &current) {
+	const auto request = [&](auto &&data) {
+		requestFileReference(
+			origin,
+			loader,
+			requestId,
+			current,
+			std::move(data));
+	};
+	const auto fail = [&] {
+		loader->refreshFileReferenceFrom({}, requestId, current);
+	};
+	origin.match([&](Data::FileOriginMessage data) {
+		if (const auto item = App::histItemById(data)) {
+			if (const auto channel = item->history()->peer->asChannel()) {
+				request(MTPchannels_GetMessages(
+					channel->inputChannel,
+					MTP_vector<MTPInputMessage>(
+						1,
+						MTP_inputMessageID(MTP_int(item->id)))));
+			} else {
+				request(MTPmessages_GetMessages(
+					MTP_vector<MTPInputMessage>(
+						1,
+						MTP_inputMessageID(MTP_int(item->id)))));
+			}
+		} else {
+			fail();
+		}
+	}, [&](Data::FileOriginUserPhoto data) {
+		if (const auto user = App::user(data.userId)) {
+			request(MTPphotos_GetUserPhotos(
+				user->inputUser,
+				MTP_int(-1),
+				MTP_long(data.photoId),
+				MTP_int(1)));
+		} else {
+			fail();
+		}
+	}, [&](Data::FileOriginPeerPhoto data) {
+		if (const auto peer = App::peer(data.peerId)) {
+			if (const auto user = peer->asUser()) {
+				request(MTPusers_GetUsers(
+					MTP_vector<MTPInputUser>(1, user->inputUser)));
+			} else if (const auto chat = peer->asChat()) {
+				request(MTPmessages_GetChats(
+					MTP_vector<MTPint>(1, chat->inputChat)));
+			} else if (const auto channel = peer->asChannel()) {
+				request(MTPchannels_GetChannels(
+					MTP_vector<MTPInputChannel>(1, channel->inputChannel)));
+			} else {
+				fail();
+			}
+		} else {
+			fail();
+		}
+	}, [&](Data::FileOriginStickerSet data) {
+		if (data.setId == Stickers::CloudRecentSetId
+			|| data.setId == Stickers::RecentSetId) {
+			request(MTPmessages_GetRecentStickers(
+				MTP_flags(0),
+				MTP_int(0)));
+		} else if (data.setId == Stickers::FavedSetId) {
+			request(MTPmessages_GetFavedStickers(MTP_int(0)));
+		} else {
+			request(MTPmessages_GetStickerSet(
+				MTP_inputStickerSetID(
+					MTP_long(data.setId),
+					MTP_long(data.accessHash))));
+		}
+	}, [&](Data::FileOriginSavedGifs data) {
+		request(MTPmessages_GetSavedGifs(MTP_int(0)));
+	}, [&](base::none_type) {
+		fail();
+	});
+}
+
 void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
 	const QVector<MTPMessage> *v = 0;
 	switch (msgs.type()) {
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 094162d12..973d81475 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -22,6 +22,7 @@ struct MessageGroupId;
 struct SendingAlbum;
 enum class SendMediaType;
 struct FileLoadTo;
+class mtpFileLoader;
 
 namespace InlineBots {
 class Result;
@@ -55,7 +56,6 @@ inline int32 CountHash(IntRange &&range) {
 	return int32(acc & 0x7FFFFFFF);
 }
 
-
 } // namespace Api
 
 class ApiWrap : private MTP::Sender, private base::Subscriber {
@@ -97,6 +97,12 @@ public:
 	void requestParticipantsCountDelayed(not_null<ChannelData*> channel);
 	void requestChannelRangeDifference(not_null<History*> history);
 
+	void refreshFileReference(
+		Data::FileOrigin origin,
+		not_null<mtpFileLoader*> loader,
+		int requestId,
+		const QByteArray &current);
+
 	void requestChangelog(
 		const QString &sinceVersion,
 		Fn<void(const MTPUpdates &result)> callback);
@@ -315,6 +321,12 @@ private:
 		TimeMs received = 0;
 	};
 
+	using SimpleFileLocationId = Data::SimpleFileLocationId;
+	using DocumentFileLocationId = Data::DocumentFileLocationId;
+	using FileLocationId = Data::FileLocationId;
+	using UpdatedFileReferences = Data::UpdatedFileReferences;
+	using FileReferencesHandler = FnMut<void(const UpdatedFileReferences&)>;
+
 	void updatesReceived(const MTPUpdates &updates);
 	void checkQuitPreventFinished();
 
@@ -469,6 +481,14 @@ private:
 
 	void sendNotifySettingsUpdates();
 
+	template <typename Request>
+	void requestFileReference(
+		Data::FileOrigin origin,
+		not_null<mtpFileLoader*> loader,
+		int requestId,
+		const QByteArray &current,
+		Request &&data);
+
 	not_null<AuthSession*> _session;
 
 	MessageDataRequests _messageDataRequests;
@@ -605,6 +625,10 @@ private:
 	base::flat_set<not_null<const PeerData*>> _updateNotifySettingsPeers;
 	base::Timer _updateNotifySettingsTimer;
 
+	std::map<
+		Data::FileOrigin,
+		std::vector<FileReferencesHandler>> _fileReferenceHandlers;
+
 	mtpRequestId _deepLinkInfoRequestId = 0;
 
 	TimeMs _termsUpdateSendAt = 0;
diff --git a/Telegram/SourceFiles/data/data_file_origin.cpp b/Telegram/SourceFiles/data/data_file_origin.cpp
new file mode 100644
index 000000000..99f4c352f
--- /dev/null
+++ b/Telegram/SourceFiles/data/data_file_origin.cpp
@@ -0,0 +1,237 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "data/data_file_origin.h"
+
+namespace Data {
+namespace {
+
+struct FileReferenceAccumulator {
+	template <typename Type>
+	void push(const MTPVector<Type> &data) {
+		for (const auto &item : data.v) {
+			push(item);
+		}
+	}
+	void push(const MTPFileLocation &data) {
+		data.match([&](const MTPDfileLocation &data) {
+			result.emplace(SimpleFileLocationId(
+				data.vvolume_id.v,
+				data.vdc_id.v,
+				data.vlocal_id.v), data.vfile_reference.v);
+		}, [](const MTPDfileLocationUnavailable &data) {
+		});
+	}
+	void push(const MTPPhotoSize &data) {
+		data.match([](const MTPDphotoSizeEmpty &data) {
+		}, [&](const auto &data) {
+			push(data.vlocation);
+		});
+	}
+	void push(const MTPPhoto &data) {
+		data.match([&](const MTPDphoto &data) {
+			for (const auto &size : data.vsizes.v) {
+				push(size);
+			}
+		}, [](const MTPDphotoEmpty &data) {
+		});
+	}
+	void push(const MTPDocument &data) {
+		data.match([&](const MTPDdocument &data) {
+			push(data.vthumb);
+			result.emplace(
+				DocumentFileLocationId(data.vid.v),
+				data.vfile_reference.v);
+		}, [](const MTPDdocumentEmpty &data) {
+		});
+	}
+	void push(const MTPUserProfilePhoto &data) {
+		data.match([&](const MTPDuserProfilePhoto &data) {
+			push(data.vphoto_small);
+			push(data.vphoto_big);
+		}, [](const MTPDuserProfilePhotoEmpty &data) {
+		});
+	}
+	void push(const MTPChatPhoto &data) {
+		data.match([&](const MTPDchatPhoto &data) {
+			push(data.vphoto_small);
+			push(data.vphoto_big);
+		}, [](const MTPDchatPhotoEmpty &data) {
+		});
+	}
+	void push(const MTPUser &data) {
+		data.match([&](const MTPDuser &data) {
+			if (data.has_photo()) {
+				push(data.vphoto);
+			}
+		}, [](const MTPDuserEmpty &data) {
+		});
+	}
+	void push(const MTPChat &data) {
+		data.match([](const MTPDchatEmpty &data) {
+		}, [](const MTPDchannelForbidden &data) {
+		}, [](const MTPDchatForbidden &data) {
+		}, [&](const auto &data) {
+			push(data.vphoto);
+		});
+	}
+	void push(const MTPWebPage &data) {
+		data.match([&](const MTPDwebPage &data) {
+			if (data.has_document()) {
+				push(data.vdocument);
+			}
+			if (data.has_photo()) {
+				push(data.vphoto);
+			}
+		}, [](const auto &data) {
+		});
+	}
+	void push(const MTPGame &data) {
+		data.match([&](const MTPDgame &data) {
+			if (data.has_document()) {
+				push(data.vdocument);
+			}
+		}, [](const auto &data) {
+		});
+	}
+	void push(const MTPMessageMedia &data) {
+		data.match([&](const MTPDmessageMediaPhoto &data) {
+			if (data.has_photo()) {
+				push(data.vphoto);
+			}
+		}, [&](const MTPDmessageMediaDocument &data) {
+			if (data.has_document()) {
+				push(data.vdocument);
+			}
+		}, [&](const MTPDmessageMediaWebPage &data) {
+			push(data.vwebpage);
+		}, [&](const MTPDmessageMediaGame &data) {
+			push(data.vgame);
+		}, [](const auto &data) {
+		});
+	}
+	void push(const MTPMessage &data) {
+		data.match([&](const MTPDmessage &data) {
+			if (data.has_media()) {
+				push(data.vmedia);
+			}
+		}, [&](const MTPDmessageService &data) {
+			data.vaction.match(
+			[&](const MTPDmessageActionChatEditPhoto &data) {
+				push(data.vphoto);
+			}, [](const auto &data) {
+			});
+		}, [](const MTPDmessageEmpty &data) {
+		});
+	}
+	void push(const MTPmessages_Messages &data) {
+		data.match([](const MTPDmessages_messagesNotModified &) {
+		}, [&](const auto &data) {
+			push(data.vusers);
+			push(data.vchats);
+			push(data.vmessages);
+		});
+	}
+	void push(const MTPphotos_Photos &data) {
+		data.match([&](const auto &data) {
+			push(data.vusers);
+			push(data.vphotos);
+		});
+	}
+	void push(const MTPmessages_Chats &data) {
+		data.match([&](const auto &data) {
+			push(data.vchats);
+		});
+	}
+	void push(const MTPmessages_RecentStickers &data) {
+		data.match([&](const MTPDmessages_recentStickers &data) {
+			push(data.vstickers);
+		}, [](const MTPDmessages_recentStickersNotModified &data) {
+		});
+	}
+	void push(const MTPmessages_FavedStickers &data) {
+		data.match([&](const MTPDmessages_favedStickers &data) {
+			push(data.vstickers);
+		}, [](const MTPDmessages_favedStickersNotModified &data) {
+		});
+	}
+	void push(const MTPmessages_StickerSet &data) {
+		data.match([&](const MTPDmessages_stickerSet &data) {
+			push(data.vdocuments);
+		});
+	}
+	void push(const MTPmessages_SavedGifs &data) {
+		data.match([&](const MTPDmessages_savedGifs &data) {
+			push(data.vgifs);
+		}, [](const MTPDmessages_savedGifsNotModified &data) {
+		});
+	}
+
+	UpdatedFileReferences result;
+};
+
+template <typename Type>
+UpdatedFileReferences GetFileReferencesHelper(const Type &data) {
+	FileReferenceAccumulator result;
+	result.push(data);
+	return result.result;
+}
+
+} // namespace
+
+SimpleFileLocationId::SimpleFileLocationId(
+	uint64 volumeId,
+	int32 dcId,
+	int32 localId)
+: volumeId(volumeId)
+, dcId(dcId)
+, localId(localId) {
+}
+
+bool operator<(
+		const SimpleFileLocationId &a,
+		const SimpleFileLocationId &b) {
+	return std::tie(a.volumeId, a.dcId, a.localId)
+		< std::tie(b.volumeId, b.dcId, b.localId);
+}
+
+UpdatedFileReferences GetFileReferences(const MTPmessages_Messages &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(const MTPVector<MTPUser> &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(const MTPmessages_Chats &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(
+		const MTPmessages_RecentStickers &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(
+		const MTPmessages_FavedStickers &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(
+		const MTPmessages_StickerSet &data) {
+	return GetFileReferencesHelper(data);
+}
+
+UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data) {
+	return GetFileReferencesHelper(data);
+}
+
+} // namespace Data
diff --git a/Telegram/SourceFiles/data/data_file_origin.h b/Telegram/SourceFiles/data/data_file_origin.h
index c6b421dad..27a214055 100644
--- a/Telegram/SourceFiles/data/data_file_origin.h
+++ b/Telegram/SourceFiles/data/data_file_origin.h
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #pragma once
 
+#include "base/variant.h"
 #include "data/data_types.h"
 
 namespace Data {
@@ -21,6 +22,11 @@ struct FileOriginUserPhoto {
 
 	UserId userId = 0;
 	PhotoId photoId = 0;
+
+	inline bool operator<(const FileOriginUserPhoto &other) const {
+		return std::tie(userId, photoId)
+			< std::tie(other.userId, other.photoId);
+	}
 };
 
 struct FileOriginPeerPhoto {
@@ -28,6 +34,10 @@ struct FileOriginPeerPhoto {
 	}
 
 	PeerId peerId = 0;
+
+	inline bool operator<(const FileOriginPeerPhoto &other) const {
+		return peerId < other.peerId;
+	}
 };
 
 struct FileOriginStickerSet {
@@ -38,9 +48,16 @@ struct FileOriginStickerSet {
 
 	uint64 setId = 0;
 	uint64 accessHash = 0;
+
+	inline bool operator<(const FileOriginStickerSet &other) const {
+		return setId < other.setId;
+	}
 };
 
 struct FileOriginSavedGifs {
+	inline bool operator<(const FileOriginSavedGifs &) const {
+		return false;
+	}
 };
 
 using FileOrigin = base::optional_variant<
@@ -50,4 +67,34 @@ using FileOrigin = base::optional_variant<
 	FileOriginStickerSet,
 	FileOriginSavedGifs>;
 
+// Volume_id, dc_id, local_id.
+struct SimpleFileLocationId {
+	SimpleFileLocationId(uint64 volumeId, int32 dcId, int32 localId);
+
+	uint64 volumeId = 0;
+	int32 dcId = 0;
+	int32 localId = 0;
+};
+
+bool operator<(
+	const SimpleFileLocationId &a,
+	const SimpleFileLocationId &b);
+
+using DocumentFileLocationId = uint64;
+using FileLocationId = base::variant<
+	SimpleFileLocationId,
+	DocumentFileLocationId>;
+using UpdatedFileReferences = std::map<FileLocationId, QByteArray>;
+
+UpdatedFileReferences GetFileReferences(const MTPmessages_Messages &data);
+UpdatedFileReferences GetFileReferences(const MTPphotos_Photos &data);
+UpdatedFileReferences GetFileReferences(const MTPVector<MTPUser> &data);
+UpdatedFileReferences GetFileReferences(const MTPmessages_Chats &data);
+UpdatedFileReferences GetFileReferences(
+	const MTPmessages_RecentStickers &data);
+UpdatedFileReferences GetFileReferences(
+	const MTPmessages_FavedStickers &data);
+UpdatedFileReferences GetFileReferences(const MTPmessages_StickerSet &data);
+UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data);
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp
index 7b6769e12..099f51640 100644
--- a/Telegram/SourceFiles/storage/file_download.cpp
+++ b/Telegram/SourceFiles/storage/file_download.cpp
@@ -394,7 +394,7 @@ void FileLoader::startLoading(bool loadFirst, bool prior) {
 }
 
 mtpFileLoader::mtpFileLoader(
-	const StorageImageLocation *location,
+	not_null<StorageImageLocation*> location,
 	Data::FileOrigin origin,
 	int32 size,
 	LoadFromCloudSetting fromCloud
@@ -479,6 +479,34 @@ Data::FileOrigin mtpFileLoader::fileOrigin() const {
 	return _origin;
 }
 
+void mtpFileLoader::refreshFileReferenceFrom(
+		const Data::UpdatedFileReferences &data,
+		int requestId,
+		const QByteArray &current) {
+	const auto updated = [&] {
+		if (_location) {
+			const auto i = data.find(Data::SimpleFileLocationId(
+				_location->volume(),
+				_location->dc(),
+				_location->local()));
+			return (i == end(data)) ? QByteArray() : i->second;
+		}
+		const auto i = data.find(_id);
+		return (i == end(data)) ? QByteArray() : i->second;
+	}();
+	if (updated.isEmpty() || updated == current) {
+		cancel(true);
+		return;
+	}
+	if (_location) {
+		_location->refreshFileReference(updated);
+	} else {
+		_fileReference = updated;
+	}
+	const auto offset = finishSentRequestGetOffset(requestId);
+	makeRequest(offset);
+}
+
 bool mtpFileLoader::loadPart() {
 	if (_finished || _lastComplete || (!_sentRequests.empty() && !_size)) {
 		return false;
@@ -766,7 +794,7 @@ void mtpFileLoader::placeSentRequest(mtpRequestId requestId, const RequestData &
 
 int mtpFileLoader::finishSentRequestGetOffset(mtpRequestId requestId) {
 	auto it = _sentRequests.find(requestId);
-	Expects(it != _sentRequests.cend());
+	Assert(it != _sentRequests.cend());
 
 	auto requestData = it->second;
 	_downloader->requestedAmountIncrement(requestData.dcId, requestData.dcIndex, -partSize());
@@ -889,6 +917,14 @@ bool mtpFileLoader::partFailed(
 	if (MTP::isDefaultHandledError(error)) {
 		return false;
 	}
+	if (error.type().startsWith(qstr("FILE_REFERENCE_"))) {
+		Auth().api().refreshFileReference(
+			_origin,
+			this,
+			requestId,
+			_location ? _location->fileReference() : _fileReference);
+		return true;
+	}
 	cancel(true);
 	return true;
 }
diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h
index 18036c1da..7ef00e4c5 100644
--- a/Telegram/SourceFiles/storage/file_download.h
+++ b/Telegram/SourceFiles/storage/file_download.h
@@ -186,7 +186,7 @@ class mtpFileLoader : public FileLoader, public RPCSender {
 
 public:
 	mtpFileLoader(
-		const StorageImageLocation *location,
+		not_null<StorageImageLocation*> location,
 		Data::FileOrigin origin,
 		int32 size,
 		LoadFromCloudSetting fromCloud,
@@ -219,6 +219,10 @@ public:
 	void stop() override {
 		rpcInvalidate();
 	}
+	void refreshFileReferenceFrom(
+		const Data::UpdatedFileReferences &data,
+		int requestId,
+		const QByteArray &current);
 
 	~mtpFileLoader();
 
@@ -277,7 +281,7 @@ private:
 	int32 _nextRequestOffset = 0;
 
 	MTP::DcId _dcId = 0; // for photo locations
-	const StorageImageLocation *_location = nullptr;
+	StorageImageLocation *_location = nullptr;
 
 	uint64 _id = 0; // for document locations
 	uint64 _accessHash = 0;
diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp
index b0c0eed62..0cd698b70 100644
--- a/Telegram/SourceFiles/ui/images.cpp
+++ b/Telegram/SourceFiles/ui/images.cpp
@@ -1143,7 +1143,9 @@ FileLoader *StorageImage::createLoader(
 		Data::FileOrigin origin,
 		LoadFromCloudSetting fromCloud,
 		bool autoLoading) {
-	if (_location.isNull()) return 0;
+	if (_location.isNull()) {
+		return nullptr;
+	}
 	return new mtpFileLoader(
 		&_location,
 		origin,
diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h
index 858f8fe2b..cea3ffc56 100644
--- a/Telegram/SourceFiles/ui/images.h
+++ b/Telegram/SourceFiles/ui/images.h
@@ -152,6 +152,9 @@ public:
 	QByteArray fileReference() const {
 		return _fileReference;
 	}
+	void refreshFileReference(const QByteArray &data) {
+		_fileReference = data;
+	}
 
 	static StorageImageLocation FromMTP(
 		int32 width,
diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt
index bdf2d2bf8..61dda8609 100644
--- a/Telegram/gyp/telegram_sources.txt
+++ b/Telegram/gyp/telegram_sources.txt
@@ -179,6 +179,7 @@
 <(src_loc)/data/data_feed.h
 <(src_loc)/data/data_feed_messages.cpp
 <(src_loc)/data/data_feed_messages.h
+<(src_loc)/data/data_file_origin.cpp
 <(src_loc)/data/data_file_origin.h
 <(src_loc)/data/data_flags.h
 <(src_loc)/data/data_game.h