1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "export/export_api_wrap.h"
9 
10 #include "export/export_settings.h"
11 #include "export/data/export_data_types.h"
12 #include "export/output/export_output_result.h"
13 #include "export/output/export_output_file.h"
14 #include "mtproto/mtproto_response.h"
15 #include "base/value_ordering.h"
16 #include "base/bytes.h"
17 #include "base/random.h"
18 #include <set>
19 #include <deque>
20 
21 namespace Export {
22 namespace {
23 
24 constexpr auto kUserpicsSliceLimit = 100;
25 constexpr auto kFileChunkSize = 128 * 1024;
26 constexpr auto kFileRequestsCount = 2;
27 //constexpr auto kFileNextRequestDelay = crl::time(20);
28 constexpr auto kChatsSliceLimit = 100;
29 constexpr auto kMessagesSliceLimit = 100;
30 constexpr auto kTopPeerSliceLimit = 100;
31 constexpr auto kFileMaxSize = 2000 * 1024 * 1024;
32 constexpr auto kLocationCacheSize = 100'000;
33 
34 struct LocationKey {
35 	uint64 type;
36 	uint64 id;
37 
operator <Export::__anon7c2d49d30111::LocationKey38 	inline bool operator<(const LocationKey &other) const {
39 		return std::tie(type, id) < std::tie(other.type, other.id);
40 	}
41 };
42 
ComputeLocationKey(const Data::FileLocation & value)43 LocationKey ComputeLocationKey(const Data::FileLocation &value) {
44 	auto result = LocationKey();
45 	result.type = value.dcId;
46 	value.data.match([&](const MTPDinputDocumentFileLocation &data) {
47 		const auto letter = data.vthumb_size().v.isEmpty()
48 			? char(0)
49 			: data.vthumb_size().v[0];
50 		result.type |= (2ULL << 24);
51 		result.type |= (uint64(uint32(letter)) << 16);
52 		result.id = data.vid().v;
53 	}, [&](const MTPDinputPhotoFileLocation &data) {
54 		const auto letter = data.vthumb_size().v.isEmpty()
55 			? char(0)
56 			: data.vthumb_size().v[0];
57 		result.type |= (6ULL << 24);
58 		result.type |= (uint64(uint32(letter)) << 16);
59 		result.id = data.vid().v;
60 	}, [&](const MTPDinputTakeoutFileLocation &data) {
61 		result.type |= (5ULL << 24);
62 	}, [](const auto &data) {
63 		Unexpected("File location type in Export::ComputeLocationKey.");
64 	});
65 	return result;
66 }
67 
SettingsFromDialogsType(Data::DialogInfo::Type type)68 Settings::Type SettingsFromDialogsType(Data::DialogInfo::Type type) {
69 	using DialogType = Data::DialogInfo::Type;
70 	switch (type) {
71 	case DialogType::Self:
72 	case DialogType::Personal:
73 		return Settings::Type::PersonalChats;
74 	case DialogType::Bot:
75 		return Settings::Type::BotChats;
76 	case DialogType::PrivateGroup:
77 	case DialogType::PrivateSupergroup:
78 		return Settings::Type::PrivateGroups;
79 	case DialogType::PublicSupergroup:
80 		return Settings::Type::PublicGroups;
81 	case DialogType::PrivateChannel:
82 		return Settings::Type::PrivateChannels;
83 	case DialogType::PublicChannel:
84 		return Settings::Type::PublicChannels;
85 	}
86 	return Settings::Type(0);
87 }
88 
89 } // namespace
90 
91 class ApiWrap::LoadedFileCache {
92 public:
93 	using Location = Data::FileLocation;
94 
95 	LoadedFileCache(int limit);
96 
97 	void save(const Location &location, const QString &relativePath);
98 	std::optional<QString> find(const Location &location) const;
99 
100 private:
101 	int _limit = 0;
102 	std::map<LocationKey, QString> _map;
103 	std::deque<LocationKey> _list;
104 
105 };
106 
107 struct ApiWrap::StartProcess {
108 	FnMut<void(StartInfo)> done;
109 
110 	enum class Step {
111 		UserpicsCount,
112 		SplitRanges,
113 		DialogsCount,
114 		LeftChannelsCount,
115 	};
116 	std::deque<Step> steps;
117 	int splitIndex = 0;
118 	StartInfo info;
119 };
120 
121 struct ApiWrap::ContactsProcess {
122 	FnMut<void(Data::ContactsList&&)> done;
123 
124 	Data::ContactsList result;
125 
126 	int topPeersOffset = 0;
127 };
128 
129 struct ApiWrap::UserpicsProcess {
130 	FnMut<bool(Data::UserpicsInfo&&)> start;
131 	Fn<bool(DownloadProgress)> fileProgress;
132 	Fn<bool(Data::UserpicsSlice&&)> handleSlice;
133 	FnMut<void()> finish;
134 
135 	int processed = 0;
136 	std::optional<Data::UserpicsSlice> slice;
137 	uint64 maxId = 0;
138 	bool lastSlice = false;
139 	int fileIndex = 0;
140 };
141 
142 struct ApiWrap::OtherDataProcess {
143 	Data::File file;
144 	FnMut<void(Data::File&&)> done;
145 };
146 
147 struct ApiWrap::FileProcess {
148 	FileProcess(const QString &path, Output::Stats *stats);
149 
150 	Output::File file;
151 	QString relativePath;
152 
153 	Fn<bool(FileProgress)> progress;
154 	FnMut<void(const QString &relativePath)> done;
155 
156 	uint64 randomId = 0;
157 	Data::FileLocation location;
158 	Data::FileOrigin origin;
159 	int offset = 0;
160 	int size = 0;
161 
162 	struct Request {
163 		int offset = 0;
164 		QByteArray bytes;
165 	};
166 	std::deque<Request> requests;
167 	mtpRequestId requestId = 0;
168 };
169 
170 struct ApiWrap::FileProgress {
171 	int ready = 0;
172 	int total = 0;
173 };
174 
175 struct ApiWrap::ChatsProcess {
176 	Fn<bool(int count)> progress;
177 	FnMut<void(Data::DialogsInfo&&)> done;
178 
179 	Data::DialogsInfo info;
180 	int processedCount = 0;
181 	std::map<PeerId, int> indexByPeer;
182 };
183 
184 struct ApiWrap::LeftChannelsProcess : ChatsProcess {
185 	int fullCount = 0;
186 	int offset = 0;
187 	bool finished = false;
188 };
189 
190 struct ApiWrap::DialogsProcess : ChatsProcess {
191 	int splitIndexPlusOne = 0;
192 	TimeId offsetDate = 0;
193 	int32 offsetId = 0;
194 	MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
195 };
196 
197 struct ApiWrap::ChatProcess {
198 	Data::DialogInfo info;
199 
200 	FnMut<bool(const Data::DialogInfo &)> start;
201 	Fn<bool(DownloadProgress)> fileProgress;
202 	Fn<bool(Data::MessagesSlice&&)> handleSlice;
203 	FnMut<void()> done;
204 
205 	FnMut<void(MTPmessages_Messages&&)> requestDone;
206 
207 	int localSplitIndex = 0;
208 	int32 largestIdPlusOne = 1;
209 
210 	Data::ParseMediaContext context;
211 	std::optional<Data::MessagesSlice> slice;
212 	bool lastSlice = false;
213 	int fileIndex = 0;
214 };
215 
216 
217 template <typename Request>
218 class ApiWrap::RequestBuilder {
219 public:
220 	using Original = MTP::ConcurrentSender::SpecificRequestBuilder<Request>;
221 	using Response = typename Request::ResponseType;
222 
223 	RequestBuilder(
224 		Original &&builder,
225 		Fn<void(const MTP::Error&)> commonFailHandler);
226 
227 	[[nodiscard]] RequestBuilder &done(FnMut<void()> &&handler);
228 	[[nodiscard]] RequestBuilder &done(
229 		FnMut<void(Response &&)> &&handler);
230 	[[nodiscard]] RequestBuilder &fail(
231 		Fn<bool(const MTP::Error&)> &&handler);
232 
233 	mtpRequestId send();
234 
235 private:
236 	Original _builder;
237 	Fn<void(const MTP::Error&)> _commonFailHandler;
238 
239 };
240 
241 template <typename Request>
RequestBuilder(Original && builder,Fn<void (const MTP::Error &)> commonFailHandler)242 ApiWrap::RequestBuilder<Request>::RequestBuilder(
243 	Original &&builder,
244 	Fn<void(const MTP::Error&)> commonFailHandler)
245 : _builder(std::move(builder))
246 , _commonFailHandler(std::move(commonFailHandler)) {
247 }
248 
249 template <typename Request>
done(FnMut<void ()> && handler)250 auto ApiWrap::RequestBuilder<Request>::done(
251 	FnMut<void()> &&handler
252 ) -> RequestBuilder& {
253 	if (handler) {
254 		[[maybe_unused]] auto &silence_warning = _builder.done(std::move(handler));
255 	}
256 	return *this;
257 }
258 
259 template <typename Request>
done(FnMut<void (Response &&)> && handler)260 auto ApiWrap::RequestBuilder<Request>::done(
261 	FnMut<void(Response &&)> &&handler
262 ) -> RequestBuilder& {
263 	if (handler) {
264 		[[maybe_unused]] auto &silence_warning = _builder.done(std::move(handler));
265 	}
266 	return *this;
267 }
268 
269 template <typename Request>
fail(Fn<bool (const MTP::Error &)> && handler)270 auto ApiWrap::RequestBuilder<Request>::fail(
271 	Fn<bool(const MTP::Error &)> &&handler
272 ) -> RequestBuilder& {
273 	if (handler) {
274 		[[maybe_unused]] auto &silence_warning = _builder.fail([
275 			common = base::take(_commonFailHandler),
276 			specific = std::move(handler)
277 		](const MTP::Error &error) {
278 			if (!specific(error)) {
279 				common(error);
280 			}
281 		});
282 	}
283 	return *this;
284 }
285 
286 template <typename Request>
send()287 mtpRequestId ApiWrap::RequestBuilder<Request>::send() {
288 	return _commonFailHandler
289 		? _builder.fail(base::take(_commonFailHandler)).send()
290 		: _builder.send();
291 }
292 
LoadedFileCache(int limit)293 ApiWrap::LoadedFileCache::LoadedFileCache(int limit) : _limit(limit) {
294 	Expects(limit >= 0);
295 }
296 
save(const Location & location,const QString & relativePath)297 void ApiWrap::LoadedFileCache::save(
298 		const Location &location,
299 		const QString &relativePath) {
300 	if (!location) {
301 		return;
302 	}
303 	const auto key = ComputeLocationKey(location);
304 	_map[key] = relativePath;
305 	_list.push_back(key);
306 	if (_list.size() > _limit) {
307 		const auto key = _list.front();
308 		_list.pop_front();
309 		_map.erase(key);
310 	}
311 }
312 
find(const Location & location) const313 std::optional<QString> ApiWrap::LoadedFileCache::find(
314 		const Location &location) const {
315 	if (!location) {
316 		return std::nullopt;
317 	}
318 	const auto key = ComputeLocationKey(location);
319 	if (const auto i = _map.find(key); i != end(_map)) {
320 		return i->second;
321 	}
322 	return std::nullopt;
323 }
324 
FileProcess(const QString & path,Output::Stats * stats)325 ApiWrap::FileProcess::FileProcess(const QString &path, Output::Stats *stats)
326 : file(path, stats) {
327 }
328 
329 template <typename Request>
mainRequest(Request && request)330 auto ApiWrap::mainRequest(Request &&request) {
331 	Expects(_takeoutId.has_value());
332 
333 	auto original = std::move(_mtp.request(MTPInvokeWithTakeout<Request>(
334 		MTP_long(*_takeoutId),
335 		std::forward<Request>(request)
336 	)).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift)));
337 
338 	return RequestBuilder<MTPInvokeWithTakeout<Request>>(
339 		std::move(original),
340 		[=](const MTP::Error &result) { error(result); });
341 }
342 
343 template <typename Request>
splitRequest(int index,Request && request)344 auto ApiWrap::splitRequest(int index, Request &&request) {
345 	Expects(index < _splits.size());
346 
347 	//if (index == _splits.size() - 1) {
348 	//	return mainRequest(std::forward<Request>(request));
349 	//}
350 	return mainRequest(MTPInvokeWithMessagesRange<Request>(
351 		_splits[index],
352 		std::forward<Request>(request)));
353 }
354 
fileRequest(const Data::FileLocation & location,int offset)355 auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
356 	Expects(location.dcId != 0
357 		|| location.data.type() == mtpc_inputTakeoutFileLocation);
358 	Expects(_takeoutId.has_value());
359 	Expects(_fileProcess->requestId == 0);
360 
361 	return std::move(_mtp.request(MTPInvokeWithTakeout<MTPupload_GetFile>(
362 		MTP_long(*_takeoutId),
363 		MTPupload_GetFile(
364 			MTP_flags(0),
365 			location.data,
366 			MTP_int(offset),
367 			MTP_int(kFileChunkSize))
368 	)).fail([=](const MTP::Error &result) {
369 		_fileProcess->requestId = 0;
370 		if (result.type() == qstr("TAKEOUT_FILE_EMPTY")
371 			&& _otherDataProcess != nullptr) {
372 			filePartDone(
373 				0,
374 				MTP_upload_file(
375 					MTP_storage_filePartial(),
376 					MTP_int(0),
377 					MTP_bytes()));
378 		} else if (result.type() == qstr("LOCATION_INVALID")
379 			|| result.type() == qstr("VERSION_INVALID")
380 			|| result.type() == qstr("LOCATION_NOT_AVAILABLE")) {
381 			filePartUnavailable();
382 		} else if (result.code() == 400
383 			&& result.type().startsWith(qstr("FILE_REFERENCE_"))) {
384 			filePartRefreshReference(offset);
385 		} else {
386 			error(std::move(result));
387 		}
388 	}).toDC(MTP::ShiftDcId(location.dcId, MTP::kExportMediaDcShift)));
389 }
390 
ApiWrap(QPointer<MTP::Instance> weak,Fn<void (FnMut<void ()>)> runner)391 ApiWrap::ApiWrap(QPointer<MTP::Instance> weak, Fn<void(FnMut<void()>)> runner)
392 : _mtp(weak, std::move(runner))
393 , _fileCache(std::make_unique<LoadedFileCache>(kLocationCacheSize)) {
394 }
395 
errors() const396 rpl::producer<MTP::Error> ApiWrap::errors() const {
397 	return _errors.events();
398 }
399 
ioErrors() const400 rpl::producer<Output::Result> ApiWrap::ioErrors() const {
401 	return _ioErrors.events();
402 }
403 
startExport(const Settings & settings,Output::Stats * stats,FnMut<void (StartInfo)> done)404 void ApiWrap::startExport(
405 		const Settings &settings,
406 		Output::Stats *stats,
407 		FnMut<void(StartInfo)> done) {
408 	Expects(_settings == nullptr);
409 	Expects(_startProcess == nullptr);
410 
411 	_settings = std::make_unique<Settings>(settings);
412 	_stats = stats;
413 	_startProcess = std::make_unique<StartProcess>();
414 	_startProcess->done = std::move(done);
415 
416 	using Step = StartProcess::Step;
417 	if (_settings->types & Settings::Type::Userpics) {
418 		_startProcess->steps.push_back(Step::UserpicsCount);
419 	}
420 	if (_settings->types & Settings::Type::AnyChatsMask) {
421 		_startProcess->steps.push_back(Step::SplitRanges);
422 	}
423 	if (_settings->types & Settings::Type::AnyChatsMask) {
424 		_startProcess->steps.push_back(Step::DialogsCount);
425 	}
426 	if (_settings->types & Settings::Type::GroupsChannelsMask) {
427 		if (!_settings->onlySinglePeer()) {
428 			_startProcess->steps.push_back(Step::LeftChannelsCount);
429 		}
430 	}
431 	startMainSession([=] {
432 		sendNextStartRequest();
433 	});
434 }
435 
sendNextStartRequest()436 void ApiWrap::sendNextStartRequest() {
437 	Expects(_startProcess != nullptr);
438 
439 	auto &steps = _startProcess->steps;
440 	if (steps.empty()) {
441 		finishStartProcess();
442 		return;
443 	}
444 	using Step = StartProcess::Step;
445 	const auto step = steps.front();
446 	steps.pop_front();
447 	switch (step) {
448 	case Step::UserpicsCount:
449 		return requestUserpicsCount();
450 	case Step::SplitRanges:
451 		return requestSplitRanges();
452 	case Step::DialogsCount:
453 		return requestDialogsCount();
454 	case Step::LeftChannelsCount:
455 		return requestLeftChannelsCount();
456 	}
457 	Unexpected("Step in ApiWrap::sendNextStartRequest.");
458 }
459 
requestUserpicsCount()460 void ApiWrap::requestUserpicsCount() {
461 	Expects(_startProcess != nullptr);
462 
463 	mainRequest(MTPphotos_GetUserPhotos(
464 		_user,
465 		MTP_int(0),  // offset
466 		MTP_long(0), // max_id
467 		MTP_int(0)   // limit
468 	)).done([=](const MTPphotos_Photos &result) {
469 		Expects(_settings != nullptr);
470 		Expects(_startProcess != nullptr);
471 
472 		_startProcess->info.userpicsCount = result.match(
473 		[](const MTPDphotos_photos &data) {
474 			return int(data.vphotos().v.size());
475 		}, [](const MTPDphotos_photosSlice &data) {
476 			return data.vcount().v;
477 		});
478 
479 		sendNextStartRequest();
480 	}).send();
481 }
482 
requestSplitRanges()483 void ApiWrap::requestSplitRanges() {
484 	Expects(_startProcess != nullptr);
485 
486 	mainRequest(MTPmessages_GetSplitRanges(
487 	)).done([=](const MTPVector<MTPMessageRange> &result) {
488 		_splits = result.v;
489 		if (_splits.empty()) {
490 			_splits.push_back(MTP_messageRange(
491 				MTP_int(1),
492 				MTP_int(std::numeric_limits<int>::max())));
493 		}
494 		_startProcess->splitIndex = useOnlyLastSplit()
495 			? (_splits.size() - 1)
496 			: 0;
497 
498 		sendNextStartRequest();
499 	}).send();
500 }
501 
requestDialogsCount()502 void ApiWrap::requestDialogsCount() {
503 	Expects(_startProcess != nullptr);
504 
505 	if (_settings->onlySinglePeer()) {
506 		_startProcess->info.dialogsCount =
507 			(_settings->singlePeer.type() == mtpc_inputPeerChannel
508 				? 1
509 				: _splits.size());
510 		sendNextStartRequest();
511 		return;
512 	}
513 
514 	const auto offsetDate = 0;
515 	const auto offsetId = 0;
516 	const auto offsetPeer = MTP_inputPeerEmpty();
517 	const auto limit = 1;
518 	const auto hash = uint64(0);
519 	splitRequest(_startProcess->splitIndex, MTPmessages_GetDialogs(
520 		MTP_flags(0),
521 		MTPint(), // folder_id
522 		MTP_int(offsetDate),
523 		MTP_int(offsetId),
524 		offsetPeer,
525 		MTP_int(limit),
526 		MTP_long(hash)
527 	)).done([=](const MTPmessages_Dialogs &result) {
528 		Expects(_settings != nullptr);
529 		Expects(_startProcess != nullptr);
530 
531 		const auto count = result.match(
532 		[](const MTPDmessages_dialogs &data) {
533 			return int(data.vdialogs().v.size());
534 		}, [](const MTPDmessages_dialogsSlice &data) {
535 			return data.vcount().v;
536 		}, [](const MTPDmessages_dialogsNotModified &data) {
537 			return -1;
538 		});
539 		if (count < 0) {
540 			error("Unexpected dialogsNotModified received.");
541 			return;
542 		}
543 		_startProcess->info.dialogsCount += count;
544 
545 		if (++_startProcess->splitIndex >= _splits.size()) {
546 			sendNextStartRequest();
547 		} else {
548 			requestDialogsCount();
549 		}
550 	}).send();
551 }
552 
requestLeftChannelsCount()553 void ApiWrap::requestLeftChannelsCount() {
554 	Expects(_startProcess != nullptr);
555 	Expects(_leftChannelsProcess == nullptr);
556 
557 	_leftChannelsProcess = std::make_unique<LeftChannelsProcess>();
558 	requestLeftChannelsSliceGeneric([=] {
559 		Expects(_startProcess != nullptr);
560 		Expects(_leftChannelsProcess != nullptr);
561 
562 		_startProcess->info.dialogsCount
563 			+= _leftChannelsProcess->fullCount;
564 		sendNextStartRequest();
565 	});
566 }
567 
finishStartProcess()568 void ApiWrap::finishStartProcess() {
569 	Expects(_startProcess != nullptr);
570 
571 	const auto process = base::take(_startProcess);
572 	process->done(process->info);
573 }
574 
useOnlyLastSplit() const575 bool ApiWrap::useOnlyLastSplit() const {
576 	return !(_settings->types & Settings::Type::NonChannelChatsMask);
577 }
578 
requestLeftChannelsList(Fn<bool (int count)> progress,FnMut<void (Data::DialogsInfo &&)> done)579 void ApiWrap::requestLeftChannelsList(
580 		Fn<bool(int count)> progress,
581 		FnMut<void(Data::DialogsInfo&&)> done) {
582 	Expects(_leftChannelsProcess != nullptr);
583 
584 	_leftChannelsProcess->progress = std::move(progress);
585 	_leftChannelsProcess->done = std::move(done);
586 	requestLeftChannelsSlice();
587 }
588 
requestLeftChannelsSlice()589 void ApiWrap::requestLeftChannelsSlice() {
590 	requestLeftChannelsSliceGeneric([=] {
591 		Expects(_leftChannelsProcess != nullptr);
592 
593 		if (_leftChannelsProcess->finished) {
594 			const auto process = base::take(_leftChannelsProcess);
595 			process->done(std::move(process->info));
596 		} else {
597 			requestLeftChannelsSlice();
598 		}
599 	});
600 }
601 
requestDialogsList(Fn<bool (int count)> progress,FnMut<void (Data::DialogsInfo &&)> done)602 void ApiWrap::requestDialogsList(
603 		Fn<bool(int count)> progress,
604 		FnMut<void(Data::DialogsInfo&&)> done) {
605 	Expects(_dialogsProcess == nullptr);
606 
607 	_dialogsProcess = std::make_unique<DialogsProcess>();
608 	_dialogsProcess->splitIndexPlusOne = _splits.size();
609 	_dialogsProcess->progress = std::move(progress);
610 	_dialogsProcess->done = std::move(done);
611 
612 	requestDialogsSlice();
613 }
614 
startMainSession(FnMut<void ()> done)615 void ApiWrap::startMainSession(FnMut<void()> done) {
616 	using Type = Settings::Type;
617 	const auto sizeLimit = _settings->media.sizeLimit;
618 	const auto hasFiles = ((_settings->media.types != 0) && (sizeLimit > 0))
619 		|| (_settings->types & Type::Userpics);
620 
621 	using Flag = MTPaccount_InitTakeoutSession::Flag;
622 	const auto flags = Flag(0)
623 		| (_settings->types & Type::Contacts ? Flag::f_contacts : Flag(0))
624 		| (hasFiles ? Flag::f_files : Flag(0))
625 		| ((hasFiles && sizeLimit < kFileMaxSize)
626 			? Flag::f_file_max_size
627 			: Flag(0))
628 		| (_settings->types & (Type::PersonalChats | Type::BotChats)
629 			? Flag::f_message_users
630 			: Flag(0))
631 		| (_settings->types & Type::PrivateGroups
632 			? (Flag::f_message_chats | Flag::f_message_megagroups)
633 			: Flag(0))
634 		| (_settings->types & Type::PublicGroups
635 			? Flag::f_message_megagroups
636 			: Flag(0))
637 		| (_settings->types & (Type::PrivateChannels | Type::PublicChannels)
638 			? Flag::f_message_channels
639 			: Flag(0));
640 
641 	_mtp.request(MTPusers_GetUsers(
642 		MTP_vector<MTPInputUser>(1, MTP_inputUserSelf())
643 	)).done([=, done = std::move(done)](
644 			const MTPVector<MTPUser> &result) mutable {
645 		for (const auto &user : result.v) {
646 			user.match([&](const MTPDuser &data) {
647 				if (data.is_self()) {
648 					_selfId.emplace(data.vid());
649 				}
650 			}, [&](const MTPDuserEmpty&) {
651 			});
652 		}
653 		if (!_selfId) {
654 			error("Could not retrieve selfId.");
655 			return;
656 		}
657 		_mtp.request(MTPaccount_InitTakeoutSession(
658 			MTP_flags(flags),
659 			MTP_int(sizeLimit)
660 		)).done([=, done = std::move(done)](
661 				const MTPaccount_Takeout &result) mutable {
662 			_takeoutId = result.match([](const MTPDaccount_takeout &data) {
663 				return data.vid().v;
664 			});
665 			done();
666 		}).fail([=](const MTP::Error &result) {
667 			error(result);
668 		}).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift)).send();
669 	}).fail([=](const MTP::Error &result) {
670 		error(result);
671 	}).send();
672 }
673 
requestPersonalInfo(FnMut<void (Data::PersonalInfo &&)> done)674 void ApiWrap::requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done) {
675 	mainRequest(MTPusers_GetFullUser(
676 		_user
677 	)).done([=, done = std::move(done)](const MTPUserFull &result) mutable {
678 		Expects(result.type() == mtpc_userFull);
679 
680 		const auto &full = result.c_userFull();
681 		if (full.vuser().type() == mtpc_user) {
682 			done(Data::ParsePersonalInfo(result));
683 		} else {
684 			error("Bad user type.");
685 		}
686 	}).send();
687 }
688 
requestOtherData(const QString & suggestedPath,FnMut<void (Data::File &&)> done)689 void ApiWrap::requestOtherData(
690 		const QString &suggestedPath,
691 		FnMut<void(Data::File&&)> done) {
692 	Expects(_otherDataProcess == nullptr);
693 
694 	_otherDataProcess = std::make_unique<OtherDataProcess>();
695 	_otherDataProcess->done = std::move(done);
696 	_otherDataProcess->file.location.data = MTP_inputTakeoutFileLocation();
697 	_otherDataProcess->file.suggestedPath = suggestedPath;
698 	loadFile(
699 		_otherDataProcess->file,
700 		Data::FileOrigin(),
701 		[](FileProgress progress) { return true; },
702 		[=](const QString &result) { otherDataDone(result); });
703 }
704 
otherDataDone(const QString & relativePath)705 void ApiWrap::otherDataDone(const QString &relativePath) {
706 	Expects(_otherDataProcess != nullptr);
707 
708 	const auto process = base::take(_otherDataProcess);
709 	process->file.relativePath = relativePath;
710 	if (relativePath.isEmpty()) {
711 		process->file.skipReason = Data::File::SkipReason::Unavailable;
712 	}
713 	process->done(std::move(process->file));
714 }
715 
requestUserpics(FnMut<bool (Data::UserpicsInfo &&)> start,Fn<bool (DownloadProgress)> progress,Fn<bool (Data::UserpicsSlice &&)> slice,FnMut<void ()> finish)716 void ApiWrap::requestUserpics(
717 		FnMut<bool(Data::UserpicsInfo&&)> start,
718 		Fn<bool(DownloadProgress)> progress,
719 		Fn<bool(Data::UserpicsSlice&&)> slice,
720 		FnMut<void()> finish) {
721 	Expects(_userpicsProcess == nullptr);
722 
723 	_userpicsProcess = std::make_unique<UserpicsProcess>();
724 	_userpicsProcess->start = std::move(start);
725 	_userpicsProcess->fileProgress = std::move(progress);
726 	_userpicsProcess->handleSlice = std::move(slice);
727 	_userpicsProcess->finish = std::move(finish);
728 
729 	mainRequest(MTPphotos_GetUserPhotos(
730 		_user,
731 		MTP_int(0), // offset
732 		MTP_long(_userpicsProcess->maxId),
733 		MTP_int(kUserpicsSliceLimit)
734 	)).done([=](const MTPphotos_Photos &result) mutable {
735 		Expects(_userpicsProcess != nullptr);
736 
737 		auto startInfo = result.match(
738 		[](const MTPDphotos_photos &data) {
739 			return Data::UserpicsInfo{ int(data.vphotos().v.size()) };
740 		}, [](const MTPDphotos_photosSlice &data) {
741 			return Data::UserpicsInfo{ data.vcount().v };
742 		});
743 		if (!_userpicsProcess->start(std::move(startInfo))) {
744 			return;
745 		}
746 
747 		handleUserpicsSlice(result);
748 	}).send();
749 }
750 
handleUserpicsSlice(const MTPphotos_Photos & result)751 void ApiWrap::handleUserpicsSlice(const MTPphotos_Photos &result) {
752 	Expects(_userpicsProcess != nullptr);
753 
754 	result.match([&](const auto &data) {
755 		if constexpr (MTPDphotos_photos::Is<decltype(data)>()) {
756 			_userpicsProcess->lastSlice = true;
757 		}
758 		loadUserpicsFiles(Data::ParseUserpicsSlice(
759 			data.vphotos(),
760 			_userpicsProcess->processed));
761 	});
762 }
763 
loadUserpicsFiles(Data::UserpicsSlice && slice)764 void ApiWrap::loadUserpicsFiles(Data::UserpicsSlice &&slice) {
765 	Expects(_userpicsProcess != nullptr);
766 	Expects(!_userpicsProcess->slice.has_value());
767 
768 	if (slice.list.empty()) {
769 		_userpicsProcess->lastSlice = true;
770 	}
771 	_userpicsProcess->slice = std::move(slice);
772 	_userpicsProcess->fileIndex = 0;
773 	loadNextUserpic();
774 }
775 
loadNextUserpic()776 void ApiWrap::loadNextUserpic() {
777 	Expects(_userpicsProcess != nullptr);
778 	Expects(_userpicsProcess->slice.has_value());
779 
780 	for (auto &list = _userpicsProcess->slice->list
781 		; _userpicsProcess->fileIndex < list.size()
782 		; ++_userpicsProcess->fileIndex) {
783 		const auto ready = processFileLoad(
784 			list[_userpicsProcess->fileIndex].image.file,
785 			Data::FileOrigin(),
786 			[=](FileProgress value) { return loadUserpicProgress(value); },
787 			[=](const QString &path) { loadUserpicDone(path); });
788 		if (!ready) {
789 			return;
790 		}
791 	}
792 	finishUserpicsSlice();
793 }
794 
finishUserpicsSlice()795 void ApiWrap::finishUserpicsSlice() {
796 	Expects(_userpicsProcess != nullptr);
797 	Expects(_userpicsProcess->slice.has_value());
798 
799 	auto slice = *base::take(_userpicsProcess->slice);
800 	if (!slice.list.empty()) {
801 		_userpicsProcess->processed += slice.list.size();
802 		_userpicsProcess->maxId = slice.list.back().id;
803 		if (!_userpicsProcess->handleSlice(std::move(slice))) {
804 			return;
805 		}
806 	}
807 	if (_userpicsProcess->lastSlice) {
808 		finishUserpics();
809 		return;
810 	}
811 
812 	mainRequest(MTPphotos_GetUserPhotos(
813 		_user,
814 		MTP_int(0), // offset
815 		MTP_long(_userpicsProcess->maxId),
816 		MTP_int(kUserpicsSliceLimit)
817 	)).done([=](const MTPphotos_Photos &result) {
818 		handleUserpicsSlice(result);
819 	}).send();
820 }
821 
loadUserpicProgress(FileProgress progress)822 bool ApiWrap::loadUserpicProgress(FileProgress progress) {
823 	Expects(_fileProcess != nullptr);
824 	Expects(_userpicsProcess != nullptr);
825 	Expects(_userpicsProcess->slice.has_value());
826 	Expects((_userpicsProcess->fileIndex >= 0)
827 		&& (_userpicsProcess->fileIndex
828 			< _userpicsProcess->slice->list.size()));
829 
830 	return _userpicsProcess->fileProgress(DownloadProgress{
831 		_fileProcess->randomId,
832 		_fileProcess->relativePath,
833 		_userpicsProcess->fileIndex,
834 		progress.ready,
835 		progress.total });
836 }
837 
loadUserpicDone(const QString & relativePath)838 void ApiWrap::loadUserpicDone(const QString &relativePath) {
839 	Expects(_userpicsProcess != nullptr);
840 	Expects(_userpicsProcess->slice.has_value());
841 	Expects((_userpicsProcess->fileIndex >= 0)
842 		&& (_userpicsProcess->fileIndex
843 			< _userpicsProcess->slice->list.size()));
844 
845 	const auto index = _userpicsProcess->fileIndex;
846 	auto &file = _userpicsProcess->slice->list[index].image.file;
847 	file.relativePath = relativePath;
848 	if (relativePath.isEmpty()) {
849 		file.skipReason = Data::File::SkipReason::Unavailable;
850 	}
851 	loadNextUserpic();
852 }
853 
finishUserpics()854 void ApiWrap::finishUserpics() {
855 	Expects(_userpicsProcess != nullptr);
856 
857 	base::take(_userpicsProcess)->finish();
858 }
859 
requestContacts(FnMut<void (Data::ContactsList &&)> done)860 void ApiWrap::requestContacts(FnMut<void(Data::ContactsList&&)> done) {
861 	Expects(_contactsProcess == nullptr);
862 
863 	_contactsProcess = std::make_unique<ContactsProcess>();
864 	_contactsProcess->done = std::move(done);
865 	mainRequest(MTPcontacts_GetSaved(
866 	)).done([=](const MTPVector<MTPSavedContact> &result) {
867 		_contactsProcess->result = Data::ParseContactsList(result);
868 		requestTopPeersSlice();
869 	}).send();
870 }
871 
requestTopPeersSlice()872 void ApiWrap::requestTopPeersSlice() {
873 	Expects(_contactsProcess != nullptr);
874 
875 	using Flag = MTPcontacts_GetTopPeers::Flag;
876 	mainRequest(MTPcontacts_GetTopPeers(
877 		MTP_flags(Flag::f_correspondents
878 			| Flag::f_bots_inline
879 			| Flag::f_phone_calls),
880 		MTP_int(_contactsProcess->topPeersOffset),
881 		MTP_int(kTopPeerSliceLimit),
882 		MTP_long(0) // hash
883 	)).done([=](const MTPcontacts_TopPeers &result) {
884 		Expects(_contactsProcess != nullptr);
885 
886 		if (!Data::AppendTopPeers(_contactsProcess->result, result)) {
887 			error("Unexpected data in ApiWrap::requestTopPeersSlice.");
888 			return;
889 		}
890 
891 		const auto offset = _contactsProcess->topPeersOffset;
892 		const auto loaded = result.match(
893 		[](const MTPDcontacts_topPeersNotModified &data) {
894 			return true;
895 		}, [](const MTPDcontacts_topPeersDisabled &data) {
896 			return true;
897 		}, [&](const MTPDcontacts_topPeers &data) {
898 			for (const auto &category : data.vcategories().v) {
899 				const auto loaded = category.match(
900 				[&](const MTPDtopPeerCategoryPeers &data) {
901 					return offset + data.vpeers().v.size() >= data.vcount().v;
902 				});
903 				if (!loaded) {
904 					return false;
905 				}
906 			}
907 			return true;
908 		});
909 
910 		if (loaded) {
911 			auto process = base::take(_contactsProcess);
912 			process->done(std::move(process->result));
913 		} else {
914 			_contactsProcess->topPeersOffset = std::max(std::max(
915 				_contactsProcess->result.correspondents.size(),
916 				_contactsProcess->result.inlineBots.size()),
917 				_contactsProcess->result.phoneCalls.size());
918 			requestTopPeersSlice();
919 		}
920 	}).send();
921 }
922 
requestSessions(FnMut<void (Data::SessionsList &&)> done)923 void ApiWrap::requestSessions(FnMut<void(Data::SessionsList&&)> done) {
924 	mainRequest(MTPaccount_GetAuthorizations(
925 	)).done([=, done = std::move(done)](
926 			const MTPaccount_Authorizations &result) mutable {
927 		auto list = Data::ParseSessionsList(result);
928 		mainRequest(MTPaccount_GetWebAuthorizations(
929 		)).done([=, done = std::move(done), list = std::move(list)](
930 				const MTPaccount_WebAuthorizations &result) mutable {
931 			list.webList = Data::ParseWebSessionsList(result).webList;
932 			done(std::move(list));
933 		}).send();
934 	}).send();
935 }
936 
requestMessages(const Data::DialogInfo & info,FnMut<bool (const Data::DialogInfo &)> start,Fn<bool (DownloadProgress)> progress,Fn<bool (Data::MessagesSlice &&)> slice,FnMut<void ()> done)937 void ApiWrap::requestMessages(
938 		const Data::DialogInfo &info,
939 		FnMut<bool(const Data::DialogInfo &)> start,
940 		Fn<bool(DownloadProgress)> progress,
941 		Fn<bool(Data::MessagesSlice&&)> slice,
942 		FnMut<void()> done) {
943 	Expects(_chatProcess == nullptr);
944 	Expects(_selfId.has_value());
945 
946 	_chatProcess = std::make_unique<ChatProcess>();
947 	_chatProcess->context.selfPeerId = peerFromUser(*_selfId);
948 	_chatProcess->info = info;
949 	_chatProcess->start = std::move(start);
950 	_chatProcess->fileProgress = std::move(progress);
951 	_chatProcess->handleSlice = std::move(slice);
952 	_chatProcess->done = std::move(done);
953 
954 	requestMessagesCount(0);
955 }
956 
requestMessagesCount(int localSplitIndex)957 void ApiWrap::requestMessagesCount(int localSplitIndex) {
958 	Expects(_chatProcess != nullptr);
959 	Expects(localSplitIndex < _chatProcess->info.splits.size());
960 
961 	requestChatMessages(
962 		_chatProcess->info.splits[localSplitIndex],
963 		0, // offset_id
964 		0, // add_offset
965 		1, // limit
966 		[=](const MTPmessages_Messages &result) {
967 		Expects(_chatProcess != nullptr);
968 
969 		const auto count = result.match(
970 			[](const MTPDmessages_messages &data) {
971 			return int(data.vmessages().v.size());
972 		}, [](const MTPDmessages_messagesSlice &data) {
973 			return data.vcount().v;
974 		}, [](const MTPDmessages_channelMessages &data) {
975 			return data.vcount().v;
976 		}, [](const MTPDmessages_messagesNotModified &data) {
977 			return -1;
978 		});
979 		if (count < 0) {
980 			error("Unexpected messagesNotModified received.");
981 			return;
982 		}
983 		const auto skipSplit = !Data::SingleMessageAfter(
984 			result,
985 			_settings->singlePeerFrom);
986 		if (skipSplit) {
987 			// No messages from the requested range, skip this split.
988 			messagesCountLoaded(localSplitIndex, 0);
989 			return;
990 		}
991 		checkFirstMessageDate(localSplitIndex, count);
992 	});
993 }
994 
checkFirstMessageDate(int localSplitIndex,int count)995 void ApiWrap::checkFirstMessageDate(int localSplitIndex, int count) {
996 	Expects(_chatProcess != nullptr);
997 	Expects(localSplitIndex < _chatProcess->info.splits.size());
998 
999 	if (_settings->singlePeerTill <= 0) {
1000 		messagesCountLoaded(localSplitIndex, count);
1001 		return;
1002 	}
1003 
1004 	// Request first message in this split to check if its' date < till.
1005 	requestChatMessages(
1006 		_chatProcess->info.splits[localSplitIndex],
1007 		1, // offset_id
1008 		-1, // add_offset
1009 		1, // limit
1010 		[=](const MTPmessages_Messages &result) {
1011 		Expects(_chatProcess != nullptr);
1012 
1013 		const auto skipSplit = !Data::SingleMessageBefore(
1014 			result,
1015 			_settings->singlePeerTill);
1016 		messagesCountLoaded(localSplitIndex, skipSplit ? 0 : count);
1017 	});
1018 }
1019 
messagesCountLoaded(int localSplitIndex,int count)1020 void ApiWrap::messagesCountLoaded(int localSplitIndex, int count) {
1021 	Expects(_chatProcess != nullptr);
1022 	Expects(localSplitIndex < _chatProcess->info.splits.size());
1023 
1024 	_chatProcess->info.messagesCountPerSplit[localSplitIndex] = count;
1025 	if (localSplitIndex + 1 < _chatProcess->info.splits.size()) {
1026 		requestMessagesCount(localSplitIndex + 1);
1027 	} else if (_chatProcess->start(_chatProcess->info)) {
1028 		requestMessagesSlice();
1029 	}
1030 }
1031 
finishExport(FnMut<void ()> done)1032 void ApiWrap::finishExport(FnMut<void()> done) {
1033 	const auto guard = gsl::finally([&] { _takeoutId = std::nullopt; });
1034 
1035 	mainRequest(MTPaccount_FinishTakeoutSession(
1036 		MTP_flags(MTPaccount_FinishTakeoutSession::Flag::f_success)
1037 	)).done(std::move(done)).send();
1038 }
1039 
skipFile(uint64 randomId)1040 void ApiWrap::skipFile(uint64 randomId) {
1041 	if (!_fileProcess || _fileProcess->randomId != randomId) {
1042 		return;
1043 	}
1044 	LOG(("Export Info: File skipped."));
1045 	Assert(!_fileProcess->requests.empty());
1046 	Assert(_fileProcess->requestId != 0);
1047 	_mtp.request(base::take(_fileProcess->requestId)).cancel();
1048 	base::take(_fileProcess)->done(QString());
1049 }
1050 
cancelExportFast()1051 void ApiWrap::cancelExportFast() {
1052 	if (_takeoutId.has_value()) {
1053 		const auto requestId = mainRequest(MTPaccount_FinishTakeoutSession(
1054 			MTP_flags(0)
1055 		)).send();
1056 		_mtp.request(requestId).detach();
1057 	}
1058 }
1059 
requestSinglePeerDialog()1060 void ApiWrap::requestSinglePeerDialog() {
1061 	auto doneSinglePeer = [=](const auto &result) {
1062 		appendSinglePeerDialogs(
1063 			Data::ParseDialogsInfo(_settings->singlePeer, result));
1064 	};
1065 	const auto requestUser = [&](const MTPInputUser &data) {
1066 		mainRequest(MTPusers_GetUsers(
1067 			MTP_vector<MTPInputUser>(1, data)
1068 		)).done(std::move(doneSinglePeer)).send();
1069 	};
1070 	_settings->singlePeer.match([&](const MTPDinputPeerUser &data) {
1071 		requestUser(MTP_inputUser(data.vuser_id(), data.vaccess_hash()));
1072 	}, [&](const MTPDinputPeerChat &data) {
1073 		mainRequest(MTPmessages_GetChats(
1074 			MTP_vector<MTPlong>(1, data.vchat_id())
1075 		)).done(std::move(doneSinglePeer)).send();
1076 	}, [&](const MTPDinputPeerChannel &data) {
1077 		mainRequest(MTPchannels_GetChannels(
1078 			MTP_vector<MTPInputChannel>(
1079 				1,
1080 				MTP_inputChannel(data.vchannel_id(), data.vaccess_hash()))
1081 		)).done(std::move(doneSinglePeer)).send();
1082 	}, [&](const MTPDinputPeerSelf &data) {
1083 		requestUser(MTP_inputUserSelf());
1084 	}, [&](const MTPDinputPeerUserFromMessage &data) {
1085 		Unexpected("From message peer in ApiWrap::requestSinglePeerDialog.");
1086 	}, [&](const MTPDinputPeerChannelFromMessage &data) {
1087 		Unexpected("From message peer in ApiWrap::requestSinglePeerDialog.");
1088 	}, [](const MTPDinputPeerEmpty &data) {
1089 		Unexpected("Empty peer in ApiWrap::requestSinglePeerDialog.");
1090 	});
1091 }
1092 
requestSinglePeerMigrated(const Data::DialogInfo & info)1093 mtpRequestId ApiWrap::requestSinglePeerMigrated(
1094 		const Data::DialogInfo &info) {
1095 	const auto input = info.input.match([&](
1096 		const MTPDinputPeerChannel & data) {
1097 		return MTP_inputChannel(
1098 			data.vchannel_id(),
1099 			data.vaccess_hash());
1100 	}, [](auto&&) -> MTPinputChannel {
1101 		Unexpected("Peer type in a supergroup.");
1102 	});
1103 	return mainRequest(MTPchannels_GetFullChannel(
1104 		input
1105 	)).done([=](const MTPmessages_ChatFull &result) {
1106 		auto info = result.match([&](
1107 				const MTPDmessages_chatFull &data) {
1108 			const auto migratedChatId = data.vfull_chat().match([&](
1109 					const MTPDchannelFull &data) {
1110 				return data.vmigrated_from_chat_id().value_or_empty();
1111 			}, [](auto &&other) -> BareId {
1112 				return 0;
1113 			});
1114 			return migratedChatId
1115 				? Data::ParseDialogsInfo(
1116 					MTP_inputPeerChat(MTP_long(migratedChatId)),
1117 					MTP_messages_chats(data.vchats()))
1118 				: Data::DialogsInfo();
1119 		});
1120 		appendSinglePeerDialogs(std::move(info));
1121 	}).send();
1122 }
1123 
appendSinglePeerDialogs(Data::DialogsInfo && info)1124 void ApiWrap::appendSinglePeerDialogs(Data::DialogsInfo &&info) {
1125 	const auto isSupergroupType = [](Data::DialogInfo::Type type) {
1126 		using Type = Data::DialogInfo::Type;
1127 		return (type == Type::PrivateSupergroup)
1128 			|| (type == Type::PublicSupergroup);
1129 	};
1130 	const auto isChannelType = [](Data::DialogInfo::Type type) {
1131 		using Type = Data::DialogInfo::Type;
1132 		return (type == Type::PrivateChannel)
1133 			|| (type == Type::PublicChannel);
1134 	};
1135 
1136 	auto migratedRequestId = mtpRequestId(0);
1137 	const auto last = _dialogsProcess->splitIndexPlusOne - 1;
1138 	for (auto &info : info.chats) {
1139 		if (isSupergroupType(info.type) && !migratedRequestId) {
1140 			migratedRequestId = requestSinglePeerMigrated(info);
1141 			continue;
1142 		} else if (isChannelType(info.type)) {
1143 			continue;
1144 		}
1145 		for (auto i = last; i != 0; --i) {
1146 			info.splits.push_back(i - 1);
1147 			info.messagesCountPerSplit.push_back(0);
1148 		}
1149 	}
1150 
1151 	if (!migratedRequestId) {
1152 		_dialogsProcess->processedCount += info.chats.size();
1153 	}
1154 	appendDialogsSlice(std::move(info));
1155 
1156 	if (migratedRequestId
1157 		|| !_dialogsProcess->progress(_dialogsProcess->processedCount)) {
1158 		return;
1159 	}
1160 	finishDialogsList();
1161 }
1162 
requestDialogsSlice()1163 void ApiWrap::requestDialogsSlice() {
1164 	Expects(_dialogsProcess != nullptr);
1165 
1166 	if (_settings->onlySinglePeer()) {
1167 		requestSinglePeerDialog();
1168 		return;
1169 	}
1170 
1171 	const auto splitIndex = _dialogsProcess->splitIndexPlusOne - 1;
1172 	const auto hash = uint64(0);
1173 	splitRequest(splitIndex, MTPmessages_GetDialogs(
1174 		MTP_flags(0),
1175 		MTPint(), // folder_id
1176 		MTP_int(_dialogsProcess->offsetDate),
1177 		MTP_int(_dialogsProcess->offsetId),
1178 		_dialogsProcess->offsetPeer,
1179 		MTP_int(kChatsSliceLimit),
1180 		MTP_long(hash)
1181 	)).done([=](const MTPmessages_Dialogs &result) {
1182 		if (result.type() == mtpc_messages_dialogsNotModified) {
1183 			error("Unexpected dialogsNotModified received.");
1184 			return;
1185 		}
1186 		auto finished = result.match(
1187 		[](const MTPDmessages_dialogs &data) {
1188 			return true;
1189 		}, [](const MTPDmessages_dialogsSlice &data) {
1190 			return data.vdialogs().v.isEmpty();
1191 		}, [](const MTPDmessages_dialogsNotModified &data) {
1192 			return true;
1193 		});
1194 
1195 		auto info = Data::ParseDialogsInfo(result);
1196 		_dialogsProcess->processedCount += info.chats.size();
1197 		const auto last = info.chats.empty()
1198 			? Data::DialogInfo()
1199 			: info.chats.back();
1200 		appendDialogsSlice(std::move(info));
1201 
1202 		if (!_dialogsProcess->progress(_dialogsProcess->processedCount)) {
1203 			return;
1204 		}
1205 
1206 		if (!finished && last.topMessageDate > 0) {
1207 			_dialogsProcess->offsetId = last.topMessageId;
1208 			_dialogsProcess->offsetDate = last.topMessageDate;
1209 			_dialogsProcess->offsetPeer = last.input;
1210 		} else if (!useOnlyLastSplit()
1211 			&& --_dialogsProcess->splitIndexPlusOne > 0) {
1212 			_dialogsProcess->offsetId = 0;
1213 			_dialogsProcess->offsetDate = 0;
1214 			_dialogsProcess->offsetPeer = MTP_inputPeerEmpty();
1215 		} else {
1216 			requestLeftChannelsIfNeeded();
1217 			return;
1218 		}
1219 		requestDialogsSlice();
1220 	}).send();
1221 }
1222 
appendDialogsSlice(Data::DialogsInfo && info)1223 void ApiWrap::appendDialogsSlice(Data::DialogsInfo &&info) {
1224 	Expects(_dialogsProcess != nullptr);
1225 	Expects(_dialogsProcess->splitIndexPlusOne <= _splits.size());
1226 
1227 	appendChatsSlice(
1228 		*_dialogsProcess,
1229 		_dialogsProcess->info.chats,
1230 		std::move(info.chats),
1231 		_dialogsProcess->splitIndexPlusOne - 1);
1232 }
1233 
requestLeftChannelsIfNeeded()1234 void ApiWrap::requestLeftChannelsIfNeeded() {
1235 	if (_settings->types & Settings::Type::GroupsChannelsMask) {
1236 		requestLeftChannelsList([=](int count) {
1237 			Expects(_dialogsProcess != nullptr);
1238 
1239 			return _dialogsProcess->progress(
1240 				_dialogsProcess->processedCount + count);
1241 		}, [=](Data::DialogsInfo &&result) {
1242 			Expects(_dialogsProcess != nullptr);
1243 
1244 			_dialogsProcess->info.left = std::move(result.left);
1245 			finishDialogsList();
1246 		});
1247 	} else {
1248 		finishDialogsList();
1249 	}
1250 }
1251 
finishDialogsList()1252 void ApiWrap::finishDialogsList() {
1253 	Expects(_dialogsProcess != nullptr);
1254 
1255 	const auto process = base::take(_dialogsProcess);
1256 	Data::FinalizeDialogsInfo(process->info, *_settings);
1257 	process->done(std::move(process->info));
1258 }
1259 
requestLeftChannelsSliceGeneric(FnMut<void ()> done)1260 void ApiWrap::requestLeftChannelsSliceGeneric(FnMut<void()> done) {
1261 	Expects(_leftChannelsProcess != nullptr);
1262 
1263 	mainRequest(MTPchannels_GetLeftChannels(
1264 		MTP_int(_leftChannelsProcess->offset)
1265 	)).done([=, done = std::move(done)](
1266 			const MTPmessages_Chats &result) mutable {
1267 		Expects(_leftChannelsProcess != nullptr);
1268 
1269 		appendLeftChannelsSlice(Data::ParseLeftChannelsInfo(result));
1270 
1271 		const auto process = _leftChannelsProcess.get();
1272 		process->offset += result.match(
1273 		[](const auto &data) {
1274 			return int(data.vchats().v.size());
1275 		});
1276 
1277 		process->fullCount = result.match(
1278 		[](const MTPDmessages_chats &data) {
1279 			return int(data.vchats().v.size());
1280 		}, [](const MTPDmessages_chatsSlice &data) {
1281 			return data.vcount().v;
1282 		});
1283 
1284 		process->finished = result.match(
1285 		[](const MTPDmessages_chats &data) {
1286 			return true;
1287 		}, [](const MTPDmessages_chatsSlice &data) {
1288 			return data.vchats().v.isEmpty();
1289 		});
1290 
1291 		if (process->progress) {
1292 			if (!process->progress(process->info.left.size())) {
1293 				return;
1294 			}
1295 		}
1296 
1297 		done();
1298 	}).send();
1299 }
1300 
appendLeftChannelsSlice(Data::DialogsInfo && info)1301 void ApiWrap::appendLeftChannelsSlice(Data::DialogsInfo &&info) {
1302 	Expects(_leftChannelsProcess != nullptr);
1303 	Expects(!_splits.empty());
1304 
1305 	appendChatsSlice(
1306 		*_leftChannelsProcess,
1307 		_leftChannelsProcess->info.left,
1308 		std::move(info.left),
1309 		_splits.size() - 1);
1310 }
1311 
appendChatsSlice(ChatsProcess & process,std::vector<Data::DialogInfo> & to,std::vector<Data::DialogInfo> && from,int splitIndex)1312 void ApiWrap::appendChatsSlice(
1313 		ChatsProcess &process,
1314 		std::vector<Data::DialogInfo> &to,
1315 		std::vector<Data::DialogInfo> &&from,
1316 		int splitIndex) {
1317 	Expects(_settings != nullptr);
1318 
1319 	const auto types = _settings->types;
1320 	const auto goodByTypes = [&](const Data::DialogInfo &info) {
1321 		return ((types & SettingsFromDialogsType(info.type)) != 0);
1322 	};
1323 	auto filtered = ranges::views::all(
1324 		from
1325 	) | ranges::views::filter([&](const Data::DialogInfo &info) {
1326 		if (goodByTypes(info)) {
1327 			return true;
1328 		} else if (info.migratedToChannelId
1329 			&& (((types & Settings::Type::PublicGroups) != 0)
1330 				|| ((types & Settings::Type::PrivateGroups) != 0))) {
1331 			return true;
1332 		}
1333 		return false;
1334 	});
1335 	to.reserve(to.size() + from.size());
1336 	for (auto &info : filtered) {
1337 		const auto nextIndex = to.size();
1338 		if (info.migratedToChannelId) {
1339 			const auto toPeerId = PeerId(info.migratedToChannelId);
1340 			const auto i = process.indexByPeer.find(toPeerId);
1341 			if (i != process.indexByPeer.end()
1342 				&& Data::AddMigrateFromSlice(
1343 					to[i->second],
1344 					info,
1345 					splitIndex,
1346 					int(_splits.size()))) {
1347 				continue;
1348 			} else if (!goodByTypes(info)) {
1349 				continue;
1350 			}
1351 		}
1352 		const auto [i, ok] = process.indexByPeer.emplace(
1353 			info.peerId,
1354 			nextIndex);
1355 		if (ok) {
1356 			to.push_back(std::move(info));
1357 		}
1358 		to[i->second].splits.push_back(splitIndex);
1359 		to[i->second].messagesCountPerSplit.push_back(0);
1360 	}
1361 }
1362 
requestMessagesSlice()1363 void ApiWrap::requestMessagesSlice() {
1364 	Expects(_chatProcess != nullptr);
1365 
1366 	const auto count = _chatProcess->info.messagesCountPerSplit[
1367 		_chatProcess->localSplitIndex];
1368 	if (!count) {
1369 		loadMessagesFiles({});
1370 		return;
1371 	}
1372 	requestChatMessages(
1373 		_chatProcess->info.splits[_chatProcess->localSplitIndex],
1374 		_chatProcess->largestIdPlusOne,
1375 		-kMessagesSliceLimit,
1376 		kMessagesSliceLimit,
1377 		[=](const MTPmessages_Messages &result) {
1378 		Expects(_chatProcess != nullptr);
1379 
1380 		result.match([&](const MTPDmessages_messagesNotModified &data) {
1381 			error("Unexpected messagesNotModified received.");
1382 		}, [&](const auto &data) {
1383 			if constexpr (MTPDmessages_messages::Is<decltype(data)>()) {
1384 				_chatProcess->lastSlice = true;
1385 			}
1386 			loadMessagesFiles(Data::ParseMessagesSlice(
1387 				_chatProcess->context,
1388 				data.vmessages(),
1389 				data.vusers(),
1390 				data.vchats(),
1391 				_chatProcess->info.relativePath));
1392 		});
1393 	});
1394 }
1395 
requestChatMessages(int splitIndex,int offsetId,int addOffset,int limit,FnMut<void (MTPmessages_Messages &&)> done)1396 void ApiWrap::requestChatMessages(
1397 		int splitIndex,
1398 		int offsetId,
1399 		int addOffset,
1400 		int limit,
1401 		FnMut<void(MTPmessages_Messages&&)> done) {
1402 	Expects(_chatProcess != nullptr);
1403 
1404 	_chatProcess->requestDone = std::move(done);
1405 	const auto doneHandler = [=](MTPmessages_Messages &&result) {
1406 		Expects(_chatProcess != nullptr);
1407 
1408 		base::take(_chatProcess->requestDone)(std::move(result));
1409 	};
1410 	const auto splitsCount = int(_splits.size());
1411 	const auto realPeerInput = (splitIndex >= 0)
1412 		? _chatProcess->info.input
1413 		: _chatProcess->info.migratedFromInput;
1414 	const auto realSplitIndex = (splitIndex >= 0)
1415 		? splitIndex
1416 		: (splitsCount + splitIndex);
1417 	if (_chatProcess->info.onlyMyMessages) {
1418 		splitRequest(realSplitIndex, MTPmessages_Search(
1419 			MTP_flags(MTPmessages_Search::Flag::f_from_id),
1420 			realPeerInput,
1421 			MTP_string(), // query
1422 			MTP_inputPeerSelf(),
1423 			MTPint(), // top_msg_id
1424 			MTP_inputMessagesFilterEmpty(),
1425 			MTP_int(0), // min_date
1426 			MTP_int(0), // max_date
1427 			MTP_int(offsetId),
1428 			MTP_int(addOffset),
1429 			MTP_int(limit),
1430 			MTP_int(0), // max_id
1431 			MTP_int(0), // min_id
1432 			MTP_long(0) // hash
1433 		)).done(doneHandler).send();
1434 	} else {
1435 		splitRequest(realSplitIndex, MTPmessages_GetHistory(
1436 			realPeerInput,
1437 			MTP_int(offsetId),
1438 			MTP_int(0), // offset_date
1439 			MTP_int(addOffset),
1440 			MTP_int(limit),
1441 			MTP_int(0), // max_id
1442 			MTP_int(0), // min_id
1443 			MTP_long(0)  // hash
1444 		)).fail([=](const MTP::Error &error) {
1445 			Expects(_chatProcess != nullptr);
1446 
1447 			if (error.type() == qstr("CHANNEL_PRIVATE")) {
1448 				if (realPeerInput.type() == mtpc_inputPeerChannel
1449 					&& !_chatProcess->info.onlyMyMessages) {
1450 
1451 					// Perhaps we just left / were kicked from channel.
1452 					// Just switch to only my messages.
1453 					_chatProcess->info.onlyMyMessages = true;
1454 					requestChatMessages(
1455 						splitIndex,
1456 						offsetId,
1457 						addOffset,
1458 						limit,
1459 						base::take(_chatProcess->requestDone));
1460 					return true;
1461 				}
1462 			}
1463 			return false;
1464 		}).done(doneHandler).send();
1465 	}
1466 }
1467 
loadMessagesFiles(Data::MessagesSlice && slice)1468 void ApiWrap::loadMessagesFiles(Data::MessagesSlice &&slice) {
1469 	Expects(_chatProcess != nullptr);
1470 	Expects(!_chatProcess->slice.has_value());
1471 
1472 	if (slice.list.empty()) {
1473 		_chatProcess->lastSlice = true;
1474 	}
1475 	_chatProcess->slice = std::move(slice);
1476 	_chatProcess->fileIndex = 0;
1477 
1478 	loadNextMessageFile();
1479 }
1480 
currentFileMessage() const1481 Data::Message *ApiWrap::currentFileMessage() const {
1482 	Expects(_chatProcess != nullptr);
1483 	Expects(_chatProcess->slice.has_value());
1484 
1485 	return &_chatProcess->slice->list[_chatProcess->fileIndex];
1486 }
1487 
currentFileMessageOrigin() const1488 Data::FileOrigin ApiWrap::currentFileMessageOrigin() const {
1489 	Expects(_chatProcess != nullptr);
1490 	Expects(_chatProcess->slice.has_value());
1491 
1492 	const auto splitIndex = _chatProcess->info.splits[
1493 		_chatProcess->localSplitIndex];
1494 	auto result = Data::FileOrigin();
1495 	result.messageId = currentFileMessage()->id;
1496 	result.split = (splitIndex >= 0)
1497 		? splitIndex
1498 		: (int(_splits.size()) + splitIndex);
1499 	result.peer = (splitIndex >= 0)
1500 		? _chatProcess->info.input
1501 		: _chatProcess->info.migratedFromInput;
1502 	return result;
1503 }
1504 
loadNextMessageFile()1505 void ApiWrap::loadNextMessageFile() {
1506 	Expects(_chatProcess != nullptr);
1507 	Expects(_chatProcess->slice.has_value());
1508 
1509 	for (auto &list = _chatProcess->slice->list
1510 		; _chatProcess->fileIndex < list.size()
1511 		; ++_chatProcess->fileIndex) {
1512 		const auto &message = list[_chatProcess->fileIndex];
1513 		if (Data::SkipMessageByDate(message, *_settings)) {
1514 			continue;
1515 		}
1516 		const auto fileProgress = [=](FileProgress value) {
1517 			return loadMessageFileProgress(value);
1518 		};
1519 		const auto ready = processFileLoad(
1520 			list[_chatProcess->fileIndex].file(),
1521 			currentFileMessageOrigin(),
1522 			fileProgress,
1523 			[=](const QString &path) { loadMessageFileDone(path); },
1524 			currentFileMessage());
1525 		if (!ready) {
1526 			return;
1527 		}
1528 		const auto thumbProgress = [=](FileProgress value) {
1529 			return loadMessageThumbProgress(value);
1530 		};
1531 		const auto thumbReady = processFileLoad(
1532 			list[_chatProcess->fileIndex].thumb().file,
1533 			currentFileMessageOrigin(),
1534 			thumbProgress,
1535 			[=](const QString &path) { loadMessageThumbDone(path); },
1536 			currentFileMessage());
1537 		if (!thumbReady) {
1538 			return;
1539 		}
1540 	}
1541 	finishMessagesSlice();
1542 }
1543 
finishMessagesSlice()1544 void ApiWrap::finishMessagesSlice() {
1545 	Expects(_chatProcess != nullptr);
1546 	Expects(_chatProcess->slice.has_value());
1547 
1548 	auto slice = *base::take(_chatProcess->slice);
1549 	if (!slice.list.empty()) {
1550 		_chatProcess->largestIdPlusOne = slice.list.back().id + 1;
1551 		const auto splitIndex = _chatProcess->info.splits[
1552 			_chatProcess->localSplitIndex];
1553 		if (splitIndex < 0) {
1554 			slice = AdjustMigrateMessageIds(std::move(slice));
1555 		}
1556 		if (!_chatProcess->handleSlice(std::move(slice))) {
1557 			return;
1558 		}
1559 	}
1560 	if (_chatProcess->lastSlice
1561 		&& (++_chatProcess->localSplitIndex
1562 			< _chatProcess->info.splits.size())) {
1563 		_chatProcess->lastSlice = false;
1564 		_chatProcess->largestIdPlusOne = 1;
1565 	}
1566 	if (!_chatProcess->lastSlice) {
1567 		requestMessagesSlice();
1568 	} else {
1569 		finishMessages();
1570 	}
1571 }
1572 
loadMessageFileProgress(FileProgress progress)1573 bool ApiWrap::loadMessageFileProgress(FileProgress progress) {
1574 	Expects(_fileProcess != nullptr);
1575 	Expects(_chatProcess != nullptr);
1576 	Expects(_chatProcess->slice.has_value());
1577 	Expects((_chatProcess->fileIndex >= 0)
1578 		&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
1579 
1580 	return _chatProcess->fileProgress(DownloadProgress{
1581 		.randomId = _fileProcess->randomId,
1582 		.path = _fileProcess->relativePath,
1583 		.itemIndex = _chatProcess->fileIndex,
1584 		.ready = progress.ready,
1585 		.total = progress.total });
1586 }
1587 
loadMessageFileDone(const QString & relativePath)1588 void ApiWrap::loadMessageFileDone(const QString &relativePath) {
1589 	Expects(_chatProcess != nullptr);
1590 	Expects(_chatProcess->slice.has_value());
1591 	Expects((_chatProcess->fileIndex >= 0)
1592 		&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
1593 
1594 	const auto index = _chatProcess->fileIndex;
1595 	auto &file = _chatProcess->slice->list[index].file();
1596 	file.relativePath = relativePath;
1597 	if (relativePath.isEmpty()) {
1598 		file.skipReason = Data::File::SkipReason::Unavailable;
1599 	}
1600 	loadNextMessageFile();
1601 }
1602 
loadMessageThumbProgress(FileProgress progress)1603 bool ApiWrap::loadMessageThumbProgress(FileProgress progress) {
1604 	return loadMessageFileProgress(progress);
1605 }
1606 
loadMessageThumbDone(const QString & relativePath)1607 void ApiWrap::loadMessageThumbDone(const QString &relativePath) {
1608 	Expects(_chatProcess != nullptr);
1609 	Expects(_chatProcess->slice.has_value());
1610 	Expects((_chatProcess->fileIndex >= 0)
1611 		&& (_chatProcess->fileIndex < _chatProcess->slice->list.size()));
1612 
1613 	const auto index = _chatProcess->fileIndex;
1614 	auto &file = _chatProcess->slice->list[index].thumb().file;
1615 	file.relativePath = relativePath;
1616 	if (relativePath.isEmpty()) {
1617 		file.skipReason = Data::File::SkipReason::Unavailable;
1618 	}
1619 	loadNextMessageFile();
1620 }
1621 
finishMessages()1622 void ApiWrap::finishMessages() {
1623 	Expects(_chatProcess != nullptr);
1624 	Expects(!_chatProcess->slice.has_value());
1625 
1626 	const auto process = base::take(_chatProcess);
1627 	process->done();
1628 }
1629 
processFileLoad(Data::File & file,const Data::FileOrigin & origin,Fn<bool (FileProgress)> progress,FnMut<void (QString)> done,Data::Message * message)1630 bool ApiWrap::processFileLoad(
1631 		Data::File &file,
1632 		const Data::FileOrigin &origin,
1633 		Fn<bool(FileProgress)> progress,
1634 		FnMut<void(QString)> done,
1635 		Data::Message *message) {
1636 	using SkipReason = Data::File::SkipReason;
1637 
1638 	if (!file.relativePath.isEmpty()
1639 		|| file.skipReason != SkipReason::None) {
1640 		return true;
1641 	} else if (!file.location && file.content.isEmpty()) {
1642 		file.skipReason = SkipReason::Unavailable;
1643 		return true;
1644 	} else if (writePreloadedFile(file, origin)) {
1645 		return !file.relativePath.isEmpty();
1646 	}
1647 
1648 	using Type = MediaSettings::Type;
1649 	const auto type = message ? v::match(message->media.content, [&](
1650 			const Data::Document &data) {
1651 		if (data.isSticker) {
1652 			return Type::Sticker;
1653 		} else if (data.isVideoMessage) {
1654 			return Type::VideoMessage;
1655 		} else if (data.isVoiceMessage) {
1656 			return Type::VoiceMessage;
1657 		} else if (data.isAnimated) {
1658 			return Type::GIF;
1659 		} else if (data.isVideoFile) {
1660 			return Type::Video;
1661 		} else {
1662 			return Type::File;
1663 		}
1664 	}, [](const auto &data) {
1665 		return Type::Photo;
1666 	}) : Type(0);
1667 
1668 	const auto limit = _settings->media.sizeLimit;
1669 	if (message && Data::SkipMessageByDate(*message, *_settings)) {
1670 		file.skipReason = SkipReason::DateLimits;
1671 		return true;
1672 	} else if ((_settings->media.types & type) != type) {
1673 		file.skipReason = SkipReason::FileType;
1674 		return true;
1675 	} else if ((message ? message->file().size : file.size) >= limit) {
1676 		// Don't load thumbs for large files that we skip.
1677 		file.skipReason = SkipReason::FileSize;
1678 		return true;
1679 	}
1680 	loadFile(file, origin, std::move(progress), std::move(done));
1681 	return false;
1682 }
1683 
writePreloadedFile(Data::File & file,const Data::FileOrigin & origin)1684 bool ApiWrap::writePreloadedFile(
1685 		Data::File &file,
1686 		const Data::FileOrigin &origin) {
1687 	Expects(_settings != nullptr);
1688 
1689 	using namespace Output;
1690 
1691 	if (const auto path = _fileCache->find(file.location)) {
1692 		file.relativePath = *path;
1693 		return true;
1694 	} else if (!file.content.isEmpty()) {
1695 		const auto process = prepareFileProcess(file, origin);
1696 		if (const auto result = process->file.writeBlock(file.content)) {
1697 			file.relativePath = process->relativePath;
1698 			_fileCache->save(file.location, file.relativePath);
1699 		} else {
1700 			ioError(result);
1701 		}
1702 		return true;
1703 	}
1704 	return false;
1705 }
1706 
loadFile(const Data::File & file,const Data::FileOrigin & origin,Fn<bool (FileProgress)> progress,FnMut<void (QString)> done)1707 void ApiWrap::loadFile(
1708 		const Data::File &file,
1709 		const Data::FileOrigin &origin,
1710 		Fn<bool(FileProgress)> progress,
1711 		FnMut<void(QString)> done) {
1712 	Expects(_fileProcess == nullptr);
1713 	Expects(file.location.dcId != 0
1714 		|| file.location.data.type() == mtpc_inputTakeoutFileLocation);
1715 
1716 	_fileProcess = prepareFileProcess(file, origin);
1717 	_fileProcess->progress = std::move(progress);
1718 	_fileProcess->done = std::move(done);
1719 
1720 	if (_fileProcess->progress) {
1721 		const auto progress = FileProgress{
1722 			_fileProcess->file.size(),
1723 			_fileProcess->size
1724 		};
1725 		if (!_fileProcess->progress(progress)) {
1726 			return;
1727 		}
1728 	}
1729 
1730 	loadFilePart();
1731 
1732 	Ensures(_fileProcess->requestId != 0);
1733 }
1734 
prepareFileProcess(const Data::File & file,const Data::FileOrigin & origin) const1735 auto ApiWrap::prepareFileProcess(
1736 	const Data::File &file,
1737 	const Data::FileOrigin &origin) const
1738 -> std::unique_ptr<FileProcess> {
1739 	Expects(_settings != nullptr);
1740 
1741 	const auto relativePath = Output::File::PrepareRelativePath(
1742 		_settings->path,
1743 		file.suggestedPath);
1744 	auto result = std::make_unique<FileProcess>(
1745 		_settings->path + relativePath,
1746 		_stats);
1747 	result->relativePath = relativePath;
1748 	result->location = file.location;
1749 	result->size = file.size;
1750 	result->origin = origin;
1751 	result->randomId = base::RandomValue<uint64>();
1752 	return result;
1753 }
1754 
loadFilePart()1755 void ApiWrap::loadFilePart() {
1756 	if (!_fileProcess
1757 		|| _fileProcess->requestId
1758 		|| _fileProcess->requests.size() >= kFileRequestsCount
1759 		|| (_fileProcess->size > 0
1760 			&& _fileProcess->offset >= _fileProcess->size)) {
1761 		return;
1762 	}
1763 
1764 	const auto offset = _fileProcess->offset;
1765 	_fileProcess->requests.push_back({ offset });
1766 	_fileProcess->requestId = fileRequest(
1767 		_fileProcess->location,
1768 		_fileProcess->offset
1769 	).done([=](const MTPupload_File &result) {
1770 		_fileProcess->requestId = 0;
1771 		filePartDone(offset, result);
1772 	}).send();
1773 	_fileProcess->offset += kFileChunkSize;
1774 
1775 	if (_fileProcess->size > 0
1776 		&& _fileProcess->requests.size() < kFileRequestsCount) {
1777 		// Only one request at a time supported right now.
1778 		//const auto runner = _runner;
1779 		//crl::on_main([=] {
1780 		//	QTimer::singleShot(kFileNextRequestDelay, [=] {
1781 		//		runner([=] {
1782 		//			loadFilePart();
1783 		//		});
1784 		//	});
1785 		//});
1786 	}
1787 }
1788 
filePartDone(int offset,const MTPupload_File & result)1789 void ApiWrap::filePartDone(int offset, const MTPupload_File &result) {
1790 	Expects(_fileProcess != nullptr);
1791 	Expects(!_fileProcess->requests.empty());
1792 
1793 	if (result.type() == mtpc_upload_fileCdnRedirect) {
1794 		error("Cdn redirect is not supported.");
1795 		return;
1796 	}
1797 	const auto &data = result.c_upload_file();
1798 	if (data.vbytes().v.isEmpty()) {
1799 		if (_fileProcess->size > 0) {
1800 			error("Empty bytes received in file part.");
1801 			return;
1802 		}
1803 		const auto result = _fileProcess->file.writeBlock({});
1804 		if (!result) {
1805 			ioError(result);
1806 			return;
1807 		}
1808 	} else {
1809 		using Request = FileProcess::Request;
1810 		auto &requests = _fileProcess->requests;
1811 		const auto i = ranges::find(
1812 			requests,
1813 			offset,
1814 			[](const Request &request) { return request.offset; });
1815 		Assert(i != end(requests));
1816 
1817 		i->bytes = data.vbytes().v;
1818 
1819 		auto &file = _fileProcess->file;
1820 		while (!requests.empty() && !requests.front().bytes.isEmpty()) {
1821 			const auto &bytes = requests.front().bytes;
1822 			if (const auto result = file.writeBlock(bytes); !result) {
1823 				ioError(result);
1824 				return;
1825 			}
1826 			requests.pop_front();
1827 		}
1828 
1829 		if (_fileProcess->progress) {
1830 			_fileProcess->progress(FileProgress{
1831 				file.size(),
1832 				_fileProcess->size });
1833 		}
1834 
1835 		if (!requests.empty()
1836 			|| !_fileProcess->size
1837 			|| _fileProcess->size > _fileProcess->offset) {
1838 			loadFilePart();
1839 			return;
1840 		}
1841 	}
1842 
1843 	auto process = base::take(_fileProcess);
1844 	const auto relativePath = process->relativePath;
1845 	_fileCache->save(process->location, relativePath);
1846 	process->done(process->relativePath);
1847 }
1848 
filePartRefreshReference(int offset)1849 void ApiWrap::filePartRefreshReference(int offset) {
1850 	Expects(_fileProcess != nullptr);
1851 	Expects(_fileProcess->requestId == 0);
1852 
1853 	const auto &origin = _fileProcess->origin;
1854 	if (!origin.messageId) {
1855 		error("FILE_REFERENCE error for non-message file.");
1856 		return;
1857 	}
1858 	if (origin.peer.type() == mtpc_inputPeerChannel
1859 		|| origin.peer.type() == mtpc_inputPeerChannelFromMessage) {
1860 		const auto channel = (origin.peer.type() == mtpc_inputPeerChannel)
1861 			? MTP_inputChannel(
1862 				origin.peer.c_inputPeerChannel().vchannel_id(),
1863 				origin.peer.c_inputPeerChannel().vaccess_hash())
1864 			: MTP_inputChannelFromMessage(
1865 				origin.peer.c_inputPeerChannelFromMessage().vpeer(),
1866 				origin.peer.c_inputPeerChannelFromMessage().vmsg_id(),
1867 				origin.peer.c_inputPeerChannelFromMessage().vchannel_id());
1868 		_fileProcess->requestId = mainRequest(MTPchannels_GetMessages(
1869 			channel,
1870 			MTP_vector<MTPInputMessage>(
1871 				1,
1872 				MTP_inputMessageID(MTP_int(origin.messageId)))
1873 		)).fail([=](const MTP::Error &error) {
1874 			_fileProcess->requestId = 0;
1875 			filePartUnavailable();
1876 			return true;
1877 		}).done([=](const MTPmessages_Messages &result) {
1878 			_fileProcess->requestId = 0;
1879 			filePartExtractReference(offset, result);
1880 		}).send();
1881 	} else {
1882 		_fileProcess->requestId = splitRequest(
1883 			origin.split,
1884 			MTPmessages_GetMessages(
1885 				MTP_vector<MTPInputMessage>(
1886 					1,
1887 					MTP_inputMessageID(MTP_int(origin.messageId)))
1888 			)
1889 		).fail([=](const MTP::Error &error) {
1890 			_fileProcess->requestId = 0;
1891 			filePartUnavailable();
1892 			return true;
1893 		}).done([=](const MTPmessages_Messages &result) {
1894 			_fileProcess->requestId = 0;
1895 			filePartExtractReference(offset, result);
1896 		}).send();
1897 	}
1898 }
1899 
filePartExtractReference(int offset,const MTPmessages_Messages & result)1900 void ApiWrap::filePartExtractReference(
1901 		int offset,
1902 		const MTPmessages_Messages &result) {
1903 	Expects(_fileProcess != nullptr);
1904 	Expects(_fileProcess->requestId == 0);
1905 
1906 	result.match([&](const MTPDmessages_messagesNotModified &data) {
1907 		error("Unexpected messagesNotModified received.");
1908 	}, [&](const auto &data) {
1909 		Expects(_selfId.has_value());
1910 
1911 		auto context = Data::ParseMediaContext();
1912 		context.selfPeerId = peerFromUser(*_selfId);
1913 		const auto messages = Data::ParseMessagesSlice(
1914 			context,
1915 			data.vmessages(),
1916 			data.vusers(),
1917 			data.vchats(),
1918 			_chatProcess->info.relativePath);
1919 		for (const auto &message : messages.list) {
1920 			if (message.id == _fileProcess->origin.messageId) {
1921 				const auto refresh1 = Data::RefreshFileReference(
1922 					_fileProcess->location,
1923 					message.file().location);
1924 				const auto refresh2 = Data::RefreshFileReference(
1925 					_fileProcess->location,
1926 					message.thumb().file.location);
1927 				if (refresh1 || refresh2) {
1928 					_fileProcess->requestId = fileRequest(
1929 						_fileProcess->location,
1930 						offset
1931 					).done([=](const MTPupload_File &result) {
1932 						_fileProcess->requestId = 0;
1933 						filePartDone(offset, result);
1934 					}).send();
1935 					return;
1936 				}
1937 			}
1938 		}
1939 		filePartUnavailable();
1940 	});
1941 }
1942 
filePartUnavailable()1943 void ApiWrap::filePartUnavailable() {
1944 	Expects(_fileProcess != nullptr);
1945 	Expects(!_fileProcess->requests.empty());
1946 
1947 	LOG(("Export Error: File unavailable."));
1948 
1949 	base::take(_fileProcess)->done(QString());
1950 }
1951 
error(const MTP::Error & error)1952 void ApiWrap::error(const MTP::Error &error) {
1953 	_errors.fire_copy(error);
1954 }
1955 
error(const QString & text)1956 void ApiWrap::error(const QString &text) {
1957 	error(MTP::Error(
1958 		MTP_rpc_error(MTP_int(0), MTP_string("API_ERROR: " + text))));
1959 }
1960 
ioError(const Output::Result & result)1961 void ApiWrap::ioError(const Output::Result &result) {
1962 	_ioErrors.fire_copy(result);
1963 }
1964 
1965 ApiWrap::~ApiWrap() = default;
1966 
1967 } // namespace Export
1968