PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Telegram/SourceFiles/application.cpp

https://gitlab.com/mba811/tdesktop
C++ | 870 lines | 726 code | 125 blank | 19 comment | 223 complexity | 3898532ff9fcd765613d77795e435ac9 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop version of Telegram messaging app, see https://telegram.org
  4. Telegram Desktop is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. It is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
  13. Copyright (c) 2014 John Preston, https://desktop.telegram.org
  14. */
  15. #include "stdafx.h"
  16. #include "application.h"
  17. #include "style.h"
  18. #include "pspecific.h"
  19. #include "fileuploader.h"
  20. #include "mainwidget.h"
  21. #include "supporttl.h"
  22. #include "lang.h"
  23. #include "boxes/confirmbox.h"
  24. #include "langloaderplain.h"
  25. #include "localstorage.h"
  26. namespace {
  27. Application *mainApp = 0;
  28. FileUploader *uploader = 0;
  29. QString lng;
  30. void mtpStateChanged(int32 dc, int32 state) {
  31. if (App::wnd()) {
  32. App::wnd()->mtpStateChanged(dc, state);
  33. }
  34. }
  35. void mtpSessionReset(int32 dc) {
  36. if (App::main() && dc == MTP::maindc()) {
  37. App::main()->getDifference();
  38. }
  39. }
  40. class _DebugWaiter : public QObject {
  41. public:
  42. _DebugWaiter(QObject *parent) : QObject(parent), _debugState(0) {
  43. }
  44. bool eventFilter(QObject *o, QEvent *e) {
  45. if (e->type() == QEvent::KeyPress) {
  46. QKeyEvent *ev = static_cast<QKeyEvent*>(e);
  47. switch (_debugState) {
  48. case 0: if (ev->key() == Qt::Key_F12) _debugState = 1; break;
  49. case 1: if (ev->key() == Qt::Key_F11) _debugState = 2; else if (ev->key() != Qt::Key_F12) _debugState = 0; break;
  50. case 2: if (ev->key() == Qt::Key_F10) _debugState = 3; else if (ev->key() != Qt::Key_F11) _debugState = 0; break;
  51. case 3: if (ev->key() == Qt::Key_F11) _debugState = 4; else if (ev->key() != Qt::Key_F10) _debugState = 0; break;
  52. case 4: if (ev->key() == Qt::Key_F12) offerDebug(); if (ev->key() != Qt::Key_F11) _debugState = 0; break;
  53. }
  54. if (cPlatform() == dbipMac && ev->key() == Qt::Key_W && (ev->modifiers() & (Qt::MetaModifier | Qt::ControlModifier))) {
  55. if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) {
  56. App::wnd()->minimizeToTray();
  57. return true;
  58. }
  59. }
  60. }
  61. return QObject::eventFilter(o, e);
  62. }
  63. void offerDebug() {
  64. ConfirmBox *box = new ConfirmBox(lang(lng_sure_enable_debug));
  65. connect(box, SIGNAL(confirmed()), App::app(), SLOT(onEnableDebugMode()));
  66. App::wnd()->showLayer(box);
  67. }
  68. private:
  69. int _debugState;
  70. };
  71. }
  72. Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
  73. serverName(psServerPrefix() + cGUIDStr()), closing(false),
  74. updateRequestId(0), updateReply(0), updateThread(0), updateDownloader(0), _translator(0) {
  75. DEBUG_LOG(("Application Info: creation.."));
  76. QByteArray d(QDir((cPlatform() == dbipWindows ? cExeDir() : cWorkingDir()).toLower()).absolutePath().toUtf8());
  77. char h[33] = { 0 };
  78. hashMd5Hex(d.constData(), d.size(), h);
  79. serverName = psServerPrefix() + h + '-' + cGUIDStr();
  80. if (mainApp) {
  81. DEBUG_LOG(("Application Error: another Application was created, terminating.."));
  82. exit(0);
  83. }
  84. mainApp = this;
  85. installEventFilter(new _DebugWaiter(this));
  86. #if defined Q_OS_LINUX || defined Q_OS_LINUX64
  87. QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/DejaVuSans.ttf"));
  88. QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/NanumMyeongjo-Regular.ttf"));
  89. #endif
  90. QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Regular.ttf"));
  91. QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Bold.ttf"));
  92. QFontDatabase::addApplicationFont(qsl(":/gui/art/fonts/OpenSans-Semibold.ttf"));
  93. float64 dpi = primaryScreen()->logicalDotsPerInch();
  94. if (dpi <= 108) { // 0-96-108
  95. cSetScreenScale(dbisOne);
  96. } else if (dpi <= 132) { // 108-120-132
  97. cSetScreenScale(dbisOneAndQuarter);
  98. } else if (dpi <= 168) { // 132-144-168
  99. cSetScreenScale(dbisOneAndHalf);
  100. } else { // 168-192-inf
  101. cSetScreenScale(dbisTwo);
  102. }
  103. if (devicePixelRatio() > 1) {
  104. cSetRetina(true);
  105. cSetRetinaFactor(devicePixelRatio());
  106. cSetIntRetinaFactor(int32(cRetinaFactor()));
  107. }
  108. if (cLang() < languageTest) {
  109. cSetLang(languageId());
  110. }
  111. if (cLang() == languageTest) {
  112. if (QFileInfo(cLangFile()).exists()) {
  113. LangLoaderPlain loader(cLangFile());
  114. cSetLangErrors(loader.errors());
  115. if (!cLangErrors().isEmpty()) {
  116. LOG(("Lang load errors: %1").arg(cLangErrors()));
  117. } else if (!loader.warnings().isEmpty()) {
  118. LOG(("Lang load warnings: %1").arg(loader.warnings()));
  119. }
  120. } else {
  121. cSetLang(languageDefault);
  122. }
  123. } else if (cLang() > languageDefault && cLang() < languageCount) {
  124. LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()] + qsl(".strings"));
  125. if (!loader.errors().isEmpty()) {
  126. LOG(("Lang load errors: %1").arg(loader.errors()));
  127. } else if (!loader.warnings().isEmpty()) {
  128. LOG(("Lang load warnings: %1").arg(loader.warnings()));
  129. }
  130. }
  131. installTranslator(_translator = new Translator());
  132. Local::start();
  133. style::startManager();
  134. anim::startManager();
  135. historyInit();
  136. DEBUG_LOG(("Application Info: inited.."));
  137. window = new Window();
  138. psInstallEventFilter();
  139. connect(&socket, SIGNAL(connected()), this, SLOT(socketConnected()));
  140. connect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
  141. connect(&socket, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(socketError(QLocalSocket::LocalSocketError)));
  142. connect(&socket, SIGNAL(bytesWritten(qint64)), this, SLOT(socketWritten(qint64)));
  143. connect(&socket, SIGNAL(readyRead()), this, SLOT(socketReading()));
  144. connect(&server, SIGNAL(newConnection()), this, SLOT(newInstanceConnected()));
  145. connect(this, SIGNAL(aboutToQuit()), this, SLOT(closeApplication()));
  146. connect(&updateCheckTimer, SIGNAL(timeout()), this, SLOT(startUpdateCheck()));
  147. connect(this, SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
  148. connect(this, SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
  149. connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(onAppStateChanged(Qt::ApplicationState)));
  150. connect(&writeUserConfigTimer, SIGNAL(timeout()), this, SLOT(onWriteUserConfig()));
  151. writeUserConfigTimer.setSingleShot(true);
  152. connect(&killDownloadSessionsTimer, SIGNAL(timeout()), this, SLOT(killDownloadSessions()));
  153. if (cManyInstance()) {
  154. startApp();
  155. } else {
  156. DEBUG_LOG(("Application Info: connecting local socket to %1..").arg(serverName));
  157. socket.connectToServer(serverName);
  158. }
  159. }
  160. void Application::onAppUpdate(const MTPhelp_AppUpdate &response) {
  161. updateRequestId = 0;
  162. cSetLastUpdateCheck(unixtime());
  163. App::writeConfig();
  164. if (response.type() == mtpc_help_noAppUpdate) {
  165. startUpdateCheck();
  166. } else {
  167. updateThread = new QThread();
  168. connect(updateThread, SIGNAL(finished()), updateThread, SLOT(deleteLater()));
  169. updateDownloader = new PsUpdateDownloader(updateThread, response.c_help_appUpdate());
  170. updateThread->start();
  171. }
  172. }
  173. bool Application::onAppUpdateFail() {
  174. updateRequestId = 0;
  175. cSetLastUpdateCheck(unixtime());
  176. App::writeConfig();
  177. startUpdateCheck();
  178. return true;
  179. }
  180. void Application::updateGotCurrent() {
  181. if (!updateReply || updateThread) return;
  182. cSetLastUpdateCheck(unixtime());
  183. QRegularExpressionMatch m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromUtf8(updateReply->readAll()));
  184. if (m.hasMatch()) {
  185. int32 currentVersion = m.captured(1).toInt();
  186. if (currentVersion > AppVersion) {
  187. updateThread = new QThread();
  188. connect(updateThread, SIGNAL(finished()), updateThread, SLOT(deleteLater()));
  189. updateDownloader = new PsUpdateDownloader(updateThread, m.captured(2));
  190. updateThread->start();
  191. }
  192. }
  193. if (updateReply) updateReply->deleteLater();
  194. updateReply = 0;
  195. if (!updateThread) {
  196. QDir updates(cWorkingDir() + "tupdates");
  197. if (updates.exists()) {
  198. QFileInfoList list = updates.entryInfoList(QDir::Files);
  199. for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
  200. if (QRegularExpression("^(tupdate|tmacupd|tlinuxupd|tlinux32upd)\\d+$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
  201. QFile(i->absoluteFilePath()).remove();
  202. }
  203. }
  204. }
  205. emit updateLatest();
  206. }
  207. startUpdateCheck(true);
  208. App::writeConfig();
  209. }
  210. void Application::updateFailedCurrent(QNetworkReply::NetworkError e) {
  211. LOG(("App Error: could not get current version (update check): %1").arg(e));
  212. if (updateReply) updateReply->deleteLater();
  213. updateReply = 0;
  214. emit updateFailed();
  215. startUpdateCheck(true);
  216. }
  217. void Application::onUpdateReady() {
  218. if (updateDownloader) {
  219. updateDownloader->deleteLater();
  220. updateDownloader = 0;
  221. }
  222. updateCheckTimer.stop();
  223. cSetLastUpdateCheck(unixtime());
  224. App::writeConfig();
  225. }
  226. void Application::onUpdateFailed() {
  227. if (updateDownloader) {
  228. updateDownloader->deleteLater();
  229. updateDownloader = 0;
  230. if (updateThread) updateThread->quit();
  231. updateThread = 0;
  232. }
  233. cSetLastUpdateCheck(unixtime());
  234. App::writeConfig();
  235. }
  236. void Application::regPhotoUpdate(const PeerId &peer, MsgId msgId) {
  237. photoUpdates.insert(msgId, peer);
  238. }
  239. void Application::clearPhotoUpdates() {
  240. photoUpdates.clear();
  241. }
  242. bool Application::isPhotoUpdating(const PeerId &peer) {
  243. for (QMap<MsgId, PeerId>::iterator i = photoUpdates.begin(), e = photoUpdates.end(); i != e; ++i) {
  244. if (i.value() == peer) {
  245. return true;
  246. }
  247. }
  248. return false;
  249. }
  250. void Application::cancelPhotoUpdate(const PeerId &peer) {
  251. for (QMap<MsgId, PeerId>::iterator i = photoUpdates.begin(), e = photoUpdates.end(); i != e;) {
  252. if (i.value() == peer) {
  253. i = photoUpdates.erase(i);
  254. } else {
  255. ++i;
  256. }
  257. }
  258. }
  259. void Application::selfPhotoCleared(const MTPUserProfilePhoto &result) {
  260. if (!App::self()) return;
  261. App::self()->setPhoto(result);
  262. emit peerPhotoDone(App::self()->id);
  263. }
  264. void Application::chatPhotoCleared(PeerId peer, const MTPmessages_StatedMessage &result) {
  265. if (App::main()) {
  266. App::main()->sentFullDataReceived(0, result);
  267. }
  268. cancelPhotoUpdate(peer);
  269. emit peerPhotoDone(peer);
  270. }
  271. void Application::selfPhotoDone(const MTPphotos_Photo &result) {
  272. if (!App::self()) return;
  273. const MTPDphotos_photo &photo(result.c_photos_photo());
  274. App::feedPhoto(photo.vphoto);
  275. App::feedUsers(photo.vusers);
  276. cancelPhotoUpdate(App::self()->id);
  277. emit peerPhotoDone(App::self()->id);
  278. }
  279. void Application::chatPhotoDone(PeerId peer, const MTPmessages_StatedMessage &result) {
  280. if (App::main()) {
  281. App::main()->sentFullDataReceived(0, result);
  282. }
  283. cancelPhotoUpdate(peer);
  284. emit peerPhotoDone(peer);
  285. }
  286. bool Application::peerPhotoFail(PeerId peer, const RPCError &e) {
  287. LOG(("Application Error: update photo failed %1: %2").arg(e.type()).arg(e.description()));
  288. cancelPhotoUpdate(peer);
  289. emit peerPhotoFail(peer);
  290. return true;
  291. }
  292. void Application::peerClearPhoto(PeerId peer) {
  293. if (App::self() && App::self()->id == peer) {
  294. MTP::send(MTPphotos_UpdateProfilePhoto(MTP_inputPhotoEmpty(), MTP_inputPhotoCropAuto()), rpcDone(&Application::selfPhotoCleared), rpcFail(&Application::peerPhotoFail, peer));
  295. } else {
  296. MTP::send(MTPmessages_EditChatPhoto(MTP_int(int32(peer & 0xFFFFFFFF)), MTP_inputChatPhotoEmpty()), rpcDone(&Application::chatPhotoCleared, peer), rpcFail(&Application::peerPhotoFail, peer));
  297. }
  298. }
  299. void Application::writeUserConfigIn(uint64 ms) {
  300. if (!writeUserConfigTimer.isActive()) {
  301. writeUserConfigTimer.start(ms);
  302. }
  303. }
  304. void Application::killDownloadSessionsStart(int32 dc) {
  305. if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) {
  306. killDownloadSessionTimes.insert(dc, getms() + MTPAckSendWaiting + MTPKillFileSessionTimeout);
  307. }
  308. if (!killDownloadSessionsTimer.isActive()) {
  309. killDownloadSessionsTimer.start(MTPAckSendWaiting + MTPKillFileSessionTimeout + 5);
  310. }
  311. }
  312. void Application::killDownloadSessionsStop(int32 dc) {
  313. killDownloadSessionTimes.remove(dc);
  314. if (killDownloadSessionTimes.isEmpty() && killDownloadSessionsTimer.isActive()) {
  315. killDownloadSessionsTimer.stop();
  316. }
  317. }
  318. void Application::checkLocalTime() {
  319. if (App::main()) App::main()->checkLastUpdate(checkms());
  320. }
  321. void Application::onWriteUserConfig() {
  322. App::writeUserConfig();
  323. }
  324. void Application::onAppStateChanged(Qt::ApplicationState state) {
  325. checkLocalTime();
  326. if (window) window->updateIsActive((state == Qt::ApplicationActive) ? cOnlineFocusTimeout() : cOfflineBlurTimeout());
  327. }
  328. void Application::killDownloadSessions() {
  329. uint64 ms = getms(), left = MTPAckSendWaiting + MTPKillFileSessionTimeout;
  330. for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
  331. if (i.value() <= ms) {
  332. for (int j = 0; j < MTPDownloadSessionsCount; ++j) {
  333. MTP::stopSession(MTP::dld[j] + i.key());
  334. }
  335. i = killDownloadSessionTimes.erase(i);
  336. } else {
  337. if (i.value() - ms < left) {
  338. left = i.value() - ms;
  339. }
  340. ++i;
  341. }
  342. }
  343. if (!killDownloadSessionTimes.isEmpty()) {
  344. killDownloadSessionsTimer.start(left);
  345. }
  346. }
  347. void Application::photoUpdated(MsgId msgId, const MTPInputFile &file) {
  348. if (!App::self()) return;
  349. QMap<MsgId, PeerId>::iterator i = photoUpdates.find(msgId);
  350. if (i != photoUpdates.end()) {
  351. PeerId peer = i.value();
  352. if (peer == App::self()->id) {
  353. MTP::send(MTPphotos_UploadProfilePhoto(file, MTP_string(""), MTP_inputGeoPointEmpty(), MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100))), rpcDone(&Application::selfPhotoDone), rpcFail(&Application::peerPhotoFail, peer));
  354. } else {
  355. History *hist = App::history(peer);
  356. hist->sendRequestId = MTP::send(MTPmessages_EditChatPhoto(MTP_int(peer & 0xFFFFFFFF), MTP_inputChatUploadedPhoto(file, MTP_inputPhotoCrop(MTP_double(0), MTP_double(0), MTP_double(100)))), rpcDone(&Application::chatPhotoDone, peer), rpcFail(&Application::peerPhotoFail, peer), 0, 0, hist->sendRequestId);
  357. }
  358. }
  359. }
  360. void Application::onEnableDebugMode() {
  361. if (!cDebug()) {
  362. logsInitDebug();
  363. cSetDebug(true);
  364. QFile f(cWorkingDir() + qsl("tdata/withdebug"));
  365. if (f.open(QIODevice::WriteOnly)) {
  366. f.write("1");
  367. f.close();
  368. }
  369. }
  370. App::wnd()->hideLayer();
  371. }
  372. Application::UpdatingState Application::updatingState() {
  373. if (!updateThread) return Application::UpdatingNone;
  374. if (!updateDownloader) return Application::UpdatingReady;
  375. return Application::UpdatingDownload;
  376. }
  377. int32 Application::updatingSize() {
  378. if (!updateDownloader) return 0;
  379. return updateDownloader->size();
  380. }
  381. int32 Application::updatingReady() {
  382. if (!updateDownloader) return 0;
  383. return updateDownloader->ready();
  384. }
  385. FileUploader *Application::uploader() {
  386. if (!::uploader) ::uploader = new FileUploader();
  387. return ::uploader;
  388. }
  389. void Application::uploadProfilePhoto(const QImage &tosend, const PeerId &peerId) {
  390. PreparedPhotoThumbs photoThumbs;
  391. QVector<MTPPhotoSize> photoSizes;
  392. QPixmap thumb = QPixmap::fromImage(tosend.scaled(160, 160, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
  393. photoThumbs.insert('a', thumb);
  394. photoSizes.push_back(MTP_photoSize(MTP_string("a"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(thumb.width()), MTP_int(thumb.height()), MTP_int(0)));
  395. QPixmap medium = QPixmap::fromImage(tosend.scaled(320, 320, Qt::KeepAspectRatio, Qt::SmoothTransformation), Qt::ColorOnly);
  396. photoThumbs.insert('b', medium);
  397. photoSizes.push_back(MTP_photoSize(MTP_string("b"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(medium.width()), MTP_int(medium.height()), MTP_int(0)));
  398. QPixmap full = QPixmap::fromImage(tosend, Qt::ColorOnly);
  399. photoThumbs.insert('c', full);
  400. photoSizes.push_back(MTP_photoSize(MTP_string("c"), MTP_fileLocationUnavailable(MTP_long(0), MTP_int(0), MTP_long(0)), MTP_int(full.width()), MTP_int(full.height()), MTP_int(0)));
  401. QByteArray jpeg;
  402. QBuffer jpegBuffer(&jpeg);
  403. full.save(&jpegBuffer, "JPG", 87);
  404. PhotoId id = MTP::nonce<PhotoId>();
  405. MTPPhoto photo(MTP_photo(MTP_long(id), MTP_long(0), MTP_int(MTP::authedId()), MTP_int(unixtime()), MTP_string(""), MTP_geoPointEmpty(), MTP_vector<MTPPhotoSize>(photoSizes)));
  406. QString file, filename;
  407. int32 filesize = 0;
  408. QByteArray data;
  409. ReadyLocalMedia ready(ToPreparePhoto, file, filename, filesize, data, id, id, qsl("jpg"), peerId, photo, photoThumbs, MTP_documentEmpty(MTP_long(0)), jpeg, false);
  410. connect(App::uploader(), SIGNAL(photoReady(MsgId, const MTPInputFile &)), App::app(), SLOT(photoUpdated(MsgId, const MTPInputFile &)), Qt::UniqueConnection);
  411. MsgId newId = clientMsgId();
  412. App::app()->regPhotoUpdate(peerId, newId);
  413. App::uploader()->uploadMedia(newId, ready);
  414. }
  415. void Application::stopUpdate() {
  416. if (updateReply) {
  417. updateReply->abort();
  418. updateReply->deleteLater();
  419. updateReply = 0;
  420. }
  421. if (updateDownloader) {
  422. updateDownloader->deleteLater();
  423. updateDownloader = 0;
  424. if (updateThread) updateThread->quit();
  425. updateThread = 0;
  426. }
  427. }
  428. void Application::startUpdateCheck(bool forceWait) {
  429. updateCheckTimer.stop();
  430. if (updateRequestId || updateThread || updateReply || !cAutoUpdate()) return;
  431. int32 updateInSecs = cLastUpdateCheck() + 3600 + (rand() % 3600) - unixtime();
  432. bool sendRequest = (updateInSecs <= 0 || updateInSecs > 7200);
  433. if (!sendRequest && !forceWait) {
  434. QDir updates(cWorkingDir() + "tupdates");
  435. if (updates.exists()) {
  436. QFileInfoList list = updates.entryInfoList(QDir::Files);
  437. for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
  438. if (QRegularExpression("^(tupdate|tmacupd|tlinuxupd|tlinux32upd)\\d+$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
  439. sendRequest = true;
  440. }
  441. }
  442. }
  443. }
  444. if (cManyInstance() && !cDebug()) return; // only main instance is updating
  445. if (sendRequest) {
  446. QNetworkRequest checkVersion(cUpdateURL());
  447. if (updateReply) updateReply->deleteLater();
  448. App::setProxySettings(updateManager);
  449. updateReply = updateManager.get(checkVersion);
  450. connect(updateReply, SIGNAL(finished()), this, SLOT(updateGotCurrent()));
  451. connect(updateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(updateFailedCurrent(QNetworkReply::NetworkError)));
  452. // updateRequestId = MTP::send(MTPhelp_GetAppUpdate(MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(cApiLang())), rpcDone(&Application::onAppUpdate), rpcFail(&Application::onAppUpdateFail);
  453. emit updateChecking();
  454. } else {
  455. updateCheckTimer.start((updateInSecs + 5) * 1000);
  456. }
  457. }
  458. namespace {
  459. QChar _toHex(ushort v) {
  460. v = v & 0x000F;
  461. return QChar::fromLatin1((v >= 10) ? ('a' + (v - 10)) : ('0' + v));
  462. }
  463. ushort _fromHex(QChar c) {
  464. return ((c.unicode() >= uchar('a')) ? (c.unicode() - uchar('a') + 10) : (c.unicode() - uchar('0'))) & 0x000F;
  465. }
  466. QString _escapeTo7bit(const QString &str) {
  467. QString result;
  468. result.reserve(str.size() * 2);
  469. for (int i = 0, l = str.size(); i != l; ++i) {
  470. QChar ch(str.at(i));
  471. ushort uch(ch.unicode());
  472. if (uch < 32 || uch > 127 || uch == ushort(uchar('%'))) {
  473. result.append('%').append(_toHex(uch >> 12)).append(_toHex(uch >> 8)).append(_toHex(uch >> 4)).append(_toHex(uch));
  474. } else {
  475. result.append(ch);
  476. }
  477. }
  478. return result;
  479. }
  480. QString _escapeFrom7bit(const QString &str) {
  481. QString result;
  482. result.reserve(str.size());
  483. for (int i = 0, l = str.size(); i != l; ++i) {
  484. QChar ch(str.at(i));
  485. if (ch == QChar::fromLatin1('%') && i + 4 < l) {
  486. result.append(QChar(ushort((_fromHex(str.at(i + 1)) << 12) | (_fromHex(str.at(i + 2)) << 8) | (_fromHex(str.at(i + 3)) << 4) | _fromHex(str.at(i + 4)))));
  487. i += 4;
  488. } else {
  489. result.append(ch);
  490. }
  491. }
  492. return result;
  493. }
  494. }
  495. void Application::socketConnected() {
  496. DEBUG_LOG(("Application Info: socket connected, this is not the first application instance, sending show command.."));
  497. closing = true;
  498. QString commands;
  499. const QStringList &lst(cSendPaths());
  500. for (QStringList::const_iterator i = lst.cbegin(), e = lst.cend(); i != e; ++i) {
  501. commands += qsl("SEND:") + _escapeTo7bit(*i) + ';';
  502. }
  503. if (!cStartUrl().isEmpty()) {
  504. commands += qsl("OPEN:") + _escapeTo7bit(cStartUrl()) + ';';
  505. }
  506. commands += qsl("CMD:show;");
  507. DEBUG_LOG(("Application Info: writing commands %1").arg(commands));
  508. socket.write(commands.toLocal8Bit());
  509. }
  510. void Application::socketWritten(qint64/* bytes*/) {
  511. if (socket.state() != QLocalSocket::ConnectedState) {
  512. DEBUG_LOG(("Application Error: socket is not connected %1").arg(socket.state()));
  513. return;
  514. }
  515. if (socket.bytesToWrite()) {
  516. return;
  517. }
  518. DEBUG_LOG(("Application Info: show command written, waiting response.."));
  519. }
  520. void Application::socketReading() {
  521. if (socket.state() != QLocalSocket::ConnectedState) {
  522. DEBUG_LOG(("Application Error: socket is not connected %1").arg(socket.state()));
  523. return;
  524. }
  525. socketRead.append(socket.readAll());
  526. if (QRegularExpression("RES:(\\d+);").match(socketRead).hasMatch()) {
  527. uint64 pid = socketRead.mid(4, socketRead.length() - 5).toULongLong();
  528. psActivateProcess(pid);
  529. DEBUG_LOG(("Application Info: show command response received, pid = %1, activating and quiting..").arg(pid));
  530. return App::quit();
  531. }
  532. }
  533. void Application::socketError(QLocalSocket::LocalSocketError e) {
  534. if (closing) {
  535. DEBUG_LOG(("Application Error: could not write show command, error %1, quiting..").arg(e));
  536. return App::quit();
  537. }
  538. if (e == QLocalSocket::ServerNotFoundError) {
  539. DEBUG_LOG(("Application Info: this is the only instance of Telegram, starting server and app.."));
  540. } else {
  541. DEBUG_LOG(("Application Info: socket connect error %1, starting server and app..").arg(e));
  542. }
  543. socket.close();
  544. psCheckLocalSocket(serverName);
  545. if (!server.listen(serverName)) {
  546. DEBUG_LOG(("Application Error: failed to start listening to %1 server, error %2").arg(serverName).arg(int(server.serverError())));
  547. return App::quit();
  548. }
  549. if (!cNoStartUpdate() && psCheckReadyUpdate()) {
  550. cSetRestartingUpdate(true);
  551. DEBUG_LOG(("Application Info: installing update instead of starting app.."));
  552. return App::quit();
  553. }
  554. startApp();
  555. }
  556. void Application::startApp() {
  557. DEBUG_LOG(("Application Info: starting app.."));
  558. Local::ReadMapState state = Local::readMap(QByteArray());
  559. DEBUG_LOG(("Application Info: local map read.."));
  560. App::readUserConfig();
  561. if (!Local::oldKey().created()) {
  562. Local::createOldKey();
  563. cSetNeedConfigResave(true);
  564. }
  565. if (cNeedConfigResave()) {
  566. App::writeConfig();
  567. App::writeUserConfig();
  568. cSetNeedConfigResave(false);
  569. }
  570. DEBUG_LOG(("Application Info: user config read.."));
  571. window->createWinId();
  572. window->init();
  573. DEBUG_LOG(("Application Info: window created.."));
  574. readSupportTemplates();
  575. MTP::start();
  576. MTP::setStateChangedHandler(mtpStateChanged);
  577. MTP::setSessionResetHandler(mtpSessionReset);
  578. DEBUG_LOG(("Application Info: MTP started.."));
  579. initImageLinkManager();
  580. App::initMedia();
  581. DEBUG_LOG(("Application Info: showing."));
  582. if (MTP::authedId()) {
  583. window->setupMain(false);
  584. } else {
  585. window->setupIntro(false);
  586. }
  587. window->firstShow();
  588. if (cStartToSettings()) {
  589. window->showSettings();
  590. }
  591. QNetworkProxyFactory::setUseSystemConfiguration(true);
  592. if (Local::oldMapVersion() < AppVersion) {
  593. psRegisterCustomScheme();
  594. if (Local::oldMapVersion() && Local::oldMapVersion() < 7010) {
  595. QString versionFeatures(lng_new_version_minor(lt_version, QString::fromStdWString(AppVersionStr), lt_link, qsl("https://desktop.telegram.org/#changelog")));
  596. if (!versionFeatures.isEmpty()) {
  597. window->serviceNotification(versionFeatures);
  598. }
  599. }
  600. }
  601. window->updateIsActive(cOnlineFocusTimeout());
  602. }
  603. void Application::socketDisconnected() {
  604. if (closing) {
  605. DEBUG_LOG(("Application Error: socket disconnected before command response received, quiting.."));
  606. return App::quit();
  607. }
  608. }
  609. void Application::newInstanceConnected() {
  610. DEBUG_LOG(("Application Info: new local socket connected"));
  611. for (QLocalSocket *client = server.nextPendingConnection(); client; client = server.nextPendingConnection()) {
  612. clients.push_back(ClientSocket(client, QByteArray()));
  613. connect(client, SIGNAL(readyRead()), this, SLOT(readClients()));
  614. connect(client, SIGNAL(disconnected()), this, SLOT(removeClients()));
  615. }
  616. }
  617. void Application::readClients() {
  618. QString startUrl;
  619. QStringList toSend;
  620. for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e; ++i) {
  621. i->second.append(i->first->readAll());
  622. if (i->second.size()) {
  623. QString cmds(QString::fromLocal8Bit(i->second));
  624. int32 from = 0, l = cmds.length();
  625. for (int32 to = cmds.indexOf(QChar(';'), from); to >= from; to = (from < l) ? cmds.indexOf(QChar(';'), from) : -1) {
  626. QStringRef cmd(&cmds, from, to - from);
  627. if (cmd.startsWith(qsl("CMD:"))) {
  628. execExternal(cmds.mid(from + 4, to - from - 4));
  629. QByteArray response(qsl("RES:%1;").arg(QCoreApplication::applicationPid()).toUtf8());
  630. i->first->write(response.data(), response.size());
  631. } else if (cmd.startsWith(qsl("SEND:"))) {
  632. if (cSendPaths().isEmpty()) {
  633. toSend.append(_escapeFrom7bit(cmds.mid(from + 5, to - from - 5)));
  634. }
  635. } else if (cmd.startsWith(qsl("OPEN:"))) {
  636. if (cStartUrl().isEmpty()) {
  637. startUrl = _escapeFrom7bit(cmds.mid(from + 5, to - from - 5));
  638. }
  639. } else {
  640. LOG(("Application Error: unknown command %1 passed in local socket").arg(QString(cmd.constData(), cmd.length())));
  641. }
  642. from = to + 1;
  643. }
  644. if (from > 0) {
  645. i->second = i->second.mid(from);
  646. }
  647. }
  648. }
  649. if (!toSend.isEmpty()) {
  650. QStringList paths(cSendPaths());
  651. paths.append(toSend);
  652. cSetSendPaths(paths);
  653. }
  654. if (!cSendPaths().isEmpty()) {
  655. if (App::wnd()) {
  656. App::wnd()->sendPaths();
  657. }
  658. }
  659. if (!startUrl.isEmpty()) {
  660. cSetStartUrl(startUrl);
  661. }
  662. if (!cStartUrl().isEmpty() && App::main() && App::self()) {
  663. App::main()->openLocalUrl(cStartUrl());
  664. cSetStartUrl(QString());
  665. }
  666. }
  667. void Application::removeClients() {
  668. DEBUG_LOG(("Application Info: remove clients slot called, clients %1").arg(clients.size()));
  669. for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e;) {
  670. if (i->first->state() != QLocalSocket::ConnectedState) {
  671. DEBUG_LOG(("Application Info: removing client"));
  672. i = clients.erase(i);
  673. e = clients.end();
  674. } else {
  675. ++i;
  676. }
  677. }
  678. }
  679. void Application::execExternal(const QString &cmd) {
  680. DEBUG_LOG(("Application Info: executing external command '%1'").arg(cmd));
  681. if (cmd == "show") {
  682. window->activate();
  683. }
  684. }
  685. void Application::closeApplication() {
  686. // close server
  687. server.close();
  688. for (ClientSockets::iterator i = clients.begin(), e = clients.end(); i != e; ++i) {
  689. disconnect(i->first, SIGNAL(disconnected()), this, SLOT(removeClients()));
  690. i->first->close();
  691. }
  692. clients.clear();
  693. MTP::stop();
  694. }
  695. Application::~Application() {
  696. App::setQuiting();
  697. window->setParent(0);
  698. anim::stopManager();
  699. socket.close();
  700. closeApplication();
  701. App::deinitMedia();
  702. deinitImageLinkManager();
  703. mainApp = 0;
  704. delete updateReply;
  705. delete ::uploader;
  706. updateReply = 0;
  707. if (updateDownloader) updateDownloader->deleteLater();
  708. updateDownloader = 0;
  709. if (updateThread) updateThread->quit();
  710. updateThread = 0;
  711. delete window;
  712. style::stopManager();
  713. Local::stop();
  714. delete _translator;
  715. }
  716. Application *Application::app() {
  717. return mainApp;
  718. }
  719. Window *Application::wnd() {
  720. return mainApp ? mainApp->window : 0;
  721. }
  722. QString Application::language() {
  723. if (!lng.length()) {
  724. lng = psCurrentLanguage();
  725. }
  726. if (!lng.length()) {
  727. lng = "en";
  728. }
  729. return lng;
  730. }
  731. int32 Application::languageId() {
  732. QByteArray l = language().toLatin1();
  733. for (int32 i = 0; i < languageCount; ++i) {
  734. if (l.at(0) == LanguageCodes[i][0] && l.at(1) == LanguageCodes[i][1]) {
  735. return i;
  736. }
  737. }
  738. return languageDefault;
  739. }
  740. MainWidget *Application::main() {
  741. return mainApp ? mainApp->window->mainWidget() : 0;
  742. }