|
var callsInit = function() { |
|
const $inj = angular.element(document).injector(); |
|
window.getInputPeerByID = $inj.get('AppPeersManager').getInputPeerByID; |
|
window.invokeApi = $inj.get('MtpApiManager').invokeApi; |
|
window.getInputUserByID = id => ([getInputPeerByID(id)]).map(p=>((p||{})._='inputUser',p))[0]; |
|
window.secureRand = new SecureRandom(); |
|
window.calls = {}; |
|
window.dhConfig = null; |
|
|
|
// inject our update handler |
|
$inj.get('$rootScope').$on('apiUpdate', (u,v)=>processUpdate(v)) |
|
} |
|
|
|
var getDhConfig = function(force) { |
|
if (dhConfig != null && !force) return Promise.resolve(dhConfig); |
|
|
|
return invokeApi('messages.getDhConfig', {}).then(function(dhConfigR) { |
|
dhConfig = {}; |
|
dhConfig.p_int = new BigInteger(dhConfigR.p); |
|
dhConfig.g_int = new BigInteger(dhConfigR.g.toString(10), 10); |
|
dhConfig.resp = dhConfigR; |
|
|
|
return dhConfig; |
|
}) |
|
} |
|
|
|
var getRandBytes = function(len) { |
|
len = len || 256; |
|
//return getDhConfig().then(function(dhConfig) { |
|
var bytes1 = new Uint8Array(len); |
|
secureRand.nextBytes(bytes1); |
|
|
|
return bytesXor(bytes1, dhConfig.resp.random); |
|
//}) |
|
} |
|
|
|
var bytesEqual = function(arr1, arr2) { |
|
if (arr1.length != arr2.length) return false; |
|
for (let i = arr1.length - 1; i >= 0; i--) { |
|
if (arr1[i] != arr2[i]) return false; |
|
} |
|
return true; |
|
} |
|
|
|
processPhoneCall = function(call) { |
|
switch(call._) { |
|
case 'phoneCallEmpty': |
|
break; |
|
|
|
case 'phoneCallWaiting': |
|
console.log(`[CALL] Waiting for ${call.participant_id} to answer...`); |
|
break; |
|
|
|
case 'phoneCallRequested': |
|
acceptCall(call); |
|
break; |
|
|
|
case 'phoneCallDiscarded': |
|
let state = calls[call.id]; |
|
let participant_id = state.incoming ? state.admin_id : state.user_id.user_id; |
|
console.log(`[CALL] ${participant_id} discarded your call because of ${call.reason._}`); |
|
//delete calls[call.id]; |
|
break; |
|
|
|
case 'phoneCall': |
|
processFullCall(call); |
|
break; |
|
|
|
case 'phoneCallAccepted': |
|
console.log(`[CALL] Call accepted by ${call.participant_id}! Doing stage 2!`); |
|
callStageTwo(call); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
processUpdate = function(update) { |
|
switch(update._) { |
|
case 'updatePhoneCall': |
|
console.log('[CALL] Phone call update:', update.phone_call); |
|
processPhoneCall(update.phone_call) |
|
break; |
|
} |
|
} |
|
|
|
|
|
|
|
acceptCall = function(call) { |
|
const PROTOCOL = {'_':'phoneCallProtocol', flags: 1, min_layer: 65, max_layer: 65}; |
|
|
|
return getDhConfig().then(function(dhc) { |
|
state = {}; |
|
|
|
state.incoming = true; |
|
|
|
state.peer = { |
|
_: 'inputPhoneCall', |
|
id: call.id, |
|
access_hash: call.access_hash, |
|
} |
|
|
|
state.user_id = getInputUserByID(call.admin_id); |
|
state.random_id = parseInt(0x7fffffff * Math.random()); |
|
|
|
state.g_int = dhc.g_int; |
|
state.p_int = dhc.p_int; |
|
|
|
state.b_int = new BigInteger(getRandBytes()); |
|
|
|
state.g_b_int = state.g_int.modPow(state.b_int, state.p_int); |
|
state.g_b = bytesFromHex(state.g_b_int.toString(16)); |
|
state.g_a_hash = call.g_a_hash; |
|
|
|
state.my_proto = PROTOCOL; |
|
|
|
return state; |
|
}).then(function(state) { |
|
calls[call.id] = state; |
|
return invokeApi('phone.acceptCall', { |
|
peer: state.peer, |
|
g_b: state.g_b, |
|
protocol: state.my_proto |
|
}).then(function (phoneCall) { |
|
console.warn(`[CALL] Call confirmed:`, phoneCall); |
|
return processPhoneCall(phoneCall.phone_call); |
|
}) |
|
}); |
|
} |
|
|
|
var calcFingerprint = function (key) { |
|
return (new BigInteger(sha1BytesSync(key).slice(-8).reverse())).toString(10); |
|
} |
|
|
|
processFullCall = function(call) { |
|
state = calls[call.id]; |
|
if (state.incoming) { |
|
console.log(`[CALL] Got more info about call from ${call.admin_id} we've accepted.`); |
|
state.g_a = call.g_a_or_b; |
|
state.g_a_int = new BigInteger(state.g_a); |
|
|
|
state.pt_g_a_hash = sha256HashSync(state.g_a); |
|
if (!bytesEqual(state.pt_g_a_hash, state.g_a_hash)) { |
|
console.error(`[CALL] HASH(G_A) != G_A_HASH!`, state.pt_g_a_hash, state.g_a_hash); |
|
} |
|
|
|
state.key_int = state.g_a_int.modPow(state.b_int, state.p_int); |
|
state.key = bytesFromHex(state.key_int.toString(16)); |
|
|
|
state.key_fingerprint = calcFingerprint(state.key); |
|
state.pt_key_fingerprint = call.key_fingerprint; |
|
|
|
if (state.pt_key_fingerprint != state.key_fingerprint) { |
|
console.error(`[CALL] Fingerprint mismatch! Got ${state.pt_key_fingerprint}, expected ${state.key_fingerprint}!`); |
|
} |
|
} |
|
|
|
state.connection = call.connection; |
|
state.alternative_connections = call.alternative_connections; |
|
|
|
calls[call.id] = state; |
|
|
|
console.info(`[CALL] Call #${call.id} ready!`); |
|
copyToClipboard(dumpCallForCLI(state)); |
|
} |
|
|
|
callMeMaybe = function(inputUser) { |
|
const PROTOCOL = {'_':'phoneCallProtocol', flags: 1, min_layer: 65, max_layer: 65}; |
|
|
|
return getDhConfig().then(function(dhc) { |
|
state = {}; |
|
state.incoming = false; |
|
|
|
state.user_id = inputUser; |
|
state.random_id = parseInt(0x7fffffff * Math.random()); |
|
|
|
state.g_int = dhc.g_int; |
|
state.p_int = dhc.p_int; |
|
|
|
state.a_int = new BigInteger(getRandBytes()); |
|
|
|
state.g_a_int = state.g_int.modPow(state.a_int, state.p_int); |
|
state.g_a = bytesFromHex(state.g_a_int.toString(16)); |
|
state.g_a_hash = sha256HashSync(state.g_a); |
|
state.my_proto = PROTOCOL; |
|
return state; |
|
}).then(function(state) { |
|
return invokeApi('phone.requestCall', { |
|
user_id: state.user_id, |
|
random_id: state.random_id, |
|
g_a_hash: state.g_a_hash, |
|
protocol: state.my_proto |
|
}).then(function(phoneCall) { |
|
phoneCall = phoneCall.phone_call; |
|
state.peer = { |
|
_: 'inputPhoneCall', |
|
id: phoneCall.id, |
|
access_hash: phoneCall.access_hash, |
|
} |
|
calls[phoneCall.id] = state; |
|
return processPhoneCall(phoneCall); |
|
}); |
|
}); |
|
}; |
|
|
|
callStageTwo = function(call) { |
|
var state = calls[call.id]; |
|
state.prt_proto = call.protocol; |
|
|
|
state.g_b = call.g_b; |
|
state.g_b_int = new BigInteger(state.g_b); |
|
|
|
state.key_int = state.g_b_int.modPow(state.a_int, state.p_int); |
|
state.key = bytesFromHex(state.key_int.toString(16)); |
|
state.key_fingerprint = calcFingerprint(state.key); |
|
|
|
calls[call.id] = state; |
|
|
|
return invokeApi('phone.confirmCall', { |
|
peer: { |
|
_: 'inputPhoneCall', |
|
id: call.id, |
|
access_hash: call.access_hash |
|
}, |
|
g_a: state.g_a, |
|
key_fingerprint: state.key_fingerprint, |
|
protocol: state.my_proto |
|
}).then(function(phoneCall) { |
|
console.info(`[CALL] Call confirmed:`, phoneCall); |
|
return processPhoneCall(phoneCall.phone_call); |
|
}); |
|
}; |
|
|
|
dumpCallForCLI = function(state) { |
|
let dumpBytes = bytes => Array.prototype.slice.call(bytes).map(i=>`\\x${i<16?'0':''}${i.toString(16)}`).join(''); // to cstring |
|
let dumpEndpoint = conn => |
|
['{', |
|
`auto ipv4 = IPv4Address("${conn.ip }");`, |
|
`auto ipv6 = IPv6Address("${conn.ipv6}");`, |
|
`endpoints.emplace_back(${conn.id}, ${conn.port}, ipv4, ipv6, (char) EP_TYPE_UDP_RELAY, (unsigned char*) "${dumpBytes(conn.peer_tag)}");`, |
|
'};'].join('\n'); |
|
|
|
ret = ['\n\n\n\n']; |
|
|
|
ret.push(`is_outgoing = ${!state.incoming};`); |
|
ret.push(`key = "${dumpBytes(state.key)}";`); |
|
|
|
ret.push(dumpEndpoint(state.connection)); |
|
for (conn of state.alternative_connections) |
|
ret.push(dumpEndpoint(conn)); |
|
|
|
ret.push('\n\n\n\n'); |
|
|
|
return ret.join('\n'); |
|
} |
|
|
|
copyToClipboard = function(text) { |
|
var copyFrom = document.createElement('textarea'); |
|
copyFrom.style.position = "absolute"; |
|
copyFrom.style.left = "10px"; |
|
copyFrom.style.top = "50px"; |
|
copyFrom.value = text; |
|
document.body.append(copyFrom); |
|
document.execCommand('copy'); |
|
copyFrom.onfocus=()=>(copyFrom.select(),document.execCommand('copy'),document.body.removeChild(copyFrom)); |
|
} |
when i try to build, i get the following error
Here is my Dockerfile