First commit of code for an OpenSimMutelist module

This commit is contained in:
Kevin Cozens
2017-12-03 15:36:21 -05:00
commit c516757762
7 changed files with 751 additions and 0 deletions

View File

@@ -0,0 +1,460 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``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 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.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Xml;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using log4net;
using Mono.Addins;
using Nini.Config;
using Nwc.XmlRpc;
using OpenMetaverse;
[assembly: Addin("OpenSimMutelist", OpenSim.VersionInfo.VersionNumber + "0.1")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
[assembly: AddinDescription("OpenSimMutelist module.")]
[assembly: AddinAuthor("Kevin Cozens")]
namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "OpenSimMutelist")]
public class MuteListModule : ISharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private bool enabled = true;
private List<Scene> m_SceneList = new List<Scene>();
private string m_MuteListURL = String.Empty;
IUserManagement m_uMan;
IUserManagement UserManagementModule
{
get
{
if (m_uMan == null)
m_uMan = m_SceneList[0].RequestModuleInterface<IUserManagement>();
return m_uMan;
}
}
#region IRegionModuleBase implementation
public void Initialise(IConfigSource config)
{
IConfig cnf = config.Configs["Messaging"];
if (cnf == null)
{
enabled = false;
return;
}
if (cnf != null && cnf.GetString("MuteListModule", "None") !=
"OpenSimMutelist")
{
enabled = false;
return;
}
m_MuteListURL = cnf.GetString("MuteListURL", "");
if (m_MuteListURL == "")
{
m_log.Error("[OS MUTELIST] Module was enabled, but no URL is given, disabling");
enabled = false;
return;
}
m_log.Info("[OS MUTELIST] Mute list enabled");
}
public void AddRegion(Scene scene)
{
if (!enabled)
return;
lock (m_SceneList)
{
m_SceneList.Add(scene);
}
}
public void RegionLoaded(Scene scene)
{
if (!enabled)
return;
scene.EventManager.OnNewClient += OnNewClient;
scene.EventManager.OnClientClosed += OnClientClosed;
}
public void RemoveRegion(Scene scene)
{
if (!enabled)
return;
lock (m_SceneList)
{
scene.EventManager.OnNewClient -= OnNewClient;
scene.EventManager.OnClientClosed -= OnClientClosed;
m_SceneList.Remove(scene);
}
}
public void PostInitialise()
{
}
public string Name
{
get { return "MuteListModule"; }
}
public Type ReplaceableInterface
{
get { return null; }
}
public void Close()
{
}
#endregion
private void OnNewClient(IClientAPI client)
{
client.OnMuteListRequest += OnMuteListRequest;
client.OnUpdateMuteListEntry += OnUpdateMuteListEntry;
client.OnRemoveMuteListEntry += OnRemoveMuteListEntry;
}
private void OnClientClosed(UUID agentID, Scene scene)
{
ScenePresence scenePresence = scene.GetScenePresence(agentID);
IClientAPI client = scenePresence.ControllingClient;
if (client != null)
{
client.OnMuteListRequest -= OnMuteListRequest;
client.OnUpdateMuteListEntry -= OnUpdateMuteListEntry;
client.OnRemoveMuteListEntry -= OnRemoveMuteListEntry;
}
}
//
// Make external XMLRPC request
//
private Hashtable GenericXMLRPCRequest(Hashtable ReqParams, string method, string server)
{
ArrayList SendParams = new ArrayList();
SendParams.Add(ReqParams);
// Send Request
XmlRpcResponse Resp;
try
{
XmlRpcRequest Req = new XmlRpcRequest(method, SendParams);
Resp = Req.Send(server, 30000);
}
catch (WebException ex)
{
m_log.ErrorFormat("[OS MUTELIST]: Unable to connect to mutelist " +
"server {0}. Exception {1}", m_MuteListURL, ex);
Hashtable ErrorHash = new Hashtable();
ErrorHash["success"] = false;
ErrorHash["errorMessage"] = "Unable to connect to mutelist server at this time. ";
ErrorHash["errorURI"] = "";
return ErrorHash;
}
catch (SocketException ex)
{
m_log.ErrorFormat(
"[OS MUTELIST]: Unable to connect to mutelist server {0}. Method {1}, params {2}. " +
"Exception {3}", m_MuteListURL, method, ReqParams, ex);
Hashtable ErrorHash = new Hashtable();
ErrorHash["success"] = false;
ErrorHash["errorMessage"] = "Unable to connect to mutelist server at this time. ";
ErrorHash["errorURI"] = "";
return ErrorHash;
}
catch (XmlException ex)
{
m_log.ErrorFormat(
"[OS MUTELIST]: Unable to connect to mutelist server {0}. Method {1}, params {2}. " +
"Exception {3}", m_MuteListURL, method, ReqParams, ex);
Hashtable ErrorHash = new Hashtable();
ErrorHash["success"] = false;
ErrorHash["errorMessage"] = "Unable to connect to mutelist server at this time. ";
ErrorHash["errorURI"] = "";
return ErrorHash;
}
if (Resp.IsFault)
{
Hashtable ErrorHash = new Hashtable();
ErrorHash["success"] = false;
ErrorHash["errorMessage"] = "Unable to process mutelist response at this time. ";
ErrorHash["errorURI"] = "";
return ErrorHash;
}
Hashtable RespData = (Hashtable)Resp.Value;
return RespData;
}
private bool GetUserMutelistServerURI(UUID userID, out string serverURI)
{
IUserManagement uManage = UserManagementModule;
if (!uManage.IsLocalGridUser(userID))
{
serverURI = uManage.GetUserServerURL(userID, "MutelistServerURI");
// Is Foreign
return true;
}
else
{
serverURI = m_MuteListURL;
// Is local
return false;
}
}
private void OnMuteListRequest(IClientAPI client, uint crc)
{
string mutelist;
IXfer xfer = client.Scene.RequestModuleInterface<IXfer>();
if (xfer == null)
return;
m_log.DebugFormat("[OS MUTELIST] Got mute list request");
string filename = "mutes"+client.AgentId.ToString();
Hashtable ReqHash = new Hashtable();
ReqHash["avataruuid"] = client.AgentId.ToString();
ReqHash["crc"] = crc.ToString();
string serverURI = String.Empty;
GetUserMutelistServerURI(client.AgentId, out serverURI);
Hashtable result = GenericXMLRPCRequest(ReqHash,
"mutelist_request", serverURI);
if (!Convert.ToBoolean(result["success"]))
mutelist = null;
else
mutelist = result["mutelist"].ToString();
if (mutelist == null || mutelist.Length == 0)
{
xfer.AddNewFile(filename, new Byte[0]);
}
else
{
Byte[] filedata = Util.UTF8.GetBytes(mutelist);
uint dataCrc = Crc32.Compute(filedata);
if (dataCrc == crc)
{
client.SendUseCachedMuteList();
return;
}
xfer.AddNewFile(filename, filedata);
}
client.SendMuteListUpdate(filename);
}
private void OnUpdateMuteListEntry(IClientAPI client, UUID MuteID, string Name, int type, uint flags)
{
m_log.DebugFormat("[OS MUTELIST] Got mute list update request");
Hashtable ReqHash = new Hashtable();
ReqHash["avataruuid"] = client.AgentId.ToString();
ReqHash["muteuuid"] = MuteID.ToString();
ReqHash["name"] = Name.ToString();
ReqHash["type"] = type.ToString();
ReqHash["flags"] = flags.ToString();
string serverURI = String.Empty;
GetUserMutelistServerURI(client.AgentId, out serverURI);
Hashtable result = GenericXMLRPCRequest(ReqHash,
"mutelist_update", serverURI);
if (!Convert.ToBoolean(result["success"]))
{
client.SendAgentAlertMessage(
result["errorMessage"].ToString(), false);
}
}
private void OnRemoveMuteListEntry(IClientAPI client, UUID MuteID, string Name)
{
m_log.DebugFormat("[OS MUTELIST] Got mute list removal request");
Hashtable ReqHash = new Hashtable();
ReqHash["avataruuid"] = client.AgentId.ToString();
ReqHash["muteuuid"] = MuteID.ToString();
ReqHash["name"] = Name.ToString();
string serverURI = String.Empty;
GetUserMutelistServerURI(client.AgentId, out serverURI);
Hashtable result = GenericXMLRPCRequest(ReqHash,
"mutelist_remove", serverURI);
if (!Convert.ToBoolean(result["success"]))
{
client.SendAgentAlertMessage(
result["errorMessage"].ToString(), false);
}
}
}
public class Crc32 : HashAlgorithm
{
public const UInt32 DefaultPolynomial = 0xedb88320;
public const UInt32 DefaultSeed = 0xffffffff;
private UInt32 hash;
private UInt32 seed;
private UInt32[] table;
private static UInt32[] defaultTable;
public Crc32()
{
table = InitializeTable(DefaultPolynomial);
seed = DefaultSeed;
Initialize();
}
public Crc32(UInt32 polynomial, UInt32 seed)
{
table = InitializeTable(polynomial);
this.seed = seed;
Initialize();
}
public override void Initialize()
{
hash = seed;
}
protected override void HashCore(byte[] buffer, int start, int length)
{
hash = CalculateHash(table, hash, buffer, start, length);
}
protected override byte[] HashFinal()
{
byte[] hashBuffer = UInt32ToBigEndianBytes(~hash);
this.HashValue = hashBuffer;
return hashBuffer;
}
public override int HashSize
{
get { return 32; }
}
public static UInt32 Compute(byte[] buffer)
{
return ~CalculateHash(InitializeTable(DefaultPolynomial), DefaultSeed, buffer, 0, buffer.Length);
}
public static UInt32 Compute(UInt32 seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(DefaultPolynomial), seed, buffer, 0, buffer.Length);
}
public static UInt32 Compute(UInt32 polynomial, UInt32 seed, byte[] buffer)
{
return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length);
}
private static UInt32[] InitializeTable(UInt32 polynomial)
{
if (polynomial == DefaultPolynomial && defaultTable != null)
return defaultTable;
UInt32[] createTable = new UInt32[256];
for (int i = 0; i < 256; i++)
{
UInt32 entry = (UInt32)i;
for (int j = 0; j < 8; j++)
if ((entry & 1) == 1)
entry = (entry >> 1) ^ polynomial;
else
entry = entry >> 1;
createTable[i] = entry;
}
if (polynomial == DefaultPolynomial)
defaultTable = createTable;
return createTable;
}
private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, byte[] buffer, int start, int size)
{
UInt32 crc = seed;
for (int i = start; i < size; i++)
unchecked
{
crc = (crc >> 8) ^ table[buffer[i] ^ crc & 0xff];
}
return crc;
}
private byte[] UInt32ToBigEndianBytes(UInt32 x)
{
return new byte[] {
(byte)((x >> 24) & 0xff),
(byte)((x >> 16) & 0xff),
(byte)((x >> 8) & 0xff),
(byte)(x & 0xff) };
}
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" ?>
<Project frameworkVersion="v4_0" name="OpenSimMutelist.Modules" path="addon-modules/OpenSimMutelist/Modules" type="Library">
<Configuration name="Debug">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<Configuration name="Release">
<Options>
<OutputPath>../../../bin/</OutputPath>
</Options>
</Configuration>
<ReferencePath>../../../bin/</ReferencePath>
<Reference localCopy="false" name="System"/>
<Reference name="System.Xml"/>
<Reference name="OpenMetaverseTypes"/>
<Reference name="OpenSim.Framework"/>
<Reference name="OpenSim.Region.Framework"/>
<Reference name="OpenSim.Region.Framework.Interfaces"/>
<Reference name="OpenSim.Region.Framework.Scenes"/>
<Reference name="OpenSim.Services.Interfaces"/>
<Reference name="log4net"/>
<Reference name="Mono.Addins"/>
<Reference name="Nini"/>
<Reference name="XMLRPC"/>
<Files>
<Match pattern="*.cs" recurse="true"/>
</Files>
</Project>

81
README Normal file
View File

@@ -0,0 +1,81 @@
OpenSimMutelist add-on module for Open Simulator
Requirements
A webserver with PHP 4.3 (or later) and with XMLRPC support.
NOTES: Not all webservers include XMLRPC support in their default installation.
This module may work with 0.8.2 but it has only been tested with 0.9.
About the files
README - The file you are currently reading
bin/OpenSimMutelist.Modules.dll - A pre-compiled module you can use
OpenSimMutelist/ - Source code for OpenSim mutelist module
webroot/*.php - Files to install on webserver
webroot/sql/osmutelist.sql - This will create the needed database tables
How it works
If you are looking for a detailed explanation of how the add-on mutelist system
works you should study the contents of the files which accompany this README
file. What follows is a general overview of the mutelist package to give you
an idea of how the pieces fit together in case you have any problems.
There are three main components to OpenSimMutelist which are a database table,
a DLL file, and a couple of PHP files.
Compiling the module
The easiest way to create the DLL file needed by OpenSim is to add the
OpenSimMutelist C# source file to the source tree of OpenSim so it will
be compiled at the same time that OpenSim is compiled.
Copy the OpenSimMutelist directory in to the addon-modules directory of your
OpenSim source tree. After you have done that, compile OpenSim in the usual
way (runprebuild and xbuild) to create the DLL file. When xbuild finishes,
you will have an OpenSimMutelist.Modules.dll file in the main bin directory
of your OpenSim source tree along with a matching .pdb (or .mdb) file. These
two files will be copied to the bin directory of your OpenSim instances
during the installation or update steps.
First time installation and configuration
The first installation step is the creation of a database table that will
hold the mutelist data. You can include the table in an existing database
you are already using with Open Simulator or you can create a database that
will only be used by the mutelist module.
Once you know the name of the database you will be using for the mutelist
data you can use osmutelist.sql (located in the webroot/sql directory) to
create the required table in the database. The name of the database will
be needed in a later step when you configure one of the PHP files.
Copy the PHP files (located in the webroot directory) to your webserver.
Remember to set the ownership and permissions on the files so the webserver
may access the files. Use a text editor to open databaseinfo.php and enter
the name or IP address of the database server, the name of the database,
and the user name and password required to access the database.
The next part of the installation process is to set up and configure your
OpenSim instances.
Copy the OpenSimMutelist.Module.dll file created during the compile steps
(above) to the bin directory of each of your OpenSim instances. The next
part of the installation process requires some changes to the OpenSim.ini
file in each of your OpenSim instances.
Open the OpenSim.ini in your favourite editorg. Find the [Messaging] section.
Set MuteListModule to OpenSimMutelist. Set MuteListURL to a URL that points
to the mutelist.php file on your webserver.
If you make these changes to running instances you will need to restart them
before the change to the OpenSim.ini will take effect and this module will be
used to help track mutelist changes.

BIN
bin/OpenSimMutelist.Modules.dll Executable file

Binary file not shown.

6
webroot/databaseinfo.php Normal file
View File

@@ -0,0 +1,6 @@
<?php
$DB_HOST = "localhost";
$DB_USER = "root";
$DB_PASSWORD = "";
$DB_NAME = "osmodules";
?>

134
webroot/mutelist.php Normal file
View File

@@ -0,0 +1,134 @@
<?php
include("settings/databaseinfo-modules.php");
// Attempt to connect to the database with the mutelist table
try {
$db = new PDO("mysql:host=$DB_HOST;dbname=$DB_NAME", $DB_USER, $DB_PASSWORD);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo "Error connecting to the database with the mutelist table\n";
file_put_contents('PDOErrors.txt', $e->getMessage() . "\n-----\n", FILE_APPEND);
exit;
}
###################### No user serviceable parts below #####################
#
# The XMLRPC server object
#
$xmlrpc_server = xmlrpc_server_create();
#
# Return list of muted agents and objects
#
xmlrpc_server_register_method($xmlrpc_server, "mutelist_request",
"mutelist_request");
function mutelist_request($method_name, $params, $app_data)
{
global $db;
$req = $params[0];
$avatarUUID = $req['avataruuid'];
$crc = $req['crc'];
$query = $db->prepare("SELECT * FROM mutelist WHERE AgentID = ?");
$result = $query->execute( array($avatarUUID) );
$mutelist = "";
if ($query->rowCount() > 0)
{
while ($row = $query->fetch(PDO::FETCH_ASSOC))
{
$mutelist .= $row["type"] . " ";
$mutelist .= $row["MuteID"] . " ";
$mutelist .= $row["MuteName"] . "|";
$mutelist .= $row["flags"] . "\n";
}
}
$response_xml = xmlrpc_encode(array(
'success' => $result,
'errorMessage' => $query->errorInfo(),
'mutelist' => $mutelist
));
print $response_xml;
}
#
# Remove an event notify reminder request
#
xmlrpc_server_register_method($xmlrpc_server, "mutelist_update",
"mutelist_update");
function mutelist_update($method_name, $params, $app_data)
{
global $db;
$req = $params[0];
$avatarUUID = $req['avataruuid'];
$muteUUID = $req['muteuuid'];
$name = $req['name'];
$type = $req['type'];
$flags = $req['flags'];
$query = $db->prepare("INSERT INTO mutelist VALUES (?, ?, ?, ?, ?, NOW())");
$result = $query->execute(
array($avatarUUID, $muteUUID, $name, $type, $flags) );
$response_xml = xmlrpc_encode(array(
'success' => $result,
'errorMessage' => $query->errorInfo()
));
print $response_xml;
}
#
# Remove an event notify reminder request
#
xmlrpc_server_register_method($xmlrpc_server, "mutelist_remove",
"mutelist_remove");
function mutelist_remove($method_name, $params, $app_data)
{
global $db;
$req = $params[0];
$avatarUUID = $req['avataruuid'];
$muteUUID = $req['muteuuid'];
$name = $req['name'];
$query = $db->prepare("DELETE FROM mutelist WHERE" .
" AgentID = ? AND MuteID = ?");
$result = $query->execute( array($avatarUUID, $muteUUID) );
$response_xml = xmlrpc_encode(array(
'success' => $result,
'errorMessage' => $query->errorInfo()
));
print $response_xml;
}
#
# Process the request
#
$request_xml = file_get_contents("php://input");
xmlrpc_server_call_method($xmlrpc_server, $request_xml, '');
xmlrpc_server_destroy($xmlrpc_server);
?>

39
webroot/sql/mutelist.sql Normal file
View File

@@ -0,0 +1,39 @@
-- phpMyAdmin SQL Dump
-- version 4.5.4.1deb2ubuntu2
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Generation Time: Nov 22, 2017 at 11:12 PM
-- Server version: 5.7.20
-- PHP Version: 7.0.22-0ubuntu0.16.04.1
--
-- Database: `osmodules`
--
-- --------------------------------------------------------
--
-- Table structure for table `mutelist`
--
CREATE TABLE `mutelist` (
`AgentID` char(36) COLLATE utf8_unicode_ci NOT NULL,
`MuteID` char(36) COLLATE utf8_unicode_ci NOT NULL,
`MuteName` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`type` int(11) UNSIGNED NOT NULL,
`flags` int(11) UNSIGNED NOT NULL,
`Stamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `mutelist`
--
ALTER TABLE `mutelist`
ADD UNIQUE KEY `AgentID_2` (`AgentID`,`MuteID`) USING BTREE,
ADD KEY `AgentID` (`AgentID`) USING BTREE;