Permalink
900f8cf Mar 2, 2016
4 contributors

Users who have contributed to this file

@mgp25 @WHAnonymous @SikiFn @derpue
153 lines (132 sloc) 4.98 KB
<?php
class Login
{
protected $phoneNumber;
protected $password;
protected $challengeData;
protected $inputKey;
protected $outputKey;
protected $parent;
public function __construct(WhatsProt $parent, $password)
{
$this->parent = $parent;
$this->password = $password;
$this->phoneNumber = $this->parent->getMyNumber();
}
/**
* Send the nodes to the WhatsApp server to log in.
*
* @throws Exception
*/
public function doLogin()
{
if ($this->parent->isLoggedIn()) {
return true;
}
$this->parent->writer->resetKey();
$this->parent->reader->resetKey();
$resource = Constants::PLATFORM.'-'.Constants::WHATSAPP_VER;
$data = $this->parent->writer->StartStream(Constants::WHATSAPP_SERVER, $resource);
$feat = $this->createFeaturesNode();
$auth = $this->createAuthNode();
$this->parent->sendData($data);
$this->parent->sendNode($feat);
$this->parent->sendNode($auth);
$this->parent->pollMessage();
$this->parent->pollMessage();
$this->parent->pollMessage();
if ($this->parent->getChallengeData() != null) {
$data = $this->createAuthResponseNode();
$this->parent->sendNode($data);
$this->parent->reader->setKey($this->inputKey);
$this->parent->writer->setKey($this->outputKey);
while (!$this->parent->pollMessage()) {
};
}
if ($this->parent->getLoginStatus() === Constants::DISCONNECTED_STATUS) {
throw new LoginFailureException();
}
$this->parent->logFile('info', '{number} successfully logged in', ['number' => $this->phoneNumber]);
$this->parent->sendAvailableForChat();
$this->parent->sendGetPrivacyBlockedList();
$this->parent->sendGetClientConfig();
$this->parent->setMessageId(substr(bin2hex(mcrypt_create_iv(64, MCRYPT_DEV_URANDOM)), 0, 22)); // 11 char hex
if (extension_loaded('curve25519') || extension_loaded('protobuf')) {
if (file_exists($this->parent->dataFolder.'axolotl-'.$this->phoneNumber.'.db')) {
$pre_keys = $this->parent->getAxolotlStore()->loadPreKeys();
if (empty($pre_keys)) {
$this->parent->sendSetPreKeys();
$this->parent->logFile('info', 'Sending prekeys to WA server');
}
}
}
return true;
}
/**
* Add stream features.
*
* @return ProtocolNode Return itself.
*/
protected function createFeaturesNode()
{
/* $readreceipts = new ProtocolNode("readreceipts", null, null, null);
$groupsv2 = new ProtocolNode("groups_v2", null, null, null);
$privacy = new ProtocolNode("privacy", null, null, null);
$presencev2 = new ProtocolNode("presence", null, null, null);*/
$parent = new ProtocolNode('stream:features', null, null, null);
return $parent;
}
/**
* Add the authentication nodes.
*
* @return ProtocolNode Returns an authentication node.
*/
protected function createAuthNode()
{
$data = $this->createAuthBlob();
$attributes = [
'user' => $this->phoneNumber,
'mechanism' => 'WAUTH-2',
];
$node = new ProtocolNode('auth', $attributes, null, $data);
return $node;
}
protected function createAuthBlob()
{
if ($this->parent->getChallengeData()) {
$key = wa_pbkdf2('sha1', base64_decode($this->password), $this->parent->getChallengeData(), 16, 20, true);
$this->inputKey = new KeyStream($key[2], $key[3]);
$this->outputKey = new KeyStream($key[0], $key[1]);
$this->parent->reader->setKey($this->inputKey);
//$this->writer->setKey($this->outputKey);
$array = "\0\0\0\0".$this->phoneNumber.$this->parent->getChallengeData().time();
$this->parent->setChallengeData(null);
return $this->outputKey->EncodeMessage($array, 0, strlen($array), false);
}
}
/**
* Add the auth response to protocoltreenode.
*
* @return ProtocolNode Returns a response node.
*/
protected function createAuthResponseNode()
{
return new ProtocolNode('response', null, null, $this->authenticate());
}
/**
* Authenticate with the WhatsApp Server.
*
* @return string Returns binary string
*/
protected function authenticate()
{
$keys = KeyStream::GenerateKeys(base64_decode($this->password), $this->parent->getChallengeData());
$this->inputKey = new KeyStream($keys[2], $keys[3]);
$this->outputKey = new KeyStream($keys[0], $keys[1]);
$array = "\0\0\0\0".$this->phoneNumber.$this->parent->getChallengeData().''.time().'000'.hex2bin('00').'000'.hex2bin('00')
.Constants::OS_VERSION.hex2bin('00').Constants::MANUFACTURER.hex2bin('00').Constants::DEVICE.hex2bin('00').Constants::BUILD_VERSION;
$response = $this->outputKey->EncodeMessage($array, 0, 4, strlen($array) - 4);
$this->parent->setOutputKey($this->outputKey);
return $response;
}
}