Overview

Namespaces

  • Budabot
    • Core
      • Modules
    • User
      • Modules
  • None
  • Tyrence
    • Modules

Classes

  • AccessLevel
  • Budabot\Core\AccessManager
  • Budabot\Core\AdminManager
  • Budabot\Core\AOChat
  • Budabot\Core\AOChatPacket
  • Budabot\Core\AOChatQueue
  • Budabot\Core\AOExtMsg
  • Budabot\Core\AsyncHttp
  • Budabot\Core\AutoInject
  • Budabot\Core\BotRunner
  • Budabot\Core\Budabot
  • Budabot\Core\BuddylistManager
  • Budabot\Core\CacheManager
  • Budabot\Core\CacheResult
  • Budabot\Core\ClassLoader
  • Budabot\Core\ColorSettingHandler
  • Budabot\Core\CommandAlias
  • Budabot\Core\CommandManager
  • Budabot\Core\ConfigFile
  • Budabot\Core\DB
  • Budabot\Core\DBRow
  • Budabot\Core\EventLoop
  • Budabot\Core\EventManager
  • Budabot\Core\GuildChannelCommandReply
  • Budabot\Core\GuildManager
  • Budabot\Core\HelpManager
  • Budabot\Core\Http
  • Budabot\Core\HttpRequest
  • Budabot\Core\LegacyLogger
  • Budabot\Core\LimitsController
  • Budabot\Core\LoggerWrapper
  • Budabot\Core\MMDBParser
  • Budabot\Core\Modules\AdminController
  • Budabot\Core\Modules\AliasController
  • Budabot\Core\Modules\AltInfo
  • Budabot\Core\Modules\AltsController
  • Budabot\Core\Modules\BanController
  • Budabot\Core\Modules\BuddylistController
  • Budabot\Core\Modules\ColorsController
  • Budabot\Core\Modules\CommandlistController
  • Budabot\Core\Modules\CommandSearchController
  • Budabot\Core\Modules\ConfigController
  • Budabot\Core\Modules\EventlistController
  • Budabot\Core\Modules\HelpController
  • Budabot\Core\Modules\LogsController
  • Budabot\Core\Modules\PlayerLookupController
  • Budabot\Core\Modules\ProfileCommandReply
  • Budabot\Core\Modules\ProfileController
  • Budabot\Core\Modules\SettingsController
  • Budabot\Core\Modules\SQLController
  • Budabot\Core\Modules\SystemController
  • Budabot\Core\Modules\UsageController
  • Budabot\Core\Modules\WhitelistController
  • Budabot\Core\NumberSettingHandler
  • Budabot\Core\OptionsSettingHandler
  • Budabot\Core\PlayerHistory
  • Budabot\Core\PlayerHistoryManager
  • Budabot\Core\PlayerManager
  • Budabot\Core\Preferences
  • Budabot\Core\PrivateChannelCommandReply
  • Budabot\Core\PrivateMessageCommandReply
  • Budabot\Core\Registry
  • Budabot\Core\SettingHandler
  • Budabot\Core\SettingManager
  • Budabot\Core\SettingObject
  • Budabot\Core\SocketManager
  • Budabot\Core\SocketNotifier
  • Budabot\Core\SubcommandManager
  • Budabot\Core\Text
  • Budabot\Core\TextSettingHandler
  • Budabot\Core\Timer
  • Budabot\Core\TimerEvent
  • Budabot\Core\TimeSettingHandler
  • Budabot\Core\Util
  • Budabot\Core\xml
  • Budabot\User\Modules\AlienArmorController
  • Budabot\User\Modules\AlienBioController
  • Budabot\User\Modules\AlienMiscController
  • Budabot\User\Modules\AOSpeakController
  • Budabot\User\Modules\AOUController
  • Budabot\User\Modules\AXPController
  • Budabot\User\Modules\BankController
  • Budabot\User\Modules\BosslootController
  • Budabot\User\Modules\BroadcastController
  • Budabot\User\Modules\BuffPerksController
  • Budabot\User\Modules\CacheController
  • Budabot\User\Modules\ChatAssistController
  • Budabot\User\Modules\ChatCheckController
  • Budabot\User\Modules\ChatLeaderController
  • Budabot\User\Modules\ChatRallyController
  • Budabot\User\Modules\ChatSayController
  • Budabot\User\Modules\ChatTopicController
  • Budabot\User\Modules\CityWaveController
  • Budabot\User\Modules\CloakController
  • Budabot\User\Modules\ClusterController
  • Budabot\User\Modules\CountdownController
  • Budabot\User\Modules\DevController
  • Budabot\User\Modules\DingController
  • Budabot\User\Modules\EventsController
  • Budabot\User\Modules\FightController
  • Budabot\User\Modules\FindOrgController
  • Budabot\User\Modules\FindPlayerController
  • Budabot\User\Modules\FunController
  • Budabot\User\Modules\GitController
  • Budabot\User\Modules\GuideController
  • Budabot\User\Modules\GuildController
  • Budabot\User\Modules\HelpbotController
  • Budabot\User\Modules\HtmlDecodeController
  • Budabot\User\Modules\ImplantController
  • Budabot\User\Modules\ImplantDesignerController
  • Budabot\User\Modules\InactiveMemberController
  • Budabot\User\Modules\ItemsController
  • Budabot\User\Modules\KillOnSightController
  • Budabot\User\Modules\LevelController
  • Budabot\User\Modules\LinksController
  • Budabot\User\Modules\LootListsController
  • Budabot\User\Modules\MdbController
  • Budabot\User\Modules\MessageInfoCommandReply
  • Budabot\User\Modules\MockCommandReply
  • Budabot\User\Modules\NanoController
  • Budabot\User\Modules\NewsController
  • Budabot\User\Modules\NotesController
  • Budabot\User\Modules\OnlineController
  • Budabot\User\Modules\OrgHistoryController
  • Budabot\User\Modules\OrglistController
  • Budabot\User\Modules\OrgMembersController
  • Budabot\User\Modules\OSController
  • Budabot\User\Modules\PlayerHistoryController
  • Budabot\User\Modules\PlayfieldController
  • Budabot\User\Modules\PocketbossController
  • Budabot\User\Modules\PremadeImplantController
  • Budabot\User\Modules\PrivateChannelController
  • Budabot\User\Modules\QuoteController
  • Budabot\User\Modules\RaffleController
  • Budabot\User\Modules\RaidController
  • Budabot\User\Modules\RandomController
  • Budabot\User\Modules\RecipeController
  • Budabot\User\Modules\RelayController
  • Budabot\User\Modules\ReputationController
  • Budabot\User\Modules\ResearchController
  • Budabot\User\Modules\RunAsController
  • Budabot\User\Modules\SendTellController
  • Budabot\User\Modules\ShoppingController
  • Budabot\User\Modules\SilenceController
  • Budabot\User\Modules\SkillsController
  • Budabot\User\Modules\SpiritsController
  • Budabot\User\Modules\StopwatchController
  • Budabot\User\Modules\Teamspeak3
  • Budabot\User\Modules\TeamspeakController
  • Budabot\User\Modules\TestController
  • Budabot\User\Modules\TimeController
  • Budabot\User\Modules\TimerController
  • Budabot\User\Modules\TimezoneController
  • Budabot\User\Modules\TowerController
  • Budabot\User\Modules\TrackerController
  • Budabot\User\Modules\TrickleController
  • Budabot\User\Modules\UnixtimeController
  • Budabot\User\Modules\VoteController
  • Budabot\User\Modules\WeatherController
  • Budabot\User\Modules\WhatBuffsController
  • Budabot\User\Modules\WhereisController
  • Budabot\User\Modules\WhoisController
  • Budabot\User\Modules\WhoisOrgController
  • Budabot\User\Modules\WhompahController
  • Command
  • DefaultStatus
  • DefineCommand
  • Description
  • Event
  • HandlesCommand
  • Help
  • Inject
  • Instance
  • Intoptions
  • Matches
  • Options
  • Setting
  • Setup
  • Type
  • Tyrence\Modules\DemoResponseCommandReply
  • Tyrence\Modules\SameChannelResponseController
  • Visibility

Interfaces

  • Budabot\Core\CommandReply

Exceptions

  • Budabot\Core\InvalidHttpRequest
  • Budabot\Core\SQLException
  • Budabot\Core\StopExecutionException

Functions

  • Budabot\Core\isWindows
  • Budabot\Core\Modules\read_input
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3: namespace Budabot\Core;
  4: 
  5: /*
  6: * $Id: aochat.php,v 1.1 2006/12/08 15:17:54 genesiscl Exp $
  7: *
  8: * Modified to handle the recent problem with the integer overflow
  9: *
 10: * Copyright (C) 2002-2005  Oskari Saarenmaa <auno@auno.org>.
 11: *
 12: * AOChat, a PHP class for talking with the Anarchy Online chat servers.
 13: * It requires the sockets extension (to connect to the chat server..)
 14: * from PHP 4.2.0+ and the BCMath extension (for generating
 15: * and calculating the login keys) to work.
 16: *
 17: * A disassembly of the official java chat client[1] for Anarchy Online
 18: * and Slicer's AO::Chat perl module[2] were used as a reference for this
 19: * class.
 20: *
 21: * [1]: <http://www.anarchy-online.com/content/community/forumsandchat/>
 22: * [2]: <http://www.hackersquest.org/ao/>
 23: *
 24: * Updates to this class can be found from the following web site:
 25: *   http://auno.org/dev/aochat.html
 26: *
 27: **************************************************************************
 28: *
 29: * This program is free software; you can redistribute it and/or modify
 30: * it under the terms of the GNU General Public License as published by
 31: * the Free Software Foundation; either version 2 of the License, or
 32: * (at your option) any later version.
 33: *
 34: * This program is distributed in the hope that it will be useful, but
 35: * WITHOUT ANY WARRANTY; without even the implied warranty of
 36: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 37: * General Public License for more details.
 38: *
 39: * You should have received a copy of the GNU General Public License
 40: * along with this program; if not, write to the Free Software
 41: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 42: * USA
 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:     /* Initialization */
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:     /* Network stuff */
127:     function connect($server, $port) {
128:         $this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
129:         if (!is_resource($this->socket)) { /* this is fatal */
130:             $this->socket = null;
131:             $this->logger->log('error', "Could not create socket");
132:             die();
133:         }
134: 
135:         // prevents bot from hanging on startup when chatserver does not send login seed
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:                 /* Hack to support extended messages */
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:     /* Login functions */
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:     /* User and group lookup functions */
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:             // hack so that packets are not discarding while waiting for char id response
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:     /* Sending various packets */
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:     /* Private chat groups */
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:     /* Buddies */
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:     /* Login key generation and encryption */
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:     * This function returns the binary equivalent postive integer to a given negative
588:     * integer of arbitrary length. This would be the same as taking a signed negative
589:     * number and treating it as if it were unsigned. To see a simple example of this
590:     * on Windows, open the Windows Calculator, punch in a negative number, select the
591:     * hex display, and then switch back to the decimal display.
592:     * http://www.hackersquest.com/boards/viewtopic.php?t=4884&start=75
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:         // We don't know how many bytes the integer might be, so
603:         // start with one byte and then grow it byte by byte until
604:         // our negative number fits inside it. This will make the resulting
605:         // positive number fit in the same number of bytes.
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:     // On linux systems, unpack("H*", pack("L*", <value>)) returns differently than on Windows.
618:     // This can be used instead of unpack/pack to get the value we need.
619:     // http://www.hackersquest.com/boards/viewtopic.php?t=4884&start=75
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:     * Takes a number and reduces it to a 32-bit value. The 32-bits
636:     * remain a binary equivalent of 32-bits from the previous number.
637:     * If the sign bit is set, the result will be negative, otherwise
638:     * the result will be zero or positive.
639:     * Function by: Feetus of RK1
640:     * http://www.hackersquest.com/boards/viewtopic.php?t=4884&start=75
641:     */
642:     function ReduceTo32Bit($value) {
643:         // If its negative, lets go positive ... its easier to do everything as positive.
644:         if (bccomp($value, 0) == -1) {
645:             $value = $this -> NegativeToUnsigned($value);
646:         }
647: 
648:         $bit  = 0x80000000;
649:         $bits = array();
650: 
651:         // Find the largest bit contained in $value above 32-bits
652:         while (bccomp($value, $bit) > -1) {
653:             $bit    = bcmul($bit, 2);
654:             $bits[] = $bit;
655:         }
656: 
657:         // Subtract out bits above 32 from $value
658:         while (null != ($bit = array_pop($bits))) {
659:             if (bccomp($value, $bit) >= 0) {
660:                 $value = bcsub($value, $bit);
661:             }
662:         }
663: 
664:         // Make negative if sign-bit is set in 32-bit value
665:         if (bccomp($value, 0x80000000) != -1) {
666:             $value  = bcsub($value, 0x80000000);
667:             $value -= 0x80000000;
668:         }
669: 
670:         return $value;
671:     }
672: 
673: 
674:     /* This is 'half' Diffie-Hellman key exchange.
675:     * 'Half' as in we already have the server's key ($dhY)
676:     * $dhN is a prime and $dhG is generator for it.
677:     *
678:     * http://en.wikipedia.org/wiki/Diffie-Hellman_key_exchange
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); /* prefix, int, ... */
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); // skip the data type id
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:                     // reached end of message
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:      * New "extended" messages, parser and abstraction.
824:      * These were introduced in 16.1.  The messages use postscript
825:      * base85 encoding (not ipv6 / rfc 1924 base85).  They also use
826:      * some custom encoding and references to further confuse things.
827:      *
828:      * Messages start with the magic marker ~& and end with ~
829:      * Messages begin with two base85 encoded numbers that define
830:      * the category and instance of the message.  After that there
831:      * are an category/instance defined amount of variables which
832:      * are prefixed by the variable type.  A base85 encoded number
833:      * takes 5 bytes.  Variable types:
834:      *
835:      * s: string, first byte is the length of the string
836:      * i: signed integer (b85)
837:      * u: unsigned integer (b85)
838:      * f: float (b85)
839:      * R: reference, b85 category and instance
840:      * F: recursive encoding
841:      * ~: end of message
842:      */
843:     public function readExtMsg($msg) {
844:         if (empty($msg)) {
845:             return false;
846:         }
847:         
848:         $message = '';
849:         while (substr($msg, 0, 2) == "~&") {
850:             // remove header '~&'
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: 
Budabot 4 Docs API documentation generated by ApiGen