3 * PHPTOCLIB: A library for AIM connectivity through PHP using the TOC protocal.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * The version of PHPTOCLIB we are running right now
26 define("PHPTOCLIB_VERSION","1.0.0 RC1");
28 // Prevents Script from Timing Out
31 // Constant Declarations
34 * Maximum size for a direct connection IM in bytes
40 define("MAX_DIM_SIZE",3072); //Default to 3kb
43 * Internally used for message type
48 define("AIM_TYPE_WARN",74);
50 * Internally used for message type
55 define("AIM_TYPE_MSG",75);
57 * Internally used for message type
62 define("AIM_TYPE_UPDATEBUDDY",76);
64 * Internally used for message type
69 define("AIM_TYPE_SIGNON",77);
71 * Internally used for message type
76 define("AIM_TYPE_NICK",78);
78 * Internally used for message type
83 define("AIM_TYPE_ERROR",79);
85 * Internally used for message type
90 define("AIM_TYPE_CHATJ",80);
92 * Internally used for message type
97 define("AIM_TYPE_CHATI",81);
99 * Internally used for message type
104 define("AIM_TYPE_CHATUPDBUD",82);
106 * Internally used for message type
111 define("AIM_TYPE_CHATINV",83);
113 * Internally used for message type
118 define("AIM_TYPE_CHATLE",84);
120 * Internally used for message type
125 define("AIM_TYPE_URL",85);
127 * Internally used for message type
132 define("AIM_TYPE_NICKSTAT",86);
134 * Internally used for message type
139 define("AIM_TYPE_PASSSTAT",87);
141 * Internally used for message type
146 define("AIM_TYPE_RVOUSP",88);
148 * Internally used for message type
153 define("AIM_TYPE_NOT_IMPLEMENTED",666);
158 * Internally used for connection type
160 * Internal type for a normal connection
165 define("CONN_TYPE_NORMAL",1);
167 * Internally used for connection type
169 * Internal type of a Dirct Connection
174 define("CONN_TYPE_DC",2);
176 * Internally used for connection type
178 *Internal type for a file transfer connection
183 define("CONN_TYPE_FT",3);
185 * Internally used for connection type
187 *Internal type for a file get connection
192 define("CONN_TYPE_FTG",4);
195 * Maximum size for a TOC packet
200 define("MAX_PACKLENGTH",2048);
208 define("SFLAP_TYPE_SIGNON",1);
215 define("SFLAP_TYPE_DATA",2);
222 define("SFLAP_TYPE_ERROR",3);
229 define("SFLAP_TYPE_SIGNOFF",4);
236 define("SFLAP_TYPE_KEEPALIVE",5);
243 define("SFLAP_MAX_LENGTH",1024);
248 * Service UID for a voice connection
253 define('VOICE_UID', '09461341-4C7F-11D1-8222-444553540000');
255 * Service UID for file sending
260 define('FILE_SEND_UID', '09461343-4C7F-11D1-8222-444553540000');
262 * Service UID for file getting
267 define('FILE_GET_UID', '09461348-4C7F-11D1-8222-444553540000');
269 * Service UID for Direct connections
274 define('IMAGE_UID', '09461345-4C7F-11D1-8222-444553540000');
276 * Service UID for Buddy Icons
281 define('BUDDY_ICON_UID', '09461346-4C7F-11D1-8222-444553540000');
283 * Service UID for stocks
288 define('STOCKS_UID', '09461347-4C7F-11D1-8222-444553540000');
290 * Service UID for games
295 define('GAMES_UID', '0946134a-4C7F-11D1-8222-444553540000');
303 define("SFLAP_SUCCESS",0);
310 define("SFLAP_ERR_UNKNOWN",1);
317 define("SFLAP_ERR_ARGS",2);
324 define("SFLAP_ERR_LENGTH",3);
331 define("SFLAP_ERR_READ",4);
338 define("SFLAP_ERR_SEND",5);
341 * FLAP version number
346 define("SFLAP_FLAP_VERSION",1);
353 define("SFLAP_TLV_TAG",1);
355 * Bytes in a FLAP header
360 define("SFLAP_HEADER_LEN",6);
363 * PHPTocLib AIM Class
365 * @author Jeremy Bryant <pickleman78@users.sourceforge.net>
366 * @author Rajiv Makhijani <rajiv@blue-tech.org>
384 * AIM Password (Plain Text)
398 var $myServer="toc.oscar.aol.com";
401 * AIM Formatted ScreenName
409 * AIM TOC Server Port
418 * Use setProfile() to update
423 var $myProfile="Powered by phpTOCLib. Please visit http://sourceforge.net/projects/phptoclib for more information"; //The profile of the bot
426 * Socket Connection Resource ID
431 var $myConnection; //Connection resource ID
434 * Roasted AIM Password
442 * Last Message Recieved From Server
450 * Current Seq Number Used to Communicate with Server
458 * Current Warning Level
459 * Getter: getWarning()
460 * Setter: setWarning()
465 var $myWarnLevel; //Warning Level of the bot
477 * Getter: getBuddies()
486 * Getter: getBlocked()
495 * Getter: getBlocked()
506 * 3 - Permit only those on your permit list
507 * 4 - Permit all those not on your deny list
514 //Below variables added 4-29 by Jeremy: Implementing chat
517 * Contains Chat Room Info
518 * $myChatRooms['roomid'] = people in room
525 //End of chat implementation
529 * Event Handler Functions
534 var $myEventHandlers = array();
537 * Array of direct connection objects(including file transfers)
542 var $myDirectConnections = array();
545 * Array of the actual connections
550 var $myConnections = array();
553 * The current state of logging
559 var $myLogging = false;
564 * Permit/Deny Mode Options
567 * 3 - Permit only those on your permit list
568 * 4 - Permit all those not on your deny list
570 * @param String $sn AIM Screenname
571 * @param String $password AIM Password
572 * @param Integer $pdmode Permit/Deny Mode
575 function Aim($sn, $password, $pdmode)
577 //Constructor assignment
578 $this->myScreenName = $this->normalize($sn);
579 $this->myPassword = $password;
580 $this->myRoastedPass = $this->roastPass($password);
582 $this->myConnection = 0;
583 $this->myWarnLevel = 0;
584 $this->myAuthCode = $this->makeCode();
585 $this->myPdMode = $pdmode;
586 $this->myFormatSN = $this->myScreenName;
588 $this->log("PHPTOCLIB v" . PHPTOCLIB_VERSION . " Object Created");
593 * Enables debug logging (Logging is disabled by default)
600 function setLogging($enable)
602 $this->myLogging=$enable;
607 if($this->myLogging){
617 * @param Array $packary Packet
618 * @param String $in Prepend
621 function logPacket($packary,$in)
623 if(!$this->myLogging || sizeof($packary)<=0 || (@strlen($packary['decoded'])<=0 && @isset($packary['decoded'])))
626 foreach($packary as $k=>$d)
628 $towrite.=$k . ":" . $d . "\r\n";
630 $towrite.="\r\n\r\n";
631 $this->log($towrite);
634 * Roasts/Hashes Password
636 * @param String $password Password
638 * @return String Roasted Password
640 function roastPass($password)
642 $roaststring = 'Tic/Toc';
643 $roasted_password = '0x';
644 for ($i = 0; $i < strlen($password); $i++)
645 $roasted_password .= bin2hex($password[$i] ^ $roaststring[($i % 7)]);
646 return $roasted_password;
650 * Access Method for myScreenName
653 * @param $formated Returns formatted Screenname if true as returned by server
654 * @return String Screenname
656 function getMyScreenName($formated = false)
660 return $this->myFormatSN;
664 return $this->normalize($this->myScreenName);
669 * Generated Authorization Code
672 * @return Integer Auth Code
676 $sn = ord($this->myScreenName[0]) - 96;
677 $pw = ord($this->myPassword[0]) - 96;
678 $a = $sn * 7696 + 738816;
682 return $c - $a + $b + 71665152;
690 * @return String Data
694 if ($this->socketcheck($this->myConnection))
696 $this->log("Disconnected.... Reconnecting in 60 seconds");
701 $header = fread($this->myConnection,SFLAP_HEADER_LEN);
703 if (strlen($header) == 0)
705 $this->myLastReceived = "";
708 $header_data = unpack("aast/Ctype/nseq/ndlen", $header);
709 $this->log(" . ", false);
710 $packet = fread($this->myConnection, $header_data['dlen']);
711 if (strlen($packet) <= 0 && $sockinfo['blocked'])
712 $this->derror("Could not read data");
714 if ($header_data['type'] == SFLAP_TYPE_SIGNON)
716 $packet_data=unpack("Ndecoded", $packet);
719 if ($header_data['type'] == SFLAP_TYPE_KEEPALIVE)
721 $this->myLastReceived = '';
724 else if (strlen($packet)>0)
726 $packet_data = unpack("a*decoded", $packet);
728 $this->log("socketcheck check now");
729 if ($this->socketcheck($this->myConnection))
731 $this->derror("Connection ended unexpectedly");
734 $data = array_merge($header_data, $packet_data);
735 $this->myLastReceived = $data;
736 $this->logPacket($data,"in");
741 * Sends Data on Socket
743 * @param String $sflap_type Type
744 * @param String $sflap_data Data
745 * @param boolean $no_null No Null
746 * @param boolean $formatted Format
748 * @return String Roasted Password
750 function sflapSend($sflap_type, $sflap_data, $no_null, $formatted)
753 if (strlen($sflap_data) >= MAX_PACKLENGTH)
754 $sflap_data = substr($sflap_data,0,MAX_PACKLENGTH);
758 $len = strlen($sflap_len);
759 $sflap_header = pack("aCnn",'*', $sflap_type, $this->mySeqNum, $len);
760 $packet = $sflap_header . $sflap_data;
764 $sflap_data = str_replace("\0","", trim($sflap_data));
767 $data = pack("a*", $sflap_data);
768 $len = strlen($sflap_data);
769 $header = pack("aCnn","*", $sflap_type, $this->mySeqNum, $len);
770 $packet = $header . $data;
773 //Make sure we are still connected
774 if ($this->socketcheck($this->myConnection))
776 $this->log("Disconnected.... reconnecting in 60 seconds");
780 $sent = fputs($this->myConnection, $packet) or $this->derror("Error sending packet to AIM");
782 sleep(ceil($this->myWarnLevel/10));
783 $this->logPacket(array($sflap_type,$sflap_data),"out");
787 * Escape the thing that TOC doesn't like,that would be
790 * @param String $data Data to Escape
793 * @return String $data Escaped Data
795 function encodeData($data)
797 $data = str_replace('"','\"', $data);
798 $data = str_replace('$','\$', $data);
799 $data = str_replace("'","\'", $data);
800 $data = str_replace('{','\{', $data);
801 $data = str_replace('}','\}', $data);
802 $data = str_replace('[','\[', $data);
803 $data = str_replace(']','\]', $data);
808 * Unescape data TOC has escaped
811 * @param String $data Data to Unescape
814 * @return String $data Unescape Data
816 function decodeData($data)
818 $data = str_replace('\"','"', $data);
819 $data = str_replace('\$','$', $data);
820 $data = str_replace("\'","'", $data);
821 $data = str_replace('\{','{', $data);
822 $data = str_replace('\}','}', $data);
823 $data = str_replace('\[','[', $data);
824 $data = str_replace('\]',']', $data);
825 $data = str_replace('"','"', $data);
826 $data = str_replace('&','&', $data);
831 * Normalize ScreenName
832 * no spaces and all lowercase
834 * @param String $nick ScreenName
836 * @return String $nick Normalized ScreenName
838 function normalize($nick)
840 $nick = str_replace(" ","", $nick);
841 $nick = strtolower($nick);
846 * Sets internal info with update buddy
847 * Currently only sets warning level
854 //Sets internal values bvase on the update buddy command
855 $this->log("Setting my warning level ...");
856 $this->sflapSend(SFLAP_TYPE_DATA,"toc_get_status " . $this->normalize($this->myScreenName),0,0);
857 //The rest of this will now be handled by the other functions. It is assumed
858 //that we may have other data queued in the socket, do we should just add this
859 //message to the queue instead of trying to set it in here
863 * Connects to AIM and Signs On Using Info Provided in Constructor
870 $this->log("Ready to sign on to the server");
871 $this->myConnection = fsockopen($this->myServer, $this->myPort, $errno, $errstr,10) or die("$errorno:$errstr");
872 $this->log("Connected to server");
873 $this->mySeqNum = (time() % 65536); //Select an arbitrary starting point for
875 if (!$this->myConnection)
876 $this->derror("Error connecting to toc.oscar.aol.com");
877 $this->log("Connected to AOL");
878 //Send the flapon packet
879 fputs($this->myConnection,"FLAPON\r\n\n\0"); //send the initial handshake
880 $this->log("Sent flapon");
881 $this->sflapRead(); //Make sure the server responds with what we expect
882 if (!$this->myLastReceived)
883 $this->derror("Error sending the initialization string");
885 //send the FLAP SIGNON packet back with what it needs
886 //There are 2 parts to the signon packet. They are sent in succession, there
887 //is no indication if either packet was correctly sent
888 $signon_packet = pack("Nnna".strlen($this->myScreenName),1,1,strlen($this->myScreenName), $this->myScreenName);
889 $this->sflapSend(SFLAP_TYPE_SIGNON, $signon_packet,1,0);
890 $this->log("sent signon packet part one");
892 $signon_packet_part2 = 'toc2_signon login.oscar.aol.com 29999 ' . $this->myScreenName . ' ' . $this->myRoastedPass . ' english-US "TIC:TOC2:REVISION" 160 ' . $this->myAuthCode;
893 $this->log($signon_packet_part2 . "");
894 $this->sflapSend(SFLAP_TYPE_DATA, $signon_packet_part2,0,0);
895 $this->log("Sent signon packet part 2... Awaiting response...");
898 $this->log("Received Sign on packet, beginning initilization...");
899 $message = $this->getLastReceived();
900 $this->log($message . "\n");
901 if (strstr($message,"ERROR:"))
903 $this->onError($message);
904 die("Fatal signon error");
906 stream_set_timeout($this->myConnection,2);
907 //The information sent before the config2 command is utterly useless to us
908 //So we will just skim through them until we reach it
910 //Add the first entry to the connection array
911 $this->myConnections[] = $this->myConnection;
914 //UPDATED 4/12/03: Now this will use the receive function and send the
915 //received messaged to the assigned handlers. This is where the signon
916 //method has no more use
918 $this->log("Done with signon proccess");
919 //socket_set_blocking($this->myConnection,false);
923 * Sends Instant Message
925 * @param String $to Message Recipient SN
926 * @param String $message Message to Send
927 * @param boolean $auto Sent as Auto Response / Away Message Style
931 function sendIM($to, $message, $auto = false)
933 if ($auto) $auto = "auto";
935 $to = $this->normalize($to);
936 $message = $this->encodeData($message);
937 $command = 'toc2_send_im "' . $to . '" "' . $message . '" ' . $auto;
938 $this->sflapSend(SFLAP_TYPE_DATA, trim($command),0,0);
939 $cleanedmessage = str_replace("<br>", " ", $this->decodeData($message));
940 $cleanedmessage = strip_tags($cleanedmessage);
941 $this->log("TO - " . $to . " : " . $cleanedmessage);
947 * @param String $message Away message (some HTML supported).
948 * Use null to remove the away message
952 function setAway($message)
954 $message = $this->encodeData($message);
955 $command = 'toc_set_away "' . $message . '"';
956 $this->sflapSend(SFLAP_TYPE_DATA, trim($command),0,0);
957 $this->log("SET AWAY MESSAGE - " . $this->decodeData($message));
962 * Not implemented fully yet
967 function setBuddyList()
969 //This better be the right message
970 $message = $this->myLastReceived['decoded'];
971 if (strpos($message,"CONFIG2:") === false)
973 $this->log("setBuddyList cannot be called at this time because I got $message");
976 $people = explode("\n",trim($message,"\n"));
977 //The first 3 elements of the array are who knows what, element 3 should be
978 //a letter followed by a person
979 for($i = 1; $i<sizeof($people); $i++)
981 @list($mode, $name) = explode(":", $people[$i]);
985 $this->myPermitList[] = $name;
988 $this->myBlockedList[] = $name;
991 $this->myBuddyList[] = $name;
1002 * Adds buddy to Permit list
1004 * @param String $buddy Buddy's Screenname
1008 function addPermit($buddy)
1010 $this->sflapSend(SFLAP_TYPE_DATA,"toc2_add_permit " . $this->normalize($buddy),0,0);
1011 $this->myPermitList[] = $this->normalize($buddy);
1018 * @param String $buddy Buddy's Screenname
1022 function blockBuddy($buddy)
1024 $this->sflapSend(SFLAP_TYPE_DATA,"toc2_add_deny " . $this->normalize($buddy),0,0);
1025 $this->myBlockedList[] = $this->normalize($buddy);
1030 * Returns last message received from server
1033 * @return String Last Message from Server
1035 function getLastReceived()
1037 if (@$instuff = $this->myLastReceived['decoded']){
1038 return $this->myLastReceived['decoded'];
1045 * Returns Buddy List
1048 * @return array Buddy List
1050 function getBuddies()
1052 return $this->myBuddyList;
1056 * Returns Permit List
1059 * @return array Permit List
1061 function getPermit()
1063 return $this->myPermitList;
1067 * Returns Blocked Buddies
1070 * @return array Blocked List
1072 function getBlocked()
1074 return $this->myBlockedList;
1081 * Reads and returns data from server
1083 * This is a wrapper for $Aim->sflap_read(), and only returns the $this->myLastReceived['data']
1084 * portion of the message. It is preferred that you do not call $Aim->sflap_read() and use this
1085 * function instead. This function has a return value. Calling this prevents the need to call
1086 * $Aim->getLastReceived()
1089 * @return String Data recieved from server
1091 function read_from_aim()
1094 $returnme = $this->getLastReceived();
1099 * Sets current internal warning level
1101 * This allows you to update the bots warning level when warned.
1103 * @param int Warning Level %
1107 function setWarningLevel($warnlevel)
1109 $this->myWarnLevel = $warnlevel;
1113 * Warns / "Evils" a User
1115 * To successfully warn another user they must have sent you a message.
1116 * There is a limit on how much and how often you can warn another user.
1117 * Normally when you warn another user they are aware who warned them,
1118 * however there is the option to warn anonymously. When warning anon.
1119 * note that the warning is less severe.
1121 * @param String $to Screenname to warn
1122 * @param boolean $anon Warn's anonymously if true. (default = false)
1126 function warnUser($to, $anon = false)
1133 $this->sflapSend(SFLAP_TYPE_DATA,"toc_evil " . $this->normalize($to) . " $anon",0,0);
1137 * Returns warning level of bot
1142 function getWarningLevel()
1144 return $this->myWarningLevel;
1148 * Sets bot's profile/info
1150 * Limited to 1024 bytes.
1152 * @param String $profiledata Profile Data (Can contain limited html: br,hr,font,b,i,u etc)
1153 * @param boolean $poweredby If true, appends link to phpTOCLib project to profile
1157 function setProfile($profiledata, $poweredby = false)
1159 if ($poweredby == false){
1160 $this->myProfile = $profiledata;
1162 $this->myProfile = $profiledata . "<font size=1 face=tahoma><br><br>Powered by phpTOCLib<br>http://sourceforge.net/projects/phptoclib</font>";
1165 $this->sflapSend(SFLAP_TYPE_DATA,"toc_set_info \"" . $this->encodeData($this->myProfile) . "\"",0,0);
1167 $this->log("Profile has been updated...");
1170 //6/29/04 by Jeremy:
1171 //Added mthod to accept a rvous,decline it, and
1172 //read from the rvous socket
1177 * Declines a direct connection request (rvous)
1179 * @param String $nick ScreenName request was from
1180 * @param String $cookie Request cookie (from server)
1181 * @param String $uuid UUID
1186 function declineRvous($nick, $cookie, $uuid)
1188 $nick = $this->normalize($nick);
1189 $this->sflapSend(SFLAP_TYPE_DATA,"toc_rvous_cancel $nick $cookie $uuid",0,0);
1193 * Accepts a direct connection request (rvous)
1195 * @param String $nick ScreenName request was from
1196 * @param String $cookie Request cookie (from server)
1197 * @param String $uuid UUID
1198 * @param String $vip IP of User DC with
1199 * @param int $port Port number to connect to
1204 function acceptRvous($nick, $cookie, $uuid, $vip, $port)
1206 $this->sflapSend(SFLAP_TYPE_DATA,"toc_rvous_accept $nick $cookie $uuid",0,0);
1208 //Now open the connection to that user
1209 if ($uuid == IMAGE_UID)
1211 $dcon = new Dconnect($vip, $port);
1213 else if ($uuid == FILE_SEND_UID)
1215 $dcon = new FileSendConnect($vip, $port);
1217 if (!$dcon->connected)
1219 $this->log("The connection failed");
1223 //Place this dcon object inside the array
1224 $this->myDirectConnections[] = $dcon;
1225 //Place the socket in an array to
1226 $this->myConnections[] = $dcon->sock;
1229 //Get rid of the first packet because its worthless
1233 $dcon->cookie = $dcon->lastReceived['cookie'];
1234 $dcon->connectedTo = $this->normalize($nick);
1239 * Sends a Message over a Direct Connection
1241 * Only works if a direct connection is already established with user
1243 * @param String $to Message Recipient SN
1244 * @param String $message Message to Send
1249 function sendDim($to, $message)
1251 //Find the connection
1252 for($i = 0;$i<sizeof($this->myDirectConnections);$i++)
1254 if ($this->normalize($to) == $this->myDirectConnections[$i]->connectedTo && $this->myDirectConnections[$i]->type == CONN_TYPE_DC)
1256 $dcon = $this->myDirectConnections[$i];
1262 $this->log("Could not find a direct connection to $to");
1265 $dcon->sendMessage($message, $this->normalize($this->myScreenName));
1270 * Closes an established Direct Connection
1272 * @param DConnect $dcon Direct Connection Object to Close
1277 function closeDcon($dcon)
1281 for($i = 0;$i<sizeof($this->myConnections);$i++)
1283 if ($dcon->sock == $this->myConnections[$i])
1284 unset($this->myConnections[$i]);
1287 $this->myConnections = array_values($this->myConnections);
1291 for($i = 0;$i<sizeof($this->myDirectConnections);$i++)
1293 if ($dcon == $this->myDirectConnections[$i])
1294 unset($this->myDirectConnections[$i]);
1296 $this->myDirectConnections = array_values($this->myDirectConnections);
1301 //Added 4/29/04 by Jeremy:
1302 //Various chat related methods
1305 * Accepts a Chat Room Invitation (Joins room)
1307 * @param String $chatid ID of Chat Room
1312 function joinChat($chatid)
1314 $this->sflapSend(SFLAP_TYPE_DATA,"toc_chat_accept " . $chatid,0,0);
1318 * Leaves a chat room
1320 * @param String $chatid ID of Chat Room
1325 function leaveChat($chatid)
1327 $this->sflapSend(SFLAP_TYPE_DATA,"toc_chat_leave " . $chatid,0,0);
1331 * Sends a message in a chat room
1333 * @param String $chatid ID of Chat Room
1334 * @param String $message Message to send
1339 function chatSay($chatid, $message)
1341 $this->sflapSend(SFLAP_TYPE_DATA,"toc_chat_send " . $chatid . " \"" . $this->encodeData($message) . "\"",0,0);
1345 * Invites a user to a chat room
1347 * @param String $chatid ID of Chat Room
1348 * @param String $who Screenname of user
1349 * @param String $message Note to include with invitiation
1354 function chatInvite($chatid, $who, $message)
1356 $who = $this->normalize($who);
1357 $this->sflapSend(SFLAP_TYPE_DATA,"toc_chat_invite " . $chatid . " \"" . $this->encodeData($message) . "\" " . $who,0,0);
1361 * Joins/Creates a new chat room
1363 * @param String $name Name of the new chat room
1364 * @param String $exchange Exchange of new chat room
1369 function joinNewChat($name, $exchange)
1371 //Creates a new chat
1372 $this->sflapSend(SFLAP_TYPE_DATA,"toc_chat_join " . $exchange . " \"" . $name . "\"",0,0);
1376 * Disconnect error handler, attempts to reconnect in 60 seconds
1378 * @param String $message Error message (desc of where error encountered etc)
1383 function derror($message)
1385 $this->log($message);
1386 $this->log("Error");
1387 fclose($this->myConnection);
1388 if ((time() - $GLOBALS['errortime']) < 600){
1389 $this->log("Reconnecting in 60 Seconds");
1393 $GLOBALS['errortime'] = time();
1397 * Returns connection type of socket (main or rvous etc)
1399 * Helper method for recieve()
1401 * @param Resource $sock Socket to determine type for
1407 function connectionType($sock)
1409 //Is it the main connection?
1410 if ($sock == $this->myConnection)
1411 return CONN_TYPE_NORMAL;
1414 for($i = 0;$i<sizeof($this->myDirectConnections);$i++)
1416 if ($sock == $this->myDirectConnections[$i]->sock)
1417 return $this->myDirectConnections[$i]->type;
1424 * Checks for new data and calls appropriate methods
1426 * This method is usually called in an infinite loop to keep checking for new data
1430 * @see connectionType
1434 //This function will be used to get the incoming data
1435 //and it will be used to call the event handlers
1437 //First, get an array of sockets that have data that is ready to be read
1439 $ready = $this->myConnections;
1440 $numrdy = stream_select($ready, $w = NULL, $x = NULL,NULL);
1442 //Now that we've waited for something, go through the $ready
1443 //array and read appropriately
1445 for($i = 0;$i<sizeof($ready);$i++)
1448 $type = $this->connectionType($ready[$i]);
1449 if ($type == CONN_TYPE_NORMAL)
1451 //Next step:Get the data sitting in the socket
1452 $message = $this->read_from_aim();
1453 if (strlen($message) <= 0)
1458 //Third step: Get the command from the server
1459 @list($cmd, $rest) = explode(":", $message);
1461 //Fourth step, take the command, test the type, and pass it off
1462 //to the correct internal handler. The internal handler will
1463 //do what needs to be done on the class internals to allow
1464 //it to work, then proceed to pass it off to the user created handle
1470 $this->onSignOn($message);
1473 $this->onConfig($message);
1476 $this->onError($message);
1479 $this->onNick($message);
1482 $this->onImIn($message);
1484 case 'UPDATE_BUDDY2':
1485 $this->onUpdateBuddy($message);
1488 $this->onWarn($message);
1491 $this->onChatJoin($message);
1494 $this->onChatIn($message);
1496 case 'CHAT_UPDATE_BUDDY':
1497 $this->onChatUpdate($message);
1500 $this->onChatInvite($message);
1503 $this->onChatLeft($message);
1506 $this->onGotoURL($message);
1509 $this->onDirStatus($message);
1511 case 'ADMIN_NICK_STATUS':
1512 $this->onAdminNick($message);
1514 case 'ADMIN_PASSWD_STATUS':
1515 $this->onAdminPasswd($message);
1518 $this->onPause($message);
1520 case 'RVOUS_PROPOSE':
1521 $this->onRvous($message);
1524 $this->log("Fell through: $message");
1525 $this->CatchAll($message);
1531 for($j = 0;$j<sizeof($this->myDirectConnections);$j++)
1533 if ($this->myDirectConnections[$j]->sock == $ready[$i])
1535 $dcon = $this->myDirectConnections[$j];
1539 //Now read from the dcon
1540 if ($dcon->type == CONN_TYPE_DC)
1542 if ($dcon->readDIM() == false)
1544 $this->closeDcon($dcon);
1548 $message['message'] = $dcon->lastMessage;
1549 if ($message['message'] == "too big")
1551 $this->sendDim("Connection dropped because you sent a message larger that " . MAX_DCON_SIZE . " bytes.", $dcon->connectedTo);
1552 $this->closeDcon($dcon);
1555 $message['from'] = $dcon->connectedTo;
1556 $this->onDimIn($message);
1560 $this->conn->myLastReceived="";
1561 //Now get out of this function because the handlers should take care
1565 //The next block of code is all the event handlers needed by the class
1566 //Some are left blank and only call the users handler because the class
1567 //either does not support the command, or cannot do anything with it
1568 // ---------------------------------------------------------------------
1571 * Direct IM In Event Handler
1573 * Called when Direct IM is received.
1574 * Call's user handler (if available) for DimIn.
1577 * @param String $data Raw message from server
1580 function onDimIn($data)
1582 $this->callHandler("DimIn", $data);
1586 * Sign On Event Handler
1588 * Called when Sign On event occurs.
1589 * Call's user handler (if available) for SIGN_ON.
1592 * @param String $data Raw message from server
1595 function onSignOn($data)
1597 $this->callHandler("SignOn", $data);
1601 * Config Event Handler
1603 * Called when Config data received.
1604 * Call's user handler (if available) for Config.
1606 * Loads buddy list and other info
1609 * @param String $data Raw message from server
1612 function onConfig($data)
1614 $this->log("onConfig Message: " . $data);
1616 if (strpos($data,"CONFIG2:") === false)
1618 $this->log("get_buddy_list cannot be called at this time because I got $data");
1621 $people = explode("\n",trim($data,"\n"));
1622 //The first 3 elements of the array are who knows what, element 3 should be
1623 //a letter followed by a person
1625 //AIM decided to add this wonderful new feature, the recent buddy thing, this kind of
1626 //messes this funtion up, so we need to adapt it... unfortuneately, its not really
1627 //clear how this works, so we are just going to add their name to the permit list.
1629 //Recent buddies I believe are in the format
1630 //number:name:number.... I think the first number counts down from 25 how long its
1631 //been... but I don't know the second number,,,,
1633 //TODO: Figure out the new recent buddies system
1635 //Note: adding that at the bottom is a quick hack and may have adverse consequences...
1636 for($i = 1;$i<sizeof($people);$i++)
1638 @list($mode, $name) = explode(":", $people[$i]);
1642 $this->myPermitList[] = $name;
1645 $this->myBlockedList[] = $name;
1648 $this->myBuddyList[] = $name;
1653 //This is assumed to be recent buddies...
1654 $this->myPermitList[]=$name;
1658 //We only get the config message once, so now we should send our pd mode
1660 $this->sflapSend(SFLAP_TYPE_DATA,"toc2_set_pdmode " . $this->myPdMode,0,0);
1661 //Adds yourself to the permit list
1662 //This is to fix an odd behavior if you have nobody on your list
1663 //the server won't send the config command... so this takes care of it
1664 $this->sflapSend(SFLAP_TYPE_DATA,"toc2_add_permit " . $this->normalize($this->myScreenName),0,0);
1666 //Now we allow the user to send a list, update anything they want, etc
1667 $this->callHandler("Config", $data);
1668 //Now that we have taken care of what the user wants, send the init_done message
1669 $this->sflapSend(SFLAP_TYPE_DATA,"toc_init_done",0,0);
1676 $this->sflapSend(SFLAP_TYPE_DATA, "toc_set_caps " . IMAGE_UID . " " . FILE_SEND_UID ." " . FILE_GET_UID . " " . BUDDY_ICON_UID . "",0,0);
1681 * Error Event Handler
1683 * Called when an Error occurs.
1684 * Call's user handler (if available) for Error.
1687 * @param String $data Raw message from server
1690 function onError($data)
1692 static $errarg = '';
1693 static $ERRORS = array(
\r
1695 901 =>'$errarg not currently available',
\r
1696 902 =>'Warning of $errarg not currently available',
\r
1697 903 =>'A message has been dropped, you are exceeding
\r
1698 the server speed limit',
\r
1699 911 =>'Error validating input',
\r
1700 912 =>'Invalid account',
\r
1701 913 =>'Error encountered while processing request',
\r
1702 914 =>'Service unavailable',
\r
1703 950 =>'Chat in $errarg is unavailable.',
\r
1704 960 =>'You are sending message too fast to $errarg',
\r
1705 961 =>'You missed an im from $errarg because it was too big.',
\r
1706 962 =>'You missed an im from $errarg because it was sent too fast.',
\r
1708 971 =>'Too many matches',
\r
1709 972 =>'Need more qualifiers',
\r
1710 973 =>'Dir service temporarily unavailable',
\r
1711 974 =>'Email lookup restricted',
\r
1712 975 =>'Keyword Ignored',
\r
1713 976 =>'No Keywords',
\r
1714 977 =>'Language not supported',
\r
1715 978 =>'Country not supported',
\r
1716 979 =>'Failure unknown $errarg',
\r
1717 980 =>'Incorrect nickname or password.',
\r
1718 981 =>'The service is temporarily unavailable.',
\r
1719 982 =>'Your warning level is currently too high to sign on.',
\r
1720 983 =>'You have been connecting and
\r
1721 disconnecting too frequently. Wait 10 minutes and try again.
\r
1722 If you continue to try, you will need to wait even longer.',
\r
1723 989 =>'An unknown signon error has occurred $errarg'
\r
1725 $data_array = explode(":", $data);
1726 for($i=0; $i<count($data_array); $i++)
1731 $cmd = $data_array[$i];
1734 $errornum = $data_array[$i];
1737 $errargs = $data_array[$i];
1741 eval("\$errorstring=\"\$ERRORS[" . $errornum . "]\";");
1742 $string = "\$errorstring=\"\$ERRORS[$errornum]\";";
1743 //This is important information! We need
1744 // a A different outputter for errors
1745 // b Just to echo it
1746 //I'm just going to do a straight echo here, becuse we assume that
1747 //the user will NEED to see this error. An option to supress it will
1748 //come later I think. Perhaps if we did an error reporting level, similar
1749 //to PHP's, and we could probably even use PHP's error outputting system
1750 //I think that may be an idea....
1752 $this->log($errorstring . "\n");
1754 $this->callHandler("Error", $data);
1758 * Nick Event Handler
1760 * Called when formatted own ScreenName is receieved
1761 * Call's user handler (if available) for Nick.
1764 * @param String $data Raw message from server
1767 function onNick($data)
1769 //This is our nick, so set a field called "myFormatSN" which will represent
1770 //the actual name given by the server to us, NOT the normalized screen name
1771 @list($cmd, $nick) = explode(":", $data);
1772 $this->myFormatSN = $nick;
1774 $this->callHandler("Nick", $data);
1778 * IM In Event Handler
1780 * Called when an Instant Message is received.
1781 * Call's user handler (if available) for IMIn.
1784 * @param String $data Raw message from server
1787 function onImIn($data)
1789 //Perhaps we should add an internal log for debugging purposes??
1790 //But now, this should probably be handled by the user purely
1792 $this->callHandler("IMIn", $data);
1796 * UpdateBuddy Event Handler
1798 * Called when a Buddy Update is receieved.
1799 * Call's user handler (if available) for UpdateBuddy.
1800 * If info is about self, updates self info (Currently ownly warning).
1802 * ToDo: Keep track of idle, warning etc on Buddy List
1805 * @param String $data Raw message from server
1808 function onUpdateBuddy($data)
1810 //Again, since our class currently does not deal with other people without
1811 //outside help, then this is also probably best left to the user. Though
1812 //we should probably allow this to replace the setMyInfo function above
1813 //by handling the input if and only if it is us
1814 //Check and see that this is the command expected
1815 if (strpos($data,"UPDATE_BUDDY2:") == -1)
1817 $this->log("A different message than expected was received");
1821 //@list($cmd, $info['sn'], $info['online'], $info['warnlevel'], $info['signon'], $info['idle'], $info['uc']) = explode(":", $command['incoming']);
1823 //@list($cmd, $sn, $online, $warning, $starttime, $idletime, $uc) = explode(":", $data);
1824 $info = $this->getMessageInfo($data);
1825 if ($this->normalize($info['sn']) == $this->normalize($this->myScreenName))
1827 $warning = rtrim($info['warnlevel'],"%");
1828 $this->myWarnLevel = $warning;
1829 $this->log("My warning level is $this->myWarnLevel %");
1832 $this->callHandler("UpdateBuddy", $data);
1836 * Warning Event Handler
1838 * Called when bot is warned.
1839 * Call's user handler (if available) for Warn.
1840 * Updates internal warning level
1843 * @param String $data Raw message from server
1846 function onWarn($data)
1850 $command['incoming'] .= ":0";
1851 $it = explode(":", $command['incoming']);
1852 $info['warnlevel'] = $it[1];
1853 $info['from'] = $it[2];
1855 //SImply update our warning level
1856 //@list($cmd, $newwarn, $user) = explode(":", $data);
1858 $info = $this->getMessageInfo($data);
1860 $this->setWarningLevel(trim($info['warnlevel'],"%"));
1861 $this->log("My warning level is $this->myWarnLevel %");
1863 $this->callHandler("Warned", $data);
1869 * Called when bot joins a chat room.
1870 * Call's user handler (if available) for ChatJoin.
1871 * Adds chat room to internal chat room list.
1874 * @param String $data Raw message from server
1877 function onChatJoin($data)
1879 @list($cmd, $rmid, $rmname) = explode(":", $data);
1880 $this->myChatRooms[$rmid] = 0;
1882 $this->callHandler("ChatJoin", $data);
1886 * Returns number of chat rooms bot is in
1889 * @param String $data Raw message from server
1892 function getNumChats()
1894 return count($this->myChatRooms);
1898 * Chat Update Handler
1900 * Called when bot received chat room data (user update).
1901 * Call's user handler (if available) for ChatUpdate.
1904 * @param String $data Raw message from server
1907 function onChatUpdate($data)
1909 $stuff = explode(":", $data);
1910 $people = sizeof($stuff);
1913 $this->callHandler("ChatUpdate", $data);
1917 * Chat Message In Handler
1919 * Called when chat room message is received.
1920 * Call's user handler (if available) for ChatIn.
1923 * @param String $data Raw message from server
1926 function onChatIn($data)
1928 $this->callHandler("ChatIn", $data);
1933 * Chat Invite Handler
1935 * Called when bot is invited to a chat room.
1936 * Call's user handler (if available) for ChatInvite.
1939 * @param String $data Raw message from server
1942 function onChatInvite($data)
1944 //@list($cmd, $name, $id, $from, $data) = explode(":", $data,6);
1945 //$data = explode(":",$data,6);
1947 //@list($nm['cmd'],$nm['name'],$nm['id'],$nm['from'],$nm['message']) = $data;
1950 $this->callHandler("ChatInvite", $data);
1956 * Called when bot leaves a chat room
1957 * Call's user handler (if available) for ChatLeft.
1960 * @param String $data Raw message from server
1963 function onChatLeft($data)
1965 $info = $this->getMessageInfo($data);
1966 unset($this->myChatRooms[$info['chatid']]);
1967 $this->callHandler("ChatLeft", $data);
1973 * Called on GotoURL.
1974 * Call's user handler (if available) for GotoURL.
1975 * No detailed info available for this / Unsupported.
1978 * @param String $data Raw message from server
1981 function onGotoURL($data)
1983 //This is of no use to the internal class
1985 $this->callHandler("GotoURL", $data);
1989 * Dir Status Handler
1991 * Called on DirStatus.
1992 * Call's user handler (if available) for DirStatus.
1993 * No detailed info available for this / Unsupported.
1996 * @param String $data Raw message from server
1999 function onDirStatus($data)
2001 //This is not currently suported
2003 $this->callHandler("DirStatus", $data);
2009 * Called on AdminNick.
2010 * Call's user handler (if available) for AdminNick.
2011 * No detailed info available for this / Unsupported.
2014 * @param String $data Raw message from server
2017 function onAdminNick($data)
2019 //NOt particularly useful to us
2020 $this->callHandler("AdminNick", $data);
2024 * AdminPasswd Handler
2026 * Called on AdminPasswd.
2027 * Call's user handler (if available) for AdminPasswd.
2028 * No detailed info available for this / Unsupported.
2031 * @param String $data Raw message from server
2034 function onAdminPasswd($data)
2036 //Also not particlualry useful to the internals
2037 $this->callHandler("AdminPasswd", $data);
2044 * Call's user handler (if available) for Pause.
2045 * No detailed info available for this / Unsupported.
2048 * @param String $data Raw message from server
2051 function onPause($data)
2053 //This is pretty useless to us too...
2055 $this->callHandler("Pause", $data);
2059 * Direct Connection Handler
2061 * Called on Direct Connection Request(Rvous).
2062 * Call's user handler (if available) for Rvous.
2065 * @param String $data Raw message from server
2068 function onRvous($data)
2070 $this->callHandler("Rvous", $data);
2076 * Called for unrecognized commands.
2077 * Logs unsupported messages to array.
2078 * Call's user handler (if available) for CatchAll.
2081 * @param String $data Raw message from server
2084 function CatchAll($data)
2086 //Add to a log of unsupported messages.
2088 $this->unsupported[] = $data;
2089 //$this->log($data);
2092 $this->callHandler("CatchAll", $data);
2096 * Calls User Handler
2098 * Calls registered handler for a specific event.
2101 * @param String $event Command (event) name (Rvous etc)
2102 * @param String $data Raw message from server
2103 * @see registerHandler
2106 function callHandler($event, $data)
2109 if (isset($this->myEventHandlers[$event]))
2111 //$function = $this->myEventHandlers[$event] . "(\$data);";
2113 call_user_func($this->myEventHandlers[$event], $data);
2117 $this->noHandler($data);
2122 * Registers a user handler
2125 * SignOn, Config, ERROR, NICK, IMIn, UpdateBuddy, Eviled, Warned, ChatJoin
2126 * ChatIn, ChatUpdate, ChatInvite, ChatLeft, GotoURL, DirStatus, AdminNick
2127 * AdminPasswd, Pause, Rvous, DimIn, CatchAll
2130 * @param String $event Event name
2131 * @param String $handler User function to call
2133 * @return boolean Returns true if successful
2135 function registerHandler($event, $handler)
2137 if (is_callable($handler))
2139 $this->myEventHandlers[$event] = $handler;
2149 * No user handler method fall back.
2151 * Does nothing with message.
2154 * @param String $message Raw server message
2157 function noHandler($message)
2159 //This function intentionally left blank
2160 //This is where the handlers will fall to for now. I plan on including a more
2161 //efficent check to avoid the apparent stack jumps that this code will produce
2162 //But for now, just fall into here, and be happy
2169 * Finds type, and returns as part of array ['type']
2170 * Puts message in ['incoming']
2172 * Helper method for getMessageInfo.
2175 * @param String $message Raw server message
2177 * @see getMessageInfo
2180 static function msg_type($message)
2183 @list($cmd, $rest) = explode(":", $message);
2187 $type = AIM_TYPE_MSG;
2190 case 'UPDATE_BUDDY2':
2191 $type = AIM_TYPE_UPDATEBUDDY;
2195 $type = AIM_TYPE_WARN;
2199 $type = AIM_TYPE_SIGNON;
2203 $type = AIM_TYPE_NICK;
2207 $type = AIM_TYPE_ERROR;
2211 $type = AIM_TYPE_CHATJ;
2215 $type = AIM_TYPE_CHATI;
2218 case 'CHAT_UPDATE_BUDDY':
2219 $type = AIM_TYPE_CHATUPDBUD;
2223 $type = AIM_TYPE_CHATINV;
2227 $type = AIM_TYPE_CHATLE;
2231 $type = AIM_TYPE_URL;
2234 case 'ADMIN_NICK_STATUS':
2235 $type = AIM_TYPE_NICKSTAT;
2238 case 'ADMIN_PASSWD_STATUS':
2239 $type = AIM_TYPE_PASSSTAT;
2242 case 'RVOUS_PROPOSE':
2243 $type = AIM_TYPE_RVOUSP;
2247 $type = AIM_TYPE_NOT_IMPLEMENTED;
2250 $command['type'] = $type;
2251 $command['incoming'] = $message;
2256 * Parses message and splits into info array
2258 * Helper method for getMessageInfo.
2261 * @param String $command Message and type (after msg_type)
2263 * @see getMessageInfo
2266 static function msg_parse($command)
2269 switch($command['type'])
2272 $command['incoming'] .= ":0";
2273 $it = explode(":", $command['incoming']);
2274 $info['warnlevel'] = $it[1];
2275 $info['from'] = $it[2];
2280 $it = explode(":", $command['incoming'],5);
2281 $info['auto'] = $it[2];
2282 $info['from'] = $it[1];
2283 $info['message'] = $it[4];
2286 case AIM_TYPE_UPDATEBUDDY:
2287 @list($cmd, $info['sn'], $info['online'], $info['warnlevel'], $info['signon'], $info['idle'], $info['uc']) = explode(":", $command['incoming']);
2290 case AIM_TYPE_SIGNON:
2291 @list($cmd, $info['version']) = explode(":", $command['incoming']);
2295 @list($cmd, $info['nickname']) = explode(":", $command['incoming']);
2297 case AIM_TYPE_ERROR:
2298 @list($cmd, $info['errorcode'], $info['args']) = explode(":", $command['incoming']);
2301 case AIM_TYPE_CHATJ:
2302 @list($cmd, $info['chatid'], $info['chatname']) = explode(":", $command['incoming']);
2305 case AIM_TYPE_CHATI:
2306 @list($cmd, $info['chatid'], $info['user'], $info['whisper'], $info['message']) = explode(":", $command['incoming'],5);
2309 case AIM_TYPE_CHATUPDBUD:
2310 @list($cmd, $info['chatid'], $info['inside'], $info['userlist']) = explode(":", $command['incoming'],3);
2313 case AIM_TYPE_CHATINV:
2314 @list($cmd, $info['chatname'], $info['chatid'], $info['from'], $info['message']) = explode(":", $command['incoming'],5);
2317 case AIM_TYPE_CHATLE:
2318 @list($cmd, $info['chatid']) = explode(":", $command['incoming']);
2322 @list($cmd, $info['windowname'], $info['url']) = explode(":", $command['incoming'],3);
2325 case AIM_TYPE_RVOUSP:
2326 @list($cmd,$info['user'],$info['uuid'],$info['cookie'],$info['seq'],$info['rip'],$info['pip'],$info['vip'],$info['port'],$info['tlvs']) = explode(":",$command['incoming'],10);
2329 case AIM_TYPE_NICKSTAT:
2330 case AIM_TYPE_PASSSTAT:
2331 @list($cmd, $info['returncode'], $info['opt']) = explode(":", $command['incoming'],3);
2335 $info['command'] = $command['incoming'];
2341 * Returns a parsed message
2343 * Calls msg_parse(msg_type( to first determine message type and then parse accordingly
2346 * @param String $command Raw server message
2351 static function getMessageInfo($message)
2353 return self::msg_parse(self::msg_type($message));
2357 * Checks socket for end of file
2360 * @param Resource $socket Socket to check
2361 * @return boolean true if end of file (socket)
2363 static function socketcheck($socket){
2364 $info = stream_get_meta_data($socket);
2365 return $info['eof'];
2366 //return(feof($socket));