PageRenderTime 41ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/php/protocol.class.php

https://github.com/cbeneyto/WhatsAPI
PHP | 558 lines | 516 code | 42 blank | 0 comment | 89 complexity | 7bc4e576324c7990de22ab2222a9c9a0 MD5 | raw file
  1. <?php
  2. require "decode.php";
  3. require "exception.php";
  4. class IncompleteMessageException extends CustomException
  5. {
  6. private $_input;
  7. public function __construct($message = null, $code = 0)
  8. {
  9. parent::__construct($message, $code);
  10. }
  11. public function setInput($input)
  12. {
  13. $this->_input = $input;
  14. }
  15. public function getInput()
  16. {
  17. return $this->_input;
  18. }
  19. }
  20. class ProtocolNode
  21. {
  22. public $_tag;
  23. public $_attributeHash;
  24. public $_children;
  25. public $_data;
  26. function __construct($tag, $attributeHash, $children, $data)
  27. {
  28. $this->_tag = $tag;
  29. $this->_attributeHash = $attributeHash;
  30. $this->_children = $children;
  31. $this->_data = $data;
  32. }
  33. public function NodeString($indent = "")
  34. {
  35. $ret = "\n" . $indent . "<" . $this->_tag;
  36. if ($this->_attributeHash != NULL)
  37. {
  38. foreach ($this->_attributeHash as $key => $value)
  39. {
  40. $ret .= " " . $key . "=\"" . $value . "\"";
  41. }
  42. }
  43. $ret .= ">";
  44. if (strlen($this->_data) > 0)
  45. {
  46. $ret .= $this->_data;
  47. }
  48. if ($this->_children)
  49. {
  50. foreach ($this->_children as $child)
  51. {
  52. $ret .= $child->NodeString($indent . " ");
  53. }
  54. $ret .= "\n" . $indent;
  55. }
  56. $ret .= "</" . $this->_tag . ">";
  57. return $ret;
  58. }
  59. public function getAttribute($attribute)
  60. {
  61. $ret = "";
  62. if (isset($this->_attributeHash[$attribute]))
  63. {
  64. $ret = $this->_attributeHash[$attribute];
  65. }
  66. return $ret;
  67. }
  68. public function getChild($tag)
  69. {
  70. $ret = NULL;
  71. if ($this->_children)
  72. {
  73. foreach ($this->_children as $child)
  74. {
  75. if (strcmp($child->_tag, $tag) == 0)
  76. {
  77. return $child;
  78. }
  79. $ret = $child->getChild($tag);
  80. if ($ret)
  81. {
  82. return $ret;
  83. }
  84. }
  85. }
  86. return NULL;
  87. }
  88. }
  89. class BinTreeNodeReader
  90. {
  91. private $_dictionary;
  92. private $_input;
  93. private $_key;
  94. function __construct($dictionary)
  95. {
  96. $this->_dictionary = $dictionary;
  97. }
  98. public function setKey($key)
  99. {
  100. $this->_key = $key;
  101. }
  102. public function nextTree($input = NULL)
  103. {
  104. if ($input != NULL)
  105. {
  106. $this->_input = $input;
  107. }
  108. $stanzaFlag = ($this->peekInt8() & 0xF0) >> 4;
  109. $stanzaSize = $this->peekInt16(1);
  110. if ($stanzaSize > strlen($this->_input))
  111. {
  112. $exception = new IncompleteMessageException("Incomplete message");
  113. $exception->setInput($this->_input);
  114. throw $exception;
  115. }
  116. $this->readInt24();
  117. if (($stanzaFlag & 8) && isset($this->_key))
  118. {
  119. $remainingData = substr($this->_input, $stanzaSize);
  120. $this->_input = $this->_key->decode($this->_input, 0, $stanzaSize) . $remainingData;
  121. }
  122. if ($stanzaSize > 0)
  123. {
  124. return $this->nextTreeInternal();
  125. }
  126. return NULL;
  127. }
  128. protected function getToken($token)
  129. {
  130. $ret = "";
  131. if (($token >= 0) && ($token < count($this->_dictionary)))
  132. {
  133. $ret = $this->_dictionary[$token];
  134. }
  135. else
  136. {
  137. throw new Exception("BinTreeNodeReader->getToken: Invalid token $token");
  138. }
  139. return $ret;
  140. }
  141. protected function readString($token)
  142. {
  143. $ret = "";
  144. if ($token == -1)
  145. {
  146. throw new Exception("BinTreeNodeReader->readString: Invalid token $token");
  147. }
  148. if (($token > 4) && ($token < 0xf5))
  149. {
  150. $ret = $this->getToken($token);
  151. }
  152. else if ($token == 0)
  153. {
  154. $ret = "";
  155. }
  156. else if ($token == 0xfc)
  157. {
  158. $size = $this->readInt8();
  159. $ret = $this->fillArray($size);
  160. }
  161. else if ($token == 0xfd)
  162. {
  163. $size = $this->readInt24();
  164. $ret = $this->fillArray($size);
  165. }
  166. else if ($token == 0xfe)
  167. {
  168. $token = $this->readInt8();
  169. $ret = $this->getToken($token + 0xf5);
  170. }
  171. else if ($token == 0xfa)
  172. {
  173. $user = $this->readString($this->readInt8());
  174. $server = $this->readString($this->readInt8());
  175. if ((strlen($user) > 0) && (strlen($server) > 0))
  176. {
  177. $ret = $user . "@" . $server;
  178. }
  179. else if (strlen($server) > 0)
  180. {
  181. $ret = $server;
  182. }
  183. }
  184. return $ret;
  185. }
  186. protected function readAttributes($size)
  187. {
  188. $attributes = array();
  189. $attribCount = ($size - 2 + $size % 2) / 2;
  190. for ($i = 0; $i < $attribCount; $i++)
  191. {
  192. $key = $this->readString($this->readInt8());
  193. $value = $this->readString($this->readInt8());
  194. $attributes[$key] = $value;
  195. }
  196. return $attributes;
  197. }
  198. protected function nextTreeInternal()
  199. {
  200. $token = $this->readInt8();
  201. $size = $this->readListSize($token);
  202. $token = $this->readInt8();
  203. if ($token == 1)
  204. {
  205. $attributes = $this->readAttributes($size);
  206. return new ProtocolNode("start", $attributes, NULL, "");
  207. }
  208. else if ($token == 2)
  209. {
  210. return NULL;
  211. }
  212. $tag = $this->readString($token);
  213. $attributes = $this->readAttributes($size);
  214. if (($size % 2) == 1)
  215. {
  216. return new ProtocolNode($tag, $attributes, NULL, "");
  217. }
  218. $token = $this->readInt8();
  219. if ($this->isListTag($token))
  220. {
  221. return new ProtocolNode($tag, $attributes, $this->readList($token), "");
  222. }
  223. return new ProtocolNode($tag, $attributes, NULL, $this->readString($token));
  224. }
  225. protected function isListTag($token)
  226. {
  227. return (($token == 248) || ($token == 0) || ($token == 249));
  228. }
  229. protected function readList($token)
  230. {
  231. $size = $this->readListSize($token);
  232. $ret = array();
  233. for ($i = 0; $i < $size; $i++)
  234. {
  235. array_push($ret, $this->nextTreeInternal());
  236. }
  237. return $ret;
  238. }
  239. protected function readListSize($token)
  240. {
  241. $size = 0;
  242. if ($token == 0xf8)
  243. {
  244. $size = $this->readInt8();
  245. }
  246. else if ($token == 0xf9)
  247. {
  248. $size = $this->readInt16();
  249. }
  250. else
  251. {
  252. throw new Exception("BinTreeNodeReader->readListSize: Invalid token $token");
  253. }
  254. return $size;
  255. }
  256. protected function peekInt24($offset = 0)
  257. {
  258. $ret = 0;
  259. if (strlen($this->_input) >= (3 + $offset))
  260. {
  261. $ret = ord(substr($this->_input, $offset, 1)) << 16;
  262. $ret |= ord(substr($this->_input, $offset + 1, 1)) << 8;
  263. $ret |= ord(substr($this->_input, $offset + 2, 1)) << 0;
  264. }
  265. return $ret;
  266. }
  267. protected function readInt24()
  268. {
  269. $ret = $this->peekInt24();
  270. if (strlen($this->_input) >= 3)
  271. {
  272. $this->_input = substr($this->_input, 3);
  273. }
  274. return $ret;
  275. }
  276. protected function peekInt16($offset = 0)
  277. {
  278. $ret = 0;
  279. if (strlen($this->_input) >= (2 + $offset))
  280. {
  281. $ret = ord(substr($this->_input, $offset, 1)) << 8;
  282. $ret |= ord(substr($this->_input, $offset + 1, 1)) << 0;
  283. }
  284. return $ret;
  285. }
  286. protected function readInt16()
  287. {
  288. $ret = $this->peekInt16();
  289. if ($ret > 0)
  290. {
  291. $this->_input = substr($this->_input, 2);
  292. }
  293. return $ret;
  294. }
  295. protected function peekInt8($offset = 0)
  296. {
  297. $ret = 0;
  298. if (strlen($this->_input) >= (1 + $offset))
  299. {
  300. $sbstr = substr($this->_input, $offset, 1);
  301. $ret = ord($sbstr);
  302. }
  303. return $ret;
  304. }
  305. protected function readInt8()
  306. {
  307. $ret = $this->peekInt8();
  308. if (strlen($this->_input) >= 1)
  309. {
  310. $this->_input = substr($this->_input, 1);
  311. }
  312. return $ret;
  313. }
  314. protected function fillArray($len)
  315. {
  316. $ret = "";
  317. if (strlen($this->_input) >= $len)
  318. {
  319. $ret = substr($this->_input, 0, $len);
  320. $this->_input = substr($this->_input, $len);
  321. }
  322. return $ret;
  323. }
  324. }
  325. class BinTreeNodeWriter
  326. {
  327. private $_output;
  328. private $_tokenMap = array();
  329. private $_key;
  330. function __construct($dictionary)
  331. {
  332. for ($i = 0; $i < count($dictionary); $i++)
  333. {
  334. if (strlen($dictionary[$i]) > 0)
  335. {
  336. $this->_tokenMap[$dictionary[$i]] = $i;
  337. }
  338. }
  339. }
  340. public function setKey($key)
  341. {
  342. $this->_key = $key;
  343. }
  344. public function StartStream($domain, $resource)
  345. {
  346. $attributes = array();
  347. $header = "WA";
  348. $header .= $this->writeInt8(1);
  349. $header .= $this->writeInt8(2);
  350. $attributes["to"] = $domain;
  351. $attributes["resource"] = $resource;
  352. $this->writeListStart(count($attributes) * 2 + 1);
  353. $this->_output .= "\x01";
  354. $this->writeAttributes($attributes);
  355. $ret = $header.$this->flushBuffer();
  356. return $ret;
  357. }
  358. public function write($node)
  359. {
  360. if ($node == NULL)
  361. {
  362. $this->_output .= "\x00";
  363. }
  364. else
  365. {
  366. $this->writeInternal($node);
  367. }
  368. return $this->flushBuffer();
  369. }
  370. protected function writeInternal($node)
  371. {
  372. $len = 1;
  373. if ($node->_attributeHash != NULL)
  374. {
  375. $len += count($node->_attributeHash) * 2;
  376. }
  377. if (count($node->_children) > 0)
  378. {
  379. $len += 1;
  380. }
  381. if (strlen($node->_data) > 0)
  382. {
  383. $len += 1;
  384. }
  385. $this->writeListStart($len);
  386. $this->writeString($node->_tag);
  387. $this->writeAttributes($node->_attributeHash);
  388. if (strlen($node->_data) > 0)
  389. {
  390. $this->writeBytes($node->_data);
  391. }
  392. if ($node->_children)
  393. {
  394. $this->writeListStart(count($node->_children));
  395. foreach ($node->_children as $child)
  396. {
  397. $this->writeInternal($child);
  398. }
  399. }
  400. }
  401. protected function flushBuffer()
  402. {
  403. $data = (isset($this->_key)) ? $this->_key->encode($this->_output, 0, strlen($this->_output)) : $this->_output;
  404. $size = strlen($data);
  405. $ret = $this->writeInt8(isset($this->_key) ? (1 << 4) : 0);
  406. $ret .= $this->writeInt16($size);
  407. $ret .= $data;
  408. $this->_output = "";
  409. return $ret;
  410. }
  411. protected function writeToken($token)
  412. {
  413. if ($token < 0xf5)
  414. {
  415. $this->_output .= chr($token);
  416. }
  417. else if ($token <= 0x1f4)
  418. {
  419. $this->_output .= "\xfe" . chr($token - 0xf5);
  420. }
  421. }
  422. protected function writeJid($user, $server)
  423. {
  424. $this->_output .= "\xfa";
  425. if (strlen($user) > 0)
  426. {
  427. $this->writeString($user);
  428. }
  429. else
  430. {
  431. $this->writeToken(0);
  432. }
  433. $this->writeString($server);
  434. }
  435. protected function writeInt8($v)
  436. {
  437. $ret = chr($v & 0xff);
  438. return $ret;
  439. }
  440. protected function writeInt16($v)
  441. {
  442. $ret = chr(($v & 0xff00) >> 8);
  443. $ret .= chr(($v & 0x00ff) >> 0);
  444. return $ret;
  445. }
  446. protected function writeInt24($v)
  447. {
  448. $ret = chr(($v & 0xff0000) >> 16);
  449. $ret .= chr(($v & 0x00ff00) >> 8);
  450. $ret .= chr(($v & 0x0000ff) >> 0);
  451. return $ret;
  452. }
  453. protected function writeBytes($bytes)
  454. {
  455. $len = strlen($bytes);
  456. if ($len >= 0x100)
  457. {
  458. $this->_output .= "\xfd";
  459. $this->_output .= $this->writeInt24($len);
  460. }
  461. else
  462. {
  463. $this->_output .= "\xfc";
  464. $this->_output .= $this->writeInt8($len);
  465. }
  466. $this->_output .= $bytes;
  467. }
  468. protected function writeString($tag)
  469. {
  470. if (isset($this->_tokenMap[$tag]))
  471. {
  472. $key = $this->_tokenMap[$tag];
  473. $this->writeToken($key);
  474. }
  475. else
  476. {
  477. $index = strpos($tag, '@');
  478. if ($index)
  479. {
  480. $server = substr($tag, $index + 1);
  481. $user = substr($tag, 0, $index);
  482. $this->writeJid($user, $server);
  483. }
  484. else
  485. {
  486. $this->writeBytes($tag);
  487. }
  488. }
  489. }
  490. protected function writeAttributes($attributes)
  491. {
  492. if ($attributes)
  493. {
  494. foreach ($attributes as $key => $value)
  495. {
  496. $this->writeString($key);
  497. $this->writeString($value);
  498. }
  499. }
  500. }
  501. protected function writeListStart($len)
  502. {
  503. if ($len == 0)
  504. {
  505. $this->_output .= "\x00";
  506. }
  507. else if ($len < 256)
  508. {
  509. $this->_output .= "\xf8" . chr($len);
  510. }
  511. else
  512. {
  513. $this->_output .= "\xf9" . chr($len);
  514. }
  515. }
  516. }