Include slightly modified economy helper

Original by https://github.com/magicoli/opensim-helpers which may
provide more functionality for other things
This commit is contained in:
lickx
2025-05-25 14:27:47 +02:00
parent 4cd1faab46
commit 8e5524d412
9 changed files with 2044 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
<?php
/**
* Gloebit default configuration
*
* DO NOT EDIT THIS FILE. It will be replaced on next update.
* Instead copy the 'define(...)' lines to config.php, before the addons include
* command, and customize from there.
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*/
if ( CURRENCY_PROVIDER != 'gloebit' ) {
die();
}
/**
* Set to true to activate sandbox mode, during initial installation and tests.
* Set to false once ready to go live.
*
* @var boolean
*/
if ( ! defined( 'GLOEBIT_SANDBOX' ) ) {
define( 'GLOEBIT_SANDBOX', false );
}
/**
* Gloebit currency conversion table
*
* Used to display a cost estimation when using the viewer Buy Currency feature.
* Conversion rate between Gloebit amount and US$ cents. Must match the packages
* and prices idsplayed on Gloebit website.
* // TODO: fetch conversion values from Gloebit website.
*
* @var array
*/
if ( ! defined( 'GLOEBIT_CONVERSION_TABLE' ) ) {
define(
'GLOEBIT_CONVERSION_TABLE',
array(
400 => 199,
1050 => 499,
2150 => 999,
4500 => 1999,
11500 => 4999,
)
);
}
/**
* Affects the suggested purchase amount. Gloebit only allow predifined amounts
* to be purchased.
* - Set threshold of 1.0 to switch to the next pack even for 1$G more
* (e.g. if user request 401, suggest 1050)
* - Set theshold to 1.1 or 1.2 to keep the low pack for small differences.
* (e.g. if user request 401, still suggest 400)
* The user can still choose another pack on the web purchase page.
*
* @GLOEBIT_CONVERSION_THRESHOLD float must be >= 1.0
*/
if ( ! defined( 'GLOEBIT_CONVERSION_THRESHOLD' ) ) {
define( 'GLOEBIT_CONVERSION_THRESHOLD', 1.2 );
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Podex default configuration file.
*
* DO NOT EDIT THIS FILE. It will be replaced on next update.
* Instead copy the 'define(...)' lines to config.php, before the addons include
* command, and customize from there.
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*/
if ( CURRENCY_PROVIDER != 'podex' ) {
die();
}
/**
* This is the message displayed to users when they use the viewer "Buy
* currency" feature. Podex Buy and sell transactions are actually provided by
* in-world terminals. Adjust the message and the url to instruct user to
* teleport to the region providing Podex terminals.
*
* @var [type]
*/
if ( ! defined( 'PODEX_ERROR_MESSAGE' ) ) {
define( 'PODEX_ERROR_MESSAGE', 'Please use our terminals in-world to proceed. Click OK to teleport to terminals region.' );
}
if ( ! defined( 'PODEX_REDIRECT_URL' ) ) {
define( 'PODEX_REDIRECT_URL', 'secondlife://Podex Exchange/128/128/21' );
}

382
helper/economy/currency.php Normal file
View File

@@ -0,0 +1,382 @@
<?php
/**
* currency.php
*
* Provides web tools for OpenSim currencies
*
* Requires an OpenSimulator Money Server
* [DTL/NSL Money Server for OpenSim](http://www.nsl.tuis.ac.jp/xoops/modules/xpwiki/?OpenSim%2FMoneyServer)
* or [Gloebit module](http://dev.gloebit.com/opensim/configuration-instructions/)
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*
* Includes portions of code from
* Melanie Thielker and Teravus Ovares (http://opensimulator.org/)
* Fumi.Iseki for CMS/LMS '09 5/31
*/
// error_reporting(E_ERROR | E_WARNING | E_PARSE);
require_once 'includes/config.php';
require_once 'includes/economy.php';
//
// The XMLRPC server object
//
$xmlrpc_server = xmlrpc_server_create();
//
// Viewer retrieves currency buy quote
//
xmlrpc_server_register_method( $xmlrpc_server, 'getCurrencyQuote', 'currency_xmlrpc_quote' );
function currency_xmlrpc_quote( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$sessionid = $req['secureSessionId'];
$amount = $req['currencyBuy'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$ret = opensim_check_secure_session( $agentid, null, $sessionid );
if ( $ret ) {
$confirmvalue = currency_get_confirm_value( $ipAddress );
switch ( CURRENCY_PROVIDER ) {
case 'gloebit':
$cost = 1; // default cost if no table;
$conversion_table = GLOEBIT_CONVERSION_TABLE;
foreach ( $conversion_table as $key => $value ) {
$cost = $value;
if ( GLOEBIT_CONVERSION_THRESHOLD > 0 ) {
$threshold = GLOEBIT_CONVERSION_THRESHOLD;
} else {
$threshold = 1;
}
if ( $key >= $amount / $threshold ) {
break;
}
}
break;
default:
$cost = currency_virtual_to_real( $amount );
$realamount = $amount;
}
$currency = array(
'estimatedCost' => $cost,
'currencyBuy' => $realamount,
);
$response_xml = xmlrpc_encode(
array(
'success' => true,
'currency' => $currency,
'confirm' => $confirmvalue,
)
);
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "Unable to Authenticate\n\nClick URL for more info.",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Viewer buys currency
//
xmlrpc_server_register_method( $xmlrpc_server, 'buyCurrency', 'currency_xmlrpc_buy' );
function currency_xmlrpc_buy( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$sessionid = $req['secureSessionId'];
$amount = $req['currencyBuy'];
$confim = $req['confirm'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
if ( $confim != currency_get_confirm_value( $ipAddress ) ) {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "\n\nMissmatch Confirm Value!!",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
$checkSecure = opensim_check_secure_session( $agentid, null, $sessionid );
if ( ! $checkSecure ) {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "\n\nMissmatch Secure Session ID!!",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
$ret = false;
$cost = currency_virtual_to_real( $amount );
$transactionPermit = currency_process_transaction( $agentid, $cost, $ipAddress );
if ( $transactionPermit ) {
$res = currency_add_money( $agentid, $amount, $sessionid );
if ( $res['success'] ) {
$ret = true;
}
}
if ( $ret ) {
$response_xml = xmlrpc_encode( array( 'success' => true ) );
} else {
switch ( CURRENCY_PROVIDER ) {
case 'podex':
$errorURI = null; // opensim_format_tp(PODEX_REDIRECT_URL, TPLINK_HOP);
$errorMessage = PODEX_ERROR_MESSAGE . ' ' . PODEX_REDIRECT_URL;
break;
case 'gloebit':
if ( defined( GLOEBIT_SANDBOX ) && GLOEBIT_SANDBOX ) {
$baseurl = 'https://sandbox.gloebit.com/purchase';
} else {
$baseurl = 'https://www.gloebit.com/purchase';
}
$server_info = opensim_get_server_info( $agentid );
$serverip = $server_info['serverIP'];
$httpport = $server_info['serverHttpPort'];
$informurl = "http://${serverip}:${httpport}/gloebit/buy_complete?agentId=${agentid}";
$errorURI = "${baseurl}?reset&r=&inform=$informurl";
$errorMessage = 'Click OK to finish the transaction on Gloebit website.';
break;
default:
$errorMessage = 'Unable to process the transaction. The gateway denied your charge. Open help page?';
$errorURI = empty( W4OS_GRID_INFO['help'] ) ? CURRENCY_HELPER_URL : W4OS_GRID_INFO['help'];
}
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => $errorMessage,
'errorURI' => $errorURI,
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Region requests account balance
//
xmlrpc_server_register_method( $xmlrpc_server, 'simulatorUserBalanceRequest', 'currency_xmlrpc_balance' );
function currency_xmlrpc_balance( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$sessionid = $req['secureSessionId'];
$balance = currency_get_balance( $agentid, $sessionid );
if ( $balance >= 0 ) {
$response_xml = xmlrpc_encode(
array(
'success' => true,
'agentId' => $agentid,
'funds' => $balance,
)
);
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'Could not authenticate your avatar. Money operations may be unavailable',
'errorURI' => ' ',
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Region initiates money transfer (Direct DB Operation for security)
//
xmlrpc_server_register_method( $xmlrpc_server, 'regionMoveMoney', 'currency_xmlrpc_regionMoveMoney' );
function currency_xmlrpc_regionMoveMoney( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$destid = $req['destId'];
$sessionid = $req['secureSessionId'];
$regionid = $req['regionId'];
$secret = $req['secret'];
$currencySecret = $req['currencySecret'];
$cash = $req['cash'];
$aggregatePermInventory = $req['aggregatePermInventory'];
$aggregatePermNextOwner = $req['aggregatePermNextOwner'];
$flags = $req['flags'];
$transactiontype = $req['transactionType'];
$description = $req['description'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$ret = opensim_check_region_secret( $regionid, $secret );
if ( $ret ) {
$ret = opensim_check_secure_session( $agentid, $regionid, $sessionid );
if ( $ret ) {
$balance = currency_get_balance( $agentid, $sessionid );
if ( $balance >= $cash ) {
currency_move_money(
$agentid,
$destid,
$cash,
$transactiontype,
$flags,
$description,
$aggregatePermInventory,
$aggregatePermNextOwner,
$ipAddress
);
$sbalance = currency_get_balance( $agentid, $sessionid );
$dbalance = currency_get_balance( $destid );
$response_xml = xmlrpc_encode(
array(
'success' => true,
'agentId' => $agentid,
'funds' => $balance,
'funds2' => $balance,
'currencySecret' => ' ',
)
);
currency_update_simulator_balance( $agentid, $sbalance, $sessionid );
currency_update_simulator_balance( $destid, $dbalance );
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'You do not have sufficient funds for this purchase',
'errorURI' => ' ',
)
);
}
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'Unable to authenticate avatar. Money operations may be unavailable',
'errorURI' => ' ',
)
);
}
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'This region is not authorized to manage your money.',
'errorURI' => ' ',
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Region claims user
//
xmlrpc_server_register_method( $xmlrpc_server, 'simulatorClaimUserRequest', 'currency_xmlrpc_claimUserRequest' );
function currency_xmlrpc_claimUserRequest( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$sessionid = $req['secureSessionId'];
$regionid = $req['regionId'];
$secret = $req['secret'];
$ret = opensim_check_region_secret( $regionid, $secret );
if ( $ret ) {
$ret = opensim_check_secure_session( $agentid, null, $sessionid );
if ( $ret ) {
$ret = opensim_set_current_region( $agentid, $regionid );
if ( $ret ) {
$balance = currency_get_balance( $agentid, $sessionid );
$response_xml = xmlrpc_encode(
array(
'success' => true,
'agentId' => $agentid,
'funds' => $balance,
'currencySecret' => ' ',
)
);
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'Error occurred, when DB was updated.',
'errorURI' => ' ',
)
);
}
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'Unable to authenticate avatar. Money operations may be unavailable.',
'errorURI' => ' ',
)
);
}
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => 'This region is not authorized to manage your money.',
'errorURI' => ' ',
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Process the request
//
$request_xml = file_get_contents( 'php://input' );
// error_log(__FILE__ . ' '. $request_xml);
xmlrpc_server_call_method( $xmlrpc_server, $request_xml, '' );
xmlrpc_server_destroy( $xmlrpc_server );
die();

View File

@@ -0,0 +1,82 @@
<?php
/**
* config.example.php
*
* Helpers configuration
* Rename this file as "config.php" before editing.
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*/
/**
* Main database.
* For grids, use Robust database credentials.
* For standalone simulators, use OpenSim database credentials.
*
* Access to OpenSim database is required
* - for search including classifieds
* - for offline messages processing
* - for economy
* It is not required if only search is needed, without classifieds (e.g. to for
* a multi-grid search engine). In this case search will only provide results
* for places, land for sale and events.
*/
define( 'OPENSIM_DB', true ); // Set to false for search only, see above
define( 'OPENSIM_DB_HOST', 'localhost' );
define( 'OPENSIM_DB_NAME', 'opensim' );
define( 'OPENSIM_DB_USER', 'opensim' );
define( 'OPENSIM_DB_PASS', 'password' );
define( 'SEARCH_TABLE_EVENTS', 'events' );
/**
* Search database credentials and settings.
* Needed if you enable search in OpenSim server.
*
* A dedicated database is:
* - strongly recommended if the search engine is shared by several grids
* - recommended and more efficient for large and/or hypergrid-enabled grids
* - optional for closed grids and standalone simulators
* These are recommendations, the Robust database can safely be used instead.
*/
define( 'SEARCH_DB_HOST', OPENSIM_DB_HOST );
define( 'SEARCH_DB_NAME', OPENSIM_DB_NAME );
define( 'SEARCH_DB_USER', OPENSIM_DB_USER );
define( 'SEARCH_DB_PASS', OPENSIM_DB_PASS );
/**
* Currency database credentials and settings.
* Needed if currency is enabled on OpenSim server.
* A dedicated database is recommended, but not mandatory.
*/
define( 'CURRENCY_DB_HOST', OPENSIM_DB_HOST );
define( 'CURRENCY_DB_NAME', OPENSIM_DB_NAME );
define( 'CURRENCY_DB_USER', OPENSIM_DB_USER );
define( 'CURRENCY_DB_PASS', OPENSIM_DB_PASS );
define( 'CURRENCY_MONEY_TBL', 'balances' );
define( 'CURRENCY_TRANSACTION_TBL', 'transactions' );
/**
* Money Server settings.
*/
define( 'CURRENCY_USE_MONEYSERVER', false );
define( 'CURRENCY_SCRIPT_KEY', '123456789' );
define( 'CURRENCY_RATE', 10 ); // amount in dollar...
define( 'CURRENCY_RATE_PER', 1000 ); // ... for this amount in virtual currency
define( 'CURRENCY_PROVIDER', null ); // NULL, 'podex' or 'gloebit'
define( 'CURRENCY_HELPER_URL', 'http://yourgrid.org/helpers/' );
// if (!defined('CURRENCY_HELPER_PATH')) define('CURRENCY_HELPER_PATH', dirname(__DIR__));
/**
* DO NOT MAKE CHANGES BELOW THIS
* Add your custom values above.
*/
require_once 'databases.php';
require_once 'functions.php';
$currency_addon = dirname( __DIR__ ) . '/addons/' . CURRENCY_PROVIDER . '.php';
if ( file_exists( $currency_addon ) ) {
require_once $currency_addon;
}

View File

@@ -0,0 +1,98 @@
<?php
/**
* New database class using PDO, replaces DB class using mysqli
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*/
class OSPDO extends PDO {
public function __construct( $dsn, $username = null, $password = null, $driver_options = null ) {
try {
@parent::__construct( $dsn, $username, $password, $driver_options );
$this->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$this->connected = true;
} catch ( PDOException $e ) {
error_log( "Could not connect to database $dsn as $username" );
// error_log($e);
$this->connected = false;
}
}
/**
* Prepare SQL query, execute with params and log error if any
*
* @param string $query
* @param array $options options passed to prepare()
* @param array $params substitute markers passed to execute()
* @return PDOstatement if success, false on error
*/
public function prepareAndExecute( $query, $params = null, $options = array() ) {
$trace = debug_backtrace()[0];
$trace = $trace['file'] . ':' . $trace['line'];
try {
$statement = $this->prepare( $query, $options );
$result = $statement->execute( $params );
} catch ( PDOException $e ) {
error_log( 'Error ' . $e->getCode() . ' ' . $e->getMessage() . ' query ' . print_r( $query, true ) . ' params ' . print_r( $params, true ) . ' ' . $trace );
return false;
}
// $statement = $this->prepare( $query, $options );
// $result = $statement->execute( $params );
if ( $result ) {
return $statement;
}
error_log( 'Error ' . $statement->errorCode() . ' ' . $statement->errorInfo()[2] . ' ' . $trace );
return false;
}
public function insert( $table, $values ) {
foreach ( $values as $field => $value ) {
$markers[] = ':' . $field;
}
$markers = implode( ',', $markers );
$fields = implode( ',', array_keys( $values ) );
$sql = "INSERT INTO $table ($fields) VALUES ($markers)";
$statement = $this->prepare( $sql );
return $statement->execute( $values );
}
}
function tableExists( $pdo, $tables ) {
if ( ! is_object( $pdo ) ) {
return false;
}
if ( ! $pdo->connected ) {
return false;
}
// error_log("pdo " . print_r($pdo, true));
if ( is_string( $tables ) ) {
$tables = array( $tables );
}
foreach ( $tables as $table ) {
// Try a select statement against the table
// Run it in try/catch in case PDO is in ERRMODE_EXCEPTION.
try {
$result = $pdo->query( "SELECT 1 FROM $table LIMIT 1" );
} catch ( Exception $e ) {
error_log( __FILE__ . ': ' . SEARCH_DB_NAME . " is missing table $table" );
// We got an exception == table not found
return false;
}
if ( $result == false ) {
error_log( __FILE__ . ': ' . SEARCH_DB_NAME . " is missing table $table" );
return false;
}
}
return true;
}
if ( defined( 'OPENSIM_DB' ) && OPENSIM_DB === true ) {
$OpenSimDB = new OSPDO( 'mysql:host=' . OPENSIM_DB_HOST . ';dbname=' . OPENSIM_DB_NAME, OPENSIM_DB_USER, OPENSIM_DB_PASS );
}

View File

@@ -0,0 +1,581 @@
<?php
/**
* economy.php
*
* Provides functions required only by currency.php and landtool.php
*
* Requires an OpenSimulator Money Server
* [DTL/NSL Money Server for OpenSim](http://www.nsl.tuis.ac.jp/xoops/modules/xpwiki/?OpenSim%2FMoneyServer)
* or [Gloebit module](http://dev.gloebit.com/opensim/configuration-instructions/)
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*
* Includes portions of code from original DTLS/NLS Money Server, by:
* Melanie Thielker and Teravus Ovares (http://opensimulator.org/)
* Fumi.Iseki for CMS/LMS '09 5/31
**/
require_once 'functions.php';
if ( defined( 'CURRENCY_DB_HOST' ) ) {
$CurrencyDB = new OSPDO( 'mysql:host=' . CURRENCY_DB_HOST . ';dbname=' . CURRENCY_DB_NAME, CURRENCY_DB_USER, CURRENCY_DB_PASS );
} else {
$CurrencyDB = &$OpenSimDB;
}
function noserver_save_transaction( $sourceId, $destId, $amount, $type, $flags, $desc, $prminvent, $nxtowner, $ip ) {
global $CurrencyDB;
if ( ! is_numeric( $amount ) ) {
return;
}
if ( ! opensim_isuuid( $sourceId ) ) {
$sourceId = NULL_KEY;
}
if ( ! opensim_isuuid( $destId ) ) {
$destId = NULL_KEY;
}
$region = NULL_KEY;
$client = $sourceId;
if ( $client == NULL_KEY ) {
$client = $destId;
}
$avt = opensim_get_avatar_session( $client );
if ( $avt != null ) {
$region = $avt['regionID'];
}
$CurrencyDB->insert(
CURRENCY_TRANSACTION_TBL,
array(
'sourceId' => $sourceId,
'destId' => $destId,
'amount' => $amount,
'flags' => $flags,
'aggregatePermInventory' => $prminvent,
'aggregatePermNextOwner' => $nxtowner,
'description' => $desc,
'transactionType' => $type,
'timeOccurred' => time(),
'RegionGenerated' => $region,
'ipGenerated' => $ip,
)
);
}
function noserver_get_balance( $agentID ) {
global $CurrencyDB;
if ( ! opensim_isuuid( $agentID ) ) {
return -1;
}
$sent_sum = 0;
$received_sum = 0;
$credits = $CurrencyDB->prepareAndExecute(
'SELECT SUM(amount) FROM ' . CURRENCY_TRANSACTION_TBL . ' WHERE destId = :destId',
array( 'destId' => $agentID )
);
if ( $credits ) {
list($received_sum) = $credits->fetch();
}
$debits = $CurrencyDB->prepareAndExecute(
'SELECT SUM(amount) FROM ' . CURRENCY_TRANSACTION_TBL . ' WHERE sourceId = :sourceId',
array( 'sourceId' => $agentID )
);
if ( $debits ) {
list($sent_sum) = $debits->fetch();
}
$cash = (int) $received_sum - (int) $sent_sum;
return $cash;
}
function currency_save_transaction( $sourceId, $destId, $amount, $type, $flags, $description, &$deprecated = null ) {
global $CurrencyDB;
if ( ! is_numeric( $amount ) ) {
return;
}
if ( ! opensim_isuuid( $sourceId ) ) {
$sourceId = NULL_KEY;
}
if ( ! opensim_isuuid( $destId ) ) {
$destId = NULL_KEY;
}
$handle = 0;
$secure = NULL_KEY;
$client = $sourceId;
$UUID = make_random_guid();
$sourceID = $sourceId;
$destID = $destId;
if ( $client == NULL_KEY ) {
$client = $destId;
}
$avt = opensim_get_avatar_session( $client );
if ( $avt != null ) {
$region = $avt['regionID'];
$secure = $avt['secureID'];
$rgn = opensim_get_region_info( $region );
if ( $rgn != null ) {
$handle = $rgn['regionHandle'];
}
}
$CurrencyDB->insert(
CURRENCY_TRANSACTION_TBL,
array(
'UUID' => $UUID,
'sender' => $sourceID,
'receiver' => $destID,
'amount' => $amount,
'objectUUID' => NULL_KEY,
'regionHandle' => $handle,
'type' => $type,
'time' => time(),
'secure' => $secure,
'status' => $flags,
'description' => $description,
)
);
}
function currency_set_currency_balance( $agentID, $amount, &$deprecated = null ) {
if ( ! opensim_isuuid( $agentID ) or ! is_numeric( $amount ) ) {
return false;
}
global $CurrencyDB;
$balances_table = CURRENCY_MONEY_TBL;
$CurrencyDB->query( "LOCK TABLES $balances_table" );
$currentbalance = $CurrencyDB->prepareAndExecute(
"SELECT balance FROM $balances_table WHERE user = :user",
array(
'user' => $agentID,
)
);
if ( $currentbalance ) {
list($cash) = $currentbalance->fetch();
$balance = (int) $cash + (int) $amount;
$result = $CurrencyDB->prepareAndExecute(
"UPDATE $balances_table SET balance = :balance WHERE user = :user",
array(
'balance' => $balance,
'user' => $agentID,
)
);
} else {
$result = false;
}
$CurrencyDB->query( "UNLOCK TABLES $balances_table" );
return $result;
}
function currency_update_simulator_balance( $agentID, $amount = -1, $secureID = null ) {
if ( ! opensim_isuuid( $agentID ) ) {
return false;
}
if ( $amount < 0 ) {
$amount = currency_get_balance( $agentID, $secureID );
if ( $amount < 0 ) {
return false;
}
}
// XML RPC to Region Server
if ( ! opensim_isuuid( $secureID, true ) ) {
return false;
}
$agentServer = opensim_get_server_info( $agentID );
if ( ! $agentServer ) {
return false;
}
$serverip = $agentServer['serverIP'];
$httpport = $agentServer['serverHttpPort'];
$serveruri = $agentServer['serverURI'];
$avatarSession = opensim_get_avatar_session( $agentID );
if ( ! $avatarSession ) {
return false;
}
$sessionID = $avatarSession['sessionID'];
if ( $secureID == null ) {
$secureID = $avatarSession['secureID'];
}
$request = xmlrpc_encode_request(
'UpdateBalance',
array(
array(
'clientUUID' => $agentID,
'clientSessionID' => $sessionID,
'clientSecureSessionID' => $secureID,
'Balance' => $amount,
),
)
);
$response = currency_xmlrpc_call( $serverip, $httpport, $serveruri, $request );
return $response;
}
function currency_move_money( $agentID, $destID, $amount, $type, $flags, $desc, $prminvent = 0, $nxtowner = 0, $ip = '' ) {
if ( ! CURRENCY_USE_MONEYSERVER ) {
noserver_save_transaction( $agentID, $destID, $amount, $type, $flags, $desc, $prminvent, $nxtowner, $ip );
return true;
}
// Direct DB access for security
// $url = preg_split("/[:\/]/", USER_SERVER_URI);
// $userip = $url[3];
currency_save_transaction( $agentID, $destID, $amount, $type, $flags, $desc );
// TODO: Shouldn't we execute both balance updates only if all of the four
// conditions are met and none of them if any of the checks fails?
if ( opensim_isuuid( $agentID ) and $agentID != NULL_KEY ) {
currency_set_currency_balance( $agentID, -$amount );
}
if ( opensim_isuuid( $destID ) and $destID != NULL_KEY ) {
currency_set_currency_balance( $destID, $amount );
}
return true;
}
function currency_add_money( $agentID, $amount, $secureID = null ) {
if ( ! opensim_isuuid( $agentID ) ) {
return false;
}
if ( ! CURRENCY_USE_MONEYSERVER ) {
noserver_save_transaction( null, $agentID, $amount, 5010, 0, 'Add Money', 0, 0, '' );
$response = array( 'success' => true );
return $response;
}
//
// XML RPC to Region Server
//
if ( ! opensim_isuuid( $secureID, true ) ) {
return false;
}
$agentServer = opensim_get_server_info( $agentID );
$serverip = $agentServer['serverIP'];
$httpport = $agentServer['serverHttpPort'];
$serveruri = $agentServer['serverURI'];
if ( $serverip == '' ) {
return false;
}
$avatarSession = opensim_get_avatar_session( $agentID );
$sessionID = $avatarSession['sessionID'];
// if ($sessionID=="") return false;
if ( $secureID == null ) {
$secureID = $avatarSession['secureID'];
}
$request = xmlrpc_encode_request(
'AddBankerMoney',
array(
array(
'clientUUID' => $agentID,
'clientSessionID' => $sessionID,
'clientSecureSessionID' => $secureID,
'amount' => $amount,
),
)
);
$response = currency_xmlrpc_call( $serverip, $httpport, $serveruri, $request );
return $response;
}
//
// Send the money to avatar for bonus
// by Milo
//
function currency_send_money( $agentID, $amount, $secretCode = null ) {
if ( ! opensim_isuuid( $agentID ) ) {
return false;
}
if ( ! CURRENCY_USE_MONEYSERVER ) {
noserver_save_transaction( null, $agentID, $amount, 5003, 0, 'Send Money', 0, 0, '' );
$response = array( 'success' => true );
return $response;
}
//
// XML RPC to Region Server
//
$agentServer = opensim_get_server_info( $agentID );
$serverip = $agentServer['serverIP'];
$httpport = $agentServer['serverHttpPort'];
$serveruri = $agentServer['serverURI'];
if ( $serverip == '' ) {
return false;
}
$serverip = gethostbyname( $serverip );
if ( $secretCode != null ) {
$secretCode = md5( $secretCode . '_' . $serverip );
} else {
$secretCode = currency_get_confirm_value( $serverip );
}
$request = xmlrpc_encode_request(
'SendMoneyBalance',
array(
array(
'clientUUID' => $agentID,
'secretAccessCode' => $secretCode,
'amount' => $amount,
),
)
);
$response = currency_xmlrpc_call( $serverip, $httpport, $serveruri, $request );
return $response;
}
function currency_get_balance( $agentID, $secureID = null ) {
$cash = -1;
if ( ! opensim_isuuid( $agentID ) ) {
return (int) $cash;
}
if ( ! CURRENCY_USE_MONEYSERVER ) {
$cash = noserver_get_balance( $agentID );
return (int) $cash;
}
if ( ! opensim_isuuid( $secureID, true ) ) {
return (int) $cash;
}
$agentServer = opensim_get_server_info( $agentID );
$serverip = $agentServer['serverIP'];
$httpport = $agentServer['serverHttpPort'];
$serveruri = $agentServer['serverURI'];
if ( $serverip == '' ) {
return (int) $cash;
}
$avatarSession = opensim_get_avatar_session( $agentID );
$sessionID = $avatarSession['sessionID'];
if ( $sessionID == '' ) {
return (int) $cash;
}
if ( $secureID == null ) {
$secureID = $avatarSession['secureID'];
}
$request = xmlrpc_encode_request(
'GetBalance',
array(
array(
'clientUUID' => $agentID,
'clientSessionID' => $sessionID,
'clientSecureSessionID' => $secureID,
),
)
);
$response = currency_xmlrpc_call( $serverip, $httpport, $serveruri, $request );
if ( $response ) {
$cash = $response['balance'];
}
return (int) $cash;
}
function currency_get_confirm_value( $ipAddress ) {
// TODO:
// Option to force key to be something else than default
$key = empty( CURRENCY_SCRIPT_KEY ) ? '1234567883789' : CURRENCY_SCRIPT_KEY;
$confirmvalue = md5( $key . '_' . $ipAddress );
return $confirmvalue;
}
function currency_process_transaction( $avatarID, $cost, $ipAddress ) {
// Do external processing here! (credit card, paypal, any money system)
// Return False if it fails!
// Remember, $amount is stored without decimal places, however it's assumed
// that the transaction amount is in Cents and has two decimal places
// 5 dollars will be 500
// 15 dollars will be 1500
// if ($avatarID==CURRENCY_BANKER) return true;
// return false;
return true;
}
function currency_virtual_to_real( $amount ) {
$cost = (int) ( CURRENCY_RATE / CURRENCY_RATE_PER * 100 * $amount );
return $cost;
}
// XML RPC
function currency_xmlrpc_call( $host, $port, $uri, $request ) {
$url = '';
if ( $uri != '' ) {
$dec = explode( ':', $uri );
if ( ! strncasecmp( $dec[0], 'http', 4 ) ) {
$url = "$dec[0]:$dec[1]";
}
}
if ( $url == '' ) {
$url = "http://$host";
}
$url = "$url:$port/";
// TODO: use file_get_contents() instead of over complicate curl procedure
$header[] = 'Content-type: text/xml';
$header[] = 'Content-length: ' . strlen( $request );
$ch = curl_init();
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_TIMEOUT, 3 );
curl_setopt( $ch, CURLOPT_HTTPHEADER, $header );
curl_setopt( $ch, CURLOPT_POSTFIELDS, $request );
$data = curl_exec( $ch );
if ( ! curl_errno( $ch ) ) {
curl_close( $ch );
}
$ret = false;
if ( $data ) {
$ret = xmlrpc_decode( $data );
}
return $ret;
}
/**
* Following functions are not specific to money and could be useful in other
* helpers. However, they are not: they are currently only used in currency
* scripts, so I leave them here, hence the opensim_ prefix.
*/
function opensim_get_avatar_session( $agentID, &$deprecated = null ) {
global $OpenSimDB;
if ( ! opensim_isuuid( $agentID ) ) {
return null;
}
$result = $OpenSimDB->query( "SELECT RegionID,SessionID,SecureSessionID FROM Presence WHERE UserID='$agentID'" );
if ( $result ) {
list($RegionID, $SessionID, $SecureSessionID) = $result->fetch();
} else {
return array();
}
$av_session['regionID'] = $RegionID;
$av_session['sessionID'] = $SessionID;
$av_session['secureID'] = $SecureSessionID;
return $av_session;
}
function opensim_set_current_region( $agentID, $regionid, &$deprecated = null ) {
global $OpenSimDB;
if ( ! opensim_isuuid( $agentID ) or ! opensim_isuuid( $regionid ) ) {
return false;
}
$sql = "UPDATE Presence SET RegionID='$regionid' WHERE UserID='$agentID'";
$result = $OpenSimDB->query( $sql );
if ( ! $result ) {
return false;
}
return true;
}
function opensim_get_server_info( $userid, &$deprecated = null ) {
global $OpenSimDB;
if ( ! opensim_isuuid( $userid ) ) {
return array();
}
$result = $OpenSimDB->query(
"SELECT serverIP,serverHttpPort,serverURI,regionSecret
FROM GridUser INNER JOIN regions ON regions.uuid=GridUser.LastRegionID
WHERE GridUser.UserID='$userid'"
);
if ( $result ) {
list($serverip, $httpport, $serveruri, $secret) = $result->fetch();
} else {
return array();
}
$serverinfo['serverIP'] = $serverip;
$serverinfo['serverHttpPort'] = $httpport;
$serverinfo['serverURI'] = $serveruri;
$serverinfo['regionSecret'] = $secret;
return $serverinfo;
}
function opensim_check_secure_session( $agentID, $regionid, $secure, &$deprecated = null ) {
global $OpenSimDB;
if ( ! opensim_isuuid( $agentID ) or ! opensim_isuuid( $secure ) ) {
return false;
}
$sql = "SELECT UserID FROM Presence WHERE UserID='$agentID' AND SecureSessionID='$secure'";
if ( opensim_isuuid( $regionid ) ) {
$sql = $sql . " AND RegionID='$regionid'";
}
$result = $OpenSimDB->query( $sql );
if ( ! $result ) {
return false;
}
list($UUID) = $result->fetch();
if ( $UUID != $agentID ) {
return false;
}
return true;
}
function opensim_check_region_secret( $regionID, $secret, &$deprecated = null ) {
global $OpenSimDB;
if ( ! opensim_isuuid( $regionID ) ) {
return false;
}
$result = $OpenSimDB->prepareAndExecute(
'SELECT UUID FROM regions WHERE UUID=:uuid AND regionSecret=:regionSecret',
array(
'uuid' => $regionID,
'regionSecret' => $secret,
)
);
if ( $result ) {
list($UUID) = $result->fetch();
if ( $UUID == $regionID ) {
return true;
}
}
return false;
}

View File

@@ -0,0 +1,612 @@
<?php
/**
* economy.php
*
* Provides functions required by helpers
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*/
/**
* Verify if given string is an UUID.
* In theory, we would check want v4-compliant uuids
* (xxxxxxxx-xxxx-4xxx-[89AB]xxx-xxxxxxxxxxxx) but OpenSimulator seems to have
* lot of non v4-compliant uuids left, so stict defaults to false.
*
* @param [type] $uuid string to verify
* @param boolean $nullok accept null value or null key as valid (default false)
* @param boolean $strict apply strict UUID v4 implentation (default false)
* @return boolean
*/
function opensim_isuuid( $uuid, $nullok = false, $strict = false ) {
if ( $uuid == null ) {
return $nullok;
}
if ( defined( 'NULL_KEY' ) && $uuid == NULL_KEY ) {
return $nullok;
}
if ( $strict ) {
return ( preg_match( '/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i', $uuid ) );
} else {
return ( preg_match( '/^[0-9A-F]{8,8}-[0-9A-F]{4,4}-[0-9A-F]{4,4}-[0-9A-F]{4,4}-[0-9A-F]{12,12}$/i', $uuid ) );
}
}
/**
* Sanitize a destination URI or URL
*
* @param string $url url or uri (secondlife:// url, hop:// url, region name...)
* @param string $gatekeeperURL default login uri to add to urls sithout host:port
* @param boolean $array_outout output as array
* @return string (default) $host:$port $region/$pos
* or array array($host, $port, $region, $pos)
*/
function opensim_sanitize_uri( $url, $gatekeeperURL = null, $array_outout = false ) {
// $normalized = opensim_format_tp($uri, TPLINK_TXT);
$host = null;
$port = null;
$region = null;
$pos = null;
$uri = urldecode( trim( $url ) );
$uri = preg_replace( '#^(.*://)?(([A-Za-z0-9_-]+\.[A-Za-z0-9\._-]+)([:/ ]+)?)?(([0-9]+)([ /:]))?([^/]+)(/|$)(.*)#', '$3:$6:$8/$10', "$uri" );
$uri = preg_replace( '/^([^:]+)::([0-9]+)/', '$1:$2', $uri );
$uri = preg_replace( '+[:/]*$+', '', $uri );
$split = explode( '/', $uri );
$uri = array_shift( $split );
if ( count( $split ) == 2 || count( $split ) == 3 ) {
$pos = implode( '/', $split );
} else {
$pos = '';
}
// $pos = preg_replace('+[^0-9/]+e', '', $pos);
$split = explode( ':', $uri );
if ( count( $split ) == 1 ) {
$region = $split[0];
} elseif ( count( $split ) == 2 && preg_match( '/ /', $split[1] ) ) {
// could probably improve the preg_replace to avoid this
$host = $split[0];
$split = explode( ' ', $split[1] );
$port = $split[0];
$region = $split[1];
} elseif ( preg_match( '/[a-z].*\.[a-z]/', $split[0] ) ) {
$host = array_shift( $split );
if ( preg_match( '/^[0-9]+$/', $split[0] ) ) {
$port = array_shift( $split );
} else {
$port = 8002;
}
$region = preg_replace( ':^/*:', '', @$split[0] );
} elseif ( function_exists( 'w4os_grid_login_uri' ) ) {
$host = parse_url( w4os_grid_login_uri(), PHP_URL_HOST );
$port = parse_url( w4os_grid_login_uri(), PHP_URL_HOST );
if ( preg_match( '/^[0-9]+$/', $split[0] ) ) {
array_shift( $split );
}
$region = preg_replace( ':^/*:', '', @$split[0] );
} else {
if ( empty( $gatekeeperURL ) ) {
return false;
}
$region = $split[2];
$split = explode( ':', preg_replace( '#.*://([^/]+)/?.*#', '$1', $gatekeeperURL ) );
$host = $split[0];
$port = $split[1];
}
if ( empty( $host ) & ! empty( $gatekeeperURL ) ) {
$split = explode( ':', preg_replace( '#.*://([^/]+)/?.*#', '$1', $gatekeeperURL ) );
$host = $split[0];
$port = $split[1];
}
if ( empty( $port ) & ! empty( $host ) ) {
$port = 80;
}
$host = strtolower( trim( $host ) );
$region = trim( str_replace( '_', ' ', $region ) );
if ( is_numeric( $region ) ) {
$pos = "$region/$pos";
$region = '';
}
if ( $array_outout ) {
return array(
'host' => $host,
'port' => $port,
'region' => $region,
'pos' => $pos,
'gatekeeper' => "http://$host:$port",
'key' => strtolower( "$host:$port/$region" ),
);
} else {
return trim(
$host
. ( empty( $port ) ? '' : ":$port" )
. ( empty( $region ) ? '' : " $region" )
. ( empty( $pos ) ? '' : "/$pos" ),
":/ \n\r\t\v\x00"
);
}
// trim(string $string, string $characters = " \n\r\t\v\x00"): string
// return preg_replace('#^[: ]*(.*)/*$#', '$1', "$host:$port $region" . ((empty($pos)) ? '' : "/$pos"));
}
/**
* Format destination uri as a valid local or hypergrid link url
*
* @param string $uri Destination uri, as "host:port:Region Name" or already formatted URL
* @param integer $format The desired format as binary flags. Several values can be specified with an addition
* e.g. TPLINK_V3HG + TPLINK_APPTP
* TPLINK_LOCAL or 1: secondlife://Region Name/x/y/z
* TPLINK_HG or 2: original HG format (obsolete?)
* TPLINK_V3HG or 4: v3 HG format (Singularity)
* TPLINK_HOP or 8: hop:// format (FireStorm)
* TPLINK_TXT or 16: host:port Region Name
* TPLINK_APPTP or 32: secondlife:///app/teleport link
* TPLINK_MAP or 64: (not implemented)
* 127: output all formats
* @param string $sep Separator for multiple formats, default new line
* @return string
*/
function opensim_format_tp( $uri, $format = TPLINK, $sep = "\n" ) {
if ( empty( $uri ) ) {
return;
}
$parts = parse_url( $uri );
// $uri = preg_replace('#!#', '', $uri);
// $uri = preg_replace('#.*://+#', '', $uri);
// $uri = preg_replace('#[\|:]#', '/', $uri);
// $uri = preg_replace('#^([^/]+)/([0-9]+)/#', '$1:$2/', $uri);
// $uri = preg_replace('#^[[:blank:]]*#', '', $uri);
// echo "$uri ";
// // $uri = preg_replace('#(\d{4}):#', '$1/', $uri);
// $parts = explode("/", $uri);
// $loginuri = array_shift($parts);
// $hostparts = explode(":", $loginuri);
// $host = $hostparts[0];
// $port = (empty($hostparts[1])) ? 80 : $hostparts[1];
// $region = urldecode(array_shift($parts));
// $pos="";
// if(count($parts) >=3 && is_numeric($parts[0]) && is_numeric($parts[1]) && is_numeric($parts[2]) ) {
// $posparts = array($parts[0],$parts[1],$parts[2]);
// $pos = join('/', $posparts);
// $pos_sl = ($parts[0]>=256 || $parts[0]>=256) ? "" : $pos;
// }
$uri_parts = opensim_sanitize_uri( $uri, '', true );
extract( $uri_parts );
$regionencoded = urlencode( $region );
$region_hop = str_replace( ' ', '%20', $region );
$pos_mandatory = ( empty( $pos ) ) ? '128/128/25' : $pos;
$links = array();
if ( $format & TPLINK_TXT ) {
$links[ TPLINK_TXT ] = "$host:$port $region/$pos";
}
if ( $format & TPLINK_LOCAL || ( $format & TPLINK_HG && empty( $host ) ) ) {
$links[ TPLINK_LOCAL ] = "secondlife://$region/$pos";
}
if ( $format & TPLINK_HG ) {
$links[ TPLINK_HG ] = "secondlife://$host:$port $region/$pos";
}
if ( $format & TPLINK_V3HG ) {
$links[ TPLINK_V3HG ] = "secondlife://http|!!$host|$port+$region";
}
if ( $format & TPLINK_HOP ) {
$links[ TPLINK_HOP ] = "hop://$host:$port/$region_hop" . ( empty( $pos ) ? '' : "/$pos" );
}
if ( $format & TPLINK_APPTP ) {
$links[ TPLINK_APPTP ] = "secondlife:///app/teleport/$host:$port+$regionencoded/" . ( ( ! empty( $pos_sl ) ) ? "$pos_sl/" : '' );
}
// if ($format & TPLINK_MAP) $links[TPLINK_MAP] = "secondlife:///app/map/$host:$port+$regionencoded/$pos";
$links = preg_replace( '#^[^[:alnum:]]*|[^[:alnum:]]+$#', '', $links );
return join( $sep, $links );
}
/**
* Use xmlrpc link_region method to request link_region data from robust
*
* @param mixed $args region uri or sanitized region array
* @param string $var output a single variable value
* @return array (or string if var specified)
*/
function opensim_link_region( $args, $var = null ) {
if ( empty( $args ) ) {
return array();
}
global $OSSEARCH_CACHE;
if ( is_array( $args ) ) {
$region_array = $args;
} else {
$region_array = opensim_sanitize_uri( $args, '', true );
}
extract( $region_array ); // $host, $port, $region, $pos, $gatekeeper, $key
if ( isset( $OSSEARCH_CACHE['link_region'][ $key ] ) ) {
$link_region = $OSSEARCH_CACHE['link_region'][ $key ];
} else {
$link_region = oxXmlRequest( $gatekeeper, 'link_region', array( 'region_name' => "$region" ) );
$OSSEARCH_CACHE['link_region'][ $key ] = $link_region;
}
if ( $link_region ) {
if ( $var ) {
return $link_region[ $var ];
} else {
return $link_region;
}
}
return array();
}
/**
* Build region URL from array
*
* @param array $region sanitized region array
* @return string
*/
function opensim_region_url( $region ) {
if ( ! is_array( $region ) ) {
return false;
}
return $region['gatekeeper'] . ( empty( $region['region'] ) ? '' : ':' . $region['region'] ) . ( empty( $region['pos'] ) ? '' : '/' . $region['pos'] );
}
function opensim_get_region( $region_uri, $var = null ) {
if ( empty( $region_uri ) ) {
return array();
}
global $OSSEARCH_CACHE;
$region = opensim_sanitize_uri( $region_uri, '', true );
$gatekeeper = $region['gatekeeper'];
$link_region = opensim_link_region( $region );
$uuid = @$link_region['uuid'];
if ( ! opensim_isuuid( $uuid ) ) {
// error_log( "opensim_get_region $region_uri invalid uuid $uuid" );
return array();
}
if ( isset( $OSSEARCH_CACHE['get_region'][ $uuid ] ) ) {
$get_region = $OSSEARCH_CACHE['get_region'][ $uuid ];
} else {
$get_region = oxXmlRequest( $gatekeeper, 'get_region', array( 'region_uuid' => "$uuid" ) );
// $get_region = oxXmlRequest('http://dev.w4os.org:8402/grid', 'get_region_by_name', ['scopeid' => NULL_KEY,'name'=>"$region"]);
$OSSEARCH_CACHE['get_region'][ $uuid ] = $get_region;
}
$get_region['link_region'] = $link_region;
if ( $get_region ) {
if ( $var ) {
return $get_region[ $var ];
} else {
return $get_region;
}
}
return array();
}
/**
* Check if region is online
*
* @param mixed $region region uri or sanitized region array
* @return boolean true if online
*/
function opensim_region_is_online( $region ) {
$data = opensim_link_region( $region );
return ( $data && $data['result'] == 'True' );
}
function opensim_user_alert( $agentID, $message, $secureID = null ) {
$agentServer = opensim_get_server_info( $agentID );
if ( ! $agentServer ) {
return false;
}
$serverip = $agentServer['serverIP'];
$httpport = $agentServer['serverHttpPort'];
$serveruri = $agentServer['serverURI'];
$avatarSession = opensim_get_avatar_session( $agentID );
if ( ! $avatarSession ) {
return false;
}
$sessionID = $avatarSession['sessionID'];
if ( $secureID == null ) {
$secureID = $avatarSession['secureID'];
}
$request = xmlrpc_encode_request(
'UserAlert',
array(
array(
'clientUUID' => $agentID,
'clientSessionID' => $sessionID,
'clientSecureSessionID' => $secureID,
'Description' => $message,
),
)
);
$response = currency_xmlrpc_call( $serverip, $httpport, $serveruti, $request );
return $response;
}
/**
* [oxXmlRequest description]
*
* @param string $gatekeeper [description]
* @param string $method [description]
* @param array $request [description]
* @return array received xml response
*/
function oxXmlRequest( $gatekeeper, $method, $request ) {
$xml_request = xmlrpc_encode_request( $method, array( $request ) );
$context = stream_context_create(
array(
'http' => array(
'method' => 'POST',
'header' => 'Content-Type: text/xml' . "\r\n",
'timeout' => 3, // most of the time below 1 sec, but leave some time for slow ones
'content' => $xml_request,
),
)
);
$response = @file_get_contents( $gatekeeper, false, $context );
if ( $response === false ) {
return false;
}
$xml_array = xmlrpc_decode( $response );
if ( empty( $xml_array ) ) {
return;
}
if ( is_array( $xml_array ) & ! xmlrpc_is_fault( $xml_array ) ) {
return $xml_array;
}
return false;
}
function osXmlResponse( $success = true, $errorMessage = false, $data = false ) {
global $request_key;
// Data given, output as xmlrpc
if ( is_array( $data ) ) {
if( ! $success && ! empty( $errorMessage ) ) {
# Avoid duplicate error messages
# TODO: improve to make sure we don't cache error messages for different requests
# (currently $request_key only identifies search arguments, client IP and gateway url,
# but client IP is the simulator, not the actual user, so in theory, the key could
# be identical for two simultaneous requests by different users in the same region,
# although this is unlikely to happen)
$tmp_dir = get_writable_tmp_dir();
$cache = $tmp_dir . '/cache-' . $request_key;
# Check if file exists and is not older than 5 seconds
if ( file_exists( $cache ) && ( time() - filemtime( $cache ) < 1.5 ) ) {
$errorMessage = '';
} else {
file_put_contents( $cache, $errorMessage );
}
}
$array = array(
'success' => $success,
'errorMessage' => $errorMessage,
'data' => $data,
);
$response_xml = xmlrpc_encode( $array );
echo $response_xml;
return;
}
// No data given, output simple boolean or error message, no change here
if ( $success ) {
$answer = new SimpleXMLElement( '<boolean>true</boolean>' );
} else {
$answer = new SimpleXMLElement( "<error>$errorMessage</error>" );
}
echo $answer->asXML();
}
function osXmlDie( $message = '' ) {
osXmlResponse( false, $message, array() );
die;
}
function osNotice( $message ) {
echo $message . "\n";
}
function osAdminNotice( $message, $error_code = 0, $die = false ) {
// get calling function and file
$trace = debug_backtrace();
if ( isset( $trace[1] ) ) {
$caller = $trace[1];
} else {
$caller = $trace[0];
}
$file = empty( $caller['file'] ) ? '' : $caller['file'];
$function = $caller['function'] . '()' ?? 'main';
$line = $caller['line'] ?? 0;
$class = $caller['class'] ?? 'main';
$type = $caller['type'] ?? '::';
if ( $class != 'main' ) {
$function = $class . $type . $function;
}
$file = $file . ':' . $line;
$message = sprintf(
'%s%s: %s in %s',
$function,
empty( $error_code ) ? '' : " Error $error_code",
$message,
$file,
);
error_log( $message );
if ( $die == true ) {
die( $error_code );
}
}
/**
* Flush output and free client so following commands are executed in background
*
* @return void
*/
function dontWait() {
$size = ob_get_length();
header( "Content-Length:$size" );
header( 'Connection:close' );
header( 'Content-Encoding: none' );
header( 'Content-Type: text/html; charset=utf-8' );
ob_flush();
ob_end_flush();
flush();
}
if ( ! function_exists( 'osdebug' ) ) {
function osdebug( $message = '' ) {
if ( empty( $message ) ) {
return;
}
if ( ! is_string( $message ) ) {
$message = print_r( $message, true );
}
error_log( 'osdebug ' . $message );
echo $message . "\n";
}
}
function set_helpers_locale( $locale = null, $domain = 'messages' ) {
mb_internal_encoding( 'UTF-8' );
$encoding = mb_internal_encoding();
if ( isset( $_GET['l'] ) ) {
$locale = $_GET['l'];
}
$languages = array_filter( array_merge( array( $locale ), explode( ',', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) );
// $results = putenv("LC_ALL=$locale");
// if (!$results) {
// exit ('putenv failed');
// }
// $currentLocale = setlocale(LC_ALL, 0);
$user_locales = array_unique( array( $locale, $locale . ".$encoding", $locale . '.UTF-8', $locale . '.utf8', $locale, 0 ) );
$user_locales = array_map(
function ( $code ) {
return preg_replace(
array( '/;.*/', '/-/' ),
array( '', '_' ),
$code
);
},
$languages
);
// Generate variants with different encodings appended
$variants = array();
foreach ( $user_locales as $lang ) {
$variants[] = $lang;
$variants[] = "$lang.$encoding";
// $variants[] = "$lang.UTF-8";
}
$variants = array_unique( $variants );
if ( ! setlocale( LC_ALL, $variants ) ) {
// error_log( "setlocale() failed: none of '" . join( ', ', $variants ) . "' does exist in this environment or setlocale() is not available on this platform" );
setlocale( LC_ALL, 0 );
return 0;
}
bindtextdomain( $domain, './locales' );
textdomain( $domain );
}
function get_writable_tmp_dir() {
if(isset($_GLOBALS['tmp_dir'])) {
return $_GLOBALS['tmp_dir'];
}
$dirs = array( sys_get_temp_dir(), ini_get('upload_tmp_dir'), '/tmp', '/var/tmp', '/usr/tmp', '.' );
foreach ( $dirs as $dir ) {
if ( @is_writable( $dir ) ) {
$_GLOBALS['tmp_dir'] = $dir;
return $dir;
}
}
error_log( __FILE__ . ':' . __LINE__ . ' ERROR - could not find a writable temporary directory, check web server and PHP config' );
return false;
// return '/tmp';
}
if( ! defined( 'NULL_KEY') ) define( 'NULL_KEY', '00000000-0000-0000-0000-000000000000' );
if( ! defined( 'TPLINK_LOCAL') ) {
define( 'TPLINK_LOCAL', 1 ); // seconlife://Region/x/y/z
define( 'TPLINK_HG', 2 ); // seconlife://yourgrid.org:8002 Region/x/y/z
define( 'TPLINK_V3HG', 4 ); // the overcomplicated stuff!
define( 'TPLINK_HOP', 8 ); // hop://yourgrid.org:8002:Region/x/y/z
define( 'TPLINK_TXT', 16 ); // yourgrid.org:8002:Region/x/y/z
define( 'TPLINK_APPTP', 32 ); // secondlife:///app/teleport/yourgrid.org:8002:Region/x/y/z
define( 'TPLINK_MAP', 64 ); // secondlife:///app/map/yourgrid.org:8002:Region/x/y/z
define( 'TPLINK', pow( 2, 8 ) - 1 ); // all formats
define( 'TPLINK_DEFAULT', TPLINK_HOP ); // default
}
define( 'HELPERS_LOCALE_DIR', dirname( __DIR__ ) . '/languages' );
function os_cache_get( $key, $default = null ) {
global $oshelpers_cache;
return isset( $oshelpers_cache[$key] ) ? $oshelpers_cache[$key] : $default;
}
function os_cache_set( $key, $value, $expire = 0 ) {
global $oshelpers_cache;
$oshelpers_cache[$key] = $value;
}
/**
* OpenSim source to help further attempts to allow Hypergrid search results.
* Infouuid is a fake parcelid resolving to region handle and (region-level?)
* pos which might (or not) give enough information to allow hg results.
* 1. Link region locally with link-region (or directly in db?)
* 2. Use local link region handle (instead of remote one) to generate infouuid
* 3. Use local link Global pos instead of remote one
*/
//
// public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y)
// {
// byte[] bytes =
// {
// (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
// (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
// (byte)x, (byte)(x >> 8), 0, 0,
// (byte)y, (byte)(y >> 8), 0, 0 };
// return new UUID(bytes, 0);
// }
//
// public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y, uint z)
// {
// byte[] bytes =
// {
// (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
// (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
// (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8),
// (byte)y, (byte)(y >> 8), 0, 0 };
// return new UUID(bytes, 0);
// }

1
helper/economy/index.php Normal file
View File

@@ -0,0 +1 @@
<?php die();

191
helper/economy/landtool.php Normal file
View File

@@ -0,0 +1,191 @@
<?php
/**
* landtool.php
*
* Provides web tools for land sales operations
*
* Requires an OpenSimulator Money Server
* [DTL/NSL Money Server for OpenSim](http://www.nsl.tuis.ac.jp/xoops/modules/xpwiki/?OpenSim%2FMoneyServer)
* or [Gloebit module](http://dev.gloebit.com/opensim/configuration-instructions/)
*
* @package magicoli/opensim-helpers
* @author Gudule Lapointe <gudule@speculoos.world>
* @link https://github.com/magicoli/opensim-helpers
* @license AGPLv3
*
* Includes portions of code from
* Melanie Thielker and Teravus Ovares (http://opensimulator.org/)
* Fumi.Iseki for CMS/LMS '09 5/31
*/
require_once 'includes/config.php';
require_once 'includes/economy.php';
// No user serviceable parts below #####################
//
// The XMLRPC server object
//
$xmlrpc_server = xmlrpc_server_create();
//
// Land purchase sections
//
// Functions are called by the viewer directly.
//
//
// Land buying functions
//
xmlrpc_server_register_method( $xmlrpc_server, 'preflightBuyLandPrep', 'buy_land_prep' );
function buy_land_prep( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$sessionid = $req['secureSessionId'];
$amount = $req['currencyBuy'];
$billableArea = $req['billableArea'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
$ret = opensim_check_secure_session( $agentid, null, $sessionid );
if ( $ret ) {
$confirmvalue = currency_get_confirm_value( $ipAddress );
$membership_levels = array(
'levels' => array(
'id' => '00000000-0000-0000-0000-000000000000',
'description' => 'some level',
),
);
$landUse = array(
'upgrade' => false,
'action' => '' . CURRENCY_HELPER_URL . '',
);
$currency = array( 'estimatedCost' => currency_virtual_to_real( $amount ) );
$membership = array(
'upgrade' => false,
'action' => '' . CURRENCY_HELPER_URL . '',
'levels' => $membership_levels,
);
$response_xml = xmlrpc_encode(
array(
'success' => true,
'currency' => $currency,
'membership' => $membership,
'landUse' => $landUse,
'currency' => $currency,
'confirm' => $confirmvalue,
)
);
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "Unable to Authenticate\n\nClick URL for more info.",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Perform the buy (所持金が足りないとき)
//
xmlrpc_server_register_method( $xmlrpc_server, 'buyLandPrep', 'buy_land' );
function buy_land( $method_name, $params, $app_data ) {
$req = $params[0];
$agentid = $req['agentId'];
$sessionid = $req['secureSessionId'];
$amount = $req['currencyBuy'];
$cost = $req['estimatedCost'];
$billableArea = $req['billableArea'];
$confim = $req['confirm'];
$ipAddress = $_SERVER['REMOTE_ADDR'];
if ( $confim != currency_get_confirm_value( $ipAddress ) ) {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "\n\nMissmatch Confirm Value!!",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
$ret = opensim_check_secure_session( $agentid, null, $sessionid );
if ( $ret ) {
if ( $amount >= 0 ) {
if ( ! $cost ) {
$cost = currency_virtual_to_real( $amount );
}
if ( ! currency_process_transaction( $agentid, $cost, $ipAddress ) ) {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "\n\nThe gateway has declined your transaction. Please update your payment method AND try again later.",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
}
$enough_money = false;
$res = currency_add_money( $agentid, $amount, $sessionid );
if ( $res['success'] ) {
$enough_money = true;
}
if ( $enough_money ) {
$amount += currency_get_balance( $agentid );
currency_move_money( $agentid, null, $amount, 5002, 0, 'Land Purchase', 0, 0, $ipAddress );
currency_update_simulator_balance( $agentid, -1, $sessionid );
$response_xml = xmlrpc_encode( array( 'success' => true ) );
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "\n\nYou do not have sufficient funds for this purchase",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
}
}
} else {
$response_xml = xmlrpc_encode(
array(
'success' => false,
'errorMessage' => "\n\nUnable to Authenticate\n\nClick URL for more info.",
'errorURI' => '' . CURRENCY_HELPER_URL . '',
)
);
}
header( 'Content-type: text/xml' );
echo $response_xml;
return '';
}
//
// Process XMLRPC request
//
$request_xml = file_get_contents( 'php://input' );
// error_log("landtool.php: ".$request_xml);
xmlrpc_server_call_method( $xmlrpc_server, $request_xml, '' );
xmlrpc_server_destroy( $xmlrpc_server );