@ -61,6 +61,40 @@ function bytesFromHex (hexString) {
return bytes ;
return bytes ;
}
}
function bytesToBase64 ( bytes ) {
var mod3 , result = '' ;
for ( var nLen = bytes . length , nUint24 = 0 , nIdx = 0 ; nIdx < nLen ; nIdx ++ ) {
mod3 = nIdx % 3 ;
nUint24 |= bytes [ nIdx ] << ( 16 >>> mod3 & 24 ) ;
if ( mod3 === 2 || nLen - nIdx === 1 ) {
result += String . fromCharCode (
uint6ToBase64 ( nUint24 >>> 18 & 63 ) ,
uint6ToBase64 ( nUint24 >>> 12 & 63 ) ,
uint6ToBase64 ( nUint24 >>> 6 & 63 ) ,
uint6ToBase64 ( nUint24 & 63 )
) ;
nUint24 = 0 ;
}
}
return result . replace ( /A(?=A$|$)/g , '=' ) ;
}
function uint6ToBase64 ( nUint6 ) {
return nUint6 < 26
? nUint6 + 65
: nUint6 < 52
? nUint6 + 71
: nUint6 < 62
? nUint6 - 4
: nUint6 === 62
? 43
: nUint6 === 63
? 47
: 65 ;
}
function bytesCmp ( bytes1 , bytes2 ) {
function bytesCmp ( bytes1 , bytes2 ) {
var len = bytes1 . length ;
var len = bytes1 . length ;
if ( len != bytes2 . length ) {
if ( len != bytes2 . length ) {
@ -120,14 +154,7 @@ function bytesFromBigInt (bigInt, len) {
}
}
function bytesToArrayBuffer ( b ) {
function bytesToArrayBuffer ( b ) {
var len = b . length ,
return ( new Uint8Array ( b ) ) . buffer ;
array = new Uint8Array ( len ) ;
for ( var i = 0 ; i < len ; ++ i ) {
array [ i ] = b [ i ] ;
}
return array . buffer ;
}
}
function bytesFromArrayBuffer ( buffer ) {
function bytesFromArrayBuffer ( buffer ) {
@ -1482,14 +1509,21 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
this . serverSalt = serverSalt ;
this . serverSalt = serverSalt ;
// if (1 == dcID) {
// var self = this;
// (function () {
// dLog('update server salt');
// self.serverSalt = [0,0,0,0,0,0,0,0];
// setTimeout(arguments.callee, nextRandomInt(2000, 12345));
// })();
// }
this . sessionID = new Array ( 8 ) ;
this . sessionID = new Array ( 8 ) ;
MtpSecureRandom . nextBytes ( this . sessionID ) ;
MtpSecureRandom . nextBytes ( this . sessionID ) ;
if ( true ) {
if ( false ) {
this . sessionID [ 0 ] = 0xA ;
this . sessionID [ 0 ] = 0xAB ;
this . sessionID [ 1 ] = 0xB ;
this . sessionID [ 1 ] = 0xCD ;
this . sessionID [ 3 ] = 0xC ;
this . sessionID [ 4 ] = 0xD ;
}
}
this . seqNo = 0 ;
this . seqNo = 0 ;
@ -1594,7 +1628,14 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
if ( this . longPollPending && ( new Date ( ) . getTime ( ) ) < this . longPollPending ) {
if ( this . longPollPending && ( new Date ( ) . getTime ( ) ) < this . longPollPending ) {
return false ;
return false ;
}
}
this . sendLongPoll ( ) ;
var self = this ;
AppConfigManager . get ( 'dc' ) . then ( function ( baseDcID ) {
if ( baseDcID != self . dcID && self . cleanupSent ( ) ) {
// console.warn('send long-poll for guest DC is delayed', self.dcID);
return ;
}
self . sendLongPoll ( ) ;
} ) ;
} ;
} ;
MtpNetworker . prototype . sendLongPoll = function ( ) {
MtpNetworker . prototype . sendLongPoll = function ( ) {
@ -1741,13 +1782,14 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
}
}
this . pendingAcks = [ ] ;
this . pendingAcks = [ ] ;
var self = this ;
var self = this ;
this . sendEncryptedRequest ( message ) . then ( function ( result ) {
this . sendEncryptedRequest ( message ) . then ( function ( result ) {
self . parseResponse ( result . data ) ;
self . parseResponse ( result . data ) . then ( function ( response ) {
// dLog('Server response', self.dcID, response);
self . processMessage ( response . response , response . messageID , response . sessionID ) ;
if ( noResponseMsgs . length ) {
$timeout ( function ( ) {
angular . forEach ( noResponseMsgs , function ( msgID ) {
angular . forEach ( noResponseMsgs , function ( msgID ) {
if ( self . sentMessages [ msgID ] ) {
if ( self . sentMessages [ msgID ] ) {
var deferred = self . sentMessages [ msgID ] . deferred ;
var deferred = self . sentMessages [ msgID ] . deferred ;
@ -1755,8 +1797,9 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
deferred . resolve ( ) ;
deferred . resolve ( ) ;
}
}
} ) ;
} ) ;
} , 200 ) ;
}
self . cleanupSent ( ) ;
} ) ;
} ) ;
} ) ;
} ;
} ;
@ -1833,7 +1876,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var dataLength = responseBuffer . byteLength - deserializer . getOffset ( ) ;
var dataLength = responseBuffer . byteLength - deserializer . getOffset ( ) ;
var encryptedData = deserializer . fetchRawBytes ( dataLength , 'encrypted_data' ) ;
var encryptedData = deserializer . fetchRawBytes ( dataLength , 'encrypted_data' ) ;
this . getDecryptedMessage ( msgKey , encryptedData ) . then ( function ( dataWithPadding ) {
return this . getDecryptedMessage ( msgKey , encryptedData ) . then ( function ( dataWithPadding ) {
var buffer = bytesToArrayBuffer ( dataWithPadding ) ;
var buffer = bytesToArrayBuffer ( dataWithPadding ) ;
var deserializer = new TLDeserialization ( buffer , { mtproto : true } ) ;
var deserializer = new TLDeserialization ( buffer , { mtproto : true } ) ;
@ -1848,7 +1891,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var offset = deserializer . getOffset ( ) ;
var offset = deserializer . getOffset ( ) ;
MtpSha1Service . hash ( dataWithPadding . slice ( 0 , offset ) ) . then ( function ( dataHashed ) {
return MtpSha1Service . hash ( dataWithPadding . slice ( 0 , offset ) ) . then ( function ( dataHashed ) {
if ( ! bytesCmp ( msgKey , dataHashed . slice ( - 16 ) ) ) {
if ( ! bytesCmp ( msgKey , dataHashed . slice ( - 16 ) ) ) {
throw new Error ( 'server msgKey mismatch' ) ;
throw new Error ( 'server msgKey mismatch' ) ;
}
}
@ -1858,9 +1901,12 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var response = deserializer . fetchObject ( '' , 'INPUT' ) ;
var response = deserializer . fetchObject ( '' , 'INPUT' ) ;
// dLog('Server response', response);
return {
response : response ,
self . processResponse ( response , messageID , sessionID , seqNo ) ;
messageID : messageID ,
sessionID : sessionID ,
seqNo : seqNo
} ;
} ) ;
} ) ;
} ) ;
} ) ;
} ;
} ;
@ -1908,10 +1954,35 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
this . sheduleRequest ( 100 ) ;
this . sheduleRequest ( 100 ) ;
} ;
} ;
MtpNetworker . prototype . processResponse = function ( response , messageID , sessionID , seqNo ) {
MtpNetworker . prototype . cleanupSent = function ( ) {
return this . processMessage ( response , messageID , sessionID ) ;
var self = this ;
var notEmpty = false ;
// dLog('clean start', this.dcID/*, this.sentMessages*/);
angular . forEach ( this . sentMessages , function ( message , msgID ) {
// dLog('clean iter', msgID, message);
if ( message . notContentRelated && self . pendingMessages [ msgID ] === undefined ) {
// dLog('clean notContentRelated', msgID);
delete self . sentMessages [ msgID ] ;
}
else if ( message . container ) {
for ( var i = 0 ; i < message . inner . length ; i ++ ) {
if ( self . sentMessages [ message . inner [ i ] ] !== undefined ) {
// dLog('clean failed, found', msgID, message.inner[i], self.sentMessages[message.inner[i]].seq_no);
notEmpty = true ;
return ;
}
}
// dLog('clean container', msgID);
delete self . sentMessages [ msgID ] ;
} else {
notEmpty = true ;
}
} ) ;
return ! notEmpty ;
} ;
} ;
MtpNetworker . prototype . processMessageAck = function ( messageID ) {
MtpNetworker . prototype . processMessageAck = function ( messageID ) {
var sentMessage = this . sentMessages [ messageID ] ;
var sentMessage = this . sentMessages [ messageID ] ;
if ( sentMessage && ! sentMessage . acked ) {
if ( sentMessage && ! sentMessage . acked ) {
@ -1948,6 +2019,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
break ;
break ;
case 'bad_server_salt' :
case 'bad_server_salt' :
dLog ( 'bad server salt' , message ) ;
var sentMsg = this . sentMessages [ message . bad _msg _id ] ;
var sentMsg = this . sentMessages [ message . bad _msg _id ] ;
if ( ! sentMsg || sentMsg . seq _no != message . bad _msg _seqno ) {
if ( ! sentMsg || sentMsg . seq _no != message . bad _msg _seqno ) {
dLog ( message . bad _msg _id , message . bad _msg _seqno ) ;
dLog ( message . bad _msg _id , message . bad _msg _seqno ) ;
@ -2005,7 +2077,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
}
}
} else {
} else {
if ( deferred ) {
if ( deferred ) {
dLog ( 'rpc response' , message . result ) ;
dLog ( 'rpc response' , message . result . _ ) ;
sentMessage . deferred . resolve ( message . result ) ;
sentMessage . deferred . resolve ( message . result ) ;
}
}
if ( sentMessage . isAPI ) {
if ( sentMessage . isAPI ) {
@ -2077,14 +2149,13 @@ factory('MtpApiManager', function (AppConfigManager, MtpAuthorizer, MtpNetworker
return $q . when ( cachedNetworkers [ dcID ] ) ;
return $q . when ( cachedNetworkers [ dcID ] ) ;
}
}
var deferred = $q . defer ( ) ,
var akk = 'dc' + dcID + '_auth_key' ,
ak = 'dc' + dcID + '_auth_key' ,
ssk = 'dc' + dcID + '_server_salt' ;
ssk = 'dc' + dcID + '_server_salt' ;
AppConfigManager . get ( ak , ssk ) . then ( function ( result ) {
return AppConfigManager . get ( ak k , ssk ) . then ( function ( result ) {
if ( cachedNetworkers [ dcID ] !== undefined ) {
if ( cachedNetworkers [ dcID ] !== undefined ) {
return deferred . resolve ( cachedNetworkers [ dcID ] ) ;
return cachedNetworkers [ dcID ] ;
}
}
var authKeyHex = result [ 0 ] ,
var authKeyHex = result [ 0 ] ,
@ -2094,26 +2165,21 @@ factory('MtpApiManager', function (AppConfigManager, MtpAuthorizer, MtpNetworker
var authKey = bytesFromHex ( authKeyHex ) ;
var authKey = bytesFromHex ( authKeyHex ) ;
var serverSalt = bytesFromHex ( serverSaltHex ) ;
var serverSalt = bytesFromHex ( serverSaltHex ) ;
return deferred . resolve ( cachedNetworkers [ dcID ] = MtpNetworkerFactory . getNetworker ( dcID , authKey , serverSalt ) ) ;
return cachedNetworkers [ dcID ] = MtpNetworkerFactory . getNetworker ( dcID , authKey , serverSalt ) ;
}
}
MtpAuthorizer . auth ( dcID ) . then ( function ( auth ) {
return MtpAuthorizer . auth ( dcID ) . then ( function ( auth ) {
var storeObj = { } ;
var storeObj = { } ;
storeObj [ ak ] = bytesToHex ( auth . authKey ) ;
storeObj [ akk ] = bytesToHex ( auth . authKey ) ;
storeObj [ ssk ] = bytesToHex ( auth . serverSalt ) ;
storeObj [ ssk ] = bytesToHex ( auth . serverSalt ) ;
AppConfigManager . set ( storeObj ) ;
AppConfigManager . set ( storeObj ) ;
deferred . resolve (
return cachedNetworkers [ dcID ] = MtpNetworkerFactory . getNetworker ( dcID , auth . authKey , auth . serverSalt ) ;
cachedNetworkers [ dcID ] = MtpNetworkerFactory . getNetworker ( dcID , auth . authKey , auth . serverSalt )
) ;
} , function ( error ) {
} , function ( error ) {
dLog ( 'Get networker error' , error , error . stack ) ;
dLog ( 'Get networker error' , error , error . stack ) ;
deferred . reject ( error ) ;
return error ;
} ) ;
} ) ;
} ) ;
} ) ;
return deferred . promise ;
} ;
} ;
function mtpInvokeApi ( method , params , options ) {
function mtpInvokeApi ( method , params , options ) {
@ -2189,7 +2255,12 @@ factory('MtpApiManager', function (AppConfigManager, MtpAuthorizer, MtpNetworker
} ) ;
} ) ;
}
}
function getBaseDcID ( ) {
return baseDcID || false ;
}
return {
return {
getBaseDcID : getBaseDcID ,
getUserID : mtpGetUserID ,
getUserID : mtpGetUserID ,
invokeApi : mtpInvokeApi ,
invokeApi : mtpInvokeApi ,
setUserAuth : mtpSetUserAuth ,
setUserAuth : mtpSetUserAuth ,
@ -2205,22 +2276,28 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
var cachedSavePromises = { } ;
var cachedSavePromises = { } ;
var cachedDownloadPromises = { } ;
var cachedDownloadPromises = { } ;
var downloadPull = [ ] ;
var downloadPulls = { } ;
var downloadActive = 0 ;
var downloadActive = 0 ;
var downloadLimit = 5 ;
var downloadLimit = 5 ;
function downloadRequest ( cb , activeDelta ) {
function downloadRequest ( dcID , cb , activeDelta ) {
if ( downloadPulls [ dcID ] === undefined ) {
downloadPulls [ dcID ] = [ ] ;
}
var downloadPull = downloadPulls [ dcID ] ;
var deferred = $q . defer ( ) ;
var deferred = $q . defer ( ) ;
downloadPull . push ( { cb : cb , deferred : deferred , activeDelta : activeDelta } ) ;
downloadPull . push ( { cb : cb , deferred : deferred , activeDelta : activeDelta } ) ;
downloadCheck ( ) ;
downloadCheck ( dcID ) ;
return deferred . promise ;
return deferred . promise ;
} ;
} ;
var index = 0 ;
var index = 0 ;
function downloadCheck ( ) {
function downloadCheck ( dcID ) {
if ( downloadActive >= downloadLimit || ! downloadPull . length ) {
var downloadPull = downloadPulls [ dcID ] ;
if ( downloadActive >= downloadLimit || ! downloadPull || ! downloadPull . length ) {
return false ;
return false ;
}
}
@ -2233,13 +2310,13 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
data . cb ( )
data . cb ( )
. then ( function ( result ) {
. then ( function ( result ) {
downloadActive -= activeDelta ;
downloadActive -= activeDelta ;
downloadCheck ( ) ;
downloadCheck ( dcID ) ;
data . deferred . resolve ( result ) ;
data . deferred . resolve ( result ) ;
} , function ( error ) {
} , function ( error ) {
downloadActive -= activeDelta ;
downloadActive -= activeDelta ;
downloadCheck ( ) ;
downloadCheck ( dcID ) ;
data . deferred . reject ( error ) ;
data . deferred . reject ( error ) ;
} )
} )
@ -2250,10 +2327,14 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
return $q . when ( cachedFS ) ;
return $q . when ( cachedFS ) ;
}
}
var deferred = $q . defer ( ) ;
$window . requestFileSystem = $window . requestFileSystem || $window . webkitRequestFileSystem ;
$window . requestFileSystem = $window . requestFileSystem || $window . webkitRequestFileSystem ;
if ( ! $window . requestFileSystem ) {
return $q . reject ( { type : 'FS_BROWSER_UNSUPPORTED' , description : 'requestFileSystem not present' } ) ;
}
var deferred = $q . defer ( ) ;
$window . requestFileSystem ( $window . TEMPORARY , 5 * 1024 * 1024 , function ( fs ) {
$window . requestFileSystem ( $window . TEMPORARY , 5 * 1024 * 1024 , function ( fs ) {
cachedFS = fs ;
cachedFS = fs ;
deferred . resolve ( ) ;
deferred . resolve ( ) ;
@ -2368,7 +2449,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
} ,
} ,
doDownload = function ( ) {
doDownload = function ( ) {
cachedFS . root . getFile ( fileName , { create : true } , function ( fileEntry ) {
cachedFS . root . getFile ( fileName , { create : true } , function ( fileEntry ) {
var downloadPromise = downloadRequest ( function ( ) {
var downloadPromise = downloadRequest ( location . dc _id , function ( ) {
// dLog('next small promise');
// dLog('next small promise');
return MtpApiManager . invokeApi ( 'upload.getFile' , {
return MtpApiManager . invokeApi ( 'upload.getFile' , {
location : angular . extend ( { } , location , { _ : 'inputFileLocation' } ) ,
location : angular . extend ( { } , location , { _ : 'inputFileLocation' } ) ,
@ -2400,7 +2481,19 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
}
}
} , errorHandler ) ;
} , errorHandler ) ;
} , doDownload ) ;
} , doDownload ) ;
} , function ( error ) {
downloadRequest ( location . dc _id , function ( ) {
// dLog('next small promise');
return MtpApiManager . invokeApi ( 'upload.getFile' , {
location : angular . extend ( { } , location , { _ : 'inputFileLocation' } ) ,
offset : 0 ,
limit : 0
} , { dcID : location . dc _id } ) ;
} ) . then ( function ( result ) {
deferred . resolve ( 'data:image/jpeg;base64,' + bytesToBase64 ( result . bytes ) )
} , errorHandler ) ;
} , errorHandler ) ;
} ) ;
return cachedDownloadPromises [ fileName ] = deferred . promise ;
return cachedDownloadPromises [ fileName ] = deferred . promise ;
}
}
@ -2415,6 +2508,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
}
}
var deferred = $q . defer ( ) ,
var deferred = $q . defer ( ) ,
cacheFileWriter ,
errorHandler = function ( error ) {
errorHandler = function ( error ) {
console . error ( error ) ;
console . error ( error ) ;
// dLog('fail');
// dLog('fail');
@ -2433,7 +2527,7 @@ factory('MtpApiFileManager', function (MtpApiManager, $q, $window) {
for ( var offset = 0 ; offset < size ; offset += limit ) {
for ( var offset = 0 ; offset < size ; offset += limit ) {
writeFileDeferred = $q . defer ( ) ;
writeFileDeferred = $q . defer ( ) ;
( function ( isFinal , offset , writeFileDeferred , writeFilePromise ) {
( function ( isFinal , offset , writeFileDeferred , writeFilePromise ) {
return downloadRequest ( function ( ) {
return downloadRequest ( dcID , function ( ) {
// dLog('next big promise');
// dLog('next big promise');
return MtpApiManager . invokeApi ( 'upload.getFile' , {
return MtpApiManager . invokeApi ( 'upload.getFile' , {
location : location ,
location : location ,