PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/php/whatsprot.class.php

https://github.com/cbeneyto/WhatsAPI
PHP | 364 lines | 314 code | 48 blank | 2 comment | 29 complexity | 5617c88c824fd300338b8858cacf0c95 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 $_socket;
  25. protected $_writer;
  26. protected $_reader;
  27. protected $_inputKey;
  28. protected $_outputKey;
  29. protected $_debug;
  30. function __construct($Number, $imei, $Nickname, $debug = false)
  31. {
  32. $this->_debug = $debug;
  33. $dict = getDictionary();
  34. $this->_writer = new BinTreeNodeWriter($dict);
  35. $this->_reader = new BinTreeNodeReader($dict);
  36. $this->_phoneNumber = $Number;
  37. $this->_imei = $imei;
  38. $this->_name = $Nickname;
  39. $this->_loginStatus = $this->_disconnectedStatus;
  40. }
  41. protected function addFeatures()
  42. {
  43. $child = new ProtocolNode("receipt_acks", NULL, NULL, "");
  44. $parent = new ProtocolNode("stream:features", NULL, array($child), "");
  45. return $parent;
  46. }
  47. protected function addAuth()
  48. {
  49. $authHash = array();
  50. $authHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
  51. $authHash["mechanism"] = "WAUTH-1";
  52. $authHash["user"] = $this->_phoneNumber;
  53. $node = new ProtocolNode("auth", $authHash, NULL, "");
  54. return $node;
  55. }
  56. public function encryptPassword()
  57. {
  58. if(stripos($this->_imei, ":") !== false){
  59. $this->_imei = strtoupper($this->_imei);
  60. return md5($this->_imei.$this->_imei);
  61. }
  62. else {
  63. return md5(strrev($this->_imei));
  64. }
  65. }
  66. protected function authenticate()
  67. {
  68. $key = pbkdf2("sha1", $this->encryptPassword(), $this->challengeData, 16, 20, true);
  69. $this->_inputKey = new KeyStream($key);
  70. $this->_outputKey = new KeyStream($key);
  71. $array = $this->_phoneNumber.$this->challengeData.time();
  72. $response = $this->_outputKey->encode($array, 0, strlen($array), false);
  73. return $response;
  74. }
  75. protected function addAuthResponse()
  76. {
  77. $resp = $this->authenticate();
  78. $respHash = array();
  79. $respHash["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl";
  80. $node = new ProtocolNode("response", $respHash, NULL, $resp);
  81. return $node;
  82. }
  83. protected function sendData($data)
  84. {
  85. socket_send( $this->_socket, $data, strlen($data), 0 );
  86. }
  87. protected function sendNode($node)
  88. {
  89. $this->DebugPrint($node->NodeString("tx ") . "\n");
  90. $this->sendData($this->_writer->write($node));
  91. }
  92. protected function readData()
  93. {
  94. $buff = "";
  95. $ret = socket_read( $this->_socket, 1024 );
  96. if ($ret)
  97. {
  98. $buff = $this->_incomplete_message . $ret;
  99. $this->_incomplete_message = "";
  100. }
  101. return $buff;
  102. }
  103. protected function processChallenge($node)
  104. {
  105. $this->challengeData = $node->_data;
  106. }
  107. protected function sendMessageReceived($msg)
  108. {
  109. $requestNode = $msg->getChild("request");
  110. if ($requestNode != null)
  111. {
  112. $xmlnsAttrib = $requestNode->getAttribute("xmlns");
  113. if (strcmp($xmlnsAttrib, "urn:xmpp:receipts") == 0)
  114. {
  115. $recievedHash = array();
  116. $recievedHash["xmlns"] = "urn:xmpp:receipts";
  117. $receivedNode = new ProtocolNode("received", $recievedHash, null, "");
  118. $messageHash = array();
  119. $messageHash["to"] = $msg->getAttribute("from");
  120. $messageHash["type"] = "chat";
  121. $messageHash["id"] = $msg->getAttribute("id");
  122. $messageNode = new ProtocolNode("message", $messageHash, array($receivedNode), "");
  123. $this->sendNode($messageNode);
  124. }
  125. }
  126. }
  127. protected function processInboundData($data)
  128. {
  129. try
  130. {
  131. $node = $this->_reader->nextTree($data);
  132. while ($node != null)
  133. {
  134. $this->DebugPrint($node->NodeString("rx ") . "\n");
  135. if (strcmp($node->_tag, "challenge") == 0)
  136. {
  137. $this->processChallenge($node);
  138. }
  139. else if (strcmp($node->_tag, "success") == 0)
  140. {
  141. $this->_loginStatus = $this->_connectedStatus;
  142. $this->_accountinfo = array('status'=>$node->getAttribute('status'),'kind'=>$node->getAttribute('kind'),'creation'=>$node->getAttribute('creation'),'expiration'=>$node->getAttribute('expiration'));
  143. }
  144. if (strcmp($node->_tag, "message") == 0)
  145. {
  146. array_push($this->_messageQueue, $node);
  147. $this->sendMessageReceived($node);
  148. }
  149. if (strcmp($node->_tag, "iq") == 0 AND strcmp($node->_attributeHash['type'], "get") == 0 AND strcmp($node->_children[0]->_tag, "ping") == 0)
  150. {
  151. $this->Pong($node->_attributeHash['id']);
  152. }
  153. if (strcmp($node->_tag, "iq") == 0 AND strcmp($node->_attributeHash['type'], "result") == 0 AND strcmp($node->_children[0]->_tag, "query") == 0)
  154. {
  155. array_push($this->_messageQueue, $node);
  156. }
  157. $node = $this->_reader->nextTree();
  158. }
  159. }
  160. catch (IncompleteMessageException $e)
  161. {
  162. $this->_incomplete_message = $e->getInput();
  163. }
  164. }
  165. public function accountInfo(){
  166. if(is_array($this->_accountinfo)){
  167. print_r($this->_accountinfo);
  168. }
  169. else{
  170. echo "No information available";
  171. }
  172. }
  173. public function Connect(){
  174. $Socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
  175. socket_connect( $Socket, $this->_whatsAppHost, $this->_port );
  176. $this->_socket = $Socket;
  177. socket_set_option($this->_socket, SOL_SOCKET, SO_RCVTIMEO, $this->_timeout);
  178. }
  179. public function Login()
  180. {
  181. $resource = "$this->_device-$this->_whatsAppVer-$this->_port";
  182. $data = $this->_writer->StartStream($this->_whatsAppServer, $resource);
  183. $feat = $this->addFeatures();
  184. $auth = $this->addAuth();
  185. $this->sendData($data);
  186. $this->sendNode($feat);
  187. $this->sendNode($auth);
  188. $this->processInboundData($this->readData());
  189. $data = $this->addAuthResponse();
  190. $this->sendNode($data);
  191. $this->_reader->setKey($this->_inputKey);
  192. $this->_writer->setKey($this->_outputKey);
  193. $cnt = 0;
  194. do
  195. {
  196. $this->processInboundData($this->readData());
  197. } while (($cnt++ < 100) && (strcmp($this->_loginStatus, $this->_disconnectedStatus) == 0));
  198. }
  199. # Pull from the socket, and place incoming messages in the message queue
  200. public function PollMessages()
  201. {
  202. $this->processInboundData($this->readData());
  203. }
  204. # Drain the message queue for application processing
  205. public function GetMessages()
  206. {
  207. $ret = $this->_messageQueue;
  208. $this->_messageQueue = array();
  209. return $ret;
  210. }
  211. protected function SendMessageNode($msgid, $to, $node)
  212. {
  213. $serverNode = new ProtocolNode("server", null, null, "");
  214. $xHash = array();
  215. $xHash["xmlns"] = "jabber:x:event";
  216. $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
  217. $messageHash = array();
  218. $messageHash["to"] = $to . "@" . $this->_whatsAppServer;
  219. $messageHash["type"] = "chat";
  220. $messageHash["id"] = $msgid;
  221. $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $node), "");
  222. $this->sendNode($messsageNode);
  223. }
  224. public function Message($msgid, $to, $txt)
  225. {
  226. $bodyNode = new ProtocolNode("body", null, null, $txt);
  227. $this->SendMessageNode($msgid, $to, $bodyNode);
  228. }
  229. public function MessageImage($msgid, $to, $url, $file, $size, $icon)
  230. {
  231. $mediaAttribs = array();
  232. $mediaAttribs["xmlns"] = "urn:xmpp:whatsapp:mms";
  233. $mediaAttribs["type"] = "image";
  234. $mediaAttribs["url"] = $url;
  235. $mediaAttribs["file"] = $file;
  236. $mediaAttribs["size"] = $size;
  237. $mediaNode = new ProtocolNode("media", $mediaAttribs, null, $icon);
  238. $this->SendMessageNode($msgid, $to, $mediaNode);
  239. }
  240. public function Location($msgid, $to, $long, $lat)
  241. {
  242. $whatsAppServer = $this->_whatsAppServer;
  243. $mediaHash = array();
  244. $mediaHash['type'] = "location";
  245. $mediaHash['longitude'] = $long;
  246. $mediaHash['latitude'] = $lat;
  247. $mediaHash['xmlns'] = "urn:xmpp:whatsapp:mms";
  248. $mediaNode = new ProtocolNode("media", $mediaHash, null, null);
  249. $messageHash = array();
  250. $messageHash["to"] = $to . "@" . $whatsAppServer;
  251. $messageHash["type"] = "chat";
  252. $messageHash["id"] = $msgid;
  253. $messageHash["author"] = $this->_phoneNumber . "@" . $this->_whatsAppServer;
  254. $messsageNode = new ProtocolNode("message", $messageHash, array($mediaNode), "");
  255. $this->sendNode($messsageNode);
  256. }
  257. public function sendStatusUpdate($msgid, $txt)
  258. {
  259. $bodyNode = new ProtocolNode("body", null, null, $txt);
  260. $serverNode = new ProtocolNode("server", null, null, "");
  261. $xHash = array();
  262. $xHash["xmlns"] = "jabber:x:event";
  263. $xNode = new ProtocolNode("x", $xHash, array($serverNode), "");
  264. $messageHash = array();
  265. $messageHash["to"] = 's.us';
  266. $messageHash["type"] = "chat";
  267. $messageHash["id"] = $msgid;
  268. $messsageNode = new ProtocolNode("message", $messageHash, array($xNode, $bodyNode), "");
  269. $this->sendNode($messsageNode);
  270. }
  271. public function Pong($msgid)
  272. {
  273. $whatsAppServer = $this->_whatsAppServer;
  274. $messageHash = array();
  275. $messageHash["to"] = $whatsAppServer;
  276. $messageHash["id"] = $msgid;
  277. $messageHash["type"] = "result";
  278. $messsageNode = new ProtocolNode("iq", $messageHash, null, "");
  279. $this->sendNode($messsageNode);
  280. }
  281. public function sendNickname($nickname)
  282. {
  283. $messageHash = array();
  284. $messageHash["name"] = $nickname;
  285. $messsageNode = new ProtocolNode("presence", $messageHash, null, "");
  286. $this->sendNode($messsageNode);
  287. }
  288. public function sendPresence($available = true){
  289. $messageHash = array();
  290. $messageHash["from"] = $this->_phoneNumber.'@'.$this->_whatsAppServer;
  291. $messageHash["type"] = ($available) ? 'available' : 'unavailable';
  292. $messsageNode = new ProtocolNode("presence", $messageHash, null, "");
  293. $this->sendNode($messsageNode);
  294. }
  295. protected function DebugPrint($debugMsg)
  296. {
  297. if ($this->_debug)
  298. {
  299. print($debugMsg);
  300. }
  301. }
  302. public function RequestLastSeen($msgid, $to)
  303. {
  304. $whatsAppServer = $this->_whatsAppServer;
  305. $queryHash = array();
  306. $queryHash['xmlns'] = "jabber:iq:last";
  307. $queryNode = new ProtocolNode("query", $queryHash, null, null);
  308. $messageHash = array();
  309. $messageHash["to"] = $to . "@" . $whatsAppServer;
  310. $messageHash["type"] = "get";
  311. $messageHash["id"] = $msgid;
  312. $messageHash["from"] = $this->_phoneNumber . "@" . $this->_whatsAppServer;
  313. $messsageNode = new ProtocolNode("iq", $messageHash, array($queryNode), "");
  314. $this->sendNode($messsageNode);
  315. }
  316. }