* @revision 04 * @license http://creativecommons.org/licenses/by-sa/3.0/de/ Creative Commons Attribution-Share Alike 3.0 Germany * @homepage http://oss.tiggerswelt.net/oscar/ * @copyright Copyright © 2009 tiggersWelt.net */ // Hack the include-path to make old implemtations happy set_include_path (dirname (dirname (__FILE__)) . PATH_SEPARATOR . get_include_path ()); // Load required libraries require_once ("oscar/common.php"); require_once ("oscar/flap.php"); require_once ("oscar/snac.php"); require_once ("oscar/tlv.php"); require_once ("oscar/capability.php"); require_once ("oscar/roster.php"); class Oscar_Client { /* State-Types */ const STATE_DISCONNECTED = 0; const STATE_AUTHENTICATING = 1; const STATE_CONNECTED = 2; /* Our socket and additional protocol-informations */ private $Socket = null; private $idleCount = 0; private $ServerFamilies = null; private $ClientFamilies = null; private $Username = ""; private $Password = ""; private $State = Oscar_Client::STATE_DISCONNECTED; private $loginState = 0; /* OSCAR-Extensions */ private $Capabilities = array (); /* Information about our online-status */ private $Status = -1; private $awayMessage = ""; /* Internal roster */ private $Roster = null; /* Handle of FLAP-writer */ private $FLAP = null; /* Callbacks */ private $Callbacks = array (); private $icbmCallbacks = array (); // {{{ __construct /** * Constructor of this class * * @access friendly * @return void */ function __construct () { # TODO: Improve this require_once ("oscar/capability/extended_tlv.php"); require_once ("oscar/capability/utf8.php"); require_once ("oscar/capability/realtimeim.php"); require_once ("oscar/capability/statusmessages.php"); require_once ("oscar/capability/filetransfer.php"); // Register hardcoded capabilities new Oscar_Capability_Extended_TLV ($this); new Oscar_Capability_UTF8 ($this); new Oscar_Capability_RealtimeIM ($this); new Oscar_Capability_StatusMessages ($this); new Oscar_Capability_Filetransfer ($this); } // }}} // {{{ registerCapability /** * Register an Oscar-Extension * * @param object $Capability * * @access public * @return bool */ public function registerCapability ($Capability) { // Get the Capability-ID $OLE_Strs = $Capability->getCapabilityID (); if (!is_array ($OLE_Strs)) $OLE_Strs = array ($OLE_Strs); foreach ($OLE_Strs as $OLE_Str) { // Strip everything unneccessary from the ID if (($OLE_Str = Oscar_TLV_Helper_Capability::stripCapability ($OLE_Str)) === false) return false; // Check if the extension was registered before if (isset ($this->Capabilities [$OLE_Str])) continue; // Register the extension $this->Capabilities [$OLE_Str] = $Capability; } return true; } // }}} // {{{ getCapabilities /** * Retrive a list of all registered capabilities * * @param bool $Flatten (optional) Just retrive their keys * * @access public * @return array */ public function getCapabilities ($Flatten = false) { if (!$Flatten) return $this->Capabilities; $out = array (); foreach ($this->Capabilities as $Key=>$Capa) $out [] = $Key; return $out; } // }}} // {{{ getFLAP /** * Retrive handle of our FLAP-writer * * @access public * @return object */ public function getFLAP ($Reset = false) { // Check if we have a valid handle if (!is_object ($this->FLAP) || $Reset) $this->FLAP = new Oscar_FLAP ($this->Socket, Oscar_FLAP::FRAME_DATA, $this); // Reset the flap if ($Reset) { $this->FLAP->reset (); $this->FLAP->setSocket ($this->Socket); } // Return handle return $this->FLAP; } // }}} // {{{ isOnline /** * Check if we are connected to Oscar * * @access public * @return bool */ public function isOnline () { return $this->State == Oscar_Client::STATE_CONNECTED; } // }}} // {{{ login /** * Perform roasted login * * @param string $Username * @param string $Password * @param string $Server (optional) * @param int $Port (optional) * @param bool $Block (optional) Wait until login was successfull * * @access public * @return bool * @see http://iserverd.khstu.ru/oscar/cli_ident.html */ public function login ($Username, $Password, $Server = null, $Port = null, $Block = true) { // Don't try to login twice if ($this->State == Oscar_Client::STATE_CONNECTED) return true; elseif ($this->State != Oscar_Client::STATE_DISCONNECTED) return null; // Check default values if ($Server === null) $Server = "login.oscar.aol.com"; if ($Port === null) $Port = 5190; // Try to connect to ICQ-Server if (!is_resource ($this->Socket = @fsockopen ($Server, $Port))) return false; // We are now in authenticating state $this->State = Oscar_Client::STATE_AUTHENTICATING; $this->loginState = 0; // Create a new FLAP-Packet $FLAP = self::getFLAP (true); // We are in authentication-mode $FLAP->setChannel (Oscar_FLAP::FRAME_SIGNON); // Stolen from ICQ 6.5.0 - DUNNO what this means, but it works $FLAP->Data = "\x80\x03\x00\x04\x00\x10\x00\x00"; $FLAP->write (); // Queue our TLV's $this->Username = $Username; $this->Password = $Password; $SNAC = new Oscar_SNAC_Auth_Request ($this, $this->Username); $SNAC->writeFLAP (); // Check wheter perform the whole login here or not if (!$Block) return null; // Handle incoming events until connected while ($this->State == Oscar_Client::STATE_AUTHENTICATING) { self::handleEvent (); usleep (125); } // Check wheter we are connected return ($this->State == Oscar_Client::STATE_CONNECTED); } // }}} // {{{ logoff /** * Close connection to Oscar * * @access public * @return bool */ public function logoff () { // Check if we are already offline if ($this->State == Oscar_Client::STATE_DISCONNECTED) return; $wasOnline = $this->isOnline (); // Say goodbye on FLAP if (is_object ($FLAP = self::getFLAP ())) $FLAP->logoff (); // Close the socket if ($wasOnline && is_resource ($this->Socket)) fclose ($this->Socket); print "logged off\n"; $this->clientDisconnected (false, !$wasOnline); } // }}} // {{{ roastPassword /** * Generate a roasted password * * @param string $Password * * @access private * @return string */ private function roastPassword ($Password) { $Roast = array (0xF3, 0x26, 0x81, 0xC4, 0x39, 0x86, 0xDB, 0x92, 0x71, 0xA3, 0xB9, 0xE6, 0x53, 0x7A, 0x95, 0x7C); for ($i = 0; $i < strlen ($Password); $i++) $Password [$i] = chr (ord ($Password [$i]) ^ $Roast [$i % count ($Roast)]); return $Password; } // }}} // {{{ getServerFamilies /** * Get Families of Server * * @access public * @return object */ public function getServerFamilies () { return $this->ServerFamilies; } // }}} // {{{ getClientFamilies /** * Get Families of Client * * @access public * @return object */ public function getClientFamilies () { return $this->ClientFamilies; } // }}} // {{{ handleEvent /** * Try to handle incoming packets * * @access public * @return void */ public function handleEvent () { // Check our internal state if ($this->State == Oscar_Client::STATE_DISCONNECTED) return false; // Get our own FLAP-Handler if (!is_object ($Resp = self::getFLAP ())) return false; // Try to receive a new FLAP-Message if (($rc = $Resp->read (true)) === false) { print "Read failed\n"; $this->State = Oscar_Client::STATE_DISCONNECTED; $this->clientDisconnected (true, false); return false; } elseif ($rc === null) { if ($this->idleCount > 89) { $this->idleCount = 0; $Resp->keepAlive (); } else $this->idleCount++; return false; } // Output some debugging-information if (!is_object ($SNAC = $Resp->getSNAC ())) { trigger_error ("Received non-SNAC packet on channel 0x" . dechex ($Resp->Channel)); Oscar_FLAP::debugPacket ($Resp->generate (), "S: "); return; } if (defined ("OSCAR_DEBUG_IN") && (OSCAR_DEBUG_IN != 0)) print "Received SNAC of Type 0x" . dechex ($SNAC->ServiceID) . " / 0x" . dechex ($SNAC->SubtypeID) . " (aka " . get_class ($SNAC) . ")\n\n"; // Try to handle login-packet if (($this->State == Oscar_Client::STATE_AUTHENTICATING) && self::handleLogin ($SNAC)) return $this->handleCallbacks ($SNAC); // Load our roster $Roster = $this->getRoster (); // Handle status-Updates from server for us if ($SNAC instanceof Oscar_SNAC_Service_Online_Info) { if (is_object ($TLV = $SNAC->findTLV ("OSCAR_TLV_Status"))) $this->Status = $TLV->Status; // Handle incoming roster-updates } elseif ($SNAC instanceof Oscar_SNAC_Feedbag_Reply) { $Roster->updateFromSNAC ($SNAC); if (defined ("OSCAR_DEBUG_IN") && (OSCAR_DEBUG_IN != 0)) print "Recieived Roster Update to version " . $SNAC->Version . "\n"; // Forward Roster-Update self::rosterUpdate ($SNAC->Version, $SNAC->LastMod); } return $this->handleCallbacks ($SNAC); } // }}} // {{{ handleLogin /** * Handle some SNACs for login-process * * @accss private * @return bool **/ private function handleLogin ($SNAC) { // Retrive class of SNAC $Class = strtolower (get_class ($SNAC)); // Set response $RespSNAC = null; // Set handled-Flag to true $rc = true; switch ($Class) { // Incoming Key for authentication case "oscar_snac_auth_authkey": $RespSNAC = new Oscar_SNAC_Auth_Login ($this, $this->Username, $this->Password, $SNAC->getKey ()); break; // Incoming Login-Reply case "oscar_snac_auth_loginreply": // Close the socket for the moment fclose ($this->Socket); $SNAC->tlvDebug (); $FLAP = self::getFLAP (); // Check for an error if (!$SNAC->isSuccess ()) { $this->State = Oscar_Client::STATE_DISCONNECTED; $this->clientOffline (true); if (is_object ($FLAP)) $FLAP->logoff (); return true; } // Connect to new server if (!($Hostname = $SNAC->getServer ()) || !($Port = $SNAC->getPort ()) || !($Cookie = $SNAC->getCookie ()) || !is_resource ($this->Socket = fsockopen ($Hostname, $Port))) { $this->State = Oscar_Client::STATE_DISCONNECTED; $this->clientOffline (true); if (is_object ($FLAP)) $FLAP->logoff (); return true; } // Reset the FLAP-Handler $FLAP = self::getFLAP (true); // We are still in authentication-mode $FLAP->setChannel (Oscar_FLAP::FRAME_SIGNON); // Add Cookie to our queue and submit to server $FLAP->queue ($Cookie); $FLAP->write (); return true; // Initial Server Families from Oscar case "oscar_snac_service_hostonline": // Remember this SNAC for later use $this->ServerFamilies = $SNAC; // Create a SNAC with *our* families if (!is_object ($this->ClientFamilies)) $this->ClientFamilies = new Oscar_SNAC_Service_ClientGroups ($this); // Send this SNAC back $RespSNAC = $this->ClientFamilies; break; // Handle accepted SNAC Versions case "oscar_snac_service_servergroups": $RespSNAC = new Oscar_SNAC_Rate_Request ($this); break; // Handle incoming rate informations case "oscar_snac_rate_reply": $BuddySNAC = new Oscar_SNAC_Buddy_QueryRights ($this); $BuddySNAC->addFeature (0x0001); $BuddySNAC->addFeature (0x0004); $RespSNAC = array ( new Oscar_SNAC_Service_Rate_Accept ($this, $SNAC), new Oscar_SNAC_Service_Request_Own_Online_Info ($this), new Oscar_SNAC_Locate_Request ($this), $BuddySNAC, new Oscar_SNAC_ICBM_Request ($this), new Oscar_SNAC_Privacy_Request ($this), new Oscar_SNAC_SSI_Request ($this), new Oscar_SNAC_SSI_Checkout ($this), ); break; case "oscar_snac_locate_response": $RespSNAC = new Oscar_SNAC_Locate_Info ($this, self::getCapabilities (true)); $this->loginState = $this->loginState + 0x0001; break; case "oscar_snac_icbm_response": // Mark ICBM-Negotiation as completed $this->loginState = $this->loginState + 0x0010; // Set ICBM Parameters $RespSNAC = new Oscar_SNAC_ICBM_Set ($this); $RespSNAC->Channel = 0; $RespSNAC->MessageFlags = Oscar_SNAC_ICBM_Set::CHANNEL_MSGS_ALLOWED | Oscar_SNAC_ICBM_Set::EVENTS_ALLOWED | Oscar_SNAC_ICBM_Set::SMS_SUPPORTED | Oscar_SNAC_ICBM_Set::OFFLINE_MSGS_ALLOWED; break; case "oscar_snac_feedbag_reply": // Forward this SNAC to handleEvent $rc = false; case "oscar_snac_ssi_unchanged": $RespSNAC = new Oscar_SNAC_SSI_Active ($this); $this->loginState = $this->loginState + 0x0100; break; // This SNAC is not handled by login-process default: return false; } // Check wheter login-process was completed if (($this->loginState & 0x0111) == 0x0111) { if ($RespSNAC === null) $RespSNAC = array (); elseif (!is_array ($RespSNAC)) $RespSNAC = array ($RespSNAC); $RespSNAC [] = new Oscar_SNAC_Service_Status_Info ($this, 0x10000000, "Hello World!"); $RespSNAC [] = new Oscar_SNAC_Service_ClientOnline ($this); $this->State = Oscar_Client::STATE_CONNECTED; $this->retriveOfflineMessages (); $this->clientOnline (); } // Submit the response if (is_object ($RespSNAC)) $RespSNAC->writeFLAP (); elseif (is_array ($RespSNAC)) foreach ($RespSNAC as $rSNAC) if (is_object ($rSNAC)) $rSNAC->writeFLAP (); return $rc; } // }}} // {{{ handleCallbacks /** * Run callbacks for in incoming SNAC * * @param object $SNAC * * @access protected * @return void **/ protected function handleCallbacks ($SNAC) { // Generate unique identifier from SNAC $snacFa = ($SNAC->ServiceID << 16); $snacID = $snacFa + $SNAC->SubtypeID; // Handle special ICBM-Callbacks if (($SNAC->ServiceID == Oscar_SNAC_ICBM::SNAC_FAMILY) && isset ($SNAC->Cookie) && isset ($this->icbmCallbacks [$SNAC->Cookie])) self::runCallbacks ($this->icbmCallbacks [$SNAC->Cookie], $SNAC); // Handle callbacks for this exact type if (isset ($this->Callbacks [$snacID])) self::runCallbacks ($this->Callbacks [$snacID], $SNAC); // Handle callbacks for this SNAC-Family if (isset ($this->Callbacks [$snacFa])) self::runCallbacks ($this->Callbacks [$snacID], $SNAC); // Handle overall-callbacks if (isset ($this->Callbacks [0x0])) self::runCallbacks ($this->Callbacks [$snacID], $SNAC); } // }}} // {{{ runCallbacks /** * Just fire some functions for a given SNAC * * @param array $Callbacks * @param object $SNAC * * @access private * @return void **/ private function runCallbacks (&$Callbacks, $SNAC) { // Check if input is an array if (!is_array ($Callbacks)) return; // Run all callbacks foreach ($Callbacks as $idx=>$Callback) { // Remove the callback if it was queried as once if ($Callback [1]) unset ($Callbacks [$idx]); // Invoke the callback an check wheter to proceed if (call_user_func ($Callback [0], $SNAC) === false) break; } } // }}} // {{{ setCallback /** * Add a callback for an incoming SNAC-Message * * @param callback $Callback * @param int $Family * @param int $Subtype (optional) * @param bool $Once (optional) * * @access public * @return bool **/ public function setCallback ($Callback, $Family, $Subtype = 0x0000, $Once = false) { // Check if this is really a callback if (!is_callable ($Callback)) return false; // Determine key for this callback $Key = ($Family << 16) + $Subtype; // Make sure that our structure is intact if (!isset ($this->Callbacks [$Key]) || !is_array ($this->Callbacks [$Key])) $this->Callbacks [$Key] = array (); // Add the callback $this->Callbacks [$Key][] = array ( $Callback, $Once, ); return true; } // }}} // {{{ setICBMCallback /** * Add a callback for an ICBM-Message * * @param callback $Callback * @param string $Cookie * @param bool $Once (optional) Just use this callback once * * @access public * @return bool **/ public function setICBMCallback ($Callback, $Cookie, $Once = false) { // Check if this is really a callback if (!is_callable ($Callback)) return false; // Make sure that our structure is intact if (!isset ($this->icbmCallbacks [$Cookie]) || !is_array ($this->icbmCallbacks [$Cookie])) $this->icbmCallbacks [$Cookie] = array (); // Add the callback $this->icbmCallbacks [$Cookie][] = array ( $Callback, $Once, ); return true; } // }}} // ================================================================ // Roster Management // {{{ getRoster /** * Retrive a handle of our internal roster * * @access public * @return object */ public function getRoster () { if (!is_object ($this->Roster)) $this->Roster = new Oscar_Roster ($this); return $this->Roster; } // }}} // {{{ requestRoster /** * Request the roster from the server * * @param int $Version (optional) * @param int $Date (optional) * * @access public * @return void */ public function requestRoster ($Version = null, $Date = null) { // Create the matching SNAC if (($Version !== null) && ($Date !== null)) { $SNAC = new Oscar_SNAC_SSI_Checkout ($this); $SNAC->Counter = $Version; $SNAC->ModTime = $Date; } else $SNAC = new Oscar_SNAC_SSI_Request_Roster ($this); // Write the SNAC to stream return $SNAC->writeFLAP (); } // }}} // {{{ requestAuthorization /** * Request authorization from someone * * @param string $From * @param string $Reason (optional) * * @access public * @return void */ public function requestAuthorization ($From, $Reason = null) { // Check for default reason if ($Reason === null) $Reason = "Please authorize me"; // Create the packet $SNAC = new Oscar_SNAC_SSI_Request_Authorization ($this); $SNAC->Username = $From; $SNAC->Reason = $Reason; return $SNAC->writeFLAP (); } // }}} // {{{ acceptAuthorization /** * Give authorization to someone * * @param string $From * @param string $Reason (optional) * * @access public * @return void */ public function acceptAuthorization ($From, $Reason = null) { // Check for default reason if ($Reason === null) $Reason = "You are welcome"; // Create the packet $SNAC = new Oscar_SNAC_SSI_Reply_Authorization ($this); $SNAC->Username = $From; $SNAC->Reason = $Reason; $SNAC->Accepted = true; return $SNAC->writeFLAP (); } // }}} // {{{ rejectAuthorization /** * Reject an authorization-request from someone * * @param string $From * @param string $Reason (optional) * * @access public * @return void */ public function rejectAuthorization ($From, $Reason = null) { // Check for default reason if ($Reason === null) $Reason = "I don't want you to add me to your roster"; // Create the packet $SNAC = new Oscar_SNAC_SSI_Reply_Authorization ($this); $SNAC->Username = $From; $SNAC->Reason = $Reason; $SNAC->Accepted = false; return $SNAC->writeFLAP (); } // }}} // {{{ acceptFutureAuthorization /** * Give authorization to someone to add us in future to his roster * * @param string $From * @param string $Reason (optional) * * @access public * @return void */ public function acceptFutureAuthorization ($From, $Reason = null) { // Check for default reason if ($Reason === null) $Reason = "You are welcome to add me in future"; // Create the packet $SNAC = new Oscar_SNAC_SSI_Grant_Future_Authorization ($this); $SNAC->Username = $From; $SNAC->Reason = $Reason; return $SNAC->writeFLAP (); } // }}} // {{{ removeAuthorization /** * Remove ourself from someone elses roster * * @param string $From * * @access public * @return void */ public function removeAuthorization ($From) { // Create the packet $SNAC = new Oscar_SNAC_Feedbag_Selfremove ($this); $SNAC->Username = $From; return $SNAC->writeFLAP (); } // }}} // ================================================================ // OSCAR API (usefull for client-implementations) // {{{ retriveOfflineMessages /** * Request stored (offline) Messages from OSCAR * * @access public * @return void **/ public function retriveOfflineMessages () { $SNAC = new Oscar_SNAC_ICBM_RetriveOffline ($this); return $SNAC->writeFLAP (); } // }}} // {{{ sendMessage /** * Send a Message via OSCAR * * @param string $To Where to send the message to * @param string $Message Which message to send * * @access public * @return bool */ public function sendMessage ($To, $Message) { $SNAC = new Oscar_SNAC_ICBM_Message ($this); $SNAC->setReceiver ($To); $SNAC->setMessage ($Message); return $SNAC->writeFLAP (); } // }}} // ================================================================ // Status and away-messaes // {{{ setStatus /** * Set our status * * @param int $Status * @param bool $awayHack (optional) Hack Away-Message for pidgin & co. * * @access public * @return void */ public function setStatus ($Status, $awayHack = false) { // Hack Away-Message for pidgin if ($awayHack) { $SNAC = new Oscar_SNAC_Locate_Info ($this); $SNAC->setMessage ($this->awayMessage); $SNAC->writeFLAP (); } // Hack for newer ICQs $SNAC = new Oscar_SNAC_ICQSrv_Client ($this); $TLV = new Oscar_TLV_ICQSrv_Data ($this); $l = strlen ($this->awayMessage); $TLV->UIN = intval ($this->Username); $TLV->Command = 2000; $TLV->Sequence = rand (1, time ()); $TLV->Payload = "\xd2\x0f" . OSCAR_Common::int16tostr ($l + 34, true) . "\x05\xb9" . "\x00\x03\x80\x00\x00\x00\x00\x00\x00\x06\x00\x01\x00\x02\x00\x02" . "\x00\x00\x04\xe4\x00\x00\x00\x02\x00\x03" . OSCAR_Common::int16tostr ($l + 4) . "\x02\x26" . OSCAR_Common::int16tostr ($l) . $this->awayMessage; $SNAC->addTLV ($TLV); $SNAC->writeFlap (); // Create a new Status-Info SNAC $SNAC = new Oscar_SNAC_Service_Status_Info ($this, $Status, $this->awayMessage); // Write SNAC to Stream return $SNAC->writeFLAP (); } // }}} // {{{ setAwayMessage /** * Set our away-message * * @param string $Message * @param int $newStatus (optional) Set new status as well * * @access public * @return void */ public function setAwayMessage ($Message, $newStatus = null) { // Store the new away-message $this->awayMessage = $Message; // Set new Status if ($newStatus === null) $newStatus = $this->Status; // Transmit the new data self::setStatus ($newStatus, true); } // }}} // {{{ getAwayMessage /** * Get our away-message * * @access public * @return string */ public function getAwayMessage () { return $this->awayMessage; } // }}} // {{{ requestAwayMessage /** * Request the away-message of a OSCAR-User * * @param string $User * * @access public * @return void * @todo Include support for syncronous operations */ public function requestAwayMessage ($User) { $SNAC = new Oscar_SNAC_Locate_Query_User_Info ($this); $SNAC->Username = $User; $SNAC->Query = Oscar_SNAC_Locate_Query_User_Info::QUERY_AWAYMESSAGE; return $SNAC->writeFLAP (); } // }}} // ================================================================ // SNAC Callbacks // {{{ clientOnline /** * This is called after a successfull authentication * * @access public * @return void */ public function clientOnline () { } // }}} // {{{ clientOffline /** * This one is called after an authentication-failure or at a normal logoff * * @param bool $Failure * * @access public * @return void */ public function clientOffline ($Failure) { } // }}} // {{{ clientDisconnected /** * Callback is invoked by FLAP-Writer whenever the connection was lost * * @param bool $Reconnect (optional) Try to reconnect * @param bool $Failure (optional) * * @access public * @return void **/ public function clientDisconnected ($Reconnect = true, $Failure = true) { $this->State = Oscar_Client::STATE_DISCONNECTED; if (is_object ($this->Roster)) $this->Roster->setOffline (); # TODO: Improve this! if ($Reconnect && ($this->Username != "") && ($this->Password != "")) $this->login ($this->Username, $this->Password, $Server = null, $Port = null, $Block = true); else $this->clientOffline ($Failure); } // }}} // {{{ receiveAwayMessage /** * Asyncronously receive an away-message * * @param string $From * @param string $Message * @param string $ContentType (optional) * * @access public * @return void */ public function receiveAwayMessage ($From, $Message, $ContentType) { } // }}} // {{{ receiveMessage /** * Receive a message from the OSCAR-Network * * @param string $From The user who send this message * @param string $Message The received message * @param enum $Encoding * * @access public * @return void */ public function receiveMessage ($From, $Message, $Encoding) { } // }}} // {{{ contactOnline /** * This callback is invoked whenever a contact comes online * * @param string $From * @param string $Status * * @access public * @return void */ public function contactOnline ($From, $Status) { } // }}} // {{{ contactOffline /** * This callback is invoked whenever a contact goes offline * * @param string $From * * @access public * @return void */ public function contactOffline ($From) { } // }}} // {{{ contactStatus /** * This callback is invoked whenever a contact changes its status * * @param string $From * @param string $Status * * @access public * @return void */ public function contactStatus ($From, $Status) { } // }}} // {{{ rosterUpdate /** * This callback is invoked whenever a new roster-version was received from server * * @param int $Version * @param int $Date * * @access public * @return void */ public function rosterUpdate ($Version, $Date) { } // }}} // {{{ remoteRosterUpdate /** * Someone else added us to his roster * * @param string $From * * @access public * @return void */ public function remoteRosterUpdate ($From) { } // }}} // {{{ authorizationRequest /** * This callback is invoked when someone else wants to add us * * You may return true to authorize the request, * you may return false to reject it or * you return null if you choose not to handle it here * * @param string $From * @param string $Reason * * @access public * @return bool */ public function authorizationRequest ($From, $Reason) { } // }}} // {{{ authorizationReply /** * This callback is invoked when someone sent an authorization-reply * * @param string $From * @param string $Reason * @param bool $Success * @param bool $Future Was this authorization given for future requests * * @access public * @return void */ public function authorizationReply ($From, $Reason, $Success, $Future) { } // }}} } ?>