PageRenderTime 28ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/whatsup/src/php/protocol.class.php

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