* @revision 02 * @license http://creativecommons.org/licenses/by-sa/3.0/de/ Creative Commons Attribution-Share Alike 3.0 Germany * @homepage http://oss.tiggerswelt.net/xmpp * @copyright Copyright © 2009 tiggersWelt.net */ require_once ('tiggerXMPP/extension.php'); /** * XEP-0077: In-Band Registration * * Small implemtation of XEP 0077 for tiggerXMPP * * @class XEP_0077 * @package tiggerXMPP * @revision 02 * @author Bernd Holzmueller */ class tiggerXMPP_XEP_0077 extends tiggerXMPP_Extension { /* Our XML-Namespace */ const XEP_NAMESPACE = 'jabber:iq:register'; /* Our entities */ private $Entities = array (); // {{{ getTags /** * Retrive a list of all tags we handle for a given namespace * * @param string $Namespace * * @access public * @return array **/ public function getTags ($NS = '') { return array ('query'); } // }}} // {{{ handle /** * Handle an incoming packet * * @param object $Tag Packet to handle * * @access public * @return object * @todo Add Support for iq set * @todo Add Support for data-Output */ public function handle ($Tag) { $this->__debug (tiggerXMPP_Stream::DEBUG_DEBUG, 'called.', __FUNCTION__, __LINE__, __CLASS__, __FILE__); if (!is_object ($p = $Tag->getParent ())) return false; // Get receiver of this packet $ToJID = strtolower ($p->getDestination ()); // Generate an answer $Resp = new phpEvents_Socket_Stream_XML_Tag ('query'); $Resp->setNamespace ($Tag->getNamespace ()); // Find a matching entity $Entity = $this->getEntity ($p->getDestination (), $p->getOriginator ()); // Handle case where no entity was found if (!is_object ($Entity)) { $Instructions = new phpEvents_Socket_Stream_XML_Tag ('instructions', $Resp, 'In-Band registration unsupported for this entity'); return array ( $Resp, new tiggerXMPP_Error (tiggerXMPP_Error::ERROR_SERVICE_UNAVAILABLE, "Extension unconfigured"), ); } // Check wheter to update entity if ($p->getAttribute ('type') == 'set') { // Handle removals if ($Tag->haveSubtags ('remove')) { if (!($rc = $Entity->removeData ($p->getOriginator ()))) $rc = !$Entity->hasData ($p->getOriginator ()); if (!$rc) return array (new tiggerXMPP_Error (tiggerXMPP_Error::ERROR_INTERNAL_SERVER_ERROR)); return true; } if ($Entity->setData ($p->getOriginator (), $Tag->getSubtags ())) return true; return array ( $Tag, new tiggerXMPP_Error (tiggerXMPP_Error::ERROR_NOT_ACCEPTABLE, "Entity refused to store data"), ); } // Check if there is a registration stored for the requesting user if ($Entity->hasData ($p->getOriginator ())) { // Set registered state $Registered = new phpEvents_Socket_Stream_XML_Tag ('registered', $Resp); // Retrive data for this JID $Fields = $Entity->getData ($p->getOriginator ()); $Mode = tiggerXMPP_XEP_0077_Field::MODE_FILLED; // Just hand out our registration-form } else { // Add instructing text $Instructions = new phpEvents_Socket_Stream_XML_Tag ('instructions', $Resp, $Entity->getInstructions ($p->getOriginator ())); // Read fields from entity $Fields = $Entity->getFields ($p->getOriginator ()); $Mode = tiggerXMPP_XEP_0077_Field::MODE_DEFINITION; } // Handle invalid field-definition if (count ($Fields) < 1) return array ( $Resp, new tiggerXMPP_Error (tiggerXMPP_Error::ERROR_SERVICE_UNAVAILABLE, "Extension misconfigured (no fields found)"), ); // Add fields to response foreach ($Fields as $Field) $Field->addToResponse ($Resp, $Mode); return $Resp; } // }}} // {{{ getEntity /** * Search an entity matching a given JID * * @param string $JID * @param stirng $For * * @access protected * @return array */ protected function getEntity ($ToJID, $For) { // Ugly method of finding best JID; $Match1 = null; $Match2 = null; if (!is_object ($P = $this->getParent ())) return false; // Just to save CPU-Time $UserHost = $P->getJID (true, true, false, $ToJID); $User = $P->getJID (true, false, false, $ToJID); $Host = $P->getJID (false, true, false, $ToJID); foreach ($this->Entities as $Handle) { // Get JID from entity $JID = $Handle->getJID (); // Check for exact match if ($JID == $ToJID) return $Handle; // Check wheter Username and Host of JID matches elseif ($P->getJID (true, true, false, $JID) == $UserHost) $Match1 = $Handle; // Check wheter user or host matches elseif (($P->getJID (true, false, false, $JID) == $User) || ($P->getJID (false, true, false, $JID) == $Host)) $Match2 = $JID; } // Check for user/host-match if (is_object ($Match1)) return $Match1; // Check for user or host-match if (is_object ($Match2)) return $Match2; // Don't do anything return false; } // }}} // {{{ registerEntity /** * Register a new Registration-Entity * * @param string JID * @param string $Instructions * @param array $Fields (optional) * @param string $Class (optional) * * @access pubic * @return object */ public function registerEntity ($JID, $Instructions, $Fields = array (), $Class = null) { // Check validity of class if (($Class === null) || !class_exists ($Class) || !is_subclass_of ($Class, "tiggerXMPP_XEP_0077_Entity")) $Class = 'tiggerXMPP_XEP_0077_Entity'; // Create a new Entity-Handle $Handle = new $Class ($JID, $this); // Set properties of handle $Handle->setInstructions ($Instructions); $Handle->setFields ($Fields); // Append handle to local storage return $this->Entities [$Handle->getJID ()] = $Handle; } // }}} } /** * XEP 0077 Entity * --------------- * Container for registration-informations assigned to a given JID * * @class XEP_0077_Entity * @package tiggerXMPP * @revision 01 * @author Bernd Holzmueller */ class tiggerXMPP_XEP_0077_Entity { // Internally stored informations private $JID = ""; private $Instructions = ""; private $Fields = array (); private $Callback = null; protected $Parent = null; // {{{ __construct /** * Create a new Entity * * @param string $JID Assign this JID * * @access friendly * @return void */ function __construct ($JID, $Parent = null) { // Try to store assigned JID $this->setJID ($JID); $this->Parent = $Parent; } // }}} // {{{ getJID /** * Retrive currently assigned JID * * @access public * @return string */ public function getJID () { return $this->JID; } // }}} // {{{ getInstructions /** * Retrive stored Instructions (maybe for a given user) * * @param string $ForJID (optional) * * @access public * @return string */ public function getInstructions ($ForJID = "") { return $this->Instructions; } // }}} // {{{ getFields /** * Retrive Fields of this entity * * @param string $ForJID (optional) * * @access public * @return array */ public function getFields ($ForJID = "") { return $this->Fields; } // }}} // {{{ setJID /** * Set JID assigned to this entity * * @param string $JID * * @access public * @return void */ public function setJID ($JID) { $this->JID = $JID; } // }}} // {{{ setInstructions /** * Set Instructions assigned to this entity * * @param stirng $Text * * @access public * @return void */ public function setInstructions ($Text) { $this->Instructions = $Text; } // }}} // {{{ setFields /** * Store Registration-Fields for this entity * * @param array $Fields * * @access public * @return void */ public function setFields ($Fields) { $this->Fields = $Fields; } // }}} // {{{ hasData /** * Check if there is registration-data stored for a JID * * @param string $JID * * @access public * @return bool */ public function hasData ($ForJID) { $Data = $this->getData ($ForJID); return (is_array ($Data) && (count ($Data) > 0)); } // }}} // {{{ getData /** * Retrive Data (represented by Field-Array) for a JID * * @param string $JID * * @access public * @return array * @todo Implement me */ public function getData ($ForJID) { return false; } // }}} // {{{ setData /** * Store data for a JID * * @param string $ForJID * @param array $Tags * * @access public * @return bool */ public function setData ($ForJID, $Tags) { if (!is_callable ($this->Callback)) return true; return call_user_func ($this->Callback, __FUNCTION__, $ForJID, $Tags); } // }}} // {{{ removeData /** * Remove all stored data for a JID * * @param string $JID * * @access public * @return bool */ public function removeData ($ForJID) { return false; } // }}} // {{{ setCallback /** * Register a generic callback-function for this entity * * @param callback $CB * * @access public * @return void **/ public function setCallback ($CB) { $this->Callback = $CB; } // }}} } /** * XEP 0077 Field * -------------- * Single/Simple Field-representation * * @class XEP_0077_Field * @package tiggerXMPP * @revision 01 * @author Bernd Holzmueller */ class tiggerXMPP_XEP_0077_Field { const NAME_USERNAME = "username"; const NAME_NICKNAME = "nick"; const NAME_PASSWORD = "password"; const NAME_FULLNAME = "name"; const NAME_FORENAME = "first"; const NAME_SURNAME = "last"; const NAME_EMAIL = "email"; const NAME_ADDRESS = "address"; const NAME_CITY = "city"; const NAME_STATE = "state"; const NAME_ZIP = "zip"; const NAME_PHONE = "phone"; const NAME_URL = "url"; const NAME_DATE = "date"; const NAME_OPASSWORD = "old_password"; const MODE_DEFINITION = "def"; const MODE_FILLED = "fill"; private $Name = ""; private $Value = ""; // {{{ __construct /** * create a new field * * @param enum $Name * * @access friendly * @return void */ function __construct ($Name = "") { $this->setName ($Name); } // }}} // {{{ setName /** * Set the Name/Type of this field * * @param enum $Name * * @access public * @return bool */ public function setName ($Name) { // Convert to lower case (just to be sure) $Name = strtolower ($Name); // Validate the name switch ($Name) { case self::NAME_USERNAME: case self::NAME_NICKNAME: case self::NAME_PASSWORD: case self::NAME_FULLNAME: case self::NAME_FORENAME: case self::NAME_SURNAME: case self::NAME_EMAIL: case self::NAME_ADDRESS: case self::NAME_CITY: case self::NAME_STATE: case self::NAME_ZIP: case self::NAME_PHONE: case self::NAME_URL: case self::NAME_DATE: case self::NAME_OPASSWORD: $this->Name = $Name; return true; } // Return error-case return false; } // }}} // {{{ addToResponse /** * Append our field to a response-packet * * @param object $Resp * @param enum $Mode (optional) * * @access public * @return void */ public function addToResponse ($Resp, $Mode = self::MODE_DEFINITION) { // Check if our name is empty if (strlen ($this->Name) == 0) return false; // Create XML-Packet $Tag = new phpEvents_Socket_Stream_XML_Tag ($this->Name, $Resp); // Fill the packet with data switch ($Mode) { case self::MODE_DEFINITION: break; case self::MODE_FILLED: $Tag->setValue ($this->Value); break; } return true; } // }}} } ?>