RegAPI for PHP5
Hey y'all. I rewrote the RegAPI libraries to work with PHP5 without the compatibility library. It's not a drop-in replacement, but should be pretty easy to use. The documentation is in the code, and see the index.php example for some usage. I tried to make it fairly robust, but I pretty much suck at PHP, so please lemme know if y'all find anything untoward. >_>
It consists of two files...
A configuration file, config.php: <php> <?php
// Get your CAPS from https://secure-web2.secondlife.com/developers/third_party_reg/#what_are_cap // and fill them in here.
define('URI_CREATE_USER', 'FILL_ME_IN_WITH_YOUR_CAPS_URL'); define('URI_GET_LAST_NAMES', 'FILL_ME_IN_WITH_YOUR_CAPS_URL'); define('URI_CHECK_NAME', 'FILL_ME_IN_WITH_YOUR_CAPS_URL'); define('URI_GET_ERROR_CODES', 'FILL_ME_IN_WITH_YOUR_CAPS_URL');
?> </php>
And the library, RegAPI.php: <php> <?php
//////////////////////////////////////////////////////////////////////////////// // // Hacked up by Liandra Ceawlin, Version 1, Tue Jul 21 11:53:34 EDT 2009 // // I'm not really much of a PHP coder, so if you find any bugs or see things // in this code that are utterly atrocious, please IM me in-world or email // support@ceawlin.com. Good luck with it! // //////////////////////////////////////////////////////////////////////////////// // // Yadda yadda so I don't get sued when it breaks. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // ////////////////////////////////////////////////////////////////////////////////
require( 'config.php' );
// Check to see if <username> (first name) and <last_name_id> (an integer code
// as returned from regapiGetLastNames below) are a valid pair to create a
// new account with. If they are, the function returns true. If not, it
// returns false. It also returns false if there was a query error, but you
// can check to see if regapiGetCurrentErrors() returns anything to see if
// there was an error or not.
function regapiCheckName( $username, $last_name_id )
{
// Yea, maybe this is kinda lame, but seriously easier than constructing
// a DOM object and ->saveXML()ing. >_>
// Build the XML. $str = "<llsd><map><key>username</key><string>".$username. "</string><key>last_name_id</key><integer>".$last_name_id. "</integer></map></llsd>";
// POST it and get a DOM object back. $dom = regapiHttpPostDOM( URI_CHECK_NAME, $str ); if( regapiProcessDOMForErrors($dom) ) return false;
// Parse out the return value and return boolean. $nodelist = $dom->getElementsByTagName( "boolean" ); if( $nodelist != NULL && $nodelist->length ) return $nodelist->item(0)->textContent == 'true'; regapiSetUnknownError(); return false; }
// Create a new user, using the required fields <username> (first name), // <last_name_id> (an integer code as returned from regapiGetLastNames below), // <email>, and <password>. <options> is an associative array of optional // parameters (see https://wiki.secondlife.com/wiki/Registration_API_Reference#Parameters_2), // where the key is the option name and the value is the, uh, value of that // option. The function returns the agent's UUID. // Returns NULL on error, then you can access the error codes with // regapiGetCurrentErrors(). function regapiCreateUser( $username, $last_name_id, $email, $password, $dob, $options ) { // A mapping of options and their types, used in verification and XML // creation. $valid_options = array( "username" => "string", "last_name_id" => "integer", "email" => "string", "password" => "string", "dob" => "string", "limited_to_estate" => "integer", "start_region_name" => "string", "start_local_x" => "float", "start_local_y" => "float", "start_local_z" => "float", "start_look_at_x" => "float", "start_look_at_y" => "float" );
// Remove any invalid optional, uh, options.
if( !is_array($options) )
$options = array();
foreach( $options as $opt => $val )
{
if( !array_key_exists($opt,$valid_options) )
{
?>Warning: Unrecognized option <?php echo $opt; ?> removed from regapiCreateUser list.
<?php
unset( $options[$opt] );
}
}
// Add the required options to the option list. $options["username"] = $username; $options["last_name_id"] = $last_name_id; $options["email"] = $email; $options["password"] = $password; $options["dob"] = $dob;
// Build the XML. // Yea, maybe this is kinda lame, but seriously easier than constructing // a DOM object and ->saveXML()ing. >_> $str = "<llsd><map>"; foreach( $options as $opt => $val ) { $type = $valid_options[$opt]; $str = $str."<key>".$opt."</key><".$type.">".$val."</".$type.">"; } $str = $str."</map></llsd>";
// POST it and get a DOM object back. $dom = regapiHttpPostDOM( URI_CREATE_USER, $str ); if( regapiProcessDOMForErrors($dom) ) return NULL;
// Parse out the return value and return agent uuid string. $nodelist = $dom->getElementsByTagName( "string" ); if( $nodelist != NULL && $nodelist->length ) return $nodelist->item(0)->textContent; regapiSetUnknownError(); return NULL; }
// This function returns an associative 2d array of error codes, with the first // key being the integer code, and the second key being either "desc" for the // short error description, or "message" for the verbose error message. See // the example index.php file for example of usage. // Returns NULL on error, then you can access the error codes with // regapiGetCurrentErrors(), although you won't be able to look up the error // code text, lol. function regapiGetErrorCodes() { global $regAPI_errorcodecache;
// We cache the first query for error codes so that subsequent calls to not // cause more queries. if( $regAPI_errorcodecache != NULL ) return $regAPI_errorcodecache;
$errors = array();
$dom = regapiHttpGetDOM( URI_GET_ERROR_CODES ); if( regapiProcessDOMForErrors($dom) ) return NULL;
// Get <llsd>. $node = $dom->documentElement; // Move to enclosing <array>. if( $node != NULL ) $node = $node->firstChild; // move to first sub-<array>. if( $node != NULL && $node->nodeName == "array" ) $node = $node->firstChild; else { regapiSetUnknownError(); return NULL; } // Move through sub-<array>s and build the error list. while( $node != NULL && $node->nodeName == "array" ) { // Build the array from the sub-<array> children. $child = $node->firstChild; // Get code. if( $child != NULL ) { $code = $child->textContent; $child = $child->nextSibling; } else { regapiSetUnknownError(); return NULL; } // Get short text. if( $child != NULL ) { $desc = $child->textContent; $child = $child->nextSibling; } else { regapiSetUnknownError(); return NULL; } // Get verbose error message. if( $child != NULL ) $message = $child->textContent; else { regapiSetUnknownError(); return NULL; } $errors[$code]["desc"] = $desc; $errors[$code]["message"] = $message; $node = $node->nextSibling; }
$regAPI_errorcodecache = $errors; return $errors; }
// Returns an associative array of available last names, in alphabetical // order, where key is the name string and value is the numeric id // corresponding to that name. If you pass <num> of 1 or more, it will // randomly choose that many names from the list of available names and return // them. If <num> is negative, it will return all available names. If you // pass something into the <username> parameter, only last names that are // available with the specified username (first name) will be returned. // WARNING: Passing something into <username> is currently very slow, and gets // even slower the larger <num> is... // Returns NULL on error, then you can access the error codes with // regapiGetCurrentErrors(). function regapiGetLastNames( $username, $num ) { $dom = regapiHttpGetDOM( URI_GET_LAST_NAMES ); if( regapiProcessDOMForErrors($dom) ) return NULL;
$names = array();
$nodelist = $dom->getElementsByTagName( "map" ); if( $nodelist != NULL && $nodelist->length ) { // This will only return the names in the first map, but there is // only one map anyway. $node = $nodelist->item(0)->firstChild; while( $node != NULL ) { $k = $node->textContent; $node = $node->nextSibling; if( $node == NULL_KEY ) { // This should never happen. return rval; } $s = $node->textContent; $node = $node->nextSibling; $names[$s] = $k; } } $rval = array(); while( ($num>=0&&count($rval)<$num) || ($num<0&&count($names)) ) { if( $num < 0 ) { $rval = $names; $names = array(); } else { $entries = array_rand( $names, $num-count($rval) ); foreach( $entries as $k ) { if( $username=="" || $username==NULL || regapiCheckName($username,$names[$k]) ) $rval[$k] = $names[$k]; unset( $names[$k] ); } } if( !count($names) ) break; } ksort( $rval ); return $rval; }
function regapiGetCurrentErrors() { global $regAPI_lasterrors; if( !count($regAPI_lasterrors) ) return NULL; return $regAPI_lasterrors; }
//////////////////////////////////////////////////////////////////////////////// // Internal things below here. You probably don't need to worry about them. ////////////////////////////////////////////////////////////////////////////////
$regAPI_lasterrors = array(); $regAPI_errorcodecache = NULL;
// Utility function for random errors not reported via XML returns from queries. // We just set a 0 (undefined) error. function regapiSetUnknownError() { global $regAPI_lasterrors; $regAPI_lasterrors = array(); $regAPI_lasterrors[] = 0; }
// This function re-sets the lasterrors array, and returns true if the XML // looks like an error message, false otherwise. function regapiProcessDOMForErrors( $dom ) { global $regAPI_lasterrors;
$regAPI_lasterrors = array();
if( $dom == NULL ) return true; // Get <llsd>. $node = $dom->documentElement; // Move to enclosing <array>. if( $node != NULL ) $node = $node->firstChild; // Move to first <integer> node, if we're really in an <array>. if( $node != NULL && $node->nodeName == "array" ) $node = $node->firstChild; else return false; $startnode = $node; // Scan over child nodes to see if they are all integers. while( $node != NULL ) { if( $node->nodeName != "integer" ) return false; $node = $node->nextSibling; } // If we get here, then the array is all integers, and it looks like an // error message. So we add the error codes to the array. $node = $startnode; while( $node != NULL ) { $regAPI_lasterrors[] = $node->textContent; $node = $node->nextSibling; } return true; }
// Send a GET request to <url> and return a DOM object, or NULL on error. function regapiHttpGetDOM( $url ) { $ch = curl_init( $url ); curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
$doc = curl_exec( $ch ); $status = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); curl_close( $ch ); if( $status >= 400 ) return NULL;
$dom = new DOMDocument(); if( !(@$dom->loadXML($doc)) ) return NULL; return $dom; }
// Send a POST request to <url> and return a DOM object, or NULL on error. function regapiHttpPostDOM( $url, $str ) { $ch = curl_init( $url );
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FALSE );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE ); curl_setopt( $ch, CURLOPT_POST, TRUE ); curl_setopt( $ch, CURLOPT_FAILONERROR, 1 ); curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 ); curl_setopt( $ch, CURLOPT_HTTPHEADER, Array("Content-Type: application/xml") ); curl_setopt( $ch, CURLOPT_POSTFIELDS, $str ); $doc = curl_exec( $ch ); $status = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); curl_close( $ch ); if( $status >= 400 ) return NULL;
$dom = new DOMDocument(); if( !(@$dom->loadXML($doc)) ) return NULL; return $dom; }
?> </php>
And here's a modified version of the example index.php that uses the new library: <php> <?php
require( 'RegAPI.php' );
function get_months() {
$months = array(); for( $i=1; $i<=12; $i++ ) { $key = date( 'n', mktime(0, 0, 0, $i, 1, 2000) ); $value = date( 'M.', mktime(0, 0, 0, $i, 1, 2000) ); $months[sprintf("%02d", $key)] = $value; } return $months;
}
function get_years() {
$today = getdate(); $max_year = $today['year'] - 90; $min_year = $today['year'] - 13; $years = array(); for( $i=$min_year; $i>=$max_year; $i-- ) $years[$i] = $i; return $years;
}
function get_days() {
$days = array(); for( $i=1; $i<=31; $i++ ) $days[sprintf("%02d",$i)] = sprintf("%02d",$i); return $days;
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') { $username = $_POST['username']; $last_name_id = (int)$_POST['last_name_id']; $email = $_POST['email']; $password = $_POST['password']; $dob = $_POST['dob_year'].'-'.$_POST['dob_month'].'-'.$_POST['dob_day'];
if( regapiCheckName($username,$last_name_id) )
{
$uuid = regapiCreateUser( $username, $last_name_id, $email, $password, $dob, NULL );
if( $uuid )
{
echo "Created ".$uuid."!
";
}
else
{
if( $errors=regapiGetCurrentErrors() )
{
$errorcodes = regapiGetErrorCodes();
foreach( $errors as $e )
{
if( array_key_exists($e,$errorcodes) )
echo $errorcodes[$e]["message"]."
";
else
echo "Unknown error.
";
}
}
}
}
else
{
echo "That name is not available.
";
}
}
?>
Create Second Life Account
<form action="<?php print $_SERVER['PHP_SELF']; ?>" method="post">
First name: | <input type="text" name="username" size="25" maxlength="31" value="" /> |
Last name: |
<select name="last_name_id"> <?php $last_names = regapiGetLastNames( NULL, -1 ); foreach( $last_names as $name => $last_name_id ) print '<option value="'.$last_name_id.'">'.$name.'</option>'; ?> </select> |
Password: | <input type="password" name="password" size="20" value="" /> |
Email: | <input type="text" name="email" size="35" value="" /> |
Date of brith: |
<select name="dob_day"> <?php $days = get_days(); foreach ($days as $key => $value) { print '<option value="'.$key.'" '.$selected.'>'.$value.'</option>'; } ?> </select> <select name="dob_month"> <?php $months = get_months(); foreach ($months as $key => $value) { print '<option value="'.$key.'" '.$selected.'>'.$value.'</option>'; } ?> </select> <select name="dob_year"> <?php $years = get_years(); foreach ($years as $key => $value) { print '<option value="'.$key.'" '.$selected.'>'.$value.'</option>'; } ?> </select> |
<input type="submit" value="Create SL Account" /> |
</form> </php>