PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/php/whatsprot.class.php

https://github.com/fefantom/WhatsAPI
PHP | 445 lines | 388 code | 52 blank | 5 comment | 37 complexity | e1dccf67250fc6d7b1ad8bf26a479750 MD5 | raw file
  1. <?php
  2. require "protocol.class.php";
  3. require "func.php";
  4. require "rc4.php";
  5. class WhatsProt
  6. {
  7. protected $_phoneNumber;
  8. protected $_imei;
  9. protected $_name;
  10. protected $_whatsAppHost = "c.whatsapp.net";
  11. protected $_whatsAppServer = "s.whatsapp.net";
  12. protected $_whatsAppRealm = "s.whatsapp.net";
  13. protected $_whatsAppDigest = "xmpp/s.whatsapp.net";
  14. protected $_device = "iPhone";
  15. protected $_whatsAppVer = "2.8.4";
  16. protected $_port = 5222;
  17. protected $_timeout = array("sec" => 2, "usec" => 0);
  18. protected $_incomplete_message = "";
  19. protected $_disconnectedStatus = "disconnected";
  20. protected $_connectedStatus = "connected";
  21. protected $_loginStatus;
  22. protected $_accountinfo;
  23. protected $_messageQueue = array();
  24. protected $_outQueue = array();
  25. protected $_lastId = false;
  26. protected $_msgCounter = 1;
  27. protected $_socket;
  28. protected $_writer;
  29. protected $_reader;
  30. protected $_inputKey;
  31. protected $_outputKey;
  32. protected $_debug;
  33. protected $_newmsgBind = false;
  34. function __construct($Number, $imei, $Nickname, $debug = false)
  35. {
  36. $this->_debug = $debug;
  37. $dict = getDictionary();
  38. $this->_writer = new BinTreeNodeWriter($dict);
  39. $this->_reader = new BinTreeNodeReader($dict);
  40. $this->_phoneNumber = $Number;
  41. $this->_imei = $imei;
  42. $this->_name = $Nickname;
  43. $this->_loginStatus = $this->_disconnectedStatus;
  44. }
  45. protected function addFeatures()
  46. {
  47. $child = new ProtocolNode("receipt_acks", NULL, NULL, "");
  48. $parent = new ProtocolNode("stream:features", NULL, array($child), "");
  49. return $parent;
  50. }
  51. protected function addAuth()
  52. {
  53. $authHash = array();
  54. $authHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
  55. $authHash["mechanism"] = "WAUTH-1";
  56. $authHash["user"] = $this->_phoneNumber;
  57. $node = new ProtocolNode("auth", $authHash, NULL, "");
  58. return $node;
  59. }
  60. public function encryptPassword()
  61. {
  62. if(stripos($this->_imei, ":") !== false){
  63. $this->_imei = strtoupper($this->_imei);
  64. return md5($this->_imei.$this->_imei);
  65. }
  66. else
  67. {
  68. return md5(strrev($this->_imei));
  69. }
  70. }
  71. protected function authenticate()
  72. {
  73. $key = pbkdf2("sha1", $this->encryptPassword(), $this->challengeData, 16, 20, true);
  74. $this->_inputKey = new KeyStream($key);
  75. $this->_outputKey = new KeyStream($key);
  76. $array = $this->_phoneNumber.$this->challengeData.time();
  77. $response = $this->_outputKey->encode($array, 0, strlen($array), false);
  78. return $response;
  79. }
  80. public function setNewMessageBind($bind)
  81. {
  82. $this->_newmsgBind = $bind;
  83. }
  84. public function addOutQueue($node)
  85. {
  86. $this->_outQueue[] = $node;
  87. }
  88. protected function addAuthResponse()
  89. {
  90. $resp = $this->authenticate();
  91. $respHash = array();
  92. $respHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
  93. $node = new ProtocolNode("response", $respHash, NULL, $resp);
  94. return $node;
  95. }
  96. protected function sendData($data)
  97. {
  98. socket_send( $this->_socket, $data, strlen($data), 0 );
  99. }
  100. protected function sendNode($node)
  101. {
  102. $this->DebugPrint($node->NodeString("tx ") . "\n");
  103. $this->sendData($this->_writer->write($node));
  104. }
  105. protected function readData()
  106. {
  107. $buff = "";
  108. $ret = socket_read( $this->_socket, 1024 );
  109. if ($ret)
  110. {
  111. $buff = $this->_incomplete_message . $ret;
  112. $this->_incomplete_message = "";
  113. }
  114. return $buff;
  115. }
  116. protected function processChallenge($node)
  117. {
  118. $this->challengeData = $node->_data;
  119. }
  120. protected function sendMessageReceived($msg)
  121. {
  122. $requestNode = $msg->getChild("request");
  123. $receivedNode = $msg->getChild("received");
  124. if ($requestNode != null || $receivedNode != null)
  125. {
  126. $recievedHash = array();
  127. $recievedHash["xmlns"] = "urn:xmpp:receipts";
  128. $receivedNode = new ProtocolNode("received", $recievedHash, null, "");
  129. $messageHash = array();
  130. $messageHash["to"] = $msg->getAttribute("from");
  131. $messageHash["type"] = "chat";
  132. $messageHash["id"] = $msg->getAttribute("id");
  133. $messageHash["t"] = time();
  134. $messageNode = new ProtocolNode("message", $messageHash, array($receivedNode), "");
  135. $this->sendNode($messageNode);
  136. }
  137. }
  138. protected function processInboundData($data)
  139. {
  140. try
  141. {
  142. $node = $this->_reader->nextTree($data);
  143. while ($node != null)
  144. {
  145. $this->DebugPrint($node->NodeString("rx ") . "\n");
  146. if (strcmp($node->_tag, "challenge") == 0)
  147. {
  148. $this->processChallenge($node);
  149. }
  150. else if (strcmp($node->_tag, "success") == 0)
  151. {
  152. $this->_loginStatus = $this->_connectedStatus;
  153. $this->_accountinfo = array('status'=>$node->getAttribute('status'),'kind'=>$node->getAttribute('kind'),'creation'=>$node->getAttribute('creation'),'expiration'=>$node->getAttribute('expiration'));
  154. }
  155. if (strcmp($node->_tag, "message") == 0)
  156. {
  157. array_push($this->_messageQueue, $node);
  158. $this->sendMessageReceived($node);
  159. if($node->hasChild('x') && $this->_lastId==$node->getAttribute('id'))
  160. $this->sendNext();
  161. if($this->_newmsgBind && $node->getChild('body'))
  162. $this->_newmsgBind->process($node);
  163. }
  164. if (strcmp($node->_tag, "iq") == 0 AND strcmp($node->_attributeHash['type'], "get") == 0 AND strcmp($node->_children[0]->_tag, "ping") == 0)
  165. {
  166. $this->Pong($node->_attributeHash['id']);
  167. }
  168. if (strcmp($node->_tag, "iq") == 0 AND strcmp($node->_attributeHash['type'], "result") == 0 AND strcmp($node->_children[0]->_tag, "query") == 0)
  169. {
  170. array_push($this->_messageQueue, $node);
  171. }
  172. $node = $this->_reader->nextTree();
  173. }
  174. }
  175. catch (IncompleteMessageException $e)
  176. {
  177. $this->_incomplete_message = $e->getInput();
  178. }
  179. }
  180. public function sendNext()
  181. {
  182. if(count($this->_outQueue)>0)
  183. {
  184. $msgnode = array_shift($this->_outQueue);
  185. $msgnode->refreshTimes();
  186. $this->_lastId = $msgnode->getAttribute('id');
  187. $this->sendNode($msgnode);
  188. }else
  189. $this->_lastId = false;
  190. }
  191. public function sendComposing($msg)
  192. {
  193. $comphash = array();
  194. $comphash['xmlns'] = "http://jabber.org/protocol/chatstates";
  195. $compose = new ProtocolNode("composing", $comphash, null, "");
  196. $messageHash = array();
  197. $messageHash["to"] = $msg->getAttribute("from");
  198. $messageHash["type"] = "chat";
  199. $messageHash["id"] = time().'-'.$this->_msgCounter;
  200. $messageHash["t"] = time();
  201. $this->_msgCounter++;
  202. $messageNode = new ProtocolNode("message", $messageHash, array($compose), "");
  203. $this->sendNode($messageNode);
  204. }
  205. public function accountInfo(){
  206. if(is_array($this->_accountinfo)){
  207. print_r($this->_accountinfo);
  208. }
  209. else{
  210. echo "No information available";
  211. }
  212. }
  213. public function Connect(){
  214. $Socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
  215. socket_connect( $Socket, $this->_whatsAppHost, $this->_port );
  216. $this->_socket = $Socket;
  217. socket_set_option($this->_socket, SOL_SOCKET, SO_RCVTIMEO, $this->_timeout);
  218. }
  219. public function Login()
  220. {
  221. $resource = "$this->_device-$this->_whatsAppVer-$this->_port";
  222. $data = $this->_writer->StartStream($this->_whatsAppServer, $resource);
  223. $feat = $this->addFeatures();
  224. $auth = $this->addAuth();
  225. $this->sendData($data);
  226. $this->sendNode($feat);
  227. $this->sendNode($auth);
  228. $this->processInboundData($this->readData());
  229. $data = $this->addAuthResponse();
  230. $this->sendNode($data);
  231. $this->_reader->setKey($this->_inputKey);
  232. $this->_writer->setKey($this->_outputKey);
  233. $cnt = 0;
  234. do
  235. {
  236. $this->processInboundData($this->readData());
  237. } while (($cnt++ < 100) && (strcmp($this->_loginStatus, $this->_disconnectedStatus) == 0));
  238. $this->sendNickname();
  239. $this->SendPresence();
  240. }
  241. # Pull from the socket, and place incoming messages in the message queue
  242. public function PollMessages()
  243. {
  244. $this->processInboundData($this->readData());
  245. }
  246. # Drain the message queue for application processing
  247. public function GetMessages()
  248. {
  249. $ret = $this->_messageQueue;
  250. $this->_messageQueue = array();
  251. return $ret;
  252. }
  253. public function WaitforReceipt()
  254. {
  255. $received = false;
  256. do{
  257. $this->PollMessages();
  258. $msgs = $this->GetMessages();
  259. foreach ($msgs as $m)
  260. {
  261. # process inbound messages
  262. if($m->_tag == "message"){
  263. if($m->getChild('received')!=null){
  264. $received = true;
  265. }
  266. }
  267. //print($m->NodeString("") . "\n");
  268. }
  269. }while(!$received);
  270. //echo "Received node!!\n";
  271. }
  272. public function SendPresence($type="available")
  273. {
  274. $presence = array();
  275. $presence['type'] = $type;
  276. $presence['name'] = $this->_name;
  277. $node = new ProtocolNode("presence", $presence, null, "");
  278. $this->sendNode($node);
  279. }
  280. protected function SendMessageNode($to, $node)
  281. {
  282. $serverNode = new ProtocolNode("server", null, null, "");
  283. $xHash = array();
  284. $xHash["xmlns"] = "jabber:x:event";
  285. $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
  286. $notify = array();
  287. $notify['xmlns'] = 'urn:xmpp:whatsapp';
  288. $notify['name'] = $this->_name;
  289. $notnode = new ProtocolNode("notify", $notify, null, "");
  290. $request = array();
  291. $request['xmlns'] = "urn:xmpp:receipts";
  292. $reqnode = new ProtocolNode("request", $request, null, "");
  293. $msgid = time().'-'.$this->_msgCounter;
  294. $messageHash = array();
  295. $messageHash["to"] = $to . "@" . $this->_whatsAppServer;
  296. $messageHash["type"] = "chat";
  297. $messageHash["id"] = $msgid;
  298. $messageHash["t"] = time();
  299. $this->_msgCounter++;
  300. $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $notnode,$reqnode,$node), "");
  301. if(!$this->_lastId){
  302. $this->_lastId = $msgid;
  303. $this->sendNode($messsageNode);
  304. }else
  305. $this->_outQueue[] = $messsageNode;
  306. }
  307. public function Message($to, $txt)
  308. {
  309. $bodyNode = new ProtocolNode("body", null, null, $txt);
  310. $this->SendMessageNode($to, $bodyNode);
  311. }
  312. public function MessageImage($to, $url, $file, $size, $icon)
  313. {
  314. $mediaAttribs = array();
  315. $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
  316. $mediaAttribs["type"] = "image";
  317. $mediaAttribs["url"] = $url;
  318. $mediaAttribs["file"] = $file;
  319. $mediaAttribs["size"] = $size;
  320. $mediaNode = new ProtocolNode("media", $mediaAttribs, null, $icon);
  321. $this->SendMessageNode($to, $mediaNode);
  322. }
  323. public function Location($msgid, $to, $long, $lat)
  324. {
  325. $whatsAppServer = $this->_whatsAppServer;
  326. $mediaHash = array();
  327. $mediaHash['type'] = "location";
  328. $mediaHash['longitude'] = $long;
  329. $mediaHash['latitude'] = $lat;
  330. $mediaHash['xmlns'] = "urn:xmpp:whatsapp:mms";
  331. $mediaNode = new ProtocolNode("media", $mediaHash, null, null);
  332. $messageHash = array();
  333. $messageHash["to"] = $to . "@" . $whatsAppServer;
  334. $messageHash["type"] = "chat";
  335. $messageHash["id"] = $msgid;
  336. $messageHash["author"] = $this->_phoneNumber . "@" . $this->_whatsAppServer;
  337. $messsageNode = new ProtocolNode("message", $messageHash, array($mediaNode), "");
  338. $this->sendNode($messsageNode);
  339. }
  340. public function sendStatusUpdate($msgid, $txt)
  341. {
  342. $bodyNode = new ProtocolNode("body", null, null, $txt);
  343. $serverNode = new ProtocolNode("server", null, null, "");
  344. $xHash = array();
  345. $xHash["xmlns"] = "jabber:x:event";
  346. $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
  347. $messageHash = array();
  348. $messageHash["to"] = 's.us';
  349. $messageHash["type"] = "chat";
  350. $messageHash["id"] = $msgid;
  351. $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $bodyNode), "");
  352. $this->sendNode($messsageNode);
  353. }
  354. public function Pong($msgid)
  355. {
  356. $whatsAppServer = $this->_whatsAppServer;
  357. $messageHash = array();
  358. $messageHash["to"] = $whatsAppServer;
  359. $messageHash["id"] = $msgid;
  360. $messageHash["type"] = "result";
  361. $messsageNode = new ProtocolNode("iq", $messageHash, null, "");
  362. $this->sendNode($messsageNode);
  363. }
  364. public function sendNickname()
  365. {
  366. $messageHash = array();
  367. $messageHash["name"] = $this->_name;
  368. $messsageNode = new ProtocolNode("presence", $messageHash, null, "");
  369. $this->sendNode($messsageNode);
  370. }
  371. protected function DebugPrint($debugMsg)
  372. {
  373. if ($this->_debug)
  374. {
  375. print($debugMsg);
  376. }
  377. }
  378. public function RequestLastSeen($msgid, $to)
  379. {
  380. $whatsAppServer = $this->_whatsAppServer;
  381. $queryHash = array();
  382. $queryHash['xmlns'] = "jabber:iq:last";
  383. $queryNode = new ProtocolNode("query", $queryHash, null, null);
  384. $messageHash = array();
  385. $messageHash["to"] = $to . "@" . $whatsAppServer;
  386. $messageHash["type"] = "get";
  387. $messageHash["id"] = $msgid;
  388. $messageHash["from"] = $this->_phoneNumber . "@" . $this->_whatsAppServer;
  389. $messsageNode = new ProtocolNode("iq", $messageHash, array($queryNode), "");
  390. $this->sendNode($messsageNode);
  391. }
  392. }
  393. ?>