diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat index f81bebf88..b343182fa 100644 --- a/Telegram/PrepareWin.bat +++ b/Telegram/PrepareWin.bat @@ -1,9 +1,9 @@ @echo OFF -set "AppVersion=7018" -set "AppVersionStrSmall=0.7.18" -set "AppVersionStr=0.7.18" -set "AppVersionStrFull=0.7.18.0" +set "AppVersion=7019" +set "AppVersionStrSmall=0.7.19" +set "AppVersionStr=0.7.19" +set "AppVersionStrFull=0.7.19.0" set "DevChannel=1" if %DevChannel% neq 0 goto preparedev diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings index 7a24798d9..413314cd4 100644 --- a/Telegram/Resources/lang.strings +++ b/Telegram/Resources/lang.strings @@ -227,6 +227,27 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org "lng_local_storage_clear_failed" = "Clear failed :("; "lng_settings_section_advanced" = "Advanced"; + +"lng_passcode_turn_on" = "Turn passcode on"; +"lng_passcode_change" = "Change passcode"; +"lng_passcode_create" = "Create passcode"; +"lng_passcode_remove" = "Remove passcode"; +"lng_passcode_turn_off" = "Turn off"; +"lng_passcode_autolock_away" = "Auto-Lock if away for:"; +"lng_passcode_autolock_inactive" = "Auto-Lock if inactive for:"; +"lng_passcode_autolock_minutes" = "{count:_not_used_|# minute|# minutes}"; +"lng_passcode_autolock_hours" = "{count:_not_used_|# hour|# hours}"; +"lng_passcode_enter_old" = "Enter old passcode"; +"lng_passcode_enter_new" = "Enter new passcode"; +"lng_passcode_confirm_new" = "Re-enter new passcode"; +"lng_passcode_about" = "When you set up a passcode, a lock icon will appear in the corner. Tap it to lock the app.\n\nNote: if you forget the passcode, you'll need to relogin in Telegram Desktop."; +"lng_passcode_differ" = "Passcodes are different"; +"lng_passcode_wrong" = "Wrong passcode"; +"lng_passcode_is_same" = "Passcode was not changed"; +"lng_passcode_enter" = "Enter your Telegram Passcode"; +"lng_passcode_submit" = "Submit"; +"lng_passcode_logout" = "or you can {link_start}log out ยป{link_end}"; + "lng_connection_type" = "Connection type:"; "lng_connection_auto_connecting" = "Default (connecting..)"; "lng_connection_auto" = "Default ({type} used)"; diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt index 476048bd2..b9b15011a 100644 --- a/Telegram/Resources/style.txt +++ b/Telegram/Resources/style.txt @@ -52,7 +52,7 @@ wndMinHeight: 480px; wndDefWidth: 800px; wndDefHeight: 600px; wndBG: #FFF; -wndShadow: sprite(184px, 21px, 19px, 19px); +wndShadow: sprite(209px, 46px, 19px, 19px); wndShadowShift: 1px; layerAlpha: 0.3; @@ -116,6 +116,12 @@ sysRes: sysButton(sysUpd) { sysCls: sysButton(sysUpd) { img: sprite(276px, 1px, 19px, 19px); } +sysLock: sysButton(sysUpd) { + img: sprite(184px, 22px, 19px, 19px); +} +sysUnlock: sysButton(sysUpd) { + img: sprite(207px, 22px, 19px, 19px); +} titleBackButton: iconedButton(btnDefIconed) { icon: sprite(133px, 197px, 13px, 20px); iconPos: point(5px, 9px); @@ -383,7 +389,7 @@ btnIntroNext: flatButton(btnDefNext, btnDefBig) { width: 300px; } -boxShadow: sprite(240px, 21px, 9px, 9px); +boxShadow: sprite(230px, 46px, 9px, 9px); introCountry: countryInput { width: 300px; @@ -637,7 +643,7 @@ dlgFilter: flatInput(inpDefGray) { phColor: #949494; phFocusColor: #a4a4a4; textMrg: margins(34px, 2px, 34px, 4px); - imgRect: sprite(208px, 28px, 24px, 24px); + imgRect: sprite(227px, 21px, 24px, 24px); imgPos: point(6px, 5px); width: 240px; @@ -1189,7 +1195,7 @@ participantFilter: flatInput(inpDefFlat) { height: 52px; font: font(16px); textMrg: margins(39px, 11px, 10px, 10px); - imgRect: sprite(208px, 28px, 24px, 24px); + imgRect: sprite(227px, 21px, 24px, 24px); imgPos: point(10px, 15px); } participantDelta: 12px; @@ -1308,7 +1314,7 @@ connectingColor: #777; connectingPadding: margins(5px, 5px, 5px, 5px); dropdownPadding: margins(10px, 10px, 10px, 10px); -dropdownShadow: sprite(240px, 35px, 6px, 6px); +dropdownShadow: sprite(241px, 46px, 6px, 6px); dropdownBorder: 1px; dropdownBorderColor: #ebebeb; dropdownBackground: white; @@ -1694,3 +1700,17 @@ backgroundSize: size(108px, 193px); backgroundScroll: flatScroll(newGroupScroll) { topsh: -2px; } + +passcodeHeaderFont: font(19px); +passcodeHeaderHeight: 80px; +passcodeSkip: 40px; +passcodeInput: flatInput(inpIntroPhone) { +} +passcodeSubmit: flatButton(btnIntroNext) { + textTop: 15px; + overTextTop: 15px; + downTextTop: 16px; + width: 225px; + font: font(19px); + overFont: font(19px); +} diff --git a/Telegram/Setup.iss b/Telegram/Setup.iss index 041c43c47..78ade6152 100644 --- a/Telegram/Setup.iss +++ b/Telegram/Setup.iss @@ -61,6 +61,7 @@ Type: files; Name: "{app}\log.txt" Type: filesandordirs; Name: "{app}\DebugLogs" Type: filesandordirs; Name: "{app}\tupdates" Type: filesandordirs; Name: "{app}\tdata" +Type: filesandordirs; Name: "{app}\tcache" Type: filesandordirs; Name: "{app}\tdumps" Type: dirifempty; Name: "{app}" Type: files; Name: "{userappdata}\{#MyAppName}\data" @@ -69,6 +70,7 @@ Type: files; Name: "{userappdata}\{#MyAppName}\log.txt" Type: filesandordirs; Name: "{userappdata}\{#MyAppName}\DebugLogs" Type: filesandordirs; Name: "{userappdata}\{#MyAppName}\tupdates" Type: filesandordirs; Name: "{userappdata}\{#MyAppName}\tdata" +Type: filesandordirs; Name: "{userappdata}\{#MyAppName}\tcache" Type: filesandordirs; Name: "{userappdata}\{#MyAppName}\tdumps" Type: dirifempty; Name: "{userappdata}\{#MyAppName}" diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 54bae380e..73fbb1096 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -18,8 +18,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "lang.h" -#include "app.h" - #include "audio.h" #include "application.h" #include "fileuploader.h" @@ -111,6 +109,11 @@ namespace App { return w ? w->settingsWidget() : 0; } + bool passcoded() { + Window *w(wnd()); + return w ? w->passcodeWidget() : 0; + } + FileUploader *uploader() { return app() ? app()->uploader() : 0; } @@ -122,6 +125,9 @@ namespace App { bool loggedOut() { Window *w(wnd()); + if (cHasPasscode()) { + cSetHasPasscode(false); + } if (w) { w->tempDirDelete(Local::ClearManagerAll); w->notifyClearFast(); @@ -130,6 +136,8 @@ namespace App { MainWidget *m(main()); if (m) m->destroyData(); MTP::authed(0); + Local::reset(); + cSetOtherOnline(0); histories().clear(); globalNotifyAllPtr = UnknownNotifySettings; @@ -138,6 +146,7 @@ namespace App { App::uploader()->clear(); clearStorageImages(); if (w) { + w->getTitle()->updateBackButton(); w->updateTitleStatus(); w->getTitle()->resizeEvent(0); } @@ -145,7 +154,12 @@ namespace App { } void logOut() { - MTP::logoutKeys(rpcDone(&loggedOut), rpcFail(&loggedOut)); + if (MTP::started()) { + MTP::logoutKeys(rpcDone(&loggedOut), rpcFail(&loggedOut)); + } else { + loggedOut(); + MTP::start(); + } } PeerId peerFromMTP(const MTPPeer &peer_id) { @@ -1477,514 +1491,6 @@ namespace App { if (cSoundNotify() && !psSkipAudioNotify()) audioPlayNotify(); } - void writeConfig() { - QDir().mkdir(cWorkingDir() + qsl("tdata")); - QFile configFile(cWorkingDir() + qsl("tdata/config")); - if (configFile.open(QIODevice::WriteOnly)) { - DEBUG_LOG(("App Info: writing config file")); - QDataStream configStream(&configFile); - configStream.setVersion(QDataStream::Qt_5_1); - configStream << quint32(dbiVersion) << qint32(AppVersion); - - configStream << quint32(dbiAutoStart) << qint32(cAutoStart()); - configStream << quint32(dbiStartMinimized) << qint32(cStartMinimized()); - configStream << quint32(dbiSendToMenu) << qint32(cSendToMenu()); - configStream << quint32(dbiWorkMode) << qint32(cWorkMode()); - configStream << quint32(dbiSeenTrayTooltip) << qint32(cSeenTrayTooltip()); - configStream << quint32(dbiAutoUpdate) << qint32(cAutoUpdate()); - configStream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck()); - configStream << quint32(dbiScale) << qint32(cConfigScale()); - configStream << quint32(dbiLang) << qint32(cLang()); - configStream << quint32(dbiLangFile) << cLangFile(); - - configStream << quint32(dbiConnectionType) << qint32(cConnectionType()); - if (cConnectionType() == dbictHttpProxy || cConnectionType() == dbictTcpProxy) { - const ConnectionProxy &proxy(cConnectionProxy()); - configStream << proxy.host << qint32(proxy.port) << proxy.user << proxy.password; - } - - TWindowPos pos(cWindowPos()); - configStream << quint32(dbiWindowPosition) << qint32(pos.x) << qint32(pos.y) << qint32(pos.w) << qint32(pos.h) << qint32(pos.moncrc) << qint32(pos.maximized); - - if (configStream.status() != QDataStream::Ok) { - LOG(("App Error: could not write user config file, status: %1").arg(configStream.status())); - } - } else { - LOG(("App Error: could not open user config file for writing")); - } - } - - void readConfig() { - QFile configFile(cWorkingDir() + qsl("tdata/config")); - if (configFile.open(QIODevice::ReadOnly)) { - DEBUG_LOG(("App Info: config file opened for reading")); - QDataStream configStream(&configFile); - configStream.setVersion(QDataStream::Qt_5_1); - - qint32 configVersion = 0; - while (true) { - quint32 blockId; - configStream >> blockId; - if (configStream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("App Info: config file read end")); - break; - } else if (configStream.status() != QDataStream::Ok) { - LOG(("App Error: could not read block id, status: %1 - config file is corrupted?..").arg(configStream.status())); - break; - } - - if (blockId == dbiVersion) { - configStream >> configVersion; - if (configVersion > AppVersion) break; - continue; - } - - switch (blockId) { - case dbiAutoStart: { - qint32 v; - configStream >> v; - cSetAutoStart(v == 1); - } break; - - case dbiStartMinimized: { - qint32 v; - configStream >> v; - cSetStartMinimized(v == 1); - } break; - - case dbiSendToMenu: { - qint32 v; - configStream >> v; - cSetSendToMenu(v == 1); - } break; - - case dbiSoundNotify: { - if (configVersion < 3008) { - qint32 v; - configStream >> v; - cSetSoundNotify(v == 1); - cSetNeedConfigResave(true); - } - } break; - - case dbiDesktopNotify: { - if (configVersion < 3008) { - qint32 v; - configStream >> v; - cSetDesktopNotify(v == 1); - cSetNeedConfigResave(true); - } - } break; - - case dbiWorkMode: { - qint32 v; - configStream >> v; - switch (v) { - case dbiwmTrayOnly: cSetWorkMode(dbiwmTrayOnly); break; - case dbiwmWindowOnly: cSetWorkMode(dbiwmWindowOnly); break; - default: cSetWorkMode(dbiwmWindowAndTray); break; - }; - } break; - - case dbiConnectionType: { - qint32 v; - configStream >> v; - - switch (v) { - case dbictHttpProxy: - case dbictTcpProxy: { - ConnectionProxy p; - qint32 port; - configStream >> p.host >> port >> p.user >> p.password; - p.port = uint32(port); - cSetConnectionProxy(p); - } - cSetConnectionType(DBIConnectionType(v)); - break; - case dbictHttpAuto: - default: cSetConnectionType(dbictAuto); break; - }; - } break; - - case dbiSeenTrayTooltip: { - qint32 v; - configStream >> v; - cSetSeenTrayTooltip(v == 1); - } break; - - case dbiAutoUpdate: { - qint32 v; - configStream >> v; - cSetAutoUpdate(v == 1); - } break; - - case dbiLastUpdateCheck: { - qint32 v; - configStream >> v; - cSetLastUpdateCheck(v); - } break; - - case dbiScale: { - qint32 v; - configStream >> v; - - DBIScale s = cRealScale(); - switch (v) { - case dbisAuto: s = dbisAuto; break; - case dbisOne: s = dbisOne; break; - case dbisOneAndQuarter: s = dbisOneAndQuarter; break; - case dbisOneAndHalf: s = dbisOneAndHalf; break; - case dbisTwo: s = dbisTwo; break; - } - if (cRetina()) s = dbisOne; - cSetConfigScale(s); - cSetRealScale(s); - } break; - - case dbiLang: { - qint32 v; - configStream >> v; - if (v == languageTest || (v >= 0 && v < languageCount)) { - cSetLang(v); - } - } break; - - case dbiLangFile: { - QString v; - configStream >> v; - cSetLangFile(v); - } break; - - case dbiWindowPosition: { - TWindowPos pos; - configStream >> pos.x >> pos.y >> pos.w >> pos.h >> pos.moncrc >> pos.maximized; - cSetWindowPos(pos); - } break; - } - - if (configStream.status() != QDataStream::Ok) { - LOG(("App Error: could not read data, status: %1 - user config file is corrupted?..").arg(configStream.status())); - break; - } - } - } - } - - void writeUserConfig() { - QFile configFile(cWorkingDir() + cDataFile() + qsl("_config")); - if (configFile.open(QIODevice::WriteOnly)) { - DEBUG_LOG(("App Info: writing user config data for encrypt")); - QByteArray toEncrypt; - toEncrypt.reserve(65536); - toEncrypt.resize(4); - { - QBuffer buffer(&toEncrypt); - buffer.open(QIODevice::Append); - - QDataStream stream(&buffer); - stream.setVersion(QDataStream::Qt_5_1); - - if (MTP::authedId()) { - stream << quint32(dbiUser) << qint32(MTP::authedId()) << quint32(MTP::maindc()); - } - - stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); - stream << quint32(dbiTileBackground) << qint32(cTileBackground() ? 1 : 0); - stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0); - stream << quint32(dbiDefaultAttach) << qint32(cDefaultAttach()); - stream << quint32(dbiSoundNotify) << qint32(cSoundNotify()); - stream << quint32(dbiDesktopNotify) << qint32(cDesktopNotify()); - stream << quint32(dbiNotifyView) << qint32(cNotifyView()); - stream << quint32(dbiAskDownloadPath) << qint32(cAskDownloadPath()); - stream << quint32(dbiDownloadPath) << (cAskDownloadPath() ? QString() : cDownloadPath()); - stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage()); - stream << quint32(dbiEmojiTab) << qint32(cEmojiTab()); - - RecentEmojiPreload v; - v.reserve(cGetRecentEmojis().size()); - for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) { - v.push_back(qMakePair(i->first->code, i->second)); - } - stream << quint32(dbiRecentEmojis) << v; - - writeAllMuted(stream); - - MTP::writeConfig(stream); - if (stream.status() != QDataStream::Ok) { - LOG(("App Error: could not write user config to memory buf, status: %1").arg(stream.status())); - return; - } - } - *(uint32*)(toEncrypt.data()) = toEncrypt.size(); - - uint32 size = toEncrypt.size(), fullSize = size; - if (fullSize & 0x0F) { - fullSize += 0x10 - (fullSize & 0x0F); - toEncrypt.resize(fullSize); - memset_rand(toEncrypt.data() + size, fullSize - size); - } - QByteArray encrypted(16 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data - hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data()); - aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 16, fullSize, &Local::oldKey(), encrypted.constData()); - - DEBUG_LOG(("App Info: writing user config file")); - QDataStream configStream(&configFile); - configStream.setVersion(QDataStream::Qt_5_1); - configStream << quint32(dbiVersion) << qint32(AppVersion); - - configStream << quint32(dbiEncryptedWithSalt) << cLocalSalt() << encrypted; // write all encrypted data - - if (configStream.status() != QDataStream::Ok) { - LOG(("App Error: could not write user config file, status: %1").arg(configStream.status())); - } - } else { - LOG(("App Error: could not open user config file for writing")); - } - } - - void readUserConfigFields(QIODevice *io) { - if (!io->isOpen()) io->open(QIODevice::ReadOnly); - - QDataStream stream(io); - stream.setVersion(QDataStream::Qt_5_1); - - while (true) { - quint32 blockId; - stream >> blockId; - if (stream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("App Info: config file read end")); - break; - } else if (stream.status() != QDataStream::Ok) { - LOG(("App Error: could not read block id, status: %1 - user config file is corrupted?..").arg(stream.status())); - break; - } - - if (blockId == dbiVersion) { // should not be in encrypted part, just ignore - qint32 configVersion; - stream >> configVersion; - continue; - } - - switch (blockId) { - case dbiEncryptedWithSalt: { - QByteArray salt, data, decrypted; - stream >> salt >> data; - - if (salt.size() != 32) { - LOG(("App Error: bad salt in encrypted part, size: %1").arg(salt.size())); - continue; - } - - cSetLocalSalt(salt); - Local::createOldKey(&salt); - - if (data.size() <= 16 || (data.size() & 0x0F)) { - LOG(("App Error: bad encrypted part size: %1").arg(data.size())); - continue; - } - uint32 fullDataLen = data.size() - 16; - decrypted.resize(fullDataLen); - const char *dataKey = data.constData(), *encrypted = data.constData() + 16; - aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &Local::oldKey(), dataKey); - uchar sha1Buffer[20]; - if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) { - LOG(("App Error: bad decrypt key, data from user-config not decrypted")); - continue; - } - uint32 dataLen = *(const uint32*)decrypted.constData(); - if (dataLen > uint32(decrypted.size()) || dataLen <= fullDataLen - 16 || dataLen < 4) { - LOG(("App Error: bad decrypted part size: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size())); - continue; - } - decrypted.resize(dataLen); - QBuffer decryptedStream(&decrypted); - decryptedStream.open(QIODevice::ReadOnly); - decryptedStream.seek(4); // skip size - readUserConfigFields(&decryptedStream); - } break; - - case dbiLoggedPhoneNumber: { - QString v; - stream >> v; - if (stream.status() == QDataStream::Ok) { - cSetLoggedPhoneNumber(v); - } - } break; - - case dbiMutePeer: { - readOneMuted(stream); - } break; - - case dbiMutedPeers: { - readAllMuted(stream); - } break; - - case dbiSendKey: { - qint32 v; - stream >> v; - cSetCtrlEnter(v == dbiskCtrlEnter); - } break; - - case dbiCatsAndDogs: { - qint32 v; - stream >> v; - } break; - - case dbiTileBackground: { - qint32 v; - stream >> v; - cSetTileBackground(v == 1); - } break; - - case dbiReplaceEmojis: { - qint32 v; - stream >> v; - cSetReplaceEmojis(v == 1); - } break; - - case dbiDefaultAttach: { - qint32 v; - stream >> v; - switch (v) { - case dbidaPhoto: cSetDefaultAttach(dbidaPhoto); break; - default: cSetDefaultAttach(dbidaDocument); break; - } - } break; - - case dbiSoundNotify: { - qint32 v; - stream >> v; - cSetSoundNotify(v == 1); - } break; - - case dbiDesktopNotify: { - qint32 v; - stream >> v; - cSetDesktopNotify(v == 1); - } break; - - case dbiNotifyView: { - qint32 v; - stream >> v; - switch (v) { - case dbinvShowNothing: cSetNotifyView(dbinvShowNothing); break; - case dbinvShowName: cSetNotifyView(dbinvShowName); break; - default: cSetNotifyView(dbinvShowPreview); break; - } - } break; - - case dbiAskDownloadPath: { - qint32 v; - stream >> v; - cSetAskDownloadPath(v == 1); - } break; - - case dbiDownloadPath: { - QString v; - stream >> v; - cSetDownloadPath(v); - } break; - - case dbiCompressPastedImage: { - qint32 v; - stream >> v; - cSetCompressPastedImage(v == 1); - } break; - - case dbiEmojiTab: { - qint32 v; - stream >> v; - switch (v) { - case dbietRecent : cSetEmojiTab(dbietRecent); break; - case dbietPeople : cSetEmojiTab(dbietPeople); break; - case dbietNature : cSetEmojiTab(dbietNature); break; - case dbietObjects : cSetEmojiTab(dbietObjects); break; - case dbietPlaces : cSetEmojiTab(dbietPlaces); break; - case dbietSymbols : cSetEmojiTab(dbietSymbols); break; - case dbietStickers: cSetEmojiTab(dbietStickers); break; - } - } break; - - case dbiRecentEmojis: { - RecentEmojiPreload v; - stream >> v; - cSetRecentEmojisPreload(v); - } break; - - default: - if (!MTP::readConfigElem(blockId, stream)) { - } - break; - } - - if (stream.status() != QDataStream::Ok) { - LOG(("App Error: could not read data, status: %1 - user config file is corrupted?..").arg(stream.status())); - break; - } - } - } - - void readUserConfig() { - QFile configFile(cWorkingDir() + cDataFile() + qsl("_config")); - if (configFile.open(QIODevice::ReadOnly)) { - DEBUG_LOG(("App Info: user config file opened for reading")); - { - QDataStream configStream(&configFile); - configStream.setVersion(QDataStream::Qt_5_1); - - quint32 blockId; - configStream >> blockId; - if (configStream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("App Info: config file read end")); - return; - } else if (configStream.status() != QDataStream::Ok) { - LOG(("App Error: could not read block id, status: %1 - user config file is corrupted?..").arg(configStream.status())); - return; - } - - if (blockId == dbiVersion) { - qint32 configVersion; - configStream >> configVersion; - if (configVersion > AppVersion) return; - - configStream >> blockId; - if (configStream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("App Info: config file read end")); - return; - } else if (configStream.status() != QDataStream::Ok) { - LOG(("App Error: could not read block id, status: %1 - user config file is corrupted?..").arg(configStream.status())); - return; - } - if (blockId != dbiEncryptedWithSalt) { // old version data - not encrypted - cSetNeedConfigResave(true); - } - } else { - cSetNeedConfigResave(true); - } - } - - configFile.reset(); - readUserConfigFields(&configFile); - } - } - - void writeAllMuted(QDataStream &stream) { // deprecated - } - - void readOneMuted(QDataStream &stream) { // deprecated - quint64 peerId; - stream >> peerId; - } - - void readAllMuted(QDataStream &stream) { - quint32 count; - stream >> count; - - for (uint32 i = 0; i < count; ++i) { - readOneMuted(stream); - } - } - void checkImageCacheSize() { int64 nowImageCacheSize = imageCacheSize(); if (nowImageCacheSize > serviceImageCacheSize + MemoryForImageCache) { diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h index c5dd461c0..6cd4c5b7b 100644 --- a/Telegram/SourceFiles/app.h +++ b/Telegram/SourceFiles/app.h @@ -39,6 +39,7 @@ namespace App { Window *wnd(); MainWidget *main(); SettingsWidget *settings(); + bool passcoded(); FileUploader *uploader(); void showSettings(); @@ -151,18 +152,6 @@ namespace App { void deinitMedia(bool completely = true); void playSound(); - void writeConfig(); - void readConfig(); - void writeUserConfig(); - void readUserConfig(); - - void muteHistory(History *history); - void unmuteHistory(History *history); - void writeAllMuted(QDataStream &stream); - void readAllMuted(QDataStream &stream); - void readOneMuted(QDataStream &stream); - bool isPeerMuted(const PeerId &peer); - void checkImageCacheSize(); bool isValidPhone(QString phone); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 180a3497b..af3d868f6 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -152,7 +152,6 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv), installTranslator(_translator = new Translator()); - Local::start(); style::startManager(); anim::startManager(); historyInit(); @@ -174,8 +173,8 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv), connect(this, SIGNAL(updateFailed()), this, SLOT(onUpdateFailed())); connect(this, SIGNAL(updateReady()), this, SLOT(onUpdateReady())); connect(this, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(onAppStateChanged(Qt::ApplicationState))); - connect(&writeUserConfigTimer, SIGNAL(timeout()), this, SLOT(onWriteUserConfig())); - writeUserConfigTimer.setSingleShot(true); + //connect(&writeUserConfigTimer, SIGNAL(timeout()), this, SLOT(onWriteUserConfig())); + //writeUserConfigTimer.setSingleShot(true); connect(&killDownloadSessionsTimer, SIGNAL(timeout()), this, SLOT(killDownloadSessions())); @@ -191,7 +190,7 @@ void Application::onAppUpdate(const MTPhelp_AppUpdate &response) { updateRequestId = 0; cSetLastUpdateCheck(unixtime()); - App::writeConfig(); + Local::writeSettings(); if (response.type() == mtpc_help_noAppUpdate) { startUpdateCheck(); } else { @@ -205,7 +204,7 @@ void Application::onAppUpdate(const MTPhelp_AppUpdate &response) { bool Application::onAppUpdateFail() { updateRequestId = 0; cSetLastUpdateCheck(unixtime()); - App::writeConfig(); + Local::writeSettings(); startUpdateCheck(); return true; } @@ -239,7 +238,7 @@ void Application::updateGotCurrent() { emit updateLatest(); } startUpdateCheck(true); - App::writeConfig(); + Local::writeSettings(); } void Application::updateFailedCurrent(QNetworkReply::NetworkError e) { @@ -259,7 +258,7 @@ void Application::onUpdateReady() { updateCheckTimer.stop(); cSetLastUpdateCheck(unixtime()); - App::writeConfig(); + Local::writeSettings(); } void Application::onUpdateFailed() { @@ -271,7 +270,7 @@ void Application::onUpdateFailed() { } cSetLastUpdateCheck(unixtime()); - App::writeConfig(); + Local::writeSettings(); } void Application::regPhotoUpdate(const PeerId &peer, MsgId msgId) { @@ -347,11 +346,11 @@ void Application::peerClearPhoto(PeerId peer) { } } -void Application::writeUserConfigIn(uint64 ms) { - if (!writeUserConfigTimer.isActive()) { - writeUserConfigTimer.start(ms); - } -} +//void Application::writeUserConfigIn(uint64 ms) { +// if (!writeUserConfigTimer.isActive()) { +// writeUserConfigTimer.start(ms); +// } +//} void Application::killDownloadSessionsStart(int32 dc) { if (killDownloadSessionTimes.constFind(dc) == killDownloadSessionTimes.cend()) { @@ -373,9 +372,9 @@ void Application::checkLocalTime() { if (App::main()) App::main()->checkLastUpdate(checkms()); } -void Application::onWriteUserConfig() { - App::writeUserConfig(); -} +//void Application::onWriteUserConfig() { +// Local::writeUserSettings(); +//} void Application::onAppStateChanged(Qt::ApplicationState state) { checkLocalTime(); @@ -649,32 +648,44 @@ void Application::socketError(QLocalSocket::LocalSocketError e) { startApp(); } +void Application::checkMapVersion() { + if (Local::oldMapVersion() < AppVersion) { + psRegisterCustomScheme(); + if (Local::oldMapVersion()) { + QString versionFeatures; + if (DevChannel && Local::oldMapVersion() < 7019) { + versionFeatures = QString::fromUtf8("\xe2\x80\x94 Passcode lock option added"); + } else if (!DevChannel && Local::oldMapVersion() < 7017) { + versionFeatures = lang(lng_new_version_minor).trimmed(); + } + if (!versionFeatures.isEmpty()) { + versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog")); + window->serviceNotification(versionFeatures); + } + } + } +} + void Application::startApp() { cChangeTimeFormat(QLocale::system().timeFormat(QLocale::ShortFormat)); DEBUG_LOG(("Application Info: starting app..")); Local::ReadMapState state = Local::readMap(QByteArray()); + if (state == Local::ReadMapPassNeeded) { + cSetHasPasscode(true); + } DEBUG_LOG(("Application Info: local map read..")); - App::readUserConfig(); - if (!Local::oldKey().created()) { - Local::createOldKey(); - cSetNeedConfigResave(true); - } - if (cNeedConfigResave()) { - App::writeConfig(); - App::writeUserConfig(); - cSetNeedConfigResave(false); - } - DEBUG_LOG(("Application Info: user config read..")); window->createWinId(); window->init(); DEBUG_LOG(("Application Info: window created..")); - MTP::start(); + if (state != Local::ReadMapPassNeeded) { + MTP::start(); + } MTP::setStateChangedHandler(mtpStateChanged); MTP::setSessionResetHandler(mtpSessionReset); @@ -685,12 +696,15 @@ void Application::startApp() { App::initMedia(); DEBUG_LOG(("Application Info: showing.")); - if (MTP::authedId()) { - window->setupMain(false); + if (state == Local::ReadMapPassNeeded) { + window->setupPasscode(false); } else { - window->setupIntro(false); + if (MTP::authedId()) { + window->setupMain(false); + } else { + window->setupIntro(false); + } } - window->firstShow(); if (cStartToSettings()) { @@ -698,20 +712,9 @@ void Application::startApp() { } QNetworkProxyFactory::setUseSystemConfiguration(true); - if (Local::oldMapVersion() < AppVersion) { - psRegisterCustomScheme(); - if (Local::oldMapVersion()) { - QString versionFeatures; - if (DevChannel && Local::oldMapVersion() < 7018) { - versionFeatures = QString::fromUtf8("\xe2\x80\x94 Windows: crash on start fixed for some Intel cards\n\xe2\x80\x94 Linux: tray icon returned in Pantheon and Gnome"); - } else if (!DevChannel && Local::oldMapVersion() < 7017) { - versionFeatures = lang(lng_new_version_minor).trimmed(); - } - if (!versionFeatures.isEmpty()) { - versionFeatures = lng_new_version_wrap(lt_version, QString::fromStdWString(AppVersionStr), lt_changes, versionFeatures, lt_link, qsl("https://desktop.telegram.org/#changelog")); - window->serviceNotification(versionFeatures); - } - } + + if (state != Local::ReadMapPassNeeded) { + checkMapVersion(); } window->updateIsActive(cOnlineFocusTimeout()); @@ -844,8 +847,7 @@ Application::~Application() { cSetChatDogImage(0); style::stopManager(); - Local::stop(); - + delete _translator; } diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h index 21495a02c..a55d8f76d 100644 --- a/Telegram/SourceFiles/application.h +++ b/Telegram/SourceFiles/application.h @@ -76,6 +76,7 @@ public: void killDownloadSessionsStop(int32 dc); void checkLocalTime(); + void checkMapVersion(); signals: @@ -107,7 +108,7 @@ public slots: void photoUpdated(MsgId msgId, const MTPInputFile &file); void onEnableDebugMode(); - void onWriteUserConfig(); +// void onWriteUserConfig(); void killDownloadSessions(); void onAppStateChanged(Qt::ApplicationState state); diff --git a/Telegram/SourceFiles/art/sprite.png b/Telegram/SourceFiles/art/sprite.png index 25c31ec9e..6c67e4062 100644 Binary files a/Telegram/SourceFiles/art/sprite.png and b/Telegram/SourceFiles/art/sprite.png differ diff --git a/Telegram/SourceFiles/art/sprite_200x.png b/Telegram/SourceFiles/art/sprite_200x.png index 2e0db7e59..86ab815e8 100644 Binary files a/Telegram/SourceFiles/art/sprite_200x.png and b/Telegram/SourceFiles/art/sprite_200x.png differ diff --git a/Telegram/SourceFiles/boxes/autolockbox.cpp b/Telegram/SourceFiles/boxes/autolockbox.cpp new file mode 100644 index 000000000..bb5bb7838 --- /dev/null +++ b/Telegram/SourceFiles/boxes/autolockbox.cpp @@ -0,0 +1,152 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "lang.h" + +#include "localstorage.h" + +#include "autolockbox.h" +#include "confirmbox.h" +#include "mainwidget.h" +#include "window.h" + +AutoLockBox::AutoLockBox() : +_done(this, lang(lng_about_done), st::langsCloseButton), +_hiding(false), a_opacity(0, 1) { + + bool haveTestLang = (cLang() == languageTest); + + int32 opts[] = { 60, 300, 3600, 18000 }, cnt = sizeof(opts) / sizeof(opts[0]); + + _width = st::langsWidth; + _height = st::addContactTitleHeight + st::langsPadding.top() + st::langsPadding.bottom() + cnt * (st::langPadding.top() + st::rbDefFlat.height + st::langPadding.bottom()) + _done.height(); + + int32 y = st::addContactTitleHeight + st::langsPadding.top(); + _options.reserve(cnt); + for (int32 i = 0; i < cnt; ++i) { + int32 v = opts[i]; + _options.push_back(new FlatRadiobutton(this, qsl("autolock"), v, (v % 3600) ? lng_passcode_autolock_minutes(lt_count, v / 60) : lng_passcode_autolock_hours(lt_count, v / 3600), (cAutoLock() == v), st::langButton)); + _options.back()->move(st::langsPadding.left() + st::langPadding.left(), y + st::langPadding.top()); + y += st::langPadding.top() + _options.back()->height() + st::langPadding.bottom(); + connect(_options.back(), SIGNAL(changed()), this, SLOT(onChange())); + } + + _done.move(0, _height - _done.height()); + + connect(&_done, SIGNAL(clicked()), this, SLOT(onClose())); + + resize(_width, _height); + + showAll(); + _cache = myGrab(this, rect()); + hideAll(); +} + +void AutoLockBox::hideAll() { + _done.hide(); + for (int32 i = 0, l = _options.size(); i < l; ++i) { + _options[i]->hide(); + } +} + +void AutoLockBox::showAll() { + _done.show(); + for (int32 i = 0, l = _options.size(); i < l; ++i) { + _options[i]->show(); + } +} + +void AutoLockBox::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Escape) { + onClose(); + } +} + +void AutoLockBox::parentResized() { + QSize s = parentWidget()->size(); + setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height); + update(); +} + +void AutoLockBox::paintEvent(QPaintEvent *e) { + QPainter p(this); + if (_cache.isNull()) { + if (!_hiding || a_opacity.current() > 0.01) { + // fill bg + p.fillRect(0, 0, _width, _height, st::boxBG->b); + + // paint shadows + p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b); + + // draw box title / text + p.setFont(st::addContactTitleFont->f); + p.setPen(st::black->p); + p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, lang(lng_languages)); + } + } else { + p.setOpacity(a_opacity.current()); + p.drawPixmap(0, 0, _cache); + } +} + +void AutoLockBox::animStep(float64 ms) { + if (ms >= 1) { + a_opacity.finish(); + _cache = QPixmap(); + if (!_hiding) { + showAll(); + setFocus(); + } + } else { + a_opacity.update(ms, anim::linear); + } + update(); +} + +void AutoLockBox::onChange() { + if (isHidden()) return; + + for (int32 i = 0, l = _options.size(); i < l; ++i) { + int32 v = _options[i]->val(); + if (_options[i]->checked()) { + cSetAutoLock(v); + Local::writeUserSettings(); + } + } + App::wnd()->checkAutoLock(); + onClose(); +} + +void AutoLockBox::onClose() { + emit closed(); +} + +void AutoLockBox::startHide() { + _hiding = true; + if (_cache.isNull()) { + _cache = myGrab(this, rect()); + hideAll(); + } + a_opacity.start(0); +} + +AutoLockBox::~AutoLockBox() { + for (int32 i = 0, l = _options.size(); i < l; ++i) { + delete _options[i]; + } +} diff --git a/Telegram/SourceFiles/boxes/autolockbox.h b/Telegram/SourceFiles/boxes/autolockbox.h new file mode 100644 index 000000000..727c20794 --- /dev/null +++ b/Telegram/SourceFiles/boxes/autolockbox.h @@ -0,0 +1,53 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "layerwidget.h" + +class AutoLockBox : public LayeredWidget { + Q_OBJECT + +public: + + AutoLockBox(); + void parentResized(); + void animStep(float64 ms); + void keyPressEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *e); + void startHide(); + ~AutoLockBox(); + +public slots: + + void onChange(); + void onClose(); + +private: + + void hideAll(); + void showAll(); + + QVector _options; + int32 _width, _height; + BottomButton _done; + + bool _hiding; + QPixmap _cache; + + anim::fvalue a_opacity; +}; diff --git a/Telegram/SourceFiles/boxes/connectionbox.cpp b/Telegram/SourceFiles/boxes/connectionbox.cpp index b8ef5d458..d8b8cb487 100644 --- a/Telegram/SourceFiles/boxes/connectionbox.cpp +++ b/Telegram/SourceFiles/boxes/connectionbox.cpp @@ -18,6 +18,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "lang.h" +#include "localstorage.h" + #include "connectionbox.h" #include "mainwidget.h" #include "window.h" @@ -203,7 +205,7 @@ void ConnectionBox::onSave() { QNetworkProxyFactory::setUseSystemConfiguration(false); QNetworkProxyFactory::setUseSystemConfiguration(true); } - App::writeConfig(); + Local::writeSettings(); MTP::restart(); reinitImageLinkManager(); emit closed(); diff --git a/Telegram/SourceFiles/boxes/downloadpathbox.cpp b/Telegram/SourceFiles/boxes/downloadpathbox.cpp index f9a4a7bc6..5981baf2e 100644 --- a/Telegram/SourceFiles/boxes/downloadpathbox.cpp +++ b/Telegram/SourceFiles/boxes/downloadpathbox.cpp @@ -18,6 +18,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "lang.h" +#include "localstorage.h" + #include "downloadpathbox.h" #include "gui/filedialog.h" @@ -181,7 +183,7 @@ void DownloadPathBox::onEditPath() { void DownloadPathBox::onSave() { cSetDownloadPath(_defaultRadio.checked() ? QString() : (_tempRadio.checked() ? qsl("tmp") : _path)); - App::writeUserConfig(); + Local::writeUserSettings(); emit closed(); } diff --git a/Telegram/SourceFiles/boxes/languagebox.cpp b/Telegram/SourceFiles/boxes/languagebox.cpp index 6a36e3c88..25a973fb2 100644 --- a/Telegram/SourceFiles/boxes/languagebox.cpp +++ b/Telegram/SourceFiles/boxes/languagebox.cpp @@ -18,6 +18,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "lang.h" +#include "localstorage.h" + #include "languagebox.h" #include "confirmbox.h" #include "mainwidget.h" @@ -183,7 +185,7 @@ void LanguageBox::onSave() { for (int32 i = 0, l = _langs.size(); i < l; ++i) { if (_langs[i]->checked()) { cSetLang(_langs[i]->val()); - App::writeConfig(); + Local::writeSettings(); cSetRestarting(true); cSetRestartingToSettings(true); App::quit(); diff --git a/Telegram/SourceFiles/boxes/passcodebox.cpp b/Telegram/SourceFiles/boxes/passcodebox.cpp new file mode 100644 index 000000000..c6e6de55c --- /dev/null +++ b/Telegram/SourceFiles/boxes/passcodebox.cpp @@ -0,0 +1,279 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "lang.h" + +#include "passcodebox.h" +#include "window.h" + +#include "localstorage.h" + +PasscodeBox::PasscodeBox(bool turningOff) : _turningOff(turningOff), +_about(st::addContactWidth - st::addContactPadding.left() - st::addContactPadding.right()), +_saveButton(this, lang(lng_settings_save), st::btnSelectDone), +_cancelButton(this, lang(lng_cancel), st::btnSelectCancel), +_oldPasscode(this, st::inpAddContact, lang(lng_passcode_enter_old)), +_newPasscode(this, st::inpAddContact, lang(lng_passcode_enter_new)), +_reenterPasscode(this, st::inpAddContact, lang(lng_passcode_confirm_new)), +a_opacity(0, 1), _hiding(false) { + + _width = st::addContactWidth; + _about.setRichText(st::usernameFont, lang(lng_passcode_about)); + int32 aboutHeight = _about.countHeight(_width - st::addContactPadding.left() - st::addContactPadding.right()); + _oldPasscode.setEchoMode(QLineEdit::Password); + _newPasscode.setEchoMode(QLineEdit::Password); + _reenterPasscode.setEchoMode(QLineEdit::Password); + if (turningOff) { + _oldPasscode.show(); + _boxTitle = lang(lng_passcode_remove); + _height = st::addContactTitleHeight + st::addContactPadding.top() + 1 * _oldPasscode.height() + st::usernameSkip + aboutHeight + st::addContactPadding.bottom() + _saveButton.height(); + } else { + if (cHasPasscode()) { + _oldPasscode.show(); + _boxTitle = lang(lng_passcode_change); + _height = st::addContactTitleHeight + st::addContactPadding.top() + 3 * _oldPasscode.height() + st::usernameSkip * 2 + 1 * st::addContactDelta + aboutHeight + st::addContactPadding.bottom() + _saveButton.height(); + } else { + _oldPasscode.hide(); + _boxTitle = lang(lng_passcode_create); + _height = st::addContactTitleHeight + st::addContactPadding.top() + 2 * _oldPasscode.height() + st::usernameSkip + 1 * st::addContactDelta + aboutHeight + st::addContactPadding.bottom() + _saveButton.height(); + } + } + + _oldPasscode.setGeometry(st::addContactPadding.left(), st::addContactTitleHeight + st::addContactPadding.top(), _width - st::addContactPadding.left() - st::addContactPadding.right(), _oldPasscode.height()); + _newPasscode.setGeometry(st::addContactPadding.left(), _oldPasscode.y() + ((turningOff || cHasPasscode()) ? (_oldPasscode.height() + st::usernameSkip) : 0), _oldPasscode.width(), _oldPasscode.height()); + _reenterPasscode.setGeometry(st::addContactPadding.left(), _newPasscode.y() + _newPasscode.height() + st::addContactDelta, _newPasscode.width(), _newPasscode.height()); + + int32 buttonTop = _height - _cancelButton.height(); + _cancelButton.move(0, buttonTop); + _saveButton.move(_width - _saveButton.width(), buttonTop); + + connect(&_saveButton, SIGNAL(clicked()), this, SLOT(onSave())); + connect(&_cancelButton, SIGNAL(clicked()), this, SLOT(onCancel())); + + _badOldTimer.setSingleShot(true); + connect(&_badOldTimer, SIGNAL(timeout()), this, SLOT(onBadOldPasscode())); + + connect(&_oldPasscode, SIGNAL(changed()), this, SLOT(onOldChanged())); + connect(&_newPasscode, SIGNAL(changed()), this, SLOT(onNewChanged())); + connect(&_reenterPasscode, SIGNAL(changed()), this, SLOT(onNewChanged())); + + resize(_width, _height); + + showAll(); + _cache = myGrab(this, rect()); + hideAll(); +} + +void PasscodeBox::hideAll() { + _oldPasscode.hide(); + _newPasscode.hide(); + _reenterPasscode.hide(); + _saveButton.hide(); + _cancelButton.hide(); +} + +void PasscodeBox::showAll() { + if (_turningOff) { + _oldPasscode.show(); + _newPasscode.hide(); + _reenterPasscode.hide(); + } else { + if (cHasPasscode()) { + _oldPasscode.show(); + } else { + _oldPasscode.hide(); + } + _newPasscode.show(); + _reenterPasscode.show(); + } + _saveButton.show(); + _cancelButton.show(); +} + +void PasscodeBox::keyPressEvent(QKeyEvent *e) { + if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) { + if (_oldPasscode.hasFocus()) { + if (_turningOff) { + onSave(); + } else { + _newPasscode.setFocus(); + } + } else if (_newPasscode.hasFocus()) { + _reenterPasscode.setFocus(); + } else if (_reenterPasscode.hasFocus()) { + if (cHasPasscode() && _oldPasscode.text().isEmpty()) { + _oldPasscode.setFocus(); + _oldPasscode.notaBene(); + } else if (_newPasscode.text().isEmpty()) { + _newPasscode.setFocus(); + _newPasscode.notaBene(); + } else if (_reenterPasscode.text().isEmpty()) { + _reenterPasscode.notaBene(); + } else { + onSave(); + } + } + } else if (e->key() == Qt::Key_Escape) { + onCancel(); + } +} + +void PasscodeBox::parentResized() { + QSize s = parentWidget()->size(); + setGeometry((s.width() - _width) / 2, (s.height() - _height) / 2, _width, _height); + update(); +} + +void PasscodeBox::paintEvent(QPaintEvent *e) { + QPainter p(this); + if (_cache.isNull()) { + if (!_hiding || a_opacity.current() > 0.01) { + // fill bg + p.fillRect(QRect(QPoint(0, 0), size()), st::boxBG->b); + + // paint shadows + p.fillRect(0, st::addContactTitleHeight, _width, st::scrollDef.topsh, st::scrollDef.shColor->b); + p.fillRect(0, size().height() - st::btnSelectCancel.height - st::scrollDef.bottomsh, _width, st::scrollDef.bottomsh, st::scrollDef.shColor->b); + + p.setPen(st::usernameColor->p); + _about.draw(p, st::addContactPadding.left(), (_turningOff ? _oldPasscode : _reenterPasscode).y() + _oldPasscode.height() + st::usernameSkip, _width - st::addContactPadding.left() - st::addContactPadding.right()); + + if (!_oldError.isEmpty()) { + p.setPen(st::setErrColor->p); + p.drawText(QRect(0, _oldPasscode.y() + _oldPasscode.height(), _width, st::usernameSkip), _oldError, style::al_center); + } + + if (!_newError.isEmpty()) { + p.setPen(st::setErrColor->p); + p.drawText(QRect(0, _reenterPasscode.y() + _reenterPasscode.height(), _width, st::usernameSkip), _newError, style::al_center); + } + + // paint button sep + p.fillRect(st::btnSelectCancel.width, size().height() - st::btnSelectCancel.height, st::lineWidth, st::btnSelectCancel.height, st::btnSelectSep->b); + + // draw box title / text + p.setPen(st::black->p); + p.setFont(st::addContactTitleFont->f); + p.drawText(st::addContactTitlePos.x(), st::addContactTitlePos.y() + st::addContactTitleFont->ascent, _boxTitle); + } + } else { + p.setOpacity(a_opacity.current()); + p.drawPixmap(0, 0, _cache); + } +} + +void PasscodeBox::animStep(float64 dt) { + if (dt >= 1) { + a_opacity.finish(); + _cache = QPixmap(); + if (!_hiding) { + showAll(); + if (_oldPasscode.isHidden()) { + _newPasscode.setFocus(); + } else { + _oldPasscode.setFocus(); + } + } + } else { + a_opacity.update(dt, anim::linear); + } + update(); +} + +void PasscodeBox::onSave() { + QString old = _oldPasscode.text(), pwd = _newPasscode.text(), conf = _reenterPasscode.text(); + if (_turningOff || cHasPasscode()) { + if (Local::checkPasscode(old.toUtf8())) { + if (_turningOff) pwd = conf = QString(); + } else { + _oldPasscode.setDisabled(true); + _newPasscode.setDisabled(true); + _reenterPasscode.setDisabled(true); + _saveButton.setDisabled(true); + _oldError = QString(); + update(); + _badOldTimer.start(WrongPasscodeTimeout); + return; + } + } + if (!_turningOff && pwd.isEmpty()) { + _newPasscode.setFocus(); + _newPasscode.notaBene(); + return; + } + if (pwd != conf) { + _reenterPasscode.setFocus(); + _reenterPasscode.notaBene(); + if (!conf.isEmpty()) { + _newError = lang(lng_passcode_differ); + update(); + } + } else if (!_turningOff && cHasPasscode() && old == pwd) { + _newPasscode.setFocus(); + _newPasscode.notaBene(); + _newError = lang(lng_passcode_is_same); + update(); + } else { + Local::setPasscode(pwd.toUtf8()); + App::wnd()->checkAutoLock(); + App::wnd()->getTitle()->showUpdateBtn(); + emit closed(); + } +} + +void PasscodeBox::onBadOldPasscode() { + _oldPasscode.setDisabled(false); + _newPasscode.setDisabled(false); + _reenterPasscode.setDisabled(false); + _saveButton.setDisabled(false); + _oldPasscode.selectAll(); + _oldPasscode.setFocus(); + _oldPasscode.notaBene(); + _oldError = lang(lng_passcode_wrong); + update(); +} + +void PasscodeBox::onOldChanged() { + if (!_oldError.isEmpty()) { + _oldError = QString(); + update(); + } +} + +void PasscodeBox::onNewChanged() { + if (!_newError.isEmpty()) { + _newError = QString(); + update(); + } +} + +void PasscodeBox::onCancel() { + emit closed(); +} + +void PasscodeBox::startHide() { + _hiding = true; + if (_cache.isNull()) { + _cache = myGrab(this, rect()); + hideAll(); + } + a_opacity.start(0); +} + +PasscodeBox::~PasscodeBox() { +} diff --git a/Telegram/SourceFiles/boxes/passcodebox.h b/Telegram/SourceFiles/boxes/passcodebox.h new file mode 100644 index 000000000..7e1d33b3f --- /dev/null +++ b/Telegram/SourceFiles/boxes/passcodebox.h @@ -0,0 +1,64 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "layerwidget.h" + +class PasscodeBox : public LayeredWidget { + Q_OBJECT + +public: + + PasscodeBox(bool turningOff = false); + void parentResized(); + void animStep(float64 dt); + void keyPressEvent(QKeyEvent *e); + void paintEvent(QPaintEvent *e); + void startHide(); + ~PasscodeBox(); + +public slots: + + void onSave(); + void onBadOldPasscode(); + void onOldChanged(); + void onNewChanged(); + void onCancel(); + +private: + + void hideAll(); + void showAll(); + + bool _turningOff; + + QString _boxTitle; + Text _about; + + int32 _width, _height; + FlatButton _saveButton, _cancelButton; + FlatInput _oldPasscode, _newPasscode, _reenterPasscode; + + QPixmap _cache; + + anim::fvalue a_opacity; + bool _hiding; + + QTimer _badOldTimer; + QString _oldError, _newError; +}; diff --git a/Telegram/SourceFiles/boxes/photocropbox.cpp b/Telegram/SourceFiles/boxes/photocropbox.cpp index fd975ccba..9d30e1ba4 100644 --- a/Telegram/SourceFiles/boxes/photocropbox.cpp +++ b/Telegram/SourceFiles/boxes/photocropbox.cpp @@ -19,7 +19,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "style.h" #include "lang.h" -#include "app.h" #include "application.h" #include "mainwidget.h" #include "photocropbox.h" diff --git a/Telegram/SourceFiles/boxes/photosendbox.cpp b/Telegram/SourceFiles/boxes/photosendbox.cpp index 92443cf62..11bcf1ac9 100644 --- a/Telegram/SourceFiles/boxes/photosendbox.cpp +++ b/Telegram/SourceFiles/boxes/photosendbox.cpp @@ -19,7 +19,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "style.h" #include "lang.h" -#include "app.h" +#include "localstorage.h" + #include "mainwidget.h" #include "photosendbox.h" @@ -205,8 +206,10 @@ void PhotoSendBox::onSend(bool ctrlShiftEnter) { if (App::main()) App::main()->confirmShareContact(ctrlShiftEnter, _phone, _fname, _lname); } else { if (!_compressed.isHidden()) { - cSetCompressPastedImage(_compressed.checked()); - App::writeUserConfig(); + if (_compressed.checked() != cCompressPastedImage()) { + cSetCompressPastedImage(_compressed.checked()); + Local::writeUserSettings(); + } } if (_compressed.isHidden() || _compressed.checked()) { _img->ctrlShiftEnter = ctrlShiftEnter; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 46651e840..aade792c0 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -17,8 +17,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -static const int32 AppVersion = 7018; -static const wchar_t *AppVersionStr = L"0.7.18"; +static const int32 AppVersion = 7019; +static const wchar_t *AppVersionStr = L"0.7.19"; static const bool DevChannel = true; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; @@ -123,6 +123,8 @@ enum { UpdateDelayConstPart = 8 * 3600, // 8 hour min time between update check requests UpdateDelayRandPart = 8 * 3600, // 8 hour max - min time between update check requests + + WrongPasscodeTimeout = 1500, }; inline bool isServiceUser(uint64 id) { diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 33b50dc7a..8d0edaf24 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1544,6 +1544,8 @@ bool DialogsWidget::addNewContact(int32 uid, bool show) { } void DialogsWidget::onListScroll() { +// if (!App::self()) return; + list.loadPeerPhotos(scroll.scrollTop()); if (list.state() == DialogsListWidget::SearchedState) { if (scroll.scrollTop() > (list.searchList().size() + list.filteredList().size() + list.peopleList().size()) * st::dlgHeight - PreloadHeightsCount * scroll.height()) { diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp index d5cc525f0..f79317068 100644 --- a/Telegram/SourceFiles/dropdown.cpp +++ b/Telegram/SourceFiles/dropdown.cpp @@ -496,7 +496,7 @@ void EmojiPanInner::mouseReleaseEvent(QMouseEvent *e) { } void EmojiPanInner::onSaveConfig() { - App::writeUserConfig(); + Local::writeUserSettings(); } void EmojiPanInner::mouseMoveEvent(QMouseEvent *e) { @@ -871,7 +871,7 @@ void EmojiPan::onTabChange() { else if (_stickers.checked()) newTab = dbietStickers; if (newTab != cEmojiTab()) { cSetEmojiTab(newTab); - App::writeUserConfig(); + Local::writeUserSettings(); _scroll.scrollToY(0); } _inner.showEmojiPack(newTab); diff --git a/Telegram/SourceFiles/gui/filedialog.cpp b/Telegram/SourceFiles/gui/filedialog.cpp index 969833b78..0fc5348ae 100644 --- a/Telegram/SourceFiles/gui/filedialog.cpp +++ b/Telegram/SourceFiles/gui/filedialog.cpp @@ -18,7 +18,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "gui/filedialog.h" -#include "app.h" #include "application.h" void filedialogInit() { diff --git a/Telegram/SourceFiles/gui/text.cpp b/Telegram/SourceFiles/gui/text.cpp index fb14d77e8..cf32afb49 100644 --- a/Telegram/SourceFiles/gui/text.cpp +++ b/Telegram/SourceFiles/gui/text.cpp @@ -19,7 +19,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "text.h" #include "lang.h" -#include "app.h" #include diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index dc169aa8a..86f11b378 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -25,6 +25,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "boxes/photosendbox.h" #include "mainwidget.h" #include "window.h" +#include "passcodewidget.h" +#include "window.h" #include "fileuploader.h" #include "localstorage.h" @@ -554,7 +556,7 @@ void HistoryList::dragActionFinish(const QPoint &screenPos, Qt::MouseButton butt uint32 sel = _selected.cbegin().value(); if (sel != FullItemSel && (sel & 0xFFFF) == ((sel >> 16) & 0xFFFF)) { _selected.clear(); - App::main()->activate(); + App::wnd()->setInnerFocus(); } } } @@ -1734,6 +1736,7 @@ void HistoryWidget::activate() { return; } else { App::main()->dialogsActivate(); + return; } } if (_list) { @@ -1926,7 +1929,7 @@ void HistoryWidget::showPeer(const PeerId &peer, MsgId msgId, bool force, bool l clearLoadingAround(); emit peerShown(histPeer); - return activate(); + return App::wnd()->setInnerFocus(); } updateTyping(false); } @@ -2633,7 +2636,7 @@ bool HistoryWidget::animStep(float64 ms) { } onListScroll(); } - activate(); + App::wnd()->setInnerFocus(); } else { a_bgCoord.update(dt1, st::introHideFunc); a_bgAlpha.update(dt1, st::introAlphaHideFunc); @@ -2660,7 +2663,7 @@ void HistoryWidget::onPhotoSelect() { if (cDefaultAttach() != dbidaPhoto) { cSetDefaultAttach(dbidaPhoto); - App::writeUserConfig(); + Local::writeUserSettings(); } QStringList photoExtensions(cPhotoExtensions()); @@ -2688,7 +2691,7 @@ void HistoryWidget::onDocumentSelect() { if (cDefaultAttach() != dbidaDocument) { cSetDefaultAttach(dbidaDocument); - App::writeUserConfig(); + Local::writeUserSettings(); } QStringList photoExtensions(cPhotoExtensions()); diff --git a/Telegram/SourceFiles/intro/intro.cpp b/Telegram/SourceFiles/intro/intro.cpp index d4cb35db5..b4bb82b1d 100644 --- a/Telegram/SourceFiles/intro/intro.cpp +++ b/Telegram/SourceFiles/intro/intro.cpp @@ -19,7 +19,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "lang.h" #include "style.h" -#include "app.h" +#include "localstorage.h" #include "intro/intro.h" #include "intro/introsteps.h" @@ -89,7 +89,7 @@ void IntroWidget::langChangeTo(int32 langId) { void IntroWidget::onChangeLang() { cSetLang(_langChangeTo); - App::writeConfig(); + Local::writeSettings(); cSetRestarting(true); cSetRestartingToSettings(false); App::quit(); @@ -332,8 +332,7 @@ void IntroWidget::mousePressEvent(QMouseEvent *e) { } void IntroWidget::finish(const MTPUser &user, const QImage &photo) { - wnd->setupMain(true); - wnd->startMain(user); + wnd->setupMain(true, &user); if (!photo.isNull()) { App::app()->uploadProfilePhoto(photo, MTP::authedId()); } diff --git a/Telegram/SourceFiles/intro/intro.h b/Telegram/SourceFiles/intro/intro.h index 99914fd3f..87371a9a0 100644 --- a/Telegram/SourceFiles/intro/intro.h +++ b/Telegram/SourceFiles/intro/intro.h @@ -17,9 +17,6 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org */ #pragma once -#include -#include "gui/flatbutton.h" - class Window; class IntroSteps; class IntroPhone; @@ -39,7 +36,7 @@ public: void resizeEvent(QResizeEvent *e); void mousePressEvent(QMouseEvent *e); void keyPressEvent(QKeyEvent *e); - + void updateWideMode(); void animShow(const QPixmap &bgAnimCache, bool back = false); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index fcf5c7530..f5ec61422 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -18,6 +18,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "stdafx.h" #include "localstorage.h" +#include "lang.h" + namespace { typedef quint64 FileKey; @@ -57,7 +59,7 @@ namespace { return result; } - QString _basePath; + QString _basePath, _userBasePath; bool _started = false; _local_inner::Manager *_manager = 0; @@ -66,47 +68,81 @@ namespace { return _manager && !_basePath.isEmpty(); } - bool keyAlreadyUsed(QString &name) { - name += '0'; - if (QFileInfo(name).exists()) return true; - name[name.size() - 1] = '1'; - return QFileInfo(name).exists(); + bool _userWorking() { + return _manager && !_basePath.isEmpty() && !_userBasePath.isEmpty(); } - FileKey genKey() { - if (!_working()) return 0; + enum FileOptions { + UserPath = 0x01, + SafePath = 0x02, + }; + + bool keyAlreadyUsed(QString &name, int options = UserPath | SafePath) { + name += '0'; + if (QFileInfo(name).exists()) return true; + if (options & SafePath) { + name[name.size() - 1] = '1'; + return QFileInfo(name).exists(); + } + return false; + } + + FileKey genKey(int options = UserPath | SafePath) { + if (options & UserPath) { + if (!_userWorking()) return 0; + } else { + if (!_working()) return 0; + } FileKey result; - QString path; - path.reserve(_basePath.size() + 0x11); - path += _basePath; + QString base = (options & UserPath) ? _userBasePath : _basePath, path; + path.reserve(base.size() + 0x11); + path += base; do { result = MTP::nonce(); - path.resize(_basePath.size()); + path.resize(base.size()); path += toFilePart(result); - } while (!result || keyAlreadyUsed(path)); + } while (!result || keyAlreadyUsed(path, options)); return result; } - void clearKey(const FileKey &key, bool safe = true) { - if (!_working()) return; + void clearKey(const FileKey &key, int options = UserPath | SafePath) { + if (options & UserPath) { + if (!_userWorking()) return; + } else { + if (!_working()) return; + } - QString name; - name.reserve(_basePath.size() + 0x11); - name += _basePath; - name += toFilePart(key); - name += '0'; + QString base = (options & UserPath) ? _userBasePath : _basePath, name; + name.reserve(base.size() + 0x11); + name.append(base).append(toFilePart(key)).append('0'); QFile::remove(name); - if (safe) { + if (options & SafePath) { name[name.size() - 1] = '1'; QFile::remove(name); } } - QByteArray _passKeySalt, _passKeyEncrypted; + bool _checkStreamStatus(QDataStream &stream) { + if (stream.status() != QDataStream::Ok) { + LOG(("Bad data stream status: %1").arg(stream.status())); + return false; + } + return true; + } - mtpAuthKey _oldKey, _passKey, _localKey; + uint32 _dateTimeSize() { + return (sizeof(qint64) + sizeof(quint32) + sizeof(qint8)); + } + + uint32 _stringSize(const QString &str) { + return sizeof(quint32) + str.size() * sizeof(ushort); + } + + QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted; + + mtpAuthKey _oldKey, _settingsKey, _passKey, _localKey; void createLocalKey(const QByteArray &pass, QByteArray *salt, mtpAuthKey *result) { uchar key[LocalEncryptKeySize] = { 0 }; int32 iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password @@ -169,20 +205,24 @@ namespace { }; struct FileWriteDescriptor { - FileWriteDescriptor(const FileKey &key, bool safe = true) : dataSize(0) { - init(toFilePart(key), safe); + FileWriteDescriptor(const FileKey &key, int options = UserPath | SafePath) : dataSize(0) { + init(toFilePart(key), options); } - FileWriteDescriptor(const QString &name, bool safe = true) : dataSize(0) { - init(name, safe); + FileWriteDescriptor(const QString &name, int options = UserPath | SafePath) : dataSize(0) { + init(name, options); } - void init(const QString &name, bool safe) { - if (!_working()) return; + void init(const QString &name, int options) { + if (options & UserPath) { + if (!_userWorking()) return; + } else { + if (!_working()) return; + } // detect order of read attempts and file version QString toTry[2]; - toTry[0] = _basePath + name + '0'; - if (safe) { - toTry[1] = _basePath + name + '1'; + toTry[0] = ((options & UserPath) ? _userBasePath : _basePath) + name + '0'; + if (options & SafePath) { + toTry[1] = ((options & UserPath) ? _userBasePath : _basePath) + name + '1'; QFileInfo toTry0(toTry[0]); QFileInfo toTry1(toTry[1]); if (toTry0.exists()) { @@ -224,7 +264,7 @@ namespace { return true; } - QByteArray prepareEncrypted(EncryptedDescriptor &data, const mtpAuthKey &key = _localKey) { + static QByteArray prepareEncrypted(EncryptedDescriptor &data, const mtpAuthKey &key = _localKey) { data.finish(); QByteArray &toEncrypt(data.data); @@ -274,16 +314,20 @@ namespace { } }; - bool readFile(FileReadDescriptor &result, const QString &name, bool safe = true) { - if (!_working()) return false; - + bool readFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath) { + if (options & UserPath) { + if (!_userWorking()) return false; + } else { + if (!_working()) return false; + } + // detect order of read attempts QString toTry[2]; - toTry[0] = _basePath + name + '0'; - if (safe) { + toTry[0] = ((options & UserPath) ? _userBasePath : _basePath) + name + '0'; + if (options & SafePath) { QFileInfo toTry0(toTry[0]); if (toTry0.exists()) { - toTry[1] = _basePath + name + '1'; + toTry[1] = ((options & UserPath) ? _userBasePath : _basePath) + name + '1'; QFileInfo toTry1(toTry[1]); if (toTry1.exists()) { QDateTime mod0 = toTry0.lastModified(), mod1 = toTry1.lastModified(); @@ -380,7 +424,7 @@ namespace { aesDecryptLocal(encryptedData, decrypted.data(), fullLen, &key, encryptedKey); uchar sha1Buffer[20]; if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), encryptedKey, 16)) { - LOG(("App Error: bad decrypt key, data not decrypted")); + LOG(("App Info: bad decrypt key, data not decrypted - incorrect password?")); return false; } @@ -402,16 +446,16 @@ namespace { return true; } - - bool readEncryptedFile(FileReadDescriptor &result, const QString &name, bool safe = true) { - if (!readFile(result, name, safe)) { + + bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath, const mtpAuthKey &key = _localKey) { + if (!readFile(result, name, options)) { return false; } QByteArray encrypted; result.stream >> encrypted; EncryptedDescriptor data; - if (!decryptLocal(data, encrypted)) { + if (!decryptLocal(data, encrypted, key)) { result.stream.setDevice(0); if (result.buffer.isOpen()) result.buffer.close(); result.buffer.setBuffer(0); @@ -433,6 +477,12 @@ namespace { return true; } + bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, int options = UserPath | SafePath, const mtpAuthKey &key = _localKey) { + return readEncryptedFile(result, toFilePart(fkey), options, key); + } + + FileKey _dataNameKey = 0; + enum { // Local Storage Keys lskUserMap = 0, lskDraft, // data: PeerId peer @@ -443,6 +493,7 @@ namespace { lskAudios, // data: StorageKey location lskRecentStickers, // no data lskBackground, // no data + lskUserSettings, // no data }; typedef QMap DraftsMap; @@ -462,6 +513,8 @@ namespace { FileKey _backgroundKey = 0; bool _backgroundWasRead = false; + FileKey _userSettingsKey = 0; + typedef QPair FileDesc; // file, size typedef QMap StorageMap; StorageMap _imagesMap, _stickersMap, _audiosMap; @@ -502,7 +555,7 @@ namespace { quint32 size = 0; for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { // location + type + namelen + name + date + size - size += sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + i.value().name.size() * sizeof(ushort) + (sizeof(qint64) + sizeof(quint32) + sizeof(qint8)) + sizeof(quint32); + size += sizeof(quint64) * 2 + sizeof(quint32) + _stringSize(i.value().name) + _dateTimeSize() + sizeof(quint32); } EncryptedDescriptor data(size); for (FileLocations::const_iterator i = _fileLocations.cbegin(); i != _fileLocations.cend(); ++i) { @@ -515,7 +568,7 @@ namespace { void _readLocations() { FileReadDescriptor locations; - if (!readEncryptedFile(locations, toFilePart(_locationsKey))) { + if (!readEncryptedFile(locations, _locationsKey)) { clearKey(_locationsKey); _locationsKey = 0; _writeMap(); @@ -540,24 +593,653 @@ namespace { } } + mtpDcOptions *_dcOpts = 0; + bool _readSetting(quint32 blockId, QDataStream &stream, int version) { + switch (blockId) { + case dbiDcOption: { + quint32 dcId, port; + QString host, ip; + stream >> dcId >> host >> ip >> port; + if (!_checkStreamStatus(stream)) return false; + + if (_dcOpts) _dcOpts->insert(dcId, mtpDcOption(dcId, host.toUtf8().constData(), ip.toUtf8().constData(), port)); + } break; + + case dbiMaxGroupCount: { + qint32 maxSize; + stream >> maxSize; + if (!_checkStreamStatus(stream)) return false; + + cSetMaxGroupCount(maxSize); + } break; + + case dbiUser: { + quint32 dcId; + qint32 uid; + stream >> uid >> dcId; + if (!_checkStreamStatus(stream)) return false; + + DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(uid)); + MTP::configure(dcId, uid); + } break; + + case dbiKey: { + qint32 dcId; + quint32 key[64]; + stream >> dcId; + stream.readRawData((char*)key, 256); + if (!_checkStreamStatus(stream)) return false; + + DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(mb(key, 256).str())); + dcId = dcId % _mtp_internal::dcShift; + mtpAuthKeyPtr keyPtr(new mtpAuthKey()); + keyPtr->setKey(key); + keyPtr->setDC(dcId); + + MTP::setKey(dcId, keyPtr); + } break; + + case dbiAutoStart: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetAutoStart(v == 1); + } break; + + case dbiStartMinimized: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetStartMinimized(v == 1); + } break; + + case dbiSendToMenu: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetSendToMenu(v == 1); + } break; + + case dbiSoundNotify: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetSoundNotify(v == 1); + } break; + + case dbiDesktopNotify: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetDesktopNotify(v == 1); + } break; + + case dbiWorkMode: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + switch (v) { + case dbiwmTrayOnly: cSetWorkMode(dbiwmTrayOnly); break; + case dbiwmWindowOnly: cSetWorkMode(dbiwmWindowOnly); break; + default: cSetWorkMode(dbiwmWindowAndTray); break; + }; + } break; + + case dbiConnectionType: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + switch (v) { + case dbictHttpProxy: + case dbictTcpProxy: { + ConnectionProxy p; + qint32 port; + stream >> p.host >> port >> p.user >> p.password; + if (!_checkStreamStatus(stream)) return false; + + p.port = uint32(port); + cSetConnectionProxy(p); + } + cSetConnectionType(DBIConnectionType(v)); + break; + case dbictHttpAuto: + default: cSetConnectionType(dbictAuto); break; + }; + } break; + + case dbiSeenTrayTooltip: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetSeenTrayTooltip(v == 1); + } break; + + case dbiAutoUpdate: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetAutoUpdate(v == 1); + } break; + + case dbiLastUpdateCheck: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetLastUpdateCheck(v); + } break; + + case dbiScale: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + DBIScale s = cRealScale(); + switch (v) { + case dbisAuto: s = dbisAuto; break; + case dbisOne: s = dbisOne; break; + case dbisOneAndQuarter: s = dbisOneAndQuarter; break; + case dbisOneAndHalf: s = dbisOneAndHalf; break; + case dbisTwo: s = dbisTwo; break; + } + if (cRetina()) s = dbisOne; + cSetConfigScale(s); + cSetRealScale(s); + } break; + + case dbiLang: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + if (v == languageTest || (v >= 0 && v < languageCount)) { + cSetLang(v); + } + } break; + + case dbiLangFile: { + QString v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetLangFile(v); + } break; + + case dbiWindowPosition: { + TWindowPos pos; + stream >> pos.x >> pos.y >> pos.w >> pos.h >> pos.moncrc >> pos.maximized; + if (!_checkStreamStatus(stream)) return false; + + cSetWindowPos(pos); + } break; + + case dbiLoggedPhoneNumber: { + QString v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetLoggedPhoneNumber(v); + } break; + + case dbiMutePeer: { // deprecated + quint64 peerId; + stream >> peerId; + if (!_checkStreamStatus(stream)) return false; + } break; + + case dbiMutedPeers: { // deprecated + quint32 count; + stream >> count; + if (!_checkStreamStatus(stream)) return false; + + for (uint32 i = 0; i < count; ++i) { + quint64 peerId; + stream >> peerId; + } + if (!_checkStreamStatus(stream)) return false; + } break; + + case dbiSendKey: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetCtrlEnter(v == dbiskCtrlEnter); + } break; + + case dbiCatsAndDogs: { // deprecated + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + } break; + + case dbiTileBackground: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetTileBackground(v == 1); + } break; + + case dbiAutoLock: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetAutoLock(v); + } break; + + case dbiReplaceEmojis: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetReplaceEmojis(v == 1); + } break; + + case dbiDefaultAttach: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + switch (v) { + case dbidaPhoto: cSetDefaultAttach(dbidaPhoto); break; + default: cSetDefaultAttach(dbidaDocument); break; + } + } break; + + case dbiNotifyView: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + switch (v) { + case dbinvShowNothing: cSetNotifyView(dbinvShowNothing); break; + case dbinvShowName: cSetNotifyView(dbinvShowName); break; + default: cSetNotifyView(dbinvShowPreview); break; + } + } break; + + case dbiAskDownloadPath: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetAskDownloadPath(v == 1); + } break; + + case dbiDownloadPath: { + QString v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetDownloadPath(v); + } break; + + case dbiCompressPastedImage: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetCompressPastedImage(v == 1); + } break; + + case dbiEmojiTab: { + qint32 v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + switch (v) { + case dbietRecent: cSetEmojiTab(dbietRecent); break; + case dbietPeople: cSetEmojiTab(dbietPeople); break; + case dbietNature: cSetEmojiTab(dbietNature); break; + case dbietObjects: cSetEmojiTab(dbietObjects); break; + case dbietPlaces: cSetEmojiTab(dbietPlaces); break; + case dbietSymbols: cSetEmojiTab(dbietSymbols); break; + case dbietStickers: cSetEmojiTab(dbietStickers); break; + } + } break; + + case dbiRecentEmojis: { + RecentEmojiPreload v; + stream >> v; + if (!_checkStreamStatus(stream)) return false; + + cSetRecentEmojisPreload(v); + } break; + + default: + LOG(("App Error: unknown blockId in _readSetting: %1").arg(blockId)); + return false; + } + + return true; + } + + bool _readOldSettings(bool remove = true) { + bool result = false; + QFile file(cWorkingDir() + qsl("tdata/config")); + if (file.open(QIODevice::ReadOnly)) { + LOG(("App Info: reading old config..")); + QDataStream stream(&file); + stream.setVersion(QDataStream::Qt_5_1); + + qint32 version = 0; + while (!stream.atEnd()) { + quint32 blockId; + stream >> blockId; + if (!_checkStreamStatus(stream)) break; + + if (blockId == dbiVersion) { + stream >> version; + if (!_checkStreamStatus(stream)) break; + + if (version > AppVersion) break; + } else if (!_readSetting(blockId, stream, version)) { + break; + } + } + file.close(); + result = true; + } + if (remove) file.remove(); + return result; + } + + void _readOldUserSettingsFields(QIODevice *device, qint32 &version) { + QDataStream stream(device); + stream.setVersion(QDataStream::Qt_5_1); + + while (!stream.atEnd()) { + quint32 blockId; + stream >> blockId; + if (!_checkStreamStatus(stream)) { + break; + } + + if (blockId == dbiVersion) { + stream >> version; + if (!_checkStreamStatus(stream)) { + break; + } + + if (version > AppVersion) return; + } else if (blockId == dbiEncryptedWithSalt) { + QByteArray salt, data, decrypted; + stream >> salt >> data; + if (!_checkStreamStatus(stream)) { + break; + } + + if (salt.size() != 32) { + LOG(("App Error: bad salt in old user config encrypted part, size: %1").arg(salt.size())); + continue; + } + + createLocalKey(QByteArray(), &salt, &_oldKey); + + if (data.size() <= 16 || (data.size() & 0x0F)) { + LOG(("App Error: bad encrypted part size in old user config: %1").arg(data.size())); + continue; + } + uint32 fullDataLen = data.size() - 16; + decrypted.resize(fullDataLen); + const char *dataKey = data.constData(), *encrypted = data.constData() + 16; + aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &_oldKey, dataKey); + uchar sha1Buffer[20]; + if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) { + LOG(("App Error: bad decrypt key, data from old user config not decrypted")); + continue; + } + uint32 dataLen = *(const uint32*)decrypted.constData(); + if (dataLen > uint32(decrypted.size()) || dataLen <= fullDataLen - 16 || dataLen < 4) { + LOG(("App Error: bad decrypted part size in old user config: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size())); + continue; + } + decrypted.resize(dataLen); + QBuffer decryptedStream(&decrypted); + decryptedStream.open(QIODevice::ReadOnly); + decryptedStream.seek(4); // skip size + LOG(("App Info: reading encrypted old user config..")); + + _readOldUserSettingsFields(&decryptedStream, version); + } else if (!_readSetting(blockId, stream, version)) { + return; + } + } + } + + bool _readOldUserSettings(bool remove = true) { + bool result = false; + QFile file(cWorkingDir() + cDataFile() + qsl("_config")); + if (file.open(QIODevice::ReadOnly)) { + LOG(("App Info: reading old user config..")); + qint32 version = 0; + + mtpDcOptions dcOpts(cDcOptions()); + _dcOpts = &dcOpts; + _readOldUserSettingsFields(&file, version); + cSetDcOptions(dcOpts); + + file.close(); + result = true; + } + if (remove) file.remove(); + return result; + } + + void _readOldMtpDataFields(QIODevice *device, qint32 &version) { + QDataStream stream(device); + stream.setVersion(QDataStream::Qt_5_1); + + while (!stream.atEnd()) { + quint32 blockId; + stream >> blockId; + if (!_checkStreamStatus(stream)) { + break; + } + + if (blockId == dbiVersion) { + stream >> version; + if (!_checkStreamStatus(stream)) { + break; + } + + if (version > AppVersion) return; + } else if (blockId == dbiEncrypted) { + QByteArray data, decrypted; + stream >> data; + if (!_checkStreamStatus(stream)) { + break; + } + + if (!_oldKey.created()) { + LOG(("MTP Error: reading old encrypted keys without old key!")); + continue; + } + + if (data.size() <= 16 || (data.size() & 0x0F)) { + LOG(("MTP Error: bad encrypted part size in old keys: %1").arg(data.size())); + continue; + } + uint32 fullDataLen = data.size() - 16; + decrypted.resize(fullDataLen); + const char *dataKey = data.constData(), *encrypted = data.constData() + 16; + aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &_oldKey, dataKey); + uchar sha1Buffer[20]; + if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) { + LOG(("MTP Error: bad decrypt key, data from old keys not decrypted")); + continue; + } + uint32 dataLen = *(const uint32*)decrypted.constData(); + if (dataLen > uint32(decrypted.size()) || dataLen <= fullDataLen - 16 || dataLen < 4) { + LOG(("MTP Error: bad decrypted part size in old keys: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size())); + continue; + } + decrypted.resize(dataLen); + QBuffer decryptedStream(&decrypted); + decryptedStream.open(QIODevice::ReadOnly); + decryptedStream.seek(4); // skip size + LOG(("App Info: reading encrypted old keys..")); + + _readOldMtpDataFields(&decryptedStream, version); + } else if (!_readSetting(blockId, stream, version)) { + return; + } + } + } + + bool _readOldMtpData(bool remove = true) { + bool result = false; + QFile file(cWorkingDir() + cDataFile()); + if (file.open(QIODevice::ReadOnly)) { + LOG(("App Info: reading old keys..")); + qint32 version = 0; + + mtpDcOptions dcOpts(cDcOptions()); + _dcOpts = &dcOpts; + _readOldMtpDataFields(&file, version); + cSetDcOptions(dcOpts); + + file.close(); + result = true; + } + if (remove) file.remove(); + return result; + } + + void _writeUserSettings() { + if (!_userSettingsKey) { + _userSettingsKey = genKey(); + _mapChanged = true; + _writeMap(WriteMapFast); + } + + uint32 size = 11 * (sizeof(quint32) + sizeof(qint32)); + size += sizeof(quint32) + _stringSize(cAskDownloadPath() ? QString() : cDownloadPath()); + size += sizeof(quint32) + sizeof(qint32) + cGetRecentEmojis().size() * (sizeof(uint32) + sizeof(ushort)); + + EncryptedDescriptor data(size); + data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter); + data.stream << quint32(dbiTileBackground) << qint32(cTileBackground() ? 1 : 0); + data.stream << quint32(dbiAutoLock) << qint32(cAutoLock()); + data.stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0); + data.stream << quint32(dbiDefaultAttach) << qint32(cDefaultAttach()); + data.stream << quint32(dbiSoundNotify) << qint32(cSoundNotify()); + data.stream << quint32(dbiDesktopNotify) << qint32(cDesktopNotify()); + data.stream << quint32(dbiNotifyView) << qint32(cNotifyView()); + data.stream << quint32(dbiAskDownloadPath) << qint32(cAskDownloadPath()); + data.stream << quint32(dbiDownloadPath) << (cAskDownloadPath() ? QString() : cDownloadPath()); + data.stream << quint32(dbiCompressPastedImage) << qint32(cCompressPastedImage()); + data.stream << quint32(dbiEmojiTab) << qint32(cEmojiTab()); + + RecentEmojiPreload v; + v.reserve(cGetRecentEmojis().size()); + for (RecentEmojiPack::const_iterator i = cGetRecentEmojis().cbegin(), e = cGetRecentEmojis().cend(); i != e; ++i) { + v.push_back(qMakePair(i->first->code, i->second)); + } + data.stream << quint32(dbiRecentEmojis) << v; + + FileWriteDescriptor file(_userSettingsKey); + file.writeEncrypted(data); + } + + void _readUserSettings() { + FileReadDescriptor userSettings; + if (!readEncryptedFile(userSettings, _userSettingsKey)) { + _readOldUserSettings(); + return _writeUserSettings(); + } + + LOG(("App Info: reading encrypted user settings..")); + while (!userSettings.stream.atEnd()) { + quint32 blockId; + userSettings.stream >> blockId; + if (!_checkStreamStatus(userSettings.stream)) { + return _writeUserSettings(); + } + + if (!_readSetting(blockId, userSettings.stream, userSettings.version)) { + return _writeUserSettings(); + } + } + } + + void _writeMtpData() { + FileWriteDescriptor mtp(toFilePart(_dataNameKey), SafePath); + if (!_localKey.created()) { + LOG(("App Error: localkey not created in _writeMtpData()")); + return; + } + + mtpKeysMap keys = MTP::getKeys(); + + quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(quint32); + size += keys.size() * (sizeof(quint32) + sizeof(quint32) + 256); + + EncryptedDescriptor data(size); + data.stream << quint32(dbiUser) << qint32(MTP::authedId()) << quint32(MTP::maindc()); + for (mtpKeysMap::const_iterator i = keys.cbegin(), e = keys.cend(); i != e; ++i) { + data.stream << quint32(dbiKey) << quint32((*i)->getDC()); + (*i)->write(data.stream); + } + + mtp.writeEncrypted(data, _localKey); + } + + void _readMtpData() { + FileReadDescriptor mtp; + if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), SafePath)) { + if (_localKey.created()) { + _readOldMtpData(); + _writeMtpData(); + } + return; + } + + LOG(("App Info: reading encrypted mtp data..")); + while (!mtp.stream.atEnd()) { + quint32 blockId; + mtp.stream >> blockId; + if (!_checkStreamStatus(mtp.stream)) { + return _writeMtpData(); + } + + if (!_readSetting(blockId, mtp.stream, mtp.version)) { + return _writeMtpData(); + } + } + } + Local::ReadMapState _readMap(const QByteArray &pass) { uint64 ms = getms(); QByteArray dataNameUtf8 = cDataFile().toUtf8(); - uint64 dataNameHash[2]; + FileKey dataNameHash[2]; hashMd5(dataNameUtf8.constData(), dataNameUtf8.size(), dataNameHash); - _basePath = cWorkingDir() + qsl("tdata/") + toFilePart(dataNameHash[0]) + QChar('/'); + _dataNameKey = dataNameHash[0]; + _userBasePath = _basePath + toFilePart(_dataNameKey) + QChar('/'); FileReadDescriptor mapData; if (!readFile(mapData, qsl("map"))) { return Local::ReadMapFailed; } + LOG(("App Info: reading map..")); QByteArray salt, keyEncrypted, mapEncrypted; mapData.stream >> salt >> keyEncrypted >> mapEncrypted; - if (mapData.stream.status() != QDataStream::Ok) { - LOG(("App Error: could not read salt / key from map file - corrupted?..").arg(mapData.stream.status())); + if (!_checkStreamStatus(mapData.stream)) { return Local::ReadMapFailed; } + if (salt.size() != LocalEncryptSaltSize) { LOG(("App Error: bad salt in map file, size: %1").arg(salt.size())); return Local::ReadMapFailed; @@ -566,7 +1248,7 @@ namespace { EncryptedDescriptor keyData, map; if (!decryptLocal(keyData, keyEncrypted, _passKey)) { - LOG(("App Error: could not decrypt pass-protected key from map file, maybe bad password..")); + LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password..")); return Local::ReadMapPassNeeded; } uchar key[LocalEncryptKeySize] = { 0 }; @@ -583,12 +1265,13 @@ namespace { LOG(("App Error: could not decrypt map.")); return Local::ReadMapFailed; } + LOG(("App Info: reading encrypted map..")); DraftsMap draftsMap, draftsPositionsMap; DraftsNotReadMap draftsNotReadMap; StorageMap imagesMap, stickersMap, audiosMap; qint64 storageImagesSize = 0, storageStickersSize = 0, storageAudiosSize = 0; - quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0; + quint64 locationsKey = 0, recentStickersKey = 0, backgroundKey = 0, userSettingsKey = 0; while (!map.stream.atEnd()) { quint32 keyType; map.stream >> keyType; @@ -659,12 +1342,14 @@ namespace { case lskBackground: { map.stream >> backgroundKey; } break; + case lskUserSettings: { + map.stream >> userSettingsKey; + } break; default: LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType)); return Local::ReadMapFailed; } - if (map.stream.status() != QDataStream::Ok) { - LOG(("App Error: reading encrypted map bad status: %1").arg(map.stream.status())); + if (!_checkStreamStatus(map.stream)) { return Local::ReadMapFailed; } } @@ -683,6 +1368,7 @@ namespace { _locationsKey = locationsKey; _recentStickersKey = recentStickersKey; _backgroundKey = backgroundKey; + _userSettingsKey = userSettingsKey; _oldMapVersion = mapData.version; if (_oldMapVersion < AppVersion) { _mapChanged = true; @@ -695,6 +1381,9 @@ namespace { _readLocations(); } + _readUserSettings(); + _readMtpData(); + LOG(("Map read time: %1").arg(getms() - ms)); return Local::ReadMapDone; } @@ -706,12 +1395,12 @@ namespace { } _manager->writingMap(); if (!_mapChanged) return; - if (_basePath.isEmpty()) { - LOG(("App Error: _basePath is empty in writeMap()")); + if (_userBasePath.isEmpty()) { + LOG(("App Error: _userBasePath is empty in writeMap()")); return; } - QDir().mkpath(_basePath); + if (!QDir().exists(_userBasePath)) QDir().mkpath(_userBasePath); FileWriteDescriptor map(qsl("map")); if (_passKeySalt.isEmpty() || _passKeyEncrypted.isEmpty()) { @@ -727,7 +1416,7 @@ namespace { EncryptedDescriptor passKeyData(LocalEncryptKeySize); _localKey.write(passKeyData.stream); - _passKeyEncrypted = map.prepareEncrypted(passKeyData, _passKey); + _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, _passKey); } map.writeData(_passKeySalt); map.writeData(_passKeyEncrypted); @@ -739,6 +1428,7 @@ namespace { if (!_stickersMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _stickersMap.size() * (sizeof(quint64) * 3 + sizeof(qint32)); if (!_audiosMap.isEmpty()) mapSize += sizeof(quint32) * 2 + _audiosMap.size() * (sizeof(quint64) * 3 + sizeof(qint32)); if (_locationsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_userSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentStickersKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_backgroundKey) mapSize += sizeof(quint32) + sizeof(quint64); EncryptedDescriptor mapData(mapSize); @@ -775,6 +1465,9 @@ namespace { if (_locationsKey) { mapData.stream << quint32(lskLocations) << quint64(_locationsKey); } + if (_userSettingsKey) { + mapData.stream << quint32(lskUserSettings) << quint64(_userSettingsKey); + } if (_recentStickersKey) { mapData.stream << quint32(lskRecentStickers) << quint64(_recentStickersKey); } @@ -783,8 +1476,6 @@ namespace { } map.writeEncrypted(mapData); - map.finish(); - _mapChanged = false; } @@ -844,14 +1535,6 @@ namespace _local_inner { namespace Local { - mtpAuthKey &oldKey() { - return _oldKey; - } - - void createOldKey(QByteArray *salt) { - createLocalKey(QByteArray(), salt, &_oldKey); - } - void start() { if (!_started) { _started = true; @@ -868,6 +1551,177 @@ namespace Local { } } + void readSettings() { + Local::start(); + + _basePath = cWorkingDir() + qsl("tdata/"); + if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); + + FileReadDescriptor settingsData; + if (!readFile(settingsData, qsl("settings"), SafePath)) { + _readOldSettings(); + _readOldUserSettings(false); // needed further in _readUserSettings + _readOldMtpData(false); // needed further in _readMtpData + return writeSettings(); + } + LOG(("App Info: reading settings..")); + + QByteArray salt, settingsEncrypted; + settingsData.stream >> salt >> settingsEncrypted; + if (!_checkStreamStatus(settingsData.stream)) { + return writeSettings(); + } + + if (salt.size() != LocalEncryptSaltSize) { + LOG(("App Error: bad salt in settings file, size: %1").arg(salt.size())); + return writeSettings(); + } + createLocalKey(QByteArray(), &salt, &_settingsKey); + + EncryptedDescriptor settings; + if (!decryptLocal(settings, settingsEncrypted, _settingsKey)) { + LOG(("App Error: could not decrypt settings from settings file, maybe bad passcode..")); + return writeSettings(); + } + mtpDcOptions dcOpts(cDcOptions()); + _dcOpts = &dcOpts; + LOG(("App Info: reading encrypted settings..")); + while (!settings.stream.atEnd()) { + quint32 blockId; + settings.stream >> blockId; + if (!_checkStreamStatus(settings.stream)) { + return writeSettings(); + } + + if (!_readSetting(blockId, settings.stream, settingsData.version)) { + return writeSettings(); + } + } + if (dcOpts.isEmpty()) { + const BuiltInDc *bdcs = builtInDcs(); + for (int i = 0, l = builtInDcsCount(); i < l; ++i) { + dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port)); + DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); + } + } + cSetDcOptions(dcOpts); + + _settingsSalt = salt; + } + + void writeSettings() { + if (_basePath.isEmpty()) { + LOG(("App Error: _basePath is empty in writeSettings()")); + return; + } + + if (!QDir().exists(_basePath)) QDir().mkpath(_basePath); + + FileWriteDescriptor settings(qsl("settings"), SafePath); + if (_settingsSalt.isEmpty() || !_settingsKey.created()) { + _settingsSalt.resize(LocalEncryptSaltSize); + memset_rand(_settingsSalt.data(), _settingsSalt.size()); + createLocalKey(QByteArray(), &_settingsSalt, &_settingsKey); + } + settings.writeData(_settingsSalt); + + mtpDcOptions dcOpts(cDcOptions()); + if (dcOpts.isEmpty()) { + const BuiltInDc *bdcs = builtInDcs(); + for (int i = 0, l = builtInDcsCount(); i < l; ++i) { + dcOpts.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port)); + DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); + } + cSetDcOptions(dcOpts); + } + + quint32 size = 10 * (sizeof(quint32) + sizeof(qint32)); + for (mtpDcOptions::const_iterator i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { + size += sizeof(quint32) + sizeof(quint32) + sizeof(quint32); + size += _stringSize(QString::fromUtf8(i->host.data(), i->host.size())) + _stringSize(QString::fromUtf8(i->ip.data(), i->ip.size())); + } + size += sizeof(quint32) + _stringSize(cLangFile()); + + size += sizeof(quint32) + sizeof(qint32); + if (cConnectionType() == dbictHttpProxy || cConnectionType() == dbictTcpProxy) { + const ConnectionProxy &proxy(cConnectionProxy()); + size += _stringSize(proxy.host) + sizeof(qint32) + _stringSize(proxy.user) + _stringSize(proxy.password); + } + + size += sizeof(quint32) + sizeof(qint32) * 6; + + EncryptedDescriptor data(size); + data.stream << quint32(dbiMaxGroupCount) << qint32(cMaxGroupCount()); + data.stream << quint32(dbiAutoStart) << qint32(cAutoStart()); + data.stream << quint32(dbiStartMinimized) << qint32(cStartMinimized()); + data.stream << quint32(dbiSendToMenu) << qint32(cSendToMenu()); + data.stream << quint32(dbiWorkMode) << qint32(cWorkMode()); + data.stream << quint32(dbiSeenTrayTooltip) << qint32(cSeenTrayTooltip()); + data.stream << quint32(dbiAutoUpdate) << qint32(cAutoUpdate()); + data.stream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck()); + data.stream << quint32(dbiScale) << qint32(cConfigScale()); + data.stream << quint32(dbiLang) << qint32(cLang()); + for (mtpDcOptions::const_iterator i = dcOpts.cbegin(), e = dcOpts.cend(); i != e; ++i) { + data.stream << quint32(dbiDcOption) << quint32(i->id); + data.stream << QString::fromUtf8(i->host.data(), i->host.size()) << QString::fromUtf8(i->ip.data(), i->ip.size()); + data.stream << quint32(i->port); + } + data.stream << quint32(dbiLangFile) << cLangFile(); + + data.stream << quint32(dbiConnectionType) << qint32(cConnectionType()); + if (cConnectionType() == dbictHttpProxy || cConnectionType() == dbictTcpProxy) { + const ConnectionProxy &proxy(cConnectionProxy()); + data.stream << proxy.host << qint32(proxy.port) << proxy.user << proxy.password; + } + + TWindowPos pos(cWindowPos()); + data.stream << quint32(dbiWindowPosition) << qint32(pos.x) << qint32(pos.y) << qint32(pos.w) << qint32(pos.h) << qint32(pos.moncrc) << qint32(pos.maximized); + + settings.writeEncrypted(data, _settingsKey); + } + + void writeUserSettings() { + _writeUserSettings(); + } + + void writeMtpData() { + _writeMtpData(); + } + + void reset() { + _passKeySalt.clear(); // reset passcode, local key + _draftsMap.clear(); + _draftsPositionsMap.clear(); + _imagesMap.clear(); + _draftsNotReadMap.clear(); + _stickersMap.clear(); + _audiosMap.clear(); + _locationsKey = _userSettingsKey = _recentStickersKey = _backgroundKey = 0; + _mapChanged = true; + _writeMap(WriteMapNow); + + _writeMtpData(); + } + + bool checkPasscode(const QByteArray &passcode) { + mtpAuthKey tmp; + createLocalKey(passcode, &_passKeySalt, &tmp); + return (tmp == _passKey); + } + + void setPasscode(const QByteArray &passcode) { + createLocalKey(passcode, &_passKeySalt, &_passKey); + + EncryptedDescriptor passKeyData(LocalEncryptKeySize); + _localKey.write(passKeyData.stream); + _passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, _passKey); + + _mapChanged = true; + _writeMap(WriteMapNow); + + cSetHasPasscode(!passcode.isEmpty()); + } + ReadMapState readMap(const QByteArray &pass) { ReadMapState result = _readMap(pass); if (result == ReadMapFailed) { @@ -901,8 +1755,7 @@ namespace Local { _mapChanged = true; _writeMap(WriteMapFast); } - QString to = _basePath + toFilePart(i.value()); - EncryptedDescriptor data(sizeof(quint64) + sizeof(quint32) + text.size() * sizeof(QChar)); + EncryptedDescriptor data(sizeof(quint64) + _stringSize(text)); data.stream << quint64(peer) << text; FileWriteDescriptor file(i.value()); file.writeEncrypted(data); @@ -919,7 +1772,7 @@ namespace Local { return QString(); } FileReadDescriptor draft; - if (!readEncryptedFile(draft, toFilePart(j.value()))) { + if (!readEncryptedFile(draft, j.value())) { clearKey(j.value()); _draftsMap.erase(j); return QString(); @@ -949,7 +1802,6 @@ namespace Local { _mapChanged = true; _writeMap(WriteMapFast); } - QString to = _basePath + toFilePart(i.value()); EncryptedDescriptor data(sizeof(quint64) + sizeof(qint32) * 3); data.stream << quint64(peer) << qint32(cur.position) << qint32(cur.anchor) << qint32(cur.scroll); FileWriteDescriptor file(i.value()); @@ -963,7 +1815,7 @@ namespace Local { return MessageCursor(); } FileReadDescriptor draft; - if (!readEncryptedFile(draft, toFilePart(j.value()))) { + if (!readEncryptedFile(draft, j.value())) { clearKey(j.value()); _draftsPositionsMap.erase(j); return MessageCursor(); @@ -1069,7 +1921,7 @@ namespace Local { qint32 size = _storageImageSize(image.data.size()); StorageMap::const_iterator i = _imagesMap.constFind(location); if (i == _imagesMap.cend()) { - i = _imagesMap.insert(location, FileDesc(genKey(), size)); + i = _imagesMap.insert(location, FileDesc(genKey(UserPath), size)); _storageImagesSize += size; _mapChanged = true; _writeMap(); @@ -1078,7 +1930,7 @@ namespace Local { } EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + image.data.size()); data.stream << quint64(location.first) << quint64(location.second) << quint32(image.type) << image.data; - FileWriteDescriptor file(i.value().first, false); + FileWriteDescriptor file(i.value().first, UserPath); file.writeEncrypted(data); if (i.value().second != size) { _storageImagesSize += size; @@ -1093,8 +1945,8 @@ namespace Local { return StorageImageSaved(); } FileReadDescriptor draft; - if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) { - clearKey(j.value().first, false); + if (!readEncryptedFile(draft, j.value().first, UserPath)) { + clearKey(j.value().first, UserPath); _storageImagesSize -= j.value().second; _imagesMap.erase(j); return StorageImageSaved(); @@ -1122,7 +1974,7 @@ namespace Local { qint32 size = _storageStickerSize(sticker.size()); StorageMap::const_iterator i = _stickersMap.constFind(location); if (i == _stickersMap.cend()) { - i = _stickersMap.insert(location, FileDesc(genKey(), size)); + i = _stickersMap.insert(location, FileDesc(genKey(UserPath), size)); _storageStickersSize += size; _mapChanged = true; _writeMap(); @@ -1131,7 +1983,7 @@ namespace Local { } EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + sticker.size()); data.stream << quint64(location.first) << quint64(location.second) << sticker; - FileWriteDescriptor file(i.value().first, false); + FileWriteDescriptor file(i.value().first, UserPath); file.writeEncrypted(data); if (i.value().second != size) { _storageStickersSize += size; @@ -1146,8 +1998,8 @@ namespace Local { return QByteArray(); } FileReadDescriptor draft; - if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) { - clearKey(j.value().first, false); + if (!readEncryptedFile(draft, j.value().first, UserPath)) { + clearKey(j.value().first, UserPath); _storageStickersSize -= j.value().second; _stickersMap.erase(j); return QByteArray(); @@ -1174,7 +2026,7 @@ namespace Local { qint32 size = _storageAudioSize(audio.size()); StorageMap::const_iterator i = _audiosMap.constFind(location); if (i == _audiosMap.cend()) { - i = _audiosMap.insert(location, FileDesc(genKey(), size)); + i = _audiosMap.insert(location, FileDesc(genKey(UserPath), size)); _storageAudiosSize += size; _mapChanged = true; _writeMap(); @@ -1183,7 +2035,7 @@ namespace Local { } EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + audio.size()); data.stream << quint64(location.first) << quint64(location.second) << audio; - FileWriteDescriptor file(i.value().first, false); + FileWriteDescriptor file(i.value().first, UserPath); file.writeEncrypted(data); if (i.value().second != size) { _storageAudiosSize += size; @@ -1198,8 +2050,8 @@ namespace Local { return QByteArray(); } FileReadDescriptor draft; - if (!readEncryptedFile(draft, toFilePart(j.value().first), false)) { - clearKey(j.value().first, false); + if (!readEncryptedFile(draft, j.value().first, UserPath)) { + clearKey(j.value().first, UserPath); _storageAudiosSize -= j.value().second; _audiosMap.erase(j); return QByteArray(); @@ -1242,7 +2094,7 @@ namespace Local { DocumentData *doc = i->first; // id + value + access + date + namelen + name + mimelen + mime + dc + size + width + height + type - size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + (sizeof(quint32) + doc->name.size() * sizeof(ushort)) + (sizeof(quint32) + doc->mime.size() * sizeof(ushort)) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32); + size += sizeof(quint64) + sizeof(qint16) + sizeof(quint64) + sizeof(qint32) + _stringSize(doc->name) + _stringSize(doc->mime) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32) + sizeof(qint32); } EncryptedDescriptor data(size); for (RecentStickerPack::const_iterator i = recent.cbegin(); i != recent.cend(); ++i) { @@ -1259,7 +2111,7 @@ namespace Local { if (!_recentStickersKey) return; FileReadDescriptor stickers; - if (!readEncryptedFile(stickers, toFilePart(_recentStickersKey))) { + if (!readEncryptedFile(stickers, _recentStickersKey)) { clearKey(_recentStickersKey); _recentStickersKey = 0; _writeMap(); @@ -1320,7 +2172,7 @@ namespace Local { _backgroundWasRead = true; FileReadDescriptor bg; - if (!readEncryptedFile(bg, toFilePart(_backgroundKey))) { + if (!readEncryptedFile(bg, _backgroundKey)) { clearKey(_backgroundKey); _backgroundKey = 0; _writeMap(); @@ -1491,21 +2343,34 @@ namespace Local { audios = data->audios; } switch (task) { - case ClearManagerAll: - result = (QDir(cTempDir()).removeRecursively() && QDir(_basePath).removeRecursively()); - break; + case ClearManagerAll: { + result = QDir(cTempDir()).removeRecursively(); + QDirIterator di(_userBasePath, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot); + while (di.hasNext()) { + di.next(); + const QFileInfo& fi = di.fileInfo(); + if (fi.isDir() && !fi.isSymLink()) { + if (!QDir(di.filePath()).removeRecursively()) result = false; + } else { + QString path = di.filePath(); + if (!path.endsWith(QLatin1String("map0")) && !path.endsWith(QLatin1String("map1"))) { + if (!QFile::remove(di.filePath())) result = false; + } + } + } + } break; case ClearManagerDownloads: result = QDir(cTempDir()).removeRecursively(); break; case ClearManagerStorage: for (StorageMap::const_iterator i = images.cbegin(), e = images.cend(); i != e; ++i) { - clearKey(i.value().first, false); + clearKey(i.value().first, UserPath); } for (StorageMap::const_iterator i = stickers.cbegin(), e = stickers.cend(); i != e; ++i) { - clearKey(i.value().first, false); + clearKey(i.value().first, UserPath); } for (StorageMap::const_iterator i = audios.cbegin(), e = audios.cend(); i != e; ++i) { - clearKey(i.value().first, false); + clearKey(i.value().first, UserPath); } result = true; break; diff --git a/Telegram/SourceFiles/localstorage.h b/Telegram/SourceFiles/localstorage.h index 765a4477e..d9dc29628 100644 --- a/Telegram/SourceFiles/localstorage.h +++ b/Telegram/SourceFiles/localstorage.h @@ -50,11 +50,18 @@ namespace _local_inner { namespace Local { - mtpAuthKey &oldKey(); - void createOldKey(QByteArray *salt = 0); - void start(); void stop(); + + void readSettings(); + void writeSettings(); + void writeUserSettings(); + void writeMtpData(); + + void reset(); + + bool checkPasscode(const QByteArray &passcode); + void setPasscode(const QByteArray &passcode); enum ClearManagerTask { ClearManagerAll = 0xFFFF, diff --git a/Telegram/SourceFiles/main.cpp b/Telegram/SourceFiles/main.cpp index 68e15b3d1..33078488a 100644 --- a/Telegram/SourceFiles/main.cpp +++ b/Telegram/SourceFiles/main.cpp @@ -19,6 +19,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "application.h" #include "pspecific.h" +#include "localstorage.h" + int main(int argc, char *argv[]) { #ifdef _NEED_WIN_GENERATE_DUMP _oldWndExceptionFilter = SetUnhandledExceptionFilter(_exceptionFilter); @@ -34,9 +36,10 @@ int main(int argc, char *argv[]) { } logsInit(); - App::readConfig(); + Local::readSettings(); if (cFromAutoStart() && !cAutoStart()) { psAutoStart(false, true); + Local::stop(); return 0; } @@ -64,6 +67,7 @@ int main(int argc, char *argv[]) { } } psFinish(); + Local::stop(); DEBUG_LOG(("Application Info: Telegram done, result: %1").arg(result)); diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 575a1d729..d22c33514 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -399,6 +399,8 @@ _failDifferenceTimeout(1), _lastUpdateTime(0), _cachedX(0), _cachedY(0), _backgr show(); setFocus(); + + App::initMedia(); } mtpRequestId MainWidget::onForward(const PeerId &peer, bool forwardSelected) { @@ -462,6 +464,8 @@ void MainWidget::noHider(HistoryHider *destroyed) { } void MainWidget::hiderLayer(HistoryHider *h) { + if (App::passcoded()) return; + hider = h; if (cWideMode()) { hider->show(); @@ -1249,6 +1253,7 @@ void MainWidget::onParentResize(const QSize &newSize) { } void MainWidget::updateOnlineDisplay() { + if (this != App::main()) return; history.updateOnlineDisplay(history.x(), width() - history.x() - st::sysBtnDelta * 2 - st::sysCls.img.pxWidth() - st::sysRes.img.pxWidth() - st::sysMin.img.pxWidth()); if (profile) profile->updateOnlineDisplay(); if (App::wnd()->settingsWidget()) App::wnd()->settingsWidget()->updateOnlineDisplay(); @@ -2091,6 +2096,7 @@ void MainWidget::onPeerShown(PeerData *peer) { } void MainWidget::onUpdateNotifySettings() { + if (this != App::main()) return; while (!updateNotifySettingPeers.isEmpty()) { PeerData *peer = *updateNotifySettingPeers.begin(); updateNotifySettingPeers.erase(updateNotifySettingPeers.begin()); @@ -2260,6 +2266,7 @@ void MainWidget::updUpdated(int32 pts, int32 seq) { } void MainWidget::feedDifference(const MTPVector &users, const MTPVector &chats, const MTPVector &msgs, const MTPVector &other) { + App::wnd()->checkAutoLock(); App::feedUsers(users); App::feedChats(chats); feedMessageIds(other); @@ -2283,6 +2290,8 @@ void MainWidget::getDifferenceForce() { } void MainWidget::getDifference() { + if (this != App::main()) return; + LOG(("Getting difference! no updates timer: %1, remains: %2").arg(noUpdatesTimer.isActive() ? 1 : 0).arg(noUpdatesTimer.remainingTime())); if (!updInited) return; @@ -2303,9 +2312,13 @@ void MainWidget::getDifference() { } void MainWidget::start(const MTPUser &user) { - MTP::authed(user.c_userSelf().vid.v); + int32 uid = user.c_userSelf().vid.v; + if (MTP::authedId() != uid) { + MTP::authed(uid); + Local::writeMtpData(); + } + cSetOtherOnline(0); - App::initMedia(); App::feedUsers(MTP_vector(1, user)); App::app()->startUpdateCheck(); MTP::send(MTPupdates_GetState(), rpcDone(&MainWidget::gotState)); @@ -2571,6 +2584,9 @@ MainWidget::~MainWidget() { } void MainWidget::updateOnline(bool gotOtherOffline) { + if (this != App::main()) return; + App::wnd()->checkAutoLock(); + bool isOnline = App::wnd()->isActive(); int updateIn = cOnlineUpdatePeriod(); if (isOnline) { @@ -2608,6 +2624,7 @@ void MainWidget::updateOnline(bool gotOtherOffline) { } void MainWidget::checkIdleFinish() { + if (this != App::main()) return; if (psIdleTime() < uint64(cOfflineIdleTimeout())) { _idleFinishTimer.stop(); _isIdle = false; @@ -2621,6 +2638,8 @@ void MainWidget::checkIdleFinish() { void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) { if (end <= from || !MTP::authedId()) return; + App::wnd()->checkAutoLock(); + if (mtpTypeId(*from) == mtpc_new_session_created) { MTPNewSession newSession(from, end); updSeq = 0; diff --git a/Telegram/SourceFiles/mtproto/mtp.cpp b/Telegram/SourceFiles/mtproto/mtp.cpp index 763da994a..8411ceed0 100644 --- a/Telegram/SourceFiles/mtproto/mtp.cpp +++ b/Telegram/SourceFiles/mtproto/mtp.cpp @@ -32,7 +32,7 @@ namespace { typedef QMap AuthExportRequests; // holds target dc for auth export request AuthExportRequests authExportRequests; - bool started = false; + bool _started = false; uint32 layer; @@ -161,7 +161,7 @@ namespace { DEBUG_LOG(("MTP Info: changing request %1 dc%2 to %3").arg(requestId).arg((dc > 0) ? "" : " and main dc").arg(newdc)); if (dc < 0) { - if (MTP::authedId()) { // import auth, set dc and resend + if (MTP::authedId() && !authExportRequests.contains(requestId)) { // import auth, set dc and resend DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdc)); DCAuthWaiters &waiters(authWaiters[newdc]); if (!waiters.size()) { @@ -334,7 +334,7 @@ namespace { namespace _mtp_internal { MTProtoSessionPtr getSession(int32 dc) { - if (!started) return MTProtoSessionPtr(); + if (!_started) return MTProtoSessionPtr(); if (!dc) return mainSession; if (!(dc % _mtp_internal::dcShift)) { dc += mainSession->getDC(); @@ -588,19 +588,13 @@ namespace MTP { void start() { unixtimeInit(); - if (!Local::oldKey().created()) { - LOG(("App Error: trying to start MTP without local key!")); - return; - } - - mtpLoadData(); MTProtoDCMap &dcs(mtpDCMap()); mainSession = MTProtoSessionPtr(new MTProtoSession()); mainSession->start(mtpMainDC()); sessions[mainSession->getDC()] = mainSession; - started = true; + _started = true; resender = new _mtp_internal::RequestResender(); if (mtpNeedConfig()) { @@ -608,15 +602,19 @@ namespace MTP { } } + bool started() { + return _started; + } + void restart() { - if (!started) return; + if (!_started) return; for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { (*i)->restart(); } } void restart(int32 dcMask) { - if (!started) return; + if (!_started) return; for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { if ((*i)->getDC() % _mtp_internal::dcShift == dcMask % _mtp_internal::dcShift) { @@ -625,23 +623,27 @@ namespace MTP { } } - void setdc(int32 dc, bool fromZeroOnly) { - if (!started) return; - - int32 m = mainSession->getDC(); - if (!dc || m == dc || (m && fromZeroOnly)) return; + void configure(int32 dc, int32 user) { + if (_started) return; mtpSetDC(dc); - mainSession = _mtp_internal::getSession(dc); + mtpAuthed(user); + } + + void setdc(int32 dc, bool fromZeroOnly) { + if (!dc || !_started) return; + mtpSetDC(dc, fromZeroOnly); + if (dc != mainSession->getDC()) { + mainSession = _mtp_internal::getSession(dc); + } + Local::writeMtpData(); } int32 maindc() { - if (!started) return 0; - - return mainSession->getDC(); + return mtpMainDC(); } int32 dcstate(int32 dc) { - if (!started) return 0; + if (!_started) return 0; if (!dc) return mainSession->getState(); if (!(dc % _mtp_internal::dcShift)) { @@ -655,7 +657,7 @@ namespace MTP { } QString dctransport(int32 dc) { - if (!started) return QString(); + if (!_started) return QString(); if (!dc) return mainSession->transport(); if (!(dc % _mtp_internal::dcShift)) { @@ -669,7 +671,7 @@ namespace MTP { } void initdc(int32 dc) { - if (!started) return; + if (!_started) return; _mtp_internal::getSession(dc); } @@ -781,15 +783,15 @@ namespace MTP { void updateDcOptions(const QVector &options) { mtpUpdateDcOptions(options); - App::writeUserConfig(); + Local::writeSettings(); } - void writeConfig(QDataStream &stream) { - return mtpWriteConfig(stream); + mtpKeysMap getKeys() { + return mtpGetKeys(); } - bool readConfigElem(int32 blockId, QDataStream &stream) { - return mtpReadConfigElem(blockId, stream); + void setKey(int32 dc, mtpAuthKeyPtr key) { + return mtpSetKey(dc, key); } }; diff --git a/Telegram/SourceFiles/mtproto/mtp.h b/Telegram/SourceFiles/mtproto/mtp.h index e3f77da3e..5e4bc6cf7 100644 --- a/Telegram/SourceFiles/mtproto/mtp.h +++ b/Telegram/SourceFiles/mtproto/mtp.h @@ -78,11 +78,15 @@ namespace MTP { }; void start(); + bool started(); void restart(); void restart(int32 dcMask); + void configure(int32 dc, int32 user); + void setdc(int32 dc, bool fromZeroOnly = false); int32 maindc(); + int32 dcstate(int32 dc = 0); QString dctransport(int32 dc = 0); void initdc(int32 dc); @@ -131,8 +135,8 @@ namespace MTP { return result; } - void writeConfig(QDataStream &stream); - bool readConfigElem(int32 blockId, QDataStream &stream); + mtpKeysMap getKeys(); + void setKey(int32 dc, mtpAuthKeyPtr key); }; diff --git a/Telegram/SourceFiles/mtproto/mtpAuthKey.h b/Telegram/SourceFiles/mtproto/mtpAuthKey.h index 6b11a7ccf..e9431935f 100644 --- a/Telegram/SourceFiles/mtproto/mtpAuthKey.h +++ b/Telegram/SourceFiles/mtproto/mtpAuthKey.h @@ -91,6 +91,8 @@ public: static const uint64 RecreateKeyId = 0xFFFFFFFFFFFFFFFFL; + friend bool operator==(const mtpAuthKey &a, const mtpAuthKey &b); + private: char _key[256]; @@ -100,7 +102,12 @@ private: }; +inline bool operator==(const mtpAuthKey &a, const mtpAuthKey &b) { + return !memcmp(a._key, b._key, 256); +} + typedef QSharedPointer mtpAuthKeyPtr; +typedef QVector mtpKeysMap; inline void aesEncrypt(const void *src, void *dst, uint32 len, void *key, void *iv) { uchar aes_key[32], aes_iv[32]; diff --git a/Telegram/SourceFiles/mtproto/mtpConnection.cpp b/Telegram/SourceFiles/mtproto/mtpConnection.cpp index 83c6c20ef..0eba8dd12 100644 --- a/Telegram/SourceFiles/mtproto/mtpConnection.cpp +++ b/Telegram/SourceFiles/mtproto/mtpConnection.cpp @@ -180,12 +180,6 @@ namespace { return false; } - BN_sub_word(&bnPrime, 1); // (p - 1) / 2 - BN_div_word(&bnPrime, 2); - - if (BN_is_prime_ex(&bnPrime, MTPMillerRabinIterCount, ctx, NULL) == 0) { - return false; - } switch (g) { case 2: { int32 mod8 = BN_mod_word(&bnPrime, 8); @@ -226,7 +220,14 @@ namespace { default: LOG(("BigNum PT Error: bad g value: %1").arg(g)); return false; - break; + break; + } + + BN_sub_word(&bnPrime, 1); // (p - 1) / 2 + BN_div_word(&bnPrime, 2); + + if (BN_is_prime_ex(&bnPrime, MTPMillerRabinIterCount, ctx, NULL) == 0) { + return false; } return true; @@ -1105,13 +1106,13 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne // createConn(); if (!dc) { - const mtpDcOptions &gDcOptions(mtpDCOptions()); - if (!gDcOptions.size()) { + const mtpDcOptions &options(cDcOptions()); + if (options.isEmpty()) { LOG(("MTP Error: connect failed, no DCs")); dc = 0; return; } - dc = gDcOptions.cbegin().value().id; + dc = options.cbegin().value().id; DEBUG_LOG(("MTP Info: searching for any DC, %1 selected..").arg(dc)); } @@ -1699,10 +1700,10 @@ void MTProtoConnectionPrivate::socketStart(bool afterConfig) { pingId = pingMsgId = toSendPingId = 0; const mtpDcOption *dcOption = 0; - const mtpDcOptions &gDcOptions(mtpDCOptions()); - mtpDcOptions::const_iterator dcIndex = gDcOptions.constFind(dc % _mtp_internal::dcShift); + const mtpDcOptions &options(cDcOptions()); + mtpDcOptions::const_iterator dcIndex = options.constFind(dc % _mtp_internal::dcShift); DEBUG_LOG(("MTP Info: connecting to DC %1..").arg(dc)); - if (dcIndex == gDcOptions.cend()) { + if (dcIndex == options.cend()) { if (afterConfig) { LOG(("MTP Error: DC %1 options not found right after config load!").arg(dc)); return restart(); diff --git a/Telegram/SourceFiles/mtproto/mtpDC.cpp b/Telegram/SourceFiles/mtproto/mtpDC.cpp index e698c3b5e..a90864d60 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.cpp +++ b/Telegram/SourceFiles/mtproto/mtpDC.cpp @@ -25,283 +25,13 @@ namespace { MTProtoDCMap gDCs; bool configLoadedOnce = false; + bool mainDCChanged = false; int32 mainDC = 2; - int userId = 0; - mtpDcOptions gDCOptions; + int32 userId = 0; typedef QMap _KeysMapForWrite; _KeysMapForWrite _keysMapForWrite; QMutex _keysMapForWriteMutex; - - int32 readAuthKeysFields(QIODevice *io) { - if (!io->isOpen()) io->open(QIODevice::ReadOnly); - - QDataStream stream(io); - stream.setVersion(QDataStream::Qt_5_1); - - int32 oldFound = 0; - - while (true) { - quint32 blockId; - stream >> blockId; - if (stream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("MTP Info: keys file read end")); - break; - } else if (stream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not read block id, status: %1 - keys file is corrupted?..").arg(stream.status())); - break; - } - - if (blockId == dbiVersion) { - qint32 keysVersion; - stream >> keysVersion; - continue; // should not be in encrypted part, just ignore - } - - if (blockId != dbiEncrypted && blockId != dbiKey) { - oldFound = 2; - } - - switch (blockId) { - case dbiEncrypted: { - QByteArray data, decrypted; - stream >> data; - - if (!Local::oldKey().created()) { - LOG(("MTP Error: reading encrypted keys without local key!")); - continue; - } - - if (data.size() <= 16 || (data.size() & 0x0F)) { - LOG(("MTP Error: bad encrypted part size: %1").arg(data.size())); - continue; - } - uint32 fullDataLen = data.size() - 16; - decrypted.resize(fullDataLen); - const char *dataKey = data.constData(), *encrypted = data.constData() + 16; - aesDecryptLocal(encrypted, decrypted.data(), fullDataLen, &Local::oldKey(), dataKey); - uchar sha1Buffer[20]; - if (memcmp(hashSha1(decrypted.constData(), decrypted.size(), sha1Buffer), dataKey, 16)) { - LOG(("MTP Error: bad decrypt key, data from user-config not decrypted")); - continue; - } - uint32 dataLen = *(const uint32*)decrypted.constData(); - if (dataLen > uint32(decrypted.size()) || dataLen <= fullDataLen - 16 || dataLen < 4) { - LOG(("MTP Error: bad decrypted part size: %1, fullDataLen: %2, decrypted size: %3").arg(dataLen).arg(fullDataLen).arg(decrypted.size())); - continue; - } - decrypted.resize(dataLen); - QBuffer decryptedStream(&decrypted); - decryptedStream.open(QIODevice::ReadOnly); - decryptedStream.seek(4); // skip size - readAuthKeysFields(&decryptedStream); - } break; - - case dbiKey: { - qint32 dcId; - quint32 key[64]; - stream >> dcId; - stream.readRawData((char*)key, 256); - if (stream.status() == QDataStream::Ok) { - DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(mb(key, 256).str())); - dcId = dcId % _mtp_internal::dcShift; - mtpAuthKeyPtr keyPtr(new mtpAuthKey()); - keyPtr->setKey(key); - keyPtr->setDC(dcId); - - MTProtoDCPtr dc(new MTProtoDC(dcId, keyPtr)); - gDCs.insert(dcId, dc); - } - } break; - - case dbiUser: { - quint32 dcId; - qint32 uid; - stream >> uid >> dcId; - if (stream.status() == QDataStream::Ok) { - DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(uid)); - - userId = uid; - mainDC = dcId; - } - } break; - - case dbiDcOption: { - quint32 dcId, port; - QString host, ip; - stream >> dcId >> host >> ip >> port; - - if (stream.status() == QDataStream::Ok) { - gDCOptions.insert(dcId, mtpDcOption(dcId, host.toUtf8().constData(), ip.toUtf8().constData(), port)); - } - } break; - - case dbiConfig1: { - quint32 maxSize; - stream >> maxSize; - if (stream.status() == QDataStream::Ok) { - cSetMaxGroupCount(maxSize); - } - } break; - } - - if (stream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not read data, status: %1 - keys file is corrupted?..").arg(stream.status())); - break; - } - } - - return oldFound; - } - - int32 readAuthKeys(QFile &file) { - QDataStream stream(&file); - stream.setVersion(QDataStream::Qt_5_1); - - int32 oldFound = 0; - quint32 blockId; - stream >> blockId; - if (stream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("MTP Info: keys file read end")); - return oldFound; - } else if (stream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not read block id, status: %1 - keys file is corrupted?..").arg(stream.status())); - return oldFound; - } - - if (blockId == dbiVersion) { - qint32 keysVersion; - stream >> keysVersion; - if (keysVersion > AppVersion) return oldFound; - - stream >> blockId; - if (stream.status() == QDataStream::ReadPastEnd) { - DEBUG_LOG(("MTP Info: keys file read end")); - return oldFound; - } else if (stream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not read block id, status: %1 - keys file is corrupted?..").arg(stream.status())); - return oldFound; - } - if (blockId != dbiEncrypted) { - oldFound = (blockId != dbiKey) ? 2 : 1; - } - } else { - oldFound = 2; - } - - file.reset(); - oldFound = qMax(oldFound, readAuthKeysFields(&file)); - - return oldFound; - } - - void writeAuthKeys(); - void readAuthKeys() { - QFile keysFile(cWorkingDir() + cDataFile()); - if (keysFile.open(QIODevice::ReadOnly)) { - DEBUG_LOG(("MTP Info: keys file opened for reading")); - int32 oldFound = readAuthKeys(keysFile); - if (gDCOptions.isEmpty()) { - const BuiltInDc *bdcs = builtInDcs(); - for (int i = 0, l = builtInDcsCount(); i < l; ++i) { - gDCOptions.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port)); - DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); - } - } - if (mainDC && gDCOptions.find(mainDC) == gDCOptions.cend()) { // load first dc info - userId = 0; - mainDC = (gDCOptions.constFind(2) == gDCOptions.cend()) ? gDCOptions.begin().key() : 2; - } else { - DEBUG_LOG(("MTP Info: config from local, dc option count: %1").arg(gDCOptions.size())); - } - - if (oldFound > 0) { - writeAuthKeys(); - if (oldFound > 1) { - App::writeUserConfig(); - } - DEBUG_LOG(("MTP Info: rewritten old data / config to new data and config")); - } - } else { - DEBUG_LOG(("MTP Info: could not open keys file for reading")); - const BuiltInDc *bdcs = builtInDcs(); - for (int i = 0, l = builtInDcsCount(); i < l; ++i) { - gDCOptions.insert(bdcs[i].id, mtpDcOption(bdcs[i].id, "", bdcs[i].ip, bdcs[i].port)); - DEBUG_LOG(("MTP Info: adding built in DC %1 connect option: %2:%3").arg(bdcs[i].id).arg(bdcs[i].ip).arg(bdcs[i].port)); - } - } - } - - typedef QVector _KeysToWrite; - void writeAuthKeys() { - _KeysToWrite keysToWrite; - { - QMutexLocker lock(&_keysMapForWriteMutex); - for (_KeysMapForWrite::const_iterator i = _keysMapForWrite.cbegin(), e = _keysMapForWrite.cend(); i != e; ++i) { - keysToWrite.push_back(i.value()); - } - } - - QFile keysFile(cWorkingDir() + cDataFile()); - if (keysFile.open(QIODevice::WriteOnly)) { - DEBUG_LOG(("MTP Info: writing keys data for encrypt")); - QByteArray toEncrypt; - toEncrypt.reserve(65536); - toEncrypt.resize(4); - { - QBuffer buffer(&toEncrypt); - buffer.open(QIODevice::Append); - - QDataStream stream(&buffer); - stream.setVersion(QDataStream::Qt_5_1); - - for (_KeysToWrite::const_iterator i = keysToWrite.cbegin(), e = keysToWrite.cend(); i != e; ++i) { - stream << quint32(dbiKey) << quint32((*i)->getDC()); - (*i)->write(stream); - } - - if (stream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not write keys to memory buf, status: %1").arg(stream.status())); - } - } - *(uint32*)(toEncrypt.data()) = toEncrypt.size(); - - uint32 size = toEncrypt.size(), fullSize = size; - if (fullSize & 0x0F) { - fullSize += 0x10 - (fullSize & 0x0F); - toEncrypt.resize(fullSize); - memset_rand(toEncrypt.data() + size, fullSize - size); - } - QByteArray encrypted(16 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data - hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data()); - aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 16, fullSize, &Local::oldKey(), encrypted.constData()); - - DEBUG_LOG(("MTP Info: keys file opened for writing %1 keys").arg(keysToWrite.size())); - QDataStream keysStream(&keysFile); - keysStream.setVersion(QDataStream::Qt_5_1); - keysStream << quint32(dbiVersion) << qint32(AppVersion); - - keysStream << quint32(dbiEncrypted) << encrypted; // write all encrypted data - - if (keysStream.status() != QDataStream::Ok) { - LOG(("MTP Error: could not write keys, status: %1").arg(keysStream.status())); - } - } else { - LOG(("MTP Error: could not open keys file for writing")); - } - } - - class _KeysReader { - public: - _KeysReader() { - readAuthKeys(); - } - }; - -} - -void mtpLoadData() { - static _KeysReader keysReader; } int32 mtpAuthed() { @@ -309,9 +39,8 @@ int32 mtpAuthed() { } void mtpAuthed(int32 uid) { - if (userId != uid && mainDC) { + if (userId != uid) { userId = uid; - App::writeUserConfig(); } } @@ -319,10 +48,6 @@ MTProtoDCMap &mtpDCMap() { return gDCs; } -const mtpDcOptions &mtpDCOptions() { - return gDCOptions; -} - bool mtpNeedConfig() { return !configLoadedOnce; } @@ -344,12 +69,11 @@ void mtpLogoutOtherDCs() { } } -void mtpSetDC(int32 dc) { +void mtpSetDC(int32 dc, bool firstOnly) { + if (!dc || (firstOnly && mainDCChanged)) return; + mainDCChanged = true; if (dc != mainDC) { mainDC = dc; - if (userId) { - App::writeUserConfig(); - } } } @@ -367,7 +91,7 @@ MTProtoDC::MTProtoDC(int32 id, const mtpAuthKeyPtr &key) : _id(id), _key(key), _ void MTProtoDC::authKeyWrite() { DEBUG_LOG(("AuthKey Info: MTProtoDC::authKeyWrite() slot, dc %1").arg(_id)); if (_key) { - writeAuthKeys(); + Local::writeMtpData(); } } @@ -414,7 +138,7 @@ namespace { cSetMaxGroupCount(data.vchat_size_max.v); configLoadedOnce = true; - App::writeUserConfig(); + Local::writeSettings(); mtpConfigLoader()->done(); } @@ -427,18 +151,22 @@ namespace { void mtpUpdateDcOptions(const QVector &options) { QSet already, restart; - for (QVector::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { - const MTPDdcOption &optData(i->c_dcOption()); - if (already.constFind(optData.vid.v) == already.cend()) { - already.insert(optData.vid.v); - mtpDcOptions::const_iterator a = gDCOptions.constFind(optData.vid.v); - if (a != gDCOptions.cend()) { - if (a.value().ip != optData.vip_address.c_string().v || a.value().port != optData.vport.v) { - restart.insert(optData.vid.v); + { + mtpDcOptions opts(cDcOptions()); + for (QVector::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { + const MTPDdcOption &optData(i->c_dcOption()); + if (already.constFind(optData.vid.v) == already.cend()) { + already.insert(optData.vid.v); + mtpDcOptions::const_iterator a = opts.constFind(optData.vid.v); + if (a != opts.cend()) { + if (a.value().ip != optData.vip_address.c_string().v || a.value().port != optData.vport.v) { + restart.insert(optData.vid.v); + } } + opts.insert(optData.vid.v, mtpDcOption(optData.vid.v, optData.vhostname.c_string().v, optData.vip_address.c_string().v, optData.vport.v)); } - gDCOptions.insert(optData.vid.v, mtpDcOption(optData.vid.v, optData.vhostname.c_string().v, optData.vip_address.c_string().v, optData.vport.v)); } + cSetDcOptions(opts); } for (QSet::const_iterator i = restart.cbegin(), e = restart.cend(); i != e; ++i) { MTP::restart(*i); @@ -490,9 +218,10 @@ void MTProtoConfigLoader::enumDC() { } else { MTP::killSession(MTP::cfg + _enumCurrent); } - for (mtpDcOptions::const_iterator i = gDCOptions.cbegin(), e = gDCOptions.cend(); i != e; ++i) { + const mtpDcOptions &options(cDcOptions()); + for (mtpDcOptions::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { if (i.key() == _enumCurrent) { - _enumCurrent = (++i == e) ? gDCOptions.cbegin().key() : i.key(); + _enumCurrent = (++i == e) ? options.cbegin().key() : i.key(); break; } } @@ -512,51 +241,16 @@ void mtpDestroyConfigLoader() { configLoader = 0; } -void mtpWriteConfig(QDataStream &stream) { - if (userId) { - stream << quint32(dbiUser) << qint32(userId) << quint32(mainDC); +mtpKeysMap mtpGetKeys() { + mtpKeysMap result; + QMutexLocker lock(&_keysMapForWriteMutex); + for (_KeysMapForWrite::const_iterator i = _keysMapForWrite.cbegin(), e = _keysMapForWrite.cend(); i != e; ++i) { + result.push_back(i.value()); } - for (mtpDcOptions::const_iterator i = gDCOptions.cbegin(), e = gDCOptions.cend(); i != e; ++i) { - stream << quint32(dbiDcOption) << i->id << QString(i->host.c_str()) << QString(i->ip.c_str()) << i->port; - } - stream << quint32(dbiConfig1) << qint32(cMaxGroupCount()); + return result; } -bool mtpReadConfigElem(int32 blockId, QDataStream &stream) { - switch (blockId) { - case dbiUser: { - quint32 dcId; - qint32 uid; - stream >> uid >> dcId; - if (stream.status() == QDataStream::Ok) { - DEBUG_LOG(("MTP Info: user found, dc %1, uid %2").arg(dcId).arg(uid)); - - userId = uid; - mainDC = dcId; - return true; - } - } break; - - case dbiDcOption: { - quint32 dcId, port; - QString host, ip; - stream >> dcId >> host >> ip >> port; - - if (stream.status() == QDataStream::Ok) { - gDCOptions.insert(dcId, mtpDcOption(dcId, host.toUtf8().constData(), ip.toUtf8().constData(), port)); - return true; - } - } break; - - case dbiConfig1: { - quint32 maxSize; - stream >> maxSize; - if (stream.status() == QDataStream::Ok) { - cSetMaxGroupCount(maxSize); - return true; - } - } break; - } - - return false; +void mtpSetKey(int32 dcId, mtpAuthKeyPtr key) { + MTProtoDCPtr dc(new MTProtoDC(dcId, key)); + gDCs.insert(dcId, dc); } diff --git a/Telegram/SourceFiles/mtproto/mtpDC.h b/Telegram/SourceFiles/mtproto/mtpDC.h index 6f2111015..4a5bd2c0d 100644 --- a/Telegram/SourceFiles/mtproto/mtpDC.h +++ b/Telegram/SourceFiles/mtproto/mtpDC.h @@ -60,17 +60,6 @@ private: typedef QSharedPointer MTProtoDCPtr; typedef QMap MTProtoDCMap; -struct mtpDcOption { - mtpDcOption(int _id, const string &_host, const string &_ip, int _port) : id(_id), host(_host), ip(_ip), port(_port) { - } - - int id; - string host; - string ip; - int port; -}; -typedef QMap mtpDcOptions; - class MTProtoConfigLoader : public QObject { Q_OBJECT @@ -101,20 +90,17 @@ private: MTProtoConfigLoader *mtpConfigLoader(); void mtpDestroyConfigLoader(); -const mtpDcOptions &mtpDCOptions(); MTProtoDCMap &mtpDCMap(); bool mtpNeedConfig(); int32 mtpMainDC(); void mtpLogoutOtherDCs(); -void mtpSetDC(int32 dc); +void mtpSetDC(int32 dc, bool firstOnly = false); uint32 mtpMaxChatSize(); -void mtpWriteAuthKeys(); -void mtpLoadData(); int32 mtpAuthed(); void mtpAuthed(int32 uid); -void mtpWriteConfig(QDataStream &stream); -bool mtpReadConfigElem(int32 blockId, QDataStream &stream); +mtpKeysMap mtpGetKeys(); +void mtpSetKey(int32 dc, mtpAuthKeyPtr key); void mtpUpdateDcOptions(const QVector &options); diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp new file mode 100644 index 000000000..6c66ea743 --- /dev/null +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -0,0 +1,216 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" +#include "lang.h" +#include "style.h" + +#include "localstorage.h" + +#include "passcodewidget.h" +#include "window.h" +#include "application.h" +#include "gui/text.h" + +class LogOutLink : public ITextLink { +public: + + void onClick(Qt::MouseButton) const { + App::wnd()->onLogout(); + } + +}; + +PasscodeWidget::PasscodeWidget(QWidget *parent) : QWidget(parent), +_passcode(this, st::passcodeInput), +_submit(this, lang(lng_passcode_submit), st::passcodeSubmit), +_logout(this, lng_passcode_logout(lt_link_start, textcmdStartLink(1), lt_link_end, textcmdStopLink())) { + setGeometry(QRect(0, st::titleHeight, App::wnd()->width(), App::wnd()->height() - st::titleHeight)); + connect(App::wnd(), SIGNAL(resized(const QSize &)), this, SLOT(onParentResize(const QSize &))); + + _passcode.setEchoMode(QLineEdit::Password); + connect(&_submit, SIGNAL(clicked()), this, SLOT(onSubmit())); + + _errorTimer.setSingleShot(true); + connect(&_errorTimer, SIGNAL(timeout()), this, SLOT(onError())); + + _logout.setLink(1, TextLinkPtr(new LogOutLink())); + + connect(&_passcode, SIGNAL(changed()), this, SLOT(onChanged())); + connect(&_passcode, SIGNAL(accepted()), this, SLOT(onSubmit())); + + show(); + _passcode.setFocus(); +} + +void PasscodeWidget::onParentResize(const QSize &newSize) { + resize(newSize); +} + +void PasscodeWidget::onSubmit() { + if (_passcode.text().isEmpty()) { + _passcode.setFocus(); + _passcode.notaBene(); + return; + } + + if (App::main()) { + if (Local::checkPasscode(_passcode.text().toUtf8())) { + App::wnd()->clearPasscode(); + } else { + _error = QString(); + _passcode.setDisabled(true); + _errorTimer.start(WrongPasscodeTimeout); + return; + } + } else { + if (Local::readMap(_passcode.text().toUtf8()) != Local::ReadMapPassNeeded) { + App::app()->checkMapVersion(); + + MTP::start(); + if (MTP::authedId()) { + App::wnd()->setupMain(true); + } else { + App::wnd()->setupIntro(true); + } + } else { + _error = QString(); + _passcode.setDisabled(true); + _errorTimer.start(WrongPasscodeTimeout); + update(); + return; + } + } +} + +void PasscodeWidget::onError() { + _error = lang(lng_passcode_wrong); + _passcode.setDisabled(false); + _passcode.selectAll(); + _passcode.setFocus(); + _passcode.notaBene(); + update(); +} + +void PasscodeWidget::onChanged() { + if (!_error.isEmpty()) { + _error = QString(); + update(); + } +} + +void PasscodeWidget::animShow(const QPixmap &bgAnimCache, bool back) { + _bgAnimCache = bgAnimCache; + + anim::stop(this); + showAll(); + _animCache = myGrab(this, rect()); + + a_coord = back ? anim::ivalue(-st::introSlideShift, 0) : anim::ivalue(st::introSlideShift, 0); + a_alpha = anim::fvalue(0, 1); + a_bgCoord = back ? anim::ivalue(0, st::introSlideShift) : anim::ivalue(0, -st::introSlideShift); + a_bgAlpha = anim::fvalue(1, 0); + + hideAll(); + anim::start(this); + show(); +} + +bool PasscodeWidget::animStep(float64 ms) { + float64 fullDuration = st::introSlideDelta + st::introSlideDuration, dt = ms / fullDuration; + float64 dt1 = (ms > st::introSlideDuration) ? 1 : (ms / st::introSlideDuration), dt2 = (ms > st::introSlideDelta) ? (ms - st::introSlideDelta) / (st::introSlideDuration) : 0; + bool res = true; + if (dt2 >= 1) { + res = false; + a_bgCoord.finish(); + a_bgAlpha.finish(); + a_coord.finish(); + a_alpha.finish(); + + _animCache = _bgAnimCache = QPixmap(); + + showAll(); + setInnerFocus(); + } else { + a_bgCoord.update(dt1, st::introHideFunc); + a_bgAlpha.update(dt1, st::introAlphaHideFunc); + a_coord.update(dt2, st::introShowFunc); + a_alpha.update(dt2, st::introAlphaShowFunc); + } + update(); + return res; +} + +void PasscodeWidget::showAll() { + _passcode.show(); + _submit.show(); + _logout.show(); +} + +void PasscodeWidget::hideAll() { + _passcode.hide(); + _submit.hide(); + _logout.hide(); +} + +void PasscodeWidget::paintEvent(QPaintEvent *e) { + bool trivial = (rect() == e->rect()); + setMouseTracking(true); + + QPainter p(this); + if (!trivial) { + p.setClipRect(e->rect()); + } + + if (animating()) { + p.setOpacity(a_bgAlpha.current()); + p.drawPixmap(a_bgCoord.current(), 0, _bgAnimCache); + p.setOpacity(a_alpha.current()); + p.drawPixmap(a_coord.current(), 0, _animCache); + } else { + p.fillRect(rect(), st::setBG->b); + + p.setFont(st::passcodeHeaderFont->f); + p.drawText(QRect(0, _passcode.y() - st::passcodeHeaderHeight, width(), st::passcodeHeaderHeight), lang(lng_passcode_enter), style::al_center); + + if (!_error.isEmpty()) { + p.setFont(st::boxFont->f); + p.setPen(st::setErrColor->p); + p.drawText(QRect(0, _passcode.y() + _passcode.height(), width(), st::usernameSkip), _error, style::al_center); + } + } +} + +void PasscodeWidget::resizeEvent(QResizeEvent *e) { + _passcode.move((width() - _passcode.width()) / 2, (height() / 3)); + _submit.move(_passcode.x(), _passcode.y() + _passcode.height() + st::passcodeSkip); + _logout.move(_passcode.x() + (_passcode.width() - _logout.width()) / 2, _submit.y() + _submit.height() + st::linkFont->ascent); +} + +void PasscodeWidget::mousePressEvent(QMouseEvent *e) { + +} + +void PasscodeWidget::keyPressEvent(QKeyEvent *e) { +} + +void PasscodeWidget::setInnerFocus() { + _passcode.setFocus(); +} + +PasscodeWidget::~PasscodeWidget() { +} diff --git a/Telegram/SourceFiles/passcodewidget.h b/Telegram/SourceFiles/passcodewidget.h new file mode 100644 index 000000000..bdadbe62d --- /dev/null +++ b/Telegram/SourceFiles/passcodewidget.h @@ -0,0 +1,62 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014 John Preston, https://desktop.telegram.org +*/ +#pragma once + +class PasscodeWidget : public QWidget, public Animated { + Q_OBJECT + +public: + + PasscodeWidget(QWidget *parent); + + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent *e); + void mousePressEvent(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + void setInnerFocus(); + + void animShow(const QPixmap &bgAnimCache, bool back = false); + bool animStep(float64 ms); + + ~PasscodeWidget(); + +public slots: + + void onParentResize(const QSize &newSize); + void onError(); + void onChanged(); + void onSubmit(); + +signals: + +private: + + void showAll(); + void hideAll(); + + QPixmap _animCache, _bgAnimCache; + anim::ivalue a_coord, a_bgCoord; + anim::fvalue a_alpha, a_bgAlpha; + + FlatButton _submit; + FlatInput _passcode; + FlatLabel _logout; + QString _error; + QTimer _errorTimer; + +}; diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 5d71ed46c..20c8b70c7 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -22,6 +22,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "application.h" #include "mainwidget.h" +#include "localstorage.h" + #include #include #include @@ -703,7 +705,7 @@ void PsMainWindow::psSavePosition(Qt::WindowState state) { if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { cSetWindowPos(curPos); - App::writeConfig(); + Local::writeSettings(); } } } @@ -1369,6 +1371,10 @@ void psUserActionDone() { _lastUserAction = getms(true); } +bool psIdleSupported() { + return false; +} + uint64 psIdleTime() { return getms(true) - _lastUserAction; } diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h index ef4e5a56f..d03bd5cef 100644 --- a/Telegram/SourceFiles/pspecific_linux.h +++ b/Telegram/SourceFiles/pspecific_linux.h @@ -167,6 +167,7 @@ private: }; void psUserActionDone(); +bool psIdleSupported(); uint64 psIdleTime(); bool psSkipAudioNotify(); diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp index 7b3a9d5e5..9aafcfc89 100644 --- a/Telegram/SourceFiles/pspecific_mac.cpp +++ b/Telegram/SourceFiles/pspecific_mac.cpp @@ -23,6 +23,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "historywidget.h" +#include "localstorage.h" + namespace { QStringList _initLogs; @@ -61,9 +63,14 @@ void MacPrivate::notifyClicked(unsigned long long peer) { History *history = App::history(PeerId(peer)); App::wnd()->showFromTray(); - App::wnd()->hideSettings(); - App::main()->showPeer(history->peer->id, false, true); - App::wnd()->notifyClear(history); + if (App::passcoded()) { + App::wnd()->passcodeWidget()->setInnerFocus(); + App::wnd()->notifyClear(); + } else { + App::wnd()->hideSettings(); + App::main()->showPeer(history->peer->id, false, true); + App::wnd()->notifyClear(history); + } } void MacPrivate::notifyReplied(unsigned long long peer, const char *str) { @@ -268,7 +275,7 @@ void PsMainWindow::psSavePosition(Qt::WindowState state) { if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { cSetWindowPos(curPos); - App::writeConfig(); + Local::writeSettings(); } } } @@ -475,12 +482,12 @@ void PsMainWindow::psNotifyShown(NotifyWindow *w) { } void PsMainWindow::psPlatformNotify(HistoryItem *item) { - QString title = (cNotifyView() <= dbinvShowName) ? item->history()->peer->name : qsl("Telegram Desktop"); - QString subtitle = (cNotifyView() <= dbinvShowName) ? item->notificationHeader() : QString(); - QPixmap pix = (cNotifyView() <= dbinvShowName) ? item->history()->peer->photo->pix(st::notifyMacPhotoSize) : QPixmap(); - QString msg = (cNotifyView() <= dbinvShowPreview) ? item->notificationText() : lang(lng_notification_preview); + QString title = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->history()->peer->name : qsl("Telegram Desktop"); + QString subtitle = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->notificationHeader() : QString(); + QPixmap pix = (!App::passcoded() && cNotifyView() <= dbinvShowName) ? item->history()->peer->photo->pix(st::notifyMacPhotoSize) : QPixmap(); + QString msg = (!App::passcoded() && cNotifyView() <= dbinvShowPreview) ? item->notificationText() : lang(lng_notification_preview); - _private.showNotify(item->history()->peer->id, pix, title, subtitle, msg, (cNotifyView() <= dbinvShowPreview)); + _private.showNotify(item->history()->peer->id, pix, title, subtitle, msg, !App::passcoded() && (cNotifyView() <= dbinvShowPreview)); } bool PsMainWindow::eventFilter(QObject *obj, QEvent *evt) { @@ -924,6 +931,10 @@ void psUserActionDone() { _lastUserAction = getms(true); } +bool psIdleSupported() { + return objc_idleSupported(); +} + uint64 psIdleTime() { int64 idleTime = 0; return objc_idleTime(idleTime) ? idleTime : (getms(true) - _lastUserAction); diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h index 1fb495f9c..0afd935d4 100644 --- a/Telegram/SourceFiles/pspecific_mac.h +++ b/Telegram/SourceFiles/pspecific_mac.h @@ -195,6 +195,7 @@ private: }; void psUserActionDone(); +bool psIdleSupported(); uint64 psIdleTime(); bool psSkipAudioNotify(); diff --git a/Telegram/SourceFiles/pspecific_mac_p.h b/Telegram/SourceFiles/pspecific_mac_p.h index 0f9c637d9..1bb506cb1 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.h +++ b/Telegram/SourceFiles/pspecific_mac_p.h @@ -57,6 +57,7 @@ void objc_activateWnd(WId winId); void objc_debugShowAlert(const QString &str); void objc_outputDebugString(const QString &str); +bool objc_idleSupported(); bool objc_idleTime(int64 &idleTime); bool objc_showOpenWithMenu(int x, int y, const QString &file); diff --git a/Telegram/SourceFiles/pspecific_mac_p.mm b/Telegram/SourceFiles/pspecific_mac_p.mm index 33ef8834a..b562192e8 100644 --- a/Telegram/SourceFiles/pspecific_mac_p.mm +++ b/Telegram/SourceFiles/pspecific_mac_p.mm @@ -149,14 +149,12 @@ public: void onNotifyClick(NSUserNotification *notification) { NSNumber *peerObj = [[notification userInfo] objectForKey:@"peer"]; unsigned long long peerLong = peerObj ? [peerObj unsignedLongLongValue] : 0; - LOG(("Received notification click with peer %1").arg(peerLong)); wnd->notifyClicked(peerLong); } void onNotifyReply(NSUserNotification *notification) { NSNumber *peerObj = [[notification userInfo] objectForKey:@"peer"]; unsigned long long peerLong = peerObj ? [peerObj unsignedLongLongValue] : 0; - LOG(("Received notification reply with peer %1").arg(peerLong)); wnd->notifyReplied(peerLong, [[[notification response] string] UTF8String]); } @@ -328,6 +326,11 @@ PsMacWindowPrivate::~PsMacWindowPrivate() { delete data; } +bool objc_idleSupported() { + int64 idleTime = 0; + return objc_idleTime(idleTime); +} + bool objc_idleTime(int64 &idleTime) { // taken from https://github.com/trueinteractions/tint/issues/53 CFMutableDictionaryRef properties = 0; CFTypeRef obj; diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp index 03f8bd0fc..287b9987b 100644 --- a/Telegram/SourceFiles/pspecific_wnd.cpp +++ b/Telegram/SourceFiles/pspecific_wnd.cpp @@ -22,6 +22,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "application.h" #include "mainwidget.h" +#include "localstorage.h" + #include #include #include @@ -717,6 +719,10 @@ namespace { } switch (msg) { + case WM_TIMECHANGE: { + App::wnd()->checkAutoLockIn(100); + } return false; + case WM_WTSSESSION_CHANGE: { if (wParam == WTS_SESSION_LOGOFF || wParam == WTS_SESSION_LOCK) { sessionLoggedOff = true; @@ -1135,7 +1141,7 @@ void PsMainWindow::psSavePosition(Qt::WindowState state) { if (curPos.w >= st::wndMinWidth && curPos.h >= st::wndMinHeight) { if (curPos.x != pos.x || curPos.y != pos.y || curPos.w != pos.w || curPos.h != pos.h || curPos.moncrc != pos.moncrc || curPos.maximized != pos.maximized) { cSetWindowPos(curPos); - App::writeConfig(); + Local::writeSettings(); } } } @@ -1752,6 +1758,7 @@ namespace { if (len && len < nameBufSize) { if (QRegularExpression(qsl("^Telegram(\\s*\\(\\d+\\))?$")).match(QString::fromStdWString(nameBuf)).hasMatch()) { BOOL res = ::SetForegroundWindow(hWnd); + ::SetFocus(hWnd); return FALSE; } } @@ -1769,6 +1776,12 @@ void psUserActionDone() { if (sessionLoggedOff) sessionLoggedOff = false; } +bool psIdleSupported() { + LASTINPUTINFO lii; + lii.cbSize = sizeof(LASTINPUTINFO); + return GetLastInputInfo(&lii); +} + uint64 psIdleTime() { LASTINPUTINFO lii; lii.cbSize = sizeof(LASTINPUTINFO); diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h index 311d512e7..8be2c9159 100644 --- a/Telegram/SourceFiles/pspecific_wnd.h +++ b/Telegram/SourceFiles/pspecific_wnd.h @@ -168,6 +168,7 @@ private: }; void psUserActionDone(); +bool psIdleSupported(); uint64 psIdleTime(); bool psSkipAudioNotify(); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 3d1733569..93cfa1afa 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -20,6 +20,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "settings.h" #include "lang.h" +mtpDcOptions gDcOptions; + bool gTestMode = false; bool gDebug = false; bool gManyInstance = false; @@ -76,6 +78,9 @@ bool gCompressPastedImage = true; QString gTimeFormat = qsl("hh:mm"); +int32 gAutoLock = 3600; +bool gHasPasscode = false; + DBIEmojiTab gEmojiTab = dbietRecent; RecentEmojiPack gRecentEmojis; RecentEmojiPreload gRecentEmojisPreload; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index d6748ebae..d793fef5d 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -41,6 +41,18 @@ inline void cSet##Name(const Type &Name) { \ g##Name = Name; \ } +struct mtpDcOption { + mtpDcOption(int _id, const string &_host, const string &_ip, int _port) : id(_id), host(_host), ip(_ip), port(_port) { + } + + int id; + string host; + string ip; + int port; +}; +typedef QMap mtpDcOptions; +DeclareSetting(mtpDcOptions, DcOptions); + DeclareSetting(bool, TestMode); DeclareSetting(QString, LoggedPhoneNumber); DeclareReadSetting(uint32, ConnectionsInSession); @@ -108,6 +120,9 @@ DeclareSetting(DBIScale, ConfigScale); DeclareSetting(bool, CompressPastedImage); DeclareSetting(QString, TimeFormat); +DeclareSetting(int32, AutoLock); +DeclareSetting(bool, HasPasscode); + inline void cChangeTimeFormat(const QString &newFormat) { if (!newFormat.isEmpty()) cSetTimeFormat(newFormat); } diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp index d4ce0095d..d753602f6 100644 --- a/Telegram/SourceFiles/settingswidget.cpp +++ b/Telegram/SourceFiles/settingswidget.cpp @@ -31,6 +31,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "boxes/downloadpathbox.h" #include "boxes/usernamebox.h" #include "boxes/languagebox.h" +#include "boxes/passcodebox.h" +#include "boxes/autolockbox.h" #include "langloaderplain.h" #include "gui/filedialog.h" @@ -169,7 +171,14 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent), _storageClearFailedWidth(st::linkFont->m.width(lang(lng_local_storage_clear_failed))), // advanced + _passcodeEdit(this, lang(cHasPasscode() ? lng_passcode_change : lng_passcode_turn_on)), + _passcodeTurnOff(this, lang(lng_passcode_turn_off)), + _autoLock(this, (cAutoLock() % 3600) ? lng_passcode_autolock_minutes(lt_count, cAutoLock() / 60) : lng_passcode_autolock_hours(lt_count, cAutoLock() / 3600)), + _autoLockText(lang(psIdleSupported() ? lng_passcode_autolock_away : lng_passcode_autolock_inactive) + ' '), + _autoLockWidth(st::linkFont->m.width(_autoLockText)), _connectionType(this, lng_connection_auto(lt_type, QString())), + _connectionTypeText(lang(lng_connection_type) + ' '), + _connectionTypeWidth(st::linkFont->m.width(_connectionTypeText)), _resetSessions(this, lang(lng_settings_reset)), _logOut(this, lang(lng_settings_logout), st::btnLogout), _resetDone(false) @@ -264,13 +273,13 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : QWidget(parent), } // advanced + connect(&_passcodeEdit, SIGNAL(clicked()), this, SLOT(onPasscode())); + connect(&_passcodeTurnOff, SIGNAL(clicked()), this, SLOT(onPasscodeOff())); + connect(&_autoLock, SIGNAL(clicked()), this, SLOT(onAutoLock())); connect(&_connectionType, SIGNAL(clicked()), this, SLOT(onConnectionType())); connect(&_resetSessions, SIGNAL(clicked()), this, SLOT(onResetSessions())); connect(&_logOut, SIGNAL(clicked()), App::wnd(), SLOT(onLogout())); - _connectionTypeText = lang(lng_connection_type) + ' '; - _connectionTypeWidth = st::linkFont->m.width(_connectionTypeText); - if (App::main()) { connect(App::main(), SIGNAL(peerUpdated(PeerData*)), this, SLOT(peerUpdated(PeerData*))); } @@ -583,7 +592,15 @@ void SettingsInner::paintEvent(QPaintEvent *e) { p.setFont(st::linkFont->f); p.setPen(st::black->p); - p.drawText(_left + st::setHeaderLeft, _connectionType.y() + st::linkFont->ascent, _connectionTypeText); + if (self()) { + top += _passcodeEdit.height() + st::setLittleSkip; + if (cHasPasscode()) { + p.drawText(_left, top + st::linkFont->ascent, _autoLockText); + top += _autoLock.height() + st::setLittleSkip; + } + } + + p.drawText(_left, _connectionType.y() + st::linkFont->ascent, _connectionTypeText); if (self() && _resetDone) { p.drawText(_resetSessions.x(), _resetSessions.y() + st::linkFont->ascent, lang(lng_settings_reset_done)); @@ -681,7 +698,15 @@ void SettingsInner::resizeEvent(QResizeEvent *e) { // advanced top += st::setHeaderSkip; - _connectionType.move(_left + st::setHeaderLeft + _connectionTypeWidth, top); top += _connectionType.height() + st::setLittleSkip; + if (self()) { + _passcodeEdit.move(_left, top); + _passcodeTurnOff.move(_left + st::setWidth - _passcodeTurnOff.width(), top); top += _passcodeTurnOff.height() + st::setLittleSkip; + if (cHasPasscode()) { + _autoLock.move(_left + _autoLockWidth, top); top += _autoLock.height() + st::setLittleSkip; + } + } + + _connectionType.move(_left + _connectionTypeWidth, top); top += _connectionType.height() + st::setLittleSkip; if (self()) { _resetSessions.move(_left, top); top += _resetSessions.height() + st::setSectionSkip; _logOut.move(_left, top); @@ -775,6 +800,13 @@ void SettingsInner::updateConnectionType() { } } +void SettingsInner::passcodeChanged() { + resizeEvent(0); + _passcodeEdit.setText(lang(cHasPasscode() ? lng_passcode_change : lng_passcode_turn_on)); + _autoLock.setText((cAutoLock() % 3600) ? lng_passcode_autolock_minutes(lt_count, cAutoLock() / 60) : lng_passcode_autolock_hours(lt_count, cAutoLock() / 3600)); + showAll(); +} + void SettingsInner::updateBackgroundRect() { update(_left, _tileBackground.y() - st::setLittleSkip - st::setBackgroundSize, st::setBackgroundSize, st::setBackgroundSize); } @@ -918,6 +950,14 @@ void SettingsInner::showAll() { // advanced if (self()) { + _passcodeEdit.show(); + if (cHasPasscode()) { + _autoLock.show(); + _passcodeTurnOff.show(); + } else { + _autoLock.hide(); + _passcodeTurnOff.hide(); + } if (_resetDone) { _resetSessions.hide(); } else { @@ -925,6 +965,9 @@ void SettingsInner::showAll() { } _logOut.show(); } else { + _passcodeEdit.hide(); + _autoLock.hide(); + _passcodeTurnOff.hide(); _resetSessions.hide(); _logOut.hide(); } @@ -1020,7 +1063,7 @@ void SettingsInner::onChangeLanguage() { void SettingsInner::onSaveTestLang() { cSetLangFile(_testlang); cSetLang(languageTest); - App::writeConfig(); + Local::writeSettings(); cSetRestarting(true); App::quit(); } @@ -1033,7 +1076,7 @@ void SettingsInner::onUpdateLocalStorage() { void SettingsInner::onAutoUpdate() { cSetAutoUpdate(!cAutoUpdate()); - App::writeConfig(); + Local::writeSettings(); resizeEvent(0); if (cAutoUpdate()) { App::app()->startUpdateCheck(); @@ -1068,6 +1111,24 @@ void SettingsInner::onRestartNow() { App::quit(); } +void SettingsInner::onPasscode() { + PasscodeBox *box = new PasscodeBox(); + connect(box, SIGNAL(closed()), this, SLOT(passcodeChanged())); + App::wnd()->showLayer(box); +} + +void SettingsInner::onPasscodeOff() { + PasscodeBox *box = new PasscodeBox(true); + connect(box, SIGNAL(closed()), this, SLOT(passcodeChanged())); + App::wnd()->showLayer(box); +} + +void SettingsInner::onAutoLock() { + AutoLockBox *box = new AutoLockBox(); + connect(box, SIGNAL(closed()), this, SLOT(passcodeChanged())); + App::wnd()->showLayer(box); +} + void SettingsInner::onConnectionType() { ConnectionBox *box = new ConnectionBox(); connect(box, SIGNAL(closed()), this, SLOT(updateConnectionType()), Qt::QueuedConnection); @@ -1090,7 +1151,7 @@ void SettingsInner::onWorkmodeTray() { } cSetWorkMode(newMode); App::wnd()->psUpdateWorkmode(); - App::writeConfig(); + Local::writeSettings(); } void SettingsInner::onWorkmodeWindow() { @@ -1103,7 +1164,7 @@ void SettingsInner::onWorkmodeWindow() { } cSetWorkMode(newMode); App::wnd()->psUpdateWorkmode(); - App::writeConfig(); + Local::writeSettings(); } void SettingsInner::onAutoStart() { @@ -1114,19 +1175,19 @@ void SettingsInner::onAutoStart() { _startMinimized.setChecked(false); } else { psAutoStart(_autoStart.checked()); - App::writeConfig(); + Local::writeSettings(); } } void SettingsInner::onStartMinimized() { cSetStartMinimized(_startMinimized.checked()); - App::writeConfig(); + Local::writeSettings(); } void SettingsInner::onSendToMenu() { cSetSendToMenu(_sendToMenu.checked()); psSendToMenu(_sendToMenu.checked()); - App::writeConfig(); + Local::writeSettings(); } void SettingsInner::onScaleAuto() { @@ -1164,7 +1225,7 @@ void SettingsInner::setScale(DBIScale newScale) { if (cConfigScale() == newScale) return; cSetConfigScale(newScale); - App::writeConfig(); + Local::writeSettings(); App::wnd()->getTitle()->showUpdateBtn(); if (newScale == dbisAuto && !_dpiAutoScale.checked()) { _dpiAutoScale.setChecked(true); @@ -1184,7 +1245,7 @@ void SettingsInner::setScale(DBIScale newScale) { void SettingsInner::onSoundNotify() { cSetSoundNotify(_soundNotify.checked()); - App::writeUserConfig(); + Local::writeUserSettings(); } void SettingsInner::onDesktopNotify() { @@ -1193,11 +1254,11 @@ void SettingsInner::onDesktopNotify() { App::wnd()->notifyClear(); _senderName.setDisabled(true); _messagePreview.setDisabled(true); - App::writeUserConfig(); + Local::writeUserSettings(); } else { _senderName.setDisabled(false); _messagePreview.setDisabled(!_senderName.checked()); - App::writeUserConfig(); + Local::writeUserSettings(); } } @@ -1213,7 +1274,7 @@ void SettingsInner::onSenderName() { } else { cSetNotifyView(dbinvShowNothing); } - App::writeUserConfig(); + Local::writeUserSettings(); App::wnd()->notifyUpdateAll(); } } @@ -1226,13 +1287,13 @@ void SettingsInner::onMessagePreview() { } else { cSetNotifyView(dbinvShowNothing); } - App::writeUserConfig(); + Local::writeUserSettings(); App::wnd()->notifyUpdateAll(); } void SettingsInner::onReplaceEmojis() { cSetReplaceEmojis(_replaceEmojis.checked()); - App::writeUserConfig(); + Local::writeUserSettings(); if (_replaceEmojis.checked()) { _viewEmojis.show(); @@ -1248,14 +1309,14 @@ void SettingsInner::onViewEmojis() { void SettingsInner::onEnterSend() { if (_enterSend.checked()) { cSetCtrlEnter(false); - App::writeUserConfig(); + Local::writeUserSettings(); } } void SettingsInner::onCtrlEnterSend() { if (_ctrlEnterSend.checked()) { cSetCtrlEnter(true); - App::writeUserConfig(); + Local::writeUserSettings(); } } @@ -1323,13 +1384,13 @@ void SettingsInner::onTileBackground() { if (cTileBackground() != _tileBackground.checked()) { cSetTileBackground(_tileBackground.checked()); if (App::main()) App::main()->clearCachedBackground(); - App::writeUserConfig(); + Local::writeUserSettings(); } } void SettingsInner::onDontAskDownloadPath() { cSetAskDownloadPath(!_dontAskDownloadPath.checked()); - App::writeUserConfig(); + Local::writeUserSettings(); showAll(); resizeEvent(0); diff --git a/Telegram/SourceFiles/settingswidget.h b/Telegram/SourceFiles/settingswidget.h index d05afbbb5..851cde1c0 100644 --- a/Telegram/SourceFiles/settingswidget.h +++ b/Telegram/SourceFiles/settingswidget.h @@ -86,6 +86,8 @@ public slots: void usernameChanged(); void updateConnectionType(); + void passcodeChanged(); + void updateBackgroundRect(); void peerUpdated(PeerData *data); @@ -97,6 +99,10 @@ public slots: void onCheckNow(); void onRestartNow(); + void onPasscode(); + void onPasscodeOff(); + void onAutoLock(); + void onConnectionType(); void onUsername(); @@ -242,11 +248,14 @@ private: TempDirClearState _storageClearState; // advanced - LinkButton _connectionType, _resetSessions; - FlatButton _logOut; - + LinkButton _passcodeEdit, _passcodeTurnOff, _autoLock; + QString _autoLockText; + int32 _autoLockWidth; + LinkButton _connectionType; QString _connectionTypeText; int32 _connectionTypeWidth; + LinkButton _resetSessions; + FlatButton _logOut; bool _resetDone; diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp index 3570178c8..6095ba43d 100644 --- a/Telegram/SourceFiles/sysbuttons.cpp +++ b/Telegram/SourceFiles/sysbuttons.cpp @@ -20,6 +20,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "lang.h" #include "sysbuttons.h" +#include "passcodewidget.h" #include "window.h" #include "application.h" @@ -78,6 +79,11 @@ void SysBtn::paintEvent(QPaintEvent *e) { } } +void SysBtn::setSysBtnStyle(const style::sysButton &st) { + _st = st; + update(); +} + HitTestType SysBtn::hitTest(const QPoint &p) const { int x(p.x()), y(p.y()), w(width()), h(height()); if (x >= 0 && y >= 0 && x < w && y < h && isVisible()) { @@ -145,3 +151,15 @@ void UpdateBtn::onClick() { } App::quit(); } + +LockBtn::LockBtn(QWidget *parent, Window *window) : SysBtn(parent, st::sysLock), wnd(window) { + connect(this, SIGNAL(clicked()), this, SLOT(onClick())); +} + +void LockBtn::onClick() { + if (App::passcoded()) { + App::wnd()->passcodeWidget()->onSubmit(); + } else { + App::wnd()->setupPasscode(true); + } +} diff --git a/Telegram/SourceFiles/sysbuttons.h b/Telegram/SourceFiles/sysbuttons.h index 584aa4f45..49174154d 100644 --- a/Telegram/SourceFiles/sysbuttons.h +++ b/Telegram/SourceFiles/sysbuttons.h @@ -32,6 +32,7 @@ public: void setText(const QString &text); void paintEvent(QPaintEvent *e); + void setSysBtnStyle(const style::sysButton &st); HitTestType hitTest(const QPoint &p) const; @@ -131,3 +132,19 @@ private: Window *wnd; }; + +class LockBtn : public SysBtn { + Q_OBJECT + +public: + + LockBtn(QWidget *parent, Window *window); + +public slots: + + void onClick(); + +private: + + Window *wnd; +}; diff --git a/Telegram/SourceFiles/title.cpp b/Telegram/SourceFiles/title.cpp index 7ec146d1e..564b7c7a5 100644 --- a/Telegram/SourceFiles/title.cpp +++ b/Telegram/SourceFiles/title.cpp @@ -56,6 +56,7 @@ TitleWidget::TitleWidget(Window *window) , _settings(this, lang(lng_menu_settings), st::titleTextButton) , _contacts(this, lang(lng_menu_contacts), st::titleTextButton) , _about(this, lang(lng_menu_about), st::titleTextButton) + , _lock(this, window) , _update(this, window, lang(lng_menu_update)) , _minimize(this, window) , _maximize(this, window) @@ -64,10 +65,11 @@ TitleWidget::TitleWidget(Window *window) , lastMaximized(!(window->windowState() & Qt::WindowMaximized)) { setGeometry(0, 0, wnd->width(), st::titleHeight); + _lock.hide(); _update.hide(); _cancel.hide(); _back.hide(); - if (App::app()->updatingState() == Application::UpdatingReady) { + if (App::app()->updatingState() == Application::UpdatingReady || cHasPasscode()) { showUpdateBtn(); } stateChanged(); @@ -155,6 +157,11 @@ void TitleWidget::resizeEvent(QResizeEvent *e) { if (!_update.isHidden()) { p.setX(p.x() - _update.width()); _update.move(p); + if (!_lock.isHidden()) { + p.setX(p.x() - _lock.width()); + _lock.move(p); + p.setX(p.x() + _lock.width()); + } p.setX(p.x() + _update.width()); } _cancel.move(p.x() - _cancel.width(), 0); @@ -169,15 +176,20 @@ void TitleWidget::resizeEvent(QResizeEvent *e) { p.setX(p.x() - _minimize.width()); _minimize.move(p); } + if (_update.isHidden() && !_lock.isHidden()) { + p.setX(p.x() - _lock.width()); + _lock.move(p); + } + _settings.move(st::titleMenuOffset, 0); _back.move(st::titleMenuOffset, 0); _back.resize((_minimize.isHidden() ? (_update.isHidden() ? width() : _update.x()) : _minimize.x()) - st::titleMenuOffset, _back.height()); - if (MTP::authedId() && _back.isHidden() && _cancel.isHidden()) { - _contacts.show(); + if (MTP::authedId() && _back.isHidden() && _cancel.isHidden() && !App::passcoded()) { + if (_contacts.isHidden()) _contacts.show(); _contacts.move(_settings.x() + _settings.width(), 0); _about.move(_contacts.x() + _contacts.width(), 0); } else { - _contacts.hide(); + if (!_contacts.isHidden()) _contacts.hide(); if (!MTP::authedId()) _about.move(_settings.x() + _settings.width(), 0); } @@ -185,31 +197,41 @@ void TitleWidget::resizeEvent(QResizeEvent *e) { } void TitleWidget::updateBackButton() { - if (!cWideMode() && App::main() && App::main()->selectingPeer()) { - _cancel.show(); + if (App::passcoded()) { + if (!_cancel.isHidden()) _cancel.hide(); if (!_back.isHidden()) _back.hide(); if (!_settings.isHidden()) _settings.hide(); if (!_contacts.isHidden()) _contacts.hide(); if (!_about.isHidden()) _about.hide(); + _lock.setSysBtnStyle(st::sysUnlock); } else { - if (!_cancel.isHidden()) _cancel.hide(); - bool authed = (MTP::authedId() > 0); - if (cWideMode()) { + _lock.setSysBtnStyle(st::sysLock); + if (!cWideMode() && App::main() && App::main()->selectingPeer()) { + _cancel.show(); if (!_back.isHidden()) _back.hide(); - if (_settings.isHidden()) _settings.show(); - if (authed && _contacts.isHidden()) _contacts.show(); - if (_about.isHidden()) _about.show(); + if (!_settings.isHidden()) _settings.hide(); + if (!_contacts.isHidden()) _contacts.hide(); + if (!_about.isHidden()) _about.hide(); } else { - if (App::wnd()->needBackButton()) { - if (_back.isHidden()) _back.show(); - if (!_settings.isHidden()) _settings.hide(); - if (!_contacts.isHidden()) _contacts.hide(); - if (!_about.isHidden()) _about.hide(); - } else { + if (!_cancel.isHidden()) _cancel.hide(); + bool authed = (MTP::authedId() > 0); + if (cWideMode()) { if (!_back.isHidden()) _back.hide(); if (_settings.isHidden()) _settings.show(); if (authed && _contacts.isHidden()) _contacts.show(); if (_about.isHidden()) _about.show(); + } else { + if (App::wnd()->needBackButton()) { + if (_back.isHidden()) _back.show(); + if (!_settings.isHidden()) _settings.hide(); + if (!_contacts.isHidden()) _contacts.hide(); + if (!_about.isHidden()) _about.hide(); + } else { + if (!_back.isHidden()) _back.hide(); + if (_settings.isHidden()) _settings.show(); + if (authed && _contacts.isHidden()) _contacts.show(); + if (_about.isHidden()) _about.show(); + } } } } @@ -281,6 +303,7 @@ void TitleWidget::stateChanged(Qt::WindowState state) { void TitleWidget::showUpdateBtn() { if (!cWideMode() && App::main() && App::main()->selectingPeer()) { _cancel.show(); + _lock.hide(); _update.hide(); _minimize.hide(); _restore.hide(); @@ -288,6 +311,11 @@ void TitleWidget::showUpdateBtn() { _close.hide(); return; } + if (cHasPasscode()) { + _lock.show(); + } else { + _lock.hide(); + } bool updateReady = App::app()->updatingState() == Application::UpdatingReady; if (updateReady || cEvalScale(cConfigScale()) != cEvalScale(cRealScale())) { _update.setText(lang(updateReady ? lng_menu_update : lng_menu_restart)); @@ -338,6 +366,7 @@ HitTestType TitleWidget::hitTest(const QPoint &p) { if (x >= st::titleIconPos.x() && y >= st::titleIconPos.y() && x < st::titleIconPos.x() + st::titleIconImg.pxWidth() && y < st::titleIconPos.y() + st::titleIconImg.pxHeight()) { return HitTestIcon; } else if (false + || (_lock.hitTest(p - _lock.geometry().topLeft()) == HitTestSysButton && _lock.isVisible()) || (_update.hitTest(p - _update.geometry().topLeft()) == HitTestSysButton && _update.isVisible()) || (_minimize.hitTest(p - _minimize.geometry().topLeft()) == HitTestSysButton) || (_maximize.hitTest(p - _maximize.geometry().topLeft()) == HitTestSysButton) diff --git a/Telegram/SourceFiles/title.h b/Telegram/SourceFiles/title.h index 3879d35ee..5a785ce09 100644 --- a/Telegram/SourceFiles/title.h +++ b/Telegram/SourceFiles/title.h @@ -87,6 +87,7 @@ private: MaskedButton _back; FlatButton _cancel, _settings, _contacts, _about; + LockBtn _lock; UpdateBtn _update; MinimizeBtn _minimize; MaximizeBtn _maximize; diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 75821c3d9..2e3e33a82 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -226,7 +226,7 @@ enum DataBlockId { dbiKey = 0, dbiUser = 1, dbiDcOption = 2, - dbiConfig1 = 3, + dbiMaxGroupCount = 3, dbiMutePeer = 4, dbiSendKey = 5, dbiAutoStart = 6, @@ -257,6 +257,7 @@ enum DataBlockId { dbiLang = 31, dbiLangFile = 32, dbiTileBackground = 33, + dbiAutoLock = 34, dbiEncryptedWithSalt = 333, dbiEncrypted = 444, diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index f9099a1f6..77680becd 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org #include "pspecific.h" #include "title.h" +#include "passcodewidget.h" #include "intro/intro.h" #include "mainwidget.h" #include "layerwidget.h" @@ -156,7 +157,7 @@ void NotifyWindow::updateNotifyDisplay() { p.fillRect(st::notifyBorderWidth, h - st::notifyBorderWidth, w - st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder->b); p.fillRect(0, st::notifyBorderWidth, st::notifyBorderWidth, h - st::notifyBorderWidth, st::notifyBorder->b); - if (cNotifyView() <= dbinvShowName) { + if (!App::passcoded() && cNotifyView() <= dbinvShowName) { if (history->peer->photo->loaded()) { p.drawPixmap(st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), history->peer->photo->pix(st::notifyPhotoSize)); } else { @@ -172,7 +173,7 @@ void NotifyWindow::updateNotifyDisplay() { int32 itemWidth = w - st::notifyPhotoPos.x() - st::notifyPhotoSize - st::notifyTextLeft - st::notifyClosePos.x() - st::notifyClose.width; QRect rectForName(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyTextTop, itemWidth, st::msgNameFont->height); - if (cNotifyView() <= dbinvShowName) { + if (!App::passcoded() && cNotifyView() <= dbinvShowName) { if (history->peer->chat) { p.drawPixmap(QPoint(rectForName.left() + st::dlgChatImgLeft, rectForName.top() + st::dlgChatImgTop), App::sprite(), st::dlgChatImg); rectForName.setLeft(rectForName.left() + st::dlgChatImgSkip); @@ -188,7 +189,7 @@ void NotifyWindow::updateNotifyDisplay() { p.setPen(st::dlgDateColor->p); p.drawText(rectForName.left() + rectForName.width() + st::dlgDateSkip, rectForName.top() + st::dlgHistFont->ascent, dt); - if (cNotifyView() <= dbinvShowPreview) { + if (!App::passcoded() && cNotifyView() <= dbinvShowPreview) { const HistoryItem *textCachedFor = 0; Text itemTextCache(itemWidth); bool active = false; @@ -200,7 +201,7 @@ void NotifyWindow::updateNotifyDisplay() { } p.setPen(st::dlgNameColor->p); - if (cNotifyView() <= dbinvShowName) { + if (!App::passcoded() && cNotifyView() <= dbinvShowName) { history->nameText.drawElided(p, rectForName.left(), rectForName.top(), rectForName.width()); } else { p.setFont(st::msgNameFont->f); @@ -267,8 +268,13 @@ void NotifyWindow::mousePressEvent(QMouseEvent *e) { unlinkHistoryAndNotify(); } else if (history) { App::wnd()->showFromTray(); - App::wnd()->hideSettings(); - App::main()->showPeer(peer, 0, false, true); + if (App::passcoded()) { + App::wnd()->passcodeWidget()->setInnerFocus(); + App::wnd()->notifyClear(); + } else { + App::wnd()->hideSettings(); + App::main()->showPeer(peer, 0, false, true); + } e->ignore(); } } @@ -330,8 +336,8 @@ NotifyWindow::~NotifyWindow() { } Window::Window(QWidget *parent) : PsMainWindow(parent), _serviceHistoryRequest(0), title(0), -intro(0), main(0), settings(0), layerBG(0), _isActive(false), _topWidget(0), -_connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _mediaView(0) { +_passcode(0), intro(0), main(0), settings(0), layerBG(0), _isActive(false), _topWidget(0), +_connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _shouldLockAt(0), _mediaView(0) { icon16 = icon256.scaledToWidth(16, Qt::SmoothTransformation); icon32 = icon256.scaledToWidth(32, Qt::SmoothTransformation); @@ -359,6 +365,8 @@ _connecting(0), _clearManager(0), dragging(false), _inactivePress(false), _media _isActiveTimer.setSingleShot(true); connect(&_isActiveTimer, SIGNAL(timeout()), this, SLOT(updateIsActive())); + + connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock())); } void Window::inactivePress(bool inactive) { @@ -435,6 +443,11 @@ QWidget *Window::filedialogParent() { void Window::clearWidgets() { layerHidden(); + if (_passcode) { + _passcode->hide(); + _passcode->deleteLater(); + _passcode = 0; + } if (settings) { anim::stop(settings); settings->hide(); @@ -459,11 +472,72 @@ void Window::clearWidgets() { title->updateBackButton(); } +void Window::clearPasscode() { + if (!_passcode) return; + + QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)); + + anim::stop(_passcode); + _passcode->hide(); + _passcode->deleteLater(); + _passcode = 0; + if (intro) { + intro->animShow(bg, true); + } else { + main->animShow(bg, true); + } + notifyUpdateAll(); + title->updateBackButton(); +} + +void Window::setupPasscode(bool anim) { + QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)); + if (_passcode) { + anim::stop(_passcode); + _passcode->hide(); + _passcode->deleteLater(); + } + _passcode = new PasscodeWidget(this); + _passcode->move(0, st::titleHeight); + if (main) main->hide(); + if (settings) settings->hide(); + if (intro) intro->hide(); + if (anim) { + _passcode->animShow(bg); + } else { + _passcode->setInnerFocus(); + } + _shouldLockAt = 0; + notifyUpdateAll(); + title->updateBackButton(); +} + +void Window::checkAutoLockIn(int msec) { + if (_autoLockTimer.isActive()) { + int remain = _autoLockTimer.remainingTime(); + if (remain > 0 && remain <= msec) return; + } + _autoLockTimer.start(msec); +} + +void Window::checkAutoLock() { + if (!cHasPasscode() || App::passcoded()) return; + + App::app()->checkLocalTime(); + uint64 ms = getms(true), idle = psIdleTime(), should = cAutoLock() * 1000ULL; + if (idle >= should || (_shouldLockAt > 0 && ms > _shouldLockAt + 3000ULL)) { + setupPasscode(true); + } else { + _shouldLockAt = ms + (should - idle); + _autoLockTimer.start(should - idle); + } +} + void Window::setupIntro(bool anim) { cSetContactsReceived(false); if (intro && (intro->animating() || intro->isVisible()) && !main) return; - QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)); + QPixmap bg = anim ? myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)) : QPixmap(); clearWidgets(); intro = new IntroWidget(this); @@ -515,17 +589,22 @@ void Window::sendServiceHistoryRequest() { _serviceHistoryRequest = MTP::send(MTPmessages_GetHistory(user->input, MTP_int(0), MTP_int(0), MTP_int(1)), main->rpcDone(&MainWidget::serviceHistoryDone), main->rpcFail(&MainWidget::serviceHistoryFail)); } -void Window::setupMain(bool anim) { - QPixmap bg = myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)); +void Window::setupMain(bool anim, const MTPUser *self) { + QPixmap bg = anim ? myGrab(this, QRect(0, st::titleHeight, width(), height() - st::titleHeight)) : QPixmap(); clearWidgets(); main = new MainWidget(this); main->move(0, st::titleHeight); if (anim) { main->animShow(bg); } else { - MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), main->rpcDone(&MainWidget::startFull)); main->activate(); } + if (self) { + main->start(*self); + } else { + MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), main->rpcDone(&MainWidget::startFull)); + } + title->resizeEvent(0); fixOrder(); @@ -540,6 +619,8 @@ void Window::updateCounter() { } void Window::showSettings() { + if (_passcode) return; + if (isHidden()) showFromTray(); App::wnd()->hideLayer(); @@ -563,7 +644,7 @@ void Window::showSettings() { } void Window::hideSettings(bool fast) { - if (!settings) return; + if (!settings || _passcode) return; if (fast) { anim::stop(settings); @@ -595,11 +676,6 @@ void Window::hideSettings(bool fast) { fixOrder(); } -void Window::startMain(const MTPUser &user) { - if (main) main->start(user); - title->resizeEvent(0); -} - void Window::mtpStateChanged(int32 dc, int32 state) { if (dc == MTP::maindc()) { updateTitleStatus(); @@ -633,6 +709,10 @@ SettingsWidget *Window::settingsWidget() { return settings; } +PasscodeWidget *Window::passcodeWidget() { + return _passcode; +} + void Window::showPhoto(const PhotoLink *lnk, HistoryItem *item) { return lnk->peer() ? showPhoto(lnk->photo(), lnk->peer()) : showPhoto(lnk->photo(), item); } @@ -740,7 +820,13 @@ void Window::layerHidden() { } layerBG = 0; if (_mediaView && !_mediaView->isHidden()) _mediaView->hide(); - if (settings) { + setInnerFocus(); +} + +void Window::setInnerFocus() { + if (_passcode) { + _passcode->setInnerFocus(); + } else if (settings) { settings->setInnerFocus(); } else if (main) { main->setInnerFocus(); @@ -872,7 +958,7 @@ bool Window::minimizeToTray() { if (cPlatform() == dbipWindows && trayIcon && !cSeenTrayTooltip()) { trayIcon->showMessage(QString::fromStdWString(AppName), lang(lng_tray_icon_text), QSystemTrayIcon::Information, 10000); cSetSeenTrayTooltip(true); - App::writeConfig(); + Local::writeSettings(); } updateIsActive(cOfflineBlurTimeout()); updateTrayMenu(); @@ -1526,6 +1612,7 @@ QImage Window::iconWithCounter(int size, int count, style::color bg, bool smallI } void Window::sendPaths() { + if (App::passcoded()) return; if (_mediaView && !_mediaView->isHidden()) _mediaView->hide(); if (settings) { hideSettings(); diff --git a/Telegram/SourceFiles/window.h b/Telegram/SourceFiles/window.h index 374d7a473..df035006c 100644 --- a/Telegram/SourceFiles/window.h +++ b/Telegram/SourceFiles/window.h @@ -24,6 +24,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org class MediaView; class TitleWidget; +class PasscodeWidget; class IntroWidget; class MainWidget; class SettingsWidget; @@ -143,9 +144,11 @@ public: void updateWideMode(); bool needBackButton(); + void setupPasscode(bool anim); + void clearPasscode(); + void checkAutoLockIn(int msec); void setupIntro(bool anim); - void setupMain(bool anim); - void startMain(const MTPUser &user); + void setupMain(bool anim, const MTPUser *user = 0); void getNotifySetting(const MTPInputNotifyPeer &peer, uint32 msWait = 0); void serviceNotification(const QString &msg, bool unread = true, const MTPMessageMedia &media = MTP_messageMediaEmpty(), bool force = false); void sendServiceHistoryRequest(); @@ -164,6 +167,7 @@ public: IntroWidget *introWidget(); MainWidget *mainWidget(); SettingsWidget *settingsWidget(); + PasscodeWidget *passcodeWidget(); void showConnecting(const QString &text, const QString &reconnect = QString()); void hideConnecting(); @@ -234,10 +238,13 @@ public slots: void checkHistoryActivation(); void updateCounter(); + + void checkAutoLock(); void showSettings(); void hideSettings(bool fast = false); void layerHidden(); + void setInnerFocus(); void updateTitleStatus(); void quitFromTray(); @@ -279,6 +286,7 @@ private: mtpRequestId _serviceHistoryRequest; TitleWidget *title; + PasscodeWidget *_passcode; IntroWidget *intro; MainWidget *main; SettingsWidget *settings; @@ -300,6 +308,9 @@ private: bool _inactivePress; QTimer _inactiveTimer; + SingleTimer _autoLockTimer; + uint64 _shouldLockAt; + typedef QMap NotifyWhenMap; typedef QMap NotifyWhenMaps; NotifyWhenMaps notifyWhenMaps; diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist index e51047c06..b419da83c 100644 --- a/Telegram/Telegram.plist +++ b/Telegram/Telegram.plist @@ -11,7 +11,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.7.18 + 0.7.19 CFBundleSignature ???? CFBundleURLTypes diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc index 9fd03f866..cea54adc6 100644 Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj index e58af3357..6941e312b 100644 --- a/Telegram/Telegram.vcxproj +++ b/Telegram/Telegram.vcxproj @@ -170,6 +170,10 @@ true true + + true + true + true true @@ -318,6 +322,14 @@ true true + + true + true + + + true + true + true true @@ -334,6 +346,16 @@ true true + + true + true + true + + + true + true + true + true true @@ -398,6 +420,10 @@ true true + + true + true + true true @@ -546,6 +572,14 @@ true true + + true + true + + + true + true + true true @@ -562,6 +596,16 @@ true true + + true + true + true + + + true + true + true + true true @@ -635,6 +679,10 @@ true true + + true + true + true true @@ -783,6 +831,14 @@ true true + + true + true + + + true + true + true true @@ -799,6 +855,16 @@ true true + + true + true + true + + + true + true + true + true true @@ -846,6 +912,7 @@ + @@ -854,6 +921,7 @@ + @@ -905,7 +973,18 @@ + + + true + true + true + + + true + true + true + @@ -1190,6 +1269,34 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" "-fstdafx.h" "-f../../SourceFiles/boxes/backgroundbox.h" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing autolockbox.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/autolockbox.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing autolockbox.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/autolockbox.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing autolockbox.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/autolockbox.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing passcodebox.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/passcodebox.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing passcodebox.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/passcodebox.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing passcodebox.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/boxes/passcodebox.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + Moc%27ing animation.h... @@ -1745,7 +1852,60 @@ .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" "-fstdafx.h" "-f../../SourceFiles/overviewwidget.h" + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing passcodewidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/passcodewidget.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing passcodewidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/passcodewidget.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing passcodewidget.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/passcodewidget.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing pspecific_linux.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/pspecific_linux.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing pspecific_linux.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/pspecific_linux.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing pspecific_linux.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/pspecific_linux.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + true + true + true + + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing pspecific_mac.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/pspecific_mac.h" -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing pspecific_mac.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/pspecific_mac.h" -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing pspecific_mac.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/pspecific_mac.h" -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" + true + true + true + + + true + true + true + @@ -1808,6 +1968,11 @@ + + true + true + true + @@ -1817,4 +1982,4 @@ - + \ No newline at end of file diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters index 9960516b1..9366f5704 100644 --- a/Telegram/Telegram.vcxproj.filters +++ b/Telegram/Telegram.vcxproj.filters @@ -786,6 +786,66 @@ Generated Files\Release + + Source Files + + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + + + Source Files + + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + + + Source Files + + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + + + boxes + + + Generated Files\Deploy + + + Generated Files\Debug + + + Generated Files\Release + + + boxes + @@ -866,6 +926,9 @@ Source Files + + Source Files + @@ -1046,6 +1109,21 @@ boxes + + Source Files + + + Source Files + + + Source Files + + + boxes + + + boxes + @@ -1072,5 +1150,8 @@ langs + + Source Files + - + \ No newline at end of file diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj index 8bc2a9fe4..eb8a1ef58 100644 --- a/Telegram/Telegram.xcodeproj/project.pbxproj +++ b/Telegram/Telegram.xcodeproj/project.pbxproj @@ -1627,7 +1627,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.7.18; + CURRENT_PROJECT_VERSION = 0.7.19; DEBUG_INFORMATION_FORMAT = dwarf; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -1645,7 +1645,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 0.7.18; + CURRENT_PROJECT_VERSION = 0.7.19; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = fast; GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h; @@ -1671,10 +1671,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.7.18; + CURRENT_PROJECT_VERSION = 0.7.19; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 0.7; - DYLIB_CURRENT_VERSION = 0.7.18; + DYLIB_CURRENT_VERSION = 0.7.19; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -1812,10 +1812,10 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0.7.18; + CURRENT_PROJECT_VERSION = 0.7.19; DEBUG_INFORMATION_FORMAT = dwarf; DYLIB_COMPATIBILITY_VERSION = 0.7; - DYLIB_CURRENT_VERSION = 0.7.18; + DYLIB_CURRENT_VERSION = 0.7.19; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; diff --git a/Telegram/Version.sh b/Telegram/Version.sh index 0238c680a..d02406862 100755 --- a/Telegram/Version.sh +++ b/Telegram/Version.sh @@ -1,2 +1,2 @@ -echo 7018 0.7.18 1 +echo 7019 0.7.19 1 # AppVersion AppVersionStr DevChannel