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 .
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2014-12-01 13:47:38 +03:00
Copyright ( c ) 2014 John Preston , https : //desktop.telegram.org
2014-11-22 12:45:04 +03:00
*/
# include "stdafx.h"
# include "localstorage.h"
2015-03-02 15:34:16 +03:00
# include "lang.h"
2014-11-22 12:45:04 +03:00
namespace {
2015-05-19 18:46:45 +03:00
enum StickerSetType {
StickerSetTypeEmpty = 0 ,
StickerSetTypeID = 1 ,
StickerSetTypeShortName = 2 ,
} ;
2014-11-22 12:45:04 +03:00
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 ;
}
FileKey fromFilePart ( const QString & val ) {
FileKey result = 0 ;
int32 i = val . size ( ) ;
if ( i ! = 0x10 ) return 0 ;
while ( i > 0 ) {
- - i ;
result < < = 4 ;
uint16 ch = val . at ( i ) . unicode ( ) ;
if ( ch > = ' A ' & & ch < = ' F ' ) {
result | = ( ch - ' A ' ) + 0x0A ;
} else if ( ch > = ' 0 ' & & ch < = ' 9 ' ) {
result | = ( ch - ' 0 ' ) ;
} else {
return 0 ;
}
}
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 ;
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 {
result = MTP : : nonce < 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 ;
}
uint32 _dateTimeSize ( ) {
return ( sizeof ( qint64 ) + sizeof ( quint32 ) + sizeof ( qint8 ) ) ;
}
uint32 _stringSize ( const QString & str ) {
return sizeof ( quint32 ) + str . size ( ) * sizeof ( ushort ) ;
}
2015-05-19 18:46:45 +03:00
uint32 _bytearraySize ( const QByteArray & arr ) {
return sizeof ( quint32 ) + arr . size ( ) ;
}
2015-03-02 15:34:16 +03:00
QByteArray _settingsSalt , _passKeySalt , _passKeyEncrypted ;
2014-11-22 12:45:04 +03:00
2015-03-02 15:34:16 +03:00
mtpAuthKey _oldKey , _settingsKey , _passKey , _localKey ;
2014-11-22 12:45:04 +03:00
void createLocalKey ( const QByteArray & pass , QByteArray * salt , mtpAuthKey * result ) {
uchar key [ LocalEncryptKeySize ] = { 0 } ;
int32 iterCount = pass . size ( ) ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount ; // dont slow down for no password
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 ;
}
2015-03-02 15:34:16 +03:00
static QByteArray prepareEncrypted ( EncryptedDescriptor & data , const mtpAuthKey & 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 ( ) ) ;
aesEncryptLocal ( toEncrypt . constData ( ) , encrypted . data ( ) + 0x10 , fullSize , & key , encrypted . constData ( ) ) ;
return encrypted ;
}
bool writeEncrypted ( EncryptedDescriptor & data , const mtpAuthKey & key = _localKey ) {
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 ) ) {
DEBUG_LOG ( ( " App Info: bad magic %1 in '%2' " ) . arg ( mb ( magic , tdfMagicLen ) . str ( ) ) . arg ( name ) ) ;
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 ;
}
bool decryptLocal ( EncryptedDescriptor & result , const QByteArray & encrypted , const mtpAuthKey & key = _localKey ) {
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
bool readEncryptedFile ( FileReadDescriptor & result , const QString & name , int options = UserPath | SafePath , const mtpAuthKey & key = _localKey ) {
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 ;
}
2015-03-02 15:34:16 +03:00
bool readEncryptedFile ( FileReadDescriptor & result , const FileKey & fkey , int options = UserPath | SafePath , const mtpAuthKey & key = _localKey ) {
return readEncryptedFile ( result , toFilePart ( fkey ) , options , key ) ;
}
FileKey _dataNameKey = 0 ;
2014-11-22 12:45:04 +03:00
enum { // Local Storage Keys
2015-05-19 18:46:45 +03: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
lskRecentHashtags = 0x0a , // no data
lskStickers = 0x0b , // no data
2014-11-22 12:45:04 +03:00
} ;
typedef QMap < PeerId , FileKey > DraftsMap ;
DraftsMap _draftsMap , _draftsPositionsMap ;
typedef QMap < PeerId , bool > DraftsNotReadMap ;
DraftsNotReadMap _draftsNotReadMap ;
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 ;
2014-12-05 16:44:27 +03:00
FileKey _locationsKey = 0 ;
2015-01-02 17:55:24 +03:00
2015-05-19 18:46:45 +03:00
FileKey _recentStickersKeyOld = 0 , _stickersKey = 0 ;
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 ;
2015-03-24 13:00:27 +03:00
FileKey _recentHashtagsKey = 0 ;
2015-03-24 18:18:20 +03:00
bool _recentHashtagsWereRead = false ;
2015-03-02 15:34:16 +03:00
2015-01-02 17:55:24 +03:00
typedef QPair < FileKey , qint32 > FileDesc ; // file, size
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 ;
2014-12-03 16:10:32 +03:00
int32 _oldMapVersion = 0 ;
2014-12-05 16:44:27 +03:00
enum WriteMapWhen {
WriteMapNow ,
WriteMapFast ,
WriteMapSoon ,
} ;
void _writeMap ( WriteMapWhen when = WriteMapSoon ) ;
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 ( ) ;
if ( _fileLocations . isEmpty ( ) ) {
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-01-02 17:55:24 +03:00
// location + type + namelen + name + date + size
2015-03-02 15:34:16 +03:00
size + = sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + _stringSize ( i . value ( ) . name ) + _dateTimeSize ( ) + sizeof ( quint32 ) ;
2014-12-05 16:44:27 +03:00
}
2015-07-01 00:07:05 +03:00
//end mark
size + = sizeof ( quint64 ) * 2 + sizeof ( quint32 ) + _stringSize ( QString ( ) ) + _dateTimeSize ( ) + sizeof ( quint32 ) ;
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 ;
}
2014-12-05 16:44:27 +03:00
EncryptedDescriptor data ( size ) ;
for ( FileLocations : : const_iterator i = _fileLocations . cbegin ( ) ; i ! = _fileLocations . cend ( ) ; + + i ) {
data . stream < < quint64 ( i . key ( ) . first ) < < quint64 ( i . key ( ) . second ) < < quint32 ( i . value ( ) . type ) < < i . value ( ) . name < < i . value ( ) . modified < < quint32 ( i . value ( ) . size ) ;
}
2015-07-01 00:07:05 +03:00
data . stream < < quint64 ( 0 ) < < quint64 ( 0 ) < < quint32 ( 0 ) < < QString ( ) < < QDateTime : : currentDateTime ( ) < < quint32 ( 0 ) ;
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 ) ;
}
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 ;
FileLocation loc ;
quint32 type ;
locations . stream > > first > > second > > type > > loc . name > > loc . modified > > loc . size ;
2015-07-01 00:07:05 +03:00
if ( ! first & & ! second & & ! type & & loc . name . isEmpty ( ) & & ! loc . size ) { // end mark
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
if ( loc . check ( ) ) {
_fileLocations . insert ( key , loc ) ;
_fileLocationPairs . insert ( loc . name , FileLocationPair ( key , loc ) ) ;
} else {
_writeLocations ( ) ;
}
}
2015-07-01 00:07:05 +03:00
if ( endMarkFound ) {
quint32 cnt ;
locations . stream > > cnt ;
for ( int32 i = 0 ; i < cnt ; + + i ) {
quint64 kfirst , ksecond , vfirst , vsecond ;
locations . stream > > kfirst > > ksecond > > vfirst > > vsecond ;
_fileLocationAliases . insert ( MediaKey ( kfirst , ksecond ) , MediaKey ( vfirst , vsecond ) ) ;
}
}
2014-12-05 16:44:27 +03:00
}
2015-03-02 15:34:16 +03:00
mtpDcOptions * _dcOpts = 0 ;
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 ;
2015-06-10 15:48:26 +03:00
if ( _dcOpts ) _dcOpts - > insert ( dcId , mtpDcOption ( dcId , 0 , ip . toUtf8 ( ) . constData ( ) , port ) ) ;
} break ;
case dbiDcOption : {
quint32 dcIdWithShift , flags , port ;
QString ip ;
stream > > dcIdWithShift > > flags > > ip > > port ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
if ( _dcOpts ) _dcOpts - > insert ( dcIdWithShift , mtpDcOption ( dcIdWithShift % _mtp_internal : : dcShift , flags , ip . toUtf8 ( ) . constData ( ) , port ) ) ;
2015-03-02 15:34:16 +03:00
} break ;
case dbiMaxGroupCount : {
qint32 maxSize ;
stream > > maxSize ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetMaxGroupCount ( maxSize ) ;
} break ;
case dbiUser : {
quint32 dcId ;
qint32 uid ;
stream > > uid > > dcId ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
DEBUG_LOG ( ( " MTP Info: user found, dc %1, uid %2 " ) . arg ( dcId ) . arg ( uid ) ) ;
MTP : : configure ( dcId , uid ) ;
} break ;
case dbiKey : {
qint32 dcId ;
quint32 key [ 64 ] ;
stream > > dcId ;
stream . readRawData ( ( char * ) key , 256 ) ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
DEBUG_LOG ( ( " MTP Info: key found, dc %1, key: %2 " ) . arg ( dcId ) . arg ( mb ( key , 256 ) . str ( ) ) ) ;
dcId = dcId % _mtp_internal : : dcShift ;
mtpAuthKeyPtr keyPtr ( new mtpAuthKey ( ) ) ;
keyPtr - > setKey ( key ) ;
keyPtr - > setDC ( dcId ) ;
MTP : : setKey ( dcId , keyPtr ) ;
} break ;
case dbiAutoStart : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetAutoStart ( v = = 1 ) ;
} break ;
case dbiStartMinimized : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetStartMinimized ( v = = 1 ) ;
} break ;
case dbiSendToMenu : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetSendToMenu ( v = = 1 ) ;
} break ;
case dbiSoundNotify : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetSoundNotify ( v = = 1 ) ;
} break ;
case dbiDesktopNotify : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetDesktopNotify ( v = = 1 ) ;
} break ;
case dbiWorkMode : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
switch ( v ) {
case dbiwmTrayOnly : cSetWorkMode ( dbiwmTrayOnly ) ; break ;
case dbiwmWindowOnly : cSetWorkMode ( dbiwmWindowOnly ) ; break ;
default : cSetWorkMode ( dbiwmWindowAndTray ) ; break ;
} ;
} break ;
case dbiConnectionType : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
switch ( v ) {
case dbictHttpProxy :
case dbictTcpProxy : {
ConnectionProxy p ;
qint32 port ;
stream > > p . host > > port > > p . user > > p . password ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
p . port = uint32 ( port ) ;
cSetConnectionProxy ( p ) ;
}
cSetConnectionType ( DBIConnectionType ( v ) ) ;
break ;
case dbictHttpAuto :
default : cSetConnectionType ( dbictAuto ) ; break ;
} ;
} break ;
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 ) ;
} 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 ;
case dbiAutoLock : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetAutoLock ( v ) ;
} break ;
case dbiReplaceEmojis : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetReplaceEmojis ( v = = 1 ) ;
} break ;
case dbiDefaultAttach : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
switch ( v ) {
case dbidaPhoto : cSetDefaultAttach ( dbidaPhoto ) ; break ;
default : cSetDefaultAttach ( dbidaDocument ) ; break ;
}
} break ;
case dbiNotifyView : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
switch ( v ) {
case dbinvShowNothing : cSetNotifyView ( dbinvShowNothing ) ; break ;
case dbinvShowName : cSetNotifyView ( dbinvShowName ) ; break ;
default : cSetNotifyView ( dbinvShowPreview ) ; break ;
}
} break ;
case dbiAskDownloadPath : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetAskDownloadPath ( v = = 1 ) ;
} break ;
case dbiDownloadPath : {
QString v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetDownloadPath ( v ) ;
} break ;
case dbiCompressPastedImage : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetCompressPastedImage ( v = = 1 ) ;
} break ;
case dbiEmojiTab : {
qint32 v ;
stream > > v ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
switch ( v ) {
2015-05-08 15:45:14 +03:00
case dbietRecent : cSetEmojiTab ( dbietRecent ) ; break ;
case dbietPeople : cSetEmojiTab ( dbietPeople ) ; break ;
case dbietNature : cSetEmojiTab ( dbietNature ) ; break ;
case dbietFood : cSetEmojiTab ( dbietFood ) ; break ;
case dbietCelebration : cSetEmojiTab ( dbietCelebration ) ; break ;
case dbietActivity : cSetEmojiTab ( dbietActivity ) ; break ;
case dbietTravel : cSetEmojiTab ( dbietTravel ) ; break ;
case dbietObjects : cSetEmojiTab ( dbietObjects ) ; break ;
case dbietStickers : cSetEmojiTab ( dbietStickers ) ; break ;
}
} 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 ;
2015-03-24 18:49:07 +03:00
case dbiDialogLastPath : {
QString path ;
stream > > path ;
if ( ! _checkStreamStatus ( stream ) ) return false ;
cSetDialogLastPath ( path ) ;
} 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 ) ) {
LOG ( ( " App Info: reading old config.. " ) ) ;
QDataStream stream ( & file ) ;
stream . setVersion ( QDataStream : : Qt_5_1 ) ;
qint32 version = 0 ;
while ( ! stream . atEnd ( ) ) {
quint32 blockId ;
stream > > blockId ;
if ( ! _checkStreamStatus ( stream ) ) break ;
if ( blockId = = dbiVersion ) {
stream > > version ;
if ( ! _checkStreamStatus ( stream ) ) break ;
if ( version > AppVersion ) break ;
} else if ( ! _readSetting ( blockId , stream , version ) ) {
break ;
}
}
file . close ( ) ;
result = true ;
}
if ( remove ) file . remove ( ) ;
return result ;
}
void _readOldUserSettingsFields ( QIODevice * device , qint32 & version ) {
QDataStream stream ( device ) ;
stream . setVersion ( QDataStream : : Qt_5_1 ) ;
while ( ! stream . atEnd ( ) ) {
quint32 blockId ;
stream > > blockId ;
if ( ! _checkStreamStatus ( stream ) ) {
break ;
}
if ( blockId = = dbiVersion ) {
stream > > version ;
if ( ! _checkStreamStatus ( stream ) ) {
break ;
}
if ( version > AppVersion ) return ;
} else if ( blockId = = dbiEncryptedWithSalt ) {
QByteArray salt , data , decrypted ;
stream > > salt > > data ;
if ( ! _checkStreamStatus ( stream ) ) {
break ;
}
if ( salt . size ( ) ! = 32 ) {
LOG ( ( " App Error: bad salt in old user config encrypted part, size: %1 " ) . arg ( salt . size ( ) ) ) ;
continue ;
}
createLocalKey ( QByteArray ( ) , & salt , & _oldKey ) ;
if ( data . size ( ) < = 16 | | ( data . size ( ) & 0x0F ) ) {
LOG ( ( " App Error: bad encrypted part size in old user config: %1 " ) . arg ( data . size ( ) ) ) ;
continue ;
}
uint32 fullDataLen = data . size ( ) - 16 ;
decrypted . resize ( fullDataLen ) ;
const char * dataKey = data . constData ( ) , * encrypted = data . constData ( ) + 16 ;
aesDecryptLocal ( encrypted , decrypted . data ( ) , fullDataLen , & _oldKey , dataKey ) ;
uchar sha1Buffer [ 20 ] ;
if ( memcmp ( hashSha1 ( decrypted . constData ( ) , decrypted . size ( ) , sha1Buffer ) , dataKey , 16 ) ) {
LOG ( ( " App Error: bad decrypt key, data from old user config not decrypted " ) ) ;
continue ;
}
uint32 dataLen = * ( const uint32 * ) decrypted . constData ( ) ;
if ( dataLen > uint32 ( decrypted . size ( ) ) | | dataLen < = fullDataLen - 16 | | dataLen < 4 ) {
LOG ( ( " App Error: bad decrypted part size in old user config: %1, fullDataLen: %2, decrypted size: %3 " ) . arg ( dataLen ) . arg ( fullDataLen ) . arg ( decrypted . size ( ) ) ) ;
continue ;
}
decrypted . resize ( dataLen ) ;
QBuffer decryptedStream ( & decrypted ) ;
decryptedStream . open ( QIODevice : : ReadOnly ) ;
decryptedStream . seek ( 4 ) ; // skip size
LOG ( ( " App Info: reading encrypted old user config.. " ) ) ;
_readOldUserSettingsFields ( & decryptedStream , version ) ;
} else if ( ! _readSetting ( blockId , stream , version ) ) {
return ;
}
}
}
bool _readOldUserSettings ( bool remove = true ) {
bool result = false ;
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 ) ) {
LOG ( ( " App Info: reading old user config.. " ) ) ;
qint32 version = 0 ;
2015-05-14 19:50:04 +03:00
mtpDcOptions dcOpts ;
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
dcOpts = cDcOptions ( ) ;
}
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 ( ) ) ;
cSetDcOptions ( dcOpts ) ;
}
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
LOG ( ( " App Info: reading encrypted old keys.. " ) ) ;
_readOldMtpDataFields ( & decryptedStream , version ) ;
} else if ( ! _readSetting ( blockId , stream , version ) ) {
return ;
}
}
}
bool _readOldMtpData ( bool remove = true ) {
bool result = false ;
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 ) ) {
LOG ( ( " App Info: reading old keys.. " ) ) ;
qint32 version = 0 ;
2015-05-14 19:50:04 +03:00
mtpDcOptions dcOpts ;
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
dcOpts = cDcOptions ( ) ;
}
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 ( ) ) ;
cSetDcOptions ( dcOpts ) ;
}
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 ) ;
}
uint32 size = 11 * ( sizeof ( quint32 ) + sizeof ( qint32 ) ) ;
size + = sizeof ( quint32 ) + _stringSize ( cAskDownloadPath ( ) ? QString ( ) : cDownloadPath ( ) ) ;
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 ) ) ;
2015-03-24 18:49:07 +03:00
size + = sizeof ( quint32 ) + _stringSize ( cDialogLastPath ( ) ) ;
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 ) ;
data . stream < < quint32 ( dbiAutoLock ) < < qint32 ( cAutoLock ( ) ) ;
data . stream < < quint32 ( dbiReplaceEmojis ) < < qint32 ( cReplaceEmojis ( ) ? 1 : 0 ) ;
data . stream < < quint32 ( dbiDefaultAttach ) < < qint32 ( cDefaultAttach ( ) ) ;
data . stream < < quint32 ( dbiSoundNotify ) < < qint32 ( cSoundNotify ( ) ) ;
data . stream < < quint32 ( dbiDesktopNotify ) < < qint32 ( cDesktopNotify ( ) ) ;
data . stream < < quint32 ( dbiNotifyView ) < < qint32 ( cNotifyView ( ) ) ;
data . stream < < quint32 ( dbiAskDownloadPath ) < < qint32 ( cAskDownloadPath ( ) ) ;
data . stream < < quint32 ( dbiDownloadPath ) < < ( cAskDownloadPath ( ) ? QString ( ) : cDownloadPath ( ) ) ;
data . stream < < quint32 ( dbiCompressPastedImage ) < < qint32 ( cCompressPastedImage ( ) ) ;
data . stream < < quint32 ( dbiEmojiTab ) < < qint32 ( cEmojiTab ( ) ) ;
2015-03-24 18:49:07 +03:00
data . stream < < quint32 ( dbiDialogLastPath ) < < cDialogLastPath ( ) ;
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 ;
}
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 ( ) ;
}
LOG ( ( " App Info: reading encrypted user settings.. " ) ) ;
while ( ! userSettings . stream . atEnd ( ) ) {
quint32 blockId ;
userSettings . stream > > blockId ;
if ( ! _checkStreamStatus ( userSettings . stream ) ) {
return _writeUserSettings ( ) ;
}
if ( ! _readSetting ( blockId , userSettings . stream , userSettings . version ) ) {
return _writeUserSettings ( ) ;
}
}
}
void _writeMtpData ( ) {
FileWriteDescriptor mtp ( toFilePart ( _dataNameKey ) , SafePath ) ;
if ( ! _localKey . created ( ) ) {
LOG ( ( " App Error: localkey not created in _writeMtpData() " ) ) ;
return ;
}
mtpKeysMap keys = MTP : : getKeys ( ) ;
quint32 size = sizeof ( quint32 ) + sizeof ( qint32 ) + sizeof ( quint32 ) ;
size + = keys . size ( ) * ( sizeof ( quint32 ) + sizeof ( quint32 ) + 256 ) ;
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( dbiUser ) < < qint32 ( MTP : : authedId ( ) ) < < quint32 ( MTP : : maindc ( ) ) ;
for ( mtpKeysMap : : const_iterator i = keys . cbegin ( ) , e = keys . cend ( ) ; i ! = e ; + + i ) {
data . stream < < quint32 ( dbiKey ) < < quint32 ( ( * i ) - > getDC ( ) ) ;
( * i ) - > write ( data . stream ) ;
}
mtp . writeEncrypted ( data , _localKey ) ;
}
void _readMtpData ( ) {
FileReadDescriptor mtp ;
if ( ! readEncryptedFile ( mtp , toFilePart ( _dataNameKey ) , SafePath ) ) {
if ( _localKey . created ( ) ) {
_readOldMtpData ( ) ;
_writeMtpData ( ) ;
}
return ;
}
LOG ( ( " App Info: reading encrypted mtp data.. " ) ) ;
while ( ! mtp . stream . atEnd ( ) ) {
quint32 blockId ;
mtp . stream > > blockId ;
if ( ! _checkStreamStatus ( mtp . stream ) ) {
return _writeMtpData ( ) ;
}
if ( ! _readSetting ( blockId , mtp . stream , mtp . version ) ) {
return _writeMtpData ( ) ;
}
}
}
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 ;
}
2015-03-02 15:34:16 +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 ) ) {
2015-03-02 15:34:16 +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 ;
}
2015-03-02 15:34:16 +03:00
LOG ( ( " App Info: reading encrypted map.. " ) ) ;
2014-11-22 12:45:04 +03:00
DraftsMap draftsMap , draftsPositionsMap ;
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-05-19 18:46:45 +03:00
quint64 locationsKey = 0 , recentStickersKeyOld = 0 , stickersKey = 0 , backgroundKey = 0 , userSettingsKey = 0 , recentHashtagsKey = 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 ;
draftsPositionsMap . insert ( p , key ) ;
}
} 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-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 ;
2015-03-24 13:00:27 +03:00
case lskRecentHashtags : {
map . stream > > recentHashtagsKey ;
} break ;
2015-05-19 18:46:45 +03:00
case lskStickers : {
map . stream > > stickersKey ;
} 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 ;
_draftsPositionsMap = draftsPositionsMap ;
_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-05-19 18:46:45 +03:00
_recentStickersKeyOld = recentStickersKeyOld ;
_stickersKey = stickersKey ;
2015-02-03 18:02:46 +03:00
_backgroundKey = backgroundKey ;
2015-03-02 15:34:16 +03:00
_userSettingsKey = userSettingsKey ;
2015-03-24 13:00:27 +03:00
_recentHashtagsKey = recentHashtagsKey ;
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-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 ) ) ;
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 ;
if ( ! _draftsPositionsMap . isEmpty ( ) ) mapSize + = sizeof ( quint32 ) * 2 + _draftsPositionsMap . 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-05-19 18:46:45 +03:00
if ( _recentStickersKeyOld ) mapSize + = sizeof ( quint32 ) + sizeof ( quint64 ) ;
if ( _stickersKey ) 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 ) ;
if ( _recentHashtagsKey ) 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 ( ) ) ;
}
}
if ( ! _draftsPositionsMap . isEmpty ( ) ) {
mapData . stream < < quint32 ( lskDraftPosition ) < < quint32 ( _draftsPositionsMap . size ( ) ) ;
for ( DraftsMap : : const_iterator i = _draftsPositionsMap . cbegin ( ) , e = _draftsPositionsMap . cend ( ) ; i ! = e ; + + i ) {
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-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-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 ) ;
}
if ( _recentHashtagsKey ) {
mapData . stream < < quint32 ( lskRecentHashtags ) < < quint64 ( _recentHashtagsKey ) ;
}
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 {
void start ( ) {
if ( ! _started ) {
_started = true ;
_manager = new _local_inner : : Manager ( ) ;
}
}
void stop ( ) {
if ( _manager ) {
_writeMap ( WriteMapNow ) ;
_manager - > finish ( ) ;
_manager - > deleteLater ( ) ;
_manager = 0 ;
}
}
2015-03-02 15:34:16 +03:00
void readSettings ( ) {
Local : : start ( ) ;
_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 ( ) ;
}
LOG ( ( " App Info: reading settings.. " ) ) ;
QByteArray salt , settingsEncrypted ;
settingsData . stream > > salt > > settingsEncrypted ;
if ( ! _checkStreamStatus ( settingsData . stream ) ) {
return writeSettings ( ) ;
}
if ( salt . size ( ) ! = LocalEncryptSaltSize ) {
LOG ( ( " App Error: bad salt in settings file, size: %1 " ) . arg ( salt . size ( ) ) ) ;
return writeSettings ( ) ;
}
createLocalKey ( QByteArray ( ) , & salt , & _settingsKey ) ;
EncryptedDescriptor settings ;
if ( ! decryptLocal ( settings , settingsEncrypted , _settingsKey ) ) {
LOG ( ( " App Error: could not decrypt settings from settings file, maybe bad passcode.. " ) ) ;
return writeSettings ( ) ;
}
2015-05-14 19:50:04 +03:00
mtpDcOptions dcOpts ;
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
dcOpts = cDcOptions ( ) ;
}
2015-03-02 15:34:16 +03:00
_dcOpts = & dcOpts ;
LOG ( ( " App Info: reading encrypted settings.. " ) ) ;
while ( ! settings . stream . atEnd ( ) ) {
quint32 blockId ;
settings . stream > > blockId ;
if ( ! _checkStreamStatus ( settings . stream ) ) {
return writeSettings ( ) ;
}
if ( ! _readSetting ( blockId , settings . stream , settingsData . version ) ) {
return writeSettings ( ) ;
}
}
if ( dcOpts . isEmpty ( ) ) {
const BuiltInDc * bdcs = builtInDcs ( ) ;
for ( int i = 0 , l = builtInDcsCount ( ) ; i < l ; + + i ) {
2015-06-10 15:48:26 +03:00
dcOpts . insert ( bdcs [ i ] . id , mtpDcOption ( bdcs [ i ] . id , 0 , 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 ) {
int32 flags = MTPDdcOption_flag_ipv6 , idWithShift = bdcsipv6 [ i ] . id + ( flags * _mtp_internal : : dcShift ) ;
dcOpts . insert ( idWithShift , mtpDcOption ( bdcsipv6 [ i ] . id , flags , bdcsipv6 [ i ] . ip , bdcsipv6 [ i ] . port ) ) ;
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 ( ) ) ;
cSetDcOptions ( dcOpts ) ;
}
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 ) ;
2015-05-14 19:50:04 +03:00
mtpDcOptions dcOpts ;
{
QReadLocker lock ( MTP : : dcOptionsMutex ( ) ) ;
dcOpts = cDcOptions ( ) ;
}
2015-03-02 15:34:16 +03:00
if ( dcOpts . isEmpty ( ) ) {
const BuiltInDc * bdcs = builtInDcs ( ) ;
for ( int i = 0 , l = builtInDcsCount ( ) ; i < l ; + + i ) {
2015-06-10 15:48:26 +03:00
dcOpts . insert ( bdcs [ i ] . id , mtpDcOption ( bdcs [ i ] . id , 0 , 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 ) {
dcOpts . insert ( bdcsipv6 [ i ] . id + ( MTPDdcOption_flag_ipv6 * _mtp_internal : : dcShift ) , mtpDcOption ( bdcsipv6 [ i ] . id , MTPDdcOption_flag_ipv6 , bdcsipv6 [ i ] . ip , bdcsipv6 [ i ] . port ) ) ;
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 ( ) ) ;
2015-03-02 15:34:16 +03:00
cSetDcOptions ( dcOpts ) ;
}
2015-06-25 21:04:40 +03:00
quint32 size = 11 * ( sizeof ( quint32 ) + sizeof ( qint32 ) ) ;
2015-03-02 15:34:16 +03:00
for ( mtpDcOptions : : const_iterator i = dcOpts . cbegin ( ) , e = dcOpts . cend ( ) ; i ! = e ; + + i ) {
size + = sizeof ( quint32 ) + sizeof ( quint32 ) + sizeof ( quint32 ) ;
2015-06-10 15:48:26 +03:00
size + = sizeof ( quint32 ) + _stringSize ( QString : : fromUtf8 ( i - > ip . data ( ) , i - > ip . size ( ) ) ) ;
2015-03-02 15:34:16 +03:00
}
size + = sizeof ( quint32 ) + _stringSize ( cLangFile ( ) ) ;
size + = sizeof ( quint32 ) + sizeof ( qint32 ) ;
if ( cConnectionType ( ) = = dbictHttpProxy | | cConnectionType ( ) = = dbictTcpProxy ) {
const ConnectionProxy & proxy ( cConnectionProxy ( ) ) ;
size + = _stringSize ( proxy . host ) + sizeof ( qint32 ) + _stringSize ( proxy . user ) + _stringSize ( proxy . password ) ;
}
size + = sizeof ( quint32 ) + sizeof ( qint32 ) * 6 ;
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( dbiMaxGroupCount ) < < qint32 ( cMaxGroupCount ( ) ) ;
data . stream < < quint32 ( dbiAutoStart ) < < qint32 ( cAutoStart ( ) ) ;
data . stream < < quint32 ( dbiStartMinimized ) < < qint32 ( cStartMinimized ( ) ) ;
data . stream < < quint32 ( dbiSendToMenu ) < < qint32 ( cSendToMenu ( ) ) ;
data . stream < < quint32 ( dbiWorkMode ) < < qint32 ( cWorkMode ( ) ) ;
data . stream < < quint32 ( dbiSeenTrayTooltip ) < < qint32 ( cSeenTrayTooltip ( ) ) ;
data . stream < < quint32 ( dbiAutoUpdate ) < < qint32 ( cAutoUpdate ( ) ) ;
data . stream < < quint32 ( dbiLastUpdateCheck ) < < qint32 ( cLastUpdateCheck ( ) ) ;
data . stream < < quint32 ( dbiScale ) < < qint32 ( cConfigScale ( ) ) ;
data . stream < < quint32 ( dbiLang ) < < qint32 ( cLang ( ) ) ;
for ( mtpDcOptions : : const_iterator i = dcOpts . cbegin ( ) , e = dcOpts . cend ( ) ; i ! = e ; + + i ) {
2015-06-10 15:48:26 +03:00
data . stream < < quint32 ( dbiDcOption ) < < quint32 ( i . key ( ) ) ;
data . stream < < quint32 ( i - > flags ) < < QString : : fromUtf8 ( i - > ip . data ( ) , i - > ip . size ( ) ) ;
2015-03-02 15:34:16 +03:00
data . stream < < quint32 ( i - > port ) ;
}
data . stream < < quint32 ( dbiLangFile ) < < cLangFile ( ) ;
data . stream < < quint32 ( dbiConnectionType ) < < qint32 ( cConnectionType ( ) ) ;
if ( cConnectionType ( ) = = dbictHttpProxy | | cConnectionType ( ) = = dbictTcpProxy ) {
const ConnectionProxy & proxy ( cConnectionProxy ( ) ) ;
data . stream < < proxy . host < < qint32 ( proxy . port ) < < proxy . user < < proxy . password ;
}
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 ( ) {
_passKeySalt . clear ( ) ; // reset passcode, local key
_draftsMap . clear ( ) ;
_draftsPositionsMap . clear ( ) ;
_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-05-19 18:46:45 +03:00
_locationsKey = _recentStickersKeyOld = _stickersKey = _backgroundKey = _userSettingsKey = _recentHashtagsKey = 0 ;
2015-03-02 15:34:16 +03:00
_mapChanged = true ;
_writeMap ( WriteMapNow ) ;
_writeMtpData ( ) ;
}
bool checkPasscode ( const QByteArray & passcode ) {
mtpAuthKey tmp ;
createLocalKey ( passcode , & _passKeySalt , & tmp ) ;
return ( tmp = = _passKey ) ;
}
void setPasscode ( const QByteArray & passcode ) {
createLocalKey ( passcode , & _passKeySalt , & _passKey ) ;
EncryptedDescriptor passKeyData ( LocalEncryptKeySize ) ;
_localKey . write ( passKeyData . stream ) ;
_passKeyEncrypted = FileWriteDescriptor : : prepareEncrypted ( passKeyData , _passKey ) ;
_mapChanged = true ;
_writeMap ( WriteMapNow ) ;
cSetHasPasscode ( ! passcode . isEmpty ( ) ) ;
}
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-03-19 12:18:19 +03:00
void writeDraft ( const PeerId & peer , const MessageDraft & draft ) {
2014-11-22 12:45:04 +03:00
if ( ! _working ( ) ) return ;
2015-03-19 12:18:19 +03:00
if ( draft . replyTo < = 0 & & draft . text . isEmpty ( ) ) {
2014-11-22 12:45:04 +03:00
DraftsMap : : iterator i = _draftsMap . find ( peer ) ;
if ( i ! = _draftsMap . cend ( ) ) {
clearKey ( i . value ( ) ) ;
_draftsMap . erase ( i ) ;
_mapChanged = true ;
_writeMap ( ) ;
}
_draftsNotReadMap . remove ( peer ) ;
} else {
DraftsMap : : const_iterator i = _draftsMap . constFind ( peer ) ;
if ( i = = _draftsMap . cend ( ) ) {
i = _draftsMap . insert ( peer , genKey ( ) ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
2015-03-19 12:18:19 +03:00
EncryptedDescriptor data ( sizeof ( quint64 ) + _stringSize ( draft . text ) + sizeof ( qint32 ) ) ;
2015-04-07 01:15:29 +03:00
data . stream < < quint64 ( peer ) < < draft . text < < qint32 ( draft . replyTo ) < < qint32 ( draft . previewCancelled ? 1 : 0 ) ;
2014-11-22 12:45:04 +03:00
FileWriteDescriptor file ( i . value ( ) ) ;
file . writeEncrypted ( data ) ;
_draftsNotReadMap . remove ( peer ) ;
}
}
2015-03-19 12:18:19 +03:00
MessageDraft readDraft ( const PeerId & peer ) {
if ( ! _draftsNotReadMap . remove ( peer ) ) return MessageDraft ( ) ;
2014-11-22 12:45:04 +03:00
DraftsMap : : iterator j = _draftsMap . find ( peer ) ;
if ( j = = _draftsMap . cend ( ) ) {
2015-03-19 12:18:19 +03:00
return MessageDraft ( ) ;
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 ) ;
2015-03-19 12:18:19 +03:00
return MessageDraft ( ) ;
2014-11-22 12:45:04 +03:00
}
quint64 draftPeer ;
QString draftText ;
2015-04-07 01:15:29 +03:00
qint32 draftReplyTo = 0 , draftPreviewCancelled = 0 ;
2014-11-22 12:45:04 +03:00
draft . stream > > draftPeer > > draftText ;
2015-03-19 12:18:19 +03:00
if ( draft . version > = 7021 ) draft . stream > > draftReplyTo ;
2015-04-07 01:15:29 +03:00
if ( draft . version > = 8001 ) draft . stream > > draftPreviewCancelled ;
return ( draftPeer = = peer ) ? MessageDraft ( MsgId ( draftReplyTo ) , draftText , ( draftPreviewCancelled = = 1 ) ) : MessageDraft ( ) ;
2014-11-22 12:45:04 +03:00
}
void writeDraftPositions ( const PeerId & peer , const MessageCursor & cur ) {
if ( ! _working ( ) ) return ;
if ( cur . position = = 0 & & cur . anchor = = 0 & & cur . scroll = = 0 ) {
DraftsMap : : iterator i = _draftsPositionsMap . find ( peer ) ;
if ( i ! = _draftsPositionsMap . cend ( ) ) {
clearKey ( i . value ( ) ) ;
_draftsPositionsMap . erase ( i ) ;
_mapChanged = true ;
_writeMap ( ) ;
}
} else {
DraftsMap : : const_iterator i = _draftsPositionsMap . constFind ( peer ) ;
if ( i = = _draftsPositionsMap . cend ( ) ) {
i = _draftsPositionsMap . insert ( peer , genKey ( ) ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
EncryptedDescriptor data ( sizeof ( quint64 ) + sizeof ( qint32 ) * 3 ) ;
data . stream < < quint64 ( peer ) < < qint32 ( cur . position ) < < qint32 ( cur . anchor ) < < qint32 ( cur . scroll ) ;
FileWriteDescriptor file ( i . value ( ) ) ;
file . writeEncrypted ( data ) ;
}
}
MessageCursor readDraftPositions ( const PeerId & peer ) {
DraftsMap : : iterator j = _draftsPositionsMap . find ( peer ) ;
if ( j = = _draftsPositionsMap . cend ( ) ) {
return MessageCursor ( ) ;
}
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 ( ) ) ;
_draftsPositionsMap . erase ( j ) ;
return MessageCursor ( ) ;
}
quint64 draftPeer ;
qint32 curPosition , curAnchor , curScroll ;
draft . stream > > draftPeer > > curPosition > > curAnchor > > curScroll ;
return ( draftPeer = = peer ) ? MessageCursor ( curPosition , curAnchor , curScroll ) : MessageCursor ( ) ;
}
bool hasDraftPositions ( const PeerId & peer ) {
return ( _draftsPositionsMap . constFind ( peer ) ! = _draftsPositionsMap . cend ( ) ) ;
}
2015-07-01 00:07:05 +03:00
void writeFileLocation ( MediaKey location , const FileLocation & local ) {
2014-12-05 16:44:27 +03:00
if ( local . name . isEmpty ( ) ) return ;
2015-07-01 00:07:05 +03:00
FileLocationAliases : : const_iterator aliasIt = _fileLocationAliases . constFind ( location ) ;
if ( aliasIt ! = _fileLocationAliases . cend ( ) ) {
location = aliasIt . value ( ) ;
}
2014-12-05 16:44:27 +03:00
FileLocationPairs : : iterator i = _fileLocationPairs . find ( local . name ) ;
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 ) ;
_fileLocationPairs . insert ( local . name , FileLocationPair ( location , local ) ) ;
_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 ) {
QFileInfo info ( i . value ( ) . name ) ;
if ( ! info . exists ( ) | | info . lastModified ( ) ! = i . value ( ) . modified | | info . size ( ) ! = i . value ( ) . size ) {
_fileLocationPairs . remove ( i . value ( ) . name ) ;
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
}
}
StorageImageSaved readImage ( const StorageKey & location ) {
2015-01-02 17:55:24 +03:00
StorageMap : : iterator j = _imagesMap . find ( location ) ;
if ( j = = _imagesMap . cend ( ) ) {
2014-11-22 12:45:04 +03:00
return StorageImageSaved ( ) ;
}
FileReadDescriptor draft ;
2015-03-02 15:34:16 +03:00
if ( ! readEncryptedFile ( draft , j . value ( ) . first , UserPath ) ) {
clearKey ( j . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
_storageImagesSize - = j . value ( ) . second ;
_imagesMap . erase ( j ) ;
2014-11-22 12:45:04 +03:00
return StorageImageSaved ( ) ;
}
QByteArray imageData ;
quint64 locFirst , locSecond ;
quint32 imageType ;
draft . stream > > locFirst > > locSecond > > imageType > > imageData ;
2015-05-19 18:46:45 +03:00
return ( locFirst = = location . first & & locSecond = = location . second ) ? StorageImageSaved ( StorageFileType ( imageType ) , imageData ) : StorageImageSaved ( ) ;
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-05-19 18:46:45 +03:00
QByteArray readStickerImage ( const StorageKey & location ) {
StorageMap : : iterator j = _stickerImagesMap . find ( location ) ;
if ( j = = _stickerImagesMap . cend ( ) ) {
2015-01-02 17:55:24 +03:00
return QByteArray ( ) ;
}
FileReadDescriptor draft ;
2015-03-02 15:34:16 +03:00
if ( ! readEncryptedFile ( draft , j . value ( ) . first , UserPath ) ) {
clearKey ( j . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
_storageStickersSize - = j . value ( ) . second ;
2015-05-19 18:46:45 +03:00
_stickerImagesMap . erase ( j ) ;
2015-01-02 17:55:24 +03:00
return QByteArray ( ) ;
}
QByteArray stickerData ;
quint64 locFirst , locSecond ;
draft . stream > > locFirst > > locSecond > > stickerData ;
return ( locFirst = = location . first & & locSecond = = location . second ) ? stickerData : QByteArray ( ) ;
}
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 ;
}
}
QByteArray readAudio ( const StorageKey & location ) {
StorageMap : : iterator j = _audiosMap . find ( location ) ;
if ( j = = _audiosMap . cend ( ) ) {
return QByteArray ( ) ;
}
FileReadDescriptor draft ;
2015-03-02 15:34:16 +03:00
if ( ! readEncryptedFile ( draft , j . value ( ) . first , UserPath ) ) {
clearKey ( j . value ( ) . first , UserPath ) ;
2015-01-02 17:55:24 +03:00
_storageAudiosSize - = j . value ( ) . second ;
_audiosMap . erase ( j ) ;
return QByteArray ( ) ;
}
QByteArray audioData ;
quint64 locFirst , locSecond ;
draft . stream > > locFirst > > locSecond > > audioData ;
return ( locFirst = = location . first & & locSecond = = location . second ) ? audioData : QByteArray ( ) ;
}
int32 hasAudios ( ) {
return _audiosMap . size ( ) ;
}
qint64 storageAudiosSize ( ) {
return _storageAudiosSize ;
}
2015-05-21 13:44:26 +03:00
void _writeStickerSet ( QDataStream & stream , uint64 setId ) {
StickerSets : : const_iterator it = cStickerSets ( ) . constFind ( setId ) ;
2015-06-28 15:37:10 +03:00
if ( it = = cStickerSets ( ) . cend ( ) ) return ;
2015-05-21 13:44:26 +03:00
2015-06-28 15:37:10 +03:00
bool notLoaded = ( it - > flags & MTPDstickerSet_flag_NOT_LOADED ) ;
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 ) {
DocumentData * doc = * j ;
2015-07-01 00:07:05 +03:00
stream < < quint64 ( doc - > id ) < < quint64 ( doc - > access ) < < qint32 ( doc - > date ) < < doc - > name < < doc - > mime < < qint32 ( doc - > dc ) < < qint32 ( doc - > size ) < < qint32 ( doc - > dimensions . width ( ) ) < < qint32 ( doc - > dimensions . height ( ) ) < < qint32 ( doc - > type ) < < doc - > sticker ( ) - > alt ;
switch ( doc - > sticker ( ) - > set . type ( ) ) {
2015-05-21 13:44:26 +03:00
case mtpc_inputStickerSetID : {
stream < < qint32 ( StickerSetTypeID ) ;
} break ;
case mtpc_inputStickerSetShortName : {
stream < < qint32 ( StickerSetTypeShortName ) ;
} break ;
case mtpc_inputStickerSetEmpty :
default : {
stream < < qint32 ( StickerSetTypeEmpty ) ;
} break ;
}
2015-07-01 00:07:05 +03:00
const StorageImageLocation & loc ( doc - > sticker ( ) - > loc ) ;
2015-05-21 13:44:26 +03:00
stream < < qint32 ( loc . width ) < < qint32 ( loc . height ) < < qint32 ( loc . dc ) < < quint64 ( loc . volume ) < < qint32 ( loc . local ) < < quint64 ( loc . secret ) ;
}
}
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
const StickerSets & sets ( cStickerSets ( ) ) ;
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-05-19 18:46:45 +03:00
quint32 size = sizeof ( quint32 ) + _bytearraySize ( cStickersHash ( ) ) ;
for ( StickerSets : : const_iterator i = sets . cbegin ( ) ; i ! = sets . cend ( ) ; + + i ) {
2015-06-28 15:37:10 +03:00
bool notLoaded = ( i - > flags & MTPDstickerSet_flag_NOT_LOADED ) ;
if ( notLoaded ) {
if ( ! ( i - > flags & MTPDstickerSet_flag_disabled ) ) { // waiting to receive
return ;
}
} else {
if ( i - > stickers . isEmpty ( ) ) continue ;
}
2015-05-19 18:46:45 +03:00
2015-06-28 15:37:10 +03:00
// id + access + title + shortName + stickersCount + hash + flags
size + = sizeof ( quint64 ) * 2 + _stringSize ( i - > title ) + _stringSize ( i - > shortName ) + sizeof ( quint32 ) + sizeof ( qint32 ) * 2 ;
2015-05-19 18:46:45 +03:00
for ( StickerPack : : const_iterator j = i - > stickers . cbegin ( ) , e = i - > stickers . cend ( ) ; j ! = e ; + + j ) {
DocumentData * doc = * j ;
2015-01-02 17:55:24 +03:00
2015-05-19 18:46:45 +03:00
// id + access + date + namelen + name + mimelen + mime + dc + size + width + height + type + alt + type-of-set
2015-07-01 00:07:05 +03:00
size + = sizeof ( quint64 ) + sizeof ( quint64 ) + sizeof ( qint32 ) + _stringSize ( doc - > name ) + _stringSize ( doc - > mime ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + _stringSize ( doc - > sticker ( ) - > alt ) + sizeof ( qint32 ) ;
2015-05-19 18:46:45 +03:00
// thumb-width + thumb-height + thumb-dc + thumb-volume + thumb-local + thumb-secret
size + = sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( qint32 ) + sizeof ( quint64 ) + sizeof ( qint32 ) + sizeof ( quint64 ) ;
}
2015-06-28 15:37:10 +03:00
+ + setsCount ;
}
if ( ! _stickersKey ) {
_stickersKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
2015-01-02 17:55:24 +03:00
}
EncryptedDescriptor data ( size ) ;
2015-06-28 15:37:10 +03:00
data . stream < < quint32 ( setsCount ) < < cStickersHash ( ) ;
2015-05-21 13:44:26 +03:00
_writeStickerSet ( data . stream , CustomStickerSetId ) ;
for ( StickerSetsOrder : : const_iterator i = cStickerSetsOrder ( ) . cbegin ( ) , e = cStickerSetsOrder ( ) . cend ( ) ; i ! = e ; + + i ) {
_writeStickerSet ( data . stream , * i ) ;
2015-01-02 17:55:24 +03:00
}
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
2015-05-19 18:46:45 +03:00
StickerSets & sets ( cRefStickerSets ( ) ) ;
sets . clear ( ) ;
2015-06-28 15:37:10 +03:00
StickerSetsOrder & order ( cRefStickerSetsOrder ( ) ) ;
order . clear ( ) ;
2015-05-21 13:44:26 +03:00
2015-05-19 18:46:45 +03:00
RecentStickerPack & recent ( cRefRecentStickers ( ) ) ;
recent . clear ( ) ;
cSetStickersHash ( QByteArray ( ) ) ;
2015-06-28 15:37:10 +03:00
StickerSet & def ( sets . insert ( DefaultStickerSetId , StickerSet ( DefaultStickerSetId , 0 , lang ( lng_stickers_default_set ) , QString ( ) , 0 , 0 , MTPDstickerSet_flag_official ) ) . value ( ) ) ;
StickerSet & custom ( sets . insert ( CustomStickerSetId , StickerSet ( CustomStickerSetId , 0 , lang ( lng_custom_stickers ) , QString ( ) , 0 , 0 , 0 ) ) . 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 ) ) ) ;
}
2015-05-19 18:46:45 +03:00
DocumentData * doc = App : : documentSet ( id , 0 , access , 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 ( ) ) {
sets . remove ( DefaultStickerSetId ) ;
} else {
order . push_front ( DefaultStickerSetId ) ;
}
2015-05-19 18:46:45 +03:00
if ( custom . stickers . isEmpty ( ) ) sets . remove ( CustomStickerSetId ) ;
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 ;
}
StickerSets & sets ( cRefStickerSets ( ) ) ;
sets . clear ( ) ;
2015-05-21 13:44:26 +03:00
StickerSetsOrder & order ( cRefStickerSetsOrder ( ) ) ;
order . clear ( ) ;
2015-05-19 18:46:45 +03:00
quint32 cnt ;
QByteArray hash ;
stickers . stream > > cnt > > hash ;
2015-06-01 23:24:09 +03:00
if ( stickers . version < 8019 ) {
hash . clear ( ) ; // bad data in old caches
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 ;
}
2015-05-19 18:46:45 +03:00
if ( setId = = DefaultStickerSetId ) {
2015-05-20 00:19:11 +03:00
setTitle = lang ( lng_stickers_default_set ) ;
2015-06-28 15:37:10 +03:00
setFlags | = MTPDstickerSet_flag_official ;
order . push_front ( setId ) ;
2015-05-19 18:46:45 +03:00
} else if ( setId = = CustomStickerSetId ) {
setTitle = lang ( lng_custom_stickers ) ;
2015-06-01 23:24:09 +03:00
} else if ( setId ) {
2015-05-21 13:44:26 +03:00
order . push_back ( setId ) ;
2015-06-01 23:24:09 +03:00
} else {
continue ;
2015-05-19 18:46:45 +03:00
}
2015-06-28 15:37:10 +03:00
StickerSet & set ( sets . insert ( setId , StickerSet ( setId , setAccess , setTitle , setShortName , 0 , setHash , setFlags ) ) . value ( ) ) ;
if ( scnt < 0 ) { // disabled not loaded set
set . count = - scnt ;
continue ;
}
2015-05-19 18:46:45 +03:00
set . stickers . reserve ( scnt ) ;
QMap < uint64 , bool > read ;
2015-06-28 16:16:25 +03:00
for ( int32 j = 0 ; j < scnt ; + + j ) {
2015-05-19 18:46:45 +03:00
quint64 id , access ;
QString name , mime , alt ;
qint32 date , dc , size , width , height , type , typeOfSet ;
stickers . stream > > id > > access > > date > > name > > mime > > dc > > size > > width > > height > > type > > alt > > typeOfSet ;
qint32 thumbWidth , thumbHeight , thumbDc , thumbLocal ;
quint64 thumbVolume , thumbSecret ;
stickers . stream > > thumbWidth > > thumbHeight > > thumbDc > > thumbVolume > > thumbLocal > > thumbSecret ;
if ( read . contains ( id ) ) continue ;
read . insert ( id , true ) ;
if ( setId = = DefaultStickerSetId | | setId = = CustomStickerSetId ) {
typeOfSet = StickerSetTypeEmpty ;
}
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 ) {
switch ( typeOfSet ) {
case StickerSetTypeID : {
attributes . push_back ( MTP_documentAttributeSticker ( MTP_string ( alt ) , MTP_inputStickerSetID ( MTP_long ( setId ) , MTP_long ( setAccess ) ) ) ) ;
} break ;
case StickerSetTypeShortName : {
attributes . push_back ( MTP_documentAttributeSticker ( MTP_string ( alt ) , MTP_inputStickerSetShortName ( MTP_string ( setShortName ) ) ) ) ;
} break ;
case StickerSetTypeEmpty :
default : {
attributes . push_back ( MTP_documentAttributeSticker ( MTP_string ( alt ) , MTP_inputStickerSetEmpty ( ) ) ) ;
} break ;
}
}
if ( width > 0 & & height > 0 ) {
attributes . push_back ( MTP_documentAttributeImageSize ( MTP_int ( width ) , MTP_int ( height ) ) ) ;
}
StorageImageLocation thumb ( thumbWidth , thumbHeight , thumbDc , thumbVolume , thumbLocal , thumbSecret ) ;
DocumentData * doc = App : : documentSet ( id , 0 , access , date , attributes , mime , thumb . dc ? ImagePtr ( thumb ) : ImagePtr ( ) , dc , size , thumb ) ;
2015-07-01 00:07:05 +03:00
if ( ! doc - > sticker ( ) ) continue ;
2015-05-19 18:46:45 +03:00
set . stickers . push_back ( doc ) ;
2015-06-28 15:37:10 +03:00
+ + set . count ;
2015-05-19 18:46:45 +03:00
}
2015-01-02 17:55:24 +03:00
}
2015-05-19 18:46:45 +03:00
cSetStickersHash ( hash ) ;
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 ) ;
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
void writeRecentHashtags ( ) {
if ( ! _working ( ) ) return ;
const RecentHashtagPack & write ( cRecentWriteHashtags ( ) ) , & search ( cRecentSearchHashtags ( ) ) ;
if ( write . isEmpty ( ) & & search . isEmpty ( ) ) readRecentHashtags ( ) ;
if ( write . isEmpty ( ) & & search . isEmpty ( ) ) {
if ( _recentHashtagsKey ) {
clearKey ( _recentHashtagsKey ) ;
_recentHashtagsKey = 0 ;
_mapChanged = true ;
}
_writeMap ( ) ;
} else {
if ( ! _recentHashtagsKey ) {
_recentHashtagsKey = genKey ( ) ;
_mapChanged = true ;
_writeMap ( WriteMapFast ) ;
}
quint32 size = sizeof ( quint32 ) * 2 , writeCnt = 0 , searchCnt = 0 ;
for ( RecentHashtagPack : : const_iterator i = write . cbegin ( ) ; i ! = write . cend ( ) ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) {
size + = _stringSize ( i - > first ) + sizeof ( quint16 ) ;
+ + writeCnt ;
}
}
for ( RecentHashtagPack : : const_iterator i = search . cbegin ( ) ; i ! = search . cend ( ) ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) {
size + = _stringSize ( i - > first ) + sizeof ( quint16 ) ;
+ + searchCnt ;
}
}
EncryptedDescriptor data ( size ) ;
data . stream < < quint32 ( writeCnt ) < < quint32 ( searchCnt ) ;
for ( RecentHashtagPack : : const_iterator i = write . cbegin ( ) ; i ! = write . cend ( ) ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) data . stream < < i - > first < < quint16 ( i - > second ) ;
}
for ( RecentHashtagPack : : const_iterator i = search . cbegin ( ) ; i ! = search . cend ( ) ; + + i ) {
if ( ! i - > first . isEmpty ( ) ) data . stream < < i - > first < < quint16 ( i - > second ) ;
}
FileWriteDescriptor file ( _recentHashtagsKey ) ;
file . writeEncrypted ( data ) ;
}
}
void readRecentHashtags ( ) {
2015-03-24 18:18:20 +03:00
if ( _recentHashtagsWereRead ) return ;
_recentHashtagsWereRead = true ;
2015-03-24 13:00:27 +03:00
if ( ! _recentHashtagsKey ) return ;
FileReadDescriptor hashtags ;
if ( ! readEncryptedFile ( hashtags , _recentHashtagsKey ) ) {
clearKey ( _recentHashtagsKey ) ;
_recentHashtagsKey = 0 ;
_writeMap ( ) ;
return ;
}
quint32 writeCount = 0 , searchCount = 0 ;
hashtags . stream > > writeCount > > searchCount ;
QString tag ;
quint16 count ;
RecentHashtagPack write , search ;
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 ) ;
}
2014-11-22 12:45:04 +03:00
struct ClearManagerData {
QThread * thread ;
2015-01-02 17:55:24 +03:00
StorageMap images , stickers , audios ;
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 ;
}
if ( ! _draftsPositionsMap . isEmpty ( ) ) {
_draftsPositionsMap . clear ( ) ;
_mapChanged = true ;
}
if ( _locationsKey ) {
_locationsKey = 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 ;
}
2015-03-24 13:00:27 +03:00
if ( _recentHashtagsKey ) {
_recentHashtagsKey = 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 ;
}
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 ;
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 ;
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
}
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 ;
}
}
}
}