Initial push
This commit is contained in:
38
README.md
38
README.md
@@ -1,2 +1,36 @@
|
||||
# empty-trash
|
||||
Clickable terminal for emptying trash in OpenSim
|
||||
# Clickable terminal for emptying trash in OpenSim
|
||||
|
||||
This is to secure your grid users' inventories on the hypergrid.
|
||||
Especially important when your grid uses hypergrid 1.0!
|
||||
|
||||
In robust.ini set AllowDelete to false:
|
||||
```
|
||||
[InventoryService]
|
||||
LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService"
|
||||
|
||||
; Will calls to purge folders (empty trash) and immediately delete/update items or folders (not move to trash first) succeed?
|
||||
; If this is set to false then some other arrangement must be made to perform these operations if necessary.
|
||||
AllowDelete = false
|
||||
```
|
||||
|
||||
If the php script only listens on your private subnet, take note that
|
||||
by default, llHttpRequest (which the terminal uses) can't request webpages
|
||||
from private subnets. There are two ways to address this:
|
||||
|
||||
1. Insecure: Remove your private subnet from the blacklist in OpenSim.ini:
|
||||
```
|
||||
[Network]
|
||||
; 192.168.0.0/16 removed:
|
||||
OutboundDisallowForUserScripts = 0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32
|
||||
```
|
||||
Caution, this would allow any lsl script (even in a visitor's attachment) to
|
||||
access any webserver in that private subnet, for example your NAS storage!
|
||||
|
||||
2. Secure: Use [opensim-lickx](https://github.com/lickx/opensim-lickx) instead of stock OpenSim.
|
||||
This (only) allows requests from the /lslhttp folder even on blacklisted private networks.
|
||||
See commit 7503d1a23fd4a45742295cdb44223d2f7ac4033a if you would like to apply this
|
||||
to your own OpenSim fork.
|
||||
|
||||
|
||||
If the php script listens on the public ip (not recommended), ALLOWED_HOST should
|
||||
be filled in config.php, and the relevant section uncommented in trash.php.
|
||||
|
||||
42
intranet-lsl
Normal file
42
intranet-lsl
Normal file
@@ -0,0 +1,42 @@
|
||||
# Example for making a webserver on the private subnet with nginx on port 80
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
# the lsl script will do a llHttpRequest to 192.168.0.10/lslhttp/trash.php
|
||||
server_name 192.168.0.10;
|
||||
|
||||
access_log /var/log/nginx/intranet-lsl_access.log;
|
||||
error_log /var/log/nginx/intranet-lsl_error.log;
|
||||
|
||||
# within this folder, you'd make the lslhttp folder:
|
||||
root /var/www/intranet-lsl;
|
||||
|
||||
# Add index.php to the list if you are using PHP
|
||||
index index.html index.htm index.php;
|
||||
|
||||
location / {
|
||||
# First attempt to serve request as file, then
|
||||
# as directory, then fall back to displaying a 404.
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
# pass PHP scripts to FastCGI server
|
||||
#
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
|
||||
# With php-fpm (or other unix sockets):
|
||||
fastcgi_pass unix:/run/php/php-fpm.sock;
|
||||
# With php-cgi (or other tcp sockets):
|
||||
#fastcgi_pass 127.0.0.1:9000;
|
||||
}
|
||||
|
||||
# deny access to .htaccess files, if Apache's document root
|
||||
# concurs with nginx's one
|
||||
#
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
|
||||
43
inworld/terminal.lsl
Normal file
43
inworld/terminal.lsl
Normal file
@@ -0,0 +1,43 @@
|
||||
key g_kHttpRequest;
|
||||
key g_kUser = NULL_KEY;
|
||||
|
||||
default
|
||||
{
|
||||
state_entry()
|
||||
{
|
||||
}
|
||||
|
||||
touch_end(integer i)
|
||||
{
|
||||
key kID = llDetectedKey(0);
|
||||
if (kID == NULL_KEY) return;
|
||||
if (g_kUser != NULL_KEY) return; // in use by another user
|
||||
|
||||
string sName = llKey2Name(kID);
|
||||
if (~llSubStringIndex(sName, "@")) {
|
||||
llDialog(kID, "This terminal can only be used by local grid residents", ["OK"], -1234);
|
||||
return;
|
||||
}
|
||||
|
||||
g_kUser = kID;
|
||||
llSetText("In use by "+sName, <1,1,1>, 1);
|
||||
string sBody = "userID="+(string)g_kUser;
|
||||
g_kHttpRequest = llHTTPRequest(
|
||||
"http://192.168.0.10/lslhttp/trash.php",
|
||||
[HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
|
||||
sBody);
|
||||
}
|
||||
|
||||
http_response(key kHttpRequest, integer iStatus, list lMetadata, string sBody)
|
||||
{
|
||||
if (kHttpRequest != g_kHttpRequest) return;
|
||||
|
||||
if (iStatus == 200) {
|
||||
llDialog(g_kUser, sBody, ["OK"], -1234);
|
||||
} else {
|
||||
llDialog(g_kUser, "Status: "+(string)iStatus, ["OK"], -1234);
|
||||
}
|
||||
g_kUser = NULL_KEY;
|
||||
llSetText("", <1,1,1>, 1);
|
||||
}
|
||||
}
|
||||
9
lslhttp/index.html
Normal file
9
lslhttp/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>lsl</title>
|
||||
</head>
|
||||
<body>
|
||||
<p></p>
|
||||
</body>
|
||||
</html>
|
||||
135
lslhttp/trash.php
Normal file
135
lslhttp/trash.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
include("trash_config.php");
|
||||
|
||||
// Don't change below this line -----------------------------------------
|
||||
$NULL_KEY = "00000000-0000-0000-0000-000000000000";
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
||||
http_response_code(404);
|
||||
die();
|
||||
}
|
||||
|
||||
// Only inworld objects owned by ALLOWED_STAFF may call this script
|
||||
$objectOwner = isset($_SERVER["HTTP_X_SECONDLIFE_OWNER_KEY"]) ? $_SERVER["HTTP_X_SECONDLIFE_OWNER_KEY"] : $NULL_KEY;
|
||||
if ($objectOwner != $ALLOWED_STAFF || $objectOwner == $NULL_KEY || strlen($objectOwner) != 36) {
|
||||
http_response_code(401);
|
||||
die();
|
||||
}
|
||||
|
||||
// COMMENT OUT WHEN ONLY LISTENING ON A PRIVATE IP, SUCH AS 192.168.0.10
|
||||
// NEEDED WHEN LISTENING ON A PUBLIC IP! FILL IN ALLOWED_HOST ABOVE WITH THE
|
||||
// FQDN OF THE SERVER THAT RUNS THE SIM WITH THE TRASH TERMINAL.
|
||||
/*
|
||||
// Only grid-local inworld objects hosted on ALLOWED_HOST may call this script
|
||||
$hostip = isset($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : "unknown";
|
||||
if ($hostip == "unknown") {
|
||||
// couldn't resolve client ip
|
||||
http_response_code(401);
|
||||
die();
|
||||
} else {
|
||||
$hostname = gethostbyaddr($hostip);
|
||||
if (!str_ends_with($hostname, $ALLOWED_HOST)) {
|
||||
// end of hostname DOESN'T match $ALLOWED_HOST
|
||||
http_response_code(401);
|
||||
die();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Get the user of whom to empty trash for
|
||||
$userID = isset($_POST["userID"]) ? $_POST["userID"] : $NULL_KEY;
|
||||
if ($userID == $NULL_KEY || strlen($userID) != 36) {
|
||||
http_response_code(403);
|
||||
die();
|
||||
}
|
||||
|
||||
function GetSubFolders($parentFolder)
|
||||
{
|
||||
global $conn, $userID;
|
||||
$sql = "SELECT folderName, folderID FROM inventoryfolders WHERE parentFolderID=? AND agentID=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("ss", $parentFolder, $userID);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$folderList = array();
|
||||
if ($result->num_rows > 0) {
|
||||
for ($i = 0; $i < $result->num_rows; $i++) {
|
||||
$row = $result->fetch_assoc();
|
||||
$folderName = $row["folderName"];
|
||||
$folderID = $row["folderID"];
|
||||
//print("Adding folder $folderName\n");
|
||||
array_push($folderList, $folderID);
|
||||
$subFolderIDs = GetSubFolders($folderID);
|
||||
if (!empty($subFolderIDs)) {
|
||||
$folderList = array_merge($folderList, $subFolderIDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
$stmt->close();
|
||||
return $folderList;
|
||||
}
|
||||
|
||||
function DeleteSubFolders($parentFolder)
|
||||
{
|
||||
global $conn, $userID;
|
||||
$sql = "DELETE FROM inventoryfolders WHERE parentFolderID=? AND agentID=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("ss", $parentFolder, $userID);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
function DeleteItems($parentFolder)
|
||||
{
|
||||
global $conn, $userID;
|
||||
$sql = "DELETE FROM inventoryitems WHERE parentFolderID=? AND avatarID=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("ss", $parentFolder, $userID);
|
||||
$stmt->execute();
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
// Create and check db connection
|
||||
$conn = new mysqli($dbhost, $dbuser, $dbpass, $dbname);
|
||||
if ($conn->connect_error) {
|
||||
die("Database connection failed: " . $conn->connect_error);
|
||||
}
|
||||
|
||||
// Get trash folder UUID
|
||||
$sql = "SELECT folderID FROM inventoryfolders WHERE type=14 AND agentID=?";
|
||||
$stmt = $conn->prepare($sql);
|
||||
$stmt->bind_param("s", $userID);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$stmt->close();
|
||||
if ($result->num_rows > 0) {
|
||||
$row = $result->fetch_assoc();
|
||||
$trashFolderID = $row["folderID"];
|
||||
//print("Trash folder UUID: " . $trashFolderID . "\n");
|
||||
$allTrashFolders = GetSubFolders($trashFolderID);
|
||||
//print("Found " . sizeof($allTrashFolders) . " total folders in trash\n");
|
||||
|
||||
//print("Emptying trash...\n");
|
||||
|
||||
// Delete items and folders not directly under Trash rootfolder
|
||||
if (sizeof($allTrashFolders) > 0) {
|
||||
// Delete items and folders in every subfolder of Trash
|
||||
foreach($allTrashFolders as $folder) {
|
||||
DeleteItems($folder);
|
||||
DeleteSubFolders($folder);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete items and folders directly under Trash rootfolder
|
||||
DeleteItems($trashFolderID);
|
||||
DeleteSubFolders($trashFolderID);
|
||||
http_response_code(200);
|
||||
print("\nTrash has been emptied.\n\nYou may now also empty the Trash folder in your viewer inventory.");
|
||||
} else {
|
||||
http_response_code(200);
|
||||
print("\nERROR: No trash folder found.\n\nContact an admin!");
|
||||
}
|
||||
|
||||
$conn->close();
|
||||
|
||||
?>
|
||||
13
lslhttp/trash_config.php
Normal file
13
lslhttp/trash_config.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
$dbhost = "localhost";
|
||||
$dbuser = "opensim";
|
||||
$dbpass = "password";
|
||||
$dbname = "grid";
|
||||
|
||||
// Who must be the object owner that we allow calls from? Fill in the avi key:
|
||||
$ALLOWED_STAFF = "777fef4a-4cc9-4637-ade2-8a80476e251c";
|
||||
|
||||
// Which host (running the sim with the terminal) do we allow calls from?
|
||||
$ALLOWED_HOST = "mygrid.example.com";
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user