diff --git a/.travis.yml b/.travis.yml index 8ee61e5..5ad1f98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: node_js node_js: - "0.10" after_script: - - npm run zuul - npm run coveralls env: global: diff --git a/gulpfile.js b/gulpfile.js index 6396da3..f58f9c0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,44 +4,26 @@ var mocha = require('gulp-mocha'); var docco = require('gulp-docco'); var del = require('del'); -gulp.task('docs-api', function () { +gulp.task('docs', function () { del(['./docs/api'], function() { - gulp.src('telegram.link.js') + gulp.src('./lib/**') .pipe(docco({'layout': 'linear'})) - .pipe(gulp.dest('./docs/api')) + .pipe(gulp.dest('./docs/api')); }); }); - -gulp.task('quality-lib', function () { - return gulp.src('node_modules/lib/**/*.js') - .pipe(jshint()) - .pipe(jshint.reporter('default')) -}); -gulp.task('quality-api', function () { - return gulp.src('telegram.link.js') +gulp.task('quality', function () { + return gulp.src('../lib/**') .pipe(jshint()) - .pipe(jshint.reporter('default')) + .pipe(jshint.reporter('default')); }); - -gulp.task('test-lib', function () { - return gulp.src('./test/lib/**/*.js') - .pipe(mocha({reporter: 'mocha-better-spec-reporter', timeout: '10s'})); -}); -gulp.task('test-api', function () { +gulp.task('test', function () { return gulp.src('./test/*.js') - .pipe(mocha({reporter: 'mocha-better-spec-reporter', timeout: '10s'})); + .pipe(mocha({reporter: 'mocha-better-spec-reporter', timeout: '20s'})); }); -gulp.task('cov-test-lib', function () { - return gulp.src('./test/lib/**/*.js') - .pipe(mocha({reporter: 'mocha-lcov-reporter', timeout: '120s'})); -}); -gulp.task('cov-test-api', function () { +gulp.task('cover', function () { return gulp.src('./test/*.js') .pipe(mocha({reporter: 'mocha-lcov-reporter', timeout: '120s'})); }); -gulp.task('default', ['quality', 'test-lib']); -gulp.task('quality', ['quality-lib', 'quality-api']); -gulp.task('test', ['quality', 'test-lib', 'test-api']); -gulp.task('cover', ['cov-test-lib', 'cov-test-api']); +gulp.task('default', ['quality', 'test']); \ No newline at end of file diff --git a/node_modules/lib/static.js b/lib/static.js similarity index 98% rename from node_modules/lib/static.js rename to lib/static.js index 7f6d1c0..38bb6f1 100644 --- a/node_modules/lib/static.js +++ b/lib/static.js @@ -24,7 +24,7 @@ exports.telegram = { }; // Register `Telegram Messanger` public keys -var PublicKey = require('./crypto-util/public-key'); +var PublicKey = require('telegram-mt-node').security.PublicKey; PublicKey.addKey({ fingerprint: '0x9a996a1db11c729b', modulus: 'c6aeda78b02a251db4b6441031f467fa871faed32526c436524b1fb3b5dca28efb8c089dd1b46d92c895993d87108254951c5f001a0f055f3063dcd14d431a300eb9e29517e359a1c9537e5e87ab1b116faecf5d17546ebc21db234d9d336a693efcb2b6fbcca1e7d1a0be414dca408a11609b9c4269a920b09fed1f9a1597be02761430f09e4bc48fcafbe289054c99dba51b6b5eb7d9c3a2ab4e490545b4676bd620e93804bcac93bf94f73f92c729ca899477ff17625ef14a934d51dc11d5f8650a3364586b3a52fcff2fedec8a8406cac4e751705a472e55707e3c8cd5594342b119c6c3293532d85dbe9271ed54a2fd18b4dc79c04a30951107d5639397', diff --git a/telegram.link.js b/lib/telegram.link.js similarity index 76% rename from telegram.link.js rename to lib/telegram.link.js index e4d7402..1bbc0f0 100644 --- a/telegram.link.js +++ b/lib/telegram.link.js @@ -10,33 +10,33 @@ // with `TELEGRAM MESSANGER` // Print library version -console.log(require('lib/static').signature); -console.log(('v.%s', require('./package.json').version)); +console.log(require('./static').signature); +console.log('v.%s', require('../package.json').version); // Register the project name on the logging sys var getLogger = require('get-log'); -getLogger.PROJECT_NAME = require('./package.json').name; +getLogger.PROJECT_NAME = require('../package.json').name; // Export the class module.exports = exports = TelegramLink; // Import dependencies -var crypto = require('lib/crypto-util'); -var TcpConnection = require('lib/net').TcpConnection; -var HttpConnection = require('lib/net').HttpConnection; var TypeObject = require('telegram-tl-node').TypeObject; -var mtproto = require('lib/mtproto'); +var mtproto = require('telegram-mt-node'); var flow = require('get-flow'); +var TcpConnection = mtproto.net.TcpConnection; +var HttpConnection = mtproto.net.HttpConnection; +var utility = mtproto.utility; +var security = mtproto.security; + // The constructor requires a primary telegram DataCenter address as argument -function TelegramLink(primaryDC) { -// this._connection = new TcpConnection(primaryDC); - this._connection = new HttpConnection({ - proxyHost: process.env.PROXY_HOST, - proxyPort: process.env.PROXY_PORT, - host: primaryDC.host, - port: primaryDC.port - }); +function TelegramLink(primaryDC, connectionType) { + if (connectionType && 'TCP' === connectionType) { + this._connection = new TcpConnection(primaryDC); + } else { + this._connection = new HttpConnection(primaryDC); + } } // The method creates a connection to the DataCenter, @@ -72,8 +72,8 @@ TelegramLink.prototype.authorization = function (callback) { // Request a PQ pair number function requestPQ(callback) { // Create a nonce for the client - var clientNonce = crypto.createNonce(16); - mtproto.req_pq({ + var clientNonce = utility.createNonce(16); + mtproto.service.req_pq({ props: { nonce: clientNonce }, @@ -90,7 +90,7 @@ TelegramLink.prototype.authorization = function (callback) { // Find the P and Q prime numbers function findPAndQ(resPQ) { - var pqFinder = new crypto.PQFinder(resPQ.pq); + var pqFinder = new security.PQFinder(resPQ.pq); if (logger.isDebugEnabled()) logger.debug('Start finding P and Q, with PQ = %s', pqFinder.getPQPairNumber()); var pq = pqFinder.findPQ(); if (logger.isDebugEnabled()) logger.debug('Found P = %s and Q = %s', pq[0], pq[1]); @@ -108,7 +108,7 @@ TelegramLink.prototype.authorization = function (callback) { for (var i = 0; i < fingerprints.length; i++) { var fingerprint = fingerprints[i]; if (logger.isDebugEnabled()) logger.debug('Searching fingerprint %s in store', fingerprint); - var publicKey = crypto.PublicKey.retrieveKey(fingerprint); + var publicKey = security.PublicKey.retrieveKey(fingerprint); if (publicKey) { if (logger.isDebugEnabled()) logger.debug('Fingerprint %s found in keyStore.', fingerprint); obj.fingerprint = fingerprint; @@ -122,8 +122,8 @@ TelegramLink.prototype.authorization = function (callback) { // Create the pq_inner_data buffer function createPQInnerData(obj) { var resPQ = obj.resPQ; - var newNonce = crypto.createNonce(32); - var pqInnerData = new mtproto.P_q_inner_data({props: { + var newNonce = utility.createNonce(32); + var pqInnerData = new mtproto.type.P_q_inner_data({props: { pq: resPQ.pq, p: obj.pBuffer, q: obj.qBuffer, @@ -139,21 +139,20 @@ TelegramLink.prototype.authorization = function (callback) { // Encrypt the pq_inner_data with RSA function encryptPQInnerDataWithRSA(obj) { // Create the data with hash to be encrypt - var hash = crypto.createSHA1Hash(obj.pqInnerData); + var hash = utility.createSHA1Hash(obj.pqInnerData); var dataWithHash = Buffer.concat([hash, obj.pqInnerData]); - if (logger.isDebugEnabled()) { - logger.debug('Data to be encrypted contains: hash(%s), pqInnerData(%s), total length %s', - hash.length, obj.pqInnerData.length, dataWithHash.length); - } + if (logger.isDebugEnabled()) logger.debug('Data to be encrypted contains: ' + + 'hash(%s), pqInnerData(%s), total length %s', + hash.length, obj.pqInnerData.length, dataWithHash.length); // Encrypt data with RSA - obj.encryptedData = crypto.rsaEncrypt({message: dataWithHash, key: obj.publicKey}); + obj.encryptedData = security.cipher.rsaEncrypt(dataWithHash, obj.publicKey); return obj; } // Request server DH parameters function requestDHParams(callback, obj) { var resPQ = obj.resPQ; - mtproto.req_DH_params({ + mtproto.service.req_DH_params({ props: { nonce: resPQ.nonce, server_nonce: resPQ.server_nonce, @@ -168,11 +167,11 @@ TelegramLink.prototype.authorization = function (callback) { logger.error(ex); if (callback) callback(ex); } else { - if (serverDHParams.typeName == 'mtproto.Server_DH_params_ok') { + if (serverDHParams.typeName == 'mtproto.type.Server_DH_params_ok') { if (logger.isDebugEnabled()) logger.debug('\'Server_DH_params_ok\' received from Telegram.'); obj.serverDHParams = serverDHParams; callback(null, obj, duration); - } else if (serverDHParams.typeName == 'mtproto.Server_DH_params_ko') { + } else if (serverDHParams.typeName == 'mtproto.type.Server_DH_params_ko') { logger.warn('\'Server_DH_params_ko\' received from Telegram!'); callback(createError(JSON.stringify(serverDHParams), 'EDHPARAMKO')); } else { @@ -189,23 +188,17 @@ TelegramLink.prototype.authorization = function (callback) { function decryptDHParams(obj, duration) { var newNonce = TypeObject.stringValue2Buffer(obj.newNonce, 32); var serverNonce = TypeObject.stringValue2Buffer(obj.resPQ.server_nonce, 16); - if (logger.isDebugEnabled()) { - logger.debug('newNonce = %s, serverNonce = %s', newNonce.toString('hex'), serverNonce.toString('hex')); - } - var hashNS = crypto.createSHA1Hash([newNonce, serverNonce]); - var hashSN = crypto.createSHA1Hash([serverNonce, newNonce]); - var hashNN = crypto.createSHA1Hash([newNonce, newNonce]); - if (logger.isDebugEnabled()) { - logger.debug('hashNS = %s, hashSN = %s, hashNN = %s', hashNS.toString('hex'), hashSN.toString('hex'), hashNN.toString('hex')); - } + if (logger.isDebugEnabled()) logger.debug('newNonce = %s, serverNonce = %s', newNonce.toString('hex'), serverNonce.toString('hex')); + var hashNS = utility.createSHA1Hash([newNonce, serverNonce]); + var hashSN = utility.createSHA1Hash([serverNonce, newNonce]); + var hashNN = utility.createSHA1Hash([newNonce, newNonce]); + if (logger.isDebugEnabled()) logger.debug('hashNS = %s, hashSN = %s, hashNN = %s', hashNS.toString('hex'), hashSN.toString('hex'), hashNN.toString('hex')); // Create the AES key var aesKey = Buffer.concat([hashNS, hashSN.slice(0, 12)]); var aesIv = Buffer.concat([Buffer.concat([hashSN.slice(12), hashNN]), newNonce.slice(0, 4)]); - if (logger.isDebugEnabled()) { - logger.debug('aesKey = %s, aesIv = %s', aesKey.toString('hex'), aesIv.toString('hex')); - } + if (logger.isDebugEnabled()) logger.debug('aesKey = %s, aesIv = %s', aesKey.toString('hex'), aesIv.toString('hex')); // Decrypt the message - var answerWithHash = crypto.aesDecrypt( + var answerWithHash = security.cipher.aesDecrypt( obj.serverDHParams.encrypted_answer, aesKey, aesIv @@ -216,7 +209,7 @@ TelegramLink.prototype.authorization = function (callback) { if (logger.isDebugEnabled()) logger.debug('answerWithHash(%s) = %s', answerWithHash.length, answerWithHash.toString('hex')); var answer = answerWithHash.slice(20, 564 + 20); if (logger.isDebugEnabled()) logger.debug('answer(%s) = %s', answer.length, answer.toString('hex')); - var serverDHInnerData = new mtproto.Server_DH_inner_data({ + var serverDHInnerData = new mtproto.type.Server_DH_inner_data({ buffer: answer }).deserialize(); if (logger.isDebugEnabled()) logger.debug('serverDHInnerData = %s obtained in %sms', JSON.stringify(serverDHInnerData), duration); @@ -228,7 +221,7 @@ TelegramLink.prototype.authorization = function (callback) { throw createError('ServerNonce mismatch %s != %s', obj.serverDHParams.server_nonce, serverDHInnerData.server_nonce); } // Synch the local time with the server time - crypto.timeSynchronization(serverDHInnerData.server_time * 1000, duration); + mtproto.time.timeSynchronization(serverDHInnerData.server_time * 1000, duration); obj.serverDHInnerData = serverDHInnerData; return obj; } @@ -240,12 +233,12 @@ TelegramLink.prototype.authorization = function (callback) { if (logger.isDebugEnabled()) logger.debug('Start calculating g_b'); // Calculate g_b var g = obj.serverDHInnerData.g; - var b = crypto.createNonce(256); + var b = utility.createNonce(256); var dhPrime = obj.serverDHInnerData.dh_prime; - var gb = crypto.modPow(g, b, dhPrime); + var gb = utility.modPow(g, b, dhPrime); if (logger.isDebugEnabled()) logger.debug('g_b(%s) = %s', gb.length, gb.toString('hex')); // Create client DH inner data - obj.clientDHInnerData = new mtproto.Client_DH_inner_data({props: { + obj.clientDHInnerData = new mtproto.type.Client_DH_inner_data({props: { nonce: obj.resPQ.nonce, server_nonce: obj.resPQ.server_nonce, retry_id: retryCount, @@ -256,27 +249,24 @@ TelegramLink.prototype.authorization = function (callback) { // Encrypt Client DH inner data function encryptClientDHInnerDataWithAES(obj) { - var hash = crypto.createSHA1Hash(obj.clientDHInnerData); + var hash = utility.createSHA1Hash(obj.clientDHInnerData); var dataWithHash = Buffer.concat([hash, obj.clientDHInnerData]); - if (logger.isDebugEnabled()) { - logger.debug('Data to be encrypted contains: hash(%s), clientDHInnerData(%s), total length %s', - hash.length, obj.clientDHInnerData.length, dataWithHash.length); - } - obj.encryptClientDHInnerData = crypto.aesEncrypt( + if (logger.isDebugEnabled()) logger.debug('Data to be encrypted contains: ' + + 'hash(%s), clientDHInnerData(%s), total length %s', + hash.length, obj.clientDHInnerData.length, dataWithHash.length); + obj.encryptClientDHInnerData = security.cipher.aesEncrypt( dataWithHash, obj.aes.key, obj.aes.iv ); - if (logger.isDebugEnabled()) { - logger.debug('encryptClientDHInnerData(%s) = %s', - obj.encryptClientDHInnerData.length, obj.encryptClientDHInnerData.toString('hex')); - } + if (logger.isDebugEnabled()) logger.debug('encryptClientDHInnerData(%s) = %s', + obj.encryptClientDHInnerData.length, obj.encryptClientDHInnerData.toString('hex')); return obj; } // Set client DH parameters function setClientDHParams(callback, obj) { - mtproto.set_client_DH_params({ + mtproto.service.set_client_DH_params({ props: { nonce: obj.resPQ.nonce, server_nonce: obj.resPQ.server_nonce, @@ -288,14 +278,14 @@ TelegramLink.prototype.authorization = function (callback) { logger.error(ex); if (callback) callback(ex); } else { - if (setClientDHParamsAnswer.typeName == 'mtproto.Dh_gen_ok') { + if (setClientDHParamsAnswer.typeName == 'mtproto.type.Dh_gen_ok') { if (logger.isDebugEnabled()) logger.debug('\'Dh_gen_ok\' received from Telegram.'); obj.setClientDHParamsAnswer = setClientDHParamsAnswer; callback(null, obj, duration); - } else if (setClientDHParamsAnswer.typeName == 'mtproto.Dh_gen_retry') { + } else if (setClientDHParamsAnswer.typeName == 'mtproto.type.Dh_gen_retry') { logger.warn('\'Dh_gen_retry\' received from Telegram!'); callback(createError(JSON.stringify(serverDHParams), 'EDHPARAMRETRY')); - } else if (setClientDHParamsAnswer.typeName == 'mtproto.Dh_gen_fail') { + } else if (setClientDHParamsAnswer.typeName == 'mtproto.type.Dh_gen_fail') { logger.warn('\'Dh_gen_fail\' received from Telegram!'); callback(createError(JSON.stringify(serverDHParams), 'EDHPARAMFAIL')); } else { diff --git a/node_modules/lib/crypto-util/index.js b/node_modules/lib/crypto-util/index.js deleted file mode 100644 index 1c6a051..0000000 --- a/node_modules/lib/crypto-util/index.js +++ /dev/null @@ -1,207 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -// Export the classes -exports.PublicKey = require('./public-key'); -exports.PQFinder = require('./pq-finder'); - -// Export functions -exports.createSHA1Hash = createSHA1Hash; -exports.createRandomBuffer = createRandomBuffer; -exports.createNonce = createNonce; -exports.rsaEncrypt = rsaEncrypt; -exports.aesDecrypt = aesDecrypt; -exports.aesEncrypt = aesEncrypt; -exports.modPow = modPow; -exports.createMessageId = createMessageId; -exports.timeSynchronization = timeSynchronization; - -// Import dependencies -var crypto = require('crypto'); -var util = require('util'); -var BigInteger = require('jsbn'); -var CryptoJS = require("node-cryptojs-aes").CryptoJS; -var getLogger = require('get-log'); - -// Create SHA1 hash starting from a buffer or an array of buffers -function createSHA1Hash(buffer) { - var logger = getLogger('crypto-util.createSHA1Hash'); - var sha1sum = crypto.createHash('sha1'); - if (util.isArray(buffer)) { - if (logger.isDebugEnabled()) logger.debug('It\'s an Array of buffers'); - for (var i = 0; i < buffer.length; i++) { - sha1sum.update(buffer[i]); - } - } else { - if (logger.isDebugEnabled()) logger.debug('It\'s only one buffer'); - sha1sum.update(buffer); - } - return sha1sum.digest(); -} - -// Create a random Buffer -function createRandomBuffer(bytesLength) { - return new Buffer(crypto.randomBytes(bytesLength)); -} - -// Create a new nonce -function createNonce(bytesLength) { - return '0x' + createRandomBuffer(bytesLength).toString('hex'); -} - -// RSA encrypt function, param: { key: publicKey, message: messageBuffer } -function rsaEncrypt(param) { - var logger = getLogger('crypto-util.rsaEncrypt'); - - var publicKey = param.key; - var messageBuffer = param.message; - var messageLength = messageBuffer.length; - if (!messageBuffer || messageLength > 255) { - logger.warn('Message is undefined or exceeds 255 bytes length.'); - return; - } - if (messageLength < 255) { - // Add random bytes as padding - var paddingLength = 255 - messageLength; - messageBuffer = Buffer.concat([messageBuffer, createRandomBuffer(paddingLength)]); - } - if (logger.isDebugEnabled()) logger.debug('Message to be encrypt (%s) = %s', - messageBuffer.length, messageBuffer.toString('hex')); - // Encrypt the message - var modulus = new BigInteger(publicKey.getModulus(), 16); - var exponent = new BigInteger(publicKey.getExponent(), 16); - var message = new BigInteger(messageBuffer); - var encryptedMessage = new Buffer(message.modPowInt(exponent, modulus).toByteArray()); - if (encryptedMessage.length > 256) { - encryptedMessage = encryptedMessage.slice(encryptedMessage.length - 256); - } - if (logger.isDebugEnabled()) logger.debug('Encrypted message(%s) = %s', encryptedMessage.length, encryptedMessage.toString('hex')); - return encryptedMessage; -} - -// AES decrypt function -function aesDecrypt(msg, key, iv) { - var logger = getLogger('crypto-util.aesDecrypt'); - var encryptedMsg = buffer2WordArray(msg); - var keyWordArray = buffer2WordArray(key); - var ivWordArray = buffer2WordArray(iv); - if (logger.isDebugEnabled()) { - logger.debug('encryptedMsg = %s', JSON.stringify(encryptedMsg)); - logger.debug('keyWordArray = %s', JSON.stringify(keyWordArray)); - logger.debug('ivWordArray = %s', JSON.stringify(ivWordArray)); - } - var decryptedWordArray = CryptoJS.AES.decrypt({ciphertext: encryptedMsg}, keyWordArray, { - iv: ivWordArray, - padding: CryptoJS.pad.NoPadding, - mode: CryptoJS.mode.IGE - }); - if (logger.isDebugEnabled()) logger.debug('decryptedWordArray = %s', JSON.stringify(decryptedWordArray)); - return wordArray2Buffer(decryptedWordArray); -} - -// AES encrypt function -function aesEncrypt(msg, key, iv) { - var logger = getLogger('crypto-util.aesEncrypt'); - // Check if padding is needed - var padding = msg.length % 16; - if (padding > 0) { - paddingBuffer = createRandomBuffer(16 - padding); - msg = Buffer.concat([msg, paddingBuffer]); - } - // Convert buffers to wordArrays - var plainMsg = buffer2WordArray(msg); - var keyWordArray = buffer2WordArray(key); - var ivWordArray = buffer2WordArray(iv); - if (logger.isDebugEnabled()) { - logger.debug('plainMsg = %s', JSON.stringify(plainMsg)); - logger.debug('keyWordArray = %s', JSON.stringify(keyWordArray)); - logger.debug('ivWordArray = %s', JSON.stringify(ivWordArray)); - } - // Encrypt plain message - var encryptedWordArray = CryptoJS.AES.encrypt(plainMsg, keyWordArray, { - iv: ivWordArray, - padding: CryptoJS.pad.NoPadding, - mode: CryptoJS.mode.IGE - }).ciphertext; - if (logger.isDebugEnabled()) logger.debug('encryptedWordArray = %s', JSON.stringify(encryptedWordArray)); - // Return the encrypted buffer - return wordArray2Buffer(encryptedWordArray); -} - -// Mod Pow -function modPow(x, e, m) { - var logger = getLogger('crypto-util.aesDecrypt'); - var bigX = (typeof x == 'number') ? new BigInteger(x + '', 10) : new BigInteger(x.toString('hex'), 16); - var bigE = (typeof e == 'number') ? new BigInteger(e + '', 10) : new BigInteger(e.toString('hex'), 16); - var bigM = (typeof m == 'number') ? new BigInteger(m + '', 10) : new BigInteger(m.toString('hex'), 16); - var bigResult = bigX.modPow(bigE, bigM); - if (logger.isDebugEnabled()) logger.debug('X = %s, E = %s, M = %s, result = %s', bigX, bigE, bigM, bigResult); - var result = new Buffer(bigResult.toByteArray()); - if (result.length > 256) { - result = result.slice(result.length - 256); - } - return result; -} - -function buffer2WordArray(buffer) { - var length = buffer.length; - var wordArray = []; - for (var i = 0; i < length; i++) { - wordArray[i >>> 2] |= buffer[i] << (24 - 8 * (i % 4)); - } - return new CryptoJS.lib.WordArray.init(wordArray, length); -} - -function wordArray2Buffer(wordArray) { - var words = wordArray.words; - var sigBytes = wordArray.sigBytes; - var buffer = new Buffer(sigBytes); - for (var i = 0; i < sigBytes; i++) { - buffer.writeUInt8((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff, i); - } - return buffer; -} - -// Time offset between local time and server time -var timeOffset = 0; -var lastResponseTime = Number.MAX_VALUE; - -// Create a message ID starting from the local time -function createMessageId() { - var logger = getLogger('crypto-util.createMessageId'); - // Constants - var thousand = new BigInteger((1000).toString()); - var lowerMultiplier = new BigInteger((4294964).toString()); - // Take the time and sum the time-offset with the server clock - var time = new BigInteger((new Date().getTime() + timeOffset).toString()); - // Divide the time by 1000 `result[0]` and take the fractional part `result[1]` - var result = time.divideAndRemainder(thousand); - // Prepare lower 32 bit using the fractional part of the time - var lower = result[1].multiply(lowerMultiplier); - // Create the message id - var messageId = result[0].shiftLeft(32).add(lower); - if (logger.isDebugEnabled()) { - logger.debug('MessageId(%s) was created with time = %s and offset = %s, lower = %s, messageID binary = %s', - messageId.toString(), time, timeOffset, lower, messageId.toString(2)); - } - return messageId.toString(); -} - -// Synchronize the local time with the server time -function timeSynchronization(serverTime, requestDuration) { - var logger = getLogger('crypto-util.timeSynchronization'); - var localTime = new Date().getTime(); - var responseTime = requestDuration / 2; - if (lastResponseTime > responseTime) { - lastResponseTime = responseTime; - timeOffset = (serverTime + responseTime) - localTime; - logger.info('(ServerTime %s + ServerResponseTime %s) - LocalTime %s = timeOffset %s ', - serverTime, responseTime, localTime, timeOffset); - } else { - logger.info('Discarded: ServerResponseTime (%s) higher than previous one (%s) ', - responseTime, lastResponseTime); - } -} \ No newline at end of file diff --git a/node_modules/lib/crypto-util/pq-finder.js b/node_modules/lib/crypto-util/pq-finder.js deleted file mode 100644 index d36afcb..0000000 --- a/node_modules/lib/crypto-util/pq-finder.js +++ /dev/null @@ -1,96 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -// PQFinder class -// -// This class find the `PQ pair with P < Q` starting from a `BigInteger` value - -// Import dependencies -var BigInteger = require('bigint-node'); - -// The constructor may be called either giving a `Buffer` with the binary image or -// a `String / Number` representation of the `BigInteger` number -function PQFinder(pqNumber) { - this._pqNumber = ("number" == typeof pqNumber) ? createBigIntFromNumber(pqNumber) : - ("string" == typeof pqNumber) ? createBigIntFromString(pqNumber) : createBigIntFromBuffer(pqNumber); -} - -// Get the original PQ pair number. -PQFinder.prototype.getPQPairNumber = function () { - return this._pqNumber; -}; - -// The method calculates the pair P and Q with p < q and returns an array where `p = [0] and q = [1]` -PQFinder.prototype.findPQ = function () { - if (!this._pq) { - var num = this._pqNumber; - var prime; - for (var i = 0; i < 3; i++) { - var q = createBigIntFromNumber((nextRandom(128) & 15) + 17); - var x = createBigIntFromNumber(nextRandom(1000000000) + 1); - var y = x.duplicate(); - var lim = 1 << (i + 18); - for (var j = 1; j < lim; j++) { - var a = x.duplicate(); - var b = x.duplicate(); - var c = q.duplicate(); - while (!b.eql(BigInteger.Zero())) { - if (b.repr[0] & 1) { - c.addEquals(a); - if (c.gt(num)) { - c = c.subtract(num); - } - } - a.addEquals(a); - if (a.gt(num)) { - a = a.subtract(num); - } - b = b.shiftRight(1); - } - x = c.duplicate(); - var z = y.gt(x) ? y.subtract(x) : x.subtract(y); - prime = z.gcd(num, a, b); - if (!prime.eql(BigInteger.One())) { - break; - } - if ((j & (j - 1)) === 0) { - y = x.duplicate(); - } - } - if (prime.gt(BigInteger.One())) { - break; - } - } - var cofactor = num.divide(prime)[0]; - this._pq = cofactor.gt(prime) ? [prime, cofactor] : [cofactor, prime]; - } - return this._pq; -}; - - -// the method returns a new Buffer for each P and Q value as array -PQFinder.prototype.getPQAsBuffer = function () { - return [new Buffer(this.findPQ()[0].toRawBytes()), new Buffer(this.findPQ()[1].toRawBytes())]; -}; - -function createBigIntFromNumber (num) { - return new BigInteger.FromNumber(num); -} - -function createBigIntFromString (num) { - return new BigInteger.ParseFromString(num, 10); -} - -function createBigIntFromBuffer (num) { - return new BigInteger.FromRawBytes(num); -} - -function nextRandom (max) { - return Math.floor(Math.random() * max); -} - -// Export the class -module.exports = exports = PQFinder; diff --git a/node_modules/lib/crypto-util/public-key.js b/node_modules/lib/crypto-util/public-key.js deleted file mode 100644 index edc180f..0000000 --- a/node_modules/lib/crypto-util/public-key.js +++ /dev/null @@ -1,48 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -// PublicKey class -// -// This class represents a Public Key - -// The constructor requires the fingerprint, the modulus and the exponent -function PublicKey(params) { - this._fingerprint = params.fingerprint; - this._modulus = params.modulus; - this._exponent = params.exponent; -} - -PublicKey.prototype.getFingerprint = function () { - return this._fingerprint; -}; - -PublicKey.prototype.getModulus = function () { - return this._modulus; -}; - -PublicKey.prototype.getExponent = function () { - return this._exponent; -}; - -// The key store -var keyStore = {}; - -// Add a key to key store, it requires the fingerprint, the key and the exponent: -// -// PublicKey.addKey{fingerprint: '...', modulus: '...', exponent: '...'}); -// -PublicKey.addKey = function (params) { - var newKey = new PublicKey(params); - keyStore[newKey.getFingerprint()] = newKey; -}; - -// Retrieve a key with the fingerprint -PublicKey.retrieveKey = function (fingerprint) { - return keyStore[fingerprint]; -}; - -// Export the class -module.exports = exports = PublicKey; diff --git a/node_modules/lib/mtproto.js b/node_modules/lib/mtproto.js deleted file mode 100644 index bbf47b8..0000000 --- a/node_modules/lib/mtproto.js +++ /dev/null @@ -1,294 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -// MTProto module -// -// This module provide the `MTProto classes & method` as defined by the Telegram specification. The MTProto -// is the standard protocol to communicate with the Telegram data-center. - -// Import dependencies -var util = require('util'); -var crypto = require("lib/crypto-util"); -var TypeObject = require("telegram-tl-node").TypeObject; -var TypeBuilder = require("telegram-tl-node").TypeBuilder; - -// MtProto API as provided by Telegram -var api = {"constructors": [ - {"id": "481674261", "predicate": "vector", "params": [], "type": "Vector t"}, - {"id": "85337187", "predicate": "resPQ", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "pq", "type": "bytes"}, - {"name": "server_public_key_fingerprints", "type": "Vector"} - ], "type": "ResPQ"}, - {"id": "-2083955988", "predicate": "p_q_inner_data", "params": [ - {"name": "pq", "type": "bytes"}, - {"name": "p", "type": "bytes"}, - {"name": "q", "type": "bytes"}, - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "new_nonce", "type": "int256"} - ], "type": "P_Q_inner_data"}, - {"id": "2043348061", "predicate": "server_DH_params_fail", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "new_nonce_hash", "type": "int128"} - ], "type": "Server_DH_Params"}, - {"id": "-790100132", "predicate": "server_DH_params_ok", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "encrypted_answer", "type": "bytes"} - ], "type": "Server_DH_Params"}, - {"id": "-1249309254", "predicate": "server_DH_inner_data", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "g", "type": "int"}, - {"name": "dh_prime", "type": "bytes"}, - {"name": "g_a", "type": "bytes"}, - {"name": "server_time", "type": "int"} - ], "type": "Server_DH_inner_data"}, - {"id": "1715713620", "predicate": "client_DH_inner_data", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "retry_id", "type": "long"}, - {"name": "g_b", "type": "bytes"} - ], "type": "Client_DH_Inner_Data"}, - {"id": "1003222836", "predicate": "dh_gen_ok", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "new_nonce_hash1", "type": "int128"} - ], "type": "Set_client_DH_params_answer"}, - {"id": "1188831161", "predicate": "dh_gen_retry", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "new_nonce_hash2", "type": "int128"} - ], "type": "Set_client_DH_params_answer"}, - {"id": "-1499615742", "predicate": "dh_gen_fail", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "new_nonce_hash3", "type": "int128"} - ], "type": "Set_client_DH_params_answer"}, - {"id": "-212046591", "predicate": "rpc_result", "params": [ - {"name": "req_msg_id", "type": "long"}, - {"name": "result", "type": "Object"} - ], "type": "RpcResult"}, - {"id": "558156313", "predicate": "rpc_error", "params": [ - {"name": "error_code", "type": "int"}, - {"name": "error_message", "type": "string"} - ], "type": "RpcError"}, - {"id": "1579864942", "predicate": "rpc_answer_unknown", "params": [], "type": "RpcDropAnswer"}, - {"id": "-847714938", "predicate": "rpc_answer_dropped_running", "params": [], "type": "RpcDropAnswer"}, - {"id": "-1539647305", "predicate": "rpc_answer_dropped", "params": [ - {"name": "msg_id", "type": "long"}, - {"name": "seq_no", "type": "int"}, - {"name": "bytes", "type": "int"} - ], "type": "RpcDropAnswer"}, - {"id": "155834844", "predicate": "future_salt", "params": [ - {"name": "valid_since", "type": "int"}, - {"name": "valid_until", "type": "int"}, - {"name": "salt", "type": "long"} - ], "type": "FutureSalt"}, - {"id": "-1370486635", "predicate": "future_salts", "params": [ - {"name": "req_msg_id", "type": "long"}, - {"name": "now", "type": "int"}, - {"name": "salts", "type": "vector"} - ], "type": "FutureSalts"}, - {"id": "880243653", "predicate": "pong", "params": [ - {"name": "msg_id", "type": "long"}, - {"name": "ping_id", "type": "long"} - ], "type": "Pong"}, - {"id": "-501201412", "predicate": "destroy_session_ok", "params": [ - {"name": "session_id", "type": "long"} - ], "type": "DestroySessionRes"}, - {"id": "1658015945", "predicate": "destroy_session_none", "params": [ - {"name": "session_id", "type": "long"} - ], "type": "DestroySessionRes"}, - {"id": "-1631450872", "predicate": "new_session_created", "params": [ - {"name": "first_msg_id", "type": "long"}, - {"name": "unique_id", "type": "long"}, - {"name": "server_salt", "type": "long"} - ], "type": "NewSession"}, - {"id": "1945237724", "predicate": "msg_container", "params": [ - {"name": "messages", "type": "vector<%Message>"} - ], "type": "MessageContainer"}, - {"id": "1538843921", "predicate": "message", "params": [ - {"name": "msg_id", "type": "long"}, - {"name": "seqno", "type": "int"}, - {"name": "bytes", "type": "int"}, - {"name": "body", "type": "Object"} - ], "type": "Message"}, - {"id": "-530561358", "predicate": "msg_copy", "params": [ - {"name": "orig_message", "type": "Message"} - ], "type": "MessageCopy"}, - {"id": "812830625", "predicate": "gzip_packed", "params": [ - {"name": "packed_data", "type": "bytes"} - ], "type": "Object"}, - {"id": "1658238041", "predicate": "msgs_ack", "params": [ - {"name": "msg_ids", "type": "Vector"} - ], "type": "MsgsAck"}, - {"id": "-1477445615", "predicate": "bad_msg_notification", "params": [ - {"name": "bad_msg_id", "type": "long"}, - {"name": "bad_msg_seqno", "type": "int"}, - {"name": "error_code", "type": "int"} - ], "type": "BadMsgNotification"}, - {"id": "-307542917", "predicate": "bad_server_salt", "params": [ - {"name": "bad_msg_id", "type": "long"}, - {"name": "bad_msg_seqno", "type": "int"}, - {"name": "error_code", "type": "int"}, - {"name": "new_server_salt", "type": "long"} - ], "type": "BadMsgNotification"}, - {"id": "2105940488", "predicate": "msg_resend_req", "params": [ - {"name": "msg_ids", "type": "Vector"} - ], "type": "MsgResendReq"}, - {"id": "-630588590", "predicate": "msgs_state_req", "params": [ - {"name": "msg_ids", "type": "Vector"} - ], "type": "MsgsStateReq"}, - {"id": "81704317", "predicate": "msgs_state_info", "params": [ - {"name": "req_msg_id", "type": "long"}, - {"name": "info", "type": "bytes"} - ], "type": "MsgsStateInfo"}, - {"id": "-1933520591", "predicate": "msgs_all_info", "params": [ - {"name": "msg_ids", "type": "Vector"}, - {"name": "info", "type": "bytes"} - ], "type": "MsgsAllInfo"}, - {"id": "661470918", "predicate": "msg_detailed_info", "params": [ - {"name": "msg_id", "type": "long"}, - {"name": "answer_msg_id", "type": "long"}, - {"name": "bytes", "type": "int"}, - {"name": "status", "type": "int"} - ], "type": "MsgDetailedInfo"}, - {"id": "-2137147681", "predicate": "msg_new_detailed_info", "params": [ - {"name": "answer_msg_id", "type": "long"}, - {"name": "bytes", "type": "int"}, - {"name": "status", "type": "int"} - ], "type": "MsgDetailedInfo"} -], "methods": [ - {"id": "1615239032", "method": "req_pq", "params": [ - {"name": "nonce", "type": "int128"} - ], "type": "ResPQ"}, - {"id": "-686627650", "method": "req_DH_params", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "p", "type": "bytes"}, - {"name": "q", "type": "bytes"}, - {"name": "public_key_fingerprint", "type": "long"}, - {"name": "encrypted_data", "type": "bytes"} - ], "type": "Server_DH_Params"}, - {"id": "-184262881", "method": "set_client_DH_params", "params": [ - {"name": "nonce", "type": "int128"}, - {"name": "server_nonce", "type": "int128"}, - {"name": "encrypted_data", "type": "bytes"} - ], "type": "Set_client_DH_params_answer"}, - {"id": "1491380032", "method": "rpc_drop_answer", "params": [ - {"name": "req_msg_id", "type": "long"} - ], "type": "RpcDropAnswer"}, - {"id": "-1188971260", "method": "get_future_salts", "params": [ - {"name": "num", "type": "int"} - ], "type": "FutureSalts"}, - {"id": "2059302892", "method": "ping", "params": [ - {"name": "ping_id", "type": "long"} - ], "type": "Pong"}, - {"id": "-213746804", "method": "ping_delay_disconnect", "params": [ - {"name": "ping_id", "type": "long"}, - {"name": "disconnect_delay", "type": "int"} - ], "type": "Pong"}, - {"id": "-414113498", "method": "destroy_session", "params": [ - {"name": "session_id", "type": "long"} - ], "type": "DestroySessionRes"}, - {"id": "-1835453025", "method": "http_wait", "params": [ - {"name": "max_delay", "type": "int"}, - {"name": "wait_after", "type": "int"}, - {"name": "max_wait", "type": "int"} - ], "type": "HttpWait"} -]}; - -// Declare the `mtproto` module -var mtproto = { _id: 'mtproto'}; - -// PlainMessage class -// -// This class provide a plain wrapper for message as defined in MTProto -// -// To get an instance for `serialization`: -// -// new PlainMessage({message: myMessageBuffer}); -// Provide the payload as `Buffer`: -// -// To get an instance for `de-serialization`: -// -// new PlainMessage({buffer: myBuffer}); -// Provide a `buffer` containing the plain message from which extract the payload -// -// The `constructor`: -mtproto.PlainMessage = function (options) { - var super_ = this.constructor.super_.bind(this); - var opts = options ? options : {}; - this._message = opts.message; - super_(opts.buffer, opts.offset); - if (this._message) { - this._message = Buffer.isBuffer(this._message) ? this._message : new Buffer(this._message, 'hex'); - this._authKeyId = 0; - this._messageId = crypto.createMessageId(); - this._messageLength = this._message.length; - } - this._logger = require('get-log')('mtproto.PlainMessage'); -}; - -util.inherits(mtproto.PlainMessage, TypeObject); - -// This method serialize the PlainMessage -mtproto.PlainMessage.prototype.serialize = function () { - if (!this._message) { - return false; - } - this.writeLong(this._authKeyId); - this.writeLong(this._messageId); - this.writeInt(this._messageLength); - // call low level method - this._writeBytes(this._message); - if (this._logger.isDebugEnabled()) this._logger.debug('Serialize message(%s) with authKeyId: %s, messageId: %s and writeOffset: %s', - this._messageLength, this._authKeyId, this._messageId, this._writeOffset); - return this.retrieveBuffer(); -}; - -// This method de-serialize the PlainMessage -mtproto.PlainMessage.prototype.deserialize = function () { - if (!this.isReadonly()) { - return false; - } - this._authKeyId = this.readLong(); - this._messageId = this.readLong(); - this._messageLength = this.readInt(); - this._message = this._readBytes(this._messageLength); - if (this._logger.isDebugEnabled()) this._logger.debug('De-serialize message(%s) with authKeyId: %s and messageId: %s', - this._messageLength, this._authKeyId, this._messageId); - return this; -}; - -// This method returns the payload -mtproto.PlainMessage.prototype.getMessage = function () { - return this._message; -}; - -// This method returns the message ID -mtproto.PlainMessage.prototype.getMessageId = function () { - return this._messageId; -}; - -// Register the `TypeLanguage class` list -mtproto._classes = ['ResPQ', 'P_Q_inner_data', 'Server_DH_Params', 'Server_DH_inner_data', 'Client_DH_Inner_Data', 'Set_client_DH_params_answer']; - -// Build registered classes -TypeBuilder.buildTypes(api.constructors, mtproto._classes, mtproto); - -// Register the `TypeLanguage method` list -mtproto._methods = ['req_pq', 'req_DH_params', 'set_client_DH_params']; - -// Build registered methods -TypeBuilder.buildTypes(api.methods, mtproto._methods, mtproto, mtproto.PlainMessage); - -// Export the package -module.exports = exports = mtproto; \ No newline at end of file diff --git a/node_modules/lib/net/http-connection.js b/node_modules/lib/net/http-connection.js deleted file mode 100644 index b382367..0000000 --- a/node_modules/lib/net/http-connection.js +++ /dev/null @@ -1,115 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -// HttpConnection class -// -// This class provides a HTTP transport to communicate with `Telegram` using `MTProto` protocol - -// Import dependencies -var http = require('http'); -var util = require('util'); -var logger = require('get-log')('net.HttpConnection'); - -// The constructor accepts optionally an object to specify the connection address as following: -// -// new HttpConnection({host: "173.240.5.253", port: "443"}); -// -// `localhost:80` address is used as default otherwise -function HttpConnection(options) { - var httpPath = require('../static').telegram.httpPath; - options = options ? - ({ - host: (options.proxyHost || options.host || 'localhost'), - port: (options.proxyPort || options.port || '80'), -// path: 'http://' + (options.host || 'localhost') + ':' + (options.port || '80') + httpPath - path: httpPath, - withCredentials: false - }) : - ({ - path: 'http://localhost:80' + httpPath - }); - this.options = util._extend({ - localAddress: process.env.LOCAL_ADDRESS - }, options); - this._config = JSON.stringify(this.options); - if (logger.isDebugEnabled()) logger.debug('created with %s', this._config); -} - -HttpConnection.prototype.connect = function (callback) { - if (logger.isDebugEnabled()) logger.debug('connected to %s', this._config); - this._writeBuffers = []; - this._writeOffset = 0; - if (callback) callback(); -}; - -HttpConnection.prototype.write = function (data, callback) { - this._writeBuffers.push(data); - this._writeOffset += data.length; - if (logger.isDebugEnabled()) logger.debug('add buffer(%s) to the write buffer queue, total length %s', - data.length, this._writeOffset); - if (callback) callback(); -}; - -HttpConnection.prototype.read = function (callback) { - var self = this; - var options = util._extend(this.options, { - method: (this._writeOffset === 0 ? 'GET' : 'POST'), - responseType: 'arraybuffer' - }); - var onError = function (e) { - self._request.removeListener('error', arguments.callee); - logger.error('Error %s reading from %s', e.code, self._config); - if (callback) { - callback(e); - } - }; - this._request = http.request(options, function (res) { - if (logger.isDebugEnabled()) { - logger.debug('reading from %s', this._config); - logger.debug('status: ' + res.statusCode); - logger.debug('headers: ' + JSON.stringify(res.headers)); - } - var onData = function (data) { - res.removeListener('data', arguments.callee); - self._request.removeListener('error', onError); - if (res.statusCode === 200) { - if (callback) { - callback(null, data); - } - } else { - if (callback) { - callback(data); - } - } - }; - res.on('data', onData); - }.bind(this)); - this._request.setHeader('Content-Length', this._writeOffset); - this._request.setHeader('Connection', 'keep-alive'); - this._request.removeHeader('Content-Type'); - this._request.removeHeader('Accept'); - if (logger.isDebugEnabled()) { - logger.debug('content-length = %s', this._request.getHeader('Content-Length')); - logger.debug('content-type = %s', this._request.getHeader('Content-Type')); - logger.debug('accept = %s', this._request.getHeader('Accept')); - logger.debug('host = %s', this._request.getHeader('Host')); - logger.debug('connection = %s', this._request.getHeader('Connection')); - } - this._request.on('error', onError); - var request = Buffer.concat(this._writeBuffers); - this._writeBuffers = []; - this._writeOffset = 0; - if (logger.isDebugEnabled()) logger.debug('writing request(%s) to %s', request.length, this._config); - this._request.end(request); -}; - -// Call back, nothing else.. -HttpConnection.prototype.close = function (callback) { - if (callback) callback(); -}; - -// Export the class -module.exports = exports = HttpConnection; \ No newline at end of file diff --git a/node_modules/lib/net/index.js b/node_modules/lib/net/index.js deleted file mode 100644 index 97ae979..0000000 --- a/node_modules/lib/net/index.js +++ /dev/null @@ -1,8 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -exports.TcpConnection = require('./tcp-connection'); -exports.HttpConnection = require('./http-connection'); \ No newline at end of file diff --git a/node_modules/lib/net/package.json b/node_modules/lib/net/package.json deleted file mode 100644 index df4c3a4..0000000 --- a/node_modules/lib/net/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "telegram.link-net", - "version": "0.1.0", - "browser": { - "http": "http-browserify" - } -} \ No newline at end of file diff --git a/node_modules/lib/net/tcp-connection.js b/node_modules/lib/net/tcp-connection.js deleted file mode 100644 index 83cea20..0000000 --- a/node_modules/lib/net/tcp-connection.js +++ /dev/null @@ -1,220 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the Simplified BSD License -// http://telegram.link - -// TcpConnection class -// -// This class provides a TCP socket to communicate with `Telegram` using `MTProto` protocol - -// Import dependencies -var net = require('net'); -var util = require('util'); -var TypeObject = require("telegram-tl-node").TypeObject; -var logger = require('get-log')('net.TcpConnection'); - -// The constructor accepts optionally an object to specify the connection address as following: -// -// new TcpConnection({host: "173.240.5.253", port: "443"}); -// -// `localhost:80` address is used as default otherwise -function TcpConnection(options) { - this.options = util._extend({ host: 'localhost', port: '80', localAddress: process.env.LOCAL_ADDRESS }, options); - this._config = JSON.stringify(this.options); - if (logger.isDebugEnabled()) logger.debug('Created with %s', this._config); -} - -// This method opens the connection and calls back when done -TcpConnection.prototype.connect = function (callback) { - var self = this; - if (logger.isDebugEnabled()) logger.debug('Connecting to %s', self._config); - if (this._socket) { - if (callback) { - callback(null); - } - return; - } - var onError = function (e) { - socket.removeListener('error', onError); - logger.error('Error %s connecting to %s', e.code, self._config); - this._socket = undefined; - if (callback) { - callback(e); - } - }; - var socket = net.connect(this.options, function () { - socket.removeListener('error', onError); - - if (logger.isDebugEnabled()) logger.debug('Connected to ' + self._config); - - var abridgedFlag = new Buffer(1); - abridgedFlag.writeUInt8(0xef, 0); - if (logger.isDebugEnabled()) logger.debug('Sending abridgedFlag to ' + self._config); - socket.write(abridgedFlag, 'UTF8', function () { - if (logger.isDebugEnabled()) logger.debug('AbridgedFlag sent to ' + self._config); - if (callback) { - callback(null); - } - }); - }); - socket.setKeepAlive(true); - socket.setNoDelay(true); - socket.on('error', onError); - this._socket = socket; -}; - -// This method writes the given data and calls back when done -TcpConnection.prototype.write = function (data, callback) { - var self = this; - var socket = this._socket; - if (!socket && callback) { - callback(createError('Not yet connected', 'ENOTCONNECTED')); - return; - } - if ((data.length % 4) !== 0 && callback) { - callback(createError('Data length must be multiple of 4','EMULTIPLE4')); - return; - } - if (!Buffer.isBuffer(data)) { - if (logger.isDebugEnabled()) logger.debug('Given data is not a Buffer'); - data = new Buffer(data); - } - var message = new TcpConnection.Message({message: data}); - var request = message.serialize(); - if (logger.isDebugEnabled()) logger.debug('Writing %s to %s', request.toString('hex'), self._config); - var onError = function (e) { - socket.removeListener('error', arguments.callee); - logger.error('Error %s writing %s bytes to %s', e.code, request.length, self._config); - if (callback) { - callback(e); - } - }; - socket.write(request, 'UTF8', function () { - if (logger.isDebugEnabled()) logger.debug('Wrote %s bytes to %s', request.length, self._config); - socket.removeListener('error', onError); - if (callback) { - callback(null); - } - }); - socket.on('error', onError); -}; - -// This method reads the data from the connection and calls back when done -TcpConnection.prototype.read = function (callback) { - var self = this; - if (logger.isDebugEnabled()) logger.debug('Reading from %s', self._config); - var socket = this._socket; - if (!socket && callback) { - callback(createError('Not yet connected', 'ENOTCONNECTED')); - return; - } - var onError = function (e) { - socket.removeListener('error', arguments.callee); - logger.error('Error %s reading from %s', e.code, self._config); - if (callback) { - callback(e); - } - }; - var onData = function (data) { - socket.removeListener('data', arguments.callee); - socket.removeListener('error', onError); - var message = new TcpConnection.Message({buffer: data}).deserialize(); - var payload = message.getMessage(); - if (logger.isDebugEnabled()) logger.debug('Read %s bytes from %s', payload.toString('hex'), self._config); - - if (callback) { - callback(null, payload); - } - }; - socket.on('data', onData); - socket.on('error', onError); -}; - -// This method close the connection and calls back when done -TcpConnection.prototype.close = function (callback) { - var self = this; - var socket = this._socket; - if (socket) { - if (logger.isDebugEnabled()) logger.debug('Disconnecting from ' + self._config); - var onError = function (e) { - socket.removeListener('error', arguments.callee); - logger.error('Error %s disconnecting from %s', e.code, self._config); - if (callback) { - callback(e); - } - }; - socket.on('end', function () { - socket.removeListener('end', arguments.callee); - socket.removeListener('error', onError); - if (logger.isDebugEnabled()) logger.debug('Disconnected from ' + self._config); - if (callback) { - callback(null); - } - }); - socket.on('error', onError); - socket.end(); - this._socket = undefined; - } else if (callback) { - callback(null); - } -}; - -function createError(msg, code) { - var error = new Error(msg); - error.code = code; - return error; -} - -// TcpConnection inner class: -// -// TcpConnection.Message class -// -// To get an instance for `serialization`: -// -// new TcpConnection.Message({message: myMessageBuffer}); -// Provide the payload as `Buffer`: -// -// To get an instance for `de-serialization`: -// -// new TcpConnection.Message({buffer: myBuffer}); -// Provide a `buffer` containing the plain message from which extract the payload -// -// The `constructor`: -TcpConnection.Message = function (options) { - var super_ = this.constructor.super_.bind(this); - var opts = options ? options : {}; - this._message = opts.message; - super_(opts.buffer, opts.offset); - if (this._message) { - this._message = Buffer.isBuffer(this._message) ? this._message : new Buffer(this._message, 'hex'); - } -}; - -util.inherits(TcpConnection.Message, TypeObject); - -// This method serialize the Message -TcpConnection.Message.prototype.serialize = function () { - if (!this._message) { - return false; - } - this.writeBytes(this._message, true); - return this.retrieveBuffer(); -}; - -// This method de-serialize the Message -TcpConnection.Message.prototype.deserialize = function () { - if (!this.isReadonly()) { - return false; - } - this._message = this.readBytes(true); - return this; -}; - -// This method returns the payload -TcpConnection.Message.prototype.getMessage = function () { - return this._message; -}; - -// Export the class -module.exports = exports = TcpConnection; \ No newline at end of file diff --git a/package.json b/package.json index 2bcc07f..341e514 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "telegram.link", - "version": "0.1.2", - "description": "Telegram API Node.js library", + "version": "0.2.0", + "description": "Telegram API library", "keywords": [ "telegram", "api", @@ -16,37 +16,35 @@ "bugs": { "url": "https://github.com/enricostara/telegram.link/issues" }, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { "telegram-tl-node": "latest", + "telegram-mt-node": "latest", "get-log": "latest", - "get-flow": "latest", - "jsbn": "latest", - "bigint-node": "latest", - "node-cryptojs-aes": "enricostara/node-cryptojs-aes", - "http-browserify": "enricostara/http-browserify" + "get-flow": "latest" }, "devDependencies": { - "coveralls": "latest", - "del": "latest", "gulp": "latest", - "gulp-docco": "latest", "gulp-jshint": "latest", "gulp-mocha": "latest", - "istanbul": "latest", + "gulp-docco": "latest", + "del": "latest", + "should": "latest", "mocha": "latest", - "mocha-lcov-reporter": "latest", "mocha-better-spec-reporter": "latest", - "should": "latest", + "mocha-lcov-reporter": "latest", + "coveralls": "latest", + "codeclimate-test-reporter": "latest", + "istanbul": "latest", "zuul": "latest" }, - "main": "telegram.link", + "main": "./lib/telegram.link", "engines": { "node": "0.10.x" }, "scripts": { - "test": "gulp test", - "coveralls": "istanbul cover ./node_modules/gulp/bin/gulp.js cover && cat ./coverage/lcov.info | coveralls && rm -rf ./coverage", - "zuul": "zuul -- ./test/lib/crypto-util/**" + "test": "DEBUG=-mocha*,* gulp test", + "coveralls": "DEBUG=-mocha*,* istanbul cover ./node_modules/gulp/bin/gulp.js cover && cat ./coverage/lcov.info | coveralls && codeclimate < ./coverage/lcov.info && rm -rf ./coverage", + "zuul": "zuul -- ./test/**" } } diff --git a/test/lib/crypto-util/pq-finder.test.js b/test/lib/crypto-util/pq-finder.test.js deleted file mode 100644 index f7fe2e3..0000000 --- a/test/lib/crypto-util/pq-finder.test.js +++ /dev/null @@ -1,49 +0,0 @@ -require('should'); -var PQFinder = require("lib/crypto-util").PQFinder; - -describe('PQFinder', function () { - - describe('#init()', function () { - it('should create a PQFinder instance giving a number', function (done) { - var pqFinder = new PQFinder(100); - pqFinder.should.be.ok; - (pqFinder._pqNumber.equalsNum(100)).should.be.true; - done(); - }) - }); - - describe('#init()', function () { - it('should create a PQFinder instance giving a String', function (done) { - var pqFinder = new PQFinder('123456789'); - pqFinder.should.be.ok; - (pqFinder._pqNumber.equalsNum(123456789)).should.be.true; - done(); - }) - }); - - describe('#init()', function () { - it('should create a PQFinder instance giving a Buffer', function (done) { - var pqFinder = new PQFinder(new Buffer('075BCD15', 'hex')); - pqFinder.should.be.ok; - (pqFinder._pqNumber.equalsNum(123456789)).should.be.true; - done(); - }) - }); - - describe('#findPQ()', function () { - it('should find P and Q returning as [p, q]', function (done) { - - var pqFinder = new PQFinder(new Buffer('17ED48941A08F981', 'hex')); - console.log(pqFinder._pqNumber.toStr(10)); - console.log(pqFinder.findPQ()[0].toStr(16)); - console.log(pqFinder.findPQ()[1].toStr(16)); - console.log(pqFinder.getPQAsBuffer()); - (pqFinder.findPQ()[0].equalsNum(1229739323)).should.be.true; - (pqFinder.findPQ()[1].equalsNum(1402015859)).should.be.true; - - done(); - }) - }); - -}); - diff --git a/test/lib/mtproto.test.js b/test/lib/mtproto.test.js deleted file mode 100644 index 1ce52f8..0000000 --- a/test/lib/mtproto.test.js +++ /dev/null @@ -1,131 +0,0 @@ -require('should'); -var net = require('net'); -var mtproto = require('lib/mtproto'); -var TcpConnection = require("lib/net").TcpConnection; -var TypeObject = require('telegram-tl-node').TypeObject; -var TypeVector = require('telegram-tl-node').TypeVector; - -describe('mtproto', function () { - - var tcpConn, server, port; - before(function () { - server = net.createServer(function (conn) { - console.log('Server: connected'); - conn.on('data', function (data) { - - if (data.length == 1 && data[0] == 0xef) { - console.log('Server: abridgedFlag detected'); - } else { - if(data[0] == 0xef) { - data = data.slice(1); - } - console.log('Server: received data of length %s: %s', data.length, data.toString('hex')); - var requestBuffer = new TcpConnection.Message({buffer: data}).deserialize().getMessage(); - console.log('tcpMessage %s', requestBuffer.toString('hex')); - var requestBuffer = new mtproto.PlainMessage({buffer: requestBuffer}).deserialize().getMessage(); - console.log('plainMessage %s', requestBuffer.toString('hex')); - var reqPQ = new mtproto.req_pq.PayloadType({buffer: requestBuffer}).deserialize(); -// console.log(reqPQ); - var resPQ = new mtproto.ResPQ({props: { - nonce: reqPQ.nonce, - server_nonce: '0x30739073a54aba77a81ea1f4334dcfa5', - pq: new Buffer('17ed48941a08f981', 'hex'), - server_public_key_fingerprints: new TypeVector({type: 'long', list: ['0xc3b42b026ce86b21']}) - }}); - var resPQBuffer = resPQ.serialize(); - console.log('Server: Send resPQ %s', resPQBuffer.toString('hex')); - var plainMsg = new mtproto.PlainMessage({message: resPQBuffer}); - var plainMsgBuffer = plainMsg.serialize(); - var tcpMsg = new TcpConnection.Message({message: plainMsgBuffer}); - var tcpMsgBuffer = tcpMsg.serialize(); - conn.write(tcpMsgBuffer); - } - }) - }); - port = 2001; - server.listen(port, function () { - console.log('Server bound on port %s\n', port); - tcpConn = new TcpConnection({host: '0.0.0.0', port: port}); - }) - }); - - describe('#method reqPQ', function () { - it('should works', function (done) { - var nonce = '0xf67b7768bf4854bb15fa840ec843875f'; - mtproto.req_pq({ - props: { - nonce: nonce - }, - conn: tcpConn, - callback: function (ex, resPQ) { - if(ex) console.warn(ex); - resPQ.nonce.should.be.eql(nonce); - done(); - } - }) - }) - }); - -/* - describe('#class building', function () { - it('should build all requested classes', function (done) { - for (var i = 0; i < mtproto._classes.length; i++) { - var className = mtproto._classes[i]; - var obj = new mtproto[className](); - obj.should.be.ok; - obj.should.be.an.instanceof(TypeObject); - } - done(); - }) - }); -*/ - - describe('PlainMessage', function () { - - describe('#init()', function () { - it('should return an instance', function (done) { - - var msg = new mtproto.PlainMessage(); - msg.should.be.ok; - msg.should.be.an.instanceof(mtproto.PlainMessage); - msg.should.be.an.instanceof(TypeObject); - msg.isReadonly().should.be.false; - - msg = new mtproto.PlainMessage({message: new Buffer('FFFF', 'hex')}); - msg.should.have.properties({_authKeyId: 0, _messageLength: 2}); - msg.getMessage().toString('hex').should.equal('ffff'); - - msg = new mtproto.PlainMessage({buffer: new Buffer('FFFF', 'hex')}); - msg.retrieveBuffer().toString('hex').should.equal('ffff'); - - done(); - }) - }); - - describe('#serialize()', function () { - it('should serialize the msg', function (done) { - var msg = new mtproto.PlainMessage({message: new Buffer('FFFF', 'hex')}); - msg._messageId = 1; - var buffer = msg.serialize(); - buffer.should.be.ok; - buffer.toString('hex').should.be.equal('0000000000000000010000000000000002000000ffff'); - done(); - }) - }); - - describe('#deserialize()', function () { - it('should de-serialize the msg', function (done) { - var msg = new mtproto.PlainMessage({buffer: new Buffer('0000000000000000010000000000000002000000ffff', 'hex')}); - msg.deserialize().should.be.ok; - msg.getMessageId().should.be.equal('0x0000000000000001'); - msg.getMessage().toString('hex').should.equal('ffff'); - done(); - }) - }); - }); - - after(function () { - tcpConn.close(); - server.close(); - }); -}); diff --git a/test/lib/net/http-connection.test.js b/test/lib/net/http-connection.test.js deleted file mode 100644 index bb29165..0000000 --- a/test/lib/net/http-connection.test.js +++ /dev/null @@ -1,114 +0,0 @@ -// telegram.link -// -// Copyright 2014 Enrico Stara 'enrico.stara@gmail.com' -// Released under the MIT license -// http://telegram.link - -require('should'); -var http = require('http'); -var HttpConnection = require("lib/net").HttpConnection; - -describe('HttpConnection', function () { - - var conn, server, port; - - before(function () { - server = http.createServer(function (req, res) { - - console.log('Server: requested URL %s', req.url); - console.log('Server: requested header %s', JSON.stringify(req.headers)); - console.log('Server: requested method %s', JSON.stringify(req.method)); - req.on('data', function(data) {console.log('data: %s', data); }); - res.writeHead(200); - res.end('1234567890'); - - }); - port = 2001; - server.listen(port, function () { - console.log("Server bound on port %s\n", port); - }) - - }); - - describe('#init()', function () { - it('should return an instance', function (done) { - conn = new HttpConnection(); - conn.should.be.ok; - conn.should.be.a.Object; - conn.options.should.have.properties({path: 'http://localhost:80/apiw1'}); - done(); - }) - }); - - - describe('#read()', function () { - it('should notify error back on reading', function (done) { - conn = new HttpConnection({host: "nohost"}); - conn.connect(); - conn.read(function (e) { - console.log(e); - done(); - }); - }) - }); - - describe('#init(host, port)', function () { - it('should return an instance with given host and port', function (done) { - conn = new HttpConnection({host: "0.0.0.0", port: port}); - conn.options.should.have.properties({path: '/apiw1'}); - done(); - }) - }); - - describe('#connect()', function () { - it('should call back after HttpConnection', function (done) { - conn = new HttpConnection({host: "0.0.0.0", port: port}); - conn.connect(done); - }) - }); - - describe('#write()', function () { - it('should call back after write', function (done) { - conn = new HttpConnection({host: "0.0.0.0", port: port}); - conn.connect(); - conn.write(new Buffer(">string<"), done); - }) - }); - - describe('#read()', function () { - it('should call back after read', function (done) { - conn = new HttpConnection({host: "0.0.0.0", port: port}); - conn.connect(); - conn.write(new Buffer(">string<")); - conn.read(function (ex, data) { - data.should.be.a.Buffer; - console.log(data); - console.log(data.toString()); - done(); - }); - }) - }); - describe('#read()', function () { - it('outgoing connection should call back after read', function (done) { - conn = new HttpConnection({ - proxyHost: process.env.PROXY_HOST, - proxyPort: process.env.PROXY_PORT, - host: "www.google.com", - port: '80' - }); - conn.connect(); - conn.read(function (ex, data) { - if(ex) { - console.log("response ko: %s", ex.toString()); - } else { - console.log("response OK: %s", data.toString()); - } - done(); - }); - }) - }); - - after(function () { - server.close(); - }); -}); diff --git a/test/lib/net/tcp-connection.test.js b/test/lib/net/tcp-connection.test.js deleted file mode 100644 index 6ce80a9..0000000 --- a/test/lib/net/tcp-connection.test.js +++ /dev/null @@ -1,178 +0,0 @@ -require('should'); -var net = require('net'); -var TcpConnection = require("lib/net").TcpConnection; - -describe('TcpConnection', function () { - - var tcpConn, server, port; - - before(function () { - server = net.createServer(function (conn) { - console.log("Server: connected"); - conn.on("data", function (data) { - - if (data.length == 1 && data[0] == 0xef) { - console.log("Server: abridgedFlag detected"); - } else { - console.log("Server: echo data of length %s:", data.length); - console.log(data); - conn.write(data); - } - }) - }); - port = 2001; - server.listen(port, function () { - console.log("Server bound on port %s\n", port); - }) - - }); - - describe('#init()', function () { - it('should return an instance', function (done) { - tcpConn = new TcpConnection(); - tcpConn.should.be.ok; - tcpConn.should.be.a.Object; - tcpConn.options.should.have.properties({host: "localhost", port: "80"}); - done(); - }) - }); - - describe('#connect()', function () { - it('should notify error back trying connection', function (done) { - tcpConn = new TcpConnection({host: "nohost"}); - tcpConn.connect(function(e) { - console.log(e); - done(); - }); - }); - }); - - describe('#write()', function () { - it('should notify error back on writing', function (done) { - tcpConn = new TcpConnection({host: "nohost"}); - tcpConn.connect(); - tcpConn.write("a string", function (e) { - console.log(e); - done(); - }); - }) - }); - - describe('#read()', function () { - it('should notify error back on reading', function (done) { - tcpConn = new TcpConnection({host: "nohost"}); - tcpConn.connect(); - tcpConn.read(function (e) { - console.log(e); - done(); - }); - }) - }); - - describe('#close()', function () { - it('should notify error back trying disconnection', function (done) { - tcpConn = new TcpConnection({host: "nohost"}); - tcpConn.connect(); - tcpConn.close(function (e) { - console.log(e); - done(); - }); - }) - }); - - describe('#init(host, port)', function () { - it('should return an instance with given host and port', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.options.should.have.properties({host: "0.0.0.0", port: port}); - done(); - }) - }); - - describe('#connect()', function () { - it('should call back after connection', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(function () { - tcpConn.close(); - done(); - }); - }) - }); - - describe('#close()', function () { - it('should call back after disconnection', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(function () { - tcpConn.close(done); - }) - }) - }); - - describe('#write()', function () { - it('should call back after write', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(); - tcpConn.write(new Buffer(">string<"), function () { - tcpConn.close(); - done(); - }); - }) - }); - - describe('#write()', function () { - it('should notify error back writing data with length not multiple of 4', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(function () { - tcpConn.write("1234567890", function (e) { - console.log(e); - tcpConn.close(); - done(); - }); - }); - }) - }); - - describe('#read()', function () { - it('should call back after read', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(); - tcpConn.write(new Buffer(">string<")); - tcpConn.read(function (ex, data) { - data.should.be.a.Buffer; - console.log(data); - console.log(data.toString()); - tcpConn.close(); - done(); - }); - }) - }); - - describe('#write()', function () { - it('should call back after write', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(); - var buffer = new Buffer(600); - tcpConn.write(buffer, function () { - tcpConn.close(); - done(); - }); - }) - }); - describe('#read()', function () { - it('should call back after read', function (done) { - tcpConn = new TcpConnection({host: "0.0.0.0", port: port}); - tcpConn.connect(); - var buffer = new Buffer(600); - tcpConn.write(buffer); - tcpConn.read(function (ex, data) { - data.should.be.a.Buffer; - data.length.should.equal(600); - tcpConn.close(); - done(); - }); - }) - }); - - after(function () { - server.close(); - }); -}); diff --git a/test/telegram.link.test.js b/test/telegram.link.test.js index bebe780..b75af11 100644 --- a/test/telegram.link.test.js +++ b/test/telegram.link.test.js @@ -1,6 +1,6 @@ require('should'); -var staticInfo = require('lib/static'); -var TelegramLink = require('../telegram.link'); +var staticInfo = require('../lib/static'); +var TelegramLink = require('../lib/telegram.link'); describe('TelegramLink', function () { var primaryDC = staticInfo.telegram.test.primaryDataCenter; @@ -22,7 +22,6 @@ describe('TelegramLink', function () { it('should returns', function (done) { this.timeout(120000); -// require('get-log').enable('*'); var telegramLink = new TelegramLink(primaryDC); telegramLink.connect(function (e) { if(e) {