diff --git a/app/js/controllers.js b/app/js/controllers.js index 7420b0c7..0bf741d6 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -467,7 +467,6 @@ angular.module('myApp.controllers', ['myApp.i18n']) peersInDialogs = {}, contactsShown; - MtpApiManager.invokeApi('account.updateStatus', {offline: false}); $scope.$on('dialogs_need_more', function () { // console.log('on need more'); showMoreDialogs(); @@ -629,7 +628,8 @@ angular.module('myApp.controllers', ['myApp.i18n']) if (error.code == 401) { MtpApiManager.logOut()['finally'](function () { - $location.url('/login'); + location.hash = '/login'; + AppRuntimeManager.reload(); }); error.handled = true; } diff --git a/app/js/lib/bin_utils.js b/app/js/lib/bin_utils.js index 47e9e05f..d59b2abe 100644 --- a/app/js/lib/bin_utils.js +++ b/app/js/lib/bin_utils.js @@ -175,6 +175,18 @@ function convertToUint8Array(bytes) { return new Uint8Array(bytes); } +function convertToByteArray(bytes) { + if (Array.isArray(bytes)) { + return bytes; + } + bytes = convertToUint8Array(bytes); + var newBytes = []; + for (var i = 0, len = bytes.length; i < len; i++) { + newBytes.push(bytes[i]); + } + return newBytes; +} + function bytesFromArrayBuffer (buffer) { var len = buffer.byteLength, byteView = new Uint8Array(buffer), diff --git a/app/js/lib/crypto_worker.js b/app/js/lib/crypto_worker.js index 4a3c9ff0..96ec3d30 100644 --- a/app/js/lib/crypto_worker.js +++ b/app/js/lib/crypto_worker.js @@ -46,3 +46,5 @@ onmessage = function (e) { postMessage({taskID: taskID, result: result}); } + +postMessage('ready'); diff --git a/app/js/lib/mtproto.js b/app/js/lib/mtproto.js index 35815532..ebcda8f3 100644 --- a/app/js/lib/mtproto.js +++ b/app/js/lib/mtproto.js @@ -9,29 +9,33 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) .factory('MtpDcConfigurator', function () { var dcOptions = Config.Modes.test + ? (location.protocol == 'https:' ? [ - {id: 1, url: 'http://173.240.5.253'}, - {id: 2, url: 'http://149.154.167.40'}, - {id: 3, url: 'http://174.140.142.5'} + {id: 1, url: 'https://pluto.web.telegram.org/apiw_test1'}, + {id: 2, url: 'https://venus.web.telegram.org/apiw_test1'}, + {id: 3, url: 'https://aurora.web.telegram.org/apiw_test1'} ] + : [ + {id: 1, url: 'http://173.240.5.253/apiw1'}, + {id: 2, url: 'http://149.154.167.40/apiw1'}, + {id: 3, url: 'http://174.140.142.5/apiw1'} + ]) : (location.protocol == 'https:' ? [ - {id: 1, url: 'https://pluto.web.telegram.org'}, - {id: 2, url: 'https://venus.web.telegram.org'}, - {id: 3, url: 'https://aurora.web.telegram.org'}, - {id: 4, url: 'https://vesta.web.telegram.org'}, - {id: 5, url: 'https://flora.web.telegram.org'} + {id: 1, url: 'https://pluto.web.telegram.org/apiw1'}, + {id: 2, url: 'https://venus.web.telegram.org/apiw1'}, + {id: 3, url: 'https://aurora.web.telegram.org/apiw1'}, + {id: 4, url: 'https://vesta.web.telegram.org/apiw1'}, + {id: 5, url: 'https://flora.web.telegram.org/apiw1'} ] : [ - {id: 1, url: 'http://173.240.5.1'}, - {id: 2, url: 'http://149.154.167.51'}, - {id: 3, url: 'http://174.140.142.6'}, - {id: 4, url: 'http://149.154.167.91'}, - {id: 5, url: 'http://149.154.171.5'} + {id: 1, url: 'http://173.240.5.1/apiw1'}, + {id: 2, url: 'http://149.154.167.51/apiw1'}, + {id: 3, url: 'http://174.140.142.6/apiw1'}, + {id: 4, url: 'http://149.154.167.91/apiw1'}, + {id: 5, url: 'http://149.154.171.5/apiw1'} ]); - var sslSubdomains = 'pluto,venus,aurora,vesta,flora'; - var chosenServers = {}; function chooseServer(dcID) { @@ -153,7 +157,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) lastMessageID = messageID; - // console.log('generated msg id', messageID); + // console.log('generated msg id', messageID, timeOffset); return longFromInts(messageID[0], messageID[1]); }; @@ -207,7 +211,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) var requestData = xhrSendBuffer ? resultBuffer : resultArray, requestPromise; try { - requestPromise = $http.post(MtpDcConfigurator.chooseServer(dcID) + '/apiw1', requestData, { + requestPromise = $http.post(MtpDcConfigurator.chooseServer(dcID), requestData, { responseType: 'arraybuffer', transformRequest: null }); @@ -990,13 +994,32 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) currentTime = tsNow(), hasApiCall = false, hasHttpWait = false, + lengthOverflow = false, + singlesCount = 0, self = this; angular.forEach(this.pendingMessages, function (value, messageID) { if (!value || value >= currentTime) { if (message = self.sentMessages[messageID]) { + var messageByteLength = (message.body.byteLength || message.body.length) + 32; + if (!message.notContentRelated && + lengthOverflow) { + return; + } + if (!message.notContentRelated && + messagesByteLen && + messagesByteLen + messageByteLength > 655360) { // 640 Kb + lengthOverflow = true; + return; + } + if (message.singleInRequest) { + singlesCount++; + if (singlesCount > 1) { + return; + } + } messages.push(message); - messagesByteLen += (message.body.byteLength || message.body.length) + 32; + messagesByteLen += messageByteLength; if (message.isAPI) { hasApiCall = true; } @@ -1113,6 +1136,10 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) self.toggleOffline(true); }); + + if (lengthOverflow || singlesCount > 1) { + this.sheduleRequest() + } }; MtpNetworker.prototype.getEncryptedMessage = function (bytes) { @@ -1174,7 +1201,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) responseType: 'arraybuffer', transformRequest: null }); - requestPromise = $http.post(MtpDcConfigurator.chooseServer(self.dcID) + '/apiw1', requestData, options); + requestPromise = $http.post(MtpDcConfigurator.chooseServer(self.dcID), requestData, options); } catch (e) { requestPromise = $q.reject(e); } @@ -1237,7 +1264,6 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) console.warn(msgKey, bytesFromArrayBuffer(dataHash)); throw new Error('server msgKey mismatch'); } - // console.log(dT(), 'after hash check'); var buffer = bytesToArrayBuffer(messageBody); var deserializerOptions = { @@ -1275,9 +1301,7 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) } }; var deserializer = new TLDeserialization(buffer, deserializerOptions); - var response = deserializer.fetchObject('', 'INPUT'); - // console.log(dT(), 'after fetch'); return { response: response, @@ -1323,10 +1347,6 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) this.nextReq = nextReq; }; - MtpNetworker.prototype.onSessionCreate = function (sessionID, messageID) { - // console.log(dT(), 'New session created', bytesToHex(sessionID)); - }; - MtpNetworker.prototype.ackMessage = function (msgID) { // console.log('ack message', msgID); this.pendingAcks.push(msgID); @@ -1447,7 +1467,13 @@ angular.module('izhukov.mtproto', ['izhukov.utils']) this.processMessageAck(message.first_msg_id); this.applyServerSalt(message.server_salt); - this.onSessionCreate(sessionID, messageID); + + var self = this; + Storage.get('dc').then(function (baseDcID) { + if (baseDcID == self.dcID && !self.upload && updatesProcessor) { + updatesProcessor(message); + } + }); break; case 'msgs_ack': diff --git a/app/js/lib/mtproto_wrapper.js b/app/js/lib/mtproto_wrapper.js index 951e8185..50a3fe7b 100644 --- a/app/js/lib/mtproto_wrapper.js +++ b/app/js/lib/mtproto_wrapper.js @@ -54,7 +54,9 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) } if (cache[dcID] !== undefined) { - return $q.when(cache[dcID]); + return {then: function (cb) { + cb(cache[dcID]); + }}; } var akk = 'dc' + dcID + '_auth_key', @@ -120,24 +122,17 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) dcID, networkerPromise; - if (dcID = options.dcID) { - networkerPromise = mtpGetNetworker(dcID, options); - } else { - networkerPromise = Storage.get('dc').then(function (baseDcID) { - return mtpGetNetworker(dcID = baseDcID || 2, options); - }); + var cachedNetworker; + var stack = (new Error()).stack; + if (!stack) { + try {window.unexistingFunction();} catch (e) { + stack = e.stack || ''; + } } - - var cachedNetworker, - stack = false; - - networkerPromise.then(function (networker) { + var performRequest = function (networker) { return (cachedNetworker = networker).wrapApiCall(method, params, options).then( function (result) { deferred.resolve(result); - // $timeout(function () { - // deferred.resolve(result); - // }, 1000); }, function (error) { console.error(dT(), 'Error', error.code, error.type, baseDcID, dcID); @@ -168,12 +163,8 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) cachedExportPromise[dcID].then(function () { (cachedNetworker = networker).wrapApiCall(method, params, options).then(function (result) { deferred.resolve(result); - }, function (error) { - rejectPromise(error); - }); - }, function (error) { - rejectPromise(error); - }); + }, rejectPromise); + }, rejectPromise); } else if (error.code == 303) { var newDcID = error.type.match(/^(PHONE_MIGRATE_|NETWORK_MIGRATE_|USER_MIGRATE_)(\d+)/)[2]; @@ -187,9 +178,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) mtpGetNetworker(newDcID, options).then(function (networker) { networker.wrapApiCall(method, params, options).then(function (result) { deferred.resolve(result); - }, function (error) { - rejectPromise(error); - }); + }, rejectPromise); }); } } @@ -197,14 +186,14 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) rejectPromise(error); } }); - }, function (error) { - rejectPromise(error); - }); + }; - if (!(stack = (stack || (new Error()).stack))) { - try {window.unexistingFunction();} catch (e) { - stack = e.stack || ''; - } + if (dcID = (options.dcID || baseDcID)) { + mtpGetNetworker(dcID, options).then(performRequest, rejectPromise); + } else { + Storage.get('dc').then(function (baseDcID) { + mtpGetNetworker(dcID = baseDcID || 2, options).then(performRequest, rejectPromise); + }); } return deferred.promise; @@ -234,14 +223,12 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) var cachedFs = false; var cachedFsPromise = false; - var apiUploadPromise = $q.when(); var cachedSavePromises = {}; var cachedDownloadPromises = {}; var cachedDownloads = {}; var downloadPulls = {}; var downloadActives = {}; - var downloadLimit = 5; function downloadRequest(dcID, cb, activeDelta) { if (downloadPulls[dcID] === undefined) { @@ -251,7 +238,9 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) var downloadPull = downloadPulls[dcID]; var deferred = $q.defer(); downloadPull.push({cb: cb, deferred: deferred, activeDelta: activeDelta}); - downloadCheck(dcID); + setZeroTimeout(function () { + downloadCheck(dcID); + }); return deferred.promise; }; @@ -260,6 +249,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) function downloadCheck(dcID) { var downloadPull = downloadPulls[dcID]; + var downloadLimit = dcID == 'upload' ? 17 : 5; if (downloadActives[dcID] >= downloadLimit || !downloadPull || !downloadPull.length) { return false; @@ -514,15 +504,23 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) } function uploadFile (file) { - var fileSize = file.size, - // partSize = fileSize > 102400 ? 65536 : 4096, - // partSize = fileSize > 102400 ? 524288 : 4096, - partSize = fileSize > 102400 ? 524288 : 32768, - isBigFile = fileSize >= 10485760, - totalParts = Math.ceil(fileSize / partSize), - canceled = false, - resolved = false, - doneParts = 0; + var fileSize = file.size, + isBigFile = fileSize >= 10485760, + canceled = false, + resolved = false, + doneParts = 0, + partSize = 262144, // 256 Kb + activeDelta = 2; + + if (fileSize > 67108864) { + partSize = 524288; + activeDelta = 4; + } + else if (fileSize < 102400) { + partSize = 32768; + activeDelta = 1; + } + var totalParts = Math.ceil(fileSize / partSize); if (totalParts > 1500) { return $q.reject({type: 'FILE_TOO_BIG'}); @@ -533,6 +531,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) errorHandler = function (error) { // console.error('Up Error', error); deferred.reject(error); + canceled = true; errorHandler = angular.noop; }, part = 0, @@ -546,35 +545,34 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) }; - var fileReadPromise = $q.when(); - for (offset = 0; offset < fileSize; offset += partSize) { (function (offset, part) { - fileReadPromise = fileReadPromise.then(function () { - var fileReadDeferred = $q.defer(); + downloadRequest('upload', function () { + var uploadDeferred = $q.defer(); var reader = new FileReader(); var blob = file.slice(offset, offset + partSize); reader.onloadend = function (e) { - if (canceled || e.target.readyState != FileReader.DONE) { + if (canceled) { + uploadDeferred.reject(); return; } - var apiCurPromise = apiUploadPromise = apiUploadPromise.then(function () { - return MtpApiManager.invokeApi(isBigFile ? 'upload.saveBigFilePart' : 'upload.saveFilePart', { - file_id: fileID, - file_part: part, - file_total_parts: totalParts, - bytes: e.target.result - }, { - startMaxLength: partSize + 256, - fileUpload: true - }); - }, errorHandler); - - apiCurPromise.then(function (result) { + if (e.target.readyState != FileReader.DONE) { + return; + } + MtpApiManager.invokeApi(isBigFile ? 'upload.saveBigFilePart' : 'upload.saveFilePart', { + file_id: fileID, + file_part: part, + file_total_parts: totalParts, + bytes: e.target.result + }, { + startMaxLength: partSize + 256, + fileUpload: true, + singleInRequest: true + }).then(function (result) { doneParts++; - fileReadDeferred.resolve(); + uploadDeferred.resolve(); if (doneParts >= totalParts) { deferred.resolve(resultInputFile); resolved = true; @@ -587,8 +585,8 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) reader.readAsArrayBuffer(blob); - return fileReadDeferred.promise; - }); + return uploadDeferred.promise; + }, activeDelta); })(offset, part++); } diff --git a/app/js/lib/ng_utils.js b/app/js/lib/ng_utils.js index 61e17733..d5eb5702 100644 --- a/app/js/lib/ng_utils.js +++ b/app/js/lib/ng_utils.js @@ -484,12 +484,12 @@ angular.module('izhukov.utils', []) .service('CryptoWorker', function ($timeout, $q) { - var worker = window.Worker && new Worker('js/lib/crypto_worker.js') || false, + var webWorker = false, + naClEmbed = false, taskID = 0, awaiting = {}, webCrypto = window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle) || window.msCrypto && window.msCrypto.subtle, useSha1Crypto = webCrypto && webCrypto.digest !== undefined, - aesNaClEmbed = false, finalizeTask = function (taskID, result) { var deferred = awaiting[taskID]; if (deferred !== undefined) { @@ -502,7 +502,7 @@ angular.module('izhukov.utils', []) if (navigator.mimeTypes['application/x-pnacl'] !== undefined) { var listener = $('
').appendTo($('body'))[0]; listener.addEventListener('load', function (e) { - aesNaClEmbed = listener.firstChild; + naClEmbed = listener.firstChild; console.log(dT(), 'NaCl ready'); }, true); listener.addEventListener('message', function (e) { @@ -513,13 +513,18 @@ angular.module('izhukov.utils', []) }, true); } - if (worker) { - worker.onmessage = function (e) { - finalizeTask(e.data.taskID, e.data.result); + if (window.Worker) { + var tmpWorker = new Worker('js/lib/crypto_worker.js'); + tmpWorker.onmessage = function (e) { + if (!webWorker) { + webWorker = tmpWorker; + } else { + finalizeTask(e.data.taskID, e.data.result); + } }; - worker.onerror = function(error) { + tmpWorker.onerror = function(error) { console.error('CW error', error, error.stack); - worker = false; + webWorker = false; }; } @@ -531,7 +536,7 @@ angular.module('izhukov.utils', []) params.task = task; params.taskID = taskID; - (embed || worker).postMessage(params); + (embed || webWorker).postMessage(params); taskID++; @@ -561,34 +566,35 @@ angular.module('izhukov.utils', []) }); }, aesEncrypt: function (bytes, keyBytes, ivBytes) { - if (aesNaClEmbed) { + if (naClEmbed) { return performTaskWorker('aes-encrypt', { bytes: addPadding(convertToArrayBuffer(bytes)), keyBytes: convertToArrayBuffer(keyBytes), ivBytes: convertToArrayBuffer(ivBytes) - }, aesNaClEmbed); + }, naClEmbed); } return $timeout(function () { return convertToArrayBuffer(aesEncryptSync(bytes, keyBytes, ivBytes)); }); }, aesDecrypt: function (encryptedBytes, keyBytes, ivBytes) { - if (aesNaClEmbed) { + if (naClEmbed) { return performTaskWorker('aes-decrypt', { encryptedBytes: addPadding(convertToArrayBuffer(encryptedBytes)), keyBytes: convertToArrayBuffer(keyBytes), ivBytes: convertToArrayBuffer(ivBytes) - }, aesNaClEmbed); + }, naClEmbed); } return $timeout(function () { return convertToArrayBuffer(aesDecryptSync(encryptedBytes, keyBytes, ivBytes)); }); }, factorize: function (bytes) { - if (aesNaClEmbed && bytes.length <= 8) { - return performTaskWorker('factorize', {bytes: bytes}, aesNaClEmbed); + bytes = convertToByteArray(bytes); + if (naClEmbed && bytes.length <= 8) { + return performTaskWorker('factorize', {bytes: bytes}, naClEmbed); } - if (worker) { + if (webWorker) { return performTaskWorker('factorize', {bytes: bytes}); } return $timeout(function () { @@ -596,7 +602,7 @@ angular.module('izhukov.utils', []) }); }, modPow: function (x, y, m) { - if (worker) { + if (webWorker) { return performTaskWorker('mod-pow', { x: x, y: y, diff --git a/app/js/lib/polyfill.js b/app/js/lib/polyfill.js index 30e9e8ab..8d28947e 100644 --- a/app/js/lib/polyfill.js +++ b/app/js/lib/polyfill.js @@ -128,4 +128,4 @@ if (!Function.prototype.bind) { }; global.setZeroTimeout = setZeroTimeout; -})(this); \ No newline at end of file +})(this); diff --git a/app/js/lib/tl_utils.js b/app/js/lib/tl_utils.js index 3c92adb0..5b6ed2b4 100644 --- a/app/js/lib/tl_utils.js +++ b/app/js/lib/tl_utils.js @@ -148,6 +148,9 @@ TLSerialization.prototype.storeString = function (s, field) { TLSerialization.prototype.storeBytes = function (bytes, field) { + if (bytes instanceof ArrayBuffer) { + bytes = new Uint8Array(bytes); + } this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':bytes'); var len = bytes.byteLength || bytes.length; @@ -160,6 +163,7 @@ TLSerialization.prototype.storeBytes = function (bytes, field) { this.byteView[this.offset++] = (len & 0xFF00) >> 8; this.byteView[this.offset++] = (len & 0xFF0000) >> 16; } + this.byteView.set(bytes, this.offset); this.offset += len; diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js index a86851bc..c56a68b2 100644 --- a/app/js/lib/utils.js +++ b/app/js/lib/utils.js @@ -63,7 +63,7 @@ function onContentLoaded (cb) { }; function tsNow (seconds) { - var t = +new Date(); + var t = +new Date() + (window.tsOffset || 0); return seconds ? Math.floor(t / 1000) : t; } diff --git a/app/js/services.js b/app/js/services.js index fc3de5fd..04b56d53 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -401,7 +401,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) phones: [] }; - if (this.result.tel !== undefined) { + if (this.result.tel != undefined) { for (var i = 0; i < this.result.tel.length; i++) { contact.phones.push(this.result.tel[i].value); } @@ -741,7 +741,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) offset: offset || 0, limit: limit || 0, max_id: maxID || 0 - }).then(function (historyResult) { + }, {noErrorBox: true}).then(function (historyResult) { AppUsersManager.saveApiUsers(historyResult.users); AppChatsManager.saveApiChats(historyResult.chats); saveMessages(historyResult.messages); @@ -2810,6 +2810,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) switch (updateMessage._) { case 'updatesTooLong': + case 'new_session_created': forceGetDifference(); break; @@ -3003,11 +3004,13 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) function attach () { MtpNetworkerFactory.setUpdatesProcessor(processUpdateMessage); - MtpApiManager.invokeApi('updates.getState', {noErrorBox: true}).then(function (stateResult) { + MtpApiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then(function (stateResult) { curState.seq = stateResult.seq; curState.pts = stateResult.pts; curState.date = stateResult.date; - isSynchronizing = false; + setTimeout(function () { + isSynchronizing = false; + }, 1000); }) }