2014-11-22 09:45:04 +00:00
/*
This file is part of Telegram Desktop ,
2018-01-03 10:23:14 +00:00
the official desktop application for the Telegram messaging service .
2014-11-22 09:45:04 +00:00
2018-01-03 10:23:14 +00:00
For license and copyright information please follow this link :
https : //github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2014-11-22 09:45:04 +00:00
*/
2017-03-04 10:23:56 +00:00
# include "storage/localstorage.h"
2020-06-09 16:57:05 +00:00
//
2017-03-04 10:23:56 +00:00
# include "storage/serialize_common.h"
2020-06-09 16:57:05 +00:00
# include "storage/storage_account.h"
# include "storage/details/storage_file_utilities.h"
# include "storage/details/storage_settings_scheme.h"
2020-04-09 12:27:53 +00:00
# include "data/data_session.h"
2020-06-09 16:57:05 +00:00
# include "data/data_document.h"
2020-04-09 12:27:53 +00:00
# include "data/data_document_media.h"
2020-02-11 12:00:04 +00:00
# include "base/platform/base_platform_info.h"
2020-06-09 16:57:05 +00:00
# include "ui/effects/animation_value.h"
2018-04-26 16:14:21 +00:00
# include "core/update_checker.h"
2019-02-13 12:36:59 +00:00
# include "media/audio/media_audio.h"
2017-02-23 06:57:04 +00:00
# include "mtproto/dc_options.h"
2019-01-21 13:42:21 +00:00
# include "core/application.h"
2019-07-24 11:45:24 +00:00
# include "main/main_session.h"
2019-08-26 16:36:23 +00:00
# include "window/themes/window_theme.h"
2019-09-13 06:06:02 +00:00
# include "facades.h"
2015-03-02 12:34:16 +00:00
2019-09-04 07:19:15 +00:00
# include <QtCore/QDirIterator>
2020-02-13 14:39:29 +00:00
# ifndef Q_OS_WIN
# include <unistd.h>
# endif // Q_OS_WIN
2020-06-09 16:57:05 +00:00
//extern "C" {
//#include <openssl/evp.h>
//} // extern "C"
2017-03-04 10:23:56 +00:00
2016-09-15 19:15:49 +00:00
namespace Local {
2014-11-22 09:45:04 +00:00
namespace {
2017-12-19 16:57:42 +00:00
constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024 ;
2019-02-19 06:57:53 +00:00
constexpr auto kFileLoaderQueueStopTimeout = crl : : time ( 5000 ) ;
2019-03-22 09:13:39 +00:00
2019-09-03 18:04:38 +00:00
const auto kThemeNewPathRelativeTag = qstr ( " special://new_tag " ) ;
2020-06-09 16:57:05 +00:00
using namespace Storage : : details ;
using Storage : : FileKey ;
2018-08-31 13:01:42 +00:00
using Database = Storage : : Cache : : Database ;
2014-11-22 09:45:04 +00:00
2018-08-27 11:35:58 +00:00
QString _basePath , _userBasePath , _userDbPath ;
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
bool _started = false ;
TaskQueue * _localLoader = nullptr ;
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
bool _working ( ) {
2020-06-16 06:42:47 +00:00
return ! _basePath . isEmpty ( ) ;
2016-09-15 19:15:49 +00:00
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
bool CheckStreamStatus ( QDataStream & stream ) {
2016-09-15 19:15:49 +00:00
if ( stream . status ( ) ! = QDataStream : : Ok ) {
LOG ( ( " Bad data stream status: %1 " ) . arg ( stream . status ( ) ) ) ;
return false ;
2015-03-02 12:34:16 +00:00
}
2016-09-15 19:15:49 +00:00
return true ;
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
QByteArray _settingsSalt ;
2017-02-25 16:44:02 +00:00
auto OldKey = MTP : : AuthKeyPtr ( ) ;
auto SettingsKey = MTP : : AuthKeyPtr ( ) ;
2016-10-28 12:44:28 +00:00
2018-07-19 14:58:40 +00:00
FileKey _themeKeyDay = 0 ;
FileKey _themeKeyNight = 0 ;
// Theme key legacy may be read in start() with settings.
2019-09-05 06:51:46 +00:00
// But it should be moved to keyDay or keyNight inside InitialLoadTheme()
2018-07-19 14:58:40 +00:00
// and never used after.
FileKey _themeKeyLegacy = 0 ;
2017-04-13 17:59:05 +00:00
FileKey _langPackKey = 0 ;
2018-10-30 11:23:54 +00:00
FileKey _languagesKey = 0 ;
2016-09-15 19:15:49 +00:00
2020-06-09 16:57:05 +00:00
int32 _oldSettingsVersion = 0 ;
2016-09-15 19:15:49 +00:00
2017-03-28 12:30:38 +00:00
enum class WriteMapWhen {
Now ,
Fast ,
Soon ,
2016-09-15 19:15:49 +00:00
} ;
2020-06-09 16:57:05 +00:00
void applyReadContext ( ReadSettingsContext & & context ) {
Core : : App ( ) . dcOptions ( ) - > addFromOther ( std : : move ( context . dcOptions ) ) ;
2016-09-15 19:15:49 +00:00
2020-06-09 16:57:05 +00:00
_themeKeyLegacy = context . themeKeyLegacy ;
_themeKeyDay = context . themeKeyDay ;
_themeKeyNight = context . themeKeyNight ;
_langPackKey = context . langPackKey ;
_languagesKey = context . languagesKey ;
}
2019-06-22 10:36:35 +00:00
2020-06-09 16:57:05 +00:00
bool _readOldSettings ( bool remove , ReadSettingsContext & context ) {
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 ) ;
2015-11-26 17:34:52 +00:00
2020-06-09 16:57:05 +00:00
qint32 version = 0 ;
while ( ! stream . atEnd ( ) ) {
quint32 blockId ;
stream > > blockId ;
if ( ! CheckStreamStatus ( stream ) ) break ;
2015-06-30 21:07:05 +00:00
2020-06-09 16:57:05 +00:00
if ( blockId = = dbiVersion ) {
stream > > version ;
if ( ! CheckStreamStatus ( stream ) ) break ;
2015-12-31 15:27:21 +00:00
2020-06-09 16:57:05 +00:00
if ( version > AppVersion ) break ;
} else if ( ! ReadSetting ( blockId , stream , version , context ) ) {
break ;
2015-12-31 15:27:21 +00:00
}
2014-12-05 13:44:27 +00:00
}
2020-06-09 16:57:05 +00:00
file . close ( ) ;
result = true ;
2016-09-15 19:15:49 +00:00
}
2020-06-09 16:57:05 +00:00
if ( remove ) file . remove ( ) ;
return result ;
2016-09-15 19:15:49 +00:00
}
2015-06-30 21:07:05 +00:00
2020-06-09 16:57:05 +00:00
void _readOldUserSettingsFields (
QIODevice * device ,
qint32 & version ,
ReadSettingsContext & context ) {
QDataStream stream ( device ) ;
stream . setVersion ( QDataStream : : Qt_5_1 ) ;
2016-09-15 19:15:49 +00:00
2020-06-09 16:57:05 +00:00
while ( ! stream . atEnd ( ) ) {
quint32 blockId ;
stream > > blockId ;
if ( ! CheckStreamStatus ( stream ) ) {
2016-09-15 19:15:49 +00:00
break ;
2014-12-05 13:44:27 +00:00
}
2015-06-30 21:07:05 +00:00
2020-06-09 16:57:05 +00:00
if ( blockId = = dbiVersion ) {
stream > > version ;
if ( ! CheckStreamStatus ( stream ) ) {
break ;
}
2015-12-31 15:27:21 +00:00
2020-06-09 16:57:05 +00:00
if ( version > AppVersion ) return ;
} else if ( blockId = = dbiEncryptedWithSalt ) {
QByteArray salt , data , decrypted ;
stream > > salt > > data ;
if ( ! CheckStreamStatus ( stream ) ) {
break ;
}
2014-12-05 13:44:27 +00:00
2020-06-09 16:57:05 +00:00
if ( salt . size ( ) ! = 32 ) {
LOG ( ( " App Error: bad salt in old user config encrypted part, size: %1 " ) . arg ( salt . size ( ) ) ) ;
continue ;
}
2020-06-15 16:25:02 +00:00
OldKey = CreateLegacyLocalKey ( QByteArray ( ) , salt ) ;
2015-09-09 08:19:25 +00:00
2020-06-09 16:57:05 +00:00
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 ;
2015-09-09 08:19:25 +00:00
}
2020-06-09 16:57:05 +00:00
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 , context ) ;
} else if ( ! ReadSetting ( blockId , stream , version , context ) ) {
return ;
2016-09-15 19:15:49 +00:00
}
}
}
2015-03-02 12:34:16 +00:00
2017-02-23 06:57:04 +00:00
bool _readOldUserSettings ( bool remove , ReadSettingsContext & context ) {
2016-09-15 19:15:49 +00:00
bool result = false ;
QFile file ( cWorkingDir ( ) + cDataFile ( ) + ( cTestMode ( ) ? qsl ( " _test " ) : QString ( ) ) + qsl ( " _config " ) ) ;
if ( file . open ( QIODevice : : ReadOnly ) ) {
LOG ( ( " App Info: reading old user config... " ) ) ;
qint32 version = 0 ;
2017-02-23 06:57:04 +00:00
_readOldUserSettingsFields ( & file , version , context ) ;
2016-09-15 19:15:49 +00:00
file . close ( ) ;
result = true ;
}
if ( remove ) file . remove ( ) ;
return result ;
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
void _readOldMtpDataFields (
QIODevice * device ,
qint32 & version ,
ReadSettingsContext & context ) {
2016-09-15 19:15:49 +00:00
QDataStream stream ( device ) ;
stream . setVersion ( QDataStream : : Qt_5_1 ) ;
2015-03-02 12:34:16 +00:00
2016-09-15 19:15:49 +00:00
while ( ! stream . atEnd ( ) ) {
quint32 blockId ;
stream > > blockId ;
2020-06-09 16:57:05 +00:00
if ( ! CheckStreamStatus ( stream ) ) {
2016-09-15 19:15:49 +00:00
break ;
}
2015-03-02 12:34:16 +00:00
2016-09-15 19:15:49 +00:00
if ( blockId = = dbiVersion ) {
stream > > version ;
2020-06-09 16:57:05 +00:00
if ( ! CheckStreamStatus ( stream ) ) {
2016-09-15 19:15:49 +00:00
break ;
2015-03-02 12:34:16 +00:00
}
2016-09-15 19:15:49 +00:00
if ( version > AppVersion ) return ;
} else if ( blockId = = dbiEncrypted ) {
QByteArray data , decrypted ;
stream > > data ;
2020-06-09 16:57:05 +00:00
if ( ! CheckStreamStatus ( stream ) ) {
2016-09-15 19:15:49 +00:00
break ;
}
2015-03-02 12:34:16 +00:00
2017-02-25 16:44:02 +00:00
if ( ! OldKey ) {
2016-09-15 19:15:49 +00:00
LOG ( ( " MTP Error: reading old encrypted keys without old key! " ) ) ;
continue ;
2015-03-02 12:34:16 +00:00
}
2016-09-15 19:15:49 +00:00
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 ;
2017-02-25 16:44:02 +00:00
aesDecryptLocal ( encrypted , decrypted . data ( ) , fullDataLen , OldKey , dataKey ) ;
2016-09-15 19:15:49 +00:00
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... " ) ) ;
2015-03-02 12:34:16 +00:00
2017-02-23 06:57:04 +00:00
_readOldMtpDataFields ( & decryptedStream , version , context ) ;
2020-06-09 16:57:05 +00:00
} else if ( ! ReadSetting ( blockId , stream , version , context ) ) {
2016-09-15 19:15:49 +00:00
return ;
}
}
}
2015-03-02 12:34:16 +00:00
2017-02-23 06:57:04 +00:00
bool _readOldMtpData ( bool remove , ReadSettingsContext & context ) {
2016-09-15 19:15:49 +00:00
bool result = false ;
2020-06-09 16:57:05 +00:00
const auto testPostfix = ( cTestMode ( ) ? qsl ( " _test " ) : QString ( ) ) ;
QFile file ( cWorkingDir ( ) + cDataFile ( ) + testPostfix ) ;
2016-09-15 19:15:49 +00:00
if ( file . open ( QIODevice : : ReadOnly ) ) {
LOG ( ( " App Info: reading old keys... " ) ) ;
qint32 version = 0 ;
2017-02-23 06:57:04 +00:00
_readOldMtpDataFields ( & file , version , context ) ;
2016-09-15 19:15:49 +00:00
file . close ( ) ;
result = true ;
}
if ( remove ) file . remove ( ) ;
return result ;
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
} // namespace
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
void finish ( ) {
2020-06-16 06:42:47 +00:00
delete base : : take ( _localLoader ) ;
2020-06-09 16:57:05 +00:00
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
void InitialLoadTheme ( ) ;
bool ApplyDefaultNightMode ( ) ;
void readLangPack ( ) ;
2020-06-08 17:24:36 +00:00
2020-06-09 16:57:05 +00:00
void start ( ) {
2020-06-16 06:42:47 +00:00
Expects ( _basePath . isEmpty ( ) ) ;
2017-02-15 08:50:11 +00:00
2020-06-09 16:57:05 +00:00
_localLoader = new TaskQueue ( kFileLoaderQueueStopTimeout ) ;
2017-02-15 08:50:11 +00:00
2020-06-09 16:57:05 +00:00
_basePath = cWorkingDir ( ) + qsl ( " tdata/ " ) ;
if ( ! QDir ( ) . exists ( _basePath ) ) QDir ( ) . mkpath ( _basePath ) ;
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
ReadSettingsContext context ;
FileReadDescriptor settingsData ;
if ( ! ReadFile ( settingsData , cTestMode ( ) ? qsl ( " settings_test " ) : qsl ( " settings " ) , _basePath ) ) {
_readOldSettings ( true , context ) ;
_readOldUserSettings ( false , context ) ; // needed further in _readUserSettings
_readOldMtpData ( false , context ) ; // needed further in _readMtpData
applyReadContext ( std : : move ( context ) ) ;
2020-05-12 10:04:53 +00:00
2020-06-09 16:57:05 +00:00
if ( ! ApplyDefaultNightMode ( ) ) {
writeSettings ( ) ;
}
return ;
2016-09-15 19:15:49 +00:00
}
2020-06-09 16:57:05 +00:00
LOG ( ( " App Info: reading settings... " ) ) ;
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
QByteArray salt , settingsEncrypted ;
settingsData . stream > > salt > > settingsEncrypted ;
if ( ! CheckStreamStatus ( settingsData . stream ) ) {
return writeSettings ( ) ;
}
2017-02-23 06:57:04 +00:00
2020-06-09 16:57:05 +00:00
if ( salt . size ( ) ! = LocalEncryptSaltSize ) {
LOG ( ( " App Error: bad salt in settings file, size: %1 " ) . arg ( salt . size ( ) ) ) ;
return writeSettings ( ) ;
}
2020-06-15 16:25:02 +00:00
SettingsKey = CreateLegacyLocalKey ( QByteArray ( ) , salt ) ;
2017-02-23 06:57:04 +00:00
2020-06-09 16:57:05 +00:00
EncryptedDescriptor settings ;
if ( ! DecryptLocal ( settings , settingsEncrypted , SettingsKey ) ) {
LOG ( ( " App Error: could not decrypt settings from settings file, maybe bad passcode... " ) ) ;
return writeSettings ( ) ;
2016-09-15 19:15:49 +00:00
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
LOG ( ( " App Info: reading encrypted settings... " ) ) ;
while ( ! settings . stream . atEnd ( ) ) {
2016-09-15 19:15:49 +00:00
quint32 blockId ;
2020-06-09 16:57:05 +00:00
settings . stream > > blockId ;
if ( ! CheckStreamStatus ( settings . stream ) ) {
return writeSettings ( ) ;
2016-09-15 19:15:49 +00:00
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
if ( ! ReadSetting ( blockId , settings . stream , settingsData . version , context ) ) {
return writeSettings ( ) ;
2016-09-15 19:15:49 +00:00
}
}
2020-06-09 16:57:05 +00:00
_oldSettingsVersion = settingsData . version ;
_settingsSalt = salt ;
2020-06-15 09:35:35 +00:00
applyReadContext ( std : : move ( context ) ) ;
2020-06-09 16:57:05 +00:00
InitialLoadTheme ( ) ;
readLangPack ( ) ;
2016-09-15 19:15:49 +00:00
}
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
void writeSettings ( ) {
if ( _basePath . isEmpty ( ) ) {
LOG ( ( " App Error: _basePath is empty in writeSettings() " ) ) ;
2016-09-15 19:15:49 +00:00
return ;
}
2016-02-21 14:27:54 +00:00
2020-06-09 16:57:05 +00:00
if ( ! QDir ( ) . exists ( _basePath ) ) QDir ( ) . mkpath ( _basePath ) ;
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
FileWriteDescriptor settings (
cTestMode ( ) ? qsl ( " settings_test " ) : qsl ( " settings " ) ,
_basePath ) ;
if ( _settingsSalt . isEmpty ( ) | | ! SettingsKey ) {
_settingsSalt . resize ( LocalEncryptSaltSize ) ;
memset_rand ( _settingsSalt . data ( ) , _settingsSalt . size ( ) ) ;
2020-06-15 16:25:02 +00:00
SettingsKey = CreateLegacyLocalKey ( QByteArray ( ) , _settingsSalt ) ;
2020-06-09 16:57:05 +00:00
}
settings . writeData ( _settingsSalt ) ;
2015-03-02 12:34:16 +00:00
2020-06-09 16:57:05 +00:00
const auto dcOptionsSerialized = Core : : App ( ) . dcOptions ( ) - > serialize ( ) ;
const auto applicationSettings = Core : : App ( ) . settings ( ) . serialize ( ) ;
2015-03-02 12:34:16 +00:00
2016-09-15 19:15:49 +00:00
quint32 size = 12 * ( sizeof ( quint32 ) + sizeof ( qint32 ) ) ;
2017-02-23 06:57:04 +00:00
size + = sizeof ( quint32 ) + Serialize : : bytearraySize ( dcOptionsSerialized ) ;
2019-08-23 13:52:59 +00:00
size + = sizeof ( quint32 ) + Serialize : : bytearraySize ( applicationSettings ) ;
2018-06-26 13:58:29 +00:00
size + = sizeof ( quint32 ) + Serialize : : stringSize ( Global : : TxtDomainString ( ) ) ;
2020-06-09 16:57:05 +00:00
size + = sizeof ( quint32 ) + Serialize : : stringSize ( cDialogLastPath ( ) ) ;
2015-03-02 12:34:16 +00:00
2018-04-27 17:26:45 +00:00
auto & proxies = Global : : RefProxiesList ( ) ;
const auto & proxy = Global : : SelectedProxy ( ) ;
auto proxyIt = ranges : : find ( proxies , proxy ) ;
2019-11-13 14:12:04 +00:00
if ( proxy . type ! = MTP : : ProxyData : : Type : : None
2018-04-27 17:26:45 +00:00
& & proxyIt = = end ( proxies ) ) {
2018-04-28 14:06:59 +00:00
proxies . push_back ( proxy ) ;
proxyIt = end ( proxies ) - 1 ;
2018-04-27 17:26:45 +00:00
}
size + = sizeof ( quint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) ;
for ( const auto & proxy : proxies ) {
size + = sizeof ( qint32 ) + Serialize : : stringSize ( proxy . host ) + sizeof ( qint32 ) + Serialize : : stringSize ( proxy . user ) + Serialize : : stringSize ( proxy . password ) ;
}
2017-06-27 22:03:37 +00:00
2018-07-19 14:58:40 +00:00
// Theme keys and night mode.
size + = sizeof ( quint32 ) + sizeof ( quint64 ) * 2 + sizeof ( quint32 ) ;
2017-04-13 17:59:05 +00:00
if ( _langPackKey ) {
size + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2016-10-28 12:44:28 +00:00
}
2017-08-02 15:07:28 +00:00
size + = sizeof ( quint32 ) + sizeof ( qint32 ) * 8 ;
2016-09-15 19:15:49 +00:00
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( dbiChatSizeMax ) < < qint32 ( Global : : ChatSizeMax ( ) ) ;
data . stream < < quint32 ( dbiMegagroupSizeMax ) < < qint32 ( Global : : MegagroupSizeMax ( ) ) ;
data . stream < < quint32 ( dbiSavedGifsLimit ) < < qint32 ( Global : : SavedGifsLimit ( ) ) ;
data . stream < < quint32 ( dbiStickersRecentLimit ) < < qint32 ( Global : : StickersRecentLimit ( ) ) ;
2017-08-02 15:07:28 +00:00
data . stream < < quint32 ( dbiStickersFavedLimit ) < < qint32 ( Global : : StickersFavedLimit ( ) ) ;
2016-09-15 19:15:49 +00:00
data . stream < < quint32 ( dbiAutoStart ) < < qint32 ( cAutoStart ( ) ) ;
data . stream < < quint32 ( dbiStartMinimized ) < < qint32 ( cStartMinimized ( ) ) ;
data . stream < < quint32 ( dbiSendToMenu ) < < qint32 ( cSendToMenu ( ) ) ;
2017-03-04 19:36:59 +00:00
data . stream < < quint32 ( dbiWorkMode ) < < qint32 ( Global : : WorkMode ( ) . value ( ) ) ;
2016-09-15 19:15:49 +00:00
data . stream < < quint32 ( dbiSeenTrayTooltip ) < < qint32 ( cSeenTrayTooltip ( ) ) ;
data . stream < < quint32 ( dbiAutoUpdate ) < < qint32 ( cAutoUpdate ( ) ) ;
data . stream < < quint32 ( dbiLastUpdateCheck ) < < qint32 ( cLastUpdateCheck ( ) ) ;
2018-10-15 11:44:48 +00:00
data . stream < < quint32 ( dbiScalePercent ) < < qint32 ( cConfigScale ( ) ) ;
2017-02-23 06:57:04 +00:00
data . stream < < quint32 ( dbiDcOptions ) < < dcOptionsSerialized ;
2019-08-23 13:52:59 +00:00
data . stream < < quint32 ( dbiApplicationSettings ) < < applicationSettings ;
2018-06-26 13:58:29 +00:00
data . stream < < quint32 ( dbiTxtDomainString ) < < Global : : TxtDomainString ( ) ;
2020-06-09 16:57:05 +00:00
data . stream < < quint32 ( dbiDialogLastPath ) < < cDialogLastPath ( ) ;
2018-09-20 17:56:45 +00:00
data . stream < < quint32 ( dbiAnimationsDisabled ) < < qint32 ( anim : : Disabled ( ) ? 1 : 0 ) ;
2015-03-02 12:34:16 +00:00
2018-04-27 17:26:45 +00:00
data . stream < < quint32 ( dbiConnectionType ) < < qint32 ( dbictProxiesList ) ;
data . stream < < qint32 ( proxies . size ( ) ) ;
2018-11-05 13:58:24 +00:00
data . stream < < qint32 ( proxyIt - begin ( proxies ) ) + 1 ;
data . stream < < qint32 ( Global : : ProxySettings ( ) ) ;
data . stream < < qint32 ( Global : : UseProxyForCalls ( ) ? 1 : 0 ) ;
2018-04-27 17:26:45 +00:00
for ( const auto & proxy : proxies ) {
data . stream < < qint32 ( kProxyTypeShift + int ( proxy . type ) ) ;
data . stream < < proxy . host < < qint32 ( proxy . port ) < < proxy . user < < proxy . password ;
}
2017-06-27 22:03:37 +00:00
2016-09-15 19:15:49 +00:00
data . stream < < quint32 ( dbiTryIPv6 ) < < qint32 ( Global : : TryIPv6 ( ) ) ;
2018-07-19 14:58:40 +00:00
data . stream
< < quint32 ( dbiThemeKey )
< < quint64 ( _themeKeyDay )
< < quint64 ( _themeKeyNight )
< < quint32 ( Window : : Theme : : IsNightMode ( ) ? 1 : 0 ) ;
2017-04-13 17:59:05 +00:00
if ( _langPackKey ) {
data . stream < < quint32 ( dbiLangPackKey ) < < quint64 ( _langPackKey ) ;
2016-10-28 12:44:28 +00:00
}
2018-10-30 11:23:54 +00:00
if ( _languagesKey ) {
data . stream < < quint32 ( dbiLanguagesKey ) < < quint64 ( _languagesKey ) ;
}
2015-03-02 12:34:16 +00:00
2017-07-03 12:23:41 +00:00
auto position = cWindowPos ( ) ;
data . stream < < quint32 ( dbiWindowPosition ) < < qint32 ( position . x ) < < qint32 ( position . y ) < < qint32 ( position . w ) < < qint32 ( position . h ) ;
data . stream < < qint32 ( position . moncrc ) < < qint32 ( position . maximized ) ;
DEBUG_LOG ( ( " Window Pos: Writing to storage %1, %2, %3, %4 (maximized %5) " ) . arg ( position . x ) . arg ( position . y ) . arg ( position . w ) . arg ( position . h ) . arg ( Logs : : b ( position . maximized ) ) ) ;
2015-03-02 12:34:16 +00:00
2017-02-25 16:44:02 +00:00
settings . writeEncrypted ( data , SettingsKey ) ;
2016-09-15 19:15:49 +00:00
}
2015-03-02 12:34:16 +00:00
2018-05-03 06:39:10 +00:00
const QString & AutoupdatePrefix ( const QString & replaceWith = { } ) {
2018-07-11 22:14:44 +00:00
Expects ( ! Core : : UpdaterDisabled ( ) ) ;
2018-05-03 06:39:10 +00:00
static auto value = QString ( ) ;
2020-06-09 16:57:05 +00:00
if ( ! replaceWith . isEmpty ( ) ) {
value = replaceWith ;
2016-02-12 16:35:06 +00:00
}
2020-06-09 16:57:05 +00:00
return value ;
2016-09-15 19:15:49 +00:00
}
2016-02-12 16:35:06 +00:00
2020-06-09 16:57:05 +00:00
QString autoupdatePrefixFile ( ) {
Expects ( ! Core : : UpdaterDisabled ( ) ) ;
2017-08-02 15:07:28 +00:00
2020-06-09 16:57:05 +00:00
return cWorkingDir ( ) + " tdata/prefix " ;
2016-09-15 19:15:49 +00:00
}
2015-09-29 18:44:31 +00:00
2020-06-09 16:57:05 +00:00
const QString & readAutoupdatePrefixRaw ( ) {
Expects ( ! Core : : UpdaterDisabled ( ) ) ;
const auto & result = AutoupdatePrefix ( ) ;
if ( ! result . isEmpty ( ) ) {
return result ;
}
QFile f ( autoupdatePrefixFile ( ) ) ;
if ( f . open ( QIODevice : : ReadOnly ) ) {
const auto value = QString : : fromUtf8 ( f . readAll ( ) ) ;
if ( ! value . isEmpty ( ) ) {
return AutoupdatePrefix ( value ) ;
2016-01-09 12:51:42 +00:00
}
2015-05-21 10:44:26 +00:00
}
2020-06-09 16:57:05 +00:00
return AutoupdatePrefix ( " https://updates.tdesktop.com " ) ;
2016-09-15 19:15:49 +00:00
}
2016-07-21 10:09:47 +00:00
2020-06-09 16:57:05 +00:00
void writeAutoupdatePrefix ( const QString & prefix ) {
if ( Core : : UpdaterDisabled ( ) ) {
return ;
}
2015-06-01 10:58:46 +00:00
2020-06-09 16:57:05 +00:00
const auto current = readAutoupdatePrefixRaw ( ) ;
if ( current ! = prefix ) {
AutoupdatePrefix ( prefix ) ;
QFile f ( autoupdatePrefixFile ( ) ) ;
if ( f . open ( QIODevice : : WriteOnly ) ) {
f . write ( prefix . toUtf8 ( ) ) ;
f . close ( ) ;
2015-05-19 15:46:45 +00:00
}
2020-06-09 16:57:05 +00:00
if ( cAutoUpdate ( ) ) {
Core : : UpdateChecker checker ;
checker . start ( ) ;
2015-06-28 12:37:10 +00:00
}
2015-05-19 15:46:45 +00:00
}
2016-09-15 19:15:49 +00:00
}
2015-05-19 15:46:45 +00:00
2020-06-09 16:57:05 +00:00
QString readAutoupdatePrefix ( ) {
Expects ( ! Core : : UpdaterDisabled ( ) ) ;
2015-05-19 15:46:45 +00:00
2020-06-09 16:57:05 +00:00
auto result = readAutoupdatePrefixRaw ( ) ;
return result . replace ( QRegularExpression ( " /+$ " ) , QString ( ) ) ;
}
2016-06-27 16:25:21 +00:00
2020-06-09 16:57:05 +00:00
void reset ( ) {
if ( _localLoader ) {
_localLoader - > stop ( ) ;
}
2016-01-09 12:51:42 +00:00
2020-06-09 16:57:05 +00:00
Window : : Theme : : Background ( ) - > reset ( ) ;
_oldSettingsVersion = 0 ;
}
2016-07-21 10:09:47 +00:00
2020-06-09 16:57:05 +00:00
int32 oldSettingsVersion ( ) {
return _oldSettingsVersion ;
2016-09-15 19:15:49 +00:00
}
2016-01-09 12:51:42 +00:00
2020-06-09 16:57:05 +00:00
class CountWaveformTask : public Task {
public :
CountWaveformTask ( not_null < Data : : DocumentMedia * > media )
: _doc ( media - > owner ( ) )
, _loc ( _doc - > location ( true ) )
, _data ( media - > bytes ( ) )
, _wavemax ( 0 ) {
if ( _data . isEmpty ( ) & & ! _loc . accessEnable ( ) ) {
_doc = nullptr ;
}
2018-07-19 14:58:40 +00:00
}
2020-06-09 16:57:05 +00:00
void process ( ) override {
if ( ! _doc ) return ;
2015-06-28 12:37:10 +00:00
2020-06-09 16:57:05 +00:00
_waveform = audioCountWaveform ( _loc , _data ) ;
_wavemax = _waveform . empty ( )
? char ( 0 )
: * ranges : : max_element ( _waveform ) ;
2017-01-18 10:26:33 +00:00
}
2020-06-09 16:57:05 +00:00
void finish ( ) override {
if ( const auto voice = _doc ? _doc - > voice ( ) : nullptr ) {
if ( ! _waveform . isEmpty ( ) ) {
voice - > waveform = _waveform ;
voice - > wavemax = _wavemax ;
}
if ( voice - > waveform . isEmpty ( ) ) {
voice - > waveform . resize ( 1 ) ;
voice - > waveform [ 0 ] = - 2 ;
voice - > wavemax = 0 ;
} else if ( voice - > waveform [ 0 ] < 0 ) {
voice - > waveform [ 0 ] = - 2 ;
voice - > wavemax = 0 ;
2019-01-29 07:29:38 +00:00
}
2020-06-10 10:49:10 +00:00
_doc - > owner ( ) . requestDocumentViewRepaint ( _doc ) ;
2018-07-19 14:58:40 +00:00
}
2016-09-15 19:15:49 +00:00
}
2020-06-09 16:57:05 +00:00
~ CountWaveformTask ( ) {
if ( _data . isEmpty ( ) & & _doc ) {
_loc . accessDisable ( ) ;
}
2016-09-15 19:15:49 +00:00
}
2015-05-19 15:46:45 +00:00
2020-06-09 16:57:05 +00:00
protected :
DocumentData * _doc = nullptr ;
FileLocation _loc ;
QByteArray _data ;
VoiceWaveform _waveform ;
char _wavemax ;
2015-05-19 15:46:45 +00:00
2020-06-09 16:57:05 +00:00
} ;
2016-06-27 16:25:21 +00:00
2020-06-09 16:57:05 +00:00
void countVoiceWaveform ( not_null < Data : : DocumentMedia * > media ) {
const auto document = media - > owner ( ) ;
if ( const auto voice = document - > voice ( ) ) {
if ( _localLoader ) {
voice - > waveform . resize ( 1 + sizeof ( TaskId ) ) ;
voice - > waveform [ 0 ] = - 1 ; // counting
TaskId taskId = _localLoader - > addTask (
std : : make_unique < CountWaveformTask > ( media ) ) ;
memcpy ( voice - > waveform . data ( ) + 1 , & taskId , sizeof ( taskId ) ) ;
2019-01-16 17:26:26 +00:00
}
2019-01-16 12:25:29 +00:00
}
2020-06-09 16:57:05 +00:00
}
2019-01-28 13:59:49 +00:00
2020-06-09 16:57:05 +00:00
void cancelTask ( TaskId id ) {
if ( _localLoader ) {
_localLoader - > cancelTask ( id ) ;
2016-07-21 10:09:47 +00:00
}
2016-09-15 19:15:49 +00:00
}
2016-07-21 10:09:47 +00:00
2018-07-19 14:58:40 +00:00
Window : : Theme : : Saved readThemeUsingKey ( FileKey key ) {
2019-09-03 18:04:38 +00:00
using namespace Window : : Theme ;
2016-10-28 12:44:28 +00:00
FileReadDescriptor theme ;
2020-06-09 16:57:05 +00:00
if ( ! ReadEncryptedFile ( theme , key , _basePath , SettingsKey ) ) {
2018-07-19 14:58:40 +00:00
return { } ;
2016-10-28 12:44:28 +00:00
}
2019-09-03 18:04:38 +00:00
auto tag = QString ( ) ;
auto result = Saved ( ) ;
auto & object = result . object ;
auto & cache = result . cache ;
theme . stream > > object . content ;
theme . stream > > tag > > object . pathAbsolute ;
if ( tag = = kThemeNewPathRelativeTag ) {
2019-09-05 05:18:21 +00:00
auto creator = qint32 ( ) ;
theme . stream
> > object . pathRelative
> > object . cloud . id
> > object . cloud . accessHash
> > object . cloud . slug
> > object . cloud . title
> > object . cloud . documentId
> > creator ;
object . cloud . createdBy = creator ;
2019-09-03 18:04:38 +00:00
} else {
object . pathRelative = tag ;
}
2016-10-28 12:44:28 +00:00
if ( theme . stream . status ( ) ! = QDataStream : : Ok ) {
2018-07-19 14:58:40 +00:00
return { } ;
2016-10-28 12:44:28 +00:00
}
2017-02-03 20:07:26 +00:00
2019-09-03 18:04:38 +00:00
auto ignoreCache = false ;
2019-09-05 05:18:21 +00:00
if ( ! object . cloud . id ) {
2019-09-05 07:42:06 +00:00
auto file = QFile ( object . pathRelative ) ;
2019-09-03 18:04:38 +00:00
if ( object . pathRelative . isEmpty ( ) | | ! file . exists ( ) ) {
file . setFileName ( object . pathAbsolute ) ;
}
if ( ! file . fileName ( ) . isEmpty ( )
& & file . exists ( )
& & file . open ( QIODevice : : ReadOnly ) ) {
if ( file . size ( ) > kThemeFileSizeLimit ) {
LOG ( ( " Error: theme file too large: %1 "
" (should be less than 5 MB, got %2) "
) . arg ( file . fileName ( )
) . arg ( file . size ( ) ) ) ;
return { } ;
}
auto fileContent = file . readAll ( ) ;
file . close ( ) ;
if ( object . content ! = fileContent ) {
object . content = fileContent ;
ignoreCache = true ;
}
2016-10-28 12:44:28 +00:00
}
}
2019-09-03 18:04:38 +00:00
if ( ! ignoreCache ) {
2016-10-28 12:44:28 +00:00
quint32 backgroundIsTiled = 0 ;
2018-07-19 14:58:40 +00:00
theme . stream
2019-09-03 18:04:38 +00:00
> > cache . paletteChecksum
> > cache . contentChecksum
> > cache . colors
> > cache . background
2018-07-19 14:58:40 +00:00
> > backgroundIsTiled ;
2019-09-03 18:04:38 +00:00
cache . tiled = ( backgroundIsTiled = = 1 ) ;
2016-10-28 12:44:28 +00:00
if ( theme . stream . status ( ) ! = QDataStream : : Ok ) {
2018-07-19 14:58:40 +00:00
return { } ;
2016-10-28 12:44:28 +00:00
}
}
2018-07-19 14:58:40 +00:00
return result ;
}
2019-09-05 06:51:46 +00:00
std : : optional < QString > InitialLoadThemeUsingKey ( FileKey key ) {
2018-07-19 14:58:40 +00:00
auto read = readThemeUsingKey ( key ) ;
2019-09-03 18:04:38 +00:00
const auto result = read . object . pathAbsolute ;
if ( read . object . content . isEmpty ( )
2019-09-05 06:51:46 +00:00
| | ! Window : : Theme : : Initialize ( std : : move ( read ) ) ) {
return std : : nullopt ;
2019-09-03 18:04:38 +00:00
}
return result ;
2016-10-28 12:44:28 +00:00
}
2018-07-19 14:58:40 +00:00
void writeTheme ( const Window : : Theme : : Saved & saved ) {
2019-09-03 18:04:38 +00:00
using namespace Window : : Theme ;
2018-07-21 13:54:00 +00:00
if ( _themeKeyLegacy ) {
return ;
}
2019-09-03 18:04:38 +00:00
auto & themeKey = IsNightMode ( )
2018-07-19 14:58:40 +00:00
? _themeKeyNight
: _themeKeyDay ;
2019-09-03 18:04:38 +00:00
if ( saved . object . content . isEmpty ( ) ) {
2018-07-19 14:58:40 +00:00
if ( themeKey ) {
2020-06-09 16:57:05 +00:00
ClearKey ( themeKey , _basePath ) ;
2018-07-19 14:58:40 +00:00
themeKey = 0 ;
2016-12-20 13:03:51 +00:00
writeSettings ( ) ;
}
return ;
}
2017-02-03 20:07:26 +00:00
2018-07-19 14:58:40 +00:00
if ( ! themeKey ) {
2020-06-09 16:57:05 +00:00
themeKey = GenerateKey ( _basePath ) ;
2016-10-28 12:44:28 +00:00
writeSettings ( ) ;
}
2019-09-03 18:04:38 +00:00
const auto & object = saved . object ;
const auto & cache = saved . cache ;
const auto tag = QString ( kThemeNewPathRelativeTag ) ;
2019-09-05 05:18:21 +00:00
quint32 size = Serialize : : bytearraySize ( object . content )
+ Serialize : : stringSize ( tag )
+ Serialize : : stringSize ( object . pathAbsolute )
+ Serialize : : stringSize ( object . pathRelative )
+ sizeof ( uint64 ) * 3
+ Serialize : : stringSize ( object . cloud . slug )
+ Serialize : : stringSize ( object . cloud . title )
+ sizeof ( qint32 )
+ sizeof ( qint32 ) * 2
+ Serialize : : bytearraySize ( cache . colors )
+ Serialize : : bytearraySize ( cache . background )
+ sizeof ( quint32 ) ;
2016-10-28 12:44:28 +00:00
EncryptedDescriptor data ( size ) ;
2019-09-03 18:04:38 +00:00
data . stream
2019-09-05 05:18:21 +00:00
< < object . content
< < tag
< < object . pathAbsolute
< < object . pathRelative
< < object . cloud . id
< < object . cloud . accessHash
< < object . cloud . slug
< < object . cloud . title
< < object . cloud . documentId
< < qint32 ( object . cloud . createdBy )
2019-09-03 18:04:38 +00:00
< < cache . paletteChecksum
< < cache . contentChecksum
< < cache . colors
< < cache . background
< < quint32 ( cache . tiled ? 1 : 0 ) ;
2016-10-28 12:44:28 +00:00
2020-06-09 16:57:05 +00:00
FileWriteDescriptor file ( themeKey , _basePath ) ;
2017-02-25 16:44:02 +00:00
file . writeEncrypted ( data , SettingsKey ) ;
2016-10-28 12:44:28 +00:00
}
2016-12-20 13:03:51 +00:00
void clearTheme ( ) {
2018-07-19 14:58:40 +00:00
writeTheme ( Window : : Theme : : Saved ( ) ) ;
2016-12-20 13:03:51 +00:00
}
2019-09-05 06:51:46 +00:00
void InitialLoadTheme ( ) {
2018-07-19 14:58:40 +00:00
const auto key = ( _themeKeyLegacy ! = 0 )
? _themeKeyLegacy
: ( Window : : Theme : : IsNightMode ( )
? _themeKeyNight
: _themeKeyDay ) ;
if ( ! key ) {
return ;
2019-09-05 06:51:46 +00:00
} else if ( const auto path = InitialLoadThemeUsingKey ( key ) ) {
2018-07-19 14:58:40 +00:00
if ( _themeKeyLegacy ) {
2019-09-05 06:51:46 +00:00
Window : : Theme : : SetNightModeValue ( * path
2018-07-19 14:58:40 +00:00
= = Window : : Theme : : NightThemePath ( ) ) ;
( Window : : Theme : : IsNightMode ( )
? _themeKeyNight
: _themeKeyDay ) = base : : take ( _themeKeyLegacy ) ;
}
} else {
2016-12-20 13:03:51 +00:00
clearTheme ( ) ;
2016-10-28 12:44:28 +00:00
}
}
2020-02-11 12:00:04 +00:00
bool ApplyDefaultNightMode ( ) {
const auto NightByDefault = Platform : : IsMacStoreBuild ( ) ;
if ( ! NightByDefault
| | Window : : Theme : : IsNightMode ( )
| | _themeKeyDay
| | _themeKeyNight
| | _themeKeyLegacy ) {
return false ;
}
Window : : Theme : : ToggleNightMode ( ) ;
Window : : Theme : : KeepApplied ( ) ;
return true ;
}
2018-07-19 14:58:40 +00:00
Window : : Theme : : Saved readThemeAfterSwitch ( ) {
const auto key = Window : : Theme : : IsNightMode ( )
? _themeKeyNight
: _themeKeyDay ;
return readThemeUsingKey ( key ) ;
2016-12-20 13:03:51 +00:00
}
2017-04-13 17:59:05 +00:00
void readLangPack ( ) {
FileReadDescriptor langpack ;
2020-06-09 16:57:05 +00:00
if ( ! _langPackKey | | ! ReadEncryptedFile ( langpack , _langPackKey , _basePath , SettingsKey ) ) {
2017-04-13 17:59:05 +00:00
return ;
}
auto data = QByteArray ( ) ;
langpack . stream > > data ;
if ( langpack . stream . status ( ) = = QDataStream : : Ok ) {
2018-10-22 11:13:48 +00:00
Lang : : Current ( ) . fillFromSerialized ( data , langpack . version ) ;
2017-04-13 17:59:05 +00:00
}
}
void writeLangPack ( ) {
auto langpack = Lang : : Current ( ) . serialize ( ) ;
if ( ! _langPackKey ) {
2020-06-09 16:57:05 +00:00
_langPackKey = GenerateKey ( _basePath ) ;
2017-04-13 17:59:05 +00:00
writeSettings ( ) ;
}
EncryptedDescriptor data ( Serialize : : bytearraySize ( langpack ) ) ;
data . stream < < langpack ;
2020-06-09 16:57:05 +00:00
FileWriteDescriptor file ( _langPackKey , _basePath ) ;
2017-04-13 17:59:05 +00:00
file . writeEncrypted ( data , SettingsKey ) ;
}
2018-11-01 07:10:15 +00:00
void saveRecentLanguages ( const std : : vector < Lang : : Language > & list ) {
if ( list . empty ( ) ) {
if ( _languagesKey ) {
2020-06-09 16:57:05 +00:00
ClearKey ( _languagesKey , _basePath ) ;
2018-11-01 07:10:15 +00:00
_languagesKey = 0 ;
writeSettings ( ) ;
}
2018-10-30 11:23:54 +00:00
return ;
}
auto size = sizeof ( qint32 ) ;
for ( const auto & language : list ) {
size + = Serialize : : stringSize ( language . id )
+ Serialize : : stringSize ( language . pluralId )
+ Serialize : : stringSize ( language . baseId )
+ Serialize : : stringSize ( language . name )
+ Serialize : : stringSize ( language . nativeName ) ;
}
if ( ! _languagesKey ) {
2020-06-09 16:57:05 +00:00
_languagesKey = GenerateKey ( _basePath ) ;
2018-10-30 11:23:54 +00:00
writeSettings ( ) ;
}
EncryptedDescriptor data ( size ) ;
data . stream < < qint32 ( list . size ( ) ) ;
for ( const auto & language : list ) {
data . stream
< < language . id
< < language . pluralId
< < language . baseId
< < language . name
< < language . nativeName ;
}
2020-06-09 16:57:05 +00:00
FileWriteDescriptor file ( _languagesKey , _basePath ) ;
2018-10-30 11:23:54 +00:00
file . writeEncrypted ( data , SettingsKey ) ;
}
2018-11-01 07:10:15 +00:00
void pushRecentLanguage ( const Lang : : Language & language ) {
if ( language . id . startsWith ( ' # ' ) ) {
return ;
}
auto list = readRecentLanguages ( ) ;
list . erase (
ranges : : remove_if (
list ,
[ & ] ( const Lang : : Language & v ) { return ( v . id = = language . id ) ; } ) ,
end ( list ) ) ;
list . insert ( list . begin ( ) , language ) ;
saveRecentLanguages ( list ) ;
}
void removeRecentLanguage ( const QString & id ) {
auto list = readRecentLanguages ( ) ;
list . erase (
ranges : : remove_if (
list ,
[ & ] ( const Lang : : Language & v ) { return ( v . id = = id ) ; } ) ,
end ( list ) ) ;
saveRecentLanguages ( list ) ;
}
std : : vector < Lang : : Language > readRecentLanguages ( ) {
2018-10-30 11:23:54 +00:00
FileReadDescriptor languages ;
2020-06-09 16:57:05 +00:00
if ( ! _languagesKey | | ! ReadEncryptedFile ( languages , _languagesKey , _basePath , SettingsKey ) ) {
2018-10-30 11:23:54 +00:00
return { } ;
}
qint32 count = 0 ;
languages . stream > > count ;
if ( count < = 0 ) {
return { } ;
}
2018-11-01 07:10:15 +00:00
auto result = std : : vector < Lang : : Language > ( ) ;
2018-10-30 11:23:54 +00:00
result . reserve ( count ) ;
for ( auto i = 0 ; i ! = count ; + + i ) {
auto language = Lang : : Language ( ) ;
languages . stream
> > language . id
> > language . pluralId
> > language . baseId
> > language . name
> > language . nativeName ;
result . push_back ( language ) ;
}
if ( languages . stream . status ( ) ! = QDataStream : : Ok ) {
return { } ;
}
return result ;
}
2019-09-05 20:21:44 +00:00
Window : : Theme : : Object ReadThemeContent ( ) {
2019-09-05 07:42:06 +00:00
using namespace Window : : Theme ;
2019-09-05 20:21:44 +00:00
2019-09-05 07:42:06 +00:00
auto & themeKey = IsNightMode ( ) ? _themeKeyNight : _themeKeyDay ;
2018-07-19 14:58:40 +00:00
if ( ! themeKey ) {
2019-09-05 20:21:44 +00:00
return Object ( ) ;
2017-02-03 20:07:26 +00:00
}
FileReadDescriptor theme ;
2020-06-09 16:57:05 +00:00
if ( ! ReadEncryptedFile ( theme , themeKey , _basePath , SettingsKey ) ) {
2019-09-05 20:21:44 +00:00
return Object ( ) ;
2017-02-03 20:07:26 +00:00
}
2019-09-05 20:21:44 +00:00
QByteArray content ;
2019-08-26 16:36:23 +00:00
QString pathRelative , pathAbsolute ;
2019-09-05 20:21:44 +00:00
theme . stream > > content > > pathRelative > > pathAbsolute ;
2017-02-03 20:07:26 +00:00
if ( theme . stream . status ( ) ! = QDataStream : : Ok ) {
2019-09-05 20:21:44 +00:00
return Object ( ) ;
2017-02-03 20:07:26 +00:00
}
2019-09-05 20:21:44 +00:00
auto result = Object ( ) ;
result . pathAbsolute = pathAbsolute ;
result . content = content ;
return result ;
2017-02-03 20:07:26 +00:00
}
2018-06-26 15:34:38 +00:00
void incrementRecentHashtag ( RecentHashtagPack & recent , const QString & tag ) {
auto i = recent . begin ( ) , e = recent . end ( ) ;
for ( ; i ! = e ; + + i ) {
if ( i - > first = = tag ) {
+ + i - > second ;
if ( qAbs ( i - > second ) > 0x4000 ) {
for ( auto j = recent . begin ( ) ; j ! = e ; + + j ) {
if ( j - > second > 1 ) {
j - > second / = 2 ;
} else if ( j - > second > 0 ) {
j - > second = 1 ;
}
}
}
for ( ; i ! = recent . begin ( ) ; - - i ) {
if ( qAbs ( ( i - 1 ) - > second ) > qAbs ( i - > second ) ) {
break ;
}
qSwap ( * i , * ( i - 1 ) ) ;
}
break ;
}
}
if ( i = = e ) {
while ( recent . size ( ) > = 64 ) recent . pop_back ( ) ;
recent . push_back ( qMakePair ( tag , 1 ) ) ;
for ( i = recent . end ( ) - 1 ; i ! = recent . begin ( ) ; - - i ) {
if ( ( i - 1 ) - > second > i - > second ) {
break ;
}
qSwap ( * i , * ( i - 1 ) ) ;
}
}
}
2020-06-09 16:57:05 +00:00
bool readOldMtpData ( bool remove , ReadSettingsContext & context ) {
return _readOldMtpData ( remove , context ) ;
2016-09-15 19:15:49 +00:00
}
2014-11-22 09:45:04 +00:00
2020-06-09 16:57:05 +00:00
bool readOldUserSettings ( bool remove , ReadSettingsContext & context ) {
return _readOldUserSettings ( remove , context ) ;
2016-09-15 19:15:49 +00:00
}
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
struct ClearManagerData {
QThread * thread ;
QMutex mutex ;
QList < int > tasks ;
bool working ;
} ;
ClearManager : : ClearManager ( ) : data ( new ClearManagerData ( ) ) {
data - > thread = new QThread ( ) ;
data - > working = true ;
}
bool ClearManager : : addTask ( int task ) {
QMutexLocker lock ( & data - > mutex ) ;
if ( ! data - > working ) return false ;
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
if ( ! data - > tasks . isEmpty ( ) & & ( data - > tasks . at ( 0 ) = = ClearManagerAll ) ) return true ;
if ( task = = ClearManagerAll ) {
data - > tasks . clear ( ) ;
} else {
2014-11-22 09:45:04 +00:00
for ( int32 i = 0 , l = data - > tasks . size ( ) ; i < l ; + + i ) {
if ( data - > tasks . at ( i ) = = task ) return true ;
}
}
2016-09-15 19:15:49 +00:00
data - > tasks . push_back ( task ) ;
return true ;
}
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
bool ClearManager : : hasTask ( ClearManagerTask task ) {
QMutexLocker lock ( & data - > mutex ) ;
if ( data - > tasks . isEmpty ( ) ) return false ;
if ( data - > tasks . at ( 0 ) = = ClearManagerAll ) return true ;
for ( int32 i = 0 , l = data - > tasks . size ( ) ; i < l ; + + i ) {
if ( data - > tasks . at ( i ) = = task ) return true ;
2014-11-22 09:45:04 +00:00
}
2016-09-15 19:15:49 +00:00
return false ;
}
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
void ClearManager : : start ( ) {
moveToThread ( data - > thread ) ;
connect ( data - > thread , SIGNAL ( started ( ) ) , this , SLOT ( onStart ( ) ) ) ;
connect ( data - > thread , SIGNAL ( finished ( ) ) , data - > thread , SLOT ( deleteLater ( ) ) ) ;
connect ( data - > thread , SIGNAL ( finished ( ) ) , this , SLOT ( deleteLater ( ) ) ) ;
data - > thread - > start ( ) ;
}
2016-08-28 19:16:23 +00:00
2016-09-15 19:15:49 +00:00
void ClearManager : : stop ( ) {
{
QMutexLocker lock ( & data - > mutex ) ;
data - > tasks . clear ( ) ;
2014-11-22 09:45:04 +00:00
}
2016-09-15 19:15:49 +00:00
auto thread = data - > thread ;
thread - > quit ( ) ;
thread - > wait ( ) ;
}
2014-11-22 09:45:04 +00:00
2016-09-15 19:15:49 +00:00
ClearManager : : ~ ClearManager ( ) {
delete data ;
}
void ClearManager : : onStart ( ) {
while ( true ) {
int task = 0 ;
bool result = false ;
{
QMutexLocker lock ( & data - > mutex ) ;
if ( data - > tasks . isEmpty ( ) ) {
data - > working = false ;
break ;
2014-11-22 09:45:04 +00:00
}
2016-09-15 19:15:49 +00:00
task = data - > tasks . at ( 0 ) ;
}
switch ( task ) {
case ClearManagerAll : {
result = QDir ( cTempDir ( ) ) . removeRecursively ( ) ;
} break ;
case ClearManagerDownloads :
result = QDir ( cTempDir ( ) ) . removeRecursively ( ) ;
break ;
2014-11-22 09:45:04 +00:00
}
2016-09-15 19:15:49 +00:00
{
QMutexLocker lock ( & data - > mutex ) ;
if ( ! data - > tasks . isEmpty ( ) & & data - > tasks . at ( 0 ) = = task ) {
data - > tasks . pop_front ( ) ;
}
if ( data - > tasks . isEmpty ( ) ) {
data - > working = false ;
}
if ( result ) {
emit succeed ( task , data - > working ? 0 : this ) ;
} else {
emit failed ( task , data - > working ? 0 : this ) ;
}
if ( ! data - > working ) break ;
}
}
}
} // namespace Local