update_checker.cpp 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "core/update_checker.h"
  8. #include "platform/platform_specific.h"
  9. #include "base/platform/base_platform_info.h"
  10. #include "base/platform/base_platform_file_utilities.h"
  11. #include "base/timer.h"
  12. #include "base/bytes.h"
  13. #include "base/unixtime.h"
  14. #include "storage/localstorage.h"
  15. #include "core/application.h"
  16. #include "core/changelogs.h"
  17. #include "core/click_handler_types.h"
  18. #include "mainwindow.h"
  19. #include "main/main_account.h"
  20. #include "main/main_session.h"
  21. #include "main/main_domain.h"
  22. #include "info/info_memento.h"
  23. #include "info/info_controller.h"
  24. #include "window/window_controller.h"
  25. #include "window/window_session_controller.h"
  26. #include "settings/settings_advanced.h"
  27. #include "settings/settings_intro.h"
  28. #include "ui/layers/box_content.h"
  29. #include <QtCore/QJsonDocument>
  30. #include <QtCore/QJsonObject>
  31. #include <ksandbox.h>
  32. extern "C" {
  33. #include <openssl/rsa.h>
  34. #include <openssl/pem.h>
  35. #include <openssl/bio.h>
  36. #include <openssl/err.h>
  37. } // extern "C"
  38. #ifndef TDESKTOP_DISABLE_AUTOUPDATE
  39. #if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
  40. #include <LzmaLib.h>
  41. #else // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
  42. #include <lzma.h>
  43. #endif // else of Q_OS_WIN && !TDESKTOP_USE_PACKAGED
  44. #endif // !TDESKTOP_DISABLE_AUTOUPDATE
  45. #ifndef Q_OS_WIN
  46. #include <unistd.h>
  47. #endif // !Q_OS_WIN
  48. namespace Core {
  49. namespace {
  50. constexpr auto kUpdaterTimeout = 10 * crl::time(1000);
  51. constexpr auto kMaxResponseSize = 1024 * 1024;
  52. #ifdef TDESKTOP_DISABLE_AUTOUPDATE
  53. bool UpdaterIsDisabled = true;
  54. #else // TDESKTOP_DISABLE_AUTOUPDATE
  55. bool UpdaterIsDisabled = false;
  56. #endif // TDESKTOP_DISABLE_AUTOUPDATE
  57. std::weak_ptr<Updater> UpdaterInstance;
  58. using Progress = UpdateChecker::Progress;
  59. using State = UpdateChecker::State;
  60. #ifdef Q_OS_WIN
  61. using VersionInt = DWORD;
  62. using VersionChar = WCHAR;
  63. #else // Q_OS_WIN
  64. using VersionInt = int;
  65. using VersionChar = wchar_t;
  66. #endif // Q_OS_WIN
  67. using Loader = MTP::AbstractDedicatedLoader;
  68. struct BIODeleter {
  69. void operator()(BIO *value) {
  70. BIO_free(value);
  71. }
  72. };
  73. inline auto MakeBIO(const void *buf, int len) {
  74. return std::unique_ptr<BIO, BIODeleter>{
  75. BIO_new_mem_buf(buf, len),
  76. };
  77. }
  78. class Checker : public base::has_weak_ptr {
  79. public:
  80. Checker(bool testing);
  81. virtual void start() = 0;
  82. rpl::producer<std::shared_ptr<Loader>> ready() const;
  83. rpl::producer<> failed() const;
  84. rpl::lifetime &lifetime();
  85. virtual ~Checker() = default;
  86. protected:
  87. bool testing() const;
  88. void done(std::shared_ptr<Loader> result);
  89. void fail();
  90. private:
  91. bool _testing = false;
  92. rpl::event_stream<std::shared_ptr<Loader>> _ready;
  93. rpl::event_stream<> _failed;
  94. rpl::lifetime _lifetime;
  95. };
  96. struct Implementation {
  97. std::unique_ptr<Checker> checker;
  98. std::shared_ptr<Loader> loader;
  99. bool failed = false;
  100. };
  101. class HttpChecker : public Checker {
  102. public:
  103. HttpChecker(bool testing);
  104. void start() override;
  105. ~HttpChecker();
  106. private:
  107. void gotResponse();
  108. void gotFailure(QNetworkReply::NetworkError e);
  109. void clearSentRequest();
  110. bool handleResponse(const QByteArray &response);
  111. std::optional<QString> parseOldResponse(
  112. const QByteArray &response) const;
  113. std::optional<QString> parseResponse(const QByteArray &response) const;
  114. QString validateLatestUrl(
  115. uint64 availableVersion,
  116. bool isAvailableAlpha,
  117. QString url) const;
  118. std::unique_ptr<QNetworkAccessManager> _manager;
  119. QNetworkReply *_reply = nullptr;
  120. };
  121. class HttpLoaderActor;
  122. class HttpLoader : public Loader {
  123. public:
  124. HttpLoader(const QString &url);
  125. ~HttpLoader();
  126. private:
  127. void startLoading() override;
  128. friend class HttpLoaderActor;
  129. QString _url;
  130. std::unique_ptr<QThread> _thread;
  131. HttpLoaderActor *_actor = nullptr;
  132. };
  133. class HttpLoaderActor : public QObject {
  134. public:
  135. HttpLoaderActor(
  136. not_null<HttpLoader*> parent,
  137. not_null<QThread*> thread,
  138. const QString &url);
  139. private:
  140. void start();
  141. void sendRequest();
  142. void gotMetaData();
  143. void partFinished(qint64 got, qint64 total);
  144. void partFailed(QNetworkReply::NetworkError e);
  145. not_null<HttpLoader*> _parent;
  146. QString _url;
  147. QNetworkAccessManager _manager;
  148. std::unique_ptr<QNetworkReply> _reply;
  149. };
  150. class MtpChecker : public Checker {
  151. public:
  152. MtpChecker(base::weak_ptr<Main::Session> session, bool testing);
  153. void start() override;
  154. private:
  155. using FileLocation = MTP::DedicatedLoader::Location;
  156. using Checker::fail;
  157. Fn<void(const MTP::Error &error)> failHandler();
  158. void gotMessage(const MTPmessages_Messages &result);
  159. std::optional<FileLocation> parseMessage(
  160. const MTPmessages_Messages &result) const;
  161. std::optional<FileLocation> parseText(const QByteArray &text) const;
  162. FileLocation validateLatestLocation(
  163. uint64 availableVersion,
  164. const FileLocation &location) const;
  165. MTP::WeakInstance _mtp;
  166. };
  167. std::shared_ptr<Updater> GetUpdaterInstance() {
  168. if (const auto result = UpdaterInstance.lock()) {
  169. return result;
  170. }
  171. const auto result = std::make_shared<Updater>();
  172. UpdaterInstance = result;
  173. return result;
  174. }
  175. QString UpdatesFolder() {
  176. return cWorkingDir() + u"tupdates"_q;
  177. }
  178. void ClearAll() {
  179. base::Platform::DeleteDirectory(UpdatesFolder());
  180. }
  181. QString FindUpdateFile() {
  182. QDir updates(UpdatesFolder());
  183. if (!updates.exists()) {
  184. return QString();
  185. }
  186. const auto list = updates.entryInfoList(QDir::Files);
  187. for (const auto &info : list) {
  188. static const auto RegExp = QRegularExpression(
  189. "^("
  190. "tupdate|"
  191. "tx64upd|"
  192. "tarm64upd|"
  193. "tmacupd|"
  194. "tarmacupd|"
  195. "tlinuxupd|"
  196. ")\\d+(_[a-z\\d]+)?$",
  197. QRegularExpression::CaseInsensitiveOption
  198. );
  199. if (RegExp.match(info.fileName()).hasMatch()) {
  200. return info.absoluteFilePath();
  201. }
  202. }
  203. return QString();
  204. }
  205. QString ExtractFilename(const QString &url) {
  206. const auto expression = QRegularExpression(u"/([^/\\?]+)(\\?|$)"_q);
  207. if (const auto match = expression.match(url); match.hasMatch()) {
  208. return match.captured(1).replace(
  209. QRegularExpression(u"[^a-zA-Z0-9_\\-]"_q),
  210. QString());
  211. }
  212. return QString();
  213. }
  214. bool UnpackUpdate(const QString &filepath) {
  215. #ifndef TDESKTOP_DISABLE_AUTOUPDATE
  216. QFile input(filepath);
  217. if (!input.open(QIODevice::ReadOnly)) {
  218. LOG(("Update Error: cant read updates file!"));
  219. return false;
  220. }
  221. #if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
  222. const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
  223. #else // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
  224. const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
  225. #endif // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
  226. QByteArray compressed = input.readAll();
  227. int32 compressedLen = compressed.size() - hSize;
  228. if (compressedLen <= 0) {
  229. LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
  230. return false;
  231. }
  232. input.close();
  233. QString tempDirPath = cWorkingDir() + u"tupdates/temp"_q, readyFilePath = cWorkingDir() + u"tupdates/temp/ready"_q;
  234. base::Platform::DeleteDirectory(tempDirPath);
  235. QDir tempDir(tempDirPath);
  236. if (tempDir.exists() || QFile(readyFilePath).exists()) {
  237. LOG(("Update Error: cant clear tupdates/temp dir!"));
  238. return false;
  239. }
  240. uchar sha1Buffer[20];
  241. bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
  242. if (!goodSha1) {
  243. LOG(("Update Error: bad SHA1 hash of update file!"));
  244. return false;
  245. }
  246. RSA *pbKey = [] {
  247. const auto bio = MakeBIO(
  248. const_cast<char*>(
  249. AppBetaVersion
  250. ? UpdatesPublicBetaKey
  251. : UpdatesPublicKey),
  252. -1);
  253. return PEM_read_bio_RSAPublicKey(bio.get(), 0, 0, 0);
  254. }();
  255. if (!pbKey) {
  256. LOG(("Update Error: cant read public rsa key!"));
  257. return false;
  258. }
  259. if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
  260. RSA_free(pbKey);
  261. // try other public key, if we update from beta to stable or vice versa
  262. pbKey = [] {
  263. const auto bio = MakeBIO(
  264. const_cast<char*>(
  265. AppBetaVersion
  266. ? UpdatesPublicKey
  267. : UpdatesPublicBetaKey),
  268. -1);
  269. return PEM_read_bio_RSAPublicKey(bio.get(), 0, 0, 0);
  270. }();
  271. if (!pbKey) {
  272. LOG(("Update Error: cant read public rsa key!"));
  273. return false;
  274. }
  275. if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
  276. RSA_free(pbKey);
  277. LOG(("Update Error: bad RSA signature of update file!"));
  278. return false;
  279. }
  280. }
  281. RSA_free(pbKey);
  282. QByteArray uncompressed;
  283. int32 uncompressedLen;
  284. memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
  285. uncompressed.resize(uncompressedLen);
  286. size_t resultLen = uncompressed.size();
  287. #if defined Q_OS_WIN && !defined TDESKTOP_USE_PACKAGED // use Lzma SDK for win
  288. SizeT srcLen = compressedLen;
  289. int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
  290. if (uncompressRes != SZ_OK) {
  291. LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
  292. return false;
  293. }
  294. #else // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
  295. lzma_stream stream = LZMA_STREAM_INIT;
  296. lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
  297. if (ret != LZMA_OK) {
  298. const char *msg;
  299. switch (ret) {
  300. case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
  301. case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
  302. case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
  303. default: msg = "Unknown error, possibly a bug"; break;
  304. }
  305. LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
  306. return false;
  307. }
  308. stream.avail_in = compressedLen;
  309. stream.next_in = (uint8_t*)(compressed.constData() + hSize);
  310. stream.avail_out = resultLen;
  311. stream.next_out = (uint8_t*)uncompressed.data();
  312. lzma_ret res = lzma_code(&stream, LZMA_FINISH);
  313. if (stream.avail_in) {
  314. LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
  315. return false;
  316. } else if (stream.avail_out) {
  317. LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
  318. return false;
  319. }
  320. lzma_end(&stream);
  321. if (res != LZMA_OK && res != LZMA_STREAM_END) {
  322. const char *msg;
  323. switch (res) {
  324. case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
  325. case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
  326. case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
  327. case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
  328. case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
  329. default: msg = "Unknown error, possibly a bug"; break;
  330. }
  331. LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
  332. return false;
  333. }
  334. #endif // Q_OS_WIN && !TDESKTOP_USE_PACKAGED
  335. tempDir.mkdir(tempDir.absolutePath());
  336. quint32 version;
  337. {
  338. QDataStream stream(uncompressed);
  339. stream.setVersion(QDataStream::Qt_5_1);
  340. stream >> version;
  341. if (stream.status() != QDataStream::Ok) {
  342. LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
  343. return false;
  344. }
  345. quint64 alphaVersion = 0;
  346. if (version == 0x7FFFFFFF) { // alpha version
  347. stream >> alphaVersion;
  348. if (stream.status() != QDataStream::Ok) {
  349. LOG(("Update Error: cant read alpha version from downloaded stream, status: %1").arg(stream.status()));
  350. return false;
  351. }
  352. if (!cAlphaVersion() || alphaVersion <= cAlphaVersion()) {
  353. LOG(("Update Error: downloaded alpha version %1 is not greater, than mine %2").arg(alphaVersion).arg(cAlphaVersion()));
  354. return false;
  355. }
  356. } else if (int32(version) <= AppVersion) {
  357. LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
  358. return false;
  359. }
  360. quint32 filesCount;
  361. stream >> filesCount;
  362. if (stream.status() != QDataStream::Ok) {
  363. LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
  364. return false;
  365. }
  366. if (!filesCount) {
  367. LOG(("Update Error: update is empty!"));
  368. return false;
  369. }
  370. for (uint32 i = 0; i < filesCount; ++i) {
  371. QString relativeName;
  372. quint32 fileSize;
  373. QByteArray fileInnerData;
  374. bool executable = false;
  375. stream >> relativeName >> fileSize >> fileInnerData;
  376. #ifndef Q_OS_WIN
  377. stream >> executable;
  378. #endif // !Q_OS_WIN
  379. if (stream.status() != QDataStream::Ok) {
  380. LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
  381. return false;
  382. }
  383. if (fileSize != quint32(fileInnerData.size())) {
  384. LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
  385. return false;
  386. }
  387. QFile f(tempDirPath + '/' + relativeName);
  388. if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
  389. LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
  390. return false;
  391. }
  392. if (!f.open(QIODevice::WriteOnly)) {
  393. LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
  394. return false;
  395. }
  396. auto writtenBytes = f.write(fileInnerData);
  397. if (writtenBytes != fileSize) {
  398. f.close();
  399. LOG(("Update Error: cant write file '%1', desiredSize: %2, write result: %3").arg(tempDirPath + '/' + relativeName).arg(fileSize).arg(writtenBytes));
  400. return false;
  401. }
  402. f.close();
  403. if (executable) {
  404. QFileDevice::Permissions p = f.permissions();
  405. p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
  406. f.setPermissions(p);
  407. }
  408. }
  409. // create tdata/version file
  410. tempDir.mkdir(QDir(tempDirPath + u"/tdata"_q).absolutePath());
  411. std::wstring versionString = FormatVersionDisplay(version).toStdWString();
  412. const auto versionNum = VersionInt(version);
  413. const auto versionLen = VersionInt(versionString.size() * sizeof(VersionChar));
  414. VersionChar versionStr[32];
  415. memcpy(versionStr, versionString.c_str(), versionLen);
  416. QFile fVersion(tempDirPath + u"/tdata/version"_q);
  417. if (!fVersion.open(QIODevice::WriteOnly)) {
  418. LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + u"/version"_q));
  419. return false;
  420. }
  421. fVersion.write((const char*)&versionNum, sizeof(VersionInt));
  422. if (versionNum == 0x7FFFFFFF) { // alpha version
  423. fVersion.write((const char*)&alphaVersion, sizeof(quint64));
  424. } else {
  425. fVersion.write((const char*)&versionLen, sizeof(VersionInt));
  426. fVersion.write((const char*)&versionStr[0], versionLen);
  427. }
  428. fVersion.close();
  429. }
  430. QFile readyFile(readyFilePath);
  431. if (readyFile.open(QIODevice::WriteOnly)) {
  432. if (readyFile.write("1", 1)) {
  433. readyFile.close();
  434. } else {
  435. LOG(("Update Error: cant write ready file '%1'").arg(readyFilePath));
  436. return false;
  437. }
  438. } else {
  439. LOG(("Update Error: cant create ready file '%1'").arg(readyFilePath));
  440. return false;
  441. }
  442. input.remove();
  443. return true;
  444. #else // !TDESKTOP_DISABLE_AUTOUPDATE
  445. return false;
  446. #endif // TDESKTOP_DISABLE_AUTOUPDATE
  447. }
  448. template <typename Callback>
  449. bool ParseCommonMap(
  450. const QByteArray &json,
  451. bool testing,
  452. Callback &&callback) {
  453. auto error = QJsonParseError{ 0, QJsonParseError::NoError };
  454. const auto document = QJsonDocument::fromJson(json, &error);
  455. if (error.error != QJsonParseError::NoError) {
  456. LOG(("Update Error: MTP failed to parse JSON, error: %1"
  457. ).arg(error.errorString()));
  458. return false;
  459. } else if (!document.isObject()) {
  460. LOG(("Update Error: MTP not an object received in JSON."));
  461. return false;
  462. }
  463. const auto platforms = document.object();
  464. const auto platform = Platform::AutoUpdateKey();
  465. const auto it = platforms.constFind(platform);
  466. if (it == platforms.constEnd()) {
  467. LOG(("Update Error: MTP platform '%1' not found in response."
  468. ).arg(platform));
  469. return false;
  470. } else if (!(*it).isObject()) {
  471. LOG(("Update Error: MTP not an object found for platform '%1'."
  472. ).arg(platform));
  473. return false;
  474. }
  475. const auto types = (*it).toObject();
  476. const auto list = [&]() -> std::vector<QString> {
  477. if (cAlphaVersion()) {
  478. return { "alpha", "beta", "stable" };
  479. } else if (cInstallBetaVersion()) {
  480. return { "beta", "stable" };
  481. }
  482. return { "stable" };
  483. }();
  484. auto bestIsAvailableAlpha = false;
  485. auto bestAvailableVersion = 0ULL;
  486. for (const auto &type : list) {
  487. const auto it = types.constFind(type);
  488. if (it == types.constEnd()) {
  489. continue;
  490. } else if (!(*it).isObject()) {
  491. LOG(("Update Error: Not an object found for '%1:%2'."
  492. ).arg(platform).arg(type));
  493. return false;
  494. }
  495. const auto map = (*it).toObject();
  496. const auto key = testing ? "testing" : "released";
  497. const auto version = map.constFind(key);
  498. if (version == map.constEnd()) {
  499. continue;
  500. }
  501. const auto isAvailableAlpha = (type == "alpha");
  502. const auto availableVersion = [&] {
  503. if ((*version).isString()) {
  504. const auto string = (*version).toString();
  505. if (const auto index = string.indexOf(':'); index > 0) {
  506. return base::StringViewMid(string, 0, index).toULongLong();
  507. }
  508. return string.toULongLong();
  509. } else if ((*version).isDouble()) {
  510. return uint64(base::SafeRound((*version).toDouble()));
  511. }
  512. return 0ULL;
  513. }();
  514. if (!availableVersion) {
  515. LOG(("Update Error: Version is not valid for '%1:%2:%3'."
  516. ).arg(platform).arg(type).arg(key));
  517. return false;
  518. }
  519. const auto compare = isAvailableAlpha
  520. ? availableVersion
  521. : availableVersion * 1000;
  522. const auto bestCompare = bestIsAvailableAlpha
  523. ? bestAvailableVersion
  524. : bestAvailableVersion * 1000;
  525. if (compare > bestCompare) {
  526. bestAvailableVersion = availableVersion;
  527. bestIsAvailableAlpha = isAvailableAlpha;
  528. if (!callback(availableVersion, isAvailableAlpha, map)) {
  529. return false;
  530. }
  531. }
  532. }
  533. if (!bestAvailableVersion) {
  534. LOG(("Update Error: No valid entry found for platform '%1'."
  535. ).arg(platform));
  536. return false;
  537. }
  538. return true;
  539. }
  540. Checker::Checker(bool testing) : _testing(testing) {
  541. }
  542. rpl::producer<std::shared_ptr<Loader>> Checker::ready() const {
  543. return _ready.events();
  544. }
  545. rpl::producer<> Checker::failed() const {
  546. return _failed.events();
  547. }
  548. bool Checker::testing() const {
  549. return _testing;
  550. }
  551. void Checker::done(std::shared_ptr<Loader> result) {
  552. _ready.fire(std::move(result));
  553. }
  554. void Checker::fail() {
  555. _failed.fire({});
  556. }
  557. rpl::lifetime &Checker::lifetime() {
  558. return _lifetime;
  559. }
  560. HttpChecker::HttpChecker(bool testing) : Checker(testing) {
  561. }
  562. void HttpChecker::start() {
  563. const auto updaterVersion = Platform::AutoUpdateVersion();
  564. const auto path = Local::readAutoupdatePrefix()
  565. + qstr("/current")
  566. + (updaterVersion > 1 ? QString::number(updaterVersion) : QString());
  567. auto url = QUrl(path);
  568. DEBUG_LOG(("Update Info: requesting update state"));
  569. const auto request = QNetworkRequest(url);
  570. _manager = std::make_unique<QNetworkAccessManager>();
  571. _reply = _manager->get(request);
  572. _reply->connect(_reply, &QNetworkReply::finished, [=] {
  573. gotResponse();
  574. });
  575. _reply->connect(_reply, &QNetworkReply::errorOccurred, [=](auto e) {
  576. gotFailure(e);
  577. });
  578. }
  579. void HttpChecker::gotResponse() {
  580. if (!_reply) {
  581. return;
  582. }
  583. cSetLastUpdateCheck(base::unixtime::now());
  584. const auto response = _reply->readAll();
  585. clearSentRequest();
  586. if (response.size() >= kMaxResponseSize || !handleResponse(response)) {
  587. LOG(("Update Error: Bad update map size: %1").arg(response.size()));
  588. gotFailure(QNetworkReply::UnknownContentError);
  589. }
  590. }
  591. bool HttpChecker::handleResponse(const QByteArray &response) {
  592. const auto handle = [&](const QString &url) {
  593. done(url.isEmpty() ? nullptr : std::make_shared<HttpLoader>(url));
  594. return true;
  595. };
  596. if (const auto url = parseOldResponse(response)) {
  597. return handle(*url);
  598. } else if (const auto url = parseResponse(response)) {
  599. return handle(*url);
  600. }
  601. return false;
  602. }
  603. void HttpChecker::clearSentRequest() {
  604. const auto reply = base::take(_reply);
  605. if (!reply) {
  606. return;
  607. }
  608. reply->disconnect(reply, &QNetworkReply::finished, nullptr, nullptr);
  609. reply->disconnect(reply, &QNetworkReply::errorOccurred, nullptr, nullptr);
  610. reply->abort();
  611. reply->deleteLater();
  612. _manager = nullptr;
  613. }
  614. void HttpChecker::gotFailure(QNetworkReply::NetworkError e) {
  615. LOG(("Update Error: "
  616. "could not get current version %1").arg(e));
  617. if (const auto reply = base::take(_reply)) {
  618. reply->deleteLater();
  619. }
  620. fail();
  621. }
  622. std::optional<QString> HttpChecker::parseOldResponse(
  623. const QByteArray &response) const {
  624. const auto string = QString::fromLatin1(response);
  625. const auto old = QRegularExpression(
  626. u"^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$"_q
  627. ).match(string);
  628. if (!old.hasMatch()) {
  629. return std::nullopt;
  630. }
  631. const auto availableVersion = old.captured(1).toULongLong();
  632. const auto url = old.captured(2);
  633. const auto isAvailableAlpha = url.startsWith(qstr("beta_"));
  634. return validateLatestUrl(
  635. availableVersion,
  636. isAvailableAlpha,
  637. isAvailableAlpha ? url.mid(5) + "_{signature}" : url);
  638. }
  639. std::optional<QString> HttpChecker::parseResponse(
  640. const QByteArray &response) const {
  641. auto bestAvailableVersion = 0ULL;
  642. auto bestIsAvailableAlpha = false;
  643. auto bestLink = QString();
  644. const auto accumulate = [&](
  645. uint64 version,
  646. bool isAlpha,
  647. const QJsonObject &map) {
  648. bestAvailableVersion = version;
  649. bestIsAvailableAlpha = isAlpha;
  650. const auto link = map.constFind("link");
  651. if (link == map.constEnd()) {
  652. LOG(("Update Error: Link not found for version %1."
  653. ).arg(version));
  654. return false;
  655. } else if (!(*link).isString()) {
  656. LOG(("Update Error: Link is not a string for version %1."
  657. ).arg(version));
  658. return false;
  659. }
  660. bestLink = (*link).toString();
  661. return true;
  662. };
  663. const auto result = ParseCommonMap(response, testing(), accumulate);
  664. if (!result) {
  665. return std::nullopt;
  666. }
  667. return validateLatestUrl(
  668. bestAvailableVersion,
  669. bestIsAvailableAlpha,
  670. Local::readAutoupdatePrefix() + bestLink);
  671. }
  672. QString HttpChecker::validateLatestUrl(
  673. uint64 availableVersion,
  674. bool isAvailableAlpha,
  675. QString url) const {
  676. const auto myVersion = isAvailableAlpha
  677. ? cAlphaVersion()
  678. : uint64(AppVersion);
  679. const auto validVersion = (cAlphaVersion() || !isAvailableAlpha);
  680. if (!validVersion || availableVersion <= myVersion) {
  681. return QString();
  682. }
  683. const auto versionUrl = url.replace(
  684. "{version}",
  685. QString::number(availableVersion));
  686. const auto finalUrl = isAvailableAlpha
  687. ? QString(versionUrl).replace(
  688. "{signature}",
  689. countAlphaVersionSignature(availableVersion))
  690. : versionUrl;
  691. return finalUrl;
  692. }
  693. HttpChecker::~HttpChecker() {
  694. clearSentRequest();
  695. }
  696. HttpLoader::HttpLoader(const QString &url)
  697. : Loader(UpdatesFolder() + '/' + ExtractFilename(url), kChunkSize)
  698. , _url(url) {
  699. }
  700. void HttpLoader::startLoading() {
  701. LOG(("Update Info: Loading using HTTP from '%1'.").arg(_url));
  702. _thread = std::make_unique<QThread>();
  703. _actor = new HttpLoaderActor(this, _thread.get(), _url);
  704. _thread->start();
  705. }
  706. HttpLoader::~HttpLoader() {
  707. if (const auto thread = base::take(_thread)) {
  708. if (const auto actor = base::take(_actor)) {
  709. QObject::connect(
  710. thread.get(),
  711. &QThread::finished,
  712. actor,
  713. &QObject::deleteLater);
  714. }
  715. thread->quit();
  716. thread->wait();
  717. }
  718. }
  719. HttpLoaderActor::HttpLoaderActor(
  720. not_null<HttpLoader*> parent,
  721. not_null<QThread*> thread,
  722. const QString &url)
  723. : _parent(parent) {
  724. _url = url;
  725. moveToThread(thread);
  726. _manager.moveToThread(thread);
  727. connect(thread, &QThread::started, this, [=] { start(); });
  728. }
  729. void HttpLoaderActor::start() {
  730. sendRequest();
  731. }
  732. void HttpLoaderActor::sendRequest() {
  733. auto request = QNetworkRequest(_url);
  734. const auto rangeHeaderValue = "bytes="
  735. + QByteArray::number(_parent->alreadySize())
  736. + "-";
  737. request.setRawHeader("Range", rangeHeaderValue);
  738. request.setAttribute(
  739. QNetworkRequest::HttpPipeliningAllowedAttribute,
  740. true);
  741. _reply.reset(_manager.get(request));
  742. connect(
  743. _reply.get(),
  744. &QNetworkReply::downloadProgress,
  745. this,
  746. &HttpLoaderActor::partFinished);
  747. connect(
  748. _reply.get(),
  749. &QNetworkReply::errorOccurred,
  750. this,
  751. &HttpLoaderActor::partFailed);
  752. connect(
  753. _reply.get(),
  754. &QNetworkReply::metaDataChanged,
  755. this,
  756. &HttpLoaderActor::gotMetaData);
  757. }
  758. void HttpLoaderActor::gotMetaData() {
  759. const auto pairs = _reply->rawHeaderPairs();
  760. for (const auto &pair : pairs) {
  761. if (QString::fromUtf8(pair.first).toLower() == "content-range") {
  762. const auto m = QRegularExpression(u"/(\\d+)([^\\d]|$)"_q).match(QString::fromUtf8(pair.second));
  763. if (m.hasMatch()) {
  764. _parent->writeChunk({}, m.captured(1).toInt());
  765. }
  766. }
  767. }
  768. }
  769. void HttpLoaderActor::partFinished(qint64 got, qint64 total) {
  770. if (!_reply) return;
  771. const auto statusCode = _reply->attribute(
  772. QNetworkRequest::HttpStatusCodeAttribute);
  773. if (statusCode.isValid()) {
  774. const auto status = statusCode.toInt();
  775. if (status != 200 && status != 206 && status != 416) {
  776. LOG(("Update Error: "
  777. "Bad HTTP status received in partFinished(): %1"
  778. ).arg(status));
  779. _parent->threadSafeFailed();
  780. return;
  781. }
  782. }
  783. DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
  784. const auto data = _reply->readAll();
  785. _parent->writeChunk(bytes::make_span(data), total);
  786. }
  787. void HttpLoaderActor::partFailed(QNetworkReply::NetworkError e) {
  788. if (!_reply) return;
  789. const auto statusCode = _reply->attribute(
  790. QNetworkRequest::HttpStatusCodeAttribute);
  791. _reply.release()->deleteLater();
  792. if (statusCode.isValid()) {
  793. const auto status = statusCode.toInt();
  794. if (status == 416) { // Requested range not satisfiable
  795. _parent->writeChunk({}, _parent->alreadySize());
  796. return;
  797. }
  798. }
  799. LOG(("Update Error: failed to download part after %1, error %2"
  800. ).arg(_parent->alreadySize()
  801. ).arg(e));
  802. _parent->threadSafeFailed();
  803. }
  804. MtpChecker::MtpChecker(
  805. base::weak_ptr<Main::Session> session,
  806. bool testing)
  807. : Checker(testing)
  808. , _mtp(session) {
  809. }
  810. void MtpChecker::start() {
  811. if (!_mtp.valid()) {
  812. LOG(("Update Info: MTP is unavailable."));
  813. crl::on_main(this, [=] { fail(); });
  814. return;
  815. }
  816. const auto updaterVersion = Platform::AutoUpdateVersion();
  817. const auto feed = "tdhbcfeed"
  818. + (updaterVersion > 1 ? QString::number(updaterVersion) : QString());
  819. MTP::ResolveChannel(&_mtp, feed, [=](
  820. const MTPInputChannel &channel) {
  821. _mtp.send(
  822. MTPmessages_GetHistory(
  823. MTP_inputPeerChannel(
  824. channel.c_inputChannel().vchannel_id(),
  825. channel.c_inputChannel().vaccess_hash()),
  826. MTP_int(0), // offset_id
  827. MTP_int(0), // offset_date
  828. MTP_int(0), // add_offset
  829. MTP_int(1), // limit
  830. MTP_int(0), // max_id
  831. MTP_int(0), // min_id
  832. MTP_long(0)), // hash
  833. [=](const MTPmessages_Messages &result) { gotMessage(result); },
  834. failHandler());
  835. }, [=] { fail(); });
  836. }
  837. void MtpChecker::gotMessage(const MTPmessages_Messages &result) {
  838. const auto location = parseMessage(result);
  839. if (!location) {
  840. fail();
  841. return;
  842. } else if (location->username.isEmpty()) {
  843. done(nullptr);
  844. return;
  845. }
  846. const auto ready = [=](std::unique_ptr<MTP::DedicatedLoader> loader) {
  847. if (loader) {
  848. done(std::move(loader));
  849. } else {
  850. fail();
  851. }
  852. };
  853. MTP::StartDedicatedLoader(&_mtp, *location, UpdatesFolder(), ready);
  854. }
  855. auto MtpChecker::parseMessage(const MTPmessages_Messages &result) const
  856. -> std::optional<FileLocation> {
  857. const auto message = MTP::GetMessagesElement(result);
  858. if (!message || message->type() != mtpc_message) {
  859. LOG(("Update Error: MTP feed message not found."));
  860. return std::nullopt;
  861. }
  862. return parseText(message->c_message().vmessage().v);
  863. }
  864. auto MtpChecker::parseText(const QByteArray &text) const
  865. -> std::optional<FileLocation> {
  866. auto bestAvailableVersion = 0ULL;
  867. auto bestLocation = FileLocation();
  868. const auto accumulate = [&](
  869. uint64 version,
  870. bool isAlpha,
  871. const QJsonObject &map) {
  872. if (isAlpha) {
  873. LOG(("Update Error: MTP closed alpha found."));
  874. return false;
  875. }
  876. bestAvailableVersion = version;
  877. const auto key = testing() ? "testing" : "released";
  878. const auto entry = map.constFind(key);
  879. if (entry == map.constEnd()) {
  880. LOG(("Update Error: MTP entry not found for version %1."
  881. ).arg(version));
  882. return false;
  883. } else if (!(*entry).isString()) {
  884. LOG(("Update Error: MTP entry is not a string for version %1."
  885. ).arg(version));
  886. return false;
  887. }
  888. const auto full = (*entry).toString();
  889. const auto start = full.indexOf(':');
  890. const auto post = full.indexOf('#');
  891. if (start <= 0 || post < start) {
  892. LOG(("Update Error: MTP entry '%1' is bad for version %2."
  893. ).arg(full
  894. ).arg(version));
  895. return false;
  896. }
  897. bestLocation.username = full.mid(start + 1, post - start - 1);
  898. bestLocation.postId = base::StringViewMid(full, post + 1).toInt();
  899. if (bestLocation.username.isEmpty() || !bestLocation.postId) {
  900. LOG(("Update Error: MTP entry '%1' is bad for version %2."
  901. ).arg(full
  902. ).arg(version));
  903. return false;
  904. }
  905. return true;
  906. };
  907. const auto result = ParseCommonMap(text, testing(), accumulate);
  908. if (!result) {
  909. return std::nullopt;
  910. }
  911. return validateLatestLocation(bestAvailableVersion, bestLocation);
  912. }
  913. auto MtpChecker::validateLatestLocation(
  914. uint64 availableVersion,
  915. const FileLocation &location) const -> FileLocation {
  916. const auto myVersion = uint64(AppVersion);
  917. return (availableVersion <= myVersion) ? FileLocation() : location;
  918. }
  919. Fn<void(const MTP::Error &error)> MtpChecker::failHandler() {
  920. return [=](const MTP::Error &error) {
  921. LOG(("Update Error: MTP check failed with '%1'"
  922. ).arg(QString::number(error.code()) + ':' + error.type()));
  923. fail();
  924. };
  925. }
  926. } // namespace
  927. bool UpdaterDisabled() {
  928. return UpdaterIsDisabled;
  929. }
  930. void SetUpdaterDisabledAtStartup() {
  931. Expects(UpdaterInstance.lock() == nullptr);
  932. UpdaterIsDisabled = true;
  933. }
  934. class Updater : public base::has_weak_ptr {
  935. public:
  936. Updater();
  937. rpl::producer<> checking() const;
  938. rpl::producer<> isLatest() const;
  939. rpl::producer<Progress> progress() const;
  940. rpl::producer<> failed() const;
  941. rpl::producer<> ready() const;
  942. void start(bool forceWait);
  943. void stop();
  944. void test();
  945. State state() const;
  946. int already() const;
  947. int size() const;
  948. void setMtproto(base::weak_ptr<Main::Session> session);
  949. ~Updater();
  950. private:
  951. enum class Action {
  952. Waiting,
  953. Checking,
  954. Loading,
  955. Unpacking,
  956. Ready,
  957. };
  958. void check();
  959. void startImplementation(
  960. not_null<Implementation*> which,
  961. std::unique_ptr<Checker> checker);
  962. bool tryLoaders();
  963. void handleTimeout();
  964. void checkerDone(
  965. not_null<Implementation*> which,
  966. std::shared_ptr<Loader> loader);
  967. void checkerFail(not_null<Implementation*> which);
  968. void finalize(QString filepath);
  969. void unpackDone(bool ready);
  970. void handleChecking();
  971. void handleProgress();
  972. void handleLatest();
  973. void handleFailed();
  974. void handleReady();
  975. void scheduleNext();
  976. bool _testing = false;
  977. Action _action = Action::Waiting;
  978. base::Timer _timer;
  979. base::Timer _retryTimer;
  980. rpl::event_stream<> _checking;
  981. rpl::event_stream<> _isLatest;
  982. rpl::event_stream<Progress> _progress;
  983. rpl::event_stream<> _failed;
  984. rpl::event_stream<> _ready;
  985. Implementation _httpImplementation;
  986. Implementation _mtpImplementation;
  987. std::shared_ptr<Loader> _activeLoader;
  988. bool _usingMtprotoLoader = (cAlphaVersion() != 0);
  989. base::weak_ptr<Main::Session> _session;
  990. rpl::lifetime _lifetime;
  991. };
  992. Updater::Updater()
  993. : _timer([=] { check(); })
  994. , _retryTimer([=] { handleTimeout(); }) {
  995. checking() | rpl::start_with_next([=] {
  996. handleChecking();
  997. }, _lifetime);
  998. progress() | rpl::start_with_next([=] {
  999. handleProgress();
  1000. }, _lifetime);
  1001. failed() | rpl::start_with_next([=] {
  1002. handleFailed();
  1003. }, _lifetime);
  1004. ready() | rpl::start_with_next([=] {
  1005. handleReady();
  1006. }, _lifetime);
  1007. isLatest() | rpl::start_with_next([=] {
  1008. handleLatest();
  1009. }, _lifetime);
  1010. }
  1011. rpl::producer<> Updater::checking() const {
  1012. return _checking.events();
  1013. }
  1014. rpl::producer<> Updater::isLatest() const {
  1015. return _isLatest.events();
  1016. }
  1017. auto Updater::progress() const
  1018. -> rpl::producer<Progress> {
  1019. return _progress.events();
  1020. }
  1021. rpl::producer<> Updater::failed() const {
  1022. return _failed.events();
  1023. }
  1024. rpl::producer<> Updater::ready() const {
  1025. return _ready.events();
  1026. }
  1027. void Updater::check() {
  1028. start(false);
  1029. }
  1030. void Updater::handleReady() {
  1031. stop();
  1032. _action = Action::Ready;
  1033. if (!Quitting()) {
  1034. cSetLastUpdateCheck(base::unixtime::now());
  1035. Local::writeSettings();
  1036. }
  1037. }
  1038. void Updater::handleFailed() {
  1039. scheduleNext();
  1040. }
  1041. void Updater::handleLatest() {
  1042. if (const auto update = FindUpdateFile(); !update.isEmpty()) {
  1043. QFile(update).remove();
  1044. }
  1045. scheduleNext();
  1046. }
  1047. void Updater::handleChecking() {
  1048. _action = Action::Checking;
  1049. _retryTimer.callOnce(kUpdaterTimeout);
  1050. }
  1051. void Updater::handleProgress() {
  1052. _retryTimer.callOnce(kUpdaterTimeout);
  1053. }
  1054. void Updater::scheduleNext() {
  1055. stop();
  1056. if (!Quitting()) {
  1057. cSetLastUpdateCheck(base::unixtime::now());
  1058. Local::writeSettings();
  1059. start(true);
  1060. }
  1061. }
  1062. auto Updater::state() const -> State {
  1063. if (_action == Action::Ready) {
  1064. return State::Ready;
  1065. } else if (_action == Action::Loading) {
  1066. return State::Download;
  1067. }
  1068. return State::None;
  1069. }
  1070. int Updater::size() const {
  1071. return _activeLoader ? _activeLoader->totalSize() : 0;
  1072. }
  1073. int Updater::already() const {
  1074. return _activeLoader ? _activeLoader->alreadySize() : 0;
  1075. }
  1076. void Updater::stop() {
  1077. _httpImplementation = Implementation();
  1078. _mtpImplementation = Implementation();
  1079. _activeLoader = nullptr;
  1080. _action = Action::Waiting;
  1081. }
  1082. void Updater::start(bool forceWait) {
  1083. if (cExeName().isEmpty()) {
  1084. return;
  1085. }
  1086. _timer.cancel();
  1087. if (!cAutoUpdate() || _action != Action::Waiting) {
  1088. return;
  1089. }
  1090. _retryTimer.cancel();
  1091. const auto constDelay = cAlphaVersion() ? 600 : UpdateDelayConstPart;
  1092. const auto randDelay = cAlphaVersion() ? 300 : UpdateDelayRandPart;
  1093. const auto updateInSecs = cLastUpdateCheck()
  1094. + constDelay
  1095. + int(rand() % randDelay)
  1096. - base::unixtime::now();
  1097. auto sendRequest = (updateInSecs <= 0)
  1098. || (updateInSecs > constDelay + randDelay);
  1099. if (!sendRequest && !forceWait) {
  1100. if (!FindUpdateFile().isEmpty()) {
  1101. sendRequest = true;
  1102. }
  1103. }
  1104. if (cManyInstance() && !Logs::DebugEnabled()) {
  1105. // Only main instance is updating.
  1106. return;
  1107. }
  1108. if (sendRequest) {
  1109. startImplementation(
  1110. &_httpImplementation,
  1111. std::make_unique<HttpChecker>(_testing));
  1112. startImplementation(
  1113. &_mtpImplementation,
  1114. std::make_unique<MtpChecker>(_session, _testing));
  1115. _checking.fire({});
  1116. } else {
  1117. _timer.callOnce((updateInSecs + 5) * crl::time(1000));
  1118. }
  1119. }
  1120. void Updater::startImplementation(
  1121. not_null<Implementation*> which,
  1122. std::unique_ptr<Checker> checker) {
  1123. if (!checker) {
  1124. class EmptyChecker : public Checker {
  1125. public:
  1126. EmptyChecker() : Checker(false) {
  1127. }
  1128. void start() override {
  1129. crl::on_main(this, [=] { fail(); });
  1130. }
  1131. };
  1132. checker = std::make_unique<EmptyChecker>();
  1133. }
  1134. checker->ready(
  1135. ) | rpl::start_with_next([=](std::shared_ptr<Loader> &&loader) {
  1136. checkerDone(which, std::move(loader));
  1137. }, checker->lifetime());
  1138. checker->failed(
  1139. ) | rpl::start_with_next([=] {
  1140. checkerFail(which);
  1141. }, checker->lifetime());
  1142. *which = Implementation{ std::move(checker) };
  1143. crl::on_main(which->checker.get(), [=] {
  1144. which->checker->start();
  1145. });
  1146. }
  1147. void Updater::checkerDone(
  1148. not_null<Implementation*> which,
  1149. std::shared_ptr<Loader> loader) {
  1150. which->checker = nullptr;
  1151. which->loader = std::move(loader);
  1152. tryLoaders();
  1153. }
  1154. void Updater::checkerFail(not_null<Implementation*> which) {
  1155. which->checker = nullptr;
  1156. which->failed = true;
  1157. tryLoaders();
  1158. }
  1159. void Updater::test() {
  1160. _testing = true;
  1161. cSetLastUpdateCheck(0);
  1162. start(false);
  1163. }
  1164. void Updater::setMtproto(base::weak_ptr<Main::Session> session) {
  1165. _session = session;
  1166. }
  1167. void Updater::handleTimeout() {
  1168. if (_action == Action::Checking) {
  1169. const auto reset = [&](Implementation &which) {
  1170. if (base::take(which.checker)) {
  1171. which.failed = true;
  1172. }
  1173. };
  1174. reset(_httpImplementation);
  1175. reset(_mtpImplementation);
  1176. if (!tryLoaders()) {
  1177. cSetLastUpdateCheck(0);
  1178. _timer.callOnce(kUpdaterTimeout);
  1179. }
  1180. } else if (_action == Action::Loading) {
  1181. _failed.fire({});
  1182. }
  1183. }
  1184. bool Updater::tryLoaders() {
  1185. if (_httpImplementation.checker || _mtpImplementation.checker) {
  1186. // Some checkers didn't finish yet.
  1187. return true;
  1188. }
  1189. _retryTimer.cancel();
  1190. const auto tryOne = [&](Implementation &which) {
  1191. _activeLoader = std::move(which.loader);
  1192. if (const auto loader = _activeLoader.get()) {
  1193. _action = Action::Loading;
  1194. loader->progress(
  1195. ) | rpl::start_to_stream(_progress, loader->lifetime());
  1196. loader->ready(
  1197. ) | rpl::start_with_next([=](QString &&filepath) {
  1198. finalize(std::move(filepath));
  1199. }, loader->lifetime());
  1200. loader->failed(
  1201. ) | rpl::start_with_next([=] {
  1202. _failed.fire({});
  1203. }, loader->lifetime());
  1204. _retryTimer.callOnce(kUpdaterTimeout);
  1205. loader->wipeFolder();
  1206. loader->start();
  1207. } else {
  1208. _isLatest.fire({});
  1209. }
  1210. };
  1211. if (_mtpImplementation.failed && _httpImplementation.failed) {
  1212. _failed.fire({});
  1213. return false;
  1214. } else if (!_mtpImplementation.loader) {
  1215. tryOne(_httpImplementation);
  1216. } else if (!_httpImplementation.loader) {
  1217. tryOne(_mtpImplementation);
  1218. } else {
  1219. tryOne(_usingMtprotoLoader
  1220. ? _mtpImplementation
  1221. : _httpImplementation);
  1222. _usingMtprotoLoader = !_usingMtprotoLoader;
  1223. }
  1224. return true;
  1225. }
  1226. void Updater::finalize(QString filepath) {
  1227. if (_action != Action::Loading) {
  1228. return;
  1229. }
  1230. _retryTimer.cancel();
  1231. _activeLoader = nullptr;
  1232. _action = Action::Unpacking;
  1233. crl::async([=] {
  1234. const auto ready = UnpackUpdate(filepath);
  1235. crl::on_main([=] {
  1236. GetUpdaterInstance()->unpackDone(ready);
  1237. });
  1238. });
  1239. }
  1240. void Updater::unpackDone(bool ready) {
  1241. if (ready) {
  1242. _ready.fire({});
  1243. } else {
  1244. ClearAll();
  1245. _failed.fire({});
  1246. }
  1247. }
  1248. Updater::~Updater() {
  1249. stop();
  1250. }
  1251. UpdateChecker::UpdateChecker()
  1252. : _updater(GetUpdaterInstance()) {
  1253. if (IsAppLaunched() && Core::App().domain().started()) {
  1254. if (const auto session = Core::App().activeAccount().maybeSession()) {
  1255. _updater->setMtproto(session);
  1256. }
  1257. }
  1258. }
  1259. rpl::producer<> UpdateChecker::checking() const {
  1260. return _updater->checking();
  1261. }
  1262. rpl::producer<> UpdateChecker::isLatest() const {
  1263. return _updater->isLatest();
  1264. }
  1265. auto UpdateChecker::progress() const
  1266. -> rpl::producer<Progress> {
  1267. return _updater->progress();
  1268. }
  1269. rpl::producer<> UpdateChecker::failed() const {
  1270. return _updater->failed();
  1271. }
  1272. rpl::producer<> UpdateChecker::ready() const {
  1273. return _updater->ready();
  1274. }
  1275. void UpdateChecker::start(bool forceWait) {
  1276. _updater->start(forceWait);
  1277. }
  1278. void UpdateChecker::test() {
  1279. _updater->test();
  1280. }
  1281. void UpdateChecker::setMtproto(base::weak_ptr<Main::Session> session) {
  1282. _updater->setMtproto(session);
  1283. }
  1284. void UpdateChecker::stop() {
  1285. _updater->stop();
  1286. }
  1287. auto UpdateChecker::state() const
  1288. -> State {
  1289. return _updater->state();
  1290. }
  1291. int UpdateChecker::already() const {
  1292. return _updater->already();
  1293. }
  1294. int UpdateChecker::size() const {
  1295. return _updater->size();
  1296. }
  1297. //QString winapiErrorWrap() {
  1298. // WCHAR errMsg[2048];
  1299. // DWORD errorCode = GetLastError();
  1300. // LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
  1301. // FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, 0, 0);
  1302. // if (!errorText) {
  1303. // errorText = errorTextDefault;
  1304. // }
  1305. // StringCbPrintf(errMsg, sizeof(errMsg), L"Error code: %d, error message: %s", errorCode, errorText);
  1306. // if (errorText != errorTextDefault) {
  1307. // LocalFree(errorText);
  1308. // }
  1309. // return QString::fromWCharArray(errMsg);
  1310. //}
  1311. bool checkReadyUpdate() {
  1312. QString readyFilePath = cWorkingDir() + u"tupdates/temp/ready"_q, readyPath = cWorkingDir() + u"tupdates/temp"_q;
  1313. if (!QFile(readyFilePath).exists() || cExeName().isEmpty()) {
  1314. if (QDir(cWorkingDir() + u"tupdates/ready"_q).exists() || QDir(cWorkingDir() + u"tupdates/temp"_q).exists()) {
  1315. ClearAll();
  1316. }
  1317. return false;
  1318. }
  1319. // check ready version
  1320. QString versionPath = readyPath + u"/tdata/version"_q;
  1321. {
  1322. QFile fVersion(versionPath);
  1323. if (!fVersion.open(QIODevice::ReadOnly)) {
  1324. LOG(("Update Error: cant read version file '%1'").arg(versionPath));
  1325. ClearAll();
  1326. return false;
  1327. }
  1328. auto versionNum = VersionInt();
  1329. if (fVersion.read((char*)&versionNum, sizeof(VersionInt)) != sizeof(VersionInt)) {
  1330. LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
  1331. ClearAll();
  1332. return false;
  1333. }
  1334. if (versionNum == 0x7FFFFFFF) { // alpha version
  1335. quint64 alphaVersion = 0;
  1336. if (fVersion.read((char*)&alphaVersion, sizeof(quint64)) != sizeof(quint64)) {
  1337. LOG(("Update Error: cant read alpha version from file '%1'").arg(versionPath));
  1338. ClearAll();
  1339. return false;
  1340. }
  1341. if (!cAlphaVersion() || alphaVersion <= cAlphaVersion()) {
  1342. LOG(("Update Error: cant install alpha version %1 having alpha version %2").arg(alphaVersion).arg(cAlphaVersion()));
  1343. ClearAll();
  1344. return false;
  1345. }
  1346. } else if (versionNum <= AppVersion) {
  1347. LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
  1348. ClearAll();
  1349. return false;
  1350. }
  1351. fVersion.close();
  1352. }
  1353. #ifdef Q_OS_WIN
  1354. QString curUpdater = (cExeDir() + u"Updater.exe"_q);
  1355. QFileInfo updater(cWorkingDir() + u"tupdates/temp/Updater.exe"_q);
  1356. #elif defined Q_OS_MAC // Q_OS_WIN
  1357. QString curUpdater = (cExeDir() + cExeName() + u"/Contents/Frameworks/Updater"_q);
  1358. QFileInfo updater(cWorkingDir() + u"tupdates/temp/Telegram.app/Contents/Frameworks/Updater"_q);
  1359. #else // Q_OS_MAC
  1360. QString curUpdater = (cExeDir() + u"Updater"_q);
  1361. QFileInfo updater(cWorkingDir() + u"tupdates/temp/Updater"_q);
  1362. #endif // else for Q_OS_WIN || Q_OS_MAC
  1363. if (!updater.exists()) {
  1364. QFileInfo current(curUpdater);
  1365. if (!current.exists()) {
  1366. ClearAll();
  1367. return false;
  1368. }
  1369. if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
  1370. ClearAll();
  1371. return false;
  1372. }
  1373. }
  1374. #ifdef Q_OS_WIN
  1375. if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
  1376. DWORD errorCode = GetLastError();
  1377. if (errorCode == ERROR_ACCESS_DENIED) { // we are in write-protected dir, like Program Files
  1378. cSetWriteProtected(true);
  1379. return true;
  1380. } else {
  1381. ClearAll();
  1382. return false;
  1383. }
  1384. }
  1385. if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
  1386. ClearAll();
  1387. return false;
  1388. }
  1389. #elif defined Q_OS_MAC // Q_OS_WIN
  1390. QDir().mkpath(QFileInfo(curUpdater).absolutePath());
  1391. DEBUG_LOG(("Update Info: moving %1 to %2...").arg(updater.absoluteFilePath()).arg(curUpdater));
  1392. if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
  1393. ClearAll();
  1394. return false;
  1395. }
  1396. #else // Q_OS_MAC
  1397. // if the files in the directory are owned by user, while the directory is not,
  1398. // update will still fail since it's not possible to remove files
  1399. if (QFile::exists(curUpdater)
  1400. && unlink(QFile::encodeName(curUpdater).constData())) {
  1401. if (errno == EACCES) {
  1402. DEBUG_LOG(("Update Info: "
  1403. "could not unlink current Updater, access denied."));
  1404. cSetWriteProtected(true);
  1405. return true;
  1406. } else {
  1407. DEBUG_LOG(("Update Error: could not unlink current Updater."));
  1408. ClearAll();
  1409. return false;
  1410. }
  1411. }
  1412. if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
  1413. if (errno == EACCES) {
  1414. DEBUG_LOG(("Update Info: "
  1415. "could not copy new Updater, access denied."));
  1416. cSetWriteProtected(true);
  1417. return true;
  1418. } else {
  1419. DEBUG_LOG(("Update Error: could not copy new Updater."));
  1420. ClearAll();
  1421. return false;
  1422. }
  1423. }
  1424. #endif // else for Q_OS_WIN || Q_OS_MAC
  1425. #ifdef Q_OS_MAC
  1426. base::Platform::RemoveQuarantine(QFileInfo(curUpdater).absolutePath());
  1427. base::Platform::RemoveQuarantine(updater.absolutePath());
  1428. #endif // Q_OS_MAC
  1429. return true;
  1430. }
  1431. void UpdateApplication() {
  1432. if (UpdaterDisabled()) {
  1433. const auto url = [&] {
  1434. #ifdef OS_WIN_STORE
  1435. return "https://www.microsoft.com/en-us/store/p/telegram-desktop/9nztwsqntd0s";
  1436. #elif defined OS_MAC_STORE // OS_WIN_STORE
  1437. return "https://itunes.apple.com/ae/app/telegram-desktop/id946399090";
  1438. #else // OS_WIN_STORE || OS_MAC_STORE
  1439. if (KSandbox::isFlatpak()) {
  1440. return "https://flathub.org/apps/details/org.telegram.desktop";
  1441. } else if (KSandbox::isSnap()) {
  1442. return "https://snapcraft.io/telegram-desktop";
  1443. }
  1444. return "https://desktop.telegram.org";
  1445. #endif // OS_WIN_STORE || OS_MAC_STORE
  1446. }();
  1447. UrlClickHandler::Open(url);
  1448. } else {
  1449. cSetAutoUpdate(true);
  1450. const auto window = Core::IsAppLaunched()
  1451. ? Core::App().activePrimaryWindow()
  1452. : nullptr;
  1453. if (window) {
  1454. if (const auto controller = window->sessionController()) {
  1455. controller->showSection(
  1456. std::make_shared<Info::Memento>(
  1457. Info::Settings::Tag{ controller->session().user() },
  1458. ::Settings::Advanced::Id()),
  1459. Window::SectionShow());
  1460. } else {
  1461. window->widget()->showSpecialLayer(
  1462. Box<::Settings::LayerWidget>(window),
  1463. anim::type::normal);
  1464. }
  1465. window->widget()->showFromTray();
  1466. }
  1467. cSetLastUpdateCheck(0);
  1468. Core::UpdateChecker().start();
  1469. }
  1470. }
  1471. QString countAlphaVersionSignature(uint64 version) { // duplicated in packer.cpp
  1472. if (cAlphaPrivateKey().isEmpty()) {
  1473. LOG(("Error: Trying to count alpha version signature without alpha private key!"));
  1474. return QString();
  1475. }
  1476. QByteArray signedData = (qstr("TelegramBeta_") + QString::number(version, 16).toLower()).toUtf8();
  1477. static const int32 shaSize = 20, keySize = 128;
  1478. uchar sha1Buffer[shaSize];
  1479. hashSha1(signedData.constData(), signedData.size(), sha1Buffer); // count sha1
  1480. uint32 siglen = 0;
  1481. RSA *prKey = [] {
  1482. const auto bio = MakeBIO(
  1483. const_cast<char*>(cAlphaPrivateKey().constData()),
  1484. -1);
  1485. return PEM_read_bio_RSAPrivateKey(bio.get(), 0, 0, 0);
  1486. }();
  1487. if (!prKey) {
  1488. LOG(("Error: Could not read alpha private key!"));
  1489. return QString();
  1490. }
  1491. if (RSA_size(prKey) != keySize) {
  1492. LOG(("Error: Bad alpha private key size: %1").arg(RSA_size(prKey)));
  1493. RSA_free(prKey);
  1494. return QString();
  1495. }
  1496. QByteArray signature;
  1497. signature.resize(keySize);
  1498. if (RSA_sign(NID_sha1, (const uchar*)(sha1Buffer), shaSize, (uchar*)(signature.data()), &siglen, prKey) != 1) { // count signature
  1499. LOG(("Error: Counting alpha version signature failed!"));
  1500. RSA_free(prKey);
  1501. return QString();
  1502. }
  1503. RSA_free(prKey);
  1504. if (siglen != keySize) {
  1505. LOG(("Error: Bad alpha version signature length: %1").arg(siglen));
  1506. return QString();
  1507. }
  1508. signature = signature.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
  1509. signature = signature.replace('-', '8').replace('_', 'B');
  1510. return QString::fromUtf8(signature.mid(19, 32));
  1511. }
  1512. } // namespace Core