2014-11-22 12:45:04 +03:00
/*
This file is part of Telegram Desktop ,
2014-12-01 13:47:38 +03:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-11-22 12:45:04 +03:00
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 .
2015-10-03 16:16:42 +03:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
2014-11-22 12:45:04 +03:00
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2016-02-08 13:56:18 +03:00
Copyright ( c ) 2014 - 2016 John Preston , https : //desktop.telegram.org
2014-11-22 12:45:04 +03:00
*/
# include "stdafx.h"
# include "localstorage.h"
2016-03-23 21:12:07 +03:00
# include <openssl/evp.h>
2016-04-09 09:57:55 +04:00
# include "serialize/serialize_document.h"
# include "serialize/serialize_common.h"
2016-06-09 17:31:10 +03:00
# include "data/data_drafts.h"
2016-05-25 15:09:05 +03:00
# include "observer_peer.h"
2015-08-07 15:11:50 +03:00
# include "mainwidget.h"
2016-04-13 00:31:28 +03:00
# include "mainwindow.h"
2015-03-02 15:34:16 +03:00
# include "lang.h"
2016-04-13 00:31:28 +03:00
# include "playerwidget.h"
# include "apiwrap.h"
2015-03-02 15:34:16 +03:00
2014-11-22 12:45:04 +03:00
namespace {
typedef quint64 FileKey ;
static const char tdfMagic [ ] = { ' T ' , ' D ' , ' F ' , ' $ ' } ;
static const int32 tdfMagicLen = sizeof ( tdfMagic ) ;
QString toFilePart ( FileKey val ) {
QString result ;
result . reserve ( 0x10 ) ;
for ( int32 i = 0 ; i < 0x10 ; + + i ) {
uchar v = ( val & 0x0F ) ;
result . push_back ( ( v < 0x0A ) ? ( ' 0 ' + v ) : ( ' A ' + ( v - 0x0A ) ) ) ;
val > > = 4 ;
}
return result ;
}
2015-03-02 15:34:16 +03:00
QString _basePath , _userBasePath ;
2014-11-22 12:45:04 +03:00
bool _started = false ;
_local_inner : : Manager * _manager = 0 ;
2015-09-29 21:44:31 +03:00
TaskQueue * _localLoader = 0 ;
2014-11-22 12:45:04 +03:00
bool _working ( ) {
return _manager & & ! _basePath . isEmpty ( ) ;
}
2015-03-02 15:34:16 +03:00
bool _userWorking ( ) {
return _manager & & ! _basePath . isEmpty ( ) & & ! _userBasePath . isEmpty ( ) ;
}
enum FileOptions {
UserPath = 0x01 ,
SafePath = 0x02 ,
} ;
bool keyAlreadyUsed ( QString & name , int options = UserPath | SafePath ) {
2014-11-22 12:45:04 +03:00
name + = ' 0 ' ;
if ( QFileInfo ( name ) . exists ( ) ) return true ;
2015-03-02 15:34:16 +03:00
if ( options & SafePath ) {
name [ name . size ( ) - 1 ] = ' 1 ' ;
return QFileInfo ( name ) . exists ( ) ;
}
return false ;
2014-11-22 12:45:04 +03:00
}
2015-03-02 15:34:16 +03:00
FileKey genKey ( int options = UserPath | SafePath ) {
if ( options & UserPath ) {
if ( ! _userWorking ( ) ) return 0 ;
} else {
if ( ! _working ( ) ) return 0 ;
}
2014-11-22 12:45:04 +03:00
FileKey result ;
2015-03-02 15:34:16 +03:00
QString base = ( options & UserPath ) ? _userBasePath : _basePath , path ;
path . reserve ( base . size ( ) + 0x11 ) ;
path + = base ;
2014-11-22 12:45:04 +03:00
do {
2016-03-24 13:12:18 +03:00
result = rand_value < FileKey > ( ) ;
2015-03-02 15:34:16 +03:00
path . resize ( base . size ( ) ) ;
2014-11-22 12:45:04 +03:00
path + = toFilePart ( result ) ;
2015-03-02 15:34:16 +03:00
} while ( ! result | | keyAlreadyUsed ( path , options ) ) ;
2014-11-22 12:45:04 +03:00
return result ;
}
2015-03-02 15:34:16 +03:00
void clearKey ( const FileKey & key , int options = UserPath | SafePath ) {
if ( options & UserPath ) {
if ( ! _userWorking ( ) ) return ;
} else {
if ( ! _working ( ) ) return ;
}
2014-11-22 12:45:04 +03:00
2015-03-02 15:34:16 +03:00
QString base = ( options & UserPath ) ? _userBasePath : _basePath , name ;
name . reserve ( base . size ( ) + 0x11 ) ;
name . append ( base ) . append ( toFilePart ( key ) ) . append ( ' 0 ' ) ;
2014-11-22 12:45:04 +03:00
QFile : : remove ( name ) ;
2015-03-02 15:34:16 +03:00
if ( options & SafePath ) {
2014-11-22 12:45:04 +03:00
name [ name . size ( ) - 1 ] = ' 1 ' ;
QFile : : remove ( name ) ;
}
}
2015-03-02 15:34:16 +03:00
bool _checkStreamStatus ( QDataStream & stream ) {
if ( stream . status ( ) ! = QDataStream : : Ok ) {
LOG ( ( " Bad data stream status: %1 " ) . arg ( stream . status ( ) ) ) ;
return false ;
}
return true ;
}
QByteArray _settingsSalt , _passKeySalt , _passKeyEncrypted ;
2014-11-22 12:45:04 +03:00
2016-03-24 15:57:10 +03:00
MTP : : AuthKey _oldKey , _settingsKey , _passKey , _localKey ;
void createLocalKey ( const QByteArray & pass , QByteArray * salt , MTP : : AuthKey * result ) {
2014-11-22 12:45:04 +03:00
uchar key [ LocalEncryptKeySize ] = { 0 } ;
int32 iterCount = pass . size ( ) ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount ; // dont slow down for no password
QByteArray newSalt ;
if ( ! salt ) {
newSalt . resize ( LocalEncryptSaltSize ) ;
memset_rand ( newSalt . data ( ) , newSalt . size ( ) ) ;
salt = & newSalt ;
cSetLocalSalt ( newSalt ) ;
}
PKCS5_PBKDF2_HMAC_SHA1 ( pass . constData ( ) , pass . size ( ) , ( uchar * ) salt - > data ( ) , salt - > size ( ) , iterCount , LocalEncryptKeySize , key ) ;
result - > setKey ( key ) ;
}
struct FileReadDescriptor {
FileReadDescriptor ( ) : version ( 0 ) {
}
int32 version ;
QByteArray data ;
QBuffer buffer ;
QDataStream stream ;
~ FileReadDescriptor ( ) {
if ( version ) {
stream . setDevice ( 0 ) ;
if ( buffer . isOpen ( ) ) buffer . close ( ) ;
buffer . setBuffer ( 0 ) ;
}
}
} ;
struct EncryptedDescriptor {
EncryptedDescriptor ( ) {
}
EncryptedDescriptor ( uint32 size ) {
uint32 fullSize = sizeof ( uint32 ) + size ;
if ( fullSize & 0x0F ) fullSize + = 0x10 - ( fullSize & 0x0F ) ;
data . reserve ( fullSize ) ;
data . resize ( sizeof ( uint32 ) ) ;
buffer . setBuffer ( & data ) ;
buffer . open ( QIODevice : : WriteOnly ) ;
buffer . seek ( sizeof ( uint32 ) ) ;
stream . setDevice ( & buffer ) ;
stream . setVersion ( QDataStream : : Qt_5_1 ) ;
}
QByteArray data ;
QBuffer buffer ;
QDataStream stream ;
void finish ( ) {
if ( stream . device ( ) ) stream . setDevice ( 0 ) ;
if ( buffer . isOpen ( ) ) buffer . close ( ) ;
buffer . setBuffer ( 0 ) ;
}
~ EncryptedDescriptor ( ) {
finish ( ) ;
}
} ;
struct FileWriteDescriptor {
2015-03-02 15:34:16 +03:00
FileWriteDescriptor ( const FileKey & key , int options = UserPath | SafePath ) : dataSize ( 0 ) {
init ( toFilePart ( key ) , options ) ;
2014-11-22 12:45:04 +03:00
}
2015-03-02 15:34:16 +03:00
FileWriteDescriptor ( const QString & name , int options = UserPath | SafePath ) : dataSize ( 0 ) {
init ( name , options ) ;
2014-11-22 12:45:04 +03:00
}
2015-03-02 15:34:16 +03:00
void init ( const QString & name , int options ) {
if ( options & UserPath ) {
if ( ! _userWorking ( ) ) return ;
} else {
if ( ! _working ( ) ) return ;
}
2014-11-22 12:45:04 +03:00
// detect order of read attempts and file version
QString toTry [ 2 ] ;
2015-03-02 15:34:16 +03:00
toTry [ 0 ] = ( ( options & UserPath ) ? _userBasePath : _basePath ) + name + ' 0 ' ;
if ( options & SafePath ) {
toTry [ 1 ] = ( ( options & UserPath ) ? _userBasePath : _basePath ) + name + ' 1 ' ;
2014-11-22 12:45:04 +03:00
QFileInfo toTry0 ( toTry [ 0 ] ) ;
QFileInfo toTry1 ( toTry [ 1 ] ) ;
if ( toTry0 . exists ( ) ) {
if ( toTry1 . exists ( ) ) {
QDateTime mod0 = toTry0 . lastModified ( ) , mod1 = toTry1 . lastModified ( ) ;
if ( mod0 > mod1 ) {
qSwap ( toTry [ 0 ] , toTry [ 1 ] ) ;
}
} else {
qSwap ( toTry [ 0 ] , toTry [ 1 ] ) ;
}
toDelete = toTry [ 1 ] ;
} else if ( toTry1 . exists ( ) ) {
toDelete = toTry [ 1 ] ;
}
}
file . setFileName ( toTry [ 0 ] ) ;
if ( file . open ( QIODevice : : WriteOnly ) ) {
file . write ( tdfMagic , tdfMagicLen ) ;
qint32 version = AppVersion ;
file . write ( ( const char * ) & version , sizeof ( version ) ) ;
stream . setDevice ( & file ) ;
stream . setVersion ( QDataStream : : Qt_5_1 ) ;
}
}
bool writeData ( const QByteArray & data ) {
if ( ! file . isOpen ( ) ) return false ;
stream < < data ;
quint32 len = data . isNull ( ) ? 0xffffffff : data . size ( ) ;
if ( QSysInfo : : ByteOrder ! = QSysInfo : : BigEndian ) {
len = qbswap ( len ) ;
}
md5 . feed ( & len , sizeof ( len ) ) ;
md5 . feed ( data . constData ( ) , data . size ( ) ) ;
dataSize + = sizeof ( len ) + data . size ( ) ;
return true ;
}
2016-03-24 15:57:10 +03:00
static QByteArray prepareEncrypted ( EncryptedDescriptor & data , const MTP : : AuthKey & key = _localKey ) {
2014-11-22 12:45:04 +03:00
data . finish ( ) ;
QByteArray & toEncrypt ( data . data ) ;
// prepare for encryption
uint32 size = toEncrypt . size ( ) , fullSize = size ;
if ( fullSize & 0x0F ) {
fullSize + = 0x10 - ( fullSize & 0x0F ) ;
toEncrypt . resize ( fullSize ) ;
memset_rand ( toEncrypt . data ( ) + size , fullSize - size ) ;
}
* ( uint32 * ) toEncrypt . data ( ) = size ;
QByteArray encrypted ( 0x10 + fullSize , Qt : : Uninitialized ) ; // 128bit of sha1 - key128, sizeof(data), data
hashSha1 ( toEncrypt . constData ( ) , toEncrypt . size ( ) , encrypted . data ( ) ) ;
2016-03-24 15:57:10 +03:00
MTP : : aesEncryptLocal ( toEncrypt . constData ( ) , encrypted . data ( ) + 0x10 , fullSize , & key , encrypted . constData ( ) ) ;
2014-11-22 12:45:04 +03:00
return encrypted ;
}
2016-03-24 15:57:10 +03:00
bool writeEncrypted ( EncryptedDescriptor & data , const MTP : : AuthKey & key = _localKey ) {
2014-11-22 12:45:04 +03:00
return writeData ( prepareEncrypted ( data , key ) ) ;
}
void finish ( ) {
if ( ! file . isOpen ( ) ) return ;
stream . setDevice ( 0 ) ;
md5 . feed ( & dataSize , sizeof ( dataSize ) ) ;
qint32 version = AppVersion ;
md5 . feed ( & version , sizeof ( version ) ) ;
md5 . feed ( tdfMagic , tdfMagicLen ) ;
file . write ( ( const char * ) md5 . result ( ) , 0x10 ) ;
file . close ( ) ;
if ( ! toDelete . isEmpty ( ) ) {
QFile : : remove ( toDelete ) ;
}
}
QFile file ;
QDataStream stream ;
QString toDelete ;
HashMd5 md5 ;
int32 dataSize ;
~ FileWriteDescriptor ( ) {
finish ( ) ;
}
} ;
2015-03-02 15:34:16 +03:00
bool readFile ( FileReadDescriptor & result , const QString & name , int options = UserPath | SafePath ) {
if ( options & UserPath ) {
if ( ! _userWorking ( ) ) return false ;
} else {
if ( ! _working ( ) ) return false ;
}
2014-11-22 12:45:04 +03:00
// detect order of read attempts
QString toTry [ 2 ] ;
2015-03-02 15:34:16 +03:00
toTry [ 0 ] = ( ( options & UserPath ) ? _userBasePath : _basePath ) + name + ' 0 ' ;
if ( options & SafePath ) {
2014-11-22 12:45:04 +03:00
QFileInfo toTry0 ( toTry [ 0 ] ) ;
if ( toTry0 . exists ( ) ) {
2015-03-02 15:34:16 +03:00
toTry [ 1 ] = ( ( options & UserPath ) ? _userBasePath : _basePath ) + name + ' 1 ' ;
2014-11-22 12:45:04 +03:00
QFileInfo toTry1 ( toTry [ 1 ] ) ;
if ( toTry1 . exists ( ) ) {
QDateTime mod0 = toTry0 . lastModified ( ) , mod1 = toTry1 . lastModified ( ) ;
if ( mod0 < mod1 ) {
qSwap ( toTry [ 0 ] , toTry [ 1 ] ) ;
}
} else {
toTry [ 1 ] = QString ( ) ;
}
} else {
toTry [ 0 ] [ toTry [ 0 ] . size ( ) - 1 ] = ' 1 ' ;
}
}
for ( int32 i = 0 ; i < 2 ; + + i ) {
QString fname ( toTry [ i ] ) ;
if ( fname . isEmpty ( ) ) break ;
QFile f ( fname ) ;
if ( ! f . open ( QIODevice : : ReadOnly ) ) {
DEBUG_LOG ( ( " App Info: failed to open '%1' for reading " ) . arg ( name ) ) ;
continue ;
}
// check magic
char magic [ tdfMagicLen ] ;
if ( f . read ( magic , tdfMagicLen ) ! = tdfMagicLen ) {
DEBUG_LOG ( ( " App Info: failed to read magic from '%1' " ) . arg ( name ) ) ;
continue ;
}
if ( memcmp ( magic , tdfMagic , tdfMagicLen ) ) {
2016-01-11 23:43:29 +08:00
DEBUG_LOG ( ( " App Info: bad magic %1 in '%2' " ) . arg ( Logs : : mb ( magic , tdfMagicLen ) . str ( ) ) . arg ( name ) ) ;
2014-11-22 12:45:04 +03:00
continue ;
}
// read app version
qint32 version ;
if ( f . read ( ( char * ) & version , sizeof ( version ) ) ! = sizeof ( version ) ) {
DEBUG_LOG ( ( " App Info: failed to read version from '%1' " ) . arg ( name ) ) ;
continue ;
}
if ( version > AppVersion ) {
DEBUG_LOG ( ( " App Info: version too big %1 for '%2', my version %3 " ) . arg ( version ) . arg ( name ) . arg ( AppVersion ) ) ;
continue ;
}
// read data
QByteArray bytes = f . read ( f . size ( ) ) ;
int32 dataSize = bytes . size ( ) - 16 ;
if ( dataSize < 0 ) {
DEBUG_LOG ( ( " App Info: bad file '%1', could not read sign part " ) . arg ( name ) ) ;
continue ;
}
// check signature
HashMd5 md5 ;
md5 . feed ( bytes . constData ( ) , dataSize ) ;
md5 . feed ( & dataSize , sizeof ( dataSize ) ) ;
md5 . feed ( & version , sizeof ( version ) ) ;
md5 . feed ( magic , tdfMagicLen ) ;
if ( memcmp ( md5 . result ( ) , bytes . constData ( ) + dataSize , 16 ) ) {
DEBUG_LOG ( ( " App Info: bad file '%1', signature did not match " ) . arg ( name ) ) ;
continue ;
}
bytes . resize ( dataSize ) ;
result . data = bytes ;
bytes = QByteArray ( ) ;
result . version = version ;
result . buffer . setBuffer ( & result . data ) ;
result . buffer . open ( QIODevice : : ReadOnly ) ;
result . stream . setDevice ( & result . buffer ) ;
result . stream . setVersion ( QDataStream : : Qt_5_1 ) ;
if ( ( i = = 0 & & ! toTry [ 1 ] . isEmpty ( ) ) | | i = = 1 ) {
QFile : : remove ( toTry [ 1 - i ] ) ;
}
return true ;
}
return false ;
}
2016-03-24 15:57:10 +03:00
bool decryptLocal ( EncryptedDescriptor & result , const QByteArray & encrypted , const MTP : : AuthKey & key = _localKey ) {
2014-11-22 12:45:04 +03:00
if ( encrypted . size ( ) < = 16 | | ( encrypted . size ( ) & 0x0F ) ) {
LOG ( ( " App Error: bad encrypted part size: %1 " ) . arg ( encrypted . size ( ) ) ) ;
return false ;
}
uint32 fullLen = encrypted . size ( ) - 16 ;
QByteArray decrypted ;
decrypted . resize ( fullLen ) ;
const char * encryptedKey = encrypted . constData ( ) , * encryptedData = encrypted . constData ( ) + 16 ;
aesDecryptLocal ( encryptedData , decrypted . data ( ) , fullLen , & key , encryptedKey ) ;
uchar sha1Buffer [ 20 ] ;
if ( memcmp ( hashSha1 ( decrypted . constData ( ) , decrypted . size ( ) , sha1Buffer ) , encryptedKey , 16 ) ) {
2015-03-02 15:34:16 +03:00
LOG ( ( " App Info: bad decrypt key, data not decrypted - incorrect password? " ) ) ;
2014-11-22 12:45:04 +03:00
return false ;
}
uint32 dataLen = * ( const uint32 * ) decrypted . constData ( ) ;
if ( dataLen > uint32 ( decrypted . size ( ) ) | | dataLen < = fullLen - 16 | | dataLen < sizeof ( uint32 ) ) {
LOG ( ( " App Error: bad decrypted part size: %1, fullLen: %2, decrypted size: %3 " ) . arg ( dataLen ) . arg ( fullLen ) . arg ( decrypted . size ( ) ) ) ;
return false ;
}
decrypted . resize ( dataLen ) ;
result . data = decrypted ;
decrypted = QByteArray ( ) ;
result . buffer . setBuffer ( & result . data ) ;
result . buffer . open ( QIODevice : : ReadOnly ) ;
result . buffer . seek ( sizeof ( uint32 ) ) ; // skip len
result . stream . setDevice ( & result . buffer ) ;
result . stream . setVersion ( QDataStream : : Qt_5_1 ) ;
return true ;
}
2015-03-02 15:34:16 +03:00
2016-03-24 15:57:10 +03:00
bool readEncryptedFile ( FileReadDescriptor & result , const QString & name , int options = UserPath | SafePath , const MTP : : AuthKey & key = _localKey ) {
2015-03-02 15:34:16 +03:00
if ( ! readFile ( result , name , options ) ) {
2014-11-22 12:45:04 +03:00
return false ;
}
QByteArray encrypted ;
result . stream > > encrypted ;
EncryptedDescriptor data ;
2015-03-02 15:34:16 +03:00
if ( ! decryptLocal ( data , encrypted , key ) ) {
2014-11-22 12:45:04 +03:00
result . stream . setDevice ( 0 ) ;
if ( result . buffer . isOpen ( ) ) result . buffer . close ( ) ;
result . buffer . setBuffer ( 0 ) ;
result . data = QByteArray ( ) ;
result . version = 0 ;
return false ;
}
result . stream . setDevice ( 0 ) ;
if ( result . buffer . isOpen ( ) ) result . buffer . close ( ) ;
result . buffer . setBuffer ( 0 ) ;
result . data = data . data ;
result . buffer . setBuffer ( & result . data ) ;
result . buffer . open ( QIODevice : : ReadOnly ) ;
result . buffer . seek ( data . buffer . pos ( ) ) ;
result . stream . setDevice ( & result . buffer ) ;
result . stream . setVersion ( QDataStream : : Qt_5_1 ) ;
return true ;
}
2016-03-24 15:57:10 +03:00
bool readEncryptedFile ( FileReadDescriptor & result , const FileKey & fkey , int options = UserPath | SafePath , const MTP : : AuthKey & key = _localKey ) {
2015-03-02 15:34:16 +03:00
return readEncryptedFile ( result , toFilePart ( fkey ) , options , key ) ;
}
FileKey _dataNameKey = 0 ;
2014-11-22 12:45:04 +03:00
enum { // Local Storage Keys
2016-01-01 22:48:32 +08:00
lskUserMap = 0x00 ,
lskDraft = 0x01 , // data: PeerId peer
lskDraftPosition = 0x02 , // data: PeerId peer
lskImages = 0x03 , // data: StorageKey location
lskLocations = 0x04 , // no data
lskStickerImages = 0x05 , // data: StorageKey location
lskAudios = 0x06 , // data: StorageKey location
lskRecentStickersOld = 0x07 , // no data
lskBackground = 0x08 , // no data
lskUserSettings = 0x09 , // no data
lskRecentHashtagsAndBots = 0x0a , // no data
lskStickers = 0x0b , // no data
lskSavedPeers = 0x0c , // no data
lskReportSpamStatuses = 0x0d , // no data
lskSavedGifsOld = 0x0e , // no data
lskSavedGifs = 0x0f , // no data
2014-11-22 12:45:04 +03:00
} ;
2016-03-10 13:15:21 +03:00
enum {
2016-04-11 14:59:01 +04:00
dbiKey = 0x00 ,
dbiUser = 0x01 ,
dbiDcOptionOld = 0x02 ,
dbiChatSizeMax = 0x03 ,
dbiMutePeer = 0x04 ,
dbiSendKey = 0x05 ,
dbiAutoStart = 0x06 ,
dbiStartMinimized = 0x07 ,
dbiSoundNotify = 0x08 ,
dbiWorkMode = 0x09 ,
dbiSeenTrayTooltip = 0x0a ,
dbiDesktopNotify = 0x0b ,
dbiAutoUpdate = 0x0c ,
dbiLastUpdateCheck = 0x0d ,
dbiWindowPosition = 0x0e ,
dbiConnectionType = 0x0f ,
2016-03-10 13:15:21 +03:00
// 0x10 reserved
2016-04-11 14:59:01 +04:00
dbiDefaultAttach = 0x11 ,
dbiCatsAndDogs = 0x12 ,
dbiReplaceEmojis = 0x13 ,
dbiAskDownloadPath = 0x14 ,
dbiDownloadPathOld = 0x15 ,
dbiScale = 0x16 ,
dbiEmojiTabOld = 0x17 ,
dbiRecentEmojisOld = 0x18 ,
dbiLoggedPhoneNumber = 0x19 ,
dbiMutedPeers = 0x1a ,
2016-03-10 13:15:21 +03:00
// 0x1b reserved
2016-04-11 14:59:01 +04:00
dbiNotifyView = 0x1c ,
dbiSendToMenu = 0x1d ,
dbiCompressPastedImage = 0x1e ,
dbiLang = 0x1f ,
dbiLangFile = 0x20 ,
dbiTileBackground = 0x21 ,
dbiAutoLock = 0x22 ,
dbiDialogLastPath = 0x23 ,
dbiRecentEmojis = 0x24 ,
dbiEmojiVariants = 0x25 ,
dbiRecentStickers = 0x26 ,
dbiDcOption = 0x27 ,
dbiTryIPv6 = 0x28 ,
dbiSongVolume = 0x29 ,
2016-03-10 13:15:21 +03:00
dbiWindowsNotifications = 0x30 ,
2016-04-11 14:59:01 +04:00
dbiIncludeMuted = 0x31 ,
dbiMegagroupSizeMax = 0x32 ,
dbiDownloadPath = 0x33 ,
dbiAutoDownload = 0x34 ,
dbiSavedGifsLimit = 0x35 ,
dbiShowingSavedGifs = 0x36 ,
dbiAutoPlay = 0x37 ,
dbiAdaptiveForWide = 0x38 ,
2016-03-10 13:15:21 +03:00
dbiHiddenPinnedMessages = 0x39 ,
2016-04-11 14:59:01 +04:00
dbiDialogsMode = 0x40 ,
2016-06-22 21:41:13 +03:00
dbiModerateMode = 0x41 ,
2016-03-10 13:15:21 +03:00
2016-04-11 14:59:01 +04:00
dbiEncryptedWithSalt = 333 ,
dbiEncrypted = 444 ,
2016-03-10 13:15:21 +03:00
// 500-600 reserved
2016-04-11 14:59:01 +04:00
dbiVersion = 666 ,
2016-03-10 13:15:21 +03:00
} ;
2014-11-22 12:45:04 +03:00
typedef QMap < PeerId , FileKey > DraftsMap ;
2016-02-25 13:32:31 +03:00
DraftsMap _draftsMap , _draftCursorsMap ;
2014-11-22 12:45:04 +03:00
typedef QMap < PeerId , bool > DraftsNotReadMap ;
DraftsNotReadMap _draftsNotReadMap ;
2015-12-31 23:27:21 +08:00
typedef QPair < FileKey , qint32 > FileDesc ; // file, size
2014-12-05 16:44:27 +03:00
typedef QMultiMap < MediaKey , FileLocation > FileLocations ;
FileLocations _fileLocations ;
typedef QPair < MediaKey , FileLocation > FileLocationPair ;
typedef QMap < QString , FileLocationPair > FileLocationPairs ;
FileLocationPairs _fileLocationPairs ;
2015-07-01 00:07:05 +03:00
typedef QMap < MediaKey , MediaKey > FileLocationAliases ;
FileLocationAliases _fileLocationAliases ;
2015-12-31 23:27:21 +08:00
typedef QMap < QString , FileDesc > WebFilesMap ;
WebFilesMap _webFilesMap ;
uint64 _storageWebFilesSize = 0 ;
2015-09-09 11:19:25 +03:00
FileKey _locationsKey = 0 , _reportSpamStatusesKey = 0 ;
2015-12-31 23:27:21 +08:00
2015-12-28 18:34:45 +03:00
FileKey _recentStickersKeyOld = 0 , _stickersKey = 0 , _savedGifsKey = 0 ;
2015-12-31 23:27:21 +08:00
2015-02-03 18:02:46 +03:00
FileKey _backgroundKey = 0 ;
bool _backgroundWasRead = false ;
2015-01-02 17:55:24 +03:00
2015-03-02 15:34:16 +03:00
FileKey _userSettingsKey = 0 ;
2016-01-01 22:48:32 +08:00
FileKey _recentHashtagsAndBotsKey = 0 ;
bool _recentHashtagsAndBotsWereRead = false ;
2015-03-02 15:34:16 +03:00
2015-08-07 15:11:50 +03:00
FileKey _savedPeersKey = 0 ;
2015-01-02 17:55:24 +03:00
typedef QMap < StorageKey , FileDesc > StorageMap ;
2015-05-19 18:46:45 +03:00
StorageMap _imagesMap , _stickerImagesMap , _audiosMap ;
2015-01-02 17:55:24 +03:00
int32 _storageImagesSize = 0 , _storageStickersSize = 0 , _storageAudiosSize = 0 ;
2014-12-05 16:44:27 +03:00
2014-11-22 12:45:04 +03:00
bool _mapChanged = false ;
2015-08-13 18:11:07 +03:00
int32 _oldMapVersion = 0 , _oldSettingsVersion = 0 ;
2014-12-05 16:44:27 +03:00
enum WriteMapWhen {
WriteMapNow ,
WriteMapFast ,
WriteMapSoon ,
} ;
void _writeMap ( WriteMapWhen when = WriteMapSoon ) ;
2015-09-09 11:19:25 +03:00
2014-12-05 16:44:27 +03:00
void _writeLocations ( WriteMapWhen when = WriteMapSoon ) {
if ( when ! = WriteMapNow ) {
_manager - > writeLocations ( when = = WriteMapFast ) ;
return ;
}
2015-01-02 17:55:24 +03:00
if ( ! _working ( ) ) return ;
2014-12-05 16:44:27 +03:00
_manager - > writingLocations ( ) ;
2016-01-01 05:00:28 +08:00
if ( _fileLocations . isEmpty ( ) & & _webFilesMap . isEmpty ( ) ) {
2014-12-05 16:44:27 +03:00
if ( _locationsKey ) {
clearKey ( _locationsKey ) ;
_locationsKey = 0 ;
_mapChanged = true ;
_writeMap ( ) ;
}
} else {
if ( ! _locationsKey ) {
2015-01-02 17:55:24 +03:00
_locationsKey = genKey ( ) ;
2014-12-05 16:44:27 +03:00
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
quint32 size = 0 ;
2015-07-01 00:07:05 +03:00
for ( FileLocations : : const_iterator i = _fileLocations . cbegin ( ) , e = _fileLocations . cend ( ) ; i ! = e ; + + i ) {
2015-11-26 20:34:52 +03:00
// location + type + namelen + name
2016-04-09 09:57:55 +04:00
size + = sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + Serialize : : stringSize ( i . value ( ) . name ( ) ) ;
2015-11-26 20:34:52 +03:00
if ( AppVersion > 9013 ) {
// bookmark
2016-05-05 19:04:17 +03:00
size + = Serialize : : bytearraySize ( i . value ( ) . bookmark ( ) ) ;
2015-11-26 20:34:52 +03:00
}
// date + size
2016-05-05 19:04:17 +03:00
size + = Serialize : : dateTimeSize ( ) + sizeof ( quint32 ) ;
2014-12-05 16:44:27 +03:00
}
2015-11-26 20:34:52 +03:00
2015-07-01 00:07:05 +03:00
//end mark
2016-04-09 09:57:55 +04:00
size + = sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + Serialize : : stringSize ( QString ( ) ) ;
2015-11-26 20:34:52 +03:00
if ( AppVersion > 9013 ) {
2016-05-05 19:04:17 +03:00
size + = Serialize : : bytearraySize ( QByteArray ( ) ) ;
2015-11-26 20:34:52 +03:00
}
2016-05-05 19:04:17 +03:00
size + = Serialize : : dateTimeSize ( ) + sizeof ( quint32 ) ;
2015-11-26 20:34:52 +03:00
2015-07-01 00:07:05 +03:00
size + = sizeof ( quint32 ) ; // aliases count
for ( FileLocationAliases : : const_iterator i = _fileLocationAliases . cbegin ( ) , e = _fileLocationAliases . cend ( ) ; i ! = e ; + + i ) {
// alias + location
size + = sizeof ( quint64 ) * 2 + sizeof ( quint64 ) * 2 ;
}
2015-12-31 23:27:21 +08:00
size + = sizeof ( quint32 ) ; // web files count
for ( WebFilesMap : : const_iterator i = _webFilesMap . cbegin ( ) , e = _webFilesMap . cend ( ) ; i ! = e ; + + i ) {
// url + filekey + size
2016-04-09 09:57:55 +04:00
size + = Serialize : : stringSize ( i . key ( ) ) + sizeof ( quint64 ) + sizeof ( qint32 ) ;
2015-12-31 23:27:21 +08:00
}
2014-12-05 16:44:27 +03:00
EncryptedDescriptor data ( size ) ;
for ( FileLocations : : const_iterator i = _fileLocations . cbegin ( ) ; i ! = _fileLocations . cend ( ) ; + + i ) {
2015-11-26 20:34:52 +03:00
data . stream < < quint64 ( i . key ( ) . first ) < < quint64 ( i . key ( ) . second ) < < quint32 ( i . value ( ) . type ) < < i . value ( ) . name ( ) ;
if ( AppVersion > 9013 ) {
data . stream < < i . value ( ) . bookmark ( ) ;
}
data . stream < < i . value ( ) . modified < < quint32 ( i . value ( ) . size ) ;
2014-12-05 16:44:27 +03:00
}
2015-11-26 20:34:52 +03:00
data . stream < < quint64 ( 0 ) < < quint64 ( 0 ) < < quint32 ( 0 ) < < QString ( ) ;
if ( AppVersion > 9013 ) {
data . stream < < QByteArray ( ) ;
}
data . stream < < QDateTime : : currentDateTime ( ) < < quint32 ( 0 ) ;
2015-07-01 00:07:05 +03:00
data . stream < < quint32 ( _fileLocationAliases . size ( ) ) ;
for ( FileLocationAliases : : const_iterator i = _fileLocationAliases . cbegin ( ) , e = _fileLocationAliases . cend ( ) ; i ! = e ; + + i ) {
data . stream < < quint64 ( i . key ( ) . first ) < < quint64 ( i . key ( ) . second ) < < quint64 ( i . value ( ) . first ) < < quint64 ( i . value ( ) . second ) ;
}
2015-12-31 23:27:21 +08:00
data . stream < < quint32 ( _webFilesMap . size ( ) ) ;
for ( WebFilesMap : : const_iterator i = _webFilesMap . cbegin ( ) , e = _webFilesMap . cend ( ) ; i ! = e ; + + i ) {
data . stream < < i . key ( ) < < quint64 ( i . value ( ) . first ) < < qint32 ( i . value ( ) . second ) ;
}
2014-12-05 16:44:27 +03:00
FileWriteDescriptor file ( _locationsKey ) ;
file . writeEncrypted ( data ) ;
}
}
void _readLocations ( ) {
FileReadDescriptor locations ;
2015-03-02 15:34:16 +03:00
if ( ! readEncryptedFile ( locations , _locationsKey ) ) {
2014-12-05 16:44:27 +03:00
clearKey ( _locationsKey ) ;
_locationsKey = 0 ;
_writeMap ( ) ;
return ;
}
2015-07-01 00:07:05 +03:00
bool endMarkFound = false ;
2014-12-05 16:44:27 +03:00
while ( ! locations . stream . atEnd ( ) ) {
quint64 first , second ;
2015-11-26 20:34:52 +03:00
QByteArray bookmark ;
2014-12-05 16:44:27 +03:00
FileLocation loc ;
quint32 type ;
2015-11-26 20:34:52 +03:00
locations . stream > > first > > second > > type > > loc . fname ;
if ( locations . version > 9013 ) {
locations . stream > > bookmark ;
}
locations . stream > > loc . modified > > loc . size ;
loc . setBookmark ( bookmark ) ;
2014-12-05 16:44:27 +03:00
2015-11-26 20:34:52 +03:00
if ( ! first & & ! second & & ! type & & loc . fname . isEmpty ( ) & & ! loc . size ) { // end mark
2015-07-01 00:07:05 +03:00
endMarkFound = true ;
break ;
}
2014-12-05 16:44:27 +03:00
MediaKey key ( first , second ) ;
2015-05-19 18:46:45 +03:00
loc . type = StorageFileType ( type ) ;
2014-12-05 16:44:27 +03:00
2015-11-26 20:34:52 +03:00
_fileLocations . insert ( key , loc ) ;
_fileLocationPairs . insert ( loc . fname , FileLocationPair ( key , loc ) ) ;
2014-12-05 16:44:27 +03:00
}
2015-07-01 00:07:05 +03:00
if ( endMarkFound ) {
quint32 cnt ;
locations . stream > > cnt ;
2015-07-03 14:00:11 +03:00
for ( quint32 i = 0 ; i < cnt ; + + i ) {
2015-07-01 00:07:05 +03:00
quint64 kfirst , ksecond , vfirst , vsecond ;
locations . stream > > kfirst > > ksecond > > vfirst > > vsecond ;
_fileLocationAliases . insert ( MediaKey ( kfirst , ksecond ) , MediaKey ( vfirst , vsecond ) ) ;
}
2015-12-31 23:27:21 +08:00
2016-01-01 22:48:32 +08:00
if ( ! locations . stream . atEnd ( ) ) {
_storageWebFilesSize = 0 ;
_webFilesMap . clear ( ) ;
2015-12-31 23:27:21 +08:00
2016-01-01 22:48:32 +08:00
quint32 webLocationsCount ;
locations . stream > > webLocationsCount ;
for ( quint32 i = 0 ; i < webLocationsCount ; + + i ) {
QString url ;
quint64 key ;
qint32 size ;
locations . stream > > url > > key > > size ;
_webFilesMap . insert ( url , FileDesc ( key , size ) ) ;
_storageWebFilesSize + = size ;
}
2015-12-31 23:27:21 +08:00
}
2015-07-01 00:07:05 +03:00
}
2014-12-05 16:44:27 +03:00
}
2015-09-09 11:19:25 +03:00
void _writeReportSpamStatuses ( ) {
if ( ! _working ( ) ) return ;
if ( cReportSpamStatuses ( ) . isEmpty ( ) ) {
if ( _reportSpamStatusesKey ) {
clearKey ( _reportSpamStatusesKey ) ;
_reportSpamStatusesKey = 0 ;
_mapChanged = true ;
_writeMap ( ) ;
}
} else {
if ( ! _reportSpamStatusesKey ) {
_reportSpamStatusesKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
const ReportSpamStatuses & statuses ( cReportSpamStatuses ( ) ) ;
quint32 size = sizeof ( qint32 ) ;
for ( ReportSpamStatuses : : const_iterator i = statuses . cbegin ( ) , e = statuses . cend ( ) ; i ! = e ; + + i ) {
// peer + status
size + = sizeof ( quint64 ) + sizeof ( qint32 ) ;
}
EncryptedDescriptor data ( size ) ;
data . stream < < qint32 ( statuses . size ( ) ) ;
for ( ReportSpamStatuses : : const_iterator i = statuses . cbegin ( ) , e = statuses . cend ( ) ; i ! = e ; + + i ) {
data . stream < < quint64 ( i . key ( ) ) < < qint32 ( i . value ( ) ) ;
}
FileWriteDescriptor file ( _reportSpamStatusesKey ) ;
file . writeEncrypted ( data ) ;
}
}
void _readReportSpamStatuses ( ) {
FileReadDescriptor statuses ;
if ( ! readEncryptedFile ( statuses , _reportSpamStatusesKey ) ) {
clearKey ( _reportSpamStatusesKey ) ;
_reportSpamStatusesKey = 0 ;
_writeMap ( ) ;
return ;
}
ReportSpamStatuses & map ( cRefReportSpamStatuses ( ) ) ;
map . clear ( ) ;
qint32 size = 0 ;
statuses . stream > > size ;
for ( int32 i = 0 ; i < size ; + + i ) {
quint64 peer = 0 ;
qint32 status = 0 ;
statuses . stream > > peer > > status ;
map . insert ( peer , DBIPeerReportSpamStatus ( status ) ) ;
}
}
2016-03-19 19:55:15 +03:00
MTP : : DcOptions * _dcOpts = 0 ;
2015-03-02 15:34:16 +03:00
bool _readSetting ( quint32 blockId , QDataStream & stream , int version ) {
switch ( blockId ) {
2015-06-10 15:48:26 +03:00
case dbiDcOptionOld : {
2015-03-02 15:34:16 +03:00
quint32 dcId , port ;
QString host , ip ;
stream > > dcId > > host > > ip > > port ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2016-03-19 19:55:15 +03:00
if ( _dcOpts ) _dcOpts - > insert ( dcId , MTP : : DcOption ( dcId , 0 , ip . toUtf8 ( ) . constData ( ) , port ) ) ;
2015-06-10 15:48:26 +03:00
} break ;
case dbiDcOption : {
2016-03-19 19:55:15 +03:00
quint32 dcIdWithShift , port ;
qint32 flags ;
2015-06-10 15:48:26 +03:00
QString ip ;
stream > > dcIdWithShift > > flags > > ip > > port ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2016-03-24 11:57:11 +03:00
if ( _dcOpts ) _dcOpts - > insert ( dcIdWithShift , MTP : : DcOption ( MTP : : bareDcId ( dcIdWithShift ) , MTPDdcOption : : Flags ( flags ) , ip . toUtf8 ( ) . constData ( ) , port ) ) ;
2015-03-02 15:34:16 +03:00
} break ;
2016-02-18 19:36:33 +03:00
case dbiChatSizeMax : {
2015-03-02 15:34:16 +03:00
qint32 maxSize ;
stream > > maxSize ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2016-02-18 19:36:33 +03:00
Global : : SetChatSizeMax ( maxSize ) ;
2015-03-02 15:34:16 +03:00
} break ;
2015-12-28 00:37:48 +03:00
case dbiSavedGifsLimit : {
qint32 limit ;
stream > > limit ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2016-02-18 19:36:33 +03:00
Global : : SetSavedGifsLimit ( limit ) ;
2015-12-28 00:37:48 +03:00
} break ;
2016-02-18 19:36:33 +03:00
case dbiMegagroupSizeMax : {
2015-11-03 12:49:10 -05:00
qint32 maxSize ;
stream > > maxSize ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2016-02-18 19:36:33 +03:00
Global : : SetMegagroupSizeMax ( maxSize ) ;
2015-11-03 12:49:10 -05:00
} break ;
2015-03-02 15:34:16 +03:00
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 ;
2016-01-11 23:43:29 +08:00
DEBUG_LOG ( ( " MTP Info: key found, dc %1, key: %2 " ) . arg ( dcId ) . arg ( Logs : : mb ( key , 256 ) . str ( ) ) ) ;
2016-03-24 11:57:11 +03:00
dcId = MTP : : bareDcId ( dcId ) ;
2016-03-24 15:57:10 +03:00
MTP : : AuthKeyPtr keyPtr ( new MTP : : AuthKey ( ) ) ;
2015-03-02 15:34:16 +03:00
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 ;
2015-12-24 00:19:57 +03:00
case dbiAutoDownload : {
qint32 photo , audio , gif ;
stream > > photo > > audio > > gif ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetAutoDownloadPhoto ( photo ) ;
cSetAutoDownloadAudio ( audio ) ;
cSetAutoDownloadGif ( gif ) ;
} break ;
2015-12-28 00:37:48 +03:00
case dbiAutoPlay : {
qint32 gif ;
stream > > gif ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetAutoPlayGif ( gif = = 1 ) ;
} break ;
2016-04-11 14:59:01 +04:00
case dbiDialogsMode : {
qint32 enabled , modeInt ;
stream > > enabled > > modeInt ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
Global : : SetDialogsModeEnabled ( enabled = = 1 ) ;
Dialogs : : Mode mode = Dialogs : : Mode : : All ;
if ( enabled ) {
mode = static_cast < Dialogs : : Mode > ( modeInt ) ;
if ( mode ! = Dialogs : : Mode : : All & & mode ! = Dialogs : : Mode : : Important ) {
mode = Dialogs : : Mode : : All ;
}
}
Global : : SetDialogsMode ( mode ) ;
} break ;
2016-06-22 21:41:13 +03:00
case dbiModerateMode : {
qint32 enabled ;
stream > > enabled ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
Global : : SetModerateModeEnabled ( enabled = = 1 ) ;
} break ;
2015-08-28 18:15:56 +03:00
case dbiIncludeMuted : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetIncludeMuted ( v = = 1 ) ;
} break ;
2015-12-28 00:37:48 +03:00
case dbiShowingSavedGifs : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetShowingSavedGifs ( v = = 1 ) ;
} break ;
2015-03-02 15:34:16 +03:00
case dbiDesktopNotify : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetDesktopNotify ( v = = 1 ) ;
2015-10-12 23:48:14 +02:00
if ( App : : wnd ( ) ) App : : wnd ( ) - > updateTrayMenu ( ) ;
2015-03-02 15:34:16 +03:00
} break ;
2015-08-11 22:50:48 +03:00
case dbiWindowsNotifications : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetWindowsNotifications ( v = = 1 ) ;
2015-08-19 16:54:17 +03:00
if ( cPlatform ( ) = = dbipWindows ) {
cSetCustomNotifies ( ( App : : wnd ( ) ? ! App : : wnd ( ) - > psHasNativeNotifications ( ) : true ) | | ! cWindowsNotifications ( ) ) ;
}
2015-08-11 22:50:48 +03:00
} break ;
2015-03-02 15:34:16 +03:00
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 ;
2015-06-25 21:04:40 +03:00
case dbiTryIPv6 : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetTryIPv6 ( v = = 1 ) ;
} break ;
2015-03-02 15:34:16 +03:00
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 ) ;
2015-09-16 16:04:08 +03:00
if ( App : : main ( ) ) App : : main ( ) - > ctrlEnterSubmitUpdated ( ) ;
2015-03-02 15:34:16 +03:00
} 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 ) ;
2015-04-22 14:33:52 +03:00
if ( version < 8005 & & ! _backgroundKey ) {
cSetTileBackground ( false ) ;
}
2015-03-02 15:34:16 +03:00
} break ;
2016-02-21 17:27:54 +03:00
case dbiAdaptiveForWide : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
Global : : SetAdaptiveForWide ( v = = 1 ) ;
} break ;
2015-03-02 15:34:16 +03:00
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 ;
2015-11-26 20:34:52 +03:00
case dbiDownloadPathOld : {
2015-03-02 15:34:16 +03:00
QString v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2015-11-26 20:34:52 +03:00
if ( ! v . isEmpty ( ) & & v ! = qstr ( " tmp " ) & & ! v . endsWith ( ' / ' ) ) v + = ' / ' ;
cSetDownloadPath ( v ) ;
cSetDownloadPathBookmark ( QByteArray ( ) ) ;
} break ;
case dbiDownloadPath : {
QString v ;
QByteArray bookmark ;
stream > > v > > bookmark ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
if ( ! v . isEmpty ( ) & & v ! = qstr ( " tmp " ) & & ! v . endsWith ( ' / ' ) ) v + = ' / ' ;
2015-03-02 15:34:16 +03:00
cSetDownloadPath ( v ) ;
2015-11-26 20:34:52 +03:00
cSetDownloadPathBookmark ( bookmark ) ;
psDownloadPathEnableAccess ( ) ;
2015-03-02 15:34:16 +03:00
} break ;
case dbiCompressPastedImage : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetCompressPastedImage ( v = = 1 ) ;
} break ;
2015-11-27 20:10:16 +03:00
case dbiEmojiTabOld : {
2015-03-02 15:34:16 +03:00
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
2015-11-27 20:10:16 +03:00
// deprecated
2015-05-08 15:45:14 +03:00
} break ;
case dbiRecentEmojisOld : {
RecentEmojisPreloadOld v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
if ( ! v . isEmpty ( ) ) {
RecentEmojisPreload p ;
p . reserve ( v . size ( ) ) ;
for ( int i = 0 ; i < v . size ( ) ; + + i ) {
uint64 e ( v . at ( i ) . first ) ;
switch ( e ) {
case 0xD83CDDEFLLU : e = 0xD83CDDEFD83CDDF5LLU ; break ;
case 0xD83CDDF0LLU : e = 0xD83CDDF0D83CDDF7LLU ; break ;
case 0xD83CDDE9LLU : e = 0xD83CDDE9D83CDDEALLU ; break ;
case 0xD83CDDE8LLU : e = 0xD83CDDE8D83CDDF3LLU ; break ;
case 0xD83CDDFALLU : e = 0xD83CDDFAD83CDDF8LLU ; break ;
case 0xD83CDDEBLLU : e = 0xD83CDDEBD83CDDF7LLU ; break ;
case 0xD83CDDEALLU : e = 0xD83CDDEAD83CDDF8LLU ; break ;
case 0xD83CDDEELLU : e = 0xD83CDDEED83CDDF9LLU ; break ;
case 0xD83CDDF7LLU : e = 0xD83CDDF7D83CDDFALLU ; break ;
case 0xD83CDDECLLU : e = 0xD83CDDECD83CDDE7LLU ; break ;
}
p . push_back ( qMakePair ( e , v . at ( i ) . second ) ) ;
}
cSetRecentEmojisPreload ( p ) ;
2015-03-02 15:34:16 +03:00
}
} break ;
case dbiRecentEmojis : {
2015-05-08 15:45:14 +03:00
RecentEmojisPreload v ;
2015-03-02 15:34:16 +03:00
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetRecentEmojisPreload ( v ) ;
} break ;
2015-05-19 18:46:45 +03:00
case dbiRecentStickers : {
RecentStickerPreload v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetRecentStickersPreload ( v ) ;
} break ;
2015-05-08 15:45:14 +03:00
case dbiEmojiVariants : {
EmojiColorVariants v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetEmojiVariants ( v ) ;
} break ;
2016-03-10 13:15:21 +03:00
case dbiHiddenPinnedMessages : {
Global : : HiddenPinnedMessagesMap v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
Global : : SetHiddenPinnedMessages ( v ) ;
} break ;
2015-03-24 18:49:07 +03:00
case dbiDialogLastPath : {
QString path ;
stream > > path ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetDialogLastPath ( path ) ;
} break ;
2015-07-03 11:47:16 +03:00
case dbiSongVolume : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetSongVolume ( snap ( v / 1e6 , 0. , 1. ) ) ;
} break ;
2015-03-02 15:34:16 +03:00
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 ) ) {
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading old config... " ) ) ;
2015-03-02 15:34:16 +03:00
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
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading encrypted old user config... " ) ) ;
2015-03-02 15:34:16 +03:00
_readOldUserSettingsFields ( & decryptedStream , version ) ;
} else if ( ! _readSetting ( blockId , stream , version ) ) {
return ;
}
}
}
bool _readOldUserSettings ( bool remove = true ) {
bool result = false ;
2015-04-02 13:33:19 +03:00
QFile file ( cWorkingDir ( ) + cDataFile ( ) + ( cTestMode ( ) ? qsl ( " _test " ) : QString ( ) ) + qsl ( " _config " ) ) ;
2015-03-02 15:34:16 +03:00
if ( file . open ( QIODevice : : ReadOnly ) ) {
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading old user config... " ) ) ;
2015-03-02 15:34:16 +03:00
qint32 version = 0 ;
2016-03-19 19:55:15 +03:00
MTP : : DcOptions dcOpts ;
2015-05-14 19:50:04 +03:00
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
dcOpts = Global : : DcOptions ( ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
_dcOpts = & dcOpts ;
_readOldUserSettingsFields ( & file , version ) ;
2015-05-14 19:50:04 +03:00
{
QWriteLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
Global : : SetDcOptions ( dcOpts ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
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
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading encrypted old keys... " ) ) ;
2015-03-02 15:34:16 +03:00
_readOldMtpDataFields ( & decryptedStream , version ) ;
} else if ( ! _readSetting ( blockId , stream , version ) ) {
return ;
}
}
}
bool _readOldMtpData ( bool remove = true ) {
bool result = false ;
2015-04-02 13:33:19 +03:00
QFile file ( cWorkingDir ( ) + cDataFile ( ) + ( cTestMode ( ) ? qsl ( " _test " ) : QString ( ) ) ) ;
2015-03-02 15:34:16 +03:00
if ( file . open ( QIODevice : : ReadOnly ) ) {
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading old keys... " ) ) ;
2015-03-02 15:34:16 +03:00
qint32 version = 0 ;
2016-03-19 19:55:15 +03:00
MTP : : DcOptions dcOpts ;
2015-05-14 19:50:04 +03:00
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
dcOpts = Global : : DcOptions ( ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
_dcOpts = & dcOpts ;
_readOldMtpDataFields ( & file , version ) ;
2015-05-14 19:50:04 +03:00
{
QWriteLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
Global : : SetDcOptions ( dcOpts ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
file . close ( ) ;
result = true ;
}
if ( remove ) file . remove ( ) ;
return result ;
}
void _writeUserSettings ( ) {
if ( ! _userSettingsKey ) {
_userSettingsKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
2016-06-22 21:41:13 +03:00
uint32 size = 17 * ( sizeof ( quint32 ) + sizeof ( qint32 ) ) ;
2016-05-05 19:04:17 +03:00
size + = sizeof ( quint32 ) + Serialize : : stringSize ( cAskDownloadPath ( ) ? QString ( ) : cDownloadPath ( ) ) + Serialize : : bytearraySize ( cAskDownloadPath ( ) ? QByteArray ( ) : cDownloadPathBookmark ( ) ) ;
2015-05-19 18:46:45 +03:00
size + = sizeof ( quint32 ) + sizeof ( qint32 ) + ( cRecentEmojisPreload ( ) . isEmpty ( ) ? cGetRecentEmojis ( ) . size ( ) : cRecentEmojisPreload ( ) . size ( ) ) * ( sizeof ( uint64 ) + sizeof ( ushort ) ) ;
2015-05-08 15:45:14 +03:00
size + = sizeof ( quint32 ) + sizeof ( qint32 ) + cEmojiVariants ( ) . size ( ) * ( sizeof ( uint32 ) + sizeof ( uint64 ) ) ;
2015-05-19 18:46:45 +03:00
size + = sizeof ( quint32 ) + sizeof ( qint32 ) + ( cRecentStickersPreload ( ) . isEmpty ( ) ? cGetRecentStickers ( ) . size ( ) : cRecentStickersPreload ( ) . size ( ) ) * ( sizeof ( uint64 ) + sizeof ( ushort ) ) ;
2016-04-09 09:57:55 +04:00
size + = sizeof ( quint32 ) + Serialize : : stringSize ( cDialogLastPath ( ) ) ;
2015-12-24 00:19:57 +03:00
size + = sizeof ( quint32 ) + 3 * sizeof ( qint32 ) ;
2016-04-11 14:59:01 +04:00
size + = sizeof ( quint32 ) + 2 * sizeof ( qint32 ) ;
2016-03-10 13:15:21 +03:00
if ( ! Global : : HiddenPinnedMessages ( ) . isEmpty ( ) ) {
size + = sizeof ( quint32 ) + sizeof ( qint32 ) + Global : : HiddenPinnedMessages ( ) . size ( ) * ( sizeof ( PeerId ) + sizeof ( MsgId ) ) ;
}
2015-03-02 15:34:16 +03:00
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( dbiSendKey ) < < qint32 ( cCtrlEnter ( ) ? dbiskCtrlEnter : dbiskEnter ) ;
data . stream < < quint32 ( dbiTileBackground ) < < qint32 ( cTileBackground ( ) ? 1 : 0 ) ;
2016-02-21 17:27:54 +03:00
data . stream < < quint32 ( dbiAdaptiveForWide ) < < qint32 ( Global : : AdaptiveForWide ( ) ? 1 : 0 ) ;
2015-03-02 15:34:16 +03:00
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 ( ) ) ;
2015-08-28 18:15:56 +03:00
data . stream < < quint32 ( dbiIncludeMuted ) < < qint32 ( cIncludeMuted ( ) ) ;
2015-12-28 00:37:48 +03:00
data . stream < < quint32 ( dbiShowingSavedGifs ) < < qint32 ( cShowingSavedGifs ( ) ) ;
2015-03-02 15:34:16 +03:00
data . stream < < quint32 ( dbiDesktopNotify ) < < qint32 ( cDesktopNotify ( ) ) ;
data . stream < < quint32 ( dbiNotifyView ) < < qint32 ( cNotifyView ( ) ) ;
2015-08-11 22:50:48 +03:00
data . stream < < quint32 ( dbiWindowsNotifications ) < < qint32 ( cWindowsNotifications ( ) ) ;
2015-03-02 15:34:16 +03:00
data . stream < < quint32 ( dbiAskDownloadPath ) < < qint32 ( cAskDownloadPath ( ) ) ;
2015-11-26 20:34:52 +03:00
data . stream < < quint32 ( dbiDownloadPath ) < < ( cAskDownloadPath ( ) ? QString ( ) : cDownloadPath ( ) ) < < ( cAskDownloadPath ( ) ? QByteArray ( ) : cDownloadPathBookmark ( ) ) ;
2015-03-02 15:34:16 +03:00
data . stream < < quint32 ( dbiCompressPastedImage ) < < qint32 ( cCompressPastedImage ( ) ) ;
2015-03-24 18:49:07 +03:00
data . stream < < quint32 ( dbiDialogLastPath ) < < cDialogLastPath ( ) ;
2015-07-03 11:47:16 +03:00
data . stream < < quint32 ( dbiSongVolume ) < < qint32 ( qRound ( cSongVolume ( ) * 1e6 ) ) ;
2015-12-24 00:19:57 +03:00
data . stream < < quint32 ( dbiAutoDownload ) < < qint32 ( cAutoDownloadPhoto ( ) ) < < qint32 ( cAutoDownloadAudio ( ) ) < < qint32 ( cAutoDownloadGif ( ) ) ;
2016-04-11 14:59:01 +04:00
data . stream < < quint32 ( dbiDialogsMode ) < < qint32 ( Global : : DialogsModeEnabled ( ) ? 1 : 0 ) < < static_cast < qint32 > ( Global : : DialogsMode ( ) ) ;
2016-06-22 21:41:13 +03:00
data . stream < < quint32 ( dbiModerateMode ) < < qint32 ( Global : : ModerateModeEnabled ( ) ? 1 : 0 ) ;
2015-12-28 00:37:48 +03:00
data . stream < < quint32 ( dbiAutoPlay ) < < qint32 ( cAutoPlayGif ( ) ? 1 : 0 ) ;
2015-03-02 15:34:16 +03:00
2015-05-19 18:46:45 +03:00
{
RecentEmojisPreload v ( cRecentEmojisPreload ( ) ) ;
if ( v . isEmpty ( ) ) {
v . reserve ( cGetRecentEmojis ( ) . size ( ) ) ;
for ( RecentEmojiPack : : const_iterator i = cGetRecentEmojis ( ) . cbegin ( ) , e = cGetRecentEmojis ( ) . cend ( ) ; i ! = e ; + + i ) {
v . push_back ( qMakePair ( emojiKey ( i - > first ) , i - > second ) ) ;
}
}
data . stream < < quint32 ( dbiRecentEmojis ) < < v ;
2015-03-02 15:34:16 +03:00
}
2015-05-08 15:45:14 +03:00
data . stream < < quint32 ( dbiEmojiVariants ) < < cEmojiVariants ( ) ;
2015-05-19 18:46:45 +03:00
{
RecentStickerPreload v ( cRecentStickersPreload ( ) ) ;
if ( v . isEmpty ( ) ) {
v . reserve ( cGetRecentStickers ( ) . size ( ) ) ;
for ( RecentStickerPack : : const_iterator i = cGetRecentStickers ( ) . cbegin ( ) , e = cGetRecentStickers ( ) . cend ( ) ; i ! = e ; + + i ) {
v . push_back ( qMakePair ( i - > first - > id , i - > second ) ) ;
}
}
data . stream < < quint32 ( dbiRecentStickers ) < < v ;
}
2016-03-10 13:15:21 +03:00
if ( ! Global : : HiddenPinnedMessages ( ) . isEmpty ( ) ) {
data . stream < < quint32 ( dbiHiddenPinnedMessages ) < < Global : : HiddenPinnedMessages ( ) ;
}
2015-05-08 15:45:14 +03:00
2015-03-02 15:34:16 +03:00
FileWriteDescriptor file ( _userSettingsKey ) ;
file . writeEncrypted ( data ) ;
}
void _readUserSettings ( ) {
FileReadDescriptor userSettings ;
if ( ! readEncryptedFile ( userSettings , _userSettingsKey ) ) {
_readOldUserSettings ( ) ;
return _writeUserSettings ( ) ;
}
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading encrypted user settings... " ) ) ;
2015-03-02 15:34:16 +03:00
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 ;
}
2016-03-24 15:57:10 +03:00
MTP : : AuthKeysMap keys = MTP : : getKeys ( ) ;
2015-03-02 15:34:16 +03:00
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 ( ) ) ;
2016-03-24 15:57:10 +03:00
for_const ( const MTP : : AuthKeyPtr & key , keys ) {
data . stream < < quint32 ( dbiKey ) < < quint32 ( key - > getDC ( ) ) ;
key - > write ( data . stream ) ;
2015-03-02 15:34:16 +03:00
}
mtp . writeEncrypted ( data , _localKey ) ;
}
void _readMtpData ( ) {
FileReadDescriptor mtp ;
if ( ! readEncryptedFile ( mtp , toFilePart ( _dataNameKey ) , SafePath ) ) {
if ( _localKey . created ( ) ) {
_readOldMtpData ( ) ;
_writeMtpData ( ) ;
}
return ;
}
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading encrypted mtp data... " ) ) ;
2015-03-02 15:34:16 +03:00
while ( ! mtp . stream . atEnd ( ) ) {
quint32 blockId ;
mtp . stream > > blockId ;
if ( ! _checkStreamStatus ( mtp . stream ) ) {
return _writeMtpData ( ) ;
}
if ( ! _readSetting ( blockId , mtp . stream , mtp . version ) ) {
return _writeMtpData ( ) ;
}
}
}
2014-11-22 12:45:04 +03:00
Local : : ReadMapState _readMap ( const QByteArray & pass ) {
uint64 ms = getms ( ) ;
2015-04-02 13:33:19 +03:00
QByteArray dataNameUtf8 = ( cDataFile ( ) + ( cTestMode ( ) ? qsl ( " :/test/ " ) : QString ( ) ) ) . toUtf8 ( ) ;
2015-03-02 15:34:16 +03:00
FileKey dataNameHash [ 2 ] ;
2014-11-22 12:45:04 +03:00
hashMd5 ( dataNameUtf8 . constData ( ) , dataNameUtf8 . size ( ) , dataNameHash ) ;
2015-03-02 15:34:16 +03:00
_dataNameKey = dataNameHash [ 0 ] ;
_userBasePath = _basePath + toFilePart ( _dataNameKey ) + QChar ( ' / ' ) ;
2014-11-22 12:45:04 +03:00
FileReadDescriptor mapData ;
if ( ! readFile ( mapData , qsl ( " map " ) ) ) {
return Local : : ReadMapFailed ;
}
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading map... " ) ) ;
2014-11-22 12:45:04 +03:00
QByteArray salt , keyEncrypted , mapEncrypted ;
mapData . stream > > salt > > keyEncrypted > > mapEncrypted ;
2015-03-02 15:34:16 +03:00
if ( ! _checkStreamStatus ( mapData . stream ) ) {
2014-11-22 12:45:04 +03:00
return Local : : ReadMapFailed ;
}
2015-03-02 15:34:16 +03:00
2014-11-22 12:45:04 +03:00
if ( salt . size ( ) ! = LocalEncryptSaltSize ) {
LOG ( ( " App Error: bad salt in map file, size: %1 " ) . arg ( salt . size ( ) ) ) ;
return Local : : ReadMapFailed ;
}
createLocalKey ( pass , & salt , & _passKey ) ;
EncryptedDescriptor keyData , map ;
if ( ! decryptLocal ( keyData , keyEncrypted , _passKey ) ) {
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: could not decrypt pass-protected key from map file, maybe bad password... " ) ) ;
2014-11-22 12:45:04 +03:00
return Local : : ReadMapPassNeeded ;
}
uchar key [ LocalEncryptKeySize ] = { 0 } ;
if ( keyData . stream . readRawData ( ( char * ) key , LocalEncryptKeySize ) ! = LocalEncryptKeySize | | ! keyData . stream . atEnd ( ) ) {
LOG ( ( " App Error: could not read pass-protected key from map file " ) ) ;
return Local : : ReadMapFailed ;
}
_localKey . setKey ( key ) ;
_passKeyEncrypted = keyEncrypted ;
_passKeySalt = salt ;
if ( ! decryptLocal ( map , mapEncrypted ) ) {
LOG ( ( " App Error: could not decrypt map. " ) ) ;
return Local : : ReadMapFailed ;
}
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading encrypted map... " ) ) ;
2014-11-22 12:45:04 +03:00
2016-02-25 13:32:31 +03:00
DraftsMap draftsMap , draftCursorsMap ;
2014-11-22 12:45:04 +03:00
DraftsNotReadMap draftsNotReadMap ;
2015-05-19 18:46:45 +03:00
StorageMap imagesMap , stickerImagesMap , audiosMap ;
2015-01-02 17:55:24 +03:00
qint64 storageImagesSize = 0 , storageStickersSize = 0 , storageAudiosSize = 0 ;
2015-12-28 00:37:48 +03:00
quint64 locationsKey = 0 , reportSpamStatusesKey = 0 ;
quint64 recentStickersKeyOld = 0 , stickersKey = 0 , savedGifsKey = 0 ;
2016-01-01 22:48:32 +08:00
quint64 backgroundKey = 0 , userSettingsKey = 0 , recentHashtagsAndBotsKey = 0 , savedPeersKey = 0 ;
2014-11-22 12:45:04 +03:00
while ( ! map . stream . atEnd ( ) ) {
quint32 keyType ;
map . stream > > keyType ;
switch ( keyType ) {
case lskDraft : {
quint32 count = 0 ;
map . stream > > count ;
for ( quint32 i = 0 ; i < count ; + + i ) {
FileKey key ;
quint64 p ;
map . stream > > key > > p ;
draftsMap . insert ( p , key ) ;
draftsNotReadMap . insert ( p , true ) ;
}
} break ;
case lskDraftPosition : {
quint32 count = 0 ;
map . stream > > count ;
for ( quint32 i = 0 ; i < count ; + + i ) {
FileKey key ;
quint64 p ;
map . stream > > key > > p ;
2016-02-25 13:32:31 +03:00
draftCursorsMap . insert ( p , key ) ;
2014-11-22 12:45:04 +03:00
}
} break ;
2015-01-02 17:55:24 +03:00
case lskImages : {
quint32 count = 0 ;
map . stream > > count ;
for ( quint32 i = 0 ; i < count ; + + i ) {
FileKey key ;
quint64 first , second ;
qint32 size ;
map . stream > > key > > first > > second > > size ;
imagesMap . insert ( StorageKey ( first , second ) , FileDesc ( key , size ) ) ;
storageImagesSize + = size ;
}
} break ;
2015-05-19 18:46:45 +03:00
case lskStickerImages : {
2015-01-02 17:55:24 +03:00
quint32 count = 0 ;
map . stream > > count ;
for ( quint32 i = 0 ; i < count ; + + i ) {
FileKey key ;
quint64 first , second ;
qint32 size ;
map . stream > > key > > first > > second > > size ;
2015-05-19 18:46:45 +03:00
stickerImagesMap . insert ( StorageKey ( first , second ) , FileDesc ( key , size ) ) ;
2015-01-02 17:55:24 +03:00
storageStickersSize + = size ;
}
} break ;
case lskAudios : {
2014-11-22 12:45:04 +03:00
quint32 count = 0 ;
map . stream > > count ;
for ( quint32 i = 0 ; i < count ; + + i ) {
FileKey key ;
quint64 first , second ;
qint32 size ;
map . stream > > key > > first > > second > > size ;
2015-01-02 17:55:24 +03:00
audiosMap . insert ( StorageKey ( first , second ) , FileDesc ( key , size ) ) ;
storageAudiosSize + = size ;
2014-11-22 12:45:04 +03:00
}
} break ;
2014-12-05 16:44:27 +03:00
case lskLocations : {
map . stream > > locationsKey ;
} break ;
2015-09-09 11:19:25 +03:00
case lskReportSpamStatuses : {
map . stream > > reportSpamStatusesKey ;
} break ;
2015-05-19 18:46:45 +03:00
case lskRecentStickersOld : {
map . stream > > recentStickersKeyOld ;
2015-01-02 17:55:24 +03:00
} break ;
2015-02-03 18:02:46 +03:00
case lskBackground : {
map . stream > > backgroundKey ;
} break ;
2015-03-02 15:34:16 +03:00
case lskUserSettings : {
map . stream > > userSettingsKey ;
} break ;
2016-01-01 22:48:32 +08:00
case lskRecentHashtagsAndBots : {
map . stream > > recentHashtagsAndBotsKey ;
2015-03-24 13:00:27 +03:00
} break ;
2015-05-19 18:46:45 +03:00
case lskStickers : {
map . stream > > stickersKey ;
} break ;
2015-12-28 18:34:45 +03:00
case lskSavedGifsOld : {
quint64 key ;
map . stream > > key ;
} break ;
2015-12-28 00:37:48 +03:00
case lskSavedGifs : {
map . stream > > savedGifsKey ;
} break ;
2015-08-07 15:11:50 +03:00
case lskSavedPeers : {
map . stream > > savedPeersKey ;
} break ;
2014-11-22 12:45:04 +03:00
default :
LOG ( ( " App Error: unknown key type in encrypted map: %1 " ) . arg ( keyType ) ) ;
return Local : : ReadMapFailed ;
}
2015-03-02 15:34:16 +03:00
if ( ! _checkStreamStatus ( map . stream ) ) {
2014-11-22 12:45:04 +03:00
return Local : : ReadMapFailed ;
}
}
2014-12-03 16:10:32 +03:00
2014-11-22 12:45:04 +03:00
_draftsMap = draftsMap ;
2016-02-25 13:32:31 +03:00
_draftCursorsMap = draftCursorsMap ;
2014-11-22 12:45:04 +03:00
_draftsNotReadMap = draftsNotReadMap ;
2015-01-02 17:55:24 +03:00
_imagesMap = imagesMap ;
_storageImagesSize = storageImagesSize ;
2015-05-19 18:46:45 +03:00
_stickerImagesMap = stickerImagesMap ;
2015-01-02 17:55:24 +03:00
_storageStickersSize = storageStickersSize ;
_audiosMap = audiosMap ;
_storageAudiosSize = storageAudiosSize ;
2014-12-05 16:44:27 +03:00
_locationsKey = locationsKey ;
2015-09-09 11:19:25 +03:00
_reportSpamStatusesKey = reportSpamStatusesKey ;
2015-05-19 18:46:45 +03:00
_recentStickersKeyOld = recentStickersKeyOld ;
_stickersKey = stickersKey ;
2015-12-28 00:37:48 +03:00
_savedGifsKey = savedGifsKey ;
2015-08-07 15:11:50 +03:00
_savedPeersKey = savedPeersKey ;
2015-02-03 18:02:46 +03:00
_backgroundKey = backgroundKey ;
2015-03-02 15:34:16 +03:00
_userSettingsKey = userSettingsKey ;
2016-01-01 22:48:32 +08:00
_recentHashtagsAndBotsKey = recentHashtagsAndBotsKey ;
2014-12-03 16:10:32 +03:00
_oldMapVersion = mapData . version ;
2014-12-13 01:32:06 +03:00
if ( _oldMapVersion < AppVersion ) {
2014-12-16 19:53:23 +03:00
_mapChanged = true ;
2014-12-13 01:32:06 +03:00
_writeMap ( ) ;
2014-12-16 19:53:23 +03:00
} else {
_mapChanged = false ;
2014-12-13 01:32:06 +03:00
}
2014-12-03 16:10:32 +03:00
2014-12-05 16:44:27 +03:00
if ( _locationsKey ) {
_readLocations ( ) ;
}
2015-09-09 11:19:25 +03:00
if ( _reportSpamStatusesKey ) {
_readReportSpamStatuses ( ) ;
}
2014-12-05 16:44:27 +03:00
2015-03-02 15:34:16 +03:00
_readUserSettings ( ) ;
_readMtpData ( ) ;
2014-11-22 12:45:04 +03:00
LOG ( ( " Map read time: %1 " ) . arg ( getms ( ) - ms ) ) ;
2015-08-13 18:11:07 +03:00
if ( _oldSettingsVersion < AppVersion ) {
Local : : writeSettings ( ) ;
}
2014-11-22 12:45:04 +03:00
return Local : : ReadMapDone ;
}
2014-12-05 16:44:27 +03:00
void _writeMap ( WriteMapWhen when ) {
2014-11-22 12:45:04 +03:00
if ( when ! = WriteMapNow ) {
_manager - > writeMap ( when = = WriteMapFast ) ;
return ;
}
_manager - > writingMap ( ) ;
if ( ! _mapChanged ) return ;
2015-03-02 15:34:16 +03:00
if ( _userBasePath . isEmpty ( ) ) {
LOG ( ( " App Error: _userBasePath is empty in writeMap() " ) ) ;
2014-11-22 12:45:04 +03:00
return ;
}
2015-03-02 15:34:16 +03:00
if ( ! QDir ( ) . exists ( _userBasePath ) ) QDir ( ) . mkpath ( _userBasePath ) ;
2014-11-22 12:45:04 +03:00
FileWriteDescriptor map ( qsl ( " map " ) ) ;
if ( _passKeySalt . isEmpty ( ) | | _passKeyEncrypted . isEmpty ( ) ) {
uchar local5Key [ LocalEncryptKeySize ] = { 0 } ;
QByteArray pass ( LocalEncryptKeySize , Qt : : Uninitialized ) , salt ( LocalEncryptSaltSize , Qt : : Uninitialized ) ;
memset_rand ( pass . data ( ) , pass . size ( ) ) ;
memset_rand ( salt . data ( ) , salt . size ( ) ) ;
createLocalKey ( pass , & salt , & _localKey ) ;
_passKeySalt . resize ( LocalEncryptSaltSize ) ;
memset_rand ( _passKeySalt . data ( ) , _passKeySalt . size ( ) ) ;
createLocalKey ( QByteArray ( ) , & _passKeySalt , & _passKey ) ;
EncryptedDescriptor passKeyData ( LocalEncryptKeySize ) ;
_localKey . write ( passKeyData . stream ) ;
2015-03-02 15:34:16 +03:00
_passKeyEncrypted = FileWriteDescriptor : : prepareEncrypted ( passKeyData , _passKey ) ;
2014-11-22 12:45:04 +03:00
}
map . writeData ( _passKeySalt ) ;
map . writeData ( _passKeyEncrypted ) ;
uint32 mapSize = 0 ;
if ( ! _draftsMap . isEmpty ( ) ) mapSize + = sizeof ( quint32 ) * 2 + _draftsMap . size ( ) * sizeof ( quint64 ) * 2 ;
2016-02-25 13:32:31 +03:00
if ( ! _draftCursorsMap . isEmpty ( ) ) mapSize + = sizeof ( quint32 ) * 2 + _draftCursorsMap . size ( ) * sizeof ( quint64 ) * 2 ;
2015-01-02 17:55:24 +03:00
if ( ! _imagesMap . isEmpty ( ) ) mapSize + = sizeof ( quint32 ) * 2 + _imagesMap . size ( ) * ( sizeof ( quint64 ) * 3 + sizeof ( qint32 ) ) ;
2015-05-19 18:46:45 +03:00
if ( ! _stickerImagesMap . isEmpty ( ) ) mapSize + = sizeof ( quint32 ) * 2 + _stickerImagesMap . size ( ) * ( sizeof ( quint64 ) * 3 + sizeof ( qint32 ) ) ;
2015-01-02 17:55:24 +03:00
if ( ! _audiosMap . isEmpty ( ) ) mapSize + = sizeof ( quint32 ) * 2 + _audiosMap . size ( ) * ( sizeof ( quint64 ) * 3 + sizeof ( qint32 ) ) ;
2014-12-05 16:44:27 +03:00
if ( _locationsKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2015-09-09 11:19:25 +03:00
if ( _reportSpamStatusesKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2015-05-19 18:46:45 +03:00
if ( _recentStickersKeyOld ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
if ( _stickersKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2015-12-28 00:37:48 +03:00
if ( _savedGifsKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2015-08-07 15:11:50 +03:00
if ( _savedPeersKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2015-02-03 18:02:46 +03:00
if ( _backgroundKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2015-03-24 13:00:27 +03:00
if ( _userSettingsKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2016-01-01 22:48:32 +08:00
if ( _recentHashtagsAndBotsKey ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
2014-11-22 12:45:04 +03:00
EncryptedDescriptor mapData ( mapSize ) ;
if ( ! _draftsMap . isEmpty ( ) ) {
mapData . stream < < quint32 ( lskDraft ) < < quint32 ( _draftsMap . size ( ) ) ;
for ( DraftsMap : : const_iterator i = _draftsMap . cbegin ( ) , e = _draftsMap . cend ( ) ; i ! = e ; + + i ) {
mapData . stream < < quint64 ( i . value ( ) ) < < quint64 ( i . key ( ) ) ;
}
}
2016-02-25 13:32:31 +03:00
if ( ! _draftCursorsMap . isEmpty ( ) ) {
mapData . stream < < quint32 ( lskDraftPosition ) < < quint32 ( _draftCursorsMap . size ( ) ) ;
for ( DraftsMap : : const_iterator i = _draftCursorsMap . cbegin ( ) , e = _draftCursorsMap . cend ( ) ; i ! = e ; + + i ) {
2014-11-22 12:45:04 +03:00
mapData . stream < < quint64 ( i . value ( ) ) < < quint64 ( i . key ( ) ) ;
}
}
2015-01-02 17:55:24 +03:00
if ( ! _imagesMap . isEmpty ( ) ) {
mapData . stream < < quint32 ( lskImages ) < < quint32 ( _imagesMap . size ( ) ) ;
for ( StorageMap : : const_iterator i = _imagesMap . cbegin ( ) , e = _imagesMap . cend ( ) ; i ! = e ; + + i ) {
mapData . stream < < quint64 ( i . value ( ) . first ) < < quint64 ( i . key ( ) . first ) < < quint64 ( i . key ( ) . second ) < < qint32 ( i . value ( ) . second ) ;
}
}
2015-05-19 18:46:45 +03:00
if ( ! _stickerImagesMap . isEmpty ( ) ) {
mapData . stream < < quint32 ( lskStickerImages ) < < quint32 ( _stickerImagesMap . size ( ) ) ;
for ( StorageMap : : const_iterator i = _stickerImagesMap . cbegin ( ) , e = _stickerImagesMap . cend ( ) ; i ! = e ; + + i ) {
2015-01-02 17:55:24 +03:00
mapData . stream < < quint64 ( i . value ( ) . first ) < < quint64 ( i . key ( ) . first ) < < quint64 ( i . key ( ) . second ) < < qint32 ( i . value ( ) . second ) ;
}
}
if ( ! _audiosMap . isEmpty ( ) ) {
mapData . stream < < quint32 ( lskAudios ) < < quint32 ( _audiosMap . size ( ) ) ;
for ( StorageMap : : const_iterator i = _audiosMap . cbegin ( ) , e = _audiosMap . cend ( ) ; i ! = e ; + + i ) {
2014-11-22 12:45:04 +03:00
mapData . stream < < quint64 ( i . value ( ) . first ) < < quint64 ( i . key ( ) . first ) < < quint64 ( i . key ( ) . second ) < < qint32 ( i . value ( ) . second ) ;
}
}
2014-12-05 16:44:27 +03:00
if ( _locationsKey ) {
mapData . stream < < quint32 ( lskLocations ) < < quint64 ( _locationsKey ) ;
}
2015-09-09 11:19:25 +03:00
if ( _reportSpamStatusesKey ) {
mapData . stream < < quint32 ( lskReportSpamStatuses ) < < quint64 ( _reportSpamStatusesKey ) ;
}
2015-05-19 18:46:45 +03:00
if ( _recentStickersKeyOld ) {
mapData . stream < < quint32 ( lskRecentStickersOld ) < < quint64 ( _recentStickersKeyOld ) ;
}
if ( _stickersKey ) {
mapData . stream < < quint32 ( lskStickers ) < < quint64 ( _stickersKey ) ;
2015-01-02 17:55:24 +03:00
}
2015-12-28 00:37:48 +03:00
if ( _savedGifsKey ) {
mapData . stream < < quint32 ( lskSavedGifs ) < < quint64 ( _savedGifsKey ) ;
}
2015-08-07 15:11:50 +03:00
if ( _savedPeersKey ) {
mapData . stream < < quint32 ( lskSavedPeers ) < < quint64 ( _savedPeersKey ) ;
}
2015-02-03 18:02:46 +03:00
if ( _backgroundKey ) {
mapData . stream < < quint32 ( lskBackground ) < < quint64 ( _backgroundKey ) ;
}
2015-03-24 13:00:27 +03:00
if ( _userSettingsKey ) {
mapData . stream < < quint32 ( lskUserSettings ) < < quint64 ( _userSettingsKey ) ;
}
2016-01-01 22:48:32 +08:00
if ( _recentHashtagsAndBotsKey ) {
mapData . stream < < quint32 ( lskRecentHashtagsAndBots ) < < quint64 ( _recentHashtagsAndBotsKey ) ;
2015-03-24 13:00:27 +03:00
}
2014-11-22 12:45:04 +03:00
map . writeEncrypted ( mapData ) ;
_mapChanged = false ;
}
}
namespace _local_inner {
Manager : : Manager ( ) {
_mapWriteTimer . setSingleShot ( true ) ;
connect ( & _mapWriteTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( mapWriteTimeout ( ) ) ) ;
2014-12-05 16:44:27 +03:00
_locationsWriteTimer . setSingleShot ( true ) ;
connect ( & _locationsWriteTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( locationsWriteTimeout ( ) ) ) ;
2014-11-22 12:45:04 +03:00
}
void Manager : : writeMap ( bool fast ) {
if ( ! _mapWriteTimer . isActive ( ) | | fast ) {
_mapWriteTimer . start ( fast ? 1 : WriteMapTimeout ) ;
} else if ( _mapWriteTimer . remainingTime ( ) < = 0 ) {
mapWriteTimeout ( ) ;
}
}
void Manager : : writingMap ( ) {
_mapWriteTimer . stop ( ) ;
}
2014-12-05 16:44:27 +03:00
void Manager : : writeLocations ( bool fast ) {
if ( ! _locationsWriteTimer . isActive ( ) | | fast ) {
_locationsWriteTimer . start ( fast ? 1 : WriteMapTimeout ) ;
} else if ( _locationsWriteTimer . remainingTime ( ) < = 0 ) {
locationsWriteTimeout ( ) ;
}
}
void Manager : : writingLocations ( ) {
_locationsWriteTimer . stop ( ) ;
}
2014-11-22 12:45:04 +03:00
void Manager : : mapWriteTimeout ( ) {
_writeMap ( WriteMapNow ) ;
}
2014-12-05 16:44:27 +03:00
void Manager : : locationsWriteTimeout ( ) {
_writeLocations ( WriteMapNow ) ;
}
2014-11-22 12:45:04 +03:00
void Manager : : finish ( ) {
if ( _mapWriteTimer . isActive ( ) ) {
mapWriteTimeout ( ) ;
2014-12-05 16:44:27 +03:00
}
if ( _locationsWriteTimer . isActive ( ) ) {
locationsWriteTimeout ( ) ;
2014-11-22 12:45:04 +03:00
}
}
}
namespace Local {
2016-01-11 23:43:29 +08:00
void finish ( ) {
2014-11-22 12:45:04 +03:00
if ( _manager ) {
_writeMap ( WriteMapNow ) ;
_manager - > finish ( ) ;
_manager - > deleteLater ( ) ;
_manager = 0 ;
2015-09-29 21:44:31 +03:00
delete _localLoader ;
_localLoader = 0 ;
2014-11-22 12:45:04 +03:00
}
}
2016-01-11 23:43:29 +08:00
void start ( ) {
t_assert ( _manager = = 0 ) ;
_manager = new _local_inner : : Manager ( ) ;
_localLoader = new TaskQueue ( 0 , FileLoaderQueueStopTimeout ) ;
2015-03-02 15:34:16 +03:00
_basePath = cWorkingDir ( ) + qsl ( " tdata/ " ) ;
if ( ! QDir ( ) . exists ( _basePath ) ) QDir ( ) . mkpath ( _basePath ) ;
FileReadDescriptor settingsData ;
2015-03-19 12:18:19 +03:00
if ( ! readFile ( settingsData , cTestMode ( ) ? qsl ( " settings_test " ) : qsl ( " settings " ) , SafePath ) ) {
2015-03-02 15:34:16 +03:00
_readOldSettings ( ) ;
_readOldUserSettings ( false ) ; // needed further in _readUserSettings
_readOldMtpData ( false ) ; // needed further in _readMtpData
return writeSettings ( ) ;
}
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading settings... " ) ) ;
2015-03-02 15:34:16 +03:00
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 ) ) {
2016-03-24 18:07:13 +03:00
LOG ( ( " App Error: could not decrypt settings from settings file, maybe bad passcode... " ) ) ;
2015-03-02 15:34:16 +03:00
return writeSettings ( ) ;
}
2016-03-19 19:55:15 +03:00
MTP : : DcOptions dcOpts ;
2015-05-14 19:50:04 +03:00
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
dcOpts = Global : : DcOptions ( ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
_dcOpts = & dcOpts ;
2016-03-24 18:07:13 +03:00
LOG ( ( " App Info: reading encrypted settings... " ) ) ;
2015-03-02 15:34:16 +03:00
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 ) {
2016-03-19 19:55:15 +03:00
MTPDdcOption : : Flags flags = 0 ;
2016-03-24 11:57:11 +03:00
MTP : : ShiftedDcId idWithShift = MTP : : shiftDcId ( bdcs [ i ] . id , flags ) ;
2016-03-19 19:55:15 +03:00
dcOpts . insert ( idWithShift , MTP : : DcOption ( bdcs [ i ] . id , flags , bdcs [ i ] . ip , bdcs [ i ] . port ) ) ;
2015-03-02 15:34:16 +03:00
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 ) ) ;
}
2015-06-10 15:48:26 +03:00
const BuiltInDc * bdcsipv6 = builtInDcsIPv6 ( ) ;
for ( int i = 0 , l = builtInDcsCountIPv6 ( ) ; i < l ; + + i ) {
2016-03-19 19:55:15 +03:00
MTPDdcOption : : Flags flags = MTPDdcOption : : Flag : : f_ipv6 ;
2016-03-24 11:57:11 +03:00
MTP : : ShiftedDcId idWithShift = MTP : : shiftDcId ( bdcsipv6 [ i ] . id , flags ) ;
2016-03-19 19:55:15 +03:00
dcOpts . insert ( idWithShift , MTP : : DcOption ( bdcsipv6 [ i ] . id , flags , bdcsipv6 [ i ] . ip , bdcsipv6 [ i ] . port ) ) ;
2015-06-10 15:48:26 +03:00
DEBUG_LOG ( ( " MTP Info: adding built in DC %1 IPv6 connect option: %2:%3 " ) . arg ( bdcsipv6 [ i ] . id ) . arg ( bdcsipv6 [ i ] . ip ) . arg ( bdcsipv6 [ i ] . port ) ) ;
}
2015-03-02 15:34:16 +03:00
}
2015-05-14 19:50:04 +03:00
{
QWriteLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
Global : : SetDcOptions ( dcOpts ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
2015-08-13 18:11:07 +03:00
_oldSettingsVersion = settingsData . version ;
2015-03-02 15:34:16 +03:00
_settingsSalt = salt ;
}
void writeSettings ( ) {
if ( _basePath . isEmpty ( ) ) {
LOG ( ( " App Error: _basePath is empty in writeSettings() " ) ) ;
return ;
}
if ( ! QDir ( ) . exists ( _basePath ) ) QDir ( ) . mkpath ( _basePath ) ;
2015-03-19 12:18:19 +03:00
FileWriteDescriptor settings ( cTestMode ( ) ? qsl ( " settings_test " ) : qsl ( " settings " ) , SafePath ) ;
2015-03-02 15:34:16 +03:00
if ( _settingsSalt . isEmpty ( ) | | ! _settingsKey . created ( ) ) {
_settingsSalt . resize ( LocalEncryptSaltSize ) ;
memset_rand ( _settingsSalt . data ( ) , _settingsSalt . size ( ) ) ;
createLocalKey ( QByteArray ( ) , & _settingsSalt , & _settingsKey ) ;
}
settings . writeData ( _settingsSalt ) ;
2016-03-19 19:55:15 +03:00
MTP : : DcOptions dcOpts ;
2015-05-14 19:50:04 +03:00
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
dcOpts = Global : : DcOptions ( ) ;
2015-05-14 19:50:04 +03:00
}
2015-03-02 15:34:16 +03:00
if ( dcOpts . isEmpty ( ) ) {
const BuiltInDc * bdcs = builtInDcs ( ) ;
for ( int i = 0 , l = builtInDcsCount ( ) ; i < l ; + + i ) {
2016-03-19 19:55:15 +03:00
MTPDdcOption : : Flags flags = 0 ;
2016-03-24 11:57:11 +03:00
MTP : : ShiftedDcId idWithShift = MTP : : shiftDcId ( bdcs [ i ] . id , flags ) ;
2016-03-19 19:55:15 +03:00
dcOpts . insert ( idWithShift , MTP : : DcOption ( bdcs [ i ] . id , flags , bdcs [ i ] . ip , bdcs [ i ] . port ) ) ;
2015-03-02 15:34:16 +03:00
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 ) ) ;
}
2015-05-14 19:50:04 +03:00
2015-06-10 15:48:26 +03:00
const BuiltInDc * bdcsipv6 = builtInDcsIPv6 ( ) ;
for ( int i = 0 , l = builtInDcsCountIPv6 ( ) ; i < l ; + + i ) {
2016-03-19 19:55:15 +03:00
MTPDdcOption : : Flags flags = MTPDdcOption : : Flag : : f_ipv6 ;
2016-03-24 11:57:11 +03:00
MTP : : ShiftedDcId idWithShift = MTP : : shiftDcId ( bdcsipv6 [ i ] . id , flags ) ;
2016-03-19 19:55:15 +03:00
dcOpts . insert ( idWithShift , MTP : : DcOption ( bdcsipv6 [ i ] . id , flags , bdcsipv6 [ i ] . ip , bdcsipv6 [ i ] . port ) ) ;
2015-06-10 15:48:26 +03:00
DEBUG_LOG ( ( " MTP Info: adding built in DC %1 IPv6 connect option: %2:%3 " ) . arg ( bdcsipv6 [ i ] . id ) . arg ( bdcsipv6 [ i ] . ip ) . arg ( bdcsipv6 [ i ] . port ) ) ;
}
2015-05-14 19:50:04 +03:00
QWriteLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
2016-03-19 19:55:15 +03:00
Global : : SetDcOptions ( dcOpts ) ;
2015-03-02 15:34:16 +03:00
}
2015-12-28 00:37:48 +03:00
quint32 size = 12 * ( sizeof ( quint32 ) + sizeof ( qint32 ) ) ;
2016-03-19 19:55:15 +03:00
for ( auto i = dcOpts . cbegin ( ) , e = dcOpts . cend ( ) ; i ! = e ; + + i ) {
2015-03-02 15:34:16 +03:00
size + = sizeof ( quint32 ) + sizeof ( quint32 ) + sizeof ( quint32 ) ;
2016-04-09 09:57:55 +04:00
size + = sizeof ( quint32 ) + Serialize : : stringSize ( QString : : fromUtf8 ( i - > ip . data ( ) , i - > ip . size ( ) ) ) ;
2015-03-02 15:34:16 +03:00
}
2016-04-09 09:57:55 +04:00
size + = sizeof ( quint32 ) + Serialize : : stringSize ( cLangFile ( ) ) ;
2015-03-02 15:34:16 +03:00
size + = sizeof ( quint32 ) + sizeof ( qint32 ) ;
if ( cConnectionType ( ) = = dbictHttpProxy | | cConnectionType ( ) = = dbictTcpProxy ) {
const ConnectionProxy & proxy ( cConnectionProxy ( ) ) ;
2016-04-09 09:57:55 +04:00
size + = Serialize : : stringSize ( proxy . host ) + sizeof ( qint32 ) + Serialize : : stringSize ( proxy . user ) + Serialize : : stringSize ( proxy . password ) ;
2015-03-02 15:34:16 +03:00
}
size + = sizeof ( quint32 ) + sizeof ( qint32 ) * 6 ;
EncryptedDescriptor data ( size ) ;
2016-02-18 19:36:33 +03:00
data . stream < < quint32 ( dbiChatSizeMax ) < < qint32 ( Global : : ChatSizeMax ( ) ) ;
data . stream < < quint32 ( dbiMegagroupSizeMax ) < < qint32 ( Global : : MegagroupSizeMax ( ) ) ;
data . stream < < quint32 ( dbiSavedGifsLimit ) < < qint32 ( Global : : SavedGifsLimit ( ) ) ;
2015-03-02 15:34:16 +03:00
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 ( ) ) ;
2016-03-19 19:55:15 +03:00
for ( auto i = dcOpts . cbegin ( ) , e = dcOpts . cend ( ) ; i ! = e ; + + i ) {
2015-06-10 15:48:26 +03:00
data . stream < < quint32 ( dbiDcOption ) < < quint32 ( i . key ( ) ) ;
2016-03-19 19:55:15 +03:00
data . stream < < qint32 ( i - > flags ) < < QString : : fromUtf8 ( i - > ip . data ( ) , i - > ip . size ( ) ) ;
2015-03-02 15:34:16 +03:00
data . stream < < quint32 ( i - > port ) ;
2015-12-31 23:27:21 +08:00
}
2015-03-02 15:34:16 +03:00
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 ;
}
2015-06-25 21:04:40 +03:00
data . stream < < quint32 ( dbiTryIPv6 ) < < qint32 ( cTryIPv6 ( ) ) ;
2015-03-02 15:34:16 +03:00
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 ( ) {
2015-09-29 21:44:31 +03:00
if ( _localLoader ) {
_localLoader - > stop ( ) ;
}
2015-11-24 19:19:18 +03:00
2015-03-02 15:34:16 +03:00
_passKeySalt . clear ( ) ; // reset passcode, local key
_draftsMap . clear ( ) ;
2016-02-25 13:32:31 +03:00
_draftCursorsMap . clear ( ) ;
2015-11-24 19:19:18 +03:00
_fileLocations . clear ( ) ;
_fileLocationPairs . clear ( ) ;
_fileLocationAliases . clear ( ) ;
2015-03-02 15:34:16 +03:00
_imagesMap . clear ( ) ;
_draftsNotReadMap . clear ( ) ;
2015-05-19 18:46:45 +03:00
_stickerImagesMap . clear ( ) ;
2015-03-02 15:34:16 +03:00
_audiosMap . clear ( ) ;
2015-11-24 19:19:18 +03:00
_storageImagesSize = _storageStickersSize = _storageAudiosSize = 0 ;
2015-12-31 23:27:21 +08:00
_webFilesMap . clear ( ) ;
_storageWebFilesSize = 0 ;
2015-12-28 00:37:48 +03:00
_locationsKey = _reportSpamStatusesKey = 0 ;
_recentStickersKeyOld = _stickersKey = _savedGifsKey = 0 ;
2016-01-01 22:48:32 +08:00
_backgroundKey = _userSettingsKey = _recentHashtagsAndBotsKey = _savedPeersKey = 0 ;
2015-11-24 19:19:18 +03:00
_oldMapVersion = _oldSettingsVersion = 0 ;
2015-03-02 15:34:16 +03:00
_mapChanged = true ;
_writeMap ( WriteMapNow ) ;
_writeMtpData ( ) ;
}
bool checkPasscode ( const QByteArray & passcode ) {
2016-03-24 15:57:10 +03:00
MTP : : AuthKey tmp ;
2015-03-02 15:34:16 +03:00
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 ( ) ) ;
}
2014-11-22 12:45:04 +03:00
ReadMapState readMap ( const QByteArray & pass ) {
ReadMapState result = _readMap ( pass ) ;
if ( result = = ReadMapFailed ) {
_mapChanged = true ;
_writeMap ( WriteMapNow ) ;
}
return result ;
}
2014-12-03 16:10:32 +03:00
int32 oldMapVersion ( ) {
return _oldMapVersion ;
}
2015-08-13 18:11:07 +03:00
int32 oldSettingsVersion ( ) {
return _oldSettingsVersion ;
}
2016-06-03 21:24:27 +03:00
void writeDrafts ( const PeerId & peer , const MessageDraft & localDraft , const MessageDraft & editDraft ) {
2014-11-22 12:45:04 +03:00
if ( ! _working ( ) ) return ;
2016-06-03 21:24:27 +03:00
if ( localDraft . msgId < = 0 & & localDraft . textWithTags . text . isEmpty ( ) & & editDraft . msgId < = 0 ) {
2016-05-05 19:04:17 +03:00
auto i = _draftsMap . find ( peer ) ;
2014-11-22 12:45:04 +03:00
if ( i ! = _draftsMap . cend ( ) ) {
clearKey ( i . value ( ) ) ;
_draftsMap . erase ( i ) ;
_mapChanged = true ;
_writeMap ( ) ;
}
_draftsNotReadMap . remove ( peer ) ;
} else {
2016-05-05 19:04:17 +03:00
auto i = _draftsMap . constFind ( peer ) ;
2014-11-22 12:45:04 +03:00
if ( i = = _draftsMap . cend ( ) ) {
i = _draftsMap . insert ( peer , genKey ( ) ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
2016-02-25 13:32:31 +03:00
2016-06-03 21:24:27 +03:00
auto msgTags = FlatTextarea : : serializeTagsList ( localDraft . textWithTags . tags ) ;
2016-05-05 19:04:17 +03:00
auto editTags = FlatTextarea : : serializeTagsList ( editDraft . textWithTags . tags ) ;
int size = sizeof ( quint64 ) ;
2016-06-03 21:24:27 +03:00
size + = Serialize : : stringSize ( localDraft . textWithTags . text ) + Serialize : : bytearraySize ( msgTags ) + 2 * sizeof ( qint32 ) ;
2016-05-05 19:04:17 +03:00
size + = Serialize : : stringSize ( editDraft . textWithTags . text ) + Serialize : : bytearraySize ( editTags ) + 2 * sizeof ( qint32 ) ;
EncryptedDescriptor data ( size ) ;
2016-02-25 13:32:31 +03:00
data . stream < < quint64 ( peer ) ;
2016-06-03 21:24:27 +03:00
data . stream < < localDraft . textWithTags . text < < msgTags ;
data . stream < < qint32 ( localDraft . msgId ) < < qint32 ( localDraft . previewCancelled ? 1 : 0 ) ;
2016-05-05 19:04:17 +03:00
data . stream < < editDraft . textWithTags . text < < editTags ;
data . stream < < qint32 ( editDraft . msgId ) < < qint32 ( editDraft . previewCancelled ? 1 : 0 ) ;
2016-02-25 13:32:31 +03:00
2014-11-22 12:45:04 +03:00
FileWriteDescriptor file ( i . value ( ) ) ;
file . writeEncrypted ( data ) ;
_draftsNotReadMap . remove ( peer ) ;
}
}
2016-02-25 13:32:31 +03:00
void clearDraftCursors ( const PeerId & peer ) {
DraftsMap : : iterator i = _draftCursorsMap . find ( peer ) ;
if ( i ! = _draftCursorsMap . cend ( ) ) {
clearKey ( i . value ( ) ) ;
_draftCursorsMap . erase ( i ) ;
_mapChanged = true ;
_writeMap ( ) ;
}
}
2016-06-03 21:24:27 +03:00
void _readDraftCursors ( const PeerId & peer , MessageCursor & localCursor , MessageCursor & editCursor ) {
2016-02-25 13:32:31 +03:00
DraftsMap : : iterator j = _draftCursorsMap . find ( peer ) ;
if ( j = = _draftCursorsMap . cend ( ) ) {
return ;
}
FileReadDescriptor draft ;
if ( ! readEncryptedFile ( draft , j . value ( ) ) ) {
clearDraftCursors ( peer ) ;
return ;
}
quint64 draftPeer ;
2016-06-03 21:24:27 +03:00
qint32 localPosition = 0 , localAnchor = 0 , localScroll = QFIXED_MAX ;
2016-02-25 13:32:31 +03:00
qint32 editPosition = 0 , editAnchor = 0 , editScroll = QFIXED_MAX ;
2016-06-03 21:24:27 +03:00
draft . stream > > draftPeer > > localPosition > > localAnchor > > localScroll ;
2016-02-25 13:32:31 +03:00
if ( ! draft . stream . atEnd ( ) ) {
draft . stream > > editPosition > > editAnchor > > editScroll ;
}
if ( draftPeer ! = peer ) {
clearDraftCursors ( peer ) ;
return ;
}
2016-06-03 21:24:27 +03:00
localCursor = MessageCursor ( localPosition , localAnchor , localScroll ) ;
2016-02-25 13:32:31 +03:00
editCursor = MessageCursor ( editPosition , editAnchor , editScroll ) ;
}
void readDraftsWithCursors ( History * h ) {
PeerId peer = h - > peer - > id ;
if ( ! _draftsNotReadMap . remove ( peer ) ) {
clearDraftCursors ( peer ) ;
return ;
}
2014-11-22 12:45:04 +03:00
DraftsMap : : iterator j = _draftsMap . find ( peer ) ;
if ( j = = _draftsMap . cend ( ) ) {
2016-02-25 13:32:31 +03:00
clearDraftCursors ( peer ) ;
return ;
2014-11-22 12:45:04 +03:00
}
FileReadDescriptor draft ;
2015-03-02 15:34:16 +03:00
if ( ! readEncryptedFile ( draft , j . value ( ) ) ) {
2014-11-22 12:45:04 +03:00
clearKey ( j . value ( ) ) ;
_draftsMap . erase ( j ) ;
2016-02-25 13:32:31 +03:00
clearDraftCursors ( peer ) ;
return ;
2014-11-22 12:45:04 +03:00
}
2016-02-25 13:32:31 +03:00
quint64 draftPeer = 0 ;
2016-05-05 19:04:17 +03:00
TextWithTags msgData , editData ;
QByteArray msgTagsSerialized , editTagsSerialized ;
2016-02-25 13:32:31 +03:00
qint32 msgReplyTo = 0 , msgPreviewCancelled = 0 , editMsgId = 0 , editPreviewCancelled = 0 ;
2016-05-05 19:04:17 +03:00
draft . stream > > draftPeer > > msgData . text ;
if ( draft . version > = 9048 ) {
draft . stream > > msgTagsSerialized ;
}
2016-02-25 13:32:31 +03:00
if ( draft . version > = 7021 ) {
draft . stream > > msgReplyTo ;
if ( draft . version > = 8001 ) {
draft . stream > > msgPreviewCancelled ;
if ( ! draft . stream . atEnd ( ) ) {
2016-05-05 19:04:17 +03:00
draft . stream > > editData . text ;
if ( draft . version > = 9048 ) {
draft . stream > > editTagsSerialized ;
}
draft . stream > > editMsgId > > editPreviewCancelled ;
2016-02-25 13:32:31 +03:00
}
}
}
if ( draftPeer ! = peer ) {
clearKey ( j . value ( ) ) ;
_draftsMap . erase ( j ) ;
clearDraftCursors ( peer ) ;
return ;
}
2016-05-05 19:04:17 +03:00
msgData . tags = FlatTextarea : : deserializeTagsList ( msgTagsSerialized , msgData . text . size ( ) ) ;
editData . tags = FlatTextarea : : deserializeTagsList ( editTagsSerialized , editData . text . size ( ) ) ;
2016-02-25 13:32:31 +03:00
MessageCursor msgCursor , editCursor ;
_readDraftCursors ( peer , msgCursor , editCursor ) ;
2016-06-03 21:24:27 +03:00
if ( ! h - > localDraft ( ) ) {
if ( msgData . text . isEmpty ( ) & & ! msgReplyTo ) {
h - > clearLocalDraft ( ) ;
} else {
2016-06-09 17:31:10 +03:00
h - > setLocalDraft ( std_ : : make_unique < Data : : Draft > ( msgData , msgReplyTo , msgCursor , msgPreviewCancelled ) ) ;
2016-06-03 21:24:27 +03:00
}
2016-02-25 13:32:31 +03:00
}
if ( ! editMsgId ) {
2016-04-08 18:16:52 +04:00
h - > clearEditDraft ( ) ;
2016-02-25 13:32:31 +03:00
} else {
2016-06-09 17:31:10 +03:00
h - > setEditDraft ( std_ : : make_unique < Data : : Draft > ( editData , editMsgId , editCursor , editPreviewCancelled ) ) ;
2016-02-25 13:32:31 +03:00
}
2014-11-22 12:45:04 +03:00
}
2016-02-25 13:32:31 +03:00
void writeDraftCursors ( const PeerId & peer , const MessageCursor & msgCursor , const MessageCursor & editCursor ) {
2014-11-22 12:45:04 +03:00
if ( ! _working ( ) ) return ;
2016-02-25 13:32:31 +03:00
if ( msgCursor = = MessageCursor ( ) & & editCursor = = MessageCursor ( ) ) {
clearDraftCursors ( peer ) ;
2014-11-22 12:45:04 +03:00
} else {
2016-02-25 13:32:31 +03:00
DraftsMap : : const_iterator i = _draftCursorsMap . constFind ( peer ) ;
if ( i = = _draftCursorsMap . cend ( ) ) {
i = _draftCursorsMap . insert ( peer , genKey ( ) ) ;
2014-11-22 12:45:04 +03:00
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
2016-02-25 13:32:31 +03:00
2014-11-22 12:45:04 +03:00
EncryptedDescriptor data ( sizeof ( quint64 ) + sizeof ( qint32 ) * 3 ) ;
2016-02-25 13:32:31 +03:00
data . stream < < quint64 ( peer ) < < qint32 ( msgCursor . position ) < < qint32 ( msgCursor . anchor ) < < qint32 ( msgCursor . scroll ) ;
data . stream < < qint32 ( editCursor . position ) < < qint32 ( editCursor . anchor ) < < qint32 ( editCursor . scroll ) ;
2014-11-22 12:45:04 +03:00
FileWriteDescriptor file ( i . value ( ) ) ;
file . writeEncrypted ( data ) ;
}
}
2016-02-25 13:32:31 +03:00
bool hasDraftCursors ( const PeerId & peer ) {
2016-05-31 12:46:31 +03:00
return _draftCursorsMap . contains ( peer ) ;
}
bool hasDraft ( const PeerId & peer ) {
return _draftsMap . contains ( peer ) ;
2014-11-22 12:45:04 +03:00
}
2015-07-01 00:07:05 +03:00
void writeFileLocation ( MediaKey location , const FileLocation & local ) {
2015-11-26 20:34:52 +03:00
if ( local . fname . isEmpty ( ) ) return ;
2014-12-05 16:44:27 +03:00
2015-07-01 00:07:05 +03:00
FileLocationAliases : : const_iterator aliasIt = _fileLocationAliases . constFind ( location ) ;
if ( aliasIt ! = _fileLocationAliases . cend ( ) ) {
location = aliasIt . value ( ) ;
}
2015-11-26 20:34:52 +03:00
FileLocationPairs : : iterator i = _fileLocationPairs . find ( local . fname ) ;
2014-12-05 16:44:27 +03:00
if ( i ! = _fileLocationPairs . cend ( ) ) {
if ( i . value ( ) . second = = local ) {
2015-07-01 00:07:05 +03:00
if ( i . value ( ) . first ! = location ) {
_fileLocationAliases . insert ( location , i . value ( ) . first ) ;
_writeLocations ( WriteMapFast ) ;
}
2014-12-05 16:44:27 +03:00
return ;
}
if ( i . value ( ) . first ! = location ) {
for ( FileLocations : : iterator j = _fileLocations . find ( i . value ( ) . first ) , e = _fileLocations . end ( ) ; ( j ! = e ) & & ( j . key ( ) = = i . value ( ) . first ) ; ) {
if ( j . value ( ) = = i . value ( ) . second ) {
_fileLocations . erase ( j ) ;
break ;
}
}
_fileLocationPairs . erase ( i ) ;
}
}
_fileLocations . insert ( location , local ) ;
2015-11-26 20:34:52 +03:00
_fileLocationPairs . insert ( local . fname , FileLocationPair ( location , local ) ) ;
2014-12-05 16:44:27 +03:00
_writeLocations ( WriteMapFast ) ;
}
2015-07-01 00:07:05 +03:00
FileLocation readFileLocation ( MediaKey location , bool check ) {
FileLocationAliases : : const_iterator aliasIt = _fileLocationAliases . constFind ( location ) ;
if ( aliasIt ! = _fileLocationAliases . cend ( ) ) {
location = aliasIt . value ( ) ;
}
2014-12-05 16:44:27 +03:00
FileLocations : : iterator i = _fileLocations . find ( location ) ;
for ( FileLocations : : iterator i = _fileLocations . find ( location ) ; ( i ! = _fileLocations . end ( ) ) & & ( i . key ( ) = = location ) ; ) {
if ( check ) {
2015-11-26 20:34:52 +03:00
if ( ! i . value ( ) . check ( ) ) {
_fileLocationPairs . remove ( i . value ( ) . fname ) ;
2014-12-05 16:44:27 +03:00
i = _fileLocations . erase ( i ) ;
_writeLocations ( ) ;
continue ;
}
}
return i . value ( ) ;
}
return FileLocation ( ) ;
}
2014-11-22 12:45:04 +03:00
qint32 _storageImageSize ( qint32 rawlen ) {
// fulllen + storagekey + type + len + data
qint32 result = sizeof ( uint32 ) + sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + sizeof ( quint32 ) + rawlen ;
if ( result & 0x0F ) result + = 0x10 - ( result & 0x0F ) ;
result + = tdfMagicLen + sizeof ( qint32 ) + sizeof ( quint32 ) + 0x10 + 0x10 ; // magic + version + len of encrypted + part of sha1 + md5
return result ;
}
2015-01-02 17:55:24 +03:00
qint32 _storageStickerSize ( qint32 rawlen ) {
// fulllen + storagekey + len + data
qint32 result = sizeof ( uint32 ) + sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + rawlen ;
if ( result & 0x0F ) result + = 0x10 - ( result & 0x0F ) ;
result + = tdfMagicLen + sizeof ( qint32 ) + sizeof ( quint32 ) + 0x10 + 0x10 ; // magic + version + len of encrypted + part of sha1 + md5
return result ;
}
qint32 _storageAudioSize ( qint32 rawlen ) {
// fulllen + storagekey + len + data
qint32 result = sizeof ( uint32 ) + sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + rawlen ;
if ( result & 0x0F ) result + = 0x10 - ( result & 0x0F ) ;
result + = tdfMagicLen + sizeof ( qint32 ) + sizeof ( quint32 ) + 0x10 + 0x10 ; // magic + version + len of encrypted + part of sha1 + md5
return result ;
}
2014-11-22 12:45:04 +03:00
void writeImage ( const StorageKey & location , const ImagePtr & image ) {
if ( image - > isNull ( ) | | ! image - > loaded ( ) ) return ;
2015-01-02 17:55:24 +03:00
if ( _imagesMap . constFind ( location ) ! = _imagesMap . cend ( ) ) return ;
2014-11-22 12:45:04 +03:00
QByteArray fmt = image - > savedFormat ( ) ;
2015-05-19 18:46:45 +03:00
StorageFileType format = StorageFileUnknown ;
2014-11-22 12:45:04 +03:00
if ( fmt = = " JPG " ) {
2015-05-19 18:46:45 +03:00
format = StorageFileJpeg ;
2014-11-22 12:45:04 +03:00
} else if ( fmt = = " PNG " ) {
2015-05-19 18:46:45 +03:00
format = StorageFilePng ;
2014-11-22 12:45:04 +03:00
} else if ( fmt = = " GIF " ) {
2015-05-19 18:46:45 +03:00
format = StorageFileGif ;
2014-11-22 12:45:04 +03:00
}
if ( format ) {
image - > forget ( ) ;
writeImage ( location , StorageImageSaved ( format , image - > savedData ( ) ) , false ) ;
}
}
void writeImage ( const StorageKey & location , const StorageImageSaved & image , bool overwrite ) {
if ( ! _working ( ) ) return ;
qint32 size = _storageImageSize ( image . data . size ( ) ) ;
2015-01-02 17:55:24 +03:00
StorageMap : : const_iterator i = _imagesMap . constFind ( location ) ;
if ( i = = _imagesMap . cend ( ) ) {
2015-03-02 15:34:16 +03:00
i = _imagesMap . insert ( location , FileDesc ( genKey ( UserPath ) , size ) ) ;
2015-01-02 17:55:24 +03:00
_storageImagesSize + = size ;
2014-11-22 12:45:04 +03:00
_mapChanged = true ;
_writeMap ( ) ;
} else if ( ! overwrite ) {
return ;
}
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 ;
2015-03-02 15:34:16 +03:00
FileWriteDescriptor file ( i . value ( ) . first , UserPath ) ;
2014-11-22 12:45:04 +03:00
file . writeEncrypted ( data ) ;
if ( i . value ( ) . second ! = size ) {
2015-01-02 17:55:24 +03:00
_storageImagesSize + = size ;
_storageImagesSize - = i . value ( ) . second ;
_imagesMap [ location ] . second = size ;
2014-11-22 12:45:04 +03:00
}
}
2015-10-01 17:05:05 +03:00
class AbstractCachedLoadTask : public Task {
2015-09-29 21:44:31 +03:00
public :
2015-10-01 17:05:05 +03:00
AbstractCachedLoadTask ( const FileKey & key , const StorageKey & location , bool readImageFlag , mtpFileLoader * loader ) :
_key ( key ) , _location ( location ) , _readImageFlag ( readImageFlag ) , _loader ( loader ) , _result ( 0 ) {
2015-09-29 21:44:31 +03:00
}
void process ( ) {
FileReadDescriptor image ;
if ( ! readEncryptedFile ( image , _key , UserPath ) ) {
return ;
}
QByteArray imageData ;
quint64 locFirst , locSecond ;
quint32 imageType ;
2015-10-01 17:05:05 +03:00
readFromStream ( image . stream , locFirst , locSecond , imageType , imageData ) ;
2015-09-29 21:44:31 +03:00
2015-12-31 23:27:21 +08:00
// we're saving files now before we have actual location
//if (locFirst != _location.first || locSecond != _location.second) {
// return;
//}
2015-09-29 21:44:31 +03:00
2015-10-01 17:05:05 +03:00
_result = new Result ( StorageFileType ( imageType ) , imageData , _readImageFlag ) ;
2015-09-29 21:44:31 +03:00
}
void finish ( ) {
if ( _result ) {
_loader - > localLoaded ( _result - > image , _result - > format , _result - > pixmap ) ;
} else {
2015-10-01 17:05:05 +03:00
clearInMap ( ) ;
2015-09-29 21:44:31 +03:00
_loader - > localLoaded ( StorageImageSaved ( ) ) ;
}
}
2015-10-01 17:05:05 +03:00
virtual void readFromStream ( QDataStream & stream , quint64 & first , quint64 & second , quint32 & type , QByteArray & data ) = 0 ;
virtual void clearInMap ( ) = 0 ;
2015-12-23 19:48:44 +03:00
virtual ~ AbstractCachedLoadTask ( ) {
2015-12-29 00:20:04 +03:00
deleteAndMark ( _result ) ;
2015-12-23 19:48:44 +03:00
}
2015-09-29 21:44:31 +03:00
2015-10-01 17:05:05 +03:00
protected :
2015-09-29 21:44:31 +03:00
FileKey _key ;
StorageKey _location ;
2015-10-01 17:05:05 +03:00
bool _readImageFlag ;
2015-09-29 21:44:31 +03:00
struct Result {
2015-10-01 17:05:05 +03:00
Result ( StorageFileType type , const QByteArray & data , bool readImageFlag ) : image ( type , data ) {
if ( readImageFlag ) {
QByteArray guessFormat ;
switch ( type ) {
case StorageFileGif : guessFormat = " GIF " ; break ;
case StorageFileJpeg : guessFormat = " JPG " ; break ;
case StorageFilePng : guessFormat = " PNG " ; break ;
case StorageFileWebp : guessFormat = " WEBP " ; break ;
default : guessFormat = QByteArray ( ) ; break ;
}
pixmap = QPixmap : : fromImage ( App : : readImage ( data , & guessFormat , false ) , Qt : : ColorOnly ) ;
if ( ! pixmap . isNull ( ) ) {
format = guessFormat ;
}
2015-09-29 21:44:31 +03:00
}
}
StorageImageSaved image ;
QByteArray format ;
QPixmap pixmap ;
} ;
mtpFileLoader * _loader ;
Result * _result ;
} ;
2015-10-01 17:05:05 +03:00
class ImageLoadTask : public AbstractCachedLoadTask {
public :
ImageLoadTask ( const FileKey & key , const StorageKey & location , mtpFileLoader * loader ) :
AbstractCachedLoadTask ( key , location , true , loader ) {
}
void readFromStream ( QDataStream & stream , quint64 & first , quint64 & second , quint32 & type , QByteArray & data ) {
stream > > first > > second > > type > > data ;
}
void clearInMap ( ) {
StorageMap : : iterator j = _imagesMap . find ( _location ) ;
if ( j ! = _imagesMap . cend ( ) & & j - > first = = _key ) {
clearKey ( _key , UserPath ) ;
_storageImagesSize - = j - > second ;
_imagesMap . erase ( j ) ;
}
}
} ;
2015-09-29 21:44:31 +03:00
TaskId startImageLoad ( const StorageKey & location , mtpFileLoader * loader ) {
2015-12-23 22:23:14 +03:00
StorageMap : : const_iterator j = _imagesMap . constFind ( location ) ;
2015-09-29 21:44:31 +03:00
if ( j = = _imagesMap . cend ( ) | | ! _localLoader ) {
return 0 ;
2015-09-29 18:29:21 +03:00
}
2015-09-29 21:44:31 +03:00
return _localLoader - > addTask ( new ImageLoadTask ( j - > first , location , loader ) ) ;
2015-09-29 18:29:21 +03:00
}
2014-11-22 12:45:04 +03:00
int32 hasImages ( ) {
2015-01-02 17:55:24 +03:00
return _imagesMap . size ( ) ;
}
qint64 storageImagesSize ( ) {
return _storageImagesSize ;
}
2015-05-19 18:46:45 +03:00
void writeStickerImage ( const StorageKey & location , const QByteArray & sticker , bool overwrite ) {
2015-01-02 17:55:24 +03:00
if ( ! _working ( ) ) return ;
qint32 size = _storageStickerSize ( sticker . size ( ) ) ;
2015-05-19 18:46:45 +03:00
StorageMap : : const_iterator i = _stickerImagesMap . constFind ( location ) ;
if ( i = = _stickerImagesMap . cend ( ) ) {
i = _stickerImagesMap . insert ( location , FileDesc ( genKey ( UserPath ) , size ) ) ;
2015-01-02 17:55:24 +03:00
_storageStickersSize + = size ;
_mapChanged = true ;
_writeMap ( ) ;
} else if ( ! overwrite ) {
return ;
}
EncryptedDescriptor data ( sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + sizeof ( quint32 ) + sticker . size ( ) ) ;
data . stream < < quint64 ( location . first ) < < quint64 ( location . second ) < < sticker ;
2015-03-02 15:34:16 +03:00
FileWriteDescriptor file ( i . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
file . writeEncrypted ( data ) ;
if ( i . value ( ) . second ! = size ) {
_storageStickersSize + = size ;
_storageStickersSize - = i . value ( ) . second ;
2015-05-19 18:46:45 +03:00
_stickerImagesMap [ location ] . second = size ;
2015-01-02 17:55:24 +03:00
}
}
2015-10-01 17:05:05 +03:00
class StickerImageLoadTask : public AbstractCachedLoadTask {
public :
StickerImageLoadTask ( const FileKey & key , const StorageKey & location , mtpFileLoader * loader ) :
AbstractCachedLoadTask ( key , location , true , loader ) {
2015-09-29 18:29:21 +03:00
}
2015-10-01 17:05:05 +03:00
void readFromStream ( QDataStream & stream , quint64 & first , quint64 & second , quint32 & type , QByteArray & data ) {
stream > > first > > second > > data ;
type = StorageFilePartial ;
}
void clearInMap ( ) {
2016-04-09 09:57:55 +04:00
auto j = _stickerImagesMap . find ( _location ) ;
2015-10-01 17:05:05 +03:00
if ( j ! = _stickerImagesMap . cend ( ) & & j - > first = = _key ) {
clearKey ( j . value ( ) . first , UserPath ) ;
_storageStickersSize - = j . value ( ) . second ;
_stickerImagesMap . erase ( j ) ;
}
}
} ;
2015-09-29 18:29:21 +03:00
2015-10-01 17:05:05 +03:00
TaskId startStickerImageLoad ( const StorageKey & location , mtpFileLoader * loader ) {
2016-04-09 09:57:55 +04:00
auto j = _stickerImagesMap . constFind ( location ) ;
2015-12-06 18:50:02 +03:00
if ( j = = _stickerImagesMap . cend ( ) | | ! _localLoader ) {
2015-10-01 17:05:05 +03:00
return 0 ;
2015-01-02 17:55:24 +03:00
}
2015-10-01 17:05:05 +03:00
return _localLoader - > addTask ( new StickerImageLoadTask ( j - > first , location , loader ) ) ;
2015-01-02 17:55:24 +03:00
}
2015-12-24 22:26:28 +03:00
bool willStickerImageLoad ( const StorageKey & location ) {
return _stickerImagesMap . constFind ( location ) ! = _stickerImagesMap . cend ( ) ;
2015-12-23 22:23:14 +03:00
}
2016-04-10 18:53:01 +04:00
bool copyStickerImage ( const StorageKey & oldLocation , const StorageKey & newLocation ) {
2016-04-09 09:57:55 +04:00
auto i = _stickerImagesMap . constFind ( oldLocation ) ;
2016-04-10 18:53:01 +04:00
if ( i = = _stickerImagesMap . cend ( ) ) {
return false ;
2015-12-31 13:34:43 +08:00
}
2016-04-10 18:53:01 +04:00
_stickerImagesMap . insert ( newLocation , i . value ( ) ) ;
_mapChanged = true ;
_writeMap ( ) ;
return true ;
2015-12-31 13:34:43 +08:00
}
2015-01-02 17:55:24 +03:00
int32 hasStickers ( ) {
2015-05-19 18:46:45 +03:00
return _stickerImagesMap . size ( ) ;
2014-11-22 12:45:04 +03:00
}
2015-01-02 17:55:24 +03:00
qint64 storageStickersSize ( ) {
return _storageStickersSize ;
}
void writeAudio ( const StorageKey & location , const QByteArray & audio , bool overwrite ) {
if ( ! _working ( ) ) return ;
qint32 size = _storageAudioSize ( audio . size ( ) ) ;
StorageMap : : const_iterator i = _audiosMap . constFind ( location ) ;
if ( i = = _audiosMap . cend ( ) ) {
2015-03-02 15:34:16 +03:00
i = _audiosMap . insert ( location , FileDesc ( genKey ( UserPath ) , size ) ) ;
2015-01-02 17:55:24 +03:00
_storageAudiosSize + = size ;
_mapChanged = true ;
_writeMap ( ) ;
} else if ( ! overwrite ) {
return ;
}
EncryptedDescriptor data ( sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + sizeof ( quint32 ) + audio . size ( ) ) ;
data . stream < < quint64 ( location . first ) < < quint64 ( location . second ) < < audio ;
2015-03-02 15:34:16 +03:00
FileWriteDescriptor file ( i . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
file . writeEncrypted ( data ) ;
if ( i . value ( ) . second ! = size ) {
_storageAudiosSize + = size ;
_storageAudiosSize - = i . value ( ) . second ;
_audiosMap [ location ] . second = size ;
}
}
2015-10-01 17:05:05 +03:00
class AudioLoadTask : public AbstractCachedLoadTask {
public :
AudioLoadTask ( const FileKey & key , const StorageKey & location , mtpFileLoader * loader ) :
AbstractCachedLoadTask ( key , location , false , loader ) {
2015-09-29 18:29:21 +03:00
}
2015-10-01 17:05:05 +03:00
void readFromStream ( QDataStream & stream , quint64 & first , quint64 & second , quint32 & type , QByteArray & data ) {
stream > > first > > second > > data ;
type = StorageFilePartial ;
}
void clearInMap ( ) {
2016-04-09 09:57:55 +04:00
auto j = _audiosMap . find ( _location ) ;
2015-10-01 17:05:05 +03:00
if ( j ! = _audiosMap . cend ( ) & & j - > first = = _key ) {
clearKey ( j . value ( ) . first , UserPath ) ;
_storageAudiosSize - = j . value ( ) . second ;
_audiosMap . erase ( j ) ;
}
}
} ;
2015-09-29 18:29:21 +03:00
2015-10-01 17:05:05 +03:00
TaskId startAudioLoad ( const StorageKey & location , mtpFileLoader * loader ) {
2016-04-09 09:57:55 +04:00
auto j = _audiosMap . constFind ( location ) ;
2015-12-06 18:50:02 +03:00
if ( j = = _audiosMap . cend ( ) | | ! _localLoader ) {
2015-10-01 17:05:05 +03:00
return 0 ;
2015-01-02 17:55:24 +03:00
}
2015-10-01 17:05:05 +03:00
return _localLoader - > addTask ( new AudioLoadTask ( j - > first , location , loader ) ) ;
2015-01-02 17:55:24 +03:00
}
2016-04-10 18:53:01 +04:00
bool copyAudio ( const StorageKey & oldLocation , const StorageKey & newLocation ) {
2016-04-09 09:57:55 +04:00
auto i = _audiosMap . constFind ( oldLocation ) ;
2016-04-10 18:53:01 +04:00
if ( i = = _audiosMap . cend ( ) ) {
return false ;
2016-04-09 09:57:55 +04:00
}
2016-04-10 18:53:01 +04:00
_audiosMap . insert ( newLocation , i . value ( ) ) ;
_mapChanged = true ;
_writeMap ( ) ;
return true ;
2016-04-09 09:57:55 +04:00
}
2015-01-02 17:55:24 +03:00
int32 hasAudios ( ) {
return _audiosMap . size ( ) ;
}
qint64 storageAudiosSize ( ) {
return _storageAudiosSize ;
}
2015-12-31 23:27:21 +08:00
qint32 _storageWebFileSize ( const QString & url , qint32 rawlen ) {
// fulllen + url + len + data
2016-04-09 09:57:55 +04:00
qint32 result = sizeof ( uint32 ) + Serialize : : stringSize ( url ) + sizeof ( quint32 ) + rawlen ;
2015-12-31 23:27:21 +08:00
if ( result & 0x0F ) result + = 0x10 - ( result & 0x0F ) ;
result + = tdfMagicLen + sizeof ( qint32 ) + sizeof ( quint32 ) + 0x10 + 0x10 ; // magic + version + len of encrypted + part of sha1 + md5
return result ;
}
void writeWebFile ( const QString & url , const QByteArray & content , bool overwrite ) {
if ( ! _working ( ) ) return ;
qint32 size = _storageWebFileSize ( url , content . size ( ) ) ;
WebFilesMap : : const_iterator i = _webFilesMap . constFind ( url ) ;
if ( i = = _webFilesMap . cend ( ) ) {
i = _webFilesMap . insert ( url , FileDesc ( genKey ( UserPath ) , size ) ) ;
_storageWebFilesSize + = size ;
_writeLocations ( ) ;
} else if ( ! overwrite ) {
return ;
}
2016-04-09 09:57:55 +04:00
EncryptedDescriptor data ( Serialize : : stringSize ( url ) + sizeof ( quint32 ) + sizeof ( quint32 ) + content . size ( ) ) ;
2015-12-31 23:27:21 +08:00
data . stream < < url < < content ;
FileWriteDescriptor file ( i . value ( ) . first , UserPath ) ;
file . writeEncrypted ( data ) ;
if ( i . value ( ) . second ! = size ) {
_storageWebFilesSize + = size ;
_storageWebFilesSize - = i . value ( ) . second ;
_webFilesMap [ url ] . second = size ;
}
}
class WebFileLoadTask : public Task {
public :
WebFileLoadTask ( const FileKey & key , const QString & url , webFileLoader * loader )
: _key ( key )
, _url ( url )
, _loader ( loader )
, _result ( 0 ) {
}
void process ( ) {
FileReadDescriptor image ;
if ( ! readEncryptedFile ( image , _key , UserPath ) ) {
return ;
}
QByteArray imageData ;
QString url ;
image . stream > > url > > imageData ;
_result = new Result ( StorageFilePartial , imageData ) ;
}
void finish ( ) {
if ( _result ) {
_loader - > localLoaded ( _result - > image , _result - > format , _result - > pixmap ) ;
} else {
WebFilesMap : : iterator j = _webFilesMap . find ( _url ) ;
if ( j ! = _webFilesMap . cend ( ) & & j - > first = = _key ) {
clearKey ( j . value ( ) . first , UserPath ) ;
_storageWebFilesSize - = j . value ( ) . second ;
_webFilesMap . erase ( j ) ;
}
_loader - > localLoaded ( StorageImageSaved ( ) ) ;
}
}
virtual ~ WebFileLoadTask ( ) {
deleteAndMark ( _result ) ;
}
protected :
FileKey _key ;
QString _url ;
struct Result {
Result ( StorageFileType type , const QByteArray & data ) : image ( type , data ) {
QByteArray guessFormat ;
pixmap = QPixmap : : fromImage ( App : : readImage ( data , & guessFormat , false ) , Qt : : ColorOnly ) ;
if ( ! pixmap . isNull ( ) ) {
format = guessFormat ;
}
}
StorageImageSaved image ;
QByteArray format ;
QPixmap pixmap ;
} ;
webFileLoader * _loader ;
Result * _result ;
} ;
TaskId startWebFileLoad ( const QString & url , webFileLoader * loader ) {
WebFilesMap : : const_iterator j = _webFilesMap . constFind ( url ) ;
if ( j = = _webFilesMap . cend ( ) | | ! _localLoader ) {
return 0 ;
}
return _localLoader - > addTask ( new WebFileLoadTask ( j - > first , url , loader ) ) ;
}
int32 hasWebFiles ( ) {
return _webFilesMap . size ( ) ;
}
qint64 storageWebFilesSize ( ) {
return _storageWebFilesSize ;
}
2016-02-12 19:35:06 +03:00
class CountWaveformTask : public Task {
public :
CountWaveformTask ( DocumentData * doc )
: _doc ( doc )
, _loc ( doc - > location ( true ) )
, _data ( doc - > data ( ) )
, _wavemax ( 0 ) {
if ( _data . isEmpty ( ) & & ! _loc . accessEnable ( ) ) {
_doc = 0 ;
}
}
void process ( ) {
if ( ! _doc ) return ;
_waveform = audioCountWaveform ( _loc , _data ) ;
uchar wavemax = 0 ;
for ( int32 i = 0 , l = _waveform . size ( ) ; i < l ; + + i ) {
uchar waveat = _waveform . at ( i ) ;
if ( wavemax < waveat ) wavemax = waveat ;
}
_wavemax = wavemax ;
}
void finish ( ) {
if ( VoiceData * voice = _doc ? _doc - > voice ( ) : 0 ) {
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 ;
}
const DocumentItems & items ( App : : documentItems ( ) ) ;
DocumentItems : : const_iterator i = items . constFind ( _doc ) ;
if ( i ! = items . cend ( ) ) {
for ( HistoryItemsMap : : const_iterator j = i - > cbegin ( ) , e = i - > cend ( ) ; j ! = e ; + + j ) {
Ui : : repaintHistoryItem ( j . key ( ) ) ;
}
}
}
}
virtual ~ CountWaveformTask ( ) {
if ( _data . isEmpty ( ) & & _doc ) {
_loc . accessDisable ( ) ;
}
}
protected :
DocumentData * _doc ;
FileLocation _loc ;
QByteArray _data ;
VoiceWaveform _waveform ;
char _wavemax ;
} ;
void countVoiceWaveform ( DocumentData * document ) {
if ( VoiceData * voice = document - > voice ( ) ) {
if ( _localLoader ) {
voice - > waveform . resize ( 1 + sizeof ( TaskId ) ) ;
voice - > waveform [ 0 ] = - 1 ; // counting
TaskId taskId = _localLoader - > addTask ( new CountWaveformTask ( document ) ) ;
memcpy ( voice - > waveform . data ( ) + 1 , & taskId , sizeof ( taskId ) ) ;
}
}
}
2015-09-29 21:44:31 +03:00
void cancelTask ( TaskId id ) {
if ( _localLoader ) {
_localLoader - > cancelTask ( id ) ;
}
}
2015-05-21 13:44:26 +03:00
void _writeStickerSet ( QDataStream & stream , uint64 setId ) {
2016-03-19 19:55:15 +03:00
auto it = Global : : StickerSets ( ) . constFind ( setId ) ;
if ( it = = Global : : StickerSets ( ) . cend ( ) ) return ;
2015-05-21 13:44:26 +03:00
2016-03-19 19:55:15 +03:00
bool notLoaded = ( it - > flags & MTPDstickerSet_ClientFlag : : f_not_loaded ) ;
2015-06-28 15:37:10 +03:00
if ( notLoaded ) {
stream < < quint64 ( it - > id ) < < quint64 ( it - > access ) < < it - > title < < it - > shortName < < qint32 ( - it - > count ) < < qint32 ( it - > hash ) < < qint32 ( it - > flags ) ;
return ;
} else {
if ( it - > stickers . isEmpty ( ) ) return ;
}
stream < < quint64 ( it - > id ) < < quint64 ( it - > access ) < < it - > title < < it - > shortName < < qint32 ( it - > stickers . size ( ) ) < < qint32 ( it - > hash ) < < qint32 ( it - > flags ) ;
2015-05-21 13:44:26 +03:00
for ( StickerPack : : const_iterator j = it - > stickers . cbegin ( ) , e = it - > stickers . cend ( ) ; j ! = e ; + + j ) {
2016-04-09 09:57:55 +04:00
Serialize : : Document : : writeToStream ( stream , * j ) ;
2015-05-21 13:44:26 +03:00
}
2016-01-09 20:51:42 +08:00
if ( AppVersion > 9018 ) {
stream < < qint32 ( it - > emoji . size ( ) ) ;
for ( StickersByEmojiMap : : const_iterator j = it - > emoji . cbegin ( ) , e = it - > emoji . cend ( ) ; j ! = e ; + + j ) {
stream < < emojiString ( j . key ( ) ) < < qint32 ( j - > size ( ) ) ;
for ( int32 k = 0 , l = j - > size ( ) ; k < l ; + + k ) {
stream < < quint64 ( j - > at ( k ) - > id ) ;
}
}
}
2015-05-21 13:44:26 +03:00
}
2015-05-19 18:46:45 +03:00
void writeStickers ( ) {
2015-01-02 17:55:24 +03:00
if ( ! _working ( ) ) return ;
2015-05-19 18:46:45 +03:00
2016-06-27 19:25:21 +03:00
auto & sets = Global : : StickerSets ( ) ;
2015-05-19 18:46:45 +03:00
if ( sets . isEmpty ( ) ) {
if ( _stickersKey ) {
clearKey ( _stickersKey ) ;
_stickersKey = 0 ;
2015-01-02 17:55:24 +03:00
_mapChanged = true ;
}
_writeMap ( ) ;
} else {
2015-06-28 15:37:10 +03:00
int32 setsCount = 0 ;
2015-12-28 00:37:48 +03:00
QByteArray hashToWrite ;
2016-05-05 19:04:17 +03:00
quint32 size = sizeof ( quint32 ) + Serialize : : bytearraySize ( hashToWrite ) ;
2016-06-27 19:25:21 +03:00
for_const ( auto & set , sets ) {
bool notLoaded = ( set . flags & MTPDstickerSet_ClientFlag : : f_not_loaded ) ;
2015-06-28 15:37:10 +03:00
if ( notLoaded ) {
2016-06-27 19:25:21 +03:00
if ( ! ( set . flags & MTPDstickerSet : : Flag : : f_disabled )
| | ( set . flags & MTPDstickerSet : : Flag : : f_official )
| | ( set . flags & MTPDstickerSet_ClientFlag : : f_featured ) ) { // waiting to receive
2015-06-28 15:37:10 +03:00
return ;
}
} else {
2016-06-27 19:25:21 +03:00
if ( set . stickers . isEmpty ( ) ) continue ;
2015-06-28 15:37:10 +03:00
}
2015-05-19 18:46:45 +03:00
2015-06-28 15:37:10 +03:00
// id + access + title + shortName + stickersCount + hash + flags
2016-06-27 19:25:21 +03:00
size + = sizeof ( quint64 ) * 2 + Serialize : : stringSize ( set . title ) + Serialize : : stringSize ( set . shortName ) + sizeof ( quint32 ) + sizeof ( qint32 ) * 2 ;
for_const ( auto & sticker , set . stickers ) {
size + = Serialize : : Document : : sizeInStream ( sticker ) ;
2015-05-19 18:46:45 +03:00
}
2016-01-09 20:51:42 +08:00
if ( AppVersion > 9018 ) {
size + = sizeof ( qint32 ) ; // emojiCount
2016-06-27 19:25:21 +03:00
for ( auto j = set . emoji . cbegin ( ) , e = set . emoji . cend ( ) ; j ! = e ; + + j ) {
2016-04-09 09:57:55 +04:00
size + = Serialize : : stringSize ( emojiString ( j . key ( ) ) ) + sizeof ( qint32 ) + ( j - > size ( ) * sizeof ( quint64 ) ) ;
2016-01-09 20:51:42 +08:00
}
}
2015-06-28 15:37:10 +03:00
+ + setsCount ;
}
2016-06-27 19:25:21 +03:00
size + = sizeof ( qint32 ) + ( Global : : StickerSetsOrder ( ) . size ( ) * sizeof ( quint64 ) ) ;
size + = sizeof ( qint32 ) + ( Global : : FeaturedStickerSetsOrder ( ) . size ( ) * sizeof ( quint64 ) ) ;
2015-06-28 15:37:10 +03:00
if ( ! _stickersKey ) {
_stickersKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
2015-01-02 17:55:24 +03:00
}
EncryptedDescriptor data ( size ) ;
2015-12-02 20:17:53 +03:00
data . stream < < quint32 ( setsCount ) < < hashToWrite ;
2016-06-27 19:25:21 +03:00
for_const ( auto & set , sets ) {
_writeStickerSet ( data . stream , set . id ) ;
}
data . stream < < Global : : StickerSetsOrder ( ) ;
data . stream < < Global : : FeaturedStickerSetsOrder ( ) ;
2015-05-19 18:46:45 +03:00
FileWriteDescriptor file ( _stickersKey ) ;
2015-01-02 17:55:24 +03:00
file . writeEncrypted ( data ) ;
}
}
2015-05-19 18:46:45 +03:00
void importOldRecentStickers ( ) {
if ( ! _recentStickersKeyOld ) return ;
2015-01-02 17:55:24 +03:00
FileReadDescriptor stickers ;
2015-05-19 18:46:45 +03:00
if ( ! readEncryptedFile ( stickers , _recentStickersKeyOld ) ) {
clearKey ( _recentStickersKeyOld ) ;
_recentStickersKeyOld = 0 ;
2015-01-02 17:55:24 +03:00
_writeMap ( ) ;
return ;
}
2015-06-01 13:58:46 +03:00
2016-06-27 19:25:21 +03:00
auto & sets = Global : : RefStickerSets ( ) ;
2015-05-19 18:46:45 +03:00
sets . clear ( ) ;
2015-06-28 15:37:10 +03:00
2016-06-27 19:25:21 +03:00
auto & order = Global : : RefStickerSetsOrder ( ) ;
2015-06-28 15:37:10 +03:00
order . clear ( ) ;
2015-05-21 13:44:26 +03:00
2016-06-27 19:25:21 +03:00
auto & recent = cRefRecentStickers ( ) ;
2015-05-19 18:46:45 +03:00
recent . clear ( ) ;
2016-06-27 19:25:21 +03:00
auto & def = sets . insert ( Stickers : : DefaultSetId , Stickers : : Set ( Stickers : : DefaultSetId , 0 , lang ( lng_stickers_default_set ) , QString ( ) , 0 , 0 , MTPDstickerSet : : Flag : : f_official | MTPDstickerSet : : Flag : : f_installed ) ) . value ( ) ;
auto & custom = sets . insert ( Stickers : : CustomSetId , Stickers : : Set ( Stickers : : CustomSetId , 0 , lang ( lng_custom_stickers ) , QString ( ) , 0 , 0 , MTPDstickerSet : : Flag : : f_installed ) ) . value ( ) ;
2015-01-02 17:55:24 +03:00
QMap < uint64 , bool > read ;
while ( ! stickers . stream . atEnd ( ) ) {
quint64 id , access ;
2015-03-13 16:01:25 +03:00
QString name , mime , alt ;
2015-01-02 17:55:24 +03:00
qint32 date , dc , size , width , height , type ;
qint16 value ;
stickers . stream > > id > > value > > access > > date > > name > > mime > > dc > > size > > width > > height > > type ;
2015-03-19 12:18:19 +03:00
if ( stickers . version > = 7021 ) {
2015-03-13 16:01:25 +03:00
stickers . stream > > alt ;
}
2015-05-19 18:46:45 +03:00
if ( ! value | | read . contains ( id ) ) continue ;
2015-01-02 17:55:24 +03:00
read . insert ( id , true ) ;
QVector < MTPDocumentAttribute > attributes ;
if ( ! name . isEmpty ( ) ) attributes . push_back ( MTP_documentAttributeFilename ( MTP_string ( name ) ) ) ;
if ( type = = AnimatedDocument ) {
attributes . push_back ( MTP_documentAttributeAnimated ( ) ) ;
} else if ( type = = StickerDocument ) {
2015-05-11 15:44:27 +03:00
attributes . push_back ( MTP_documentAttributeSticker ( MTP_string ( alt ) , MTP_inputStickerSetEmpty ( ) ) ) ;
2015-01-02 17:55:24 +03:00
}
if ( width > 0 & & height > 0 ) {
attributes . push_back ( MTP_documentAttributeImageSize ( MTP_int ( width ) , MTP_int ( height ) ) ) ;
}
2016-07-08 16:56:53 +03:00
DocumentData * doc = App : : documentSet ( id , 0 , access , 0 , date , attributes , mime , ImagePtr ( ) , dc , size , StorageImageLocation ( ) ) ;
2015-07-01 00:07:05 +03:00
if ( ! doc - > sticker ( ) ) continue ;
2015-05-19 18:46:45 +03:00
if ( value > 0 ) {
def . stickers . push_back ( doc ) ;
2015-06-28 15:37:10 +03:00
+ + def . count ;
2015-05-19 18:46:45 +03:00
} else {
custom . stickers . push_back ( doc ) ;
2015-06-28 15:37:10 +03:00
+ + custom . count ;
2015-05-19 18:46:45 +03:00
}
if ( recent . size ( ) < StickerPanPerRow * StickerPanRowsPerPage & & qAbs ( value ) > 1 ) recent . push_back ( qMakePair ( doc , qAbs ( value ) ) ) ;
}
2015-06-28 15:37:10 +03:00
if ( def . stickers . isEmpty ( ) ) {
2016-03-19 19:55:15 +03:00
sets . remove ( Stickers : : DefaultSetId ) ;
2015-06-28 15:37:10 +03:00
} else {
2016-03-19 19:55:15 +03:00
order . push_front ( Stickers : : DefaultSetId ) ;
2015-06-28 15:37:10 +03:00
}
2016-03-19 19:55:15 +03:00
if ( custom . stickers . isEmpty ( ) ) sets . remove ( Stickers : : CustomSetId ) ;
2015-05-19 18:46:45 +03:00
writeStickers ( ) ;
writeUserSettings ( ) ;
clearKey ( _recentStickersKeyOld ) ;
_recentStickersKeyOld = 0 ;
_writeMap ( ) ;
}
void readStickers ( ) {
if ( ! _stickersKey ) {
return importOldRecentStickers ( ) ;
}
FileReadDescriptor stickers ;
if ( ! readEncryptedFile ( stickers , _stickersKey ) ) {
clearKey ( _stickersKey ) ;
_stickersKey = 0 ;
_writeMap ( ) ;
return ;
}
2016-06-27 19:25:21 +03:00
auto & sets = Global : : RefStickerSets ( ) ;
2015-05-19 18:46:45 +03:00
sets . clear ( ) ;
2016-06-27 19:25:21 +03:00
auto & order = Global : : RefStickerSetsOrder ( ) ;
2015-05-21 13:44:26 +03:00
order . clear ( ) ;
2016-06-27 19:25:21 +03:00
auto & featuredOrder = Global : : RefFeaturedStickerSetsOrder ( ) ;
featuredOrder . clear ( ) ;
2015-05-19 18:46:45 +03:00
quint32 cnt ;
QByteArray hash ;
2015-12-28 00:37:48 +03:00
stickers . stream > > cnt > > hash ; // ignore hash, it is counted
if ( stickers . version < 8019 ) { // bad data in old caches
2015-06-01 23:24:09 +03:00
cnt + = 2 ; // try to read at least something
}
2015-05-20 00:19:11 +03:00
for ( uint32 i = 0 ; i < cnt ; + + i ) {
2015-05-19 18:46:45 +03:00
quint64 setId = 0 , setAccess = 0 ;
QString setTitle , setShortName ;
2015-06-28 15:37:10 +03:00
qint32 scnt = 0 ;
2015-05-19 18:46:45 +03:00
stickers . stream > > setId > > setAccess > > setTitle > > setShortName > > scnt ;
2015-06-28 15:37:10 +03:00
qint32 setHash = 0 , setFlags = 0 ;
if ( stickers . version > 8033 ) {
stickers . stream > > setHash > > setFlags ;
2016-03-19 19:55:15 +03:00
if ( setFlags & qFlags ( MTPDstickerSet_ClientFlag : : f_not_loaded__old ) ) {
setFlags & = ~ qFlags ( MTPDstickerSet_ClientFlag : : f_not_loaded__old ) ;
setFlags | = qFlags ( MTPDstickerSet_ClientFlag : : f_not_loaded ) ;
}
2015-06-28 15:37:10 +03:00
}
2016-06-27 19:25:21 +03:00
if ( stickers . version < 9057 ) {
setFlags | = qFlags ( MTPDstickerSet : : Flag : : f_installed ) ;
}
2015-06-28 15:37:10 +03:00
2016-03-19 19:55:15 +03:00
if ( setId = = Stickers : : DefaultSetId ) {
2015-05-20 00:19:11 +03:00
setTitle = lang ( lng_stickers_default_set ) ;
2016-03-19 19:55:15 +03:00
setFlags | = qFlags ( MTPDstickerSet : : Flag : : f_official ) ;
2016-06-27 19:25:21 +03:00
if ( stickers . version < 9057 ) {
order . push_front ( setId ) ;
}
2016-03-19 19:55:15 +03:00
} else if ( setId = = Stickers : : CustomSetId ) {
2015-05-19 18:46:45 +03:00
setTitle = lang ( lng_custom_stickers ) ;
2015-06-01 23:24:09 +03:00
} else if ( setId ) {
2016-06-27 19:25:21 +03:00
if ( stickers . version < 9057 ) {
order . push_back ( setId ) ;
}
2015-06-01 23:24:09 +03:00
} else {
continue ;
2015-05-19 18:46:45 +03:00
}
2016-06-27 19:25:21 +03:00
auto & set = sets . insert ( setId , Stickers : : Set ( setId , setAccess , setTitle , setShortName , 0 , setHash , MTPDstickerSet : : Flags ( setFlags ) ) ) . value ( ) ;
2016-06-28 21:05:38 +03:00
// We will set this flags from order lists below.
set . flags & = ~ ( MTPDstickerSet : : Flag : : f_installed | MTPDstickerSet_ClientFlag : : f_featured ) ;
2015-06-28 15:37:10 +03:00
if ( scnt < 0 ) { // disabled not loaded set
set . count = - scnt ;
continue ;
}
2015-05-19 18:46:45 +03:00
set . stickers . reserve ( scnt ) ;
2016-04-09 09:57:55 +04:00
Serialize : : Document : : StickerSetInfo info ( setId , setAccess , setShortName ) ;
OrderedSet < DocumentId > read ;
2015-06-28 16:16:25 +03:00
for ( int32 j = 0 ; j < scnt ; + + j ) {
2016-07-08 16:56:53 +03:00
auto document = Serialize : : Document : : readStickerFromStream ( stickers . version , stickers . stream , info ) ;
2016-04-09 09:57:55 +04:00
if ( ! document | | ! document - > sticker ( ) ) continue ;
2015-05-19 18:46:45 +03:00
2016-04-09 09:57:55 +04:00
if ( read . contains ( document - > id ) ) continue ;
read . insert ( document - > id ) ;
2015-05-19 18:46:45 +03:00
2016-04-09 09:57:55 +04:00
set . stickers . push_back ( document ) ;
2015-06-28 15:37:10 +03:00
+ + set . count ;
2015-05-19 18:46:45 +03:00
}
2016-01-09 19:24:16 +08:00
2016-01-09 20:51:42 +08:00
if ( stickers . version > 9018 ) {
qint32 emojiCount ;
stickers . stream > > emojiCount ;
for ( int32 j = 0 ; j < emojiCount ; + + j ) {
QString emojiString ;
qint32 stickersCount ;
stickers . stream > > emojiString > > stickersCount ;
StickerPack pack ;
pack . reserve ( stickersCount ) ;
for ( int32 k = 0 ; k < stickersCount ; + + k ) {
quint64 id ;
stickers . stream > > id ;
DocumentData * doc = App : : document ( id ) ;
if ( ! doc | | ! doc - > sticker ( ) ) continue ;
pack . push_back ( doc ) ;
}
if ( EmojiPtr e = emojiGetNoColor ( emojiFromText ( emojiString ) ) ) {
set . emoji . insert ( e , pack ) ;
}
}
2016-01-09 19:24:16 +08:00
}
2015-01-02 17:55:24 +03:00
}
2016-06-27 19:25:21 +03:00
// Read orders of installed and featured stickers.
if ( stickers . version > = 9057 ) {
stickers . stream > > order ;
stickers . stream > > featuredOrder ;
2016-06-28 21:05:38 +03:00
// Set flags and count unread featured sets.
for_const ( auto setId , order ) {
auto it = sets . find ( setId ) ;
if ( it ! = sets . cend ( ) ) {
it - > flags | = MTPDstickerSet : : Flag : : f_installed ;
2016-06-27 19:25:21 +03:00
}
}
2016-06-28 21:05:38 +03:00
int unreadCount = 0 ;
for_const ( auto setId , featuredOrder ) {
auto it = sets . find ( setId ) ;
if ( it ! = sets . cend ( ) ) {
it - > flags | = MTPDstickerSet_ClientFlag : : f_featured ;
if ( it - > flags & MTPDstickerSet_ClientFlag : : f_unread ) {
+ + unreadCount ;
}
}
}
Global : : SetFeaturedStickerSetsUnreadCount ( unreadCount ) ;
2016-06-27 19:25:21 +03:00
}
2015-12-28 00:37:48 +03:00
}
int32 countStickersHash ( bool checkOfficial ) {
uint32 acc = 0 ;
bool foundOfficial = false , foundBad = false ; ;
2016-06-28 21:05:38 +03:00
auto & sets = Global : : StickerSets ( ) ;
auto & order = Global : : StickerSetsOrder ( ) ;
2016-03-19 19:55:15 +03:00
for ( auto i = order . cbegin ( ) , e = order . cend ( ) ; i ! = e ; + + i ) {
auto j = sets . constFind ( * i ) ;
2015-12-28 00:37:48 +03:00
if ( j ! = sets . cend ( ) ) {
if ( j - > id = = 0 ) {
foundBad = true ;
2016-03-19 19:55:15 +03:00
} else if ( j - > flags & MTPDstickerSet : : Flag : : f_official ) {
2015-12-28 00:37:48 +03:00
foundOfficial = true ;
}
2016-03-19 19:55:15 +03:00
if ( ! ( j - > flags & MTPDstickerSet : : Flag : : f_disabled ) ) {
2015-12-28 00:37:48 +03:00
acc = ( acc * 20261 ) + j - > hash ;
}
}
}
return ( ! checkOfficial | | ( ! foundBad & & foundOfficial ) ) ? int32 ( acc & 0x7FFFFFFF ) : 0 ;
}
2016-06-27 19:25:21 +03:00
int32 countFeaturedStickersHash ( ) {
uint32 acc = 0 ;
2016-06-28 21:05:38 +03:00
auto & sets = Global : : StickerSets ( ) ;
auto & featured = Global : : FeaturedStickerSetsOrder ( ) ;
2016-06-27 19:25:21 +03:00
for_const ( auto setId , featured ) {
acc = ( acc * 20261 ) + uint32 ( setId > > 32 ) ;
acc = ( acc * 20261 ) + uint32 ( setId & 0xFFFFFFFF ) ;
2016-06-28 21:05:38 +03:00
auto it = sets . constFind ( setId ) ;
if ( it ! = sets . cend ( ) & & ( it - > flags & MTPDstickerSet_ClientFlag : : f_unread ) ) {
2016-06-27 19:25:21 +03:00
acc = ( acc * 20261 ) + 1U ;
}
}
return int32 ( acc & 0x7FFFFFFF ) ;
}
2015-12-28 00:37:48 +03:00
int32 countSavedGifsHash ( ) {
uint32 acc = 0 ;
const SavedGifs & saved ( cSavedGifs ( ) ) ;
for ( SavedGifs : : const_iterator i = saved . cbegin ( ) , e = saved . cend ( ) ; i ! = e ; + + i ) {
uint64 docId = ( * i ) - > id ;
2015-01-02 17:55:24 +03:00
2015-12-28 00:37:48 +03:00
acc = ( acc * 20261 ) + uint32 ( docId > > 32 ) ;
acc = ( acc * 20261 ) + uint32 ( docId & 0xFFFFFFFF ) ;
}
return int32 ( acc & 0x7FFFFFFF ) ;
}
void writeSavedGifs ( ) {
if ( ! _working ( ) ) return ;
const SavedGifs & saved ( cSavedGifs ( ) ) ;
if ( saved . isEmpty ( ) ) {
if ( _savedGifsKey ) {
clearKey ( _savedGifsKey ) ;
_savedGifsKey = 0 ;
_mapChanged = true ;
}
_writeMap ( ) ;
2015-12-02 20:17:53 +03:00
} else {
2015-12-28 00:37:48 +03:00
quint32 size = sizeof ( quint32 ) ; // count
2016-04-09 09:57:55 +04:00
for_const ( auto gif , saved ) {
size + = Serialize : : Document : : sizeInStream ( gif ) ;
2015-12-28 00:37:48 +03:00
}
if ( ! _savedGifsKey ) {
_savedGifsKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( saved . size ( ) ) ;
2016-04-09 09:57:55 +04:00
for_const ( auto gif , saved ) {
Serialize : : Document : : writeToStream ( data . stream , gif ) ;
2015-12-28 00:37:48 +03:00
}
FileWriteDescriptor file ( _savedGifsKey ) ;
file . writeEncrypted ( data ) ;
}
}
void readSavedGifs ( ) {
if ( ! _savedGifsKey ) return ;
FileReadDescriptor gifs ;
if ( ! readEncryptedFile ( gifs , _savedGifsKey ) ) {
clearKey ( _savedGifsKey ) ;
_savedGifsKey = 0 ;
_writeMap ( ) ;
return ;
}
SavedGifs & saved ( cRefSavedGifs ( ) ) ;
saved . clear ( ) ;
quint32 cnt ;
gifs . stream > > cnt ;
saved . reserve ( cnt ) ;
2016-04-09 09:57:55 +04:00
OrderedSet < DocumentId > read ;
2015-12-28 00:37:48 +03:00
for ( uint32 i = 0 ; i < cnt ; + + i ) {
2016-07-08 16:56:53 +03:00
DocumentData * document = Serialize : : Document : : readFromStream ( gifs . version , gifs . stream ) ;
2016-04-09 09:57:55 +04:00
if ( ! document | | ! document - > isAnimation ( ) ) continue ;
2015-12-28 00:37:48 +03:00
2016-04-09 09:57:55 +04:00
if ( read . contains ( document - > id ) ) continue ;
read . insert ( document - > id ) ;
2015-12-31 23:27:21 +08:00
2016-04-09 09:57:55 +04:00
saved . push_back ( document ) ;
2015-12-02 20:17:53 +03:00
}
2014-11-22 12:45:04 +03:00
}
2015-02-03 18:02:46 +03:00
void writeBackground ( int32 id , const QImage & img ) {
if ( ! _working ( ) ) return ;
QByteArray png ;
2015-04-23 18:50:11 +03:00
if ( ! img . isNull ( ) ) {
2015-02-03 18:02:46 +03:00
QBuffer buf ( & png ) ;
if ( ! img . save ( & buf , " BMP " ) ) return ;
}
if ( ! _backgroundKey ) {
_backgroundKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
2015-04-23 18:50:11 +03:00
quint32 size = sizeof ( qint32 ) + sizeof ( quint32 ) + ( png . isEmpty ( ) ? 0 : ( sizeof ( quint32 ) + png . size ( ) ) ) ;
2015-02-03 18:02:46 +03:00
EncryptedDescriptor data ( size ) ;
2015-04-23 18:50:11 +03:00
data . stream < < qint32 ( id ) ;
if ( ! png . isEmpty ( ) ) data . stream < < png ;
2015-02-03 18:02:46 +03:00
FileWriteDescriptor file ( _backgroundKey ) ;
file . writeEncrypted ( data ) ;
}
bool readBackground ( ) {
if ( _backgroundWasRead ) return false ;
_backgroundWasRead = true ;
FileReadDescriptor bg ;
2015-03-02 15:34:16 +03:00
if ( ! readEncryptedFile ( bg , _backgroundKey ) ) {
2015-02-03 18:02:46 +03:00
clearKey ( _backgroundKey ) ;
_backgroundKey = 0 ;
_writeMap ( ) ;
return false ;
}
QByteArray pngData ;
qint32 id ;
2015-04-23 18:50:11 +03:00
bg . stream > > id ;
if ( ! id | | id = = DefaultChatBackground ) {
if ( bg . version < 8005 ) {
if ( ! id ) cSetTileBackground ( ! DefaultChatBackground ) ;
App : : initBackground ( DefaultChatBackground , QImage ( ) , true ) ;
} else {
App : : initBackground ( id , QImage ( ) , true ) ;
}
return true ;
}
bg . stream > > pngData ;
2015-02-03 18:02:46 +03:00
QImage img ;
QBuffer buf ( & pngData ) ;
QImageReader reader ( & buf ) ;
2015-12-23 14:13:08 +03:00
# if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
reader . setAutoTransform ( true ) ;
# endif
2015-02-03 18:02:46 +03:00
if ( reader . read ( & img ) ) {
2015-04-23 18:50:11 +03:00
App : : initBackground ( id , img , true ) ;
2015-02-03 18:02:46 +03:00
return true ;
}
return false ;
}
2015-03-24 13:00:27 +03:00
2015-08-07 15:11:50 +03:00
uint32 _peerSize ( PeerData * peer ) {
2016-04-09 09:57:55 +04:00
uint32 result = sizeof ( quint64 ) + sizeof ( quint64 ) + Serialize : : storageImageLocationSize ( ) ;
2015-09-03 13:48:40 +03:00
if ( peer - > isUser ( ) ) {
UserData * user = peer - > asUser ( ) ;
2015-10-29 11:52:39 -04:00
// first + last + phone + username + access
2016-05-31 22:27:11 +03:00
result + = Serialize : : stringSize ( user - > firstName ) + Serialize : : stringSize ( user - > lastName ) + Serialize : : stringSize ( user - > phone ( ) ) + Serialize : : stringSize ( user - > username ) + sizeof ( quint64 ) ;
2015-10-29 11:52:39 -04:00
// flags
2015-11-22 16:24:17 +03:00
if ( AppVersion > = 9012 ) {
2015-10-29 11:52:39 -04:00
result + = sizeof ( qint32 ) ;
}
// onlineTill + contact + botInfoVersion
result + = sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) ;
2015-09-03 13:48:40 +03:00
} else if ( peer - > isChat ( ) ) {
2015-08-07 15:11:50 +03:00
ChatData * chat = peer - > asChat ( ) ;
2016-06-01 16:07:03 +03:00
// name + count + date + version + admin + forbidden + left + inviteLink
result + = Serialize : : stringSize ( chat - > name ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + Serialize : : stringSize ( chat - > inviteLink ( ) ) ;
2015-09-03 13:48:40 +03:00
} else if ( peer - > isChannel ( ) ) {
ChannelData * channel = peer - > asChannel ( ) ;
2015-08-07 15:11:50 +03:00
2016-06-01 16:07:03 +03:00
// name + access + date + version + forbidden + flags + inviteLink
result + = Serialize : : stringSize ( channel - > name ) + sizeof ( quint64 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + Serialize : : stringSize ( channel - > inviteLink ( ) ) ;
2015-08-07 15:11:50 +03:00
}
return result ;
}
2016-03-25 14:29:45 +03:00
void _writePeer ( QDataStream & stream , PeerData * peer ) {
2015-08-07 15:11:50 +03:00
stream < < quint64 ( peer - > id ) < < quint64 ( peer - > photoId ) ;
2016-04-09 09:57:55 +04:00
Serialize : : writeStorageImageLocation ( stream , peer - > photoLoc ) ;
2015-09-03 13:48:40 +03:00
if ( peer - > isUser ( ) ) {
UserData * user = peer - > asUser ( ) ;
2016-05-31 22:27:11 +03:00
stream < < user - > firstName < < user - > lastName < < user - > phone ( ) < < user - > username < < quint64 ( user - > access ) ;
2015-11-22 16:24:17 +03:00
if ( AppVersion > = 9012 ) {
2015-10-29 11:52:39 -04:00
stream < < qint32 ( user - > flags ) ;
}
2016-03-25 14:29:45 +03:00
if ( AppVersion > = 9016 ) {
2016-01-01 22:48:32 +08:00
stream < < ( user - > botInfo ? user - > botInfo - > inlinePlaceholder : QString ( ) ) ;
}
2015-10-29 11:52:39 -04:00
stream < < qint32 ( user - > onlineTill ) < < qint32 ( user - > contact ) < < qint32 ( user - > botInfo ? user - > botInfo - > version : - 1 ) ;
2015-09-03 13:48:40 +03:00
} else if ( peer - > isChat ( ) ) {
2015-08-07 15:11:50 +03:00
ChatData * chat = peer - > asChat ( ) ;
2015-11-22 16:24:17 +03:00
qint32 flagsData = ( AppVersion > = 9012 ) ? chat - > flags : ( chat - > haveLeft ( ) ? 1 : 0 ) ;
2015-10-29 15:10:49 -04:00
2015-09-21 23:57:42 +03:00
stream < < chat - > name < < qint32 ( chat - > count ) < < qint32 ( chat - > date ) < < qint32 ( chat - > version ) < < qint32 ( chat - > creator ) ;
2016-06-01 16:07:03 +03:00
stream < < qint32 ( chat - > isForbidden ? 1 : 0 ) < < qint32 ( flagsData ) < < chat - > inviteLink ( ) ;
2015-09-03 13:48:40 +03:00
} else if ( peer - > isChannel ( ) ) {
ChannelData * channel = peer - > asChannel ( ) ;
2015-08-07 15:11:50 +03:00
2015-09-21 23:57:42 +03:00
stream < < channel - > name < < quint64 ( channel - > access ) < < qint32 ( channel - > date ) < < qint32 ( channel - > version ) ;
2016-06-01 16:07:03 +03:00
stream < < qint32 ( channel - > isForbidden ? 1 : 0 ) < < qint32 ( channel - > flags ) < < channel - > inviteLink ( ) ;
2015-08-07 15:11:50 +03:00
}
}
2016-01-01 22:48:32 +08:00
PeerData * _readPeer ( FileReadDescriptor & from , int32 fileVersion = 0 ) {
2015-08-07 15:11:50 +03:00
quint64 peerId = 0 , photoId = 0 ;
from . stream > > peerId > > photoId ;
2016-04-09 09:57:55 +04:00
StorageImageLocation photoLoc ( Serialize : : readStorageImageLocation ( from . stream ) ) ;
2015-08-07 15:11:50 +03:00
2016-03-25 14:29:45 +03:00
PeerData * result = App : : peerLoaded ( peerId ) ;
bool wasLoaded = ( result ! = nullptr ) ;
2016-01-01 23:08:56 +08:00
if ( ! wasLoaded ) {
result = App : : peer ( peerId ) ;
2016-03-25 14:29:45 +03:00
result - > loadedStatus = PeerData : : FullLoaded ;
2016-01-01 23:08:56 +08:00
}
2015-09-03 13:48:40 +03:00
if ( result - > isUser ( ) ) {
2015-08-07 15:11:50 +03:00
UserData * user = result - > asUser ( ) ;
2016-01-01 22:48:32 +08:00
QString first , last , phone , username , inlinePlaceholder ;
2015-08-07 15:11:50 +03:00
quint64 access ;
2015-10-29 11:52:39 -04:00
qint32 flags = 0 , onlineTill , contact , botInfoVersion ;
from . stream > > first > > last > > phone > > username > > access ;
2015-11-22 16:24:17 +03:00
if ( from . version > = 9012 ) {
2015-10-29 11:52:39 -04:00
from . stream > > flags ;
}
2016-01-01 22:48:32 +08:00
if ( from . version > = 9016 | | fileVersion > = 9016 ) {
from . stream > > inlinePlaceholder ;
}
2015-10-29 11:52:39 -04:00
from . stream > > onlineTill > > contact > > botInfoVersion ;
2015-08-07 15:11:50 +03:00
2015-09-03 13:48:40 +03:00
bool showPhone = ! isServiceUser ( user - > id ) & & ( peerToUser ( user - > id ) ! = MTP : : authedId ( ) ) & & ( contact < = 0 ) ;
2015-08-07 15:11:50 +03:00
QString pname = ( showPhone & & ! phone . isEmpty ( ) ) ? App : : formatPhone ( phone ) : QString ( ) ;
2016-01-01 23:08:56 +08:00
if ( ! wasLoaded ) {
2016-05-31 22:27:11 +03:00
user - > setPhone ( phone ) ;
2016-01-01 23:08:56 +08:00
user - > setName ( first , last , pname , username ) ;
2015-08-07 15:11:50 +03:00
2016-01-01 23:08:56 +08:00
user - > access = access ;
2016-03-19 19:55:15 +03:00
user - > flags = MTPDuser : : Flags ( flags ) ;
2016-01-01 23:08:56 +08:00
user - > onlineTill = onlineTill ;
user - > contact = contact ;
user - > setBotInfoVersion ( botInfoVersion ) ;
if ( ! inlinePlaceholder . isEmpty ( ) & & user - > botInfo ) {
user - > botInfo - > inlinePlaceholder = inlinePlaceholder ;
}
2015-08-07 15:11:50 +03:00
2016-01-01 23:08:56 +08:00
if ( peerToUser ( user - > id ) = = MTP : : authedId ( ) ) {
user - > input = MTP_inputPeerSelf ( ) ;
user - > inputUser = MTP_inputUserSelf ( ) ;
} else {
user - > input = MTP_inputPeerUser ( MTP_int ( peerToUser ( user - > id ) ) , MTP_long ( ( user - > access = = UserNoAccess ) ? 0 : user - > access ) ) ;
user - > inputUser = MTP_inputUser ( MTP_int ( peerToUser ( user - > id ) ) , MTP_long ( ( user - > access = = UserNoAccess ) ? 0 : user - > access ) ) ;
}
2015-08-07 15:11:50 +03:00
2016-03-18 13:18:30 +03:00
user - > setUserpic ( photoLoc . isNull ( ) ? ImagePtr ( userDefPhoto ( user - > colorIndex ) ) : ImagePtr ( photoLoc ) ) ;
2016-01-01 23:08:56 +08:00
}
2015-09-03 13:48:40 +03:00
} else if ( result - > isChat ( ) ) {
ChatData * chat = result - > asChat ( ) ;
2016-06-01 16:07:03 +03:00
QString name , inviteLink ;
2015-10-29 15:10:49 -04:00
qint32 count , date , version , creator , forbidden , flagsData , flags ;
2016-06-01 16:07:03 +03:00
from . stream > > name > > count > > date > > version > > creator > > forbidden > > flagsData > > inviteLink ;
2015-09-03 13:48:40 +03:00
2015-11-22 16:24:17 +03:00
if ( from . version > = 9012 ) {
2015-10-29 15:10:49 -04:00
flags = flagsData ;
} else {
// flagsData was haveLeft
2016-03-21 21:57:03 +03:00
flags = ( flagsData = = 1 ) ? MTPDchat : : Flags ( MTPDchat : : Flag : : f_left ) : MTPDchat : : Flags ( 0 ) ;
2015-10-29 15:10:49 -04:00
}
2016-01-01 23:08:56 +08:00
if ( ! wasLoaded ) {
2016-06-02 16:57:49 +03:00
chat - > setName ( name ) ;
2016-01-01 23:08:56 +08:00
chat - > count = count ;
chat - > date = date ;
chat - > version = version ;
chat - > creator = creator ;
chat - > isForbidden = ( forbidden = = 1 ) ;
2016-03-19 19:55:15 +03:00
chat - > flags = MTPDchat : : Flags ( flags ) ;
2016-06-01 16:07:03 +03:00
chat - > setInviteLink ( inviteLink ) ;
2015-09-03 13:48:40 +03:00
2016-01-01 23:08:56 +08:00
chat - > input = MTP_inputPeerChat ( MTP_int ( peerToChat ( chat - > id ) ) ) ;
chat - > inputChat = MTP_int ( peerToChat ( chat - > id ) ) ;
2015-09-03 13:48:40 +03:00
2016-03-18 13:18:30 +03:00
chat - > setUserpic ( photoLoc . isNull ( ) ? ImagePtr ( chatDefPhoto ( chat - > colorIndex ) ) : ImagePtr ( photoLoc ) ) ;
2016-01-01 23:08:56 +08:00
}
2015-09-03 13:48:40 +03:00
} else if ( result - > isChannel ( ) ) {
ChannelData * channel = result - > asChannel ( ) ;
2016-06-01 16:07:03 +03:00
QString name , inviteLink ;
2015-09-03 13:48:40 +03:00
quint64 access ;
2016-03-20 11:16:35 +03:00
qint32 date , version , forbidden , flags ;
2016-06-01 16:07:03 +03:00
from . stream > > name > > access > > date > > version > > forbidden > > flags > > inviteLink ;
2015-09-03 13:48:40 +03:00
2016-01-01 23:08:56 +08:00
if ( ! wasLoaded ) {
2016-06-02 16:57:49 +03:00
channel - > setName ( name , QString ( ) ) ;
2016-01-01 23:08:56 +08:00
channel - > access = access ;
channel - > date = date ;
channel - > version = version ;
channel - > isForbidden = ( forbidden = = 1 ) ;
2016-03-19 19:55:15 +03:00
channel - > flags = MTPDchannel : : Flags ( flags ) ;
2016-06-01 16:07:03 +03:00
channel - > setInviteLink ( inviteLink ) ;
2015-09-03 13:48:40 +03:00
2016-01-01 23:08:56 +08:00
channel - > input = MTP_inputPeerChannel ( MTP_int ( peerToChannel ( channel - > id ) ) , MTP_long ( access ) ) ;
channel - > inputChannel = MTP_inputChannel ( MTP_int ( peerToChannel ( channel - > id ) ) , MTP_long ( access ) ) ;
2015-09-03 13:48:40 +03:00
2016-03-18 13:18:30 +03:00
channel - > setUserpic ( photoLoc . isNull ( ) ? ImagePtr ( ( channel - > isMegagroup ( ) ? chatDefPhoto ( channel - > colorIndex ) : channelDefPhoto ( channel - > colorIndex ) ) ) : ImagePtr ( photoLoc ) ) ;
2016-01-01 23:08:56 +08:00
}
}
if ( ! wasLoaded ) {
App : : markPeerUpdated ( result ) ;
emit App : : main ( ) - > peerPhotoChanged ( result ) ;
2015-08-07 15:11:50 +03:00
}
return result ;
}
2016-01-01 22:48:32 +08:00
void writeRecentHashtagsAndBots ( ) {
if ( ! _working ( ) ) return ;
const RecentHashtagPack & write ( cRecentWriteHashtags ( ) ) , & search ( cRecentSearchHashtags ( ) ) ;
const RecentInlineBots & bots ( cRecentInlineBots ( ) ) ;
if ( write . isEmpty ( ) & & search . isEmpty ( ) & & bots . isEmpty ( ) ) readRecentHashtagsAndBots ( ) ;
if ( write . isEmpty ( ) & & search . isEmpty ( ) & & bots . isEmpty ( ) ) {
if ( _recentHashtagsAndBotsKey ) {
clearKey ( _recentHashtagsAndBotsKey ) ;
_recentHashtagsAndBotsKey = 0 ;
_mapChanged = true ;
}
_writeMap ( ) ;
} else {
if ( ! _recentHashtagsAndBotsKey ) {
_recentHashtagsAndBotsKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
quint32 size = sizeof ( quint32 ) * 3 , writeCnt = 0 , searchCnt = 0 , botsCnt = cRecentInlineBots ( ) . size ( ) ;
for ( RecentHashtagPack : : const_iterator i = write . cbegin ( ) , e = write . cend ( ) ; i ! = e ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) {
2016-04-09 09:57:55 +04:00
size + = Serialize : : stringSize ( i - > first ) + sizeof ( quint16 ) ;
2016-01-01 22:48:32 +08:00
+ + writeCnt ;
}
}
for ( RecentHashtagPack : : const_iterator i = search . cbegin ( ) , e = search . cend ( ) ; i ! = e ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) {
2016-04-09 09:57:55 +04:00
size + = Serialize : : stringSize ( i - > first ) + sizeof ( quint16 ) ;
2016-01-01 22:48:32 +08:00
+ + searchCnt ;
}
}
for ( RecentInlineBots : : const_iterator i = bots . cbegin ( ) , e = bots . cend ( ) ; i ! = e ; + + i ) {
size + = _peerSize ( * i ) ;
}
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( writeCnt ) < < quint32 ( searchCnt ) ;
for ( RecentHashtagPack : : const_iterator i = write . cbegin ( ) , e = write . cend ( ) ; i ! = e ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) data . stream < < i - > first < < quint16 ( i - > second ) ;
}
for ( RecentHashtagPack : : const_iterator i = search . cbegin ( ) , e = search . cend ( ) ; i ! = e ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) data . stream < < i - > first < < quint16 ( i - > second ) ;
}
data . stream < < quint32 ( botsCnt ) ;
for ( RecentInlineBots : : const_iterator i = bots . cbegin ( ) , e = bots . cend ( ) ; i ! = e ; + + i ) {
2016-03-25 14:29:45 +03:00
_writePeer ( data . stream , * i ) ;
2016-01-01 22:48:32 +08:00
}
FileWriteDescriptor file ( _recentHashtagsAndBotsKey ) ;
file . writeEncrypted ( data ) ;
}
}
void readRecentHashtagsAndBots ( ) {
if ( _recentHashtagsAndBotsWereRead ) return ;
_recentHashtagsAndBotsWereRead = true ;
if ( ! _recentHashtagsAndBotsKey ) return ;
FileReadDescriptor hashtags ;
if ( ! readEncryptedFile ( hashtags , _recentHashtagsAndBotsKey ) ) {
clearKey ( _recentHashtagsAndBotsKey ) ;
_recentHashtagsAndBotsKey = 0 ;
_writeMap ( ) ;
return ;
}
quint32 writeCount = 0 , searchCount = 0 , botsCount = 0 ;
hashtags . stream > > writeCount > > searchCount ;
QString tag ;
quint16 count ;
RecentHashtagPack write , search ;
RecentInlineBots bots ;
if ( writeCount ) {
write . reserve ( writeCount ) ;
for ( uint32 i = 0 ; i < writeCount ; + + i ) {
hashtags . stream > > tag > > count ;
write . push_back ( qMakePair ( tag . trimmed ( ) , count ) ) ;
}
}
if ( searchCount ) {
search . reserve ( searchCount ) ;
for ( uint32 i = 0 ; i < searchCount ; + + i ) {
hashtags . stream > > tag > > count ;
search . push_back ( qMakePair ( tag . trimmed ( ) , count ) ) ;
}
}
cSetRecentWriteHashtags ( write ) ;
cSetRecentSearchHashtags ( search ) ;
if ( ! hashtags . stream . atEnd ( ) ) {
hashtags . stream > > botsCount ;
if ( botsCount ) {
bots . reserve ( botsCount ) ;
for ( uint32 i = 0 ; i < botsCount ; + + i ) {
PeerData * peer = _readPeer ( hashtags , 9016 ) ;
if ( peer & & peer - > isUser ( ) & & peer - > asUser ( ) - > botInfo & & ! peer - > asUser ( ) - > botInfo - > inlinePlaceholder . isEmpty ( ) & & ! peer - > asUser ( ) - > username . isEmpty ( ) ) {
bots . push_back ( peer - > asUser ( ) ) ;
}
}
}
cSetRecentInlineBots ( bots ) ;
}
}
2015-08-07 15:11:50 +03:00
void writeSavedPeers ( ) {
if ( ! _working ( ) ) return ;
const SavedPeers & saved ( cSavedPeers ( ) ) ;
if ( saved . isEmpty ( ) ) {
if ( _savedPeersKey ) {
clearKey ( _savedPeersKey ) ;
_savedPeersKey = 0 ;
_mapChanged = true ;
}
_writeMap ( ) ;
} else {
if ( ! _savedPeersKey ) {
_savedPeersKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
quint32 size = sizeof ( quint32 ) ;
for ( SavedPeers : : const_iterator i = saved . cbegin ( ) ; i ! = saved . cend ( ) ; + + i ) {
2016-05-05 19:04:17 +03:00
size + = _peerSize ( i . key ( ) ) + Serialize : : dateTimeSize ( ) ;
2015-08-07 15:11:50 +03:00
}
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( saved . size ( ) ) ;
for ( SavedPeers : : const_iterator i = saved . cbegin ( ) ; i ! = saved . cend ( ) ; + + i ) {
_writePeer ( data . stream , i . key ( ) ) ;
data . stream < < i . value ( ) ;
}
FileWriteDescriptor file ( _savedPeersKey ) ;
file . writeEncrypted ( data ) ;
}
}
void readSavedPeers ( ) {
if ( ! _savedPeersKey ) return ;
FileReadDescriptor saved ;
if ( ! readEncryptedFile ( saved , _savedPeersKey ) ) {
clearKey ( _savedPeersKey ) ;
_savedPeersKey = 0 ;
_writeMap ( ) ;
return ;
}
2015-11-22 16:24:17 +03:00
if ( saved . version = = 9011 ) { // broken dev version
clearKey ( _savedPeersKey ) ;
_savedPeersKey = 0 ;
_writeMap ( ) ;
return ;
}
2015-08-07 15:11:50 +03:00
quint32 count = 0 ;
saved . stream > > count ;
cRefSavedPeers ( ) . clear ( ) ;
cRefSavedPeersByTime ( ) . clear ( ) ;
QList < PeerData * > peers ;
peers . reserve ( count ) ;
for ( uint32 i = 0 ; i < count ; + + i ) {
PeerData * peer = _readPeer ( saved ) ;
if ( ! peer ) break ;
QDateTime t ;
saved . stream > > t ;
2015-12-31 23:27:21 +08:00
2015-08-07 15:11:50 +03:00
cRefSavedPeers ( ) . insert ( peer , t ) ;
cRefSavedPeersByTime ( ) . insert ( t , peer ) ;
peers . push_back ( peer ) ;
}
2016-05-25 15:09:05 +03:00
2015-10-03 14:33:51 +03:00
if ( App : : api ( ) ) App : : api ( ) - > requestPeers ( peers ) ;
2015-08-07 15:11:50 +03:00
}
void addSavedPeer ( PeerData * peer , const QDateTime & position ) {
SavedPeers & savedPeers ( cRefSavedPeers ( ) ) ;
SavedPeers : : iterator i = savedPeers . find ( peer ) ;
if ( i = = savedPeers . cend ( ) ) {
savedPeers . insert ( peer , position ) ;
} else if ( i . value ( ) ! = position ) {
cRefSavedPeersByTime ( ) . remove ( i . value ( ) , peer ) ;
i . value ( ) = position ;
cRefSavedPeersByTime ( ) . insert ( i . value ( ) , peer ) ;
}
writeSavedPeers ( ) ;
}
void removeSavedPeer ( PeerData * peer ) {
SavedPeers & savedPeers ( cRefSavedPeers ( ) ) ;
if ( savedPeers . isEmpty ( ) ) return ;
SavedPeers : : iterator i = savedPeers . find ( peer ) ;
if ( i ! = savedPeers . cend ( ) ) {
cRefSavedPeersByTime ( ) . remove ( i . value ( ) , peer ) ;
savedPeers . erase ( i ) ;
writeSavedPeers ( ) ;
}
}
2015-09-07 18:53:46 +03:00
void writeReportSpamStatuses ( ) {
2015-09-09 11:19:25 +03:00
_writeReportSpamStatuses ( ) ;
2015-09-07 18:53:46 +03:00
}
2014-11-22 12:45:04 +03:00
struct ClearManagerData {
QThread * thread ;
2015-01-02 17:55:24 +03:00
StorageMap images , stickers , audios ;
2016-01-01 15:42:06 +08:00
WebFilesMap webFiles ;
2014-11-22 12:45:04 +03:00
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 ;
if ( ! data - > tasks . isEmpty ( ) & & ( data - > tasks . at ( 0 ) = = ClearManagerAll ) ) return true ;
if ( task = = ClearManagerAll ) {
data - > tasks . clear ( ) ;
2015-01-02 17:55:24 +03:00
if ( ! _imagesMap . isEmpty ( ) ) {
_imagesMap . clear ( ) ;
_storageImagesSize = 0 ;
_mapChanged = true ;
}
2015-05-19 18:46:45 +03:00
if ( ! _stickerImagesMap . isEmpty ( ) ) {
_stickerImagesMap . clear ( ) ;
2015-01-02 17:55:24 +03:00
_storageStickersSize = 0 ;
_mapChanged = true ;
}
if ( ! _audiosMap . isEmpty ( ) ) {
_audiosMap . clear ( ) ;
_storageAudiosSize = 0 ;
2014-12-12 19:27:03 +03:00
_mapChanged = true ;
}
if ( ! _draftsMap . isEmpty ( ) ) {
_draftsMap . clear ( ) ;
_mapChanged = true ;
}
2016-02-25 13:32:31 +03:00
if ( ! _draftCursorsMap . isEmpty ( ) ) {
_draftCursorsMap . clear ( ) ;
2014-12-12 19:27:03 +03:00
_mapChanged = true ;
}
if ( _locationsKey ) {
_locationsKey = 0 ;
_mapChanged = true ;
}
2015-09-09 11:19:25 +03:00
if ( _reportSpamStatusesKey ) {
_reportSpamStatusesKey = 0 ;
_mapChanged = true ;
}
2015-05-19 18:46:45 +03:00
if ( _recentStickersKeyOld ) {
_recentStickersKeyOld = 0 ;
_mapChanged = true ;
}
if ( _stickersKey ) {
_stickersKey = 0 ;
2015-01-02 17:55:24 +03:00
_mapChanged = true ;
}
2016-01-01 22:48:32 +08:00
if ( _recentHashtagsAndBotsKey ) {
_recentHashtagsAndBotsKey = 0 ;
2015-03-24 13:00:27 +03:00
_mapChanged = true ;
}
2015-08-07 15:11:50 +03:00
if ( _savedPeersKey ) {
_savedPeersKey = 0 ;
_mapChanged = true ;
}
2014-12-12 19:27:03 +03:00
_writeMap ( ) ;
2014-11-22 12:45:04 +03:00
} else {
2015-01-02 17:55:24 +03:00
if ( task & ClearManagerStorage ) {
2014-11-22 12:45:04 +03:00
if ( data - > images . isEmpty ( ) ) {
2015-01-02 17:55:24 +03:00
data - > images = _imagesMap ;
2014-11-22 12:45:04 +03:00
} else {
2015-01-02 17:55:24 +03:00
for ( StorageMap : : const_iterator i = _imagesMap . cbegin ( ) , e = _imagesMap . cend ( ) ; i ! = e ; + + i ) {
2014-11-22 12:45:04 +03:00
StorageKey k = i . key ( ) ;
while ( data - > images . constFind ( k ) ! = data - > images . cend ( ) ) {
+ + k . second ;
}
data - > images . insert ( k , i . value ( ) ) ;
}
}
2015-01-02 17:55:24 +03:00
if ( ! _imagesMap . isEmpty ( ) ) {
_imagesMap . clear ( ) ;
_storageImagesSize = 0 ;
2014-12-12 19:27:03 +03:00
_mapChanged = true ;
}
2015-01-02 17:55:24 +03:00
if ( data - > stickers . isEmpty ( ) ) {
2015-05-19 18:46:45 +03:00
data - > stickers = _stickerImagesMap ;
2015-01-02 17:55:24 +03:00
} else {
2015-05-19 18:46:45 +03:00
for ( StorageMap : : const_iterator i = _stickerImagesMap . cbegin ( ) , e = _stickerImagesMap . cend ( ) ; i ! = e ; + + i ) {
2015-01-02 17:55:24 +03:00
StorageKey k = i . key ( ) ;
while ( data - > stickers . constFind ( k ) ! = data - > stickers . cend ( ) ) {
+ + k . second ;
}
data - > stickers . insert ( k , i . value ( ) ) ;
}
}
2015-05-19 18:46:45 +03:00
if ( ! _stickerImagesMap . isEmpty ( ) ) {
_stickerImagesMap . clear ( ) ;
2015-01-02 17:55:24 +03:00
_storageStickersSize = 0 ;
_mapChanged = true ;
}
2016-01-01 15:42:06 +08:00
if ( data - > webFiles . isEmpty ( ) ) {
data - > webFiles = _webFilesMap ;
} else {
for ( WebFilesMap : : const_iterator i = _webFilesMap . cbegin ( ) , e = _webFilesMap . cend ( ) ; i ! = e ; + + i ) {
QString k = i . key ( ) ;
while ( data - > webFiles . constFind ( k ) ! = data - > webFiles . cend ( ) ) {
k + = ' # ' ;
}
data - > webFiles . insert ( k , i . value ( ) ) ;
}
}
if ( ! _webFilesMap . isEmpty ( ) ) {
_webFilesMap . clear ( ) ;
_storageWebFilesSize = 0 ;
_writeLocations ( ) ;
}
2015-01-02 17:55:24 +03:00
if ( data - > audios . isEmpty ( ) ) {
data - > audios = _audiosMap ;
} else {
for ( StorageMap : : const_iterator i = _audiosMap . cbegin ( ) , e = _audiosMap . cend ( ) ; i ! = e ; + + i ) {
StorageKey k = i . key ( ) ;
while ( data - > audios . constFind ( k ) ! = data - > audios . cend ( ) ) {
+ + k . second ;
}
data - > audios . insert ( k , i . value ( ) ) ;
}
}
if ( ! _audiosMap . isEmpty ( ) ) {
_audiosMap . clear ( ) ;
_storageAudiosSize = 0 ;
_mapChanged = true ;
}
_writeMap ( ) ;
2014-11-22 12:45:04 +03:00
}
for ( int32 i = 0 , l = data - > tasks . size ( ) ; i < l ; + + i ) {
if ( data - > tasks . at ( i ) = = task ) return true ;
}
}
data - > tasks . push_back ( task ) ;
return true ;
}
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 ;
}
return false ;
}
void ClearManager : : start ( ) {
moveToThread ( data - > thread ) ;
connect ( data - > thread , SIGNAL ( started ( ) ) , this , SLOT ( onStart ( ) ) ) ;
data - > thread - > start ( ) ;
}
ClearManager : : ~ ClearManager ( ) {
data - > thread - > deleteLater ( ) ;
delete data ;
}
void ClearManager : : onStart ( ) {
while ( true ) {
int task = 0 ;
bool result = false ;
2015-01-02 17:55:24 +03:00
StorageMap images , stickers , audios ;
2016-01-01 15:42:06 +08:00
WebFilesMap webFiles ;
2014-11-22 12:45:04 +03:00
{
QMutexLocker lock ( & data - > mutex ) ;
if ( data - > tasks . isEmpty ( ) ) {
data - > working = false ;
break ;
}
task = data - > tasks . at ( 0 ) ;
images = data - > images ;
2015-01-02 17:55:24 +03:00
stickers = data - > stickers ;
audios = data - > audios ;
2016-01-01 15:42:06 +08:00
webFiles = data - > webFiles ;
2014-11-22 12:45:04 +03:00
}
switch ( task ) {
2015-03-02 15:34:16 +03:00
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 ( ) ;
2015-06-27 16:02:00 +03:00
if ( ! path . endsWith ( qstr ( " map0 " ) ) & & ! path . endsWith ( qstr ( " map1 " ) ) ) {
2015-03-02 15:34:16 +03:00
if ( ! QFile : : remove ( di . filePath ( ) ) ) result = false ;
}
}
}
} break ;
2014-11-22 12:45:04 +03:00
case ClearManagerDownloads :
result = QDir ( cTempDir ( ) ) . removeRecursively ( ) ;
break ;
2015-01-02 17:55:24 +03:00
case ClearManagerStorage :
2014-11-22 12:45:04 +03:00
for ( StorageMap : : const_iterator i = images . cbegin ( ) , e = images . cend ( ) ; i ! = e ; + + i ) {
2015-03-02 15:34:16 +03:00
clearKey ( i . value ( ) . first , UserPath ) ;
2014-11-22 12:45:04 +03:00
}
2015-01-02 17:55:24 +03:00
for ( StorageMap : : const_iterator i = stickers . cbegin ( ) , e = stickers . cend ( ) ; i ! = e ; + + i ) {
2015-03-02 15:34:16 +03:00
clearKey ( i . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
}
for ( StorageMap : : const_iterator i = audios . cbegin ( ) , e = audios . cend ( ) ; i ! = e ; + + i ) {
2015-03-02 15:34:16 +03:00
clearKey ( i . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
}
2016-01-01 15:42:06 +08:00
for ( WebFilesMap : : const_iterator i = webFiles . cbegin ( ) , e = webFiles . cend ( ) ; i ! = e ; + + i ) {
clearKey ( i . value ( ) . first , UserPath ) ;
}
2014-11-22 12:45:04 +03:00
result = true ;
break ;
}
{
QMutexLocker lock ( & data - > mutex ) ;
if ( 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 ;
}
}
}
}