1: <?php
2:
3: namespace Budabot\Core;
4:
5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44:
45:
46: require_once 'MMDBParser.class.php';
47: require_once 'AOChatQueue.class.php';
48: require_once 'AOChatExtMsg.class.php';
49: require_once 'AOChatPacket.class.php';
50:
51: if ((float)phpversion() < 5.0) {
52: die("AOChat class needs PHP version 5.0.0 or higher in order to work.\n");
53: }
54:
55: if (!extension_loaded("sockets")) {
56: die("AOChat class needs the Sockets extension to work.\n");
57: }
58:
59: if (!extension_loaded("bcmath")) {
60: die("AOChat class needs the BCMath extension to work.\n");
61: }
62:
63: set_time_limit(0);
64: ini_set("html_errors", 0);
65:
66: define('AOC_GROUP_NOWRITE', 0x00000002);
67: define('AOC_GROUP_NOASIAN', 0x00000020);
68: define('AOC_GROUP_MUTE', 0x01010000);
69: define('AOC_GROUP_LOG', 0x02020000);
70:
71: define('AOC_FLOOD_LIMIT', 7);
72: define('AOC_FLOOD_INC', 2);
73:
74: define('AOEM_UNKNOWN', 0xFF);
75: define('AOEM_ORG_JOIN', 0x10);
76: define('AOEM_ORG_KICK', 0x11);
77: define('AOEM_ORG_LEAVE', 0x12);
78: define('AOEM_ORG_DISBAND', 0x13);
79: define('AOEM_ORG_FORM', 0x14);
80: define('AOEM_ORG_VOTE', 0x15);
81: define('AOEM_ORG_STRIKE', 0x16);
82: define('AOEM_NW_ATTACK', 0x20);
83: define('AOEM_NW_ABANDON', 0x21);
84: define('AOEM_NW_OPENING', 0x22);
85: define('AOEM_NW_TOWER_ATT_ORG', 0x23);
86: define('AOEM_NW_TOWER_ATT', 0x24);
87: define('AOEM_NW_TOWER', 0x25);
88: define('AOEM_AI_CLOAK', 0x30);
89: define('AOEM_AI_RADAR', 0x31);
90: define('AOEM_AI_ATTACK', 0x32);
91: define('AOEM_AI_REMOVE_INIT', 0x33);
92: define('AOEM_AI_REMOVE', 0x34);
93: define('AOEM_AI_HQ_REMOVE_INIT', 0x35);
94: define('AOEM_AI_HQ_REMOVE', 0x36);
95:
96: class AOChat {
97: var $id, $gid, $chars, $char, $grp, $buddies;
98: var $socket, $last_packet, $last_ping;
99: var $chatqueue;
100:
101: var $mmdbParser;
102: var $logger;
103:
104:
105: function __construct() {
106: $this->disconnect();
107: $this->mmdbParser = new MMDBParser('data/text.mdb');
108: $this->logger = new LoggerWrapper('AOChat');
109: }
110:
111: function disconnect() {
112: if (is_resource($this->socket)) {
113: socket_close($this->socket);
114: }
115: $this->socket = null;
116: $this->char = null;
117: $this->last_packet = 0;
118: $this->last_ping = 0;
119: $this->id = array();
120: $this->gid = array();
121: $this->grp = array();
122: $this->chars = array();
123: $this->chatqueue = null;
124: }
125:
126:
127: function connect($server, $port) {
128: $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
129: if (!is_resource($this->socket)) {
130: $this->socket = null;
131: $this->logger->log('error', "Could not create socket");
132: die();
133: }
134:
135:
136: $timeout = 10;
137: socket_set_option($this->socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
138:
139: if (@socket_connect($this->socket, $server, $port) === false) {
140: $this->logger->log('error', "Could not connect to the AO Chat server ($server:$port): " . trim(socket_strerror(socket_last_error($this->socket))));
141: $this->disconnect();
142: return false;
143: }
144:
145: $this->chatqueue = new AOChatQueue(AOC_FLOOD_LIMIT, AOC_FLOOD_INC);
146:
147: return $this->socket;
148: }
149:
150: function iteration() {
151: $now = time();
152:
153: if ($this->chatqueue !== null) {
154: $packet = $this->chatqueue->getNext();
155: while ($packet !== null) {
156: $this->send_packet($packet);
157: $packet = $this->chatqueue->getNext();
158: }
159: }
160:
161: if (($now - $this->last_packet) > 60 && ($now - $this->last_ping) > 60) {
162: $this->send_ping();
163: }
164: }
165:
166: function wait_for_packet($time = 1) {
167: $this->iteration();
168:
169: $sec = (int)$time;
170: if (is_float($time)) {
171: $usec = (int)($time * 1000000 % 1000000);
172: } else {
173: $usec = 0;
174: }
175:
176: if (!socket_select($a = array($this->socket), $b = null, $c = null, $sec, $usec)) {
177: return null;
178: } else {
179: return $this->get_packet();
180: }
181: }
182:
183: function read_data($len) {
184: $data = "";
185: $rlen = $len;
186: while ($rlen > 0) {
187: if (($tmp = socket_read($this->socket, $rlen)) === false) {
188: $last_error = socket_strerror(socket_last_error($this->socket));
189: $this->logger->log('error', "Read error: $last_error");
190: die();
191: }
192: if ($tmp == "") {
193: $this->logger->log('error', "Read error: EOF - (Someone else logging on to same account?)");
194: die();
195: }
196: $data .= $tmp;
197: $rlen -= strlen($tmp);
198: }
199: return $data;
200: }
201:
202: function get_packet() {
203: $head = $this->read_data(4);
204: if (strlen($head) != 4) {
205: return false;
206: }
207:
208: list(, $type, $len) = unpack("n2", $head);
209:
210: $data = $this->read_data($len);
211:
212: $packet = new AOChatPacket("in", $type, $data);
213:
214: if ($this->logger->isEnabledFor('debug')) {
215: $this->logger->log('debug', print_r($packet, true));
216: }
217:
218: switch ($type) {
219: case AOCP_CLIENT_NAME:
220: case AOCP_CLIENT_LOOKUP:
221: list($id, $name) = $packet->args;
222: $id = "" . $id;
223: $name = ucfirst(strtolower($name));
224: $this->id[$id] = $name;
225: $this->id[$name] = $id;
226: break;
227:
228: case AOCP_GROUP_ANNOUNCE:
229: list($gid, $name, $status) = $packet->args;
230: $this->grp[$gid] = $status;
231: $this->gid[$gid] = $name;
232: $this->gid[strtolower($name)] = $gid;
233: break;
234:
235: case AOCP_GROUP_MESSAGE:
236:
237: if ($packet->args[1] === 0 && substr($packet->args[2], 0, 2) == "~&") {
238: $packet->args[2] = $this->readExtMsg($packet->args[2]);
239: }
240: break;
241:
242: case AOCP_CHAT_NOTICE:
243: $category_id = 20000;
244: $packet->args[4] = $this->mmdbParser->getMessageString($category_id, $packet->args[2]);
245: if ($packet->args[4] !== null) {
246: $packet->args[5] = $this->parse_ext_params($packet->args[3]);
247: if ($packet->args[5] !== null) {
248: $packet->args[6] = vsprintf($packet->args[4], $packet->args[5]);
249: } else {
250: $this->logger->log('error', "Could not parse chat notice: " . print_r($packet, true));
251: }
252: }
253: break;
254: }
255:
256: $this->last_packet = time();
257:
258: return $packet;
259: }
260:
261: function send_packet($packet) {
262: $data = pack("n2", $packet->type, strlen($packet->data)) . $packet->data;
263:
264: $this->logger->log('debug', $data);
265:
266: socket_write($this->socket, $data, strlen($data));
267: return true;
268: }
269:
270:
271: function authenticate($username, $password) {
272: $packet = $this->get_packet();
273: if ($packet->type != AOCP_LOGIN_SEED) {
274: return false;
275: }
276: $serverseed = $packet->args[0];
277:
278: $key = $this->generate_login_key($serverseed, $username, $password);
279: $pak = new AOChatPacket("out", AOCP_LOGIN_REQUEST, array(0, $username, $key));
280: $this->send_packet($pak);
281: $packet = $this->get_packet();
282: if ($packet->type != AOCP_LOGIN_CHARLIST) {
283: return false;
284: }
285:
286: for ($i = 0; $i < count($packet->args[0]); $i++) {
287: $this->chars[] = array(
288: "id" => $packet->args[0][$i],
289: "name" => ucfirst(strtolower($packet->args[1][$i])),
290: "level" => $packet->args[2][$i],
291: "online" => $packet->args[3][$i]);
292: }
293:
294: $this->username = $username;
295:
296: return $this->chars;
297: }
298:
299: function login($char) {
300: if (is_int($char)) {
301: $field = "id";
302: } else if (is_string($char)) {
303: $field = "name";
304: $char = ucfirst(strtolower($char));
305: }
306:
307: if (!is_array($char)) {
308: if (empty($field)) {
309: return false;
310: } else {
311: forEach($this->chars as $e) {
312: if ($e[$field] == $char) {
313: $char = $e;
314: break;
315: }
316: }
317: }
318: }
319:
320: if (!is_array($char)) {
321: $this->logger->log('error', "AOChat: no valid character to login");
322: return false;
323: }
324:
325: $loginSelect = new AOChatPacket("out", AOCP_LOGIN_SELECT, $char["id"]);
326: $this->send_packet($loginSelect);
327: $packet = $this->get_packet();
328: if ($packet->type != AOCP_LOGIN_OK) {
329: return false;
330: }
331:
332: $this->char = $char;
333:
334: return true;
335: }
336:
337:
338: function lookup_user($u) {
339: $u = ucfirst(strtolower($u));
340:
341: if ($u == '') {
342: return false;
343: }
344:
345: if (isset($this->id[$u])) {
346: return $this->id[$u];
347: }
348:
349: $this->send_packet(new AOChatPacket("out", AOCP_CLIENT_LOOKUP, $u));
350: for ($i = 0; $i < 100 && !isset($this->id[$u]); $i++) {
351:
352: $packet = $this->wait_for_packet(1);
353: if ($packet) {
354: $this->process_packet($packet);
355: }
356: }
357:
358: return isset($this->id[$u]) ? $this->id[$u] : false;
359: }
360:
361: function get_uid($user) {
362: if ($this->is_really_numeric($user)) {
363: return $this->fixunsigned($user);
364: }
365:
366: $uid = $this->lookup_user($user);
367:
368: if ($uid === false || $uid == 0 || $uid == -1 || $uid == 0xffffffff || !$this->is_really_numeric($uid)) {
369: return false;
370: }
371:
372: return $uid;
373: }
374:
375: function fixunsigned($num) {
376: if ($this->is_really_numeric($num) && bcdiv("" . $num, "2147483648", 0)) {
377: $num2 = -1 * bcsub("4294967296", "" . $num);
378: return (int)$num2;
379: }
380:
381: return (int)$num;
382: }
383:
384: function is_really_numeric($num) {
385: if (preg_match("/^([0-9\-]+)$/", "" . $num)) {
386: return true;
387: }
388:
389: return false;
390: }
391:
392: function lookup_group($arg, $type = 0) {
393: if ($type && ($is_gid = (strlen($arg) === 5 && (ord($arg[0])&~0x80) < 0x10))) {
394: return $arg;
395: }
396: if (!$is_gid) {
397: $arg = strtolower($arg);
398: }
399: return isset($this->gid[$arg]) ? $this->gid[$arg] : false;
400: }
401:
402: function get_gid($g) {
403: return $this->lookup_group($g, 1);
404: }
405:
406: function get_gname($g) {
407: if (($gid = $this->lookup_group($g, 1)) === false) {
408: return false;
409: }
410: return $this->gid[$gid];
411: }
412:
413:
414: function send_ping() {
415: $this->last_ping = time();
416: return $this->send_packet(new AOChatPacket("out", AOCP_PING, "AOChat.php"));
417: }
418:
419: function send_tell($user, $msg, $blob = "\0", $priority = null) {
420: if (($uid = $this->get_uid($user)) === false) {
421: return false;
422: }
423: if ($priority == null) {
424: $priority = AOC_PRIORITY_MED;
425: }
426: $this->chatqueue->push($priority, new AOChatPacket("out", AOCP_MSG_PRIVATE, array($uid, $msg, "\0")));
427: $this->iteration();
428: return true;
429: }
430:
431: function send_guild($msg, $blob = "\0", $priority = null) {
432: $guild_gid = false;
433: forEach ($this->grp as $gid => $status) {
434: if (ord(substr($gid, 0, 1)) == 3) {
435: $guild_gid = $gid;
436: break;
437: }
438: }
439: if (!$guild_gid) {
440: return false;
441: }
442: if ($priority == null) {
443: $priority = AOC_PRIORITY_MED;
444: }
445: $this->chatqueue->push($priority, new AOChatPacket("out", AOCP_GROUP_MESSAGE, array($guild_gid, $msg, "\0")));
446: $this->iteration();
447: return true;
448: }
449:
450: function send_group($group, $msg, $blob = "\0", $priority = null) {
451: if (($gid = $this->get_gid($group)) === false) {
452: return false;
453: }
454: if ($priority == null) {
455: $priority = AOC_PRIORITY_MED;
456: }
457: $this->chatqueue->push(AOC_PRIORITY_MED, new AOChatPacket("out", AOCP_GROUP_MESSAGE, array($gid, $msg, "\0")));
458: $this->iteration();
459: return true;
460: }
461:
462: function group_join($group) {
463: if (($gid = $this->get_gid($group)) === false) {
464: return false;
465: }
466:
467: return $this->send_packet(new AOChatPacket("out", AOCP_GROUP_DATA_SET, array($gid, $this->grp[$gid] & ~AOC_GROUP_MUTE, "\0")));
468: }
469:
470: function group_leave($group) {
471: if (($gid = $this->get_gid($group)) === false) {
472: return false;
473: }
474:
475: return $this->send_packet(new AOChatPacket("out", AOCP_GROUP_DATA_SET, array($gid, $this->grp[$gid] | AOC_GROUP_MUTE, "\0")));
476: }
477:
478: function group_status($group) {
479: if (($gid = $this->get_gid($group)) === false) {
480: return false;
481: }
482:
483: return $this->grp[$gid];
484: }
485:
486:
487: function send_privgroup($group, $msg, $blob = "\0") {
488: if (($gid = $this->get_uid($group)) === false) {
489: return false;
490: }
491:
492: return $this->send_packet(new AOChatPacket("out", AOCP_PRIVGRP_MESSAGE, array($gid, $msg, $blob)));
493: }
494:
495: function privategroup_join($group) {
496: if (($gid = $this->get_uid($group)) === false) {
497: return false;
498: }
499:
500: return $this->send_packet(new AOChatPacket("out", AOCP_PRIVGRP_JOIN, $gid));
501: }
502:
503: function privategroup_invite($user) {
504: if (($uid = $this->get_uid($user)) === false) {
505: return false;
506: }
507:
508: return $this->send_packet(new AOChatPacket("out", AOCP_PRIVGRP_INVITE, $uid));
509: }
510:
511: function privategroup_kick($user) {
512: if (($uid = $this->get_uid($user)) === false) {
513: return false;
514: }
515:
516: return $this->send_packet(new AOChatPacket("out", AOCP_PRIVGRP_KICK, $uid));
517: }
518:
519: function privategroup_leave($user) {
520: if (($uid = $this->get_uid($user)) === false) {
521: return false;
522: }
523:
524: return $this->send_packet(new AOChatPacket("out", AOCP_PRIVGRP_PART, $uid));
525: }
526:
527: function privategroup_kick_all() {
528: return $this->send_packet(new AOChatPacket("out", AOCP_PRIVGRP_KICKALL, ""));
529: }
530:
531:
532: function buddy_add($uid, $type = "\1") {
533: if ($uid == $this->char['id']) {
534: return false;
535: } else {
536: return $this->send_packet(new AOChatPacket("out", AOCP_BUDDY_ADD, array($uid, $type)));
537: }
538: }
539:
540: function buddy_remove($uid) {
541: return $this->send_packet(new AOChatPacket("out", AOCP_BUDDY_REMOVE, $uid));
542: }
543:
544: function buddy_remove_unknown() {
545: return $this->send_packet(new AOChatPacket("out", AOCP_CC, array(array("rembuddy", "?"))));
546: }
547:
548:
549: function get_random_hex_key($bits) {
550: $str = "";
551: do {
552: $str .= sprintf('%02x', mt_rand(0, 0xff));
553: } while(($bits -= 8) > 0);
554: return $str;
555: }
556:
557: function bighexdec($x) {
558: if (substr($x, 0, 2) != "0x") {
559: return $x;
560: }
561: $r = "0";
562: for ($p = $q = strlen($x) - 1; $p >= 2; $p--) {
563: $r = bcadd($r, bcmul(hexdec($x[$p]), bcpow(16, $q - $p)));
564: }
565: return $r;
566: }
567:
568: function bigdechex($x) {
569: $r = "";
570: while ($x != "0") {
571: $r = dechex(bcmod($x, 16)) . $r;
572: $x = bcdiv($x, 16);
573: }
574: return $r;
575: }
576:
577: function bcmath_powm($base, $exp, $mod) {
578: $base = $this->bighexdec($base);
579: $exp = $this->bighexdec($exp);
580: $mod = $this->bighexdec($mod);
581:
582: $r = bcpowmod($base, $exp, $mod);
583: return $this->bigdechex($r);
584: }
585:
586: 587: 588: 589: 590: 591: 592: 593:
594: function NegativeToUnsigned($value) {
595: if (bccomp($value, 0) != -1) {
596: return $value;
597: }
598:
599: $value = bcmul($value, -1);
600: $higherValue = 0xFFFFFFFF;
601:
602:
603:
604:
605:
606: while (bccomp($value, $higherValue) == 1) {
607: $higherValue = bcadd(bcmul($higherValue, 0x100), 0xFF);
608: }
609:
610: $value = bcadd(bcsub($higherValue, $value), 1);
611:
612: return $value;
613: }
614:
615:
616:
617:
618:
619:
620: function SafeDecHexReverseEndian($value) {
621: $result = "";
622: $value = (int)$this->ReduceTo32Bit($value);
623: $hex = substr("00000000".dechex($value), -8);
624:
625: $bytes = str_split($hex, 2);
626:
627: for ($i = 3; $i >= 0; $i--) {
628: $result .= $bytes[$i];
629: }
630:
631: return $result;
632: }
633:
634: 635: 636: 637: 638: 639: 640: 641:
642: function ReduceTo32Bit($value) {
643:
644: if (bccomp($value, 0) == -1) {
645: $value = $this -> NegativeToUnsigned($value);
646: }
647:
648: $bit = 0x80000000;
649: $bits = array();
650:
651:
652: while (bccomp($value, $bit) > -1) {
653: $bit = bcmul($bit, 2);
654: $bits[] = $bit;
655: }
656:
657:
658: while (null != ($bit = array_pop($bits))) {
659: if (bccomp($value, $bit) >= 0) {
660: $value = bcsub($value, $bit);
661: }
662: }
663:
664:
665: if (bccomp($value, 0x80000000) != -1) {
666: $value = bcsub($value, 0x80000000);
667: $value -= 0x80000000;
668: }
669:
670: return $value;
671: }
672:
673:
674: 675: 676: 677: 678: 679:
680: function generate_login_key($servkey, $username, $password) {
681: $dhY = "0x9c32cc23d559ca90fc31be72df817d0e124769e809f936bc14360ff4bed758f260a0d596584eacbbc2b88bdd410416163e11dbf62173393fbc0c6fefb2d855f1a03dec8e9f105bbad91b3437d8eb73fe2f44159597aa4053cf788d2f9d7012fb8d7c4ce3876f7d6cd5d0c31754f4cd96166708641958de54a6def5657b9f2e92";
682: $dhN = "0xeca2e8c85d863dcdc26a429a71a9815ad052f6139669dd659f98ae159d313d13c6bf2838e10a69b6478b64a24bd054ba8248e8fa778703b418408249440b2c1edd28853e240d8a7e49540b76d120d3b1ad2878b1b99490eb4a2a5e84caa8a91cecbdb1aa7c816e8be343246f80c637abc653b893fd91686cf8d32d6cfe5f2a6f";
683: $dhG = "0x5";
684: $dhx = "0x".$this->get_random_hex_key(256);
685:
686: $dhX = $this->bcmath_powm($dhG, $dhx, $dhN);
687: $dhK = $this->bcmath_powm($dhY, $dhx, $dhN);
688:
689: $str = sprintf("%s|%s|%s", $username, $servkey, $password);
690:
691: if (strlen($dhK) < 32) {
692: $dhK = str_repeat("0", 32-strlen($dhK)) . $dhK;
693: } else {
694: $dhK = substr($dhK, 0, 32);
695: }
696:
697: $prefix = pack("H16", $this->get_random_hex_key(64));
698: $length = 8 + 4 + strlen($str);
699: $pad = str_repeat(" ", (8 - $length % 8) % 8);
700: $strlen = pack("N", strlen($str));
701:
702: $plain = $prefix . $strlen . $str . $pad;
703: $crypted = $this->aochat_crypt($dhK, $plain);
704:
705: return $dhX . "-" . $crypted;
706: }
707:
708: function aochat_crypt($key, $str) {
709: if (strlen($key) != 32 || strlen($str) % 8 != 0) {
710: return false;
711: }
712:
713: $cycle = array(0, 0);
714: $result = array(0, 0);
715: $ret = "";
716:
717: $keyarr = unpack("V*", pack("H*", $key));
718: $dataarr = unpack("V*", $str);
719:
720: for ($i = 1; $i <= count($dataarr); $i += 2) {
721: $now[0] = (int)$this -> ReduceTo32Bit($dataarr[$i]) ^ (int)$this -> ReduceTo32Bit(@$prev[0]);
722: $now[1] = (int)$this -> ReduceTo32Bit($dataarr[$i+1]) ^ (int)$this -> ReduceTo32Bit(@$prev[1]);
723: $prev = $this -> aocrypt_permute($now, $keyarr);
724:
725: $ret .= $this -> SafeDecHexReverseEndian($prev[0]);
726: $ret .= $this -> SafeDecHexReverseEndian($prev[1]);
727: }
728:
729: return $ret;
730: }
731:
732: function aocrypt_permute($x, $y) {
733: $a = $x[0];
734: $b = $x[1];
735: $c = 0;
736: $d = (int)0x9e3779b9;
737: for ($i = 32; $i-- > 0;) {
738: $c = (int)$this -> ReduceTo32Bit($c + $d);
739: $a += (int)$this -> ReduceTo32Bit((int)$this -> ReduceTo32Bit(((int)$this -> ReduceTo32Bit($b) << 4 & -16) + $y[1]) ^ (int)$this -> ReduceTo32Bit($b + $c)) ^ (int)$this -> ReduceTo32Bit(((int)$this -> ReduceTo32Bit($b) >> 5 & 134217727) + $y[2]);
740: $b += (int)$this -> ReduceTo32Bit((int)$this -> ReduceTo32Bit(((int)$this -> ReduceTo32Bit($a) << 4 & -16) + $y[3]) ^ (int)$this -> ReduceTo32Bit($a + $c)) ^ (int)$this -> ReduceTo32Bit(((int)$this -> ReduceTo32Bit($a) >> 5 & 134217727) + $y[4]);
741: }
742: return array($a, $b);
743: }
744:
745: public function parse_ext_params(&$msg) {
746: $args = array();
747: while ($msg != '') {
748: $data_type = $msg[0];
749: $msg = substr($msg, 1);
750: switch ($data_type) {
751: case "S":
752: $len = ord($msg[0]) * 256 + ord($msg[1]);
753: $str = substr($msg, 2, $len);
754: $msg = substr($msg, $len + 2);
755: $args[] = $str;
756: break;
757:
758: case "s":
759: $len = ord($msg[0]);
760: $str = substr($msg, 1, $len - 1);
761: $msg = substr($msg, $len);
762: $args[] = $str;
763: break;
764:
765: case "I":
766: $array = unpack("N", $msg);
767: $args[] = $array[1];
768: $msg = substr($msg, 4);
769: break;
770:
771: case "i":
772: case "u":
773: $num = $this->b85g($msg);
774: $args[] = $num;
775: break;
776:
777: case "R":
778: $cat = $this->b85g($msg);
779: $ins = $this->b85g($msg);
780: $str = $this->mmdbParser->getMessageString($cat, $ins);
781: if ($str === null) {
782: $str = "Unknown ($cat, $ins)";
783: }
784: $args[] = $str;
785: break;
786:
787: case "l":
788: $array = unpack("N", $msg);
789: $msg = substr($msg, 4);
790: $cat = 20000;
791: $ins = $array[1];
792: $str = $this->mmdbParser->getMessageString($cat, $ins);
793: if ($str === null) {
794: $str = "Unknown ($cat, $ins)";
795: }
796: $args[] = $str;
797: break;
798:
799: case "~":
800:
801: break 2;
802:
803: default:
804: $this->logger->log('warn', "Unknown argument type '$data_type'");
805: return null;
806: break;
807: }
808: }
809:
810: return $args;
811: }
812:
813: public function b85g(&$str) {
814: $n = 0;
815: for ($i = 0; $i < 5; $i++) {
816: $n = $n * 85 + ord($str[$i]) - 33;
817: }
818: $str = substr($str, 5);
819: return $n;
820: }
821:
822: 823: 824: 825: 826: 827: 828: 829: 830: 831: 832: 833: 834: 835: 836: 837: 838: 839: 840: 841: 842:
843: public function readExtMsg($msg) {
844: if (empty($msg)) {
845: return false;
846: }
847:
848: $message = '';
849: while (substr($msg, 0, 2) == "~&") {
850:
851: $msg = substr($msg, 2);
852:
853: $obj = new AOExtMsg();
854: $obj->category = $this->b85g($msg);
855: $obj->instance = $this->b85g($msg);
856:
857: $obj->args = $this->parse_ext_params($msg);
858: if ($obj->args === null) {
859: $this->logger->log('warn', "Error parsing parameters for category: '$obj->category' instance: '$obj->instance' string: '$msg'");
860: } else {
861: $obj->message_string = $this->mmdbParser->getMessageString($obj->category, $obj->instance);
862: if ($obj->message_string !== null) {
863: $message .= trim(vsprintf($obj->message_string, $obj->args));
864: }
865: }
866: }
867:
868: return $message;
869: }
870: }
871: