From 34bac01109a886275e8c0a440e4390d64ff8bccf Mon Sep 17 00:00:00 2001
From: sfb
Date: Thu, 22 Mar 2012 15:48:13 -0500
Subject: [PATCH] Fixed: #1378 Imported nimetu's WebIG reference
implementation.
---
.../tools/server/www/webig/app_hello.php | 29 ++
code/ryzom/tools/server/www/webig/index.php | 133 ++++++
.../tools/server/www/webig/lib/config.php | 23 +
.../tools/server/www/webig/lib/functions.php | 394 ++++++++++++++++++
.../tools/server/www/webig/lib/pdr_util.php | 124 ++++++
.../www/webig/lib/pdr_util_character.php | 46 ++
.../server/www/webig/lib/pdr_util_guild.php | 47 +++
7 files changed, 796 insertions(+)
create mode 100644 code/ryzom/tools/server/www/webig/app_hello.php
create mode 100644 code/ryzom/tools/server/www/webig/index.php
create mode 100644 code/ryzom/tools/server/www/webig/lib/config.php
create mode 100644 code/ryzom/tools/server/www/webig/lib/functions.php
create mode 100644 code/ryzom/tools/server/www/webig/lib/pdr_util.php
create mode 100644 code/ryzom/tools/server/www/webig/lib/pdr_util_character.php
create mode 100644 code/ryzom/tools/server/www/webig/lib/pdr_util_guild.php
diff --git a/code/ryzom/tools/server/www/webig/app_hello.php b/code/ryzom/tools/server/www/webig/app_hello.php
new file mode 100644
index 000000000..0589d9ec2
--- /dev/null
+++ b/code/ryzom/tools/server/www/webig/app_hello.php
@@ -0,0 +1,29 @@
+
+
+
+ App Hello World!
+
+
+ APP Hello World!
+
+ index=(!isWEBIG ? '| logout' : '')?>
+
+ Character
+
+
+
diff --git a/code/ryzom/tools/server/www/webig/index.php b/code/ryzom/tools/server/www/webig/index.php
new file mode 100644
index 000000000..57ddd7b55
--- /dev/null
+++ b/code/ryzom/tools/server/www/webig/index.php
@@ -0,0 +1,133 @@
+login error
';
+ }
+ echo '
+
+
+ WebIG - Login
+
+ Login
+
+
+ ';
+ exit;
+}
+
+// if this was login request from app, then redirect back there
+$redirect = is($_GET['redirect'], '');
+if(!empty($redirect)){
+ header('Location: '.$redirect);
+ exit;
+}
+
+// check user privileges
+$is_admin = webig_is_admin($user['cid']>>4);
+
+// get more info about character - race, civlization, cult, guild, etc
+$character = webig_load_character($user['cid']);
+
+// user is verified
+?>
+
+
+ App Index
+
+
+ Hello "=h($user['name'])?>"!
+
+ index | Hello APP=(!isWEBIG ? '| logout' : '')?>
+
+User info';
+echo 'USER:'.dump_array($user);
+echo 'CHARACTER:'.dump_array($character);
+
+ $__end = microtime(true);
+ echo "\n---\npage created ".sprintf("%.5fsec", $__end - $__start).'
';
+
+?>
+
+
+
+
+
+ ';
+ $c=0;
+ foreach($array as $k => $v){
+ if(is_array($v)){
+ $v = dump_array($v);
+ }else{
+ // make value safe for html
+ $v = h($v);
+ }
+ echo '
+
+ '.h($k).' | '.$v.' |
+
+ ';
+ $c++;
+ }
+ echo '
+
+ |
+
+ ';
+
+ return ob_get_clean();
+}
+
+function display_teleport_list(){
+ $places = array(
+ 'Ranger Camp' => array(10314,-11734),
+ 'Shining Lake' => array(9056, -10822),
+ );
+?>
+ Teleport destinations
+
+_pdo = new PDO('mysql:host='.$GLOBALS['DBHost'].';dbname='.$GLOBALS['DBName'].';charset=utf-8', $GLOBALS['DBUserName'], $GLOBALS['DBPassword'], array(PDO::MYSQL_ATTR_INIT_COMMAND => 'set names utf8'));
+ $this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ }
+
+ public function getInstance(){
+ static $instance = null;
+ if($instance === null){
+ $instance = new DB();
+ }
+ return $instance;
+ }
+
+ /**
+ * @param string $sql
+ * @param array $params (optional)
+ * @return PDOStatement
+ */
+ function query($sql, $params=array()){
+ if(empty($params)){
+ $stmt = $this->_pdo->query($sql);
+ }else{
+ $stmt = $this->_pdo->prepare($sql);
+ $stmt->execute($params);
+ }
+ return $stmt;
+ }
+}
+
+/**
+ * Verify and log-in user
+ *
+ * @return mixed user info array or boolean FALSE when user was not verified
+ */
+function app_authenticate(){
+ // mask possible double session_start() warning
+ @session_start();
+
+ if(isWEBIG){
+ // ingame login
+
+ // gather user from $_GET or $_POST variables
+ $user = webig_user();
+
+ // verify it against database
+ $user = webig_auth($user);
+ }else{
+ // outgame login
+
+ if(isset($_POST['login'])){
+ // login request
+ $shardid = is($_POST['login']['shardid'], '');
+ $name = is($_POST['login']['name'], '');
+ $passwd = is($_POST['login']['passwd'], '');
+
+ // verify character and password against database and populate $_GET with user info
+ $user = login_auth($shardid, $name, $passwd);
+ $_SESSION['login']['error'] = ($user === false);
+ }elseif(isset($_GET['logout'])){
+ // logout request
+ unset($_SESSION['user']);
+ unset($_SESSION['authkey']);
+
+ // redirect to self without URL parameters
+ header('Location: '.$_SERVER['PHP_SELF']);
+ exit;
+ }else{
+ // continue session
+ $user = is($_SESSION['user'], false);
+
+ // verify user in session against database (e.g. user might be deleted)
+ $user = load_user($user['shardid'], null, $user['cid']);
+ }
+ }
+
+ // auth failed?
+ if(empty($user)){
+ return false;
+ }
+
+ // remove values we do not need to keep in session
+ unset($user['password']);
+ unset($user['cookie']);
+
+ // return user info array on success
+ $_SESSION['user'] = $user;
+ return $user;
+}
+
+// get user info that WebIG sends us
+function webig_user(){
+ $user = array();
+
+ // shard id (302)
+ $user['shardid'] = ryzom_get_param('shardid');
+
+ // character name (User)
+ $user['name'] = ryzom_get_param('name');
+
+ // character id (16), user id is calculated as 'uid = cid >> 4';
+ $user['cid'] = ryzom_get_param('cid');
+
+ // language
+ $user['lang'] = ryzom_get_param('lang');
+
+ $user['authkey'] = ryzom_get_param('authkey');
+
+ return $user;
+}
+
+/**
+ * Verify character using info from ig browser
+ *
+ * @param array $user
+ * @return bool return user info array on success and FALSE on error
+ */
+function webig_auth($user){
+ // find user by shard and character id (name might be temporarily changed in game)
+ $result = load_user($user['shardid'], null, $user['cid']);
+ if(empty($result)){
+ // should not happen, but user was not found
+ return false;
+ }
+
+ // Create auth key by using cookie from DB and user info from user
+ $authkey = webig_create_authkey($user, $result['cookie']);
+ if($user['authkey'] !== $authkey){
+ // something is out of sync - either user info or cookie
+ return false;
+ }
+
+ // return result from DB
+ return $result;
+}
+
+/**
+ * Verify character
+ *
+ * @param int $shardid character shard id
+ * @param string $name character name
+ * @param string $passwd plain text password
+ * @return mixed return user info array on success or boolean false on error
+ */
+function login_auth($shardid, $name, $passwd){
+ // get character from db
+ $user = load_user($shardid, $name);
+ if(empty($user)){
+ // user was not found
+ return false;
+ }
+
+ $passwd = crypt($passwd, substr($user['password'], 0, 2));
+ if($passwd !== $user['password']){
+ // password failed
+ return false;
+ }
+
+ return $user;
+}
+
+/**
+ * Fetch user info from db
+ *
+ * If name is NULL, then $cid is used
+ *
+ * @param int $shardid
+ * @param string $name
+ * @param int $cid
+ * @return array
+ */
+function load_user($shardid, $name, $cid = null){
+ // `nel`.`user` has password
+ // `ring_open`.`ring_users` has cookie
+ // `ring_open`.`characters` has char_id, char_name, home_mainland_session_id(==shardid)
+
+ $sql = 'SELECT c.`char_id` cid, c.`char_name` name, c.`home_mainland_session_id` shardid, n.`password`, u.`cookie`
+ FROM `ring_open`.`characters` c
+ JOIN `ring_open`.`ring_users` u on u.`user_id` = c.`user_id`
+ JOIN `nel`.`user` n on n.`uid` = c.`user_id`
+ WHERE c.`home_mainland_session_id` = :shardid';
+ $params = array('shardid' => $shardid);
+ if($name !== null){
+ $sql .= ' AND c.`char_name` = :name';
+ $params['name'] = $name;
+ }elseif($cid !== null){
+ $sql .= ' AND c.`char_id` = :cid';
+ $params['cid'] = $cid;
+ }else{
+ // $name or $cid both empty
+ return false;
+ }
+
+ $result = DB::getInstance()->query($sql, $params)->fetch(PDO::FETCH_ASSOC);
+ return $result;
+}
+
+/**
+ * Verify user info that ig browser sent us using cookie from database
+ *
+ * @param array $user user info array
+ * @param string $cookie User login cookie from database
+ * @return string md5 hash
+ */
+function webig_create_authkey($user, $cookie){
+ return md5($user['shardid'].$user['name'].$user['cid'].'\''.$cookie.'\'');
+}
+
+/**
+ * Return user privileges from DB
+ *
+ * @param int $uid user id (uid = cid >> 4)
+ * @return mixed array of user privileges or boolean FALSE when user was not found
+ */
+function webig_get_privileges($uid){
+ $sql = 'select `privilege` from `nel`.`user` where `uid` = :id';
+ $params = array('id' => $uid);
+
+ $result = DB::getInstance()->query($sql, $params)->fetchColumn(0);
+
+ if($result !== false){
+ $result = explode(':', $result);
+ $ret = array();
+ foreach($result as $k=>$v){
+ if($v != ''){
+ $ret[]=$v;
+ }
+ }
+ $result = $ret;
+ }
+
+ return $result;
+}
+
+/**
+ * Test user privileges
+ *
+ * @param int $uid user id
+ * @param array $priv array of privileges, like array('DEV', 'GM')
+ * @return bool
+ */
+function webig_has_privileges($uid, $priv){
+ $userpriv = webig_get_privileges($uid);
+ $result = array_intersect($priv, $userpriv);
+ return !empty($result);
+}
+
+/**
+ * Test user privileges against (DEV, SGM, GM)
+ *
+ * @param int $uid user id
+ * @return bool
+ */
+function webig_is_admin($uid){
+ // entities_game_service/player_manager/player_manager.cpp defines order
+ // DEV > SGM > EM > GM > EG > VG > SG > G > OBSERVER > PR
+ return webig_has_privileges($uid, array('DEV', 'SGM', 'EM', 'GM'));
+}
+
+/**
+ * Load character from shard save binary file
+ *
+ * @param int $cid
+ * @return mixed array with character info or boolean FALSE on error
+ */
+function webig_load_character($cid){
+ $pdr = CharacterPdr::createDefault();
+ $char = $pdr->load($cid);
+ if(empty($char)){
+ return false;
+ }
+
+ $result = array(
+ 'id' => (int) $cid,
+ 'name' => (string) $char->EntityBase->_Name['value'],
+ 'title' => (string) $char->_Title['value'],
+ 'race' => (string) $char->EntityBase->_Race['value'],
+ 'gender' => (int) $char->EntityBase->_Gender['value'] == '0' ? 'male' : 'female',
+ 'cult' => (string) $char->DeclaredCult['value'],
+ 'civ' => (string) $char->DeclaredCiv['value'],
+ 'guild' => false,
+ );
+
+ $guild_id = (int) $char->_GuildId['value'];
+ if($guild_id>0){
+ // if char is in guild, then also get guild info
+ $result['guild'] = webig_load_guild($guild_id);
+
+ // get guild rank (also from guild file)
+ $result['guild_membership'] = webig_load_guild_membership($guild_id, $cid);
+ }
+ unset($char);
+
+ return $result;
+}
+
+/**
+ * Load basic guild info (name, description, motd, culv, civ)
+ *
+ * @param int $guild_id
+ * @return mixed array with guild info or boolean FALSE on error
+ */
+function webig_load_guild($guild_id){
+ $pdr = GuildPdr::createDefault();
+ $guild = $pdr->load($guild_id);
+ if(empty($guild)){
+ return false;
+ }
+
+ $result = array(
+ 'id' => (int) $guild_id,
+ 'icon' => (string) $guild->Icon['value'],
+ 'name' => (string) $guild->_Name['value'],
+ 'description' => (string) $guild->_Description['value'],
+ 'motd' => (string) $guild->_MessageOfTheDay['value'],
+ 'cult' => (string) $guild->DeclaredCult['value'],
+ 'civ' => (string) $guild->DeclaredCiv['value'],
+ );
+ unset($guild);
+
+ return $result;
+}
+
+/**
+ * Load guild member info
+ *
+ * @param int $guild_id
+ * @param int $char_id
+ * @return mixed array with guild member info or boolean FALSE if guild or character not found
+ */
+function webig_load_guild_membership($guild_id, $char_id){
+ $pdr = GuildPdr::createDefault();
+ $guild = $pdr->load($guild_id);
+ if(empty($guild)){
+ return false;
+ }
+
+ $result = false;
+
+ // test for 'id' and type (CHAR == 0), ignore creator (should be 0x00) and dynamic
+ // 0x0000000013:00:00:87
+ $eid = sprintf('0x%010x:00:', $char_id);
+ $i = 0;
+ while(isset($guild->Members->__Key__[$i])){
+ $key = $guild->Members->__Key__[$i];
+ $pos = strpos((string)$key['value'], $eid);
+ if($pos === 1){
+ $val = $guild->Members->__Val__[$i];
+ $result = array(
+ 'grade' => (string) $val->Members->Grade['value'],
+ 'joined' => (int) $val->Members->EnterTime['value'],
+ );
+ break;
+ }
+ $i++;
+ }
+ unset($guild);
+
+ return $result;
+}
+
+// shortcut for 'isset() ? .. : ..'
+function is(&$var, $default = null){
+ return isset($var) ? $var : $default;
+}
+
+// escape string so it's safe for HTML
+function h($str){
+ return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
+}
+
+// return $_GET[var] or $_POST[var] or $default
+function ryzom_get_param($var, $default=''){
+ return is($_GET[$var], is($_POST[$var], $default));
+}
\ No newline at end of file
diff --git a/code/ryzom/tools/server/www/webig/lib/pdr_util.php b/code/ryzom/tools/server/www/webig/lib/pdr_util.php
new file mode 100644
index 000000000..1e28124a8
--- /dev/null
+++ b/code/ryzom/tools/server/www/webig/lib/pdr_util.php
@@ -0,0 +1,124 @@
+_shard_save = $shard_save;
+ $this->_sheetid_dir = $sheetid_dir;
+
+ $this->_unpack_dir = $unpack;
+ }
+
+ function setShardSaveDirectory($dir){
+ $this->_shard_save = $dir;
+ }
+ function getShardSaveDirectory(){
+ return $this->_shard_save;
+ }
+
+ function setSheetIdDirectory($dir){
+ $this->_sheetid_dir = $dir;
+ }
+ function getSheetIdDirectory(){
+ return $this->_sheetid_dir;
+ }
+
+ function setUnpackDirectory($dir){
+ $this->_unpack_dir = $dir;
+ }
+ function getUnpackDirectory(){
+ return $this->_unpack_dir;
+ }
+
+ /**
+ * Extract $pdr file to $xml file
+ *
+ * @param string $pdr
+ * @param string $xml
+ * @return bool
+ */
+ function extract($pdr, $xml){
+ if(!file_exists($pdr)){
+ return false;
+ }
+
+ $pdr_mtime = filemtime($pdr);
+ if(file_exists($xml)){
+ $xml_mtime = filemtime($xml);
+ }else{
+ $xml_mtime = 0;
+ }
+
+ $diff = $pdr_mtime - $xml_mtime;
+ if($diff > self::CACHE_MIN_TIME){
+ // remove expired xml file
+ @unlink($xml);
+
+ // change working directory to unpack directory to keep pdr_util log file in one place
+ $pwd = getcwd();
+ chdir($this->_unpack_dir);
+
+ // run pdr_util
+ $cmd = sprintf(' -s%s -x -o%s %s', $this->_sheetid_dir, $xml, $pdr);
+ exec(CMD_PDR_UTIL.' '.$cmd);
+
+ // change working directory back what it was before
+ chdir($pwd);
+ }
+
+ // if pdr_util failed, then there is no xml file
+ return file_exists($xml);
+ }
+
+ /**
+ * @param string $fname
+ * @return string ShardSaveDirectory + fname
+ */
+ function getSaveFileName($fname){
+ return $this->getShardSaveDirectory().'/'.$fname;
+ }
+
+ /**
+ * @param string $fname
+ * return string TempDirectory + $fname
+ */
+ function getXmlFileName($fname){
+ return $this->getUnpackDirectory().'/'.$fname;
+ }
+
+}
+
diff --git a/code/ryzom/tools/server/www/webig/lib/pdr_util_character.php b/code/ryzom/tools/server/www/webig/lib/pdr_util_character.php
new file mode 100644
index 000000000..7e2d689ce
--- /dev/null
+++ b/code/ryzom/tools/server/www/webig/lib/pdr_util_character.php
@@ -0,0 +1,46 @@
+getSaveFileName($char_id >> 4, $char_id & 0x0F);
+ $xml_file = $this->getXmlFileName($char_id);
+
+ if($this->extract($char_save, $xml_file)){
+ return simplexml_load_file($xml_file);
+ }
+
+ // extract failed
+ return false;
+ }
+
+ /**
+ * @param int $uid user id
+ * @param int $slot character slot, starting from 0
+ * @return string character save path + filename
+ */
+ function getSaveFileName($uid, $slot){
+ return parent::getSaveFileName(sprintf('characters/%03d/account_%d_%d_pdr.bin', $uid, $uid, $slot));
+ }
+
+ /**
+ * @param $char_id
+ * return string character xml file in unpack directory
+ */
+ function getXmlFileName($char_id){
+ return parent::getXmlFileName(sprintf('character_%d.xml', $char_id));
+ }
+}
diff --git a/code/ryzom/tools/server/www/webig/lib/pdr_util_guild.php b/code/ryzom/tools/server/www/webig/lib/pdr_util_guild.php
new file mode 100644
index 000000000..54249f781
--- /dev/null
+++ b/code/ryzom/tools/server/www/webig/lib/pdr_util_guild.php
@@ -0,0 +1,47 @@
+getSaveFileName($guild_id);
+ $xml_file = $this->getXmlFileName($guild_id);
+
+ if($this->extract($guild_save, $xml_file)){
+ return simplexml_load_file($xml_file);
+ }
+
+ // extract failed
+ return false;
+ }
+
+ /**
+ * @param int $guild_id
+ * @return string full path to guild binary file
+ */
+ function getSaveFileName($guild_id){
+ // chop off shard component from guild id
+ return parent::getSaveFileName(sprintf('guilds/guild_%05d.bin', $guild_id & 0xFFFFF));
+ }
+
+ /**
+ * @param $guild_id
+ * return string full path to extracted guild xml file
+ */
+ function getXmlFileName($guild_id){
+ return parent::getXmlFileName(sprintf('guild_%d.xml', $guild_id));
+ }
+}
+