2014-05-30 08:53:19 +00:00
/*
This file is part of Telegram Desktop ,
2014-12-01 10:47:38 +00:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-05-30 08:53:19 +00:00
Telegram Desktop is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
It is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2015-10-03 13:16:42 +00:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
2014-05-30 08:53:19 +00:00
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2017-01-11 18:31:31 +00:00
Copyright ( c ) 2014 - 2017 John Preston , https : //desktop.telegram.org
2014-05-30 08:53:19 +00:00
*/
2017-03-04 10:23:56 +00:00
# include "storage/file_upload.h"
2014-05-30 08:53:19 +00:00
2017-09-26 11:49:16 +00:00
# include "data/data_document.h"
# include "data/data_photo.h"
2017-08-04 14:54:32 +00:00
namespace Storage {
2017-03-23 16:11:35 +00:00
namespace {
constexpr auto kMaxUploadFileParallelSize = MTP : : kUploadSessionsCount * 512 * 1024 ; // max 512kb uploaded at the same time in each session
} // namespace
2017-08-04 14:54:32 +00:00
Uploader : : Uploader ( ) {
2014-05-30 08:53:19 +00:00
nextTimer . setSingleShot ( true ) ;
connect ( & nextTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( sendNext ( ) ) ) ;
2014-10-30 16:23:44 +00:00
killSessionsTimer . setSingleShot ( true ) ;
connect ( & killSessionsTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( killSessions ( ) ) ) ;
2014-05-30 08:53:19 +00:00
}
2017-08-04 14:54:32 +00:00
void Uploader : : uploadMedia ( const FullMsgId & msgId , const SendMediaReady & media ) {
2016-11-28 15:45:07 +00:00
if ( media . type = = SendMediaType : : Photo ) {
2014-05-30 08:53:19 +00:00
App : : feedPhoto ( media . photo , media . photoThumbs ) ;
2016-11-28 15:45:07 +00:00
} else if ( media . type = = SendMediaType : : File | | media . type = = SendMediaType : : Audio ) {
2014-05-30 08:53:19 +00:00
DocumentData * document ;
if ( media . photoThumbs . isEmpty ( ) ) {
2015-01-02 14:55:24 +00:00
document = App : : feedDocument ( media . document ) ;
2014-05-30 08:53:19 +00:00
} else {
2015-01-02 14:55:24 +00:00
document = App : : feedDocument ( media . document , media . photoThumbs . begin ( ) . value ( ) ) ;
2014-05-30 08:53:19 +00:00
}
document - > status = FileUploading ;
2016-02-12 16:35:06 +00:00
if ( ! media . data . isEmpty ( ) ) {
document - > setData ( media . data ) ;
}
2014-05-30 08:53:19 +00:00
if ( ! media . file . isEmpty ( ) ) {
2017-03-04 11:28:21 +00:00
document - > setLocation ( FileLocation ( media . file ) ) ;
2014-05-30 08:53:19 +00:00
}
}
queue . insert ( msgId , File ( media ) ) ;
sendNext ( ) ;
}
2017-08-04 14:54:32 +00:00
void Uploader : : upload ( const FullMsgId & msgId , const FileLoadResultPtr & file ) {
2016-11-28 15:45:07 +00:00
if ( file - > type = = SendMediaType : : Photo ) {
2017-03-10 15:45:22 +00:00
auto photo = App : : feedPhoto ( file - > photo , file - > photoThumbs ) ;
photo - > uploadingData = std : : make_unique < PhotoData : : UploadingData > ( file - > partssize ) ;
2016-11-28 15:45:07 +00:00
} else if ( file - > type = = SendMediaType : : File | | file - > type = = SendMediaType : : Audio ) {
2017-03-10 15:45:22 +00:00
auto document = file - > thumb . isNull ( ) ? App : : feedDocument ( file - > document ) : App : : feedDocument ( file - > document , file - > thumb ) ;
2015-10-27 02:39:02 +00:00
document - > status = FileUploading ;
2016-02-12 16:35:06 +00:00
if ( ! file - > content . isEmpty ( ) ) {
document - > setData ( file - > content ) ;
}
2015-10-27 02:39:02 +00:00
if ( ! file - > filepath . isEmpty ( ) ) {
2017-03-04 11:28:21 +00:00
document - > setLocation ( FileLocation ( file - > filepath ) ) ;
2015-10-27 02:39:02 +00:00
}
}
queue . insert ( msgId , File ( file ) ) ;
sendNext ( ) ;
}
2017-08-04 14:54:32 +00:00
void Uploader : : currentFailed ( ) {
2014-05-30 08:53:19 +00:00
Queue : : iterator j = queue . find ( uploading ) ;
if ( j ! = queue . end ( ) ) {
2016-11-28 15:45:07 +00:00
if ( j - > type ( ) = = SendMediaType : : Photo ) {
2014-05-30 08:53:19 +00:00
emit photoFailed ( j . key ( ) ) ;
2016-11-28 15:45:07 +00:00
} else if ( j - > type ( ) = = SendMediaType : : File ) {
2015-10-27 02:39:02 +00:00
DocumentData * doc = App : : document ( j - > id ( ) ) ;
2014-05-30 08:53:19 +00:00
if ( doc - > status = = FileUploading ) {
2015-12-08 19:07:50 +00:00
doc - > status = FileUploadFailed ;
2014-05-30 08:53:19 +00:00
}
emit documentFailed ( j . key ( ) ) ;
}
queue . erase ( j ) ;
}
requestsSent . clear ( ) ;
docRequestsSent . clear ( ) ;
2014-10-30 16:23:44 +00:00
dcMap . clear ( ) ;
2015-09-03 10:48:40 +00:00
uploading = FullMsgId ( ) ;
2014-05-30 08:53:19 +00:00
sentSize = 0 ;
2017-03-23 16:11:35 +00:00
for ( int i = 0 ; i < MTP : : kUploadSessionsCount ; + + i ) {
2014-10-30 16:23:44 +00:00
sentSizes [ i ] = 0 ;
}
2014-05-30 08:53:19 +00:00
sendNext ( ) ;
}
2017-08-04 14:54:32 +00:00
void Uploader : : killSessions ( ) {
2017-03-23 16:11:35 +00:00
for ( int i = 0 ; i < MTP : : kUploadSessionsCount ; + + i ) {
2017-02-25 16:44:02 +00:00
MTP : : stopSession ( MTP : : uploadDcId ( i ) ) ;
2014-10-30 16:23:44 +00:00
}
}
2017-08-04 14:54:32 +00:00
void Uploader : : sendNext ( ) {
2017-03-23 16:11:35 +00:00
if ( sentSize > = kMaxUploadFileParallelSize | | _paused . msg ) return ;
2014-10-30 16:23:44 +00:00
bool killing = killSessionsTimer . isActive ( ) ;
if ( queue . isEmpty ( ) ) {
if ( ! killing ) {
2014-11-05 17:43:32 +00:00
killSessionsTimer . start ( MTPAckSendWaiting + MTPKillFileSessionTimeout ) ;
2014-10-30 16:23:44 +00:00
}
return ;
}
2014-05-30 08:53:19 +00:00
2014-10-30 16:23:44 +00:00
if ( killing ) {
killSessionsTimer . stop ( ) ;
}
2015-09-03 10:48:40 +00:00
Queue : : iterator i = uploading . msg ? queue . find ( uploading ) : queue . begin ( ) ;
if ( ! uploading . msg ) {
2014-05-30 08:53:19 +00:00
uploading = i . key ( ) ;
} else if ( i = = queue . end ( ) ) {
2016-02-12 16:35:06 +00:00
i = queue . begin ( ) ;
2014-05-30 08:53:19 +00:00
uploading = i . key ( ) ;
}
2014-10-30 16:23:44 +00:00
int todc = 0 ;
2017-03-23 16:11:35 +00:00
for ( int dc = 1 ; dc < MTP : : kUploadSessionsCount ; + + dc ) {
2014-10-30 16:23:44 +00:00
if ( sentSizes [ dc ] < sentSizes [ todc ] ) {
todc = dc ;
}
}
2015-10-27 02:39:02 +00:00
2016-11-28 15:45:07 +00:00
UploadFileParts & parts ( i - > file ? ( i - > type ( ) = = SendMediaType : : Photo ? i - > file - > fileparts : i - > file - > thumbparts ) : i - > media . parts ) ;
uint64 partsOfId ( i - > file ? ( i - > type ( ) = = SendMediaType : : Photo ? i - > file - > id : i - > file - > thumbId ) : i - > media . thumbId ) ;
2015-10-27 02:39:02 +00:00
if ( parts . isEmpty ( ) ) {
2014-05-30 08:53:19 +00:00
if ( i - > docSentParts > = i - > docPartsCount ) {
if ( requestsSent . isEmpty ( ) & & docRequestsSent . isEmpty ( ) ) {
2016-02-25 16:19:54 +00:00
bool silent = i - > file & & i - > file - > to . silent ;
2016-11-28 15:45:07 +00:00
if ( i - > type ( ) = = SendMediaType : : Photo ) {
2017-03-10 15:45:22 +00:00
auto photoFilename = i - > filename ( ) ;
if ( ! photoFilename . endsWith ( qstr ( " .jpg " ) , Qt : : CaseInsensitive ) ) {
// Server has some extensions checking for inputMediaUploadedPhoto,
// so force the extension to be .jpg anyway. It doesn't matter,
// because the filename from inputFile is not used anywhere.
photoFilename + = qstr ( " .jpg " ) ;
}
emit photoReady ( uploading , silent , MTP_inputFile ( MTP_long ( i - > id ( ) ) , MTP_int ( i - > partsCount ) , MTP_string ( photoFilename ) , MTP_bytes ( i - > file ? i - > file - > filemd5 : i - > media . jpeg_md5 ) ) ) ;
2016-11-28 15:45:07 +00:00
} else if ( i - > type ( ) = = SendMediaType : : File | | i - > type ( ) = = SendMediaType : : Audio ) {
2014-05-30 08:53:19 +00:00
QByteArray docMd5 ( 32 , Qt : : Uninitialized ) ;
2015-05-29 18:52:43 +00:00
hashMd5Hex ( i - > md5Hash . result ( ) , docMd5 . data ( ) ) ;
2014-05-30 08:53:19 +00:00
2016-04-01 10:23:40 +00:00
MTPInputFile doc = ( i - > docSize > UseBigFilesFrom ) ? MTP_inputFileBig ( MTP_long ( i - > id ( ) ) , MTP_int ( i - > docPartsCount ) , MTP_string ( i - > filename ( ) ) ) : MTP_inputFile ( MTP_long ( i - > id ( ) ) , MTP_int ( i - > docPartsCount ) , MTP_string ( i - > filename ( ) ) , MTP_bytes ( docMd5 ) ) ;
2014-05-30 08:53:19 +00:00
if ( i - > partsCount ) {
2016-04-01 10:23:40 +00:00
emit thumbDocumentReady ( uploading , silent , doc , MTP_inputFile ( MTP_long ( i - > thumbId ( ) ) , MTP_int ( i - > partsCount ) , MTP_string ( i - > file ? i - > file - > thumbname : ( qsl ( " thumb. " ) + i - > media . thumbExt ) ) , MTP_bytes ( i - > file ? i - > file - > thumbmd5 : i - > media . jpeg_md5 ) ) ) ;
2014-05-30 08:53:19 +00:00
} else {
2016-02-25 16:19:54 +00:00
emit documentReady ( uploading , silent , doc ) ;
2014-05-30 08:53:19 +00:00
}
}
queue . remove ( uploading ) ;
2015-09-03 10:48:40 +00:00
uploading = FullMsgId ( ) ;
2014-05-30 08:53:19 +00:00
sendNext ( ) ;
}
return ;
}
2015-10-27 02:39:02 +00:00
QByteArray & content ( i - > file ? i - > file - > content : i - > media . data ) ;
2014-05-30 08:53:19 +00:00
QByteArray toSend ;
2015-10-27 02:39:02 +00:00
if ( content . isEmpty ( ) ) {
2014-05-30 08:53:19 +00:00
if ( ! i - > docFile ) {
2015-10-27 02:39:02 +00:00
i - > docFile . reset ( new QFile ( i - > file ? i - > file - > filepath : i - > media . file ) ) ;
2014-05-30 08:53:19 +00:00
if ( ! i - > docFile - > open ( QIODevice : : ReadOnly ) ) {
currentFailed ( ) ;
return ;
}
}
toSend = i - > docFile - > read ( i - > docPartSize ) ;
if ( i - > docSize < = UseBigFilesFrom ) {
2015-05-29 18:52:43 +00:00
i - > md5Hash . feed ( toSend . constData ( ) , toSend . size ( ) ) ;
2014-05-30 08:53:19 +00:00
}
} else {
2015-10-27 02:39:02 +00:00
toSend = content . mid ( i - > docSentParts * i - > docPartSize , i - > docPartSize ) ;
2016-11-28 15:45:07 +00:00
if ( ( i - > type ( ) = = SendMediaType : : File | | i - > type ( ) = = SendMediaType : : Audio ) & & i - > docSentParts < = UseBigFilesFrom ) {
2015-05-29 18:52:43 +00:00
i - > md5Hash . feed ( toSend . constData ( ) , toSend . size ( ) ) ;
2014-07-04 11:12:54 +00:00
}
2014-05-30 08:53:19 +00:00
}
2014-06-16 09:31:10 +00:00
if ( toSend . size ( ) > i - > docPartSize | | ( toSend . size ( ) < i - > docPartSize & & i - > docSentParts + 1 ! = i - > docPartsCount ) ) {
2014-05-30 08:53:19 +00:00
currentFailed ( ) ;
return ;
}
mtpRequestId requestId ;
if ( i - > docSize > UseBigFilesFrom ) {
2017-08-04 14:54:32 +00:00
requestId = MTP : : send ( MTPupload_SaveBigFilePart ( MTP_long ( i - > id ( ) ) , MTP_int ( i - > docSentParts ) , MTP_int ( i - > docPartsCount ) , MTP_bytes ( toSend ) ) , rpcDone ( & Uploader : : partLoaded ) , rpcFail ( & Uploader : : partFailed ) , MTP : : uploadDcId ( todc ) ) ;
2014-05-30 08:53:19 +00:00
} else {
2017-08-04 14:54:32 +00:00
requestId = MTP : : send ( MTPupload_SaveFilePart ( MTP_long ( i - > id ( ) ) , MTP_int ( i - > docSentParts ) , MTP_bytes ( toSend ) ) , rpcDone ( & Uploader : : partLoaded ) , rpcFail ( & Uploader : : partFailed ) , MTP : : uploadDcId ( todc ) ) ;
2014-05-30 08:53:19 +00:00
}
docRequestsSent . insert ( requestId , i - > docSentParts ) ;
2014-10-30 16:23:44 +00:00
dcMap . insert ( requestId , todc ) ;
2014-05-30 08:53:19 +00:00
sentSize + = i - > docPartSize ;
2014-10-30 16:23:44 +00:00
sentSizes [ todc ] + = i - > docPartSize ;
2014-05-30 08:53:19 +00:00
i - > docSentParts + + ;
} else {
2015-10-27 02:39:02 +00:00
UploadFileParts : : iterator part = parts . begin ( ) ;
2016-02-12 16:35:06 +00:00
2017-08-04 14:54:32 +00:00
mtpRequestId requestId = MTP : : send ( MTPupload_SaveFilePart ( MTP_long ( partsOfId ) , MTP_int ( part . key ( ) ) , MTP_bytes ( part . value ( ) ) ) , rpcDone ( & Uploader : : partLoaded ) , rpcFail ( & Uploader : : partFailed ) , MTP : : uploadDcId ( todc ) ) ;
2014-05-30 08:53:19 +00:00
requestsSent . insert ( requestId , part . value ( ) ) ;
2014-10-30 16:23:44 +00:00
dcMap . insert ( requestId , todc ) ;
2014-05-30 08:53:19 +00:00
sentSize + = part . value ( ) . size ( ) ;
2014-10-30 16:23:44 +00:00
sentSizes [ todc ] + = part . value ( ) . size ( ) ;
2014-05-30 08:53:19 +00:00
2015-10-27 02:39:02 +00:00
parts . erase ( part ) ;
2014-05-30 08:53:19 +00:00
}
nextTimer . start ( UploadRequestInterval ) ;
}
2017-08-04 14:54:32 +00:00
void Uploader : : cancel ( const FullMsgId & msgId ) {
2014-05-30 08:53:19 +00:00
uploaded . remove ( msgId ) ;
if ( uploading = = msgId ) {
currentFailed ( ) ;
} else {
queue . remove ( msgId ) ;
}
}
2017-08-04 14:54:32 +00:00
void Uploader : : pause ( const FullMsgId & msgId ) {
2015-12-24 19:26:28 +00:00
_paused = msgId ;
}
2017-08-04 14:54:32 +00:00
void Uploader : : unpause ( ) {
2015-12-24 19:26:28 +00:00
_paused = FullMsgId ( ) ;
sendNext ( ) ;
}
2017-08-04 14:54:32 +00:00
void Uploader : : confirm ( const FullMsgId & msgId ) {
2014-05-30 08:53:19 +00:00
}
2017-08-04 14:54:32 +00:00
void Uploader : : clear ( ) {
2014-05-30 08:53:19 +00:00
uploaded . clear ( ) ;
queue . clear ( ) ;
for ( QMap < mtpRequestId , QByteArray > : : const_iterator i = requestsSent . cbegin ( ) , e = requestsSent . cend ( ) ; i ! = e ; + + i ) {
MTP : : cancel ( i . key ( ) ) ;
}
requestsSent . clear ( ) ;
for ( QMap < mtpRequestId , int32 > : : const_iterator i = docRequestsSent . cbegin ( ) , e = docRequestsSent . cend ( ) ; i ! = e ; + + i ) {
MTP : : cancel ( i . key ( ) ) ;
}
docRequestsSent . clear ( ) ;
2014-10-30 16:23:44 +00:00
dcMap . clear ( ) ;
2014-05-30 08:53:19 +00:00
sentSize = 0 ;
2017-03-23 16:11:35 +00:00
for ( int i = 0 ; i < MTP : : kUploadSessionsCount ; + + i ) {
2017-02-25 16:44:02 +00:00
MTP : : stopSession ( MTP : : uploadDcId ( i ) ) ;
2014-10-30 16:23:44 +00:00
sentSizes [ i ] = 0 ;
}
killSessionsTimer . stop ( ) ;
2014-05-30 08:53:19 +00:00
}
2017-08-04 14:54:32 +00:00
void Uploader : : partLoaded ( const MTPBool & result , mtpRequestId requestId ) {
2014-05-30 08:53:19 +00:00
QMap < mtpRequestId , int32 > : : iterator j = docRequestsSent . end ( ) ;
QMap < mtpRequestId , QByteArray > : : iterator i = requestsSent . find ( requestId ) ;
if ( i = = requestsSent . cend ( ) ) {
j = docRequestsSent . find ( requestId ) ;
}
if ( i ! = requestsSent . cend ( ) | | j ! = docRequestsSent . cend ( ) ) {
2015-10-29 00:16:52 +00:00
if ( mtpIsFalse ( result ) ) { // failed to upload current file
2014-05-30 08:53:19 +00:00
currentFailed ( ) ;
return ;
} else {
2014-10-30 16:23:44 +00:00
QMap < mtpRequestId , int32 > : : iterator dcIt = dcMap . find ( requestId ) ;
if ( dcIt = = dcMap . cend ( ) ) { // must not happen
currentFailed ( ) ;
return ;
}
int32 dc = dcIt . value ( ) ;
dcMap . erase ( dcIt ) ;
2015-12-24 19:26:28 +00:00
int32 sentPartSize = 0 ;
2014-05-30 08:53:19 +00:00
Queue : : const_iterator k = queue . constFind ( uploading ) ;
if ( i ! = requestsSent . cend ( ) ) {
2015-12-24 19:26:28 +00:00
sentPartSize = i . value ( ) . size ( ) ;
2014-05-30 08:53:19 +00:00
requestsSent . erase ( i ) ;
} else {
2015-12-24 19:26:28 +00:00
sentPartSize = k - > docPartSize ;
2014-05-30 08:53:19 +00:00
docRequestsSent . erase ( j ) ;
}
2015-12-24 19:26:28 +00:00
sentSize - = sentPartSize ;
sentSizes [ dc ] - = sentPartSize ;
2016-11-28 15:45:07 +00:00
if ( k - > type ( ) = = SendMediaType : : Photo ) {
2015-12-24 19:26:28 +00:00
k - > fileSentSize + = sentPartSize ;
PhotoData * photo = App : : photo ( k - > id ( ) ) ;
if ( photo - > uploading ( ) & & k - > file ) {
photo - > uploadingData - > size = k - > file - > partssize ;
photo - > uploadingData - > offset = k - > fileSentSize ;
}
2014-05-30 08:53:19 +00:00
emit photoProgress ( k . key ( ) ) ;
2016-11-28 15:45:07 +00:00
} else if ( k - > type ( ) = = SendMediaType : : File | | k - > type ( ) = = SendMediaType : : Audio ) {
2015-10-27 02:39:02 +00:00
DocumentData * doc = App : : document ( k - > id ( ) ) ;
2015-12-24 19:26:28 +00:00
if ( doc - > uploading ( ) ) {
2014-05-30 08:53:19 +00:00
doc - > uploadOffset = ( k - > docSentParts - docRequestsSent . size ( ) ) * k - > docPartSize ;
if ( doc - > uploadOffset > doc - > size ) {
doc - > uploadOffset = doc - > size ;
}
}
emit documentProgress ( k . key ( ) ) ;
}
}
}
sendNext ( ) ;
}
2017-08-04 14:54:32 +00:00
bool Uploader : : partFailed ( const RPCError & error , mtpRequestId requestId ) {
2016-04-08 10:44:35 +00:00
if ( MTP : : isDefaultHandledError ( error ) ) return false ;
2015-04-04 20:01:34 +00:00
2014-05-30 08:53:19 +00:00
if ( requestsSent . constFind ( requestId ) ! = requestsSent . cend ( ) | | docRequestsSent . constFind ( requestId ) ! = docRequestsSent . cend ( ) ) { // failed to upload current file
currentFailed ( ) ;
}
sendNext ( ) ;
return true ;
}
2017-08-04 14:54:32 +00:00
Uploader : : ~ Uploader ( ) {
clear ( ) ;
}
} // namespace Storage