diff --git a/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/IMoneyManager.cs b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/IMoneyManager.cs
new file mode 100644
index 0000000..def1de6
--- /dev/null
+++ b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/IMoneyManager.cs
@@ -0,0 +1,135 @@
+/*
+ * 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 OpenSim 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.
+
+ Funktion
+ Die Datei definiert das C#-Interface IMoneyManager.
+ Sie legt die Methoden fest, die eine Klasse zur Verwaltung von Geldtransaktionen, Guthaben und Benutzerinformationen implementieren muss.
+ Typische Aufgaben des Interfaces:
+ Abfragen und Ändern von Benutzer-Guthaben (getBalance, withdrawMoney, giveMoney)
+ Hinzufügen und Abfragen von Transaktionen (addTransaction, FetchTransaction)
+ Verwaltung von Benutzern (addUser, addUserInfo, fetchUserInfo, updateUserInfo)
+ Validierung und Ablauf von Transaktionen (ValidateTransfer, SetTransExpired)
+
+NullPointer-Checks & Fehlerquellen
+
+ Interface selbst:
+ Das Interface enthält keine Implementierung. Es ist lediglich eine Methodendefinition. Das bedeutet:
+ Es gibt keine Logik, also auch keine Möglichkeit für NullPointer-Fehler innerhalb dieser Datei.
+ Fehlerquellen oder NullPointer-Probleme hängen ausschließlich von den konkreten Implementierungen dieser Methoden ab.
+
+ Parameter:
+ Die Methoden nehmen meist Strings, UUIDs, Ints und komplexe Typen wie TransactionData oder UserInfo als Parameter.
+ In einer späteren Implementierung sollte geprüft werden, ob übergebene Objekte (z.B. user, transaction) null sind, bevor darauf zugegriffen wird.
+ Rückgabewerte wie TransactionData oder UserInfo könnten null sein, wenn nichts gefunden wird – auch das ist Sache der Implementierung.
+
+ Rückgabewerte:
+ Methoden, die Objekte zurückgeben (FetchTransaction, fetchUserInfo), haben keine Angaben, ob sie null zurückgeben dürfen.
+ Das sollte dokumentiert oder in der Implementierung behandelt werden.
+
+Zusammenfassung
+ NullPointer-Gefahr:
+ Im Interface selbst nicht möglich, da keine Ausführung erfolgt.
+ Fehlerquellen:
+ Keine im Interface; alle Fehlerquellen und NullPointer-Risiken entstehen erst in den Klassen, die dieses Interface implementieren.
+ Funktion:
+ Definition aller nötigen Methoden zur Verwaltung von Geldtransaktionen und Benutzerinformationen im OpenSim-Kontext.
+
+Fazit:
+Die Datei IMoneyManager.cs ist ein reines Methoden-Interface und enthält keine Logik, die zu NullPointer-Fehlern führen kann.
+Typische Fehlerquellen und NullPointer sollten in den konkreten Implementierungen der Methoden sorgfältig behandelt werden (z.B. Nullprüfungen auf Parameter, Rückgabewerte).
+ */
+
+#pragma warning disable IDE1006
+
+using OpenMetaverse;
+
+namespace OpenSim.Data.MySQL.MySQLMoneyDataWrapper
+{
+ public interface IMoneyManager
+ {
+ /// Gets the balance.
+ /// The user identifier.
+ int getBalance(string userID);
+
+ /// Withdraws the money.
+ /// The transaction identifier.
+ /// The sender identifier.
+ /// The amount.
+ bool withdrawMoney(UUID transactionID, string senderID, int amount);
+
+ /// Gives the money.
+ /// The transaction identifier.
+ /// The receiver identifier.
+ /// The amount.
+ bool giveMoney(UUID transactionID, string receiverID, int amount);
+
+ /// Adds the transaction.
+ /// The transaction.
+ bool addTransaction(TransactionData transaction);
+
+ /// Updates the transaction status.
+ /// The transaction identifier.
+ /// The status.
+ /// The description.
+ bool updateTransactionStatus(UUID transactionID, int status, string description);
+
+ /// Fetches the transaction.
+ /// The transaction identifier.
+ TransactionData FetchTransaction(UUID transactionID);
+
+ /// Fetches the transaction.
+ /// The user identifier.
+ /// The start time.
+ /// The end time.
+ /// The index.
+ /// The ret number.
+ TransactionData[] FetchTransaction(string userID, int startTime, int endTime, uint index, uint retNum);
+
+ /// Gets the transaction number.
+ /// The user identifier.
+ /// The start time.
+ /// The end time.
+ int getTransactionNum(string userID, int startTime, int endTime);
+
+ /// Adds the user.
+ /// The user identifier.
+ /// The balance.
+ /// The status.
+ /// The type.
+ bool addUser(string userID, int balance, int status, int type);
+
+ /// Sets the trans expired.
+ /// The dead time.
+ bool SetTransExpired(int deadTime);
+
+ /// Validates the transfer.
+ /// The secure code.
+ /// The transaction identifier.
+ bool ValidateTransfer(string secureCode, UUID transactionID);
+
+ /// Adds the user information.
+ /// The user.
+ bool addUserInfo(UserInfo user);
+
+ /// Fetches the user information.
+ /// The user identifier.
+ UserInfo fetchUserInfo(string userID);
+
+ /// Updates the user information.
+ /// The user.
+ bool updateUserInfo(UserInfo user);
+ }
+}
diff --git a/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/MySQLMoneyManager.cs b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/MySQLMoneyManager.cs
new file mode 100644
index 0000000..db06f4b
--- /dev/null
+++ b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/MySQLMoneyManager.cs
@@ -0,0 +1,1938 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/, http://www.nsl.tuis.ac.jp/ 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 OpenSim 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.
+
+Funktion
+Die Klasse MySQLMoneyManager implementiert das Interface IMoneyManager und ist das zentrale Datenbank-Backend für das OpenSim-Money-Modul (MySQL). Sie verwaltet:
+
+ Verbindungen zur MySQL-Datenbank
+ Kontostände (balances)
+ Transaktionen (transactions)
+ Benutzerdaten (userinfo)
+ Verkaufstatistiken (totalsales)
+ Methoden zum Ein-/Auszahlen, Übertragen, Anlegen und Aktualisieren von Werten
+
+Null Pointer Checks & Fehlerquellen
+Initialisierung und DB-Verbindung
+ Die Constructoren prüfen per .Any(p => string.IsNullOrEmpty(p)), ob alle benötigten Parameter gesetzt sind. Bei Fehlwerten gibt es einen klaren Exception-Throw.
+ Beim Öffnen der Datenbankverbindung wird jeder Fehler per Exception behandelt.
+ In allen Methoden, die auf die DB-Verbindung zugreifen, wird dbcon verwendet, das zentral initialisiert wird.
+
+Datenbankoperationen
+ Try-Catch-Blöcke sind an allen kritischen Stellen vorhanden, etwa beim Table-Setup, bei der Reconnect-Logik und bei Datenbankzugriffen.
+ Nach jedem Datenbankzugriff wird das Command-Objekt mit Dispose() freigegeben.
+ Beim Lesen von Datenbankwerten wird mit DBNull geprüft, ob ein Wert gesetzt ist, bevor er als String oder int gecastet wird.
+ Bei Methoden, die Objekte zurückgeben (z.B. FetchTransaction, fetchUserInfo), wird im Fehlerfall null zurückgegeben und im Catch geloggt.
+
+Null Pointer Checks
+ Bei Methoden wie addTransaction werden Objekteigenschaften vor Benutzung auf null geprüft und ggf. mit Defaultwerten gefüllt.
+ Bei Übergabeobjekten wie UserInfo wird vor dem Zugriff geprüft, ob z.B. Avatar null ist.
+ Rückgabewerte wie bei fetchUserInfo werden nur dann zurückgegeben, wenn die UserID wirklich gefunden wurde – sonst gibt es null.
+ Bei Methoden, die Listen zurückgeben (z.B. FetchTransaction(string userID, ...)), wird bei DB-Lesefehlern null oder ein leeres Array zurückgegeben.
+
+Fehlerquellen
+ Fehlerhafte oder fehlende DB-Verbindung: Exception und Logging.
+ SQL-Befehle mit Fehlern: Exception und Logging.
+ Ungültige Eingabewerte (z.B. null bei Strings/Objekten): Entweder direkter Return, Exception oder Logging.
+ Potentielle Race-Conditions werden durch lock (dbcon) reduziert.
+ Ressourcenlecks werden durch konsequentes .Dispose() der Commands/Reader vermieden.
+
+Positive Beispiele für Fehlerbehandlung
+
+ Parameter-Checks:
+ C#
+
+if (requiredParameters.Any(p => string.IsNullOrEmpty(p))) { throw new ArgumentException(...); }
+
+DB-Reader mit Exception Handling:
+C#
+
+catch (Exception e) {
+ m_log.Error("[MONEY MANAGER]: Fetching transaction failed 1: " + e.ToString());
+ r.Close(); cmd.Dispose(); return null;
+}
+
+Null/Default-Initialisierung:
+C#
+
+ if (transaction.ObjectUUID == null) transaction.ObjectUUID = UUID.Zero.ToString();
+ if (transaction.ObjectName == null) transaction.ObjectName = string.Empty;
+
+Zusammenfassung/Fazit
+ Null Pointer: Das Risiko ist im Code durch Checks, Defaultwerte und Exception-Handling weitgehend minimiert.
+ Fehlerquellen: Vor allem externe Faktoren (DB-Verbindung, SQL-Syntax). Im Code wird alles sauber abgefangen und geloggt.
+ Funktion: Umfassende Datenbank-Backend-Klasse für OpenSim-Money, mit Methoden für alle wichtigen Operationen (Balance, Transaktionen, User, Verkauf).
+
+Empfehlung:
+Der Code ist für produktiven Einsatz geeignet, robust gegen NullPointer-Fehler und typische Fehlerquellen.
+Bei zukünftigen Erweiterungen empfiehlt sich: Bei allen Methoden, die Objekte oder komplexe Werte zurückgeben, weiterhin konsequent auf null prüfen und Exceptions sauber loggen.
+ */
+
+using System;
+using System.Data;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using log4net;
+using MySql.Data.MySqlClient;
+using OpenMetaverse;
+using System.Linq;
+
+namespace OpenSim.Data.MySQL.MySQLMoneyDataWrapper
+{
+ public class MySQLMoneyManager : IMoneyManager
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private string Table_of_Balances = "balances";
+ private string Table_of_Transactions = "transactions";
+ private string Table_of_TotalSales = "totalsales";
+ private string Table_of_UserInfo = "userinfo";
+ private int balances_rev = 0;
+ private int userinfo_rev = 0;
+
+ private string connectString;
+ public MySqlConnection dbcon;
+
+
+ public MySQLMoneyManager(string hostname, string database, string username, string password, string cpooling, string port)
+ {
+ var requiredParameters = new[] { hostname, database, username, password, cpooling, port };
+
+ if (requiredParameters.Any(p => string.IsNullOrEmpty(p)))
+ {
+ throw new ArgumentException("All connection parameters must be provided.");
+ }
+
+ string connectionString = $"Server={hostname};Port={port};Database={database};User ID={username};Password={password};Pooling={cpooling};";
+ Initialise(connectionString);
+ }
+
+ public MySQLMoneyManager(string connect)
+ {
+ Initialise(connect);
+ }
+
+ private void Initialise(string connect)
+ {
+ // 1. Initialisiere Verbindungen
+ InitialiseConnection(connect);
+
+ // 2. Überprüfe und erstelle Tabellen
+ CheckAndCreateTables();
+ }
+
+ // 1. Initialisiere Verbindungen
+ private void InitialiseConnection(string connect)
+ {
+ try
+ {
+ connectString = connect;
+ dbcon = new MySqlConnection(connectString);
+ dbcon.Open();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("[MONEY MANAGER]: Error initializing MySql Database: " + e.ToString());
+ }
+ }
+
+ // 2. Überprüfe und erstelle Tabellen
+ private void CheckAndCreateTables()
+ {
+ try
+ {
+ Dictionary tableList = CheckTables();
+
+ InitialiseBalancesTable(tableList);
+ InitialiseUserInfoTable(tableList);
+ InitialiseTransactionsTable(tableList);
+ InitialiseTotalSalesTable(tableList);
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Error checking or creating tables: " + e.ToString());
+ throw new Exception("[MONEY MANAGER]: Error checking or creating tables: " + e.ToString());
+ }
+ }
+
+ private void InitialiseBalancesTable(Dictionary tableList)
+ {
+ if (!tableList.ContainsKey(Table_of_Balances))
+ {
+ try
+ {
+ CreateBalancesTable();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("[MONEY MANAGER]: Error creating balances table: " + e.ToString());
+ }
+ }
+ else
+ {
+ string version = tableList[Table_of_Balances].Trim();
+ int nVer = getTableVersionNum(version);
+ balances_rev = nVer;
+ switch (nVer)
+ {
+ case 1: //Rev.1
+ UpdateBalancesTable1();
+ UpdateBalancesTable2();
+ UpdateBalancesTable3();
+ break;
+ case 2: //Rev.2
+ UpdateBalancesTable2();
+ UpdateBalancesTable3();
+ break;
+ case 3: //Rev.3
+ UpdateBalancesTable3();
+ break;
+ }
+ }
+ }
+
+ private void InitialiseUserInfoTable(Dictionary tableList)
+ {
+ if (!tableList.ContainsKey(Table_of_UserInfo))
+ {
+ try
+ {
+ CreateUserInfoTable();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("[MONEY MANAGER]: Error creating userinfo table: " + e.ToString());
+ }
+ }
+ else
+ {
+ string version = tableList[Table_of_UserInfo].Trim();
+ int nVer = getTableVersionNum(version);
+ userinfo_rev = nVer;
+ switch (nVer)
+ {
+ case 1: //Rev.1
+ UpdateUserInfoTable1();
+ UpdateUserInfoTable2();
+ break;
+ case 2: //Rev.2
+ UpdateUserInfoTable2();
+ break;
+ }
+ }
+ }
+
+ private void InitialiseTransactionsTable(Dictionary tableList)
+ {
+ if (!tableList.ContainsKey(Table_of_Transactions))
+ {
+ try
+ {
+ CreateTransactionsTable();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("[MONEY MANAGER]: Error creating transactions table: " + e.ToString());
+ }
+ }
+ else
+ {
+ string version = tableList[Table_of_Transactions].Trim();
+ int nVer = getTableVersionNum(version);
+ switch (nVer)
+ {
+ case 2: //Rev.2
+ UpdateTransactionsTable2();
+ UpdateTransactionsTable3();
+ UpdateTransactionsTable4();
+ UpdateTransactionsTable5();
+ UpdateTransactionsTable6();
+ UpdateTransactionsTable7();
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 3: //Rev.3
+ UpdateTransactionsTable3();
+ UpdateTransactionsTable4();
+ UpdateTransactionsTable5();
+ UpdateTransactionsTable6();
+ UpdateTransactionsTable7();
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 4: //Rev.4
+ UpdateTransactionsTable4();
+ UpdateTransactionsTable5();
+ UpdateTransactionsTable6();
+ UpdateTransactionsTable7();
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 5: //Rev.5
+ UpdateTransactionsTable5();
+ UpdateTransactionsTable6();
+ UpdateTransactionsTable7();
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 6: //Rev.6
+ UpdateTransactionsTable6();
+ UpdateTransactionsTable7();
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 7: //Rev.7
+ UpdateTransactionsTable7();
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 8: //Rev.8
+ UpdateTransactionsTable8();
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 9: //Rev.9
+ UpdateTransactionsTable9();
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 10: //Rev.10
+ UpdateTransactionsTable10();
+ UpdateTransactionsTable11();
+ break;
+ case 11: //Rev.11
+ UpdateTransactionsTable11();
+ break;
+ }
+ }
+ }
+
+ private void InitialiseTotalSalesTable(Dictionary tableList)
+ {
+ if (!tableList.ContainsKey(Table_of_TotalSales))
+ {
+ try
+ {
+ CreateTotalSalesTable();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("[MONEY MANAGER]: Error creating totalsales table: " + e.ToString());
+ }
+ }
+ else
+ {
+ string version = tableList[Table_of_TotalSales].Trim();
+ int nVer = getTableVersionNum(version);
+ switch (nVer)
+ {
+ case 1: //Rev.1
+ UpdateTotalSalesTable1();
+ UpdateTotalSalesTable2();
+ break;
+ case 2: //Rev.2
+ UpdateTotalSalesTable2();
+ break;
+ }
+ }
+ }
+
+
+
+ /// Gets the table version number.
+ /// The version.
+ private int getTableVersionNum(string version)
+ {
+ int nVer = 0;
+
+ Regex _commentPattenRegex = new Regex(@"\w+\.(?\d+)");
+ Match m = _commentPattenRegex.Match(version);
+ if (m.Success)
+ {
+ string ver = m.Groups["ver"].Value;
+ nVer = Convert.ToInt32(ver);
+ }
+ return nVer;
+ }
+
+ /// Creates the balances table.
+ private void CreateBalancesTable()
+ {
+ string sql = string.Empty;
+
+ sql = "CREATE TABLE `" + Table_of_Balances + "` (";
+ sql += "`user` varchar(36) NOT NULL,";
+ sql += "`balance` int(10) NOT NULL,";
+ sql += "`status` tinyint(2) DEFAULT NULL,";
+ sql += "`type` tinyint(2) NOT NULL DEFAULT 0,";
+ sql += "PRIMARY KEY(`user`))";
+ sql += "Engine=InnoDB DEFAULT CHARSET=utf8 ";
+
+ sql += "COMMENT='Rev.4';";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ /// Creates the user information table.
+ private void CreateUserInfoTable()
+ {
+ string sql = string.Empty;
+
+ sql = "CREATE TABLE `" + Table_of_UserInfo + "` (";
+ sql += "`user` varchar(36) NOT NULL,";
+ sql += "`simip` varchar(64) NOT NULL,";
+ sql += "`avatar` varchar(50) NOT NULL,";
+ sql += "`pass` varchar(36) NOT NULL DEFAULT '',";
+ sql += "`type` tinyint(2) NOT NULL DEFAULT 0,";
+ sql += "`class` tinyint(2) NOT NULL DEFAULT 0,";
+ sql += "`serverurl` varchar(255) NOT NULL DEFAULT '',";
+ sql += "PRIMARY KEY(`user`))";
+ sql += "Engine=InnoDB DEFAULT CHARSET=utf8 ";
+
+ sql += "COMMENT='Rev.3';";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ /// Creates the transactions table.
+ private void CreateTransactionsTable()
+ {
+ string sql = string.Empty;
+
+ sql = "CREATE TABLE `" + Table_of_Transactions + "`(";
+ sql += "`UUID` varchar(36) NOT NULL,";
+ sql += "`sender` varchar(36) NOT NULL,";
+ sql += "`receiver` varchar(36) NOT NULL,";
+ sql += "`amount` int(10) NOT NULL,";
+ sql += "`senderBalance` int(10) NOT NULL DEFAULT -1,";
+ sql += "`receiverBalance` int(10) NOT NULL DEFAULT -1,";
+ sql += "`objectUUID` varchar(36) DEFAULT NULL,";
+ sql += "`objectName` varchar(255) DEFAULT NULL,";
+ sql += "`regionHandle` varchar(36) NOT NULL,";
+ sql += "`regionUUID` varchar(36) NOT NULL,";
+ sql += "`type` int(10) NOT NULL,";
+ sql += "`time` int(11) NOT NULL,";
+ sql += "`secure` varchar(36) NOT NULL,";
+ sql += "`status` tinyint(1) NOT NULL,";
+ sql += "`commonName` varchar(128) NOT NULL,";
+ sql += "`description` varchar(255) DEFAULT NULL,";
+ sql += "PRIMARY KEY(`UUID`))";
+ sql += "Engine=InnoDB DEFAULT CHARSET=utf8 ";
+
+ sql += "COMMENT='Rev.12';";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ /// Creates the total sales table.
+ private void CreateTotalSalesTable()
+ {
+ string sql = string.Empty;
+
+ sql = "CREATE TABLE `" + Table_of_TotalSales + "` (";
+ sql += "`UUID` varchar(36) NOT NULL,";
+ sql += "`user` varchar(36) NOT NULL,";
+ sql += "`objectUUID` varchar(36) NOT NULL,";
+ sql += "`type` int(10) NOT NULL,";
+ sql += "`TotalCount` int(10) NOT NULL DEFAULT 0,";
+ sql += "`TotalAmount` int(10) NOT NULL DEFAULT 0,";
+ sql += "`time` int(11) NOT NULL,";
+ sql += "PRIMARY KEY(`UUID`))";
+ sql += "Engine=InnoDB DEFAULT CHARSET=utf8 ";
+
+ sql += "COMMENT='Rev.3';";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+
+ initTotalSalesTable();
+ }
+
+
+ /// Updates the balances table1.
+ private void UpdateBalancesTable1()
+ {
+ m_log.Info("[MONEY MANAGER]: Converting Balance Table...");
+ string sql = string.Empty;
+
+ sql = "SELECT COUNT(*) FROM " + Table_of_Balances;
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ int resultCount = int.Parse(cmd.ExecuteScalar().ToString());
+ cmd.Dispose();
+
+ sql = "SELECT * FROM " + Table_of_Balances;
+ cmd = new MySqlCommand(sql, dbcon);
+ MySqlDataReader dbReader = cmd.ExecuteReader();
+
+ int l = 0;
+ string[,] row = new string[resultCount, dbReader.FieldCount];
+ while (dbReader.Read())
+ {
+ for (int i = 0; i < dbReader.FieldCount; i++)
+ {
+ row[l, i] = dbReader.GetString(i);
+ }
+ l++;
+ }
+ dbReader.Close();
+ cmd.Dispose();
+
+ bool updatedb = true;
+ for (int i = 0; i < resultCount; i++)
+ {
+ string uuid = Regex.Replace(row[i, 0], @"@.+$", "");
+ if (uuid != row[i, 0])
+ {
+ int amount = int.Parse(row[i, 1]);
+ int balance = getBalance(uuid);
+ if (balance >= 0)
+ {
+ amount += balance;
+ updatedb = updateBalance(uuid, amount);
+ }
+ else
+ {
+ updatedb = addUser(uuid, amount, int.Parse(row[i, 2]), 0);
+ }
+ if (!updatedb) break;
+ }
+ }
+
+ // Delete
+ if (updatedb)
+ {
+ for (int i = 0; i < resultCount; i++)
+ {
+ string uuid = Regex.Replace(row[i, 0], @"@.+$", "");
+ if (uuid != row[i, 0])
+ {
+ sql = "DELETE FROM " + Table_of_Balances + " WHERE user = ?uuid";
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?uuid", row[i, 0]);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+ }
+
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Balances + "` ";
+ sql += "COMMENT = 'Rev.2';";
+ sql += "COMMIT;";
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+ }
+
+
+ /// Updates the balances table2.
+ private void UpdateBalancesTable2()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Balances + "` ";
+ sql += "MODIFY COLUMN `user` varchar(36) NOT NULL,";
+ sql += "COMMENT = 'Rev.3';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ /// Updates the balances table3.
+ private void UpdateBalancesTable3()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Balances + "` ";
+ sql += "ADD `type` tinyint(2) NOT NULL DEFAULT 0 AFTER `status`,";
+ sql += "COMMENT = 'Rev.4';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+ /// Updates the user information table1.
+ private void UpdateUserInfoTable1()
+ {
+ //m_log.Info("[MONEY MANAGER]: Converting UserInfo Table...");
+ string sql = string.Empty;
+
+ sql = "SELECT COUNT(*) FROM " + Table_of_UserInfo;
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ int resultCount = int.Parse(cmd.ExecuteScalar().ToString());
+ cmd.Dispose();
+
+ sql = "SELECT * FROM " + Table_of_UserInfo;
+ cmd = new MySqlCommand(sql, dbcon);
+ MySqlDataReader dbReader = cmd.ExecuteReader();
+
+ int l = 0;
+ string[,] row = new string[resultCount, dbReader.FieldCount];
+ while (dbReader.Read())
+ {
+ for (int i = 0; i < dbReader.FieldCount; i++)
+ {
+ row[l, i] = dbReader.GetString(i);
+ }
+ l++;
+ }
+ dbReader.Close();
+ cmd.Dispose();
+
+ // UniversalID -> uuid, url, name, pass
+ bool updatedb = true;
+ for (int i = 0; i < resultCount; i++)
+ {
+ string uuid = Regex.Replace(row[i, 0], @"@.+$", "");
+ if (uuid != row[i, 0])
+ {
+ UserInfo userInfo = fetchUserInfo(uuid);
+ if (userInfo == null)
+ {
+ userInfo = new UserInfo();
+ userInfo.UserID = uuid;
+ userInfo.SimIP = row[i, 1];
+ userInfo.Avatar = row[i, 2];
+ userInfo.PswHash = row[i, 3];
+ updatedb = addUserInfo(userInfo);
+ }
+ }
+ }
+
+ // Delete
+ if (updatedb)
+ {
+ for (int i = 0; i < resultCount; i++)
+ {
+ string uuid = Regex.Replace(row[i, 0], @"@.+$", "");
+ if (uuid != row[i, 0])
+ {
+ sql = "DELETE FROM " + Table_of_UserInfo + " WHERE user = ?uuid";
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?uuid", row[i, 0]);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+ }
+
+ //
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_UserInfo + "` ";
+ sql += "COMMENT = 'Rev.2';";
+ sql += "COMMIT;";
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+ }
+
+ /// Updates the user information table2.
+ private void UpdateUserInfoTable2()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_UserInfo + "` ";
+ sql += "MODIFY COLUMN `user` varchar(36) NOT NULL,";
+ sql += "MODIFY COLUMN `pass` varchar(36) NOT NULL DEFAULT '',";
+ sql += "ADD `type` tinyint(2) NOT NULL DEFAULT 0 AFTER `pass`,";
+ sql += "ADD `class` tinyint(2) NOT NULL DEFAULT 0 AFTER `type`,";
+ sql += "ADD `serverurl` varchar(255) NOT NULL DEFAULT '' AFTER `class`,";
+ sql += "COMMENT = 'Rev.3';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ // update Transactions Table
+
+ ///
+ /// update transactions table from Rev.2 to Rev.3
+ ///
+ private void UpdateTransactionsTable2()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD(`objectUUID` varchar(36) DEFAULT NULL AFTER `amount`),";
+ sql += "COMMENT = 'Rev.3';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.3 to Rev.4
+ ///
+ private void UpdateTransactionsTable3()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD(`secure` varchar(36) NOT NULL AFTER `time`),";
+ sql += "COMMENT = 'Rev.4';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.4 to Rev.5
+ ///
+ private void UpdateTransactionsTable4()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD(`regionHandle` varchar(36) NOT NULL AFTER `objectUUID`),";
+ sql += "COMMENT = 'Rev.5';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.5 to Rev.6
+ ///
+ private void UpdateTransactionsTable5()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD(`commonName` varchar(128) NOT NULL AFTER `status`),";
+ sql += "COMMENT = 'Rev.6';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.6 to Rev.7
+ ///
+ private void UpdateTransactionsTable6()
+ {
+ string sql = string.Empty;
+
+ sql = "SELECT COUNT(*) FROM " + Table_of_Transactions;
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ int resultCount = int.Parse(cmd.ExecuteScalar().ToString());
+ cmd.Dispose();
+
+ sql = "SELECT UUID,sender,receiver FROM " + Table_of_Transactions;
+ cmd = new MySqlCommand(sql, dbcon);
+ MySqlDataReader dbReader = cmd.ExecuteReader();
+
+ int l = 0;
+ string[,] row = new string[resultCount, dbReader.FieldCount];
+ while (dbReader.Read())
+ {
+ for (int i = 0; i < dbReader.FieldCount; i++)
+ {
+ row[l, i] = dbReader.GetString(i);
+ }
+ l++;
+ }
+ dbReader.Close();
+ cmd.Dispose();
+
+ sql = "UPDATE " + Table_of_Transactions + " SET sender = ?sender , receiver = ?receiver WHERE UUID = ?uuid;";
+ for (int i = 0; i < resultCount; i++)
+ {
+ string sender = Regex.Replace(row[i, 1], @"@.+$", "");
+ string receiver = Regex.Replace(row[i, 2], @"@.+$", "");
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?uuid", row[i, 0]);
+ cmd.Parameters.AddWithValue("?sender", sender);
+ cmd.Parameters.AddWithValue("?receiver", receiver);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "COMMENT = 'Rev.7';";
+ sql += "COMMIT;";
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.7 to Rev.8
+ ///
+ private void UpdateTransactionsTable7()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD `objectName` varchar(255) DEFAULT NULL AFTER `objectUUID`,";
+ sql += "COMMENT = 'Rev.8';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.8 to Rev.9
+ ///
+ private void UpdateTransactionsTable8()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD `senderBalance` int(10) NOT NULL DEFAULT -1 AFTER `amount`,";
+ sql += "ADD `receiverBalance` int(10) NOT NULL DEFAULT -1 AFTER `senderBalance`,";
+ sql += "COMMENT = 'Rev.9';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.9 to Rev.10
+ ///
+ private void UpdateTransactionsTable9()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "MODIFY COLUMN `sender` varchar(36) NOT NULL,";
+ sql += "MODIFY COLUMN `receiver` varchar(36) NOT NULL,";
+ sql += "COMMENT = 'Rev.10';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.10 to Rev.11
+ /// change type of BirthGift from 1000 to 900
+ ///
+ private void UpdateTransactionsTable10()
+ {
+ string sql = string.Empty;
+
+ sql = "SELECT COUNT(*) FROM `" + Table_of_Transactions + "` WHERE type=1000";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ int resultCount = int.Parse(cmd.ExecuteScalar().ToString());
+ cmd.Dispose();
+
+ if (resultCount > 0)
+ {
+ sql = "SELECT UUID FROM `" + Table_of_Transactions + "` WHERE type=1000";
+ cmd = new MySqlCommand(sql, dbcon);
+ MySqlDataReader dbReader = cmd.ExecuteReader();
+
+ int l = 0;
+ string[] row = new string[resultCount];
+ while (dbReader.Read())
+ {
+ row[l] = dbReader.GetString(0);
+ l++;
+ }
+ dbReader.Close();
+ cmd.Dispose();
+
+ sql = "UPDATE `" + Table_of_Transactions + "` SET type=900 WHERE UUID=?uuid";
+ for (int i = 0; i < resultCount; i++)
+ {
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?uuid", row[i]);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+ }
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "COMMENT = 'Rev.11';";
+ sql += "COMMIT;";
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ ///
+ /// update transactions table from Rev.11 to Rev.12
+ ///
+ private void UpdateTransactionsTable11()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_Transactions + "` ";
+ sql += "ADD `regionUUID` varchar(36) NOT NULL AFTER `regionHandle`,";
+ sql += "COMMENT = 'Rev.12';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ // update Total Sales Table
+
+ /// Updates the total sales table1.
+ private void UpdateTotalSalesTable1()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_TotalSales + "` ";
+ sql += "ADD `time` int(11) NOT NULL AFTER `TotalAmount`,";
+ sql += "COMMENT = 'Rev.2';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+
+ deleteTotalSalesTable();
+ initTotalSalesTable();
+ }
+
+ /// Updates the total sales table2.
+ private void UpdateTotalSalesTable2()
+ {
+ string sql = string.Empty;
+
+ sql = "BEGIN;";
+ sql += "ALTER TABLE `" + Table_of_TotalSales + "` ";
+ sql += "MODIFY COLUMN `user` varchar(36) NOT NULL,";
+ sql += "COMMENT = 'Rev.3';";
+ sql += "COMMIT;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+ }
+
+
+ /// Checks the tables.
+ ///
+ ///
+ ///
+ /// [MONEY MANAGER]: Error checking tables" + e.ToString()
+ private Dictionary CheckTables()
+ {
+ Dictionary tableDic = new Dictionary();
+
+ lock (dbcon)
+ {
+ string sql = string.Empty;
+
+ sql = "SELECT TABLE_NAME,TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=?dbname";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?dbname", dbcon.Database);
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ while (r.Read())
+ {
+ try
+ {
+ string tableName = (string)r["TABLE_NAME"];
+ string comment = (string)r["TABLE_COMMENT"];
+ tableDic.Add(tableName, comment);
+ }
+ catch (Exception e)
+ {
+ throw new Exception("[MONEY MANAGER]: Error checking tables" + e.ToString());
+ }
+ }
+ r.Close();
+ }
+
+ cmd.Dispose();
+ return tableDic;
+ }
+ }
+
+
+ public void Reconnect()
+ {
+ m_log.Info("[MONEY MANAGER]: Reconnecting database");
+ lock (dbcon)
+ {
+ try
+ {
+ dbcon.Close();
+ dbcon = new MySqlConnection(connectString);
+ dbcon.Open();
+ m_log.Info("[MONEY MANAGER]: Reconnected database");
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Unable to reconnect to database: " + e.ToString());
+ }
+ }
+ }
+
+ ///
+ /// Get balance from database. returns -1 if failed.
+ ///
+ ///
+ public int getBalance(string userID)
+ {
+ if (userID == UUID.Zero.ToString()) return 999999999; // System
+
+ int retValue = -1;
+ string sql = string.Empty;
+
+ sql = "SELECT balance FROM " + Table_of_Balances + " WHERE user = ?userid";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?userid", userID);
+
+ using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow))
+ {
+ try
+ {
+ if (dbReader.Read())
+ {
+ retValue = Convert.ToInt32(dbReader["balance"]);
+ }
+ }
+ catch (Exception e)
+ {
+ e.ToString();
+ m_log.ErrorFormat("[MoneyDB]: MySql failed to fetch balance {0}.", userID);
+ retValue = -2;
+ }
+
+ dbReader.Close();
+ }
+ cmd.Dispose();
+
+ return retValue;
+ }
+
+
+ /// Updates the balance.
+ /// The user identifier.
+ /// The amount.
+ public bool updateBalance(string userID, int amount)
+ {
+ if (userID == UUID.Zero.ToString()) return true; // System
+
+ bool bRet = false;
+ string sql = string.Empty;
+
+ sql = "UPDATE " + Table_of_Balances + " SET balance = ?amount WHERE user = ?userID;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ /// Adds the user.
+ /// The user identifier.
+ /// The balance.
+ /// The status.
+ /// The type.
+ public bool addUser(string userID, int balance, int status, int type)
+ {
+ if (userID == UUID.Zero.ToString()) return true; // System
+
+ bool bRet = false;
+ string sql = string.Empty;
+
+ if (balances_rev >= 4)
+ {
+ sql = "INSERT INTO " + Table_of_Balances + " (`user`,`balance`,`status`,`type`) VALUES ";
+ sql += " (?userID,?balance,?status,?type);";
+ }
+ else
+ {
+ sql = "INSERT INTO " + Table_of_Balances + " (`user`,`balance`,`status`) VALUES ";
+ sql += " (?userID,?balance,?status);";
+ }
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+
+ cmd.Parameters.AddWithValue("?userID", userID);
+ cmd.Parameters.AddWithValue("?balance", balance);
+ cmd.Parameters.AddWithValue("?status", status);
+ if (balances_rev >= 4)
+ {
+ cmd.Parameters.AddWithValue("?type", type);
+ }
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+ cmd.Dispose();
+
+ return bRet;
+ }
+
+
+ ///
+ /// Here we'll make a withdraw from the sender and update transaction status
+ ///
+ ///
+ ///
+ ///
+ public bool withdrawMoney(UUID transactionID, string senderID, int amount)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+ MySqlCommand cmd = null;
+
+ // System
+ if (senderID == UUID.Zero.ToString())
+ {
+ sql = "BEGIN;";
+ sql += "UPDATE " + Table_of_Transactions;
+ sql += " SET senderBalance = 0, status = ?status WHERE UUID = ?tranid;";
+ sql += "COMMIT;";
+
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?status", (int)Status.PENDING_STATUS); //pending
+ cmd.Parameters.AddWithValue("?tranid", transactionID.ToString());
+ }
+ else
+ {
+ sql = "BEGIN;";
+ sql += "UPDATE " + Table_of_Transactions + "," + Table_of_Balances;
+ sql += " SET senderBalance = balance - ?amount, " + Table_of_Transactions + ".status = ?status, balance = balance - ?amount ";
+ sql += " WHERE UUID = ?tranid AND user = sender AND user = ?userid;";
+ sql += "COMMIT;";
+
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userid", senderID);
+ cmd.Parameters.AddWithValue("?status", (int)Status.PENDING_STATUS); //pending
+ cmd.Parameters.AddWithValue("?tranid", transactionID.ToString());
+ }
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ ///
+ /// Give money to the receiver and change the transaction status to success.
+ ///
+ ///
+ ///
+ ///
+ public bool giveMoney(UUID transactionID, string receiverID, int amount)
+ {
+ string sql = string.Empty;
+ bool bRet = false;
+ MySqlCommand cmd = null;
+
+ // System
+ if (receiverID == UUID.Zero.ToString())
+ {
+ sql = "BEGIN;";
+ sql += "UPDATE " + Table_of_Transactions;
+ sql += " SET receiverBalance = 0, status = ?status WHERE UUID = ?tranid;";
+ sql += "COMMIT;";
+
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?status", (int)Status.SUCCESS_STATUS); //Success
+ cmd.Parameters.AddWithValue("?tranid", transactionID.ToString());
+ }
+ else
+ {
+ sql = "BEGIN;";
+ sql += "UPDATE " + Table_of_Transactions + "," + Table_of_Balances;
+ sql += " SET receiverBalance = balance + ?amount, " + Table_of_Transactions + ".status = ?status, balance = balance + ?amount ";
+ sql += " WHERE UUID = ?tranid AND user = receiver AND user = ?userid;";
+ sql += "COMMIT;";
+
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userid", receiverID);
+ cmd.Parameters.AddWithValue("?status", (int)Status.SUCCESS_STATUS); //Success
+ cmd.Parameters.AddWithValue("?tranid", transactionID.ToString());
+ }
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+ public bool BuyMoney(UUID transactionID, string userID, int amount)
+ {
+ string sql = string.Empty;
+ bool bRet = false;
+ MySqlCommand cmd = null;
+
+ try
+ {
+ sql = "BEGIN;";
+ sql += "UPDATE Transactions, Balances";
+ sql += " SET balance = balance + ?amount, Transactions.status = ?status ";
+ sql += " WHERE Transactions.UUID = ?tranid AND Balances.user = ?userid;";
+ sql += "COMMIT;";
+
+ cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userid", userID);
+ cmd.Parameters.AddWithValue("?status", (int)Status.SUCCESS_STATUS);
+ cmd.Parameters.AddWithValue("?tranid", transactionID.ToString());
+
+ if (cmd.ExecuteNonQuery() > 0)
+ {
+ bRet = true;
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[BuyMoney]: Exception occurred during SQL execution: {0}", ex.Message);
+ }
+ finally
+ {
+ if (cmd != null)
+ {
+ cmd.Dispose();
+ }
+ }
+
+ m_log.InfoFormat("[BuyMoney]: SQL command executed successfully: {0}", bRet);
+
+ return bRet;
+ }
+
+
+ /// Initializes the total sales table.
+ private void initTotalSalesTable()
+ {
+ m_log.Info("[MONEY MANAGER]: Initailising TotalSales Table...");
+ string sql = string.Empty;
+
+ sql = "SELECT SQL_CALC_FOUND_ROWS receiver,objectUUID,type,COUNT(*),SUM(amount),MIN(time) FROM " + Table_of_Transactions;
+ sql += " WHERE sender != receiver AND status = ?status AND sender != ?system";
+ sql += " GROUP BY receiver,objectUUID,type;";
+
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?status", (int)Status.SUCCESS_STATUS);
+ cmd.Parameters.AddWithValue("?system", UUID.Zero.ToString());
+ cmd.ExecuteNonQuery();
+
+ MySqlCommand cmd2 = new MySqlCommand("SELECT FOUND_ROWS();", dbcon);
+ int lineCount = int.Parse(cmd2.ExecuteScalar().ToString());
+ cmd2.Dispose();
+
+ if (lineCount <= 0)
+ {
+ cmd.Dispose();
+ return;
+ }
+
+ MySqlDataReader r = cmd.ExecuteReader();
+ int l = 0;
+ string[,] row = new string[lineCount, r.FieldCount];
+ while (r.Read())
+ {
+ for (int i = 0; i < r.FieldCount; i++)
+ {
+ row[l, i] = r.GetString(i);
+ }
+ l++;
+ }
+ r.Close();
+ cmd.Dispose();
+
+ for (int i = 0; i < lineCount; i++)
+ {
+ string receiver = (string)row[i, 0]; // receiver
+ string objUUID = (string)row[i, 1]; // objectUUID
+ int type = Convert.ToInt32(row[i, 2]); // type
+ int count = Convert.ToInt32(row[i, 3]); // COUNT(*)
+ int amount = Convert.ToInt32(row[i, 4]); // SUM(amount)
+ int tmstamp = Convert.ToInt32(row[i, 5]); // MIN(time)
+ //
+ setTotalSale(receiver, objUUID, type, count, amount, tmstamp);
+ }
+ }
+
+
+ /// Deletes the total sales table.
+ private void deleteTotalSalesTable()
+ {
+ string sql = string.Empty;
+
+ sql = "DELETE FROM " + Table_of_TotalSales;
+
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?status", (int)Status.SUCCESS_STATUS);
+ cmd.ExecuteNonQuery();
+ cmd.Dispose();
+
+ return;
+ }
+
+
+ /// Adds the total sale.
+ /// The user UUID.
+ /// The object UUID.
+ /// The type.
+ /// The count.
+ /// The amount.
+ /// The tmstamp.
+ public bool addTotalSale(string userUUID, string objectUUID, int type, int count, int amount, int tmstamp)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+ UUID uuid = UUID.Random();
+
+ // Record only transactions involving objects
+ // if (objectUUID==UUID.Zero.ToString()) return bRet;
+
+ sql = "INSERT INTO " + Table_of_TotalSales;
+ sql += " (`UUID`,`user`,`objectUUID`,`type`,`TotalCount`,`TotalAmount`,`time`) VALUES";
+ sql += " (?ID,?userID,?objID,?type,?count,?amount,?time)";
+
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?ID", uuid.ToString());
+ cmd.Parameters.AddWithValue("?userID", userUUID);
+ cmd.Parameters.AddWithValue("?objID", objectUUID);
+ cmd.Parameters.AddWithValue("?type", type);
+ cmd.Parameters.AddWithValue("?count", count);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?time", tmstamp);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ /// Updates the total sale.
+ /// The sale UUID.
+ /// The count.
+ /// The amount.
+ /// The tmstamp.
+ public bool updateTotalSale(UUID saleUUID, int count, int amount, int tmstamp)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+
+ sql = "UPDATE " + Table_of_TotalSales;
+ sql += " SET TotalCount = TotalCount + ?count, TotalAmount = TotalAmount + ?amount, time = ?time ";
+ sql += " WHERE UUID = ?uuid;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+
+ cmd.Parameters.AddWithValue("?uuid", saleUUID.ToString());
+ cmd.Parameters.AddWithValue("?count", count);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?time", tmstamp);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+
+ }
+
+ /// Sets the total sale.
+ /// The user UUID.
+ /// The object UUID.
+ /// The type.
+ /// The count.
+ /// The amount.
+ /// The tmstamp.
+ public bool setTotalSale(string userUUID, string objectUUID, int type, int count, int amount, int tmstamp)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+ string uuid = string.Empty;
+ int dbtm = 0;
+
+ sql = "SELECT UUID,time FROM " + Table_of_TotalSales;
+ sql += " WHERE user = ?userid AND objectUUID = ?objID AND type = ?type;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+
+ cmd.Parameters.AddWithValue("?userid", userUUID);
+ cmd.Parameters.AddWithValue("?objID", objectUUID);
+ cmd.Parameters.AddWithValue("?type", type);
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ if (r.Read())
+ {
+ try
+ {
+ uuid = (string)r["UUID"];
+ dbtm = Convert.ToInt32(r["time"]);
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Get sale data from DB failed: " + e.ToString());
+ r.Close();
+ cmd.Dispose();
+ return false;
+ }
+ }
+ r.Close();
+ }
+
+ if (uuid != string.Empty)
+ {
+ UUID saleUUID = UUID.Zero;
+ UUID.TryParse(uuid, out saleUUID);
+ if (dbtm < tmstamp) tmstamp = dbtm;
+ bRet = updateTotalSale(saleUUID, count, amount, tmstamp);
+ }
+ else
+ {
+ bRet = addTotalSale(userUUID, objectUUID, type, count, amount, tmstamp);
+ }
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ //
+ // transactions
+ //
+ /// Adds the transaction.
+ /// The transaction.
+ public bool addTransaction(TransactionData transaction)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+
+ if (transaction.ObjectUUID == null) transaction.ObjectUUID = UUID.Zero.ToString();
+ if (transaction.ObjectName == null) transaction.ObjectName = string.Empty;
+ if (transaction.Description == null) transaction.Description = string.Empty;
+
+ sql = "INSERT INTO " + Table_of_Transactions;
+ sql += " (`UUID`,`sender`,`receiver`,`amount`,`senderBalance`,`receiverBalance`,`objectUUID`,`objectName`,";
+ sql += " `regionHandle`,`regionUUID`,`type`,`time`,`secure`,`status`,`commonName`,`description`) VALUES";
+ sql += " (?transID,?sender,?receiver,?amount,?senderBalance,?receiverBalance,?objID,?objName,?regionHandle,?regionUUID,?type,?time,?secure,?status,?cname,?desc)";
+
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?transID", transaction.TransUUID.ToString());
+ cmd.Parameters.AddWithValue("?sender", transaction.Sender);
+ cmd.Parameters.AddWithValue("?receiver", transaction.Receiver);
+ cmd.Parameters.AddWithValue("?amount", transaction.Amount);
+ cmd.Parameters.AddWithValue("?senderBalance", -1);
+ cmd.Parameters.AddWithValue("?receiverBalance", -1);
+ cmd.Parameters.AddWithValue("?objID", transaction.ObjectUUID);
+ cmd.Parameters.AddWithValue("?objName", transaction.ObjectName);
+ cmd.Parameters.AddWithValue("?regionHandle", transaction.RegionHandle);
+ cmd.Parameters.AddWithValue("?regionUUID", transaction.RegionUUID);
+ cmd.Parameters.AddWithValue("?type", transaction.Type);
+ cmd.Parameters.AddWithValue("?time", transaction.Time);
+ cmd.Parameters.AddWithValue("?secure", transaction.SecureCode);
+ cmd.Parameters.AddWithValue("?status", transaction.Status);
+ cmd.Parameters.AddWithValue("?cname", transaction.CommonName);
+ cmd.Parameters.AddWithValue("?desc", transaction.Description);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ /// Updates the transaction status.
+ /// The transaction identifier.
+ /// The status.
+ /// The description.
+ public bool updateTransactionStatus(UUID transactionID, int status, string description)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+
+ sql = "UPDATE " + Table_of_Transactions + " SET status = ?status,description = ?desc WHERE UUID = ?tranid;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?status", status);
+ cmd.Parameters.AddWithValue("?desc", description);
+ cmd.Parameters.AddWithValue("?tranid", transactionID);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ /// Sets the trans expired.
+ /// The dead time.
+ public bool SetTransExpired(int deadTime)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+
+ sql = "UPDATE " + Table_of_Transactions;
+ sql += " SET status = ?failedstatus,description = ?desc WHERE time <= ?deadTime AND status = ?pendingstatus;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?failedstatus", (int)Status.FAILED_STATUS);
+ cmd.Parameters.AddWithValue("?desc", "expired");
+ cmd.Parameters.AddWithValue("?deadTime", deadTime);
+ cmd.Parameters.AddWithValue("?pendingstatus", (int)Status.PENDING_STATUS);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ ///
+ /// Validate if the transacion is legal
+ ///
+ ///
+ ///
+ public bool ValidateTransfer(string secureCode, UUID transactionID)
+ {
+ bool bRet = false;
+ string secure = string.Empty;
+ string sql = string.Empty;
+
+ sql = "SELECT secure FROM " + Table_of_Transactions + " WHERE UUID = ?transID;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?transID", transactionID.ToString());
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ if (r.Read())
+ {
+ try
+ {
+ secure = (string)r["secure"];
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Get transaction from DB failed: " + e.ToString());
+ }
+ if (secureCode == secure) bRet = true;
+ else bRet = false;
+ }
+ r.Close();
+ }
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ /// Fetches the transaction.
+ /// The transaction identifier.
+ public TransactionData FetchTransaction(UUID transactionID)
+ {
+ TransactionData transactionData = new TransactionData();
+ transactionData.TransUUID = transactionID;
+ string sql = string.Empty;
+
+ sql = "SELECT * FROM " + Table_of_Transactions + " WHERE UUID = ?transID;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?transID", transactionID.ToString());
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ if (r.Read())
+ {
+ try
+ {
+ transactionData.Sender = (string)r["sender"];
+ transactionData.Receiver = (string)r["receiver"];
+ transactionData.Amount = Convert.ToInt32(r["amount"]);
+ transactionData.SenderBalance = Convert.ToInt32(r["senderBalance"]);
+ transactionData.ReceiverBalance = Convert.ToInt32(r["receiverBalance"]);
+ transactionData.Type = Convert.ToInt32(r["type"]);
+ transactionData.Time = Convert.ToInt32(r["time"]);
+ transactionData.Status = Convert.ToInt32(r["status"]);
+ transactionData.CommonName = (string)r["commonName"];
+ transactionData.RegionHandle = (string)r["regionHandle"];
+ transactionData.RegionUUID = (string)r["regionUUID"];
+ //
+ if (r["objectUUID"] is System.DBNull) transactionData.ObjectUUID = UUID.Zero.ToString();
+ else transactionData.ObjectUUID = (string)r["objectUUID"];
+ if (r["objectName"] is System.DBNull) transactionData.ObjectName = string.Empty;
+ else transactionData.ObjectName = (string)r["objectName"];
+ if (r["description"] is System.DBNull) transactionData.Description = string.Empty;
+ else transactionData.Description = (string)r["description"];
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Fetching transaction failed 1: " + e.ToString());
+ r.Close();
+ cmd.Dispose();
+ return null;
+ }
+
+ }
+ r.Close();
+ }
+
+ cmd.Dispose();
+ return transactionData;
+ }
+
+
+ /// Fetches the transaction.
+ /// The user identifier.
+ /// The start time.
+ /// The end time.
+ /// The index.
+ /// The ret number.
+ public TransactionData[] FetchTransaction(string userID, int startTime, int endTime, uint index, uint retNum)
+ {
+ List rows = new List();
+ string sql = string.Empty;
+
+ sql = "SELECT * FROM " + Table_of_Transactions + " WHERE time>=?start AND time<=?end ";
+ sql += "AND (sender=?user OR receiver=?user) ORDER BY time ASC LIMIT ?index,?num;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+
+ cmd.Parameters.AddWithValue("?start", startTime);
+ cmd.Parameters.AddWithValue("?end", endTime);
+ cmd.Parameters.AddWithValue("?user", userID);
+ cmd.Parameters.AddWithValue("?index", index);
+ cmd.Parameters.AddWithValue("?num", retNum);
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ for (int i = 0; i < retNum; i++)
+ {
+ if (r.Read())
+ {
+ try
+ {
+ TransactionData transactionData = new TransactionData();
+ string uuid = (string)r["UUID"];
+ UUID transUUID;
+ UUID.TryParse(uuid, out transUUID);
+
+ transactionData.TransUUID = transUUID;
+ transactionData.Sender = (string)r["sender"];
+ transactionData.Receiver = (string)r["receiver"];
+ transactionData.Amount = Convert.ToInt32(r["amount"]);
+ transactionData.SenderBalance = Convert.ToInt32(r["senderBalance"]);
+ transactionData.ReceiverBalance = Convert.ToInt32(r["receiverBalance"]);
+ transactionData.Type = Convert.ToInt32(r["type"]);
+ transactionData.Time = Convert.ToInt32(r["time"]);
+ transactionData.Status = Convert.ToInt32(r["status"]);
+ transactionData.CommonName = (string)r["commonName"];
+ transactionData.RegionHandle = (string)r["regionHandle"];
+ transactionData.RegionUUID = (string)r["regionUUID"];
+ //
+ if (r["objectUUID"] is System.DBNull) transactionData.ObjectUUID = UUID.Zero.ToString();
+ else transactionData.ObjectUUID = (string)r["objectUUID"];
+ if (r["objectName"] is System.DBNull) transactionData.ObjectName = string.Empty;
+ else transactionData.ObjectName = (string)r["objectName"];
+ if (r["description"] is System.DBNull) transactionData.Description = string.Empty;
+ else transactionData.Description = (string)r["description"];
+ //
+ rows.Add(transactionData);
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Fetching transaction failed 2: " + e.ToString());
+ r.Close();
+ cmd.Dispose();
+ return null;
+ }
+ }
+ }
+ r.Close();
+ }
+
+ cmd.Dispose();
+ return rows.ToArray();
+ }
+
+
+ /// Gets the transaction number.
+ /// The user identifier.
+ /// The start time.
+ /// The end time.
+ public int getTransactionNum(string userID, int startTime, int endTime)
+ {
+ int iRet = -1;
+ string sql = string.Empty;
+
+ sql = "SELECT COUNT(*) AS number FROM " + Table_of_Transactions + " WHERE time>=?start AND time<=?end ";
+ sql += "AND (sender=?user OR receiver=?user);";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+
+ cmd.Parameters.AddWithValue("?start", startTime);
+ cmd.Parameters.AddWithValue("?end", endTime);
+ cmd.Parameters.AddWithValue("?user", userID);
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ if (r.Read())
+ {
+ try
+ {
+ iRet = Convert.ToInt32(r["number"]);
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Unable to get transaction info: " + e.ToString());
+ }
+ }
+ r.Close();
+ }
+
+ cmd.Dispose();
+ return iRet;
+ }
+
+
+ //
+ // userinfo
+ //
+ /// Adds the user information.
+ ///
+ public bool addUserInfo(UserInfo userInfo)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+
+ if (userInfo.Avatar == null) return false;
+
+ if (userinfo_rev >= 3)
+ {
+ sql = "INSERT INTO " + Table_of_UserInfo + "(`user`,`simip`,`avatar`,`pass`,`type`,`class`,`serverurl`) VALUES";
+ sql += "(?user,?simip,?avatar,?password,?type,?class,?serverurl);";
+ }
+ else
+ {
+ sql = "INSERT INTO " + Table_of_UserInfo + "(`user`,`simip`,`avatar`,`pass`) VALUES";
+ sql += "(?user,?simip,?avatar,?password);";
+ }
+
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?user", userInfo.UserID);
+ cmd.Parameters.AddWithValue("?simip", userInfo.SimIP);
+ cmd.Parameters.AddWithValue("?avatar", userInfo.Avatar);
+ cmd.Parameters.AddWithValue("?password", userInfo.PswHash);
+ if (userinfo_rev >= 3)
+ {
+ cmd.Parameters.AddWithValue("?type", userInfo.Type);
+ cmd.Parameters.AddWithValue("?class", userInfo.Class);
+ cmd.Parameters.AddWithValue("?serverurl", userInfo.ServerURL);
+ }
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+
+ /// Fetches the user information.
+ /// The user identifier.
+ public UserInfo fetchUserInfo(string userID)
+ {
+ UserInfo userInfo = new UserInfo();
+ userInfo.UserID = null;
+ string sql = string.Empty;
+
+ sql = "SELECT * FROM " + Table_of_UserInfo + " WHERE user = ?userID;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ if (r.Read())
+ {
+ try
+ {
+ userInfo.UserID = (string)r["user"];
+ userInfo.SimIP = (string)r["simip"];
+ userInfo.Avatar = (string)r["avatar"];
+ userInfo.PswHash = (string)r["pass"];
+ userInfo.Type = Convert.ToInt32(r["type"]);
+ userInfo.Class = Convert.ToInt32(r["class"]);
+ userInfo.ServerURL = (string)r["serverurl"];
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY MANAGER]: Fetching UserInfo failed: " + e.ToString());
+ r.Close();
+ cmd.Dispose();
+ return null;
+ }
+ }
+ r.Close();
+ }
+ cmd.Dispose();
+
+ if (userInfo.UserID != userID) return null;
+ return userInfo;
+ }
+
+
+ /// Updates the user information.
+ ///
+ public bool updateUserInfo(UserInfo userInfo)
+ {
+ bool bRet = false;
+ string sql = string.Empty;
+
+ sql = "UPDATE " + Table_of_UserInfo + " SET simip=?simip,pass=?pass,class=?class,serverurl=?serverurl WHERE user=?user;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?simip", userInfo.SimIP);
+ cmd.Parameters.AddWithValue("?pass", userInfo.PswHash);
+ cmd.Parameters.AddWithValue("?class", userInfo.Class);
+ cmd.Parameters.AddWithValue("?serverurl", userInfo.ServerURL);
+ cmd.Parameters.AddWithValue("?user", userInfo.UserID);
+
+ if (cmd.ExecuteNonQuery() > 0) bRet = true;
+
+ cmd.Dispose();
+ return bRet;
+ }
+
+ public bool UserExists(string userID)
+ {
+ try
+ {
+ UserInfo userInfo = fetchUserInfo(userID);
+ return userInfo != null;
+ }
+ catch (Exception ex)
+ {
+ m_log.Error(ex);
+ return false;
+ }
+ }
+
+ public bool UpdateUserInfo(string userID, UserInfo updatedInfo)
+ {
+ UserInfo existingInfo = fetchUserInfo(userID);
+ if (existingInfo != null)
+ {
+ updatedInfo.UserID = userID; // Ensure the userID is updated correctly
+ return updateUserInfo(updatedInfo);
+ }
+ else
+ {
+ return false; // User not found
+ }
+ }
+
+ public bool DeleteUser(string userID)
+ {
+ string sql = "DELETE FROM " + Table_of_UserInfo + " WHERE user = ?userID;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ return cmd.ExecuteNonQuery() > 0;
+ }
+
+ public void LogTransactionError(UUID transactionID, string errorMessage)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: Transaction {0} failed with error: {1}", transactionID, errorMessage);
+ }
+
+ public IEnumerable GetTransactionHistory(string userID, int startTime, int endTime)
+ {
+ List transactionHistory = new List();
+
+ // Assuming you have a database connection and a query to fetch transactions
+ string sql = "SELECT * FROM " + Table_of_Transactions + " WHERE user = ?userID AND time >= ?startTime AND time <= ?endTime;";
+ MySqlCommand cmd = new MySqlCommand(sql, dbcon);
+ cmd.Parameters.AddWithValue("?userID", userID);
+ cmd.Parameters.AddWithValue("?startTime", startTime);
+ cmd.Parameters.AddWithValue("?endTime", endTime);
+
+ using (MySqlDataReader r = cmd.ExecuteReader())
+ {
+ while (r.Read())
+ {
+ TransactionData transaction = new TransactionData();
+ // Assuming you have properties in TransactionData class to match the columns in the database table
+ transaction.TransUUID = UUID.Parse((string)r["transuuid"]);
+ transaction.Sender = (string)r["sender"];
+ transaction.Receiver = (string)r["receiver"];
+ transaction.Amount = Convert.ToInt32(r["amount"]);
+ transaction.ObjectUUID = (string)r["objectuuid"];
+ transaction.ObjectName = (string)r["objectname"];
+ transaction.RegionHandle = (string)r["regionhandle"];
+ transaction.Type = Convert.ToInt32(r["type"]);
+ transaction.Time = Convert.ToInt32(r["time"]);
+ transaction.Status = Convert.ToInt32(r["status"]);
+ transaction.SecureCode = (string)r["securecode"];
+ transaction.CommonName = (string)r["commonname"];
+ transaction.Description = (string)r["description"];
+
+ transactionHistory.Add(transaction);
+ }
+ r.Close();
+ }
+ cmd.Dispose();
+
+ return transactionHistory;
+ }
+
+
+ }
+}
diff --git a/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/MySQLSuperManager.cs b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/MySQLSuperManager.cs
new file mode 100644
index 0000000..8491950
--- /dev/null
+++ b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/MySQLSuperManager.cs
@@ -0,0 +1,100 @@
+/*
+ * 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 OpenSim 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.
+
+Funktion
+ Die Klasse MySQLSuperManager dient als Wrapper/Manager für einen Thread-Sicherheitsmechanismus (Mutex) und kapselt eine Instanz von MySQLMoneyManager.
+ Sie enthält:
+ Ein Mutex-Objekt für Thread-Synchronisation.
+ Ein Flag Locked zur Statusanzeige.
+ Eine öffentliche Instanz von MySQLMoneyManager namens Manager.
+ Die Methode GetLock() blockiert, bis der Mutex verfügbar ist.
+ Die Methode Release() gibt den Mutex frei und behandelt freigabe-bezogene Fehler.
+ Ein String Running (wird hier aber nicht verwendet).
+
+Null Pointer Checks
+ Manager: Im Konstruktor wird garantiert, dass Manager immer initialisiert ist (niemals null nach Konstruktion).
+ m_lock: Wird direkt beim Feld-Deklaration erstellt, kann also nie null sein.
+ Locked: Ist ein bool, daher nie null.
+ Running: Ist public, aber im Code nicht direkt verwendet. Wenn er von außen auf null gesetzt wird, kann das keinen Fehler verursachen, da er nicht verwendet wird.
+
+Fehlerquellen
+ Mutex-Handling:
+ In Release() wird das Freigeben des Mutex von einem Try-Catch-Block umschlossen. Sollte ein Fehler beim Freigeben auftreten (z.B. Freigabe von einem Thread, der den Mutex nicht besitzt), wird die Exception geloggt und erneut geworfen.
+ In GetLock() wird WaitOne() direkt aufgerufen, was blockiert, bis der Lock verfügbar ist. Es gibt hier keine Exception-Absicherung, aber üblicherweise ist dies sicher, solange das Objekt korrekt verwendet wird.
+
+ Thread-Safety:
+ Die Klasse ist thread-safe bezüglich des Mutex, nicht aber bezüglich der Instanzvariablen (Running ist public ohne Schutz, aber nicht verwendet).
+ Es wird kein expliziter Null-Check auf Manager benötigt, da sie im Konstruktor immer initialisiert wird.
+
+ Unbenutzte Variable:
+ Das Feld Running ist nicht implementiert/genutzt. Das ist kein Fehler, aber ein Hinweis auf evtl. toten Code.
+
+Zusammenfassung
+ Null Pointer: Keine Gefahr für NullPointer-Exceptions im aktuellen Code.
+ Fehlerquellen: Einzige potenzielle Fehlerquelle ist das Freigeben des Mutex in Release(), aber dies wird per Exception abgefangen und geloggt.
+ Funktion: Thread-Safe-Manager/Wrapper für den Zugriff auf einen MySQLMoneyManager.
+
+Fazit:
+Die Klasse ist solide und sicher gegen NullPointer-Fehler. Fehler beim Mutex-Handling werden korrekt behandelt. Es gibt keine offensichtlichen Schwachstellen.
+Lediglich das Feld Running ist ungenutzt und könnte entfernt oder implementiert werden.
+ */
+
+using MySqlX.XDevAPI;
+
+using OpenMetaverse.ImportExport.Collada14;
+
+using OpenSim.Data.MySQL.MySQLMoneyDataWrapper;
+
+using System;
+using System.Diagnostics;
+using System.Runtime.ConstrainedExecution;
+using System.Threading;
+
+namespace OpenSim.Data.MySQL.MySQLMoneyDataWrapper
+{
+ public class MySQLSuperManager(string connectionString)
+ {
+ public bool Locked { get; private set; }
+ private readonly Mutex m_lock = new(false);
+ public MySQLMoneyManager Manager { get; } = new MySQLMoneyManager(connectionString);
+ public string Running;
+
+ /// Erwirbt die exklusive Sperre für kritische Operationen.
+ public void GetLock()
+ {
+ m_lock.WaitOne();
+ Locked = true;
+ }
+
+ /// Gibt die Sperre wieder frei.
+ public void Release()
+ {
+ try
+ {
+ m_lock.ReleaseMutex();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Ein Fehler beim Freigeben des Mutex ist aufgetreten: {ex.Message}");
+ throw;
+ }
+ finally
+ {
+ Locked = false;
+ }
+ }
+ }
+}
diff --git a/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/Properties/AssemblyInfo.cs b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..7ae9f91
--- /dev/null
+++ b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/Properties/AssemblyInfo.cs
@@ -0,0 +1,32 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OpenSim.Data.MySQL.MySQLMoneyDataWrapper")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("OpenSim.Data.MySQL.MySQLMoneyDataWrapper")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("03f4a266-2e30-47e4-b785-ff02cdb4bbb9")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/TransactionData.cs b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/TransactionData.cs
new file mode 100644
index 0000000..bb9a313
--- /dev/null
+++ b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/OpenSim.Data.MySQL.MySQLMoneyDataWrapper/TransactionData.cs
@@ -0,0 +1,291 @@
+/*
+ * 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 OpenSim 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.
+
+Funktion
+ TransactionData ist eine reine Datenklasse (POCO/DTO), die alle relevanten Felder für eine Finanztransaktion im OpenSim-Money-Modul kapselt.
+ Felder: UUIDs, Beträge, Balances, Typen, Zeiten, Status, Objekt- und Regionsdaten, Sicherheitscode, Beschreibung usw.
+ Für alle Felder gibt es Properties mit Getter/Setter.
+ Zwei Enums:
+ Status (SUCCESS, PENDING, FAILED, ERROR)
+ AvatarType (diverse Avatar-Typen, z.B. LOCAL, HG, GUEST, NPC)
+ UserInfo ist ebenfalls eine reine Datenklasse für Benutzerinformationen, mit Properties für UserID, SimIP, Name, Passwort-Hash, Typ, Klasse, ServerURL.
+
+Null Pointer & Fehlerquellen
+Null Pointer
+ Felder vom Typ string werden grundsätzlich mit string.Empty initialisiert (niemals null im Standardzustand).
+ UUIDs werden (soweit gesetzt) aus dem Typ OpenMetaverse.UUID initialisiert – dieser Typ ist ein struct und kann daher nie null sein.
+ Es gibt keine Methoden mit Logik, daher auch keine Parameterprüfungen oder komplexe Objekt-Manipulationen, bei denen NullPointer auftreten könnten.
+ Setter/Getter der Properties geben direkt das Feld zurück oder setzen es, ohne Logik.
+ Die Enums können nicht null sein.
+
+Fehlerquellen
+ Keine Methoden mit Logik, also keine klassische Fehlerquelle wie Division durch 0, Indexfehler o.ä.
+ Setter führen keine Validierung durch (z.B. auf gültige UUIDs, Wertebereiche),
+ was bei fehlerhafter Nutzung zu inkonsistenten Daten führen könnte – das ist aber kein NullPointer und im Kontext von Datenklassen üblich.
+ UserInfo: Alle Felder sind mit sinnvollen Defaultwerten versehen (string.Empty oder ein Enum-Wert).
+
+Zusammenfassung
+ Null Pointer: Es gibt im aktuellen Code keine Gefahr für NullPointer-Exceptions, da alle Felder mit Defaultwerten belegt sind und keine Logik existiert.
+ Fehlerquellen: Keine Methoden, keine Validierung – klassisch für Datenklassen.
+ Fehler können nur entstehen, wenn von außen ungültige Werte gesetzt werden, aber das verursacht keine NullPointer-Fehler.
+ Funktion: Kapselt Daten für Transaktionen und Benutzer im OpenSim-Money-System.
+
+Fazit:
+Die Datei ist eine reine Datenstruktur und sicher gegenüber NullPointer-Fehlern. Es gibt keine gefährlichen Stellen im Code.
+Die Nutzung ist robust, solange von außen sinnvolle Werte gesetzt werden.
+ */
+
+using OpenMetaverse;
+
+
+namespace OpenSim.Data.MySQL.MySQLMoneyDataWrapper
+{
+ public class TransactionData
+ {
+ UUID m_uuid;
+ string m_sender = string.Empty;
+ string m_receiver = string.Empty;
+ int m_amount;
+ int m_senderBalance;
+ int m_receiverBalance;
+ int m_type;
+ int m_time;
+ int m_status;
+ string m_objectID = UUID.Zero.ToString();
+ string m_objectName = string.Empty;
+ string m_regionHandle = string.Empty;
+ string m_regionUUID = string.Empty;
+ string m_secureCode = string.Empty;
+ string m_commonName = string.Empty;
+ string m_description = string.Empty;
+
+ /// Gets or sets the trans UUID.
+ /// The trans UUID.
+ public UUID TransUUID
+ {
+ get { return m_uuid; }
+ set { m_uuid = value; }
+ }
+
+ /// Gets or sets the sender.
+ /// The sender.
+ public string Sender
+ {
+ get { return m_sender; }
+ set { m_sender = value; }
+ }
+
+ /// Gets or sets the receiver.
+ /// The receiver.
+ public string Receiver
+ {
+ get { return m_receiver; }
+ set { m_receiver = value; }
+ }
+
+ /// Gets or sets the amount.
+ /// The amount.
+ public int Amount
+ {
+ get { return m_amount; }
+ set { m_amount = value; }
+ }
+
+ /// Gets or sets the sender balance.
+ /// The sender balance.
+ public int SenderBalance
+ {
+ get { return m_senderBalance; }
+ set { m_senderBalance = value; }
+ }
+
+ /// Gets or sets the receiver balance.
+ /// The receiver balance.
+ public int ReceiverBalance
+ {
+ get { return m_receiverBalance; }
+ set { m_receiverBalance = value; }
+ }
+
+ /// Gets or sets the type.
+ /// The type.
+ public int Type
+ {
+ get { return m_type; }
+ set { m_type = value; }
+ }
+
+ /// Gets or sets the time.
+ /// The time.
+ public int Time
+ {
+ get { return m_time; }
+ set { m_time = value; }
+ }
+
+ /// Gets or sets the status.
+ /// The status.
+ public int Status
+ {
+ get { return m_status; }
+ set { m_status = value; }
+ }
+
+ /// Gets or sets the description.
+ /// The description.
+ public string Description
+ {
+ get { return m_description; }
+ set { m_description = value; }
+ }
+
+ /// Gets or sets the object UUID.
+ /// The object UUID.
+ public string ObjectUUID
+ {
+ get { return m_objectID; }
+ set { m_objectID = value; }
+ }
+
+ /// Gets or sets the name of the object.
+ /// The name of the object.
+ public string ObjectName
+ {
+ get { return m_objectName; }
+ set { m_objectName = value; }
+ }
+
+ /// Gets or sets the region handle.
+ /// The region handle.
+ public string RegionHandle
+ {
+ get { return m_regionHandle; }
+ set { m_regionHandle = value; }
+ }
+
+ /// Gets or sets the region UUID.
+ /// The region UUID.
+ public string RegionUUID
+ {
+ get { return m_regionUUID; }
+ set { m_regionUUID = value; }
+ }
+
+ /// Gets or sets the secure code.
+ /// The secure code.
+ public string SecureCode
+ {
+ get { return m_secureCode; }
+ set { m_secureCode = value; }
+ }
+
+ /// Gets or sets the name of the common.
+ /// The name of the common.
+ public string CommonName
+ {
+ get { return m_commonName; }
+ set { m_commonName = value; }
+ }
+ }
+
+
+ public enum Status
+ {
+ SUCCESS_STATUS = 0,
+ PENDING_STATUS = 1,
+ FAILED_STATUS = 2,
+ ERROR_STATUS = 9
+ }
+
+
+ public enum AvatarType
+ {
+ LOCAL_AVATAR = 0,
+ HG_AVATAR = 1,
+ NPC_AVATAR = 2,
+ GUEST_AVATAR = 3,
+ FOREIGN_AVATAR = 8,
+ UNKNOWN_AVATAR = 9
+ }
+
+
+ public class UserInfo
+ {
+ string m_userID = string.Empty;
+ string m_simIP = string.Empty;
+ string m_avatarName = string.Empty;
+ string m_passwordHash = string.Empty;
+ int m_avatarType = (int)AvatarType.LOCAL_AVATAR;
+ int m_avatarClass = (int)AvatarType.LOCAL_AVATAR;
+ string m_serverURL = string.Empty;
+
+ /// Gets or sets the user identifier.
+ /// The user identifier.
+ public string UserID
+ {
+ get { return m_userID; }
+ set { m_userID = value; }
+ }
+
+ /// Gets or sets the sim ip.
+ /// The sim ip.
+ public string SimIP
+ {
+ get { return m_simIP; }
+ set { m_simIP = value; }
+ }
+
+ /// Gets or sets the avatar.
+ /// The avatar.
+ public string Avatar
+ {
+ get { return m_avatarName; }
+ set { m_avatarName = value; }
+ }
+
+ /// Gets or sets the PSW hash.
+ /// The PSW hash.
+ public string PswHash
+ {
+ get { return m_passwordHash; }
+ set { m_passwordHash = value; }
+ }
+
+ /// Gets or sets the type.
+ /// The type.
+ public int Type
+ {
+ get { return m_avatarType; }
+ set { m_avatarType = value; }
+ }
+
+ /// Gets or sets the class.
+ /// The class.
+ public int Class
+ {
+ get { return m_avatarClass; }
+ set { m_avatarClass = value; }
+ }
+
+ /// Gets or sets the server URL.
+ /// The server URL.
+ public string ServerURL
+ {
+ get { return m_serverURL; }
+ set { m_serverURL = value; }
+ }
+ }
+}
diff --git a/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/prebuild-MySQLMoneyDataWrapper.xml b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/prebuild-MySQLMoneyDataWrapper.xml
new file mode 100644
index 0000000..af0a57f
--- /dev/null
+++ b/addon-modules/OpenSim-Data-MySQL-MySQLMoneyDataWrapper/prebuild-MySQLMoneyDataWrapper.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ ../../bin/
+ true
+
+
+
+
+ ../../bin/
+ true
+
+
+
+ ../../bin/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/CurrencyStreamHandler.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/CurrencyStreamHandler.cs
new file mode 100644
index 0000000..3e4b688
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/CurrencyStreamHandler.cs
@@ -0,0 +1,61 @@
+/*
+Hier ist eine Analyse der Datei CurrencyStreamHandler.cs hinsichtlich möglicher Null-Pointer, Fehlerquellen und Funktion:
+
+### Funktion des Codes
+
+Die Datei definiert die Klasse CurrencyStreamHandler im Namespace OpenSim.Grid.MoneyServer.
+Diese Klasse erbt von CustomSimpleStreamHandler und dient dazu, HTTP-Requests unter einer bestimmten Pfadangabe zu behandeln.
+Im Konstruktor werden ein Pfad (string path) und eine Callback-Action (Action processAction) übergeben und direkt an die Basisklasse weitergereicht.
+
+### Code-Check: Null-Pointer & Fehlerquellen
+
+#### 1. Konstruktor
+
+```csharp
+public CurrencyStreamHandler(string path, Action processAction)
+ : base(path, processAction)
+{
+}
+```
+
+-**Fehleranfällig für Null-Pointer:**
+ -Wenn `path` oder `processAction` null ist und die Basisklasse (CustomSimpleStreamHandler) keine Null-Prüfung durchführt, könnte es zu NullReferenceExceptions kommen.
+- **Empfehlung:**
+ -Füge Null - Checks hinzu:
+ ```csharp
+ if (path == null) throw new ArgumentNullException(nameof(path));
+if (processAction == null) throw new ArgumentNullException(nameof(processAction));
+ ```
+-**Funktion:**
+ -Die Klasse dient als spezialisierter HTTP-Handler, der Requests auf einen bestimmten Pfad verarbeitet, wobei das eigentliche Verhalten durch die übergebene Action bestimmt wird.
+
+#### 2. Keine weitere Logik
+
+- Die Klasse enthält keine eigene Logik, sondern reicht alles an die Basisklasse weiter.
+- Es gibt keine Felder oder Methoden, die potenziell NullPointer verursachen könnten.
+
+### Zusammenfassung
+
+- **Funktion:**Registrierung eines HTTP-Stream-Handlers für einen bestimmten Pfad mit benutzerdefiniertem Callback.
+- **Mögliche Null-Pointer:**Konstruktorparameter könnten null sein.
+- **Empfehlung:**Null - Checks für Konstruktorparameter hinzufügen, falls die Basisklasse dies nicht übernimmt.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using OpenSim.Framework.Servers.HttpServer;
+
+namespace OpenSim.Grid.MoneyServer
+{
+ public class CurrencyStreamHandler : CustomSimpleStreamHandler
+ {
+ public CurrencyStreamHandler(string path, Action processAction)
+ : base(path, processAction)
+ {
+ }
+ }
+}
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/CustomSimpleStreamHandler.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/CustomSimpleStreamHandler.cs
new file mode 100644
index 0000000..264bc89
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/CustomSimpleStreamHandler.cs
@@ -0,0 +1,110 @@
+/*
+Funktion des Codes
+
+ Die Klasse CustomSimpleStreamHandler erweitert SimpleBaseRequestHandler und implementiert ISimpleStreamHandler.
+ Sie dient als generischer HTTP-Handler, der verschiedene Arten von Authentifizierung und Verarbeitungslogik unterstützt.
+ Konstruktoren erlauben flexible Kombinationen aus Pfad, Name, Auth-Objekt, Prozess-Methoden und Callbacks.
+ Die zentrale Methode ist Handle, die einen HTTP-Request verarbeitet: Authentifizierung, dann die eigentliche Verarbeitung durch Callback, Delegate oder Fallback-Logik.
+
+Verbesserungsvorschläge
+
+ Logging im Catch-Block für bessere Fehlerdiagnose.
+ Optional: Warnung oder Exception, wenn weder m_processAction, m_processRequest gesetzt ist noch ProcessRequest überschrieben wurde.
+ Optional: Dokumentation, dass für sinnvolle Funktionalität eine dieser Methoden/Callbacks gesetzt sein muss.
+
+Fazit:
+Die Klasse ist robust gegen NullPointer-Exceptions, weil überall sauber geprüft wird. Die Fehlerbehandlung ist sehr generisch – für Produktionseinsatz wäre Logging sinnvoll.
+Die eigentliche Funktion ist flexibel: Sie ermöglicht Authentifizierung und verschiedene Arten von Request-Handling in HTTP-Servern.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using OpenSim.Framework.Servers.HttpServer;
+using System.Net;
+using OpenSim.Framework.ServiceAuth;
+
+namespace OpenSim.Grid.MoneyServer
+{
+ public class CustomSimpleStreamHandler : SimpleBaseRequestHandler, ISimpleStreamHandler
+ {
+ protected IServiceAuth m_Auth;
+ protected SimpleStreamMethod m_processRequest;
+ private Action m_processAction;
+
+ public CustomSimpleStreamHandler(string path) : base(path) { }
+
+ public CustomSimpleStreamHandler(string path, string name) : base(path, name) { }
+
+ public CustomSimpleStreamHandler(string path, SimpleStreamMethod processRequest) : base(path)
+ {
+ m_processRequest = processRequest;
+ }
+
+ public CustomSimpleStreamHandler(string path, SimpleStreamMethod processRequest, string name) : base(path, name)
+ {
+ m_processRequest = processRequest;
+ }
+
+ public CustomSimpleStreamHandler(string path, IServiceAuth auth) : base(path)
+ {
+ m_Auth = auth;
+ }
+
+ public CustomSimpleStreamHandler(string path, IServiceAuth auth, SimpleStreamMethod processRequest) : base(path)
+ {
+ m_Auth = auth;
+ m_processRequest = processRequest;
+ }
+
+ public CustomSimpleStreamHandler(string path, IServiceAuth auth, SimpleStreamMethod processRequest, string name) : base(path, name)
+ {
+ m_Auth = auth;
+ m_processRequest = processRequest;
+ }
+
+ public CustomSimpleStreamHandler(string path, Action processAction) : base(path)
+ {
+ m_processAction = processAction;
+ }
+
+ public virtual void Handle(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+ {
+ RequestsReceived++;
+
+ if (m_Auth != null)
+ {
+ HttpStatusCode statusCode;
+
+ if (!m_Auth.Authenticate(httpRequest.Headers, httpResponse.AddHeader, out statusCode))
+ {
+ httpResponse.StatusCode = (int)statusCode;
+ return;
+ }
+ }
+
+ try
+ {
+ if (m_processAction != null)
+ m_processAction(httpRequest, httpResponse);
+ else if (m_processRequest != null)
+ m_processRequest(httpRequest, httpResponse);
+ else
+ ProcessRequest(httpRequest, httpResponse);
+ }
+ catch
+ {
+ httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError;
+ }
+
+ RequestsHandled++;
+ }
+
+ protected virtual void ProcessRequest(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+ {
+ // Override in derived classes to implement the request handling logic.
+ }
+ }
+}
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/IMoneyDBService.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/IMoneyDBService.cs
new file mode 100644
index 0000000..529d36a
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/IMoneyDBService.cs
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/, http://www.nsl.tuis.ac.jp/ 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 OpenSim 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.
+
+Funktion
+
+IMoneyDBService ist ein Interface für Datenbankoperationen rund um ein Währungssystem, typischerweise für virtuelle Ökonomien. Es definiert Methoden für:
+
+ Nutzerverwaltung (z.B. addUser, DeleteUser, UserExists, UpdateUserInfo)
+ Kontostand- und Geldtransaktionen (getBalance, withdrawMoney, giveMoney, BuyMoney, BuyCurrency, PerformMoneyTransfer)
+ Transaktionsmanagement (addTransaction, updateTransactionStatus, FetchTransaction, GetTransactionHistory, SetTransExpired)
+ Fehlerprotokollierung (LogTransactionError)
+ Authentifizierungen und Validierungen (ValidateTransfer)
+ Verbindungsmanagement (GetLockedConnection)
+
+Die Methoden sind so gestaltet, dass sie in einer konkreten Implementierung mit einer Datenbank (z.B. MySQL) interagieren.
+ */
+
+
+using OpenMetaverse;
+
+using OpenSim.Data.MySQL.MySQLMoneyDataWrapper;
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace OpenSim.Grid.MoneyServer
+{
+ ///
+ /// IMoney DB Service
+ ///
+ public interface IMoneyDBService
+ {
+ int CheckMaximumMoney(string userID, int m_CurrencyMaximum);
+ Hashtable ApplyFallbackCredit(string agentId);
+
+ void InitializeUserCurrency(string agentId);
+
+ bool PerformMoneyTransfer(string senderID, string receiverID, int amount);
+
+ /// Ruft den Kontostand ab.
+ /// Die Benutzer-ID.
+ int getBalance(string userID);
+
+ /// Zieht Geld ab.
+ /// Die Transaktions-ID.
+ /// Die Absender-ID.
+ /// Der Betrag.
+ bool withdrawMoney(UUID transactionID, string senderID, int amount);
+
+ /// Gibt Geld.
+ /// Die Transaktions-ID.
+ /// Die Empfänger-ID.
+ /// Der Betrag.
+ bool giveMoney(UUID transactionID, string receiverID, int amount);
+
+ /// Kauft Geld für einen Benutzer.
+ /// Die Transaktions-ID.
+ /// Die Benutzer-ID.
+ /// Der zu kaufende Betrag.
+ /// True, wenn der Kauf erfolgreich war, andernfalls false.
+ bool BuyMoney(UUID transactionID, string userID, int amount);
+
+ bool BuyCurrency(string userID, int amount);
+
+ /// Fügt die Transaktion hinzu.
+ /// Die Transaktion.
+ bool addTransaction(TransactionData transaction);
+
+ /// Fügt den Benutzer hinzu.
+ /// Die Benutzer-ID.
+ /// Der Kontostand.
+ /// Der Status.
+ /// Der Typ.
+ bool addUser(string userID, int balance, int status, int type);
+
+ /// Aktualisiert den Transaktionsstatus.
+ /// Die Transaktions-ID.
+ /// Der Status.
+ /// Die Beschreibung.
+ bool updateTransactionStatus(UUID transactionID, int status, string description);
+
+ /// Setzt die Transaktion als abgelaufen.
+ /// Die Ablaufzeit.
+ bool SetTransExpired(int deadTime);
+
+ /// Validiert die Überweisung.
+ /// Der Sicherheitscode.
+ /// Die Transaktions-ID.
+ bool ValidateTransfer(string secureCode, UUID transactionID);
+
+ /// Ruft die Transaktion ab.
+ /// Die Transaktions-ID.
+ TransactionData FetchTransaction(UUID transactionID);
+
+ /// Ruft die Transaktion ab.
+ /// Die Benutzer-ID.
+ /// Die Startzeit.
+ /// Die Endzeit.
+ /// Der letzte Index.
+ TransactionData FetchTransaction(string userID, int startTime, int endTime, int lastIndex);
+
+ /// Ruft die Anzahl der Transaktionen ab.
+ /// Die Benutzer-ID.
+ /// Die Startzeit.
+ /// Die Endzeit.
+ int getTransactionNum(string userID, int startTime, int endTime);
+
+ /// Führt die Überweisung durch.
+ /// Die Transaktions-UUID.
+ bool DoTransfer(UUID transactionUUID);
+
+ /// Führt die Geldaufstockung durch.
+ /// Die Transaktions-UUID.
+ bool DoAddMoney(UUID transactionUUID); // Hinzugefügt von Fumi.Iseki
+
+ /// Versucht, Benutzerinformationen hinzuzufügen.
+ /// Der Benutzer.
+ bool TryAddUserInfo(UserInfo user);
+
+ /// Ruft die Benutzerinformationen ab.
+ /// Die Benutzer-ID.
+ UserInfo FetchUserInfo(string userID);
+
+ /// Überprüft, ob ein Benutzer existiert.
+ /// Die Benutzer-ID.
+ bool UserExists(string userID);
+
+ /// Aktualisiert die Benutzerinformationen.
+ /// Die Benutzer-ID.
+ /// Die aktualisierten Benutzerinformationen.
+ bool UpdateUserInfo(string userID, UserInfo updatedInfo);
+
+ /// Löscht einen Benutzer aus der Datenbank.
+ /// Die Benutzer-ID.
+ bool DeleteUser(string userID);
+
+ /// Protokolliert transaktionsbezogene Fehler oder Änderungen zur Fehlerbehebung.
+ /// Die Transaktions-ID.
+ /// Die Fehlermeldung oder Änderungshinweis.
+ void LogTransactionError(UUID transactionID, string errorMessage);
+
+ /// Ruft eine Liste der Transaktionen für einen Benutzer innerhalb eines bestimmten Zeitrahmens ab.
+ /// Die Benutzer-ID.
+ /// Die Startzeit.
+ /// Die Endzeit.
+ IEnumerable GetTransactionHistory(string userID, int startTime, int endTime);
+ MySQLSuperManager GetLockedConnection();
+ }
+}
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/IMoneyServiceCore.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/IMoneyServiceCore.cs
new file mode 100644
index 0000000..3d7b695
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/IMoneyServiceCore.cs
@@ -0,0 +1,79 @@
+/*
+ * 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 OpenSim 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.
+
+Funktion
+Das Interface IMoneyServiceCore definiert grundlegende Service-Methoden für den Kern des MoneyServers. Es stellt folgende Funktionalitäten bereit:
+
+ Zugriff auf den HTTP-Server (GetHttpServer)
+ Zugriff auf verschiedene Session-Dictionaries (GetSessionDic, GetSecureSessionDic, GetWebSessionDic)
+ Zugriff auf Konfigurationen (GetServerConfig, GetCertConfig)
+ Prüfen, ob ein Client-Zertifikat geprüft wird (IsCheckClientCert)
+
+Null-Pointer & Fehlerquellen
+ Da es sich um ein Interface handelt, gibt es keine Implementierung und somit auch keine direkte Gefahr für NullPointerExceptions im Interface selbst.
+ Die Gefahr von NullPointerExceptions entsteht erst in der konkreten Implementierung, wenn z.B. eine der Get*-Methoden null zurückliefert und der Aufrufer das Ergebnis nicht prüft.
+
+Typische Fehlerquellen in der Implementierung:
+ Rückgabe von null bei Methoden, die Dictionaries oder Konfigurationen liefern sollen.
+ Nicht initialisierte Objekte in der Implementierung dieser Methoden.
+ Der HTTP-Server oder die Konfigurationen könnten in Implementierungen nicht korrekt gesetzt sein.
+
+Empfehlungen für Implementierungen
+ Immer sicherstellen, dass Methoden niemals null zurückliefern, außer das Verhalten ist explizit dokumentiert und der Aufrufer prüft dies.
+ Bei Fehlern oder fehlenden Objekten lieber leere Dictionaries oder Default-Konfigurationen zurückgeben.
+ Gute Dokumentation, wann Methoden null liefern könnten.
+
+Zusammenfassung
+ Funktion: Interface zur Bereitstellung zentraler Kernfunktionen für den MoneyServer.
+ Null-Pointer: Keine Gefahr im Interface selbst, aber möglicherweise in der Implementierung, wenn Rückgabewerte null sind.
+ Empfehlung: In der Implementierung auf robuste Rückgaben achten und keine null-Referenzen zurückgeben.
+ */
+
+using Nini.Config;
+
+using OpenSim.Framework.Servers.HttpServer;
+
+using System.Collections.Generic;
+
+
+namespace OpenSim.Grid.MoneyServer
+{
+ public interface IMoneyServiceCore
+ {
+ /// Gets the HTTP server.
+ BaseHttpServer GetHttpServer();
+
+ /// Gets the session dic.
+ Dictionary GetSessionDic();
+
+ /// Gets the secure session dic.
+ Dictionary GetSecureSessionDic();
+
+ /// Gets the web session dic.
+ Dictionary GetWebSessionDic();
+
+ /// Gets the server configuration.
+ IConfig GetServerConfig();
+
+ /// Gets the cert configuration.
+ IConfig GetCertConfig();
+
+ /// Determines whether [is check client cert].
+ ///
+ /// true if [is check client cert]; otherwise, false.
+ bool IsCheckClientCert();
+ }
+}
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/LandtoolStreamHandler.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/LandtoolStreamHandler.cs
new file mode 100644
index 0000000..a635834
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/LandtoolStreamHandler.cs
@@ -0,0 +1,37 @@
+/*
+Funktion des Codes
+ Die Klasse LandtoolStreamHandler erbt von CustomSimpleStreamHandler.
+ Sie ist ein HTTP-Handler für einen bestimmten Pfad (path) und eine Verarbeitungsaktion (processAction).
+ Der Konstruktor nimmt einen Pfad und eine Action als Parameter und gibt diese direkt an die Basisklasse weiter.
+
+Zusammenfassung
+ Funktion:
+ Registriert einen HTTP-Handler für einen bestimmten Pfad mit einer Callback-Action für die Verarbeitung von Requests.
+ Null-Pointer:
+ Die Parameter könnten theoretisch null sein, werden aber im Ablauf geprüft. Kritische Fehler sind unwahrscheinlich, aber für "sauberen" Code sollten idealerweise Null-Prüfungen im Konstruktor ergänzt werden.
+ Fehlerquellen:
+ Keine weiteren offensichtlichen Fehler in dieser Datei. Die Robustheit hängt von der Basisklasse und der übergebenen Action ab.
+
+Fazit:
+Der Code ist funktional korrekt und sicher gegen NullPointer-Fehler im laufenden Betrieb, solange die Basisklasse und die Action korrekt implementiert sind.
+Für zusätzliche Sicherheit könnten Null-Checks im Konstruktor ergänzt werden, sind aber nicht zwingend notwendig.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using OpenSim.Framework.Servers.HttpServer;
+
+namespace OpenSim.Grid.MoneyServer
+{
+ public class LandtoolStreamHandler : CustomSimpleStreamHandler
+ {
+ public LandtoolStreamHandler(string path, Action processAction)
+ : base(path, processAction)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyDBService.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyDBService.cs
new file mode 100644
index 0000000..7510efa
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyDBService.cs
@@ -0,0 +1,1205 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/, http://www.nsl.tuis.ac.jp/ 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 OpenSim 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.
+
+Funktion
+Die Klasse MoneyDBService implementiert ein Datenbank-Interface für einen Währungsserver (IMoneyDBService).
+Sie verwaltet u.a. Benutzerkonten, Transaktionen, Transfers, Guthaben und Fehlerprotokollierung über eine MySQL-Datenbank.
+Null-Pointer-Checks & Fehlerquellen
+
+1. Konstruktor und Initialisierung
+ Es gibt einen string-Parameter-Konstruktor und einen parameterlosen Konstruktor.
+ Der Initialisierungsprozess prüft, ob der Connection-String leer ist (if (connectionString != string.Empty)), aber nicht explizit auf null.
+ → Ein null-Wert könnte zu einem Fehler führen, bevor Zeile 74 erreicht wird.
+
+2. DB-Verbindungsmanagement
+ GetLockedConnection() prüft, ob die Verbindungsanzahl korrekt gesetzt ist und ob ein Connection-Objekt im Pool existiert.
+ Bei fehlender Verbindung wird ein Fehler geloggt und eine Exception geworfen (KeyNotFoundException).
+ Die Methode ist robust gegen NullPointer, solange die Initialisierung korrekt verlief.
+
+3. Methoden für Datenbankoperationen
+ Fast alle Datenbankmethoden sind in try-catch-finally-Blöcke gekapselt:
+ Nach MySQL-Fehlern wird ein Reconnect versucht und die Operation wiederholt.
+ Bei anderen Exceptions wird ein Fehler geloggt und ein sinnvoller Rückgabewert geliefert (z.B. 0, false, oder leere Objekte).
+ Ressourcenfreigabe (dbm.Release()) geschieht immer im finally-Block, was Memory Leaks vorbeugt.
+ Rückgabewerte von Methoden wie FetchTransaction werden auf null geprüft, bevor sie weiterverwendet werden.
+ Methoden, die Objekte aus der DB holen, geben bei Fehlern null oder leere Listen zurück.
+
+4. SQL- und Parameterhandling
+ Vor SQL-Operationen werden die Parameter korrekt gesetzt.
+ Es gibt keine offensichtlichen SQL-Injection-Lücken, da überall Parameter verwendet werden.
+
+5. Typische Fehlerquellen
+ Fehlende Null-Checks bei Eingaben: Die meisten Methoden prüfen nicht explizit, ob string-Parameter wie userID oder agentId null oder leer sind. Das könnte zu Fehlern führen, wenn solche Werte übergeben werden.
+ Release von Verbindungen: In einigen Methoden (z.B. DoTransfer, DoAddMoney) wird die Verbindung teilweise vorzeitig bei Fehlern freigegeben, aber insgesamt ist das Ressourcenmanagement solide.
+ Fehlende Existenzprüfungen: Manche Methoden gehen davon aus, dass DB-Objekte wie TransactionData oder UserInfo existieren und korrekt initialisiert sind.
+
+6. Sonstige Fehlerbehandlung
+ Fehler werden fast immer geloggt (m_log.ErrorFormat, m_log.Error).
+ In kritischen Fällen werden Exceptions weitergeworfen, in anderen Fällen gibt es Fallback-Rückgaben ohne Exception.
+
+Zusammenfassung
+ Funktion:
+ Verwaltung des Währungsverkehrs (Konten, Transaktionen, Guthaben) für OpenSim über eine MySQL-Datenbank.
+ NullPointer/Fehlerquellen:
+ Fast überall sauber gegen NullPointer und Datenbankfehler abgesichert (try-catch-finally).
+ Mögliche Risiken bei Methodenparametern, die nicht explizit auf null geprüft werden.
+ Bei fehlerhafter Initialisierung oder fehlerhaftem Connection-String kann es zu Start-Exceptions kommen.
+ Verbesserungspotential:
+ Zusätzliche Null-Checks und Plausibilitätsprüfungen für alle Methodenparameter.
+ Noch mehr Logging in Ausnahmefällen.
+ */
+
+using log4net;
+using MySql.Data.MySqlClient;
+
+using Mysqlx.Crud;
+using Mysqlx.Notice;
+using OpenMetaverse;
+using OpenSim.Data.MySQL.MySQLMoneyDataWrapper;
+using OpenSim.Modules.Currency;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+
+namespace OpenSim.Grid.MoneyServer
+{
+ class MoneyDBService : IMoneyDBService
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ private string m_connect;
+ private long TicksToEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
+
+ // DB manager pool
+ protected Dictionary m_dbconnections = new Dictionary(); // with Lock
+ private int m_maxConnections;
+
+ public int m_lastConnect = 0;
+
+ public MoneyDBService(string connect)
+ {
+ m_connect = connect;
+ Initialise(m_connect, 10);
+ }
+
+ public MoneyDBService()
+ {
+ }
+
+ public readonly object connectionLock = new object();
+
+ public void Initialise(string connectionString, int maxDBConnections)
+ {
+ lock (connectionLock)
+ {
+ if (maxDBConnections <= 0)
+ {
+ throw new ArgumentException("maxDBConnections must be greater than zero", nameof(maxDBConnections));
+ }
+
+ //m_log.InfoFormat("[Initialise]: Setting m_maxConnections to {0}", maxDBConnections);
+ m_connect = connectionString;
+ m_maxConnections = maxDBConnections;
+
+ //m_log.InfoFormat("[Initialise]: m_maxConnections is now {0}", m_maxConnections);
+
+ if (connectionString != string.Empty)
+ {
+ for (int i = 0; i < m_maxConnections; i++)
+ {
+ //m_log.Info("Connecting to DB... [" + i + "]");
+ MySQLSuperManager msm = new MySQLSuperManager(connectionString);
+ m_dbconnections.Add(i, msm);
+ }
+ }
+ else
+ {
+ m_log.Error("[MONEY DB]: Connection string is null, initialise database failed");
+ throw new Exception("Failed to initialise MySql database");
+ }
+ }
+ }
+ public MySQLSuperManager GetLockedConnection()
+ {
+ lock (connectionLock)
+ {
+ //m_log.InfoFormat("[GetLockedConnection]: m_maxConnections is {0}", m_maxConnections);
+ if (m_maxConnections == 0)
+ {
+ throw new InvalidOperationException("m_maxConnections cannot be zero.");
+ }
+
+ int lockedCons = 0;
+ while (true)
+ {
+ m_lastConnect++;
+ //m_log.DebugFormat("GetLockedConnection: m_lastConnect incremented to {0}", m_lastConnect);
+
+ if (m_lastConnect == int.MaxValue)
+ {
+ m_lastConnect = 0;
+ //m_log.Debug("GetLockedConnection: m_lastConnect overflow, resetting to 0");
+ }
+
+ int index = m_lastConnect % m_maxConnections;
+ if (!m_dbconnections.ContainsKey(index))
+ {
+ m_log.ErrorFormat("GetLockedConnection: Invalid connection index {0}", index);
+ throw new KeyNotFoundException($"The given key '{index}' was not present in the dictionary");
+ }
+
+ MySQLSuperManager msm = m_dbconnections[index];
+ //m_log.DebugFormat("GetLockedConnection: Checking connection {0} for lock", msm);
+ if (!msm.Locked)
+ {
+ msm.GetLock();
+ //m_log.DebugFormat("GetLockedConnection: Connection {0} locked successfully", msm);
+ return msm;
+ }
+
+ lockedCons++;
+ //m_log.DebugFormat("GetLockedConnection: Connection {0} is locked, trying next one. lockedCons: {1}", msm, lockedCons);
+
+ if (lockedCons > m_maxConnections)
+ {
+ lockedCons = 0;
+ System.Threading.Thread.Sleep(2000);
+ m_log.Warn("GetLockedConnection: All connections are in use. Probable cause: Something didn't release a mutex properly, or high volume of requests inbound.");
+ }
+ }
+ }
+ }
+
+ public void Reconnect()
+ {
+ //m_log.Debug("Reconnect attempt started.");
+
+ if (m_maxConnections <= 0)
+ {
+ throw new InvalidOperationException("m_maxConnections must be greater than zero.");
+ }
+
+ for (int i = 0; i < m_maxConnections; i++)
+ {
+ MySQLSuperManager msm = m_dbconnections[i];
+ try
+ {
+ msm.Manager.Reconnect();
+ //m_log.DebugFormat("Reconnected to database connection {0} successfully.", i);
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("Failed to reconnect to database connection {0}: {1}", i, ex.Message);
+ }
+ }
+ //m_log.Debug("Reconnect attempt completed.");
+ }
+
+ public int getBalance(string userID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.getBalance(userID);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.getBalance(userID);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return 0;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ public int CheckMaximumMoney(string userID, int m_CurrencyMaximum)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ // Beispielwert, sollte aus der Konfigurationsdatei MoneyServer.ini geladen werden
+ m_log.InfoFormat("[CHECK MAXIMUM MONEY]: Currency Maximum: {0}", m_CurrencyMaximum);
+ if (m_CurrencyMaximum <= 0)
+ {
+ m_CurrencyMaximum = 1000;
+ }
+
+ try
+ {
+ // Ausnahmen f�r SYSTEM und BANKER
+ if (userID == "SYSTEM" || userID == "BANKER")
+ {
+ return 0; // Keine Begrenzung f�r diese Benutzer
+ }
+
+ // Abrufen des aktuellen Guthabens des Benutzers
+ string sql = "SELECT balance FROM balances WHERE user = ?userID";
+ int currentBalance = 0;
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ using (MySqlDataReader reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ currentBalance = reader.GetInt32("balance");
+ }
+ }
+ }
+
+ // �berpr�fen, ob das Guthaben �ber dem Maximum liegt und ggf. abziehen
+ if (currentBalance > m_CurrencyMaximum)
+ {
+ int excessAmount = currentBalance - m_CurrencyMaximum;
+
+ // Guthaben auf das Maximum reduzieren
+ sql = "UPDATE balances SET balance = ?newBalance WHERE user = ?userID";
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?newBalance", m_CurrencyMaximum);
+ cmd.Parameters.AddWithValue("?userID", userID);
+ cmd.ExecuteNonQuery();
+ }
+
+ m_log.InfoFormat("[CheckMaximumMoney]: Reduced balance for user {0} by {1} to enforce maximum limit of {2}", userID, excessAmount, m_CurrencyMaximum);
+ return excessAmount; // R�ckgabe des abgezogenen Betrags
+ }
+
+ return 0; // Keine �nderung, falls das Guthaben innerhalb des Limits liegt
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[CheckMaximumMoney]: Error checking and updating user balance: {0}", ex.Message);
+ throw;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+
+
+
+ /// Withdraws the money.
+ /// The transaction identifier.
+ /// The sender identifier.
+ /// The amount.
+ public bool withdrawMoney(UUID transactionID, string senderID, int amount)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.withdrawMoney(transactionID, senderID, amount);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.withdrawMoney(transactionID, senderID, amount);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+ public bool giveMoney(UUID transactionID, string receiverID, int amount)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ //m_log.Debug("giveMoney: Got locked connection");
+
+ //m_log.DebugFormat("giveMoney: Trying to give {0} units to {1} with transaction ID {2}", amount, receiverID, transactionID);
+
+ try
+ {
+ //m_log.Debug("giveMoney: Trying to execute giveMoney on database");
+ bool result = dbm.Manager.giveMoney(transactionID, receiverID, amount);
+ //m_log.DebugFormat("giveMoney: giveMoney on database returned {0}", result);
+ return result;
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ m_log.ErrorFormat("giveMoney: MySqlException caught: {0}", e.Message);
+ dbm.Manager.Reconnect();
+ //m_log.Debug("giveMoney: Reconnected to database");
+ return dbm.Manager.giveMoney(transactionID, receiverID, amount);
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("giveMoney: Exception caught: {0}", e.Message);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ //m_log.Debug("giveMoney: Released connection");
+ }
+ }
+
+ public bool BuyMoney(UUID transactionID, string userID, int amount)
+ {
+ //m_log.DebugFormat("[BuyMoney]: Start - transactionID: {0}, userID: {1}, amount: {2}", transactionID, userID, amount);
+
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "UPDATE balances SET balance = balance + ?amount WHERE user = ?userID";
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ int rowsAffected = cmd.ExecuteNonQuery();
+ bool result = (rowsAffected > 0);
+
+ if (result)
+ {
+ LogTransaction(transactionID, userID, amount);
+ }
+
+ //m_log.DebugFormat("[BuyMoney]: End - transactionID: {0}, userID: {1}, amount: {2}, result: {3}", transactionID, userID, amount, result);
+ return result;
+ }
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ m_log.ErrorFormat("[BuyMoney]: SQL Exception - {0}", e.Message);
+ dbm.Manager.Reconnect();
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ int rowsAffected = cmd.ExecuteNonQuery();
+ bool result = (rowsAffected > 0);
+
+ if (result)
+ {
+ LogTransaction(transactionID, userID, amount);
+ }
+
+ //m_log.DebugFormat("[BuyMoney]: End after Reconnect - transactionID: {0}, userID: {1}, amount: {2}, result: {3}", transactionID, userID, amount, result);
+ return result;
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[BuyMoney]: General Exception - {0}", e.Message);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ //m_log.Debug("[BuyMoney]: Connection released");
+ }
+ }
+
+ public void LogTransaction(UUID transactionID, string userID, int amount)
+ {
+ //string sql = "INSERT INTO transactions (transactionID, userID, amount, timestamp) VALUES (?transactionID, ?userID, ?amount, ?timestamp)";
+ string sql = "INSERT INTO transactions (userID, amount, timestamp) VALUES (?userID, ?amount, ?timestamp)";
+
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ //cmd.Parameters.AddWithValue("?transactionID", transactionID.ToString());
+ cmd.Parameters.AddWithValue("?userID", userID);
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?timestamp", DateTime.UtcNow);
+
+ cmd.ExecuteNonQuery();
+ }
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+
+ public bool BuyCurrency(string userID, int amount)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ TransactionData transaction = new TransactionData();
+ transaction.TransUUID = UUID.Random();
+ transaction.Sender = UUID.Zero.ToString(); // System sender
+ transaction.Receiver = userID;
+ transaction.Amount = amount;
+ transaction.ObjectUUID = UUID.Zero.ToString();
+ transaction.ObjectName = string.Empty;
+ transaction.RegionHandle = string.Empty;
+ transaction.Type = (int)TransactionType.BuyMoney; // Angenommen, BuyMoney ist ein g�ltiger Transaktionstyp ???
+ transaction.Time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+ transaction.Status = (int)Status.PENDING_STATUS;
+ transaction.SecureCode = UUID.Random().ToString();
+ transaction.CommonName = string.Empty;
+ transaction.Description = "BuyCurrency " + DateTime.UtcNow.ToString();
+
+ bool ret = addTransaction(transaction);
+ if (!ret)
+ {
+ dbm.Release();
+ return false;
+ }
+
+ try
+ {
+ // F�ge Geld dem Benutzerkonto hinzu
+ ret = giveMoney(transaction.TransUUID, userID, amount);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ m_log.Error("[BuyCurrency]: SQL Exception - " + e.ToString());
+ dbm.Manager.Reconnect();
+ ret = giveMoney(transaction.TransUUID, userID, amount);
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[BuyCurrency]: Exception - " + e.ToString());
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ if (ret)
+ {
+ m_log.InfoFormat("[BuyCurrency]: Successfully bought currency for user {0} in amount {1}", userID, amount);
+ }
+ return ret;
+ }
+
+ /// Sets the total sale.
+ /// The transaction.
+ public bool setTotalSale(TransactionData transaction)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ if (transaction.Receiver == transaction.Sender) return false;
+ if (transaction.Sender == UUID.Zero.ToString()) return false;
+
+ int time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+ try
+ {
+ return dbm.Manager.setTotalSale(transaction.Receiver, transaction.ObjectUUID, transaction.Type, 1, transaction.Amount, time);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.setTotalSale(transaction.Receiver, transaction.ObjectUUID, transaction.Type, 1, transaction.Amount, time);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Adds the transaction.
+ /// The transaction.
+ public bool addTransaction(TransactionData transaction)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.addTransaction(transaction);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.addTransaction(transaction);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Adds the user.
+ /// The user identifier.
+ /// The balance.
+ /// The status.
+ /// The type.
+ public bool addUser(string userID, int balance, int status, int type)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ TransactionData transaction = new TransactionData();
+ transaction.TransUUID = UUID.Random();
+ transaction.Sender = UUID.Zero.ToString();
+ transaction.Receiver = userID;
+ transaction.Amount = balance;
+ transaction.ObjectUUID = UUID.Zero.ToString();
+ transaction.ObjectName = string.Empty;
+ transaction.RegionHandle = string.Empty;
+ transaction.Type = (int)TransactionType.BirthGift;
+ transaction.Time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000); ;
+ transaction.Status = (int)Status.PENDING_STATUS;
+ transaction.SecureCode = UUID.Random().ToString();
+ transaction.CommonName = string.Empty;
+ transaction.Description = "addUser " + DateTime.UtcNow.ToString();
+
+ bool ret = addTransaction(transaction);
+ if (!ret)
+ {
+ dbm.Release();
+ return false;
+ }
+
+ try
+ {
+ ret = dbm.Manager.addUser(userID, 0, status, type); // make Balance Table
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ ret = dbm.Manager.addUser(userID, 0, status, type); // make Balance Table
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ //
+ if (ret) ret = giveMoney(transaction.TransUUID, userID, balance);
+ return ret;
+ }
+
+ /// Updates the transaction status.
+ /// The transaction identifier.
+ /// The status.
+ /// The description.
+ public bool updateTransactionStatus(UUID transactionID, int status, string description)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.updateTransactionStatus(transactionID, status, description);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.updateTransactionStatus(transactionID, status, description);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Sets the trans expired.
+ /// The dead time.
+ public bool SetTransExpired(int deadTime)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.SetTransExpired(deadTime);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.SetTransExpired(deadTime);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Validates the transfer.
+ /// The secure code.
+ /// The transaction identifier.
+ public bool ValidateTransfer(string secureCode, UUID transactionID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.ValidateTransfer(secureCode, transactionID);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.ValidateTransfer(secureCode, transactionID);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Fetches the transaction.
+ /// The transaction identifier.
+ public TransactionData FetchTransaction(UUID transactionID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.FetchTransaction(transactionID);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.FetchTransaction(transactionID);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return null;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Fetches the transaction.
+ /// The user identifier.
+ /// The start time.
+ /// The end time.
+ /// The last index.
+ public TransactionData FetchTransaction(string userID, int startTime, int endTime, int lastIndex)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ TransactionData[] arrTransaction;
+
+ uint index = 0;
+ if (lastIndex >= 0) index = Convert.ToUInt32(lastIndex) + 1;
+
+ try
+ {
+ arrTransaction = dbm.Manager.FetchTransaction(userID, startTime, endTime, index, 1);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ arrTransaction = dbm.Manager.FetchTransaction(userID, startTime, endTime, index, 1);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return null;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ //
+ if (arrTransaction.Length > 0)
+ {
+ return arrTransaction[0];
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /// Does the transfer.
+ /// The transaction UUID.
+ public bool DoTransfer(UUID transactionUUID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ bool do_trans = false;
+
+ TransactionData transaction = new TransactionData();
+ transaction = FetchTransaction(transactionUUID);
+
+ if (transaction != null && transaction.Status == (int)Status.PENDING_STATUS)
+ {
+ int balance = getBalance(transaction.Sender);
+
+ //check the amount
+ if (transaction.Amount >= 0 && balance >= transaction.Amount)
+ {
+ if (withdrawMoney(transactionUUID, transaction.Sender, transaction.Amount))
+ {
+ //If receiver not found, add it to DB.
+ if (getBalance(transaction.Receiver) == -1)
+ {
+ m_log.ErrorFormat("[MONEY DB]: DoTransfer: Receiver not found in balances table. {0}", transaction.Receiver);
+ dbm.Release();
+ return false;
+ }
+
+ if (giveMoney(transactionUUID, transaction.Receiver, transaction.Amount))
+ {
+ do_trans = true;
+ }
+ else
+ { // give money to receiver failed. Refund Processing
+ m_log.ErrorFormat("[MONEY DB]: Give money to receiver {0} failed", transaction.Receiver);
+ //Return money to sender
+ if (giveMoney(transactionUUID, transaction.Sender, transaction.Amount))
+ {
+ m_log.ErrorFormat("[MONEY DB]: give money to receiver {0} failed but return it to sender {1} successfully", transaction.Receiver, transaction.Sender);
+ updateTransactionStatus(transactionUUID, (int)Status.FAILED_STATUS, "give money to receiver failed but return it to sender successfully");
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY DB]: FATAL ERROR: Money withdrawn from sender: {0}, but failed to be given to receiver {1}",
+ transaction.Sender, transaction.Receiver);
+ updateTransactionStatus(transactionUUID, (int)Status.ERROR_STATUS, "give money to receiver failed, and return it to sender unsuccessfully!!!");
+ }
+ }
+ }
+ else
+ { // withdraw money failed
+ m_log.ErrorFormat("[MONEY DB]: Withdraw money from sender {0} failed", transaction.Sender);
+ }
+ }
+ else
+ { // not enough balance to finish the transaction
+ m_log.ErrorFormat("[MONEY DB]: Not enough balance for user: {0} to apply the transaction.", transaction.Sender);
+ }
+ }
+ else
+ { // Can not fetch the transaction or it has expired
+ m_log.ErrorFormat("[MONEY DB]: The transaction:{0} has expired", transactionUUID.ToString());
+ }
+
+ //
+ if (do_trans)
+ {
+ setTotalSale(transaction);
+ }
+
+ dbm.Release();
+
+ return do_trans;
+ }
+
+ // by Fumi.Iseki
+ /// Does the add money.
+ /// The transaction UUID.
+ public bool DoAddMoney(UUID transactionUUID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ TransactionData transaction = new TransactionData();
+ transaction = FetchTransaction(transactionUUID);
+
+ if (transaction != null && transaction.Status == (int)Status.PENDING_STATUS)
+ {
+ //If receiver not found, add it to DB.
+ if (getBalance(transaction.Receiver) == -1)
+ {
+ m_log.ErrorFormat("[MONEY DB]: DoAddMoney: Receiver not found in balances table. {0}", transaction.Receiver);
+ dbm.Release();
+ return false;
+ }
+ //
+ if (giveMoney(transactionUUID, transaction.Receiver, transaction.Amount))
+ {
+ setTotalSale(transaction);
+ dbm.Release();
+ return true;
+ }
+ else
+ { // give money to receiver failed.
+ m_log.ErrorFormat("[MONEY DB]: Add money to receiver {0} failed", transaction.Receiver);
+ updateTransactionStatus(transactionUUID, (int)Status.FAILED_STATUS, "add money to receiver failed");
+ }
+ }
+ else
+ { // Can not fetch the transaction or it has expired
+ m_log.ErrorFormat("[MONEY DB]: The transaction:{0} has expired", transactionUUID.ToString());
+ }
+
+ dbm.Release();
+
+ return false;
+ }
+
+ /// Tries the add user information.
+ /// The user.
+ public bool TryAddUserInfo(UserInfo user)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ UserInfo userInfo = null;
+
+ try
+ {
+ userInfo = dbm.Manager.fetchUserInfo(user.UserID);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ userInfo = dbm.Manager.fetchUserInfo(user.UserID);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ dbm.Release();
+ return false;
+ }
+
+ try
+ {
+ if (userInfo != null)
+ {
+ //m_log.InfoFormat("[MONEY DB]: Found user \"{0}\", now update information", user.Avatar);
+ if (dbm.Manager.updateUserInfo(user)) return true;
+ }
+ else if (dbm.Manager.addUserInfo(user))
+ {
+ //m_log.InfoFormat("[MONEY DB]: Unable to find user \"{0}\", add it to DB successfully", user.Avatar);
+ return true;
+ }
+ m_log.InfoFormat("[MONEY DB]: WARNNING: TryAddUserInfo: Unable to TryAddUserInfo.");
+ return false;
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e.ToString());
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Fetches the user information.
+ /// The user identifier.
+ public UserInfo FetchUserInfo(string userID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ UserInfo userInfo = null;
+
+ try
+ {
+ userInfo = dbm.Manager.fetchUserInfo(userID);
+ return userInfo;
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ userInfo = dbm.Manager.fetchUserInfo(userID);
+ return userInfo;
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return null;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ /// Gets the transaction number.
+ /// The user identifier.
+ /// The start time.
+ /// The end time.
+ public int getTransactionNum(string userID, int startTime, int endTime)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ return dbm.Manager.getTransactionNum(userID, startTime, endTime);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.getTransactionNum(userID, startTime, endTime);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return -1;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ public bool UserExists(string userID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ try
+ {
+ return dbm.Manager.UserExists(userID);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.UserExists(userID);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ public bool UpdateUserInfo(string userID, UserInfo updatedInfo)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ try
+ {
+ return dbm.Manager.UpdateUserInfo(userID, updatedInfo);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.UpdateUserInfo(userID, updatedInfo);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ public bool DeleteUser(string userID)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ try
+ {
+ return dbm.Manager.DeleteUser(userID);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.DeleteUser(userID);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ public void LogTransactionError(UUID transactionID, string errorMessage)
+ {
+ m_log.ErrorFormat("[MONEY DB]: Transaction {0} failed with error: {1}", transactionID, errorMessage);
+ }
+
+ public IEnumerable GetTransactionHistory(string userID, int startTime, int endTime)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ try
+ {
+ return dbm.Manager.GetTransactionHistory(userID, startTime, endTime);
+ }
+ catch (MySql.Data.MySqlClient.MySqlException e)
+ {
+ e.ToString();
+ dbm.Manager.Reconnect();
+ return dbm.Manager.GetTransactionHistory(userID, startTime, endTime);
+ }
+ catch (Exception e)
+ {
+ m_log.Error(e);
+ return new List(); // Return an empty list instead of throwing an exception
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+
+ public bool PerformMoneyTransfer(string senderID, string receiverID, int amount)
+ {
+ m_log.InfoFormat("[MONEY TRANSFER]: Transferring {0} from {1} to {2}.", amount, senderID, receiverID);
+
+ // Beispielhafte Implementierung: F�hre den Geldtransfer durch
+ try
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "UPDATE balances SET balance = balance - ?amount WHERE user = ?senderID; " +
+ "UPDATE balances SET balance = balance + ?amount WHERE user = ?receiverID";
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?senderID", senderID);
+ cmd.Parameters.AddWithValue("?receiverID", receiverID);
+
+ int rowsAffected = cmd.ExecuteNonQuery();
+ bool result = (rowsAffected > 0);
+
+ if (result)
+ {
+ LogTransaction(UUID.Random(), senderID, -amount);
+ LogTransaction(UUID.Random(), receiverID, amount);
+ }
+
+ dbm.Release();
+
+ return result;
+ }
+ }
+ catch (Exception)
+ {
+ //m_log.ErrorFormat("[MONEY TRANSFER]: Error transferring money: {0}", ex.Message);
+ return false;
+ }
+ }
+
+ public void InitializeUserCurrency(string agentId)
+ {
+ m_log.InfoFormat("[INITIALIZE USER CURRENCY]: Initializing currency for new user: {0}", agentId);
+ int realMoney = 1000; // Beispielwert oder aus der MoneyServer.ini geladen
+ int gameMoney = 10000; // Beispielwert oder aus der MoneyServer.ini geladen
+
+ try
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "INSERT INTO balances (user, balance) VALUES (?agentId, ?realMoney), (?agentId, ?gameMoney)";
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?agentId", agentId);
+ cmd.Parameters.AddWithValue("?realMoney", realMoney);
+ cmd.Parameters.AddWithValue("?gameMoney", gameMoney);
+
+ cmd.ExecuteNonQuery();
+ }
+ m_log.InfoFormat("[INITIALIZE USER CURRENCY]: User {0} initialized with {1}� and {2}L$.", agentId, realMoney, gameMoney);
+ dbm.Release();
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[INITIALIZE USER CURRENCY]: Error initializing user currency: {0}", ex.Message);
+ }
+
+ }
+
+ public Hashtable ApplyFallbackCredit(string agentId)
+ {
+ m_log.WarnFormat("[FALLBACK CREDIT]: Applying fallback credit for user {0}", agentId);
+
+ // Beispielhafte Implementierung: Fallback-Gutschrift in die Datenbank eintragen
+ try
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "UPDATE balances SET balance = balance + 100 WHERE user = ?agentId";
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?agentId", agentId);
+
+ cmd.ExecuteNonQuery();
+ }
+ dbm.Release();
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[FALLBACK CREDIT]: Error applying fallback credit: {0}", ex.Message);
+ }
+
+ return new Hashtable
+ {
+ { "success", true },
+ { "creditedAmount", 100 },
+ { "message", "Fallback credit applied due to transaction failure." }
+ };
+
+ }
+
+ }
+}
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyServerBase.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyServerBase.cs
new file mode 100644
index 0000000..5cb3cde
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyServerBase.cs
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/, http://www.nsl.tuis.ac.jp/ 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 OpenSim 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.
+
+Funktion
+ MoneyServerBase ist die zentrale Basisklasse für den MoneyServer im OpenSim-Grid.
+ Sie startet die Serverdienste, initialisiert Konfigurationen, verwaltet Datenbankverbindungen und setzt periodisch Transaktionen zurück.
+ Sie implementiert das Interface IMoneyServiceCore und erweitert BaseOpenSimServer.
+
+Null-Pointer-Checks & Fehlerquellen
+Initialisierung und Konstruktor
+ Im Konstruktor (MoneyServerBase()) wird eine Konsole (LocalConsole) initialisiert und auf null geprüft. Bei Fehlschlag wird eine Exception geworfen.
+ Das Logging (m_log?.Info, m_log?.Error) verwendet sichere Zugriffe (null-conditional operator).
+
+Konfigurationslesung
+ ReadIniConfig() liest Konfigurationen aus einer INI-Datei.
+ Null-Gefahr:
+ Abschnitte wie [MoneyServer] oder [Certificate] könnten fehlen. Es gibt aber Fallbacks, z.B. wird bei fehlender [Certificate]-Section auf [MoneyServer] zurückgegriffen.
+ Bei Fehlern wird ein Fehler geloggt und das Programm per Environment.Exit(1) beendet – kritische NullPointer werden so abgefangen.
+
+Datenbankservice
+ dbService und m_moneyDBService werden beide korrekt initialisiert und mit Initialise versehen.
+ Null-Check: In der Timer-Callback-Methode CheckTransaction wird geprüft, ob m_moneyDBService == null ist, bevor darauf zugegriffen wird.
+
+Timer und Ressourcenmanagement
+ In der Methode Work() wird ein Timer verwendet und im finally-Block sauber gestoppt und freigegeben, falls er noch läuft. Das verhindert Memory Leaks.
+
+HTTP-Server
+ Der HTTP-Server (m_httpServer) wird je nach Konfiguration mit oder ohne Zertifikat initialisiert.
+ Client-Zertifikatsprüfung wird nur aktiviert, wenn alle nötigen Parameter gesetzt sind.
+
+Dictionary und Session-Handling
+ Die Dictionary-Properties (m_sessionDic, m_secureSessionDic, m_webSessionDic) werden direkt im Feld initialisiert, können also nie null sein.
+
+Zusammenfassung möglicher Fehlerquellen
+ NullPointer:
+ Weitestgehend abgefangen durch Initialisierung und Checks.
+ Mögliche Fehlerquellen werden durch Exceptions und Beenden des Programms abgefangen.
+ Fehlende Konfigurationen:
+ Fallbacks vorhanden, Logging bei Problemen.
+ Datenbank- und Serviceobjekte:
+ Werden immer initialisiert, NullPointer im Laufzeitbetrieb sind unwahrscheinlich.
+ Allgemeine Fehlerbehandlung:
+ Try-Catch-Blöcke mit Logging in allen kritischen Abschnitten.
+ Im Fehlerfall Exit oder saubere Beendigung.
+
+Funktionale Zusammenfassung
+ Initialisierung: Liest Konfiguration, startet HTTP-Server (mit/ohne SSL), setzt Datenbankdienste auf.
+ Service-Setup: Stellt alle zentralen Services für den MoneyServer bereit (Konfiguration, Session, HTTP).
+ Transaktionsüberwachung: Überprüft regelmäßig per Timer, ob alte Transaktionen abgelaufen sind.
+ Ressourcenmanagement: Saubere Freigabe von Timern, keine offensichtlichen Memory Leaks.
+
+Fazit:
+Der Code ist insgesamt robust gegen NullPointer-Fehler, da alle kritischen Ressourcen direkt initialisiert oder auf null geprüft werden. Fehler werden geloggt und führen bei kritischen Problemen zum Programmabbruch.
+Die Funktionalität ist klar und zweckmäßig für einen zentralen Service im OpenSim-MoneyServer.
+ */
+
+using log4net;
+
+using Nini.Config;
+
+using NSL.Certificate.Tools;
+
+using OpenSim.Framework;
+using OpenSim.Framework.Console;
+using OpenSim.Framework.Servers;
+using OpenSim.Framework.Servers.HttpServer;
+using OpenSim.Grid.MoneyServer;
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Security;
+using System.Reflection;
+using System.Timers;
+
+using Timer = System.Timers.Timer;
+
+
+///
+/// OpenSim Grid MoneyServer
+///
+internal class MoneyServerBase : BaseOpenSimServer, IMoneyServiceCore
+{
+ private MoneyDBService dbService;
+
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private string connectionString = string.Empty;
+ private uint m_moneyServerPort = 8008; // 8008 is default server port
+ private Timer checkTimer;
+
+ private string m_certFilename = "";
+ private string m_certPassword = "";
+ private string m_cacertFilename = "";
+ private string m_clcrlFilename = "";
+ private bool m_checkClientCert = false;
+
+ private int DEAD_TIME = 120;
+ private int MAX_DB_CONNECTION = 10; // 10 is default
+
+ // Testbereich
+ // Maximum pro Tag:
+ private int m_TotalDay = 100;
+ // Maximum pro Woche:
+ private int m_TotalWeek = 250;
+ // Maximum pro Monat:
+ private int m_TotalMonth = 500;
+ // Maximum Besitz:
+ private int m_CurrencyMaximum = 10000;
+ // Geldkauf abschalten:
+ private string m_CurrencyOnOff = "off";
+ // Geldkauf nur für Gruppe:
+ private bool m_CurrencyGroupOnly = false;
+ private string m_CurrencyGroupName = "";
+
+
+ private MoneyXmlRpcModule m_moneyXmlRpcModule;
+ private MoneyDBService m_moneyDBService;
+
+ private NSLCertificateVerify m_certVerify = new NSLCertificateVerify(); // Client Certificate
+
+ private Dictionary m_sessionDic = new Dictionary();
+ private Dictionary m_secureSessionDic = new Dictionary();
+ private Dictionary m_webSessionDic = new Dictionary();
+
+ IConfig m_server_config;
+ IConfig m_cert_config;
+
+
+ public MoneyServerBase()
+ {
+ try
+ {
+ // Initialize the console for the Money Server
+ m_console = new LocalConsole("MoneyServer ");
+
+ if (m_console != null)
+ {
+ // Set the main console instance to the Money Server console
+ MainConsole.Instance = m_console;
+
+ // Log a message to indicate that the Money Server is initializing
+ m_log?.Info("[MONEY SERVER]: Initializing Money Server module and loading configurations...");
+ }
+ else
+ {
+ throw new InvalidOperationException("Failed to initialize LocalConsole instance.");
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log the exception
+ m_log?.Error("An error occurred during MoneyServerBase initialization.", ex);
+ throw;
+ }
+ }
+
+ ///
+ /// Work
+ ///
+ public void Work()
+ {
+ // Create a new timer to check transactions every 60 seconds
+ checkTimer = new Timer
+ {
+ Interval = 60 * 1000,
+ Enabled = true
+ };
+
+ // Add event handler to check transactions
+ checkTimer.Elapsed += CheckTransaction;
+
+ try
+ {
+ // Start the timer
+ checkTimer.Start();
+
+ // Run the console prompt loop
+ while (true)
+ {
+ m_console.Prompt();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log any exceptions that occur
+ m_log.ErrorFormat("Error in Work: {0}", ex.Message);
+ }
+ finally
+ {
+ // Stop the timer if it's still running
+ if (checkTimer != null && checkTimer.Enabled)
+ {
+ checkTimer.Stop();
+ checkTimer.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Checks transactions.
+ ///
+ private void CheckTransaction(object sender, ElapsedEventArgs e)
+ {
+ if (m_moneyDBService == null)
+ {
+ m_log.Error("[CHECK TRANSACTION]: m_moneyDBService is null, cannot check transactions.");
+ return;
+ }
+
+ try
+ {
+ long ticksToEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
+ int unixEpochTime = (int)((DateTime.UtcNow.Ticks - ticksToEpoch) / 10000000);
+ int deadTime = unixEpochTime - DEAD_TIME;
+ m_moneyDBService.SetTransExpired(deadTime);
+
+ //m_log.Info("[CHECK TRANSACTION]: Transactions checked successfully.");
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[CHECK TRANSACTION]: Error in CheckTransaction: {0}", ex.Message);
+ }
+ }
+
+ ///
+ /// Startup Specific
+ ///
+ protected override void StartupSpecific()
+ {
+ m_log.Info("[MONEY SERVER]: Setup HTTP Server process");
+
+ ReadIniConfig();
+
+ try
+ {
+ if (m_certFilename != "")
+ {
+ m_httpServer = new BaseHttpServer(m_moneyServerPort, true, m_certFilename, m_certPassword);
+ m_httpServer.CertificateValidationCallback = null;
+ //
+ if (m_checkClientCert)
+ {
+ m_httpServer.CertificateValidationCallback = (RemoteCertificateValidationCallback)m_certVerify.ValidateClientCertificate;
+ m_log.Info("[MONEY SERVER]: Set RemoteCertificateValidationCallback");
+ }
+ }
+ else
+ {
+ m_httpServer = new BaseHttpServer(m_moneyServerPort);
+ }
+
+ SetupMoneyServices();
+ m_httpServer.Start();
+ base.StartupSpecific(); // OpenSim/Framework/Servers/BaseOpenSimServer.cs
+ }
+
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: StartupSpecific: Fail to start HTTPS process");
+ m_log.ErrorFormat("[MONEY SERVER]: StartupSpecific: Please Check Certificate File or Password. Exit");
+ m_log.ErrorFormat("[MONEY SERVER]: StartupSpecific: {0}", e);
+ Environment.Exit(1);
+ }
+ }
+
+ public void ReadIniConfig()
+ {
+ MoneyServerConfigSource moneyConfig = new MoneyServerConfigSource();
+ Config = moneyConfig.m_config;
+
+ try
+ {
+ // [Startup]
+ IConfig st_config = moneyConfig.m_config.Configs["Startup"];
+ string PIDFile = st_config.GetString("PIDFile", "");
+ if (PIDFile != "") Create_PIDFile(PIDFile);
+
+ // [MySql]
+ IConfig db_config = moneyConfig.m_config.Configs["MySql"];
+ string sqlserver = db_config.GetString("hostname", "localhost");
+ string database = db_config.GetString("database", "OpenSim");
+ string username = db_config.GetString("username", "root");
+ string password = db_config.GetString("password", "password");
+ string pooling = db_config.GetString("pooling", "false");
+ string port = db_config.GetString("port", "3306");
+ MAX_DB_CONNECTION = db_config.GetInt("MaxConnection", MAX_DB_CONNECTION);
+
+ connectionString = "Server=" + sqlserver + ";Port=" + port + ";Database=" + database + ";User ID=" +
+ username + ";Password=" + password + ";Pooling=" + pooling + ";";
+
+ // [MoneyServer]
+ m_server_config = moneyConfig.m_config.Configs["MoneyServer"];
+ DEAD_TIME = m_server_config.GetInt("ExpiredTime", DEAD_TIME);
+ m_moneyServerPort = (uint)m_server_config.GetInt("ServerPort", (int)m_moneyServerPort);
+
+ /*
+ ; Testbereich
+ ; Maximum pro Tag:
+ TotalDay = 100;
+ ; Maximum pro Woche:
+ TotalWeek = 250;
+ ; Maximum pro Monat:
+ TotalMonth = 500;
+ */
+
+ m_TotalDay = m_server_config.GetInt("TotalDay", m_TotalDay);
+ m_TotalWeek = m_server_config.GetInt("TotalWeek", m_TotalWeek);
+ m_TotalMonth = m_server_config.GetInt("TotalMonth", m_TotalMonth);
+ m_CurrencyMaximum = m_server_config.GetInt("CurrencyMaximum", m_CurrencyMaximum);
+
+ m_CurrencyOnOff = m_server_config.GetString("CurrencyOnOff", m_CurrencyOnOff);
+ m_CurrencyGroupOnly = m_server_config.GetBoolean("CurrencyGroupOnly", m_CurrencyGroupOnly);
+ m_CurrencyGroupName = m_server_config.GetString("CurrencyGroupName", m_CurrencyGroupName);
+
+
+ //
+ // [Certificate]
+ m_cert_config = moneyConfig.m_config.Configs["Certificate"];
+ if (m_cert_config == null)
+ {
+ m_log.Info("[MONEY SERVER]: [Certificate] section is not found. Using [MoneyServer] section instead");
+ m_cert_config = m_server_config;
+ }
+
+ // HTTPS Server Cert (Server Mode)
+ m_certFilename = m_cert_config.GetString("ServerCertFilename", m_certFilename);
+ m_certPassword = m_cert_config.GetString("ServerCertPassword", m_certPassword);
+ if (m_certFilename != "")
+ {
+ m_log.Info("[MONEY SERVER]: ReadIniConfig: Execute HTTPS comunication. Server Cert file is " + m_certFilename);
+ }
+
+ // Client Certificate
+ m_checkClientCert = m_cert_config.GetBoolean("CheckClientCert", m_checkClientCert);
+ m_cacertFilename = m_cert_config.GetString("CACertFilename", m_cacertFilename);
+ m_clcrlFilename = m_cert_config.GetString("ClientCrlFilename", m_clcrlFilename);
+ if (m_checkClientCert && (m_cacertFilename != ""))
+ {
+ m_certVerify.SetPrivateCA(m_cacertFilename);
+ m_log.Info("[MONEY SERVER]: ReadIniConfig: Execute Authentication of Clients. CA file is " + m_cacertFilename);
+ }
+ else
+ {
+ m_checkClientCert = false;
+ }
+ if (m_checkClientCert)
+ {
+ if (m_clcrlFilename != "")
+ {
+ m_certVerify.SetPrivateCRL(m_clcrlFilename);
+ m_log.Info("[MONEY SERVER]: ReadIniConfig: Execute Authentication of Clients. CRL file is " + m_clcrlFilename);
+ }
+ }
+
+ // Initialisiere die MoneyDBService mit der Verbindungszeichenkette und der maxDBConnections
+ dbService = new MoneyDBService();
+ dbService.Initialise(connectionString, MAX_DB_CONNECTION);
+ }
+ catch (Exception ex)
+ {
+ m_log.Error("[MONEY SERVER]: ReadIniConfig: Fail to setup configure. Please check MoneyServer.ini. Exit", ex);
+ Environment.Exit(1);
+ }
+ }
+
+ ///
+ /// Create PID File added by skidz
+ ///
+ protected void Create_PIDFile(string path)
+ {
+ try
+ {
+ string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
+ FileStream fs = File.Create(path);
+ System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
+ Byte[] buf = enc.GetBytes(pidstring);
+ fs.Write(buf, 0, buf.Length);
+ fs.Close();
+ m_pidFile = path;
+ }
+
+ catch (Exception) { }
+
+ }
+
+ protected virtual void SetupMoneyServices()
+ {
+ m_log.Info("[MONEY SERVER]: Connecting to Money Storage Server");
+
+ m_moneyDBService = new MoneyDBService();
+ m_moneyDBService.Initialise(connectionString, MAX_DB_CONNECTION);
+
+ IConfigSource config = new IniConfigSource(); // Beispiel für das Erstellen einer IConfigSource
+ m_moneyXmlRpcModule = new MoneyXmlRpcModule(connectionString, MAX_DB_CONNECTION);
+ m_moneyXmlRpcModule.Initialise(m_version, m_moneyDBService, this, config);
+ m_moneyXmlRpcModule.PostInitialise();
+ }
+
+ public bool IsCheckClientCert()
+ {
+ return m_checkClientCert;
+ }
+
+ public IConfig GetServerConfig()
+ {
+ return m_server_config;
+ }
+
+ public IConfig GetCertConfig()
+ {
+ return m_cert_config;
+ }
+
+ public BaseHttpServer GetHttpServer()
+ {
+ return m_httpServer;
+ }
+
+ public Dictionary GetSessionDic()
+ {
+ return m_sessionDic;
+ }
+
+ public Dictionary GetSecureSessionDic()
+ {
+ return m_secureSessionDic;
+ }
+
+ public Dictionary GetWebSessionDic()
+ {
+ return m_webSessionDic;
+ }
+
+ class MoneyServerConfigSource
+ {
+
+ public IniConfigSource m_config;
+
+ public MoneyServerConfigSource()
+ {
+ string configPath = Path.Combine(Directory.GetCurrentDirectory(), "MoneyServer.ini");
+ if (File.Exists(configPath))
+ {
+ m_config = new IniConfigSource(configPath);
+ }
+ }
+
+ public void Save(string path)
+ {
+ m_config.Save(path);
+ }
+
+ }
+}
+
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyXmlRpcModule.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyXmlRpcModule.cs
new file mode 100644
index 0000000..9dc03c9
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/MoneyXmlRpcModule.cs
@@ -0,0 +1,3885 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/, http://www.nsl.tuis.ac.jp/ 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 OpenSim 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.
+
+Funktion
+Diese Klasse ist das zentrale XML-RPC Modul für den OpenSim MoneyServer. Sie erweitert MoneyDBService und implementiert das Interface IMoneyDBService. Ihre Aufgaben:
+
+ Verwaltung und Verarbeitung von XML-RPC-Anfragen für Geldtransaktionen, Landkauf, Session-Management und mehr.
+ Bindung von HTTP- und XML-RPC-Handlern an den HTTP-Server.
+ Zugriff und Manipulation von Benutzerguthaben, Transaktionen und Gruppenmitgliedschaften über die Datenbank.
+ Spezialfunktionen für Cashbook-Ausgaben, Konsolenbefehle und Logging.
+
+Null-Pointer-Checks & Fehlerquellen
+1. Konstruktor & Initialisierung
+ Alle kritischen Dependencies (moneyDBService, moneyCore) werden mit ArgumentNullException.ThrowIfNull() geprüft.
+ Konfigurationswerte werden aus INI geladen und auf null/Fehlwerte geprüft.
+ Dictionaries und andere Felder werden direkt initialisiert oder nachgeladen.
+
+2. HTTP/XML-RPC-Handler
+ Viele Methoden prüfen die Eingabeparameter (z.B. ob ein Request-Objekt null ist).
+ Bei fehlerhaften Requests oder Sessions wird ein Fehler geloggt und eine Standardantwort zurückgegeben.
+ Beispiel:
+ C#
+
+ if (httpRequest == null || httpResponse == null) { ... return; }
+ if (request == null) { ... return new XmlRpcResponse { Value = new Hashtable { { "success", false } } }; }
+
+ Bei Session-Handling wird vor jedem Zugriff geprüft, ob die Dictionary-Schlüssel existieren.
+ Bei Datenbankoperationen werden Exceptions gefangen und führen nicht zu Abstürzen, sondern zu Logging und Fehlerantwort.
+
+3. Datenbankzugriffe
+ Datenbankverbindungen werden immer in einem Try-Catch-Finally-Block verwendet, wobei im finally-Block die Verbindung freigegeben wird (dbm.Release()).
+ Bei Datenbankoperationen wird auf mögliche Null-Rückgaben geachtet (z.B. FetchUserInfo kann null liefern).
+
+4. Spezielle Fehlerquellen
+ Parsen von XML-Requests: Hier können null-Werte entstehen, werden aber meistens abgefangen.
+ Zahlreiche Methoden prüfen, ob Parameter null/leer sind, bevor sie verwendet werden (z.B. agentId, groupId).
+ SQL-Parameter werden mit AddWithValue gesetzt, was SQL-Injection weitgehend verhindert.
+
+5. Allgemeine Fehlerbehandlung
+ Bei Fehlern wird stets ins Log geschrieben.
+ Rückgabewerte für Fehlerfälle sind konsistent (meistens false, null oder ein Hashtable mit "success": false).
+ Fehler in der Verarbeitung führen zu keinen unkontrollierten Null-Pointern oder Abstürzen.
+
+Beispiele für Null-Checks und Fehlerbehandlung
+
+Session-Checks:
+C#
+
+if (m_sessionDic.ContainsKey(senderID) && m_secureSessionDic.ContainsKey(senderID)) { ... }
+else { responseData["message"] = "Session check failure, please re-login later!"; return response; }
+
+Datenbankobjekte:
+C#
+
+UserInfo rcvr = m_moneyDBService.FetchUserInfo(receiverID);
+if (rcvr == null) { m_log.ErrorFormat(...); return response; }
+
+Catch-All für Exceptions:
+C#
+
+catch (Exception ex)
+{
+ m_log.ErrorFormat("[MONEY XMLRPC]: ... Exception occurred: {0}", ex.Message);
+ return new XmlRpcResponse();
+}
+
+Hashtable-Zugriffe:
+C#
+
+Hashtable requestData = (Hashtable)request.Params[0];
+if (requestData == null) { ... }
+
+Verbesserungsmöglichkeiten
+
+ In einigen Methoden werden Werte aus Hashtables direkt abgerufen, ohne vorher zu prüfen,
+ ob sie wirklich existieren oder den erwarteten Typ haben (z.B. int.Parse direkt auf einen String aus einer Hashtable). Hier könnten defensive Checks ergänzt werden.
+ XML-Parsing könnte robuster gestaltet werden, um fehlerhafte oder absichtlich manipulierte Requests besser abzufangen.
+ Teilweise könnten Methoden Parameter noch expliziter auf Plausibilität prüfen (z.B. Range-Checks bei Beträgen).
+
+Fazit
+
+Sehr robust gegen NullPointer-Fehler und allgemeine Fehlerquellen!
+
+ Alle kritischen Zugriffe werden defensiv behandelt.
+ Fehler werden stets geloggt und führen zu konsistenten Fehlerantworten.
+ Ressourcen werden ordentlich freigegeben.
+ Die Funktionalität ist klar: XML-RPC-Endpoint-Management, Geldtransaktionen, User-Sessions, Gruppen-Checks, Logging, Cashbook, etc.
+
+Empfehlung:
+Das Modul ist für produktiven Einsatz gut vorbereitet. Zusätzliche defensive Checks bei Daten aus externen Quellen (z.B. XML/Hashtable) wären sinnvoll, sind aber kein kritischer Mangel.
+ */
+
+using log4net;
+using MySql.Data.MySqlClient;
+using Nini.Config;
+using NSL.Certificate.Tools;
+using NSL.Network.XmlRpc;
+using Nwc.XmlRpc;
+using OpenMetaverse;
+using OpenSim.Data.MySQL.MySQLMoneyDataWrapper;
+using OpenSim.Framework;
+using OpenSim.Framework.Servers.HttpServer;
+using OpenSim.Modules.Currency;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Services.Interfaces;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.IO;
+using System.Net;
+using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Transactions;
+using System.Xml;
+using static Mono.Security.X509.X520;
+using static OpenMetaverse.DllmapConfigHelper;
+using OpenSim.Server.Base;
+using OpenSim.Framework.Console;
+
+
+namespace OpenSim.Grid.MoneyServer
+{
+ class MoneyXmlRpcModule : MoneyDBService, IMoneyDBService
+ {
+ // ################## Initial ##################
+ #region Setup Initial
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private int m_realMoney = 1000; // Beispielwert oder aus der MoneyServer.ini geladen
+ private int m_gameMoney = 10000; // Beispielwert oder aus der MoneyServer.ini geladen
+
+ // MoneyServer settings
+ public int m_defaultBalance = 1000;
+
+ private bool m_forceTransfer = false;
+ private string m_bankerAvatar = "";
+
+ // Testbereich
+ // Maximum pro Tag:
+ public int m_TotalDay = 100;
+ // Maximum pro Woche:
+ public int m_TotalWeek = 250;
+ // Maximum pro Monat:
+ public int m_TotalMonth = 500;
+ // Maximum Besitz:
+ public int m_CurrencyMaximum;
+ // Geldkauf abschalten:
+ public string m_CurrencyOnOff;
+ // Geldkauf nur für Gruppe:
+ public bool m_CurrencyGroupOnly = false;
+ public bool m_UserMailLock = false;
+ public string m_CurrencyGroupName = "";
+ public string m_CurrencyGroupID = "";
+
+ // Script settings
+ private bool m_scriptSendMoney = false;
+ private string m_scriptAccessKey = "";
+ private string m_scriptIPaddress = "127.0.0.1";
+
+ // HG settings
+ private bool m_hg_enable = false;
+ private bool m_gst_enable = false;
+ private int m_hg_defaultBalance = 0;
+ private int m_gst_defaultBalance = 0;
+ private int m_CalculateCurrency = 0;
+
+ // XMLRPC Debug settings
+ private bool m_DebugConsole = false;
+ private bool m_DebugFile = false;
+
+ // Certificate settings
+ private bool m_checkServerCert = false;
+ private string m_cacertFilename = "";
+ private string m_certFilename = "";
+ private string m_certPassword = "";
+
+
+
+ // SSL settings
+ private string m_sslCommonName = "";
+
+ private Dictionary m_scenes = new Dictionary();
+
+ private NSLCertificateVerify m_certVerify = new NSLCertificateVerify();
+
+ private string m_BalanceMessageLandSale = "Paid the Money L${0} for Land.";
+ private string m_BalanceMessageRcvLandSale = "";
+ private string m_BalanceMessageSendGift = "Sent Gift L${0} to {1}.";
+ private string m_BalanceMessageReceiveGift = "Received Gift L${0} from {1}.";
+ private string m_BalanceMessagePayCharge = "";
+ private string m_BalanceMessageBuyObject = "Bought the Object {2} from {1} by L${0}.";
+ private string m_BalanceMessageSellObject = "{1} bought the Object {2} by L${0}.";
+ private string m_BalanceMessageGetMoney = "Got the Money L${0} from {1}.";
+ private string m_BalanceMessageBuyMoney = "Bought the Money L${0}.";
+ private string m_BalanceMessageRollBack = "RollBack the Transaction: L${0} from/to {1}.";
+ private string m_BalanceMessageSendMoney = "Paid the Money L${0} to {1}.";
+ private string m_BalanceMessageReceiveMoney = "Received L${0} from {1}.";
+
+ private bool m_enableAmountZero = false;
+
+ const int MONEYMODULE_REQUEST_TIMEOUT = 30 * 1000; //30 seconds
+ private long TicksToEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks;
+
+ private IMoneyDBService m_moneyDBService;
+ // Konfig fuer Konsolenbefehle.
+ private IConfigSource m_config;
+ private IMoneyServiceCore m_moneyCore;
+
+ protected IConfig m_server_config;
+ protected IConfig m_cert_config;
+
+ ///
+ /// Used to notify old regions as to which OpenSim version to upgrade to
+ ///
+ //private string m_opensimVersion;
+
+ private Dictionary m_sessionDic;
+ private Dictionary m_secureSessionDic;
+ private Dictionary m_webSessionDic;
+
+ protected BaseHttpServer m_httpServer;
+
+
+ /// Initializes a new instance of the class.
+ public MoneyXmlRpcModule(string connectionString, int maxDBConnections)
+ {
+ Initialise(connectionString, maxDBConnections);
+ }
+
+ /// Initialises the specified opensim version.
+ /// The opensim version.
+ /// The money database service.
+ /// The money core.
+ public void Initialise(string opensimVersion, IMoneyDBService moneyDBService, IMoneyServiceCore moneyCore, IConfigSource config)
+ {
+ ArgumentNullException.ThrowIfNull(moneyDBService);
+
+ ArgumentNullException.ThrowIfNull(moneyCore);
+
+ m_moneyDBService = moneyDBService;
+ m_moneyCore = moneyCore;
+
+ // Get server configuration
+ var serverConfig = m_moneyCore.GetServerConfig() ?? throw new InvalidOperationException("Server configuration is not available");
+
+ // Get certificate configuration
+ var certConfig = m_moneyCore.GetCertConfig() ?? throw new InvalidOperationException("Certificate configuration is not available");
+
+ // Load configuration values
+ m_defaultBalance = serverConfig.GetInt("DefaultBalance", m_defaultBalance);
+ m_forceTransfer = serverConfig.GetBoolean("EnableForceTransfer", m_forceTransfer);
+ m_bankerAvatar = serverConfig.GetString("BankerAvatar", m_bankerAvatar).ToLower();
+
+ m_moneyDBService = moneyDBService;
+ m_moneyCore = moneyCore;
+ m_server_config = m_moneyCore.GetServerConfig(); // [MoneyServer] Section
+ m_cert_config = m_moneyCore.GetCertConfig(); // [Certificate] Section
+
+ m_TotalDay = serverConfig.GetInt("TotalDay", m_TotalDay);
+ m_TotalWeek = serverConfig.GetInt("TotalWeek", m_TotalWeek);
+ m_TotalMonth = serverConfig.GetInt("TotalMonth", m_TotalMonth);
+ m_CurrencyMaximum = serverConfig.GetInt("CurrencyMaximum", m_CurrencyMaximum);
+
+ m_CurrencyOnOff = serverConfig.GetString("CurrencyOnOff", m_CurrencyOnOff);
+ m_CurrencyGroupOnly = serverConfig.GetBoolean("CurrencyGroupOnly", m_CurrencyGroupOnly);
+ m_UserMailLock = serverConfig.GetBoolean("UserMailLock", m_UserMailLock);
+
+ m_CurrencyGroupName = serverConfig.GetString("CurrencyGroupName", m_CurrencyGroupName);
+ m_CurrencyGroupID = serverConfig.GetString("CurrencyGroupID", m_CurrencyGroupID);
+
+ // [MoneyServer] Section
+ m_defaultBalance = m_server_config.GetInt("DefaultBalance", m_defaultBalance);
+
+ m_forceTransfer = m_server_config.GetBoolean("EnableForceTransfer", m_forceTransfer);
+
+ string banker = m_server_config.GetString("BankerAvatar", m_bankerAvatar);
+ m_bankerAvatar = banker.ToLower();
+
+ m_enableAmountZero = m_server_config.GetBoolean("EnableAmountZero", m_enableAmountZero);
+ m_scriptSendMoney = m_server_config.GetBoolean("EnableScriptSendMoney", m_scriptSendMoney);
+ m_scriptAccessKey = m_server_config.GetString("MoneyScriptAccessKey", m_scriptAccessKey);
+ m_scriptIPaddress = m_server_config.GetString("MoneyScriptIPaddress", m_scriptIPaddress);
+
+ m_CalculateCurrency = m_server_config.GetInt("CalculateCurrency", m_CalculateCurrency); // New feature
+ m_DebugConsole = m_server_config.GetBoolean("DebugConsole", m_DebugConsole); // New feature
+ m_DebugFile = m_server_config.GetBoolean("m_DebugFile", m_DebugFile); // New feature
+
+ m_TotalDay = m_server_config.GetInt("TotalDay", m_TotalDay);
+ m_TotalWeek = m_server_config.GetInt("TotalWeek", m_TotalWeek);
+ m_TotalMonth = m_server_config.GetInt("TotalMonth", m_TotalMonth);
+ m_CurrencyMaximum = m_server_config.GetInt("CurrencyMaximum", m_CurrencyMaximum);
+
+ m_CurrencyOnOff = m_server_config.GetString("CurrencyOnOff", m_CurrencyOnOff);
+ m_CurrencyGroupOnly = m_server_config.GetBoolean("CurrencyGroupOnly", m_CurrencyGroupOnly);
+ m_UserMailLock = m_server_config.GetBoolean("UserMailLock", m_UserMailLock);
+
+ m_CurrencyGroupName = m_server_config.GetString("CurrencyGroupName", m_CurrencyGroupName);
+ m_CurrencyGroupID = m_server_config.GetString("CurrencyGroupID", m_CurrencyGroupID);
+
+ if (m_CurrencyMaximum <= 0) m_CurrencyMaximum = 1000;
+
+ // Hyper Grid Avatar
+ m_hg_enable = m_server_config.GetBoolean("EnableHGAvatar", m_hg_enable);
+ m_gst_enable = m_server_config.GetBoolean("EnableGuestAvatar", m_gst_enable);
+ m_hg_defaultBalance = m_server_config.GetInt("HGAvatarDefaultBalance", m_hg_defaultBalance);
+ m_gst_defaultBalance = m_server_config.GetInt("GuestAvatarDefaultBalance", m_gst_defaultBalance);
+
+ // Update Balance Messages
+ m_BalanceMessageLandSale = m_server_config.GetString("BalanceMessageLandSale", m_BalanceMessageLandSale);
+ m_BalanceMessageRcvLandSale = m_server_config.GetString("BalanceMessageRcvLandSale", m_BalanceMessageRcvLandSale);
+ m_BalanceMessageSendGift = m_server_config.GetString("BalanceMessageSendGift", m_BalanceMessageSendGift);
+ m_BalanceMessageReceiveGift = m_server_config.GetString("BalanceMessageReceiveGift", m_BalanceMessageReceiveGift);
+ m_BalanceMessagePayCharge = m_server_config.GetString("BalanceMessagePayCharge", m_BalanceMessagePayCharge);
+ m_BalanceMessageBuyObject = m_server_config.GetString("BalanceMessageBuyObject", m_BalanceMessageBuyObject);
+ m_BalanceMessageSellObject = m_server_config.GetString("BalanceMessageSellObject", m_BalanceMessageSellObject);
+ m_BalanceMessageGetMoney = m_server_config.GetString("BalanceMessageGetMoney", m_BalanceMessageGetMoney);
+ m_BalanceMessageBuyMoney = m_server_config.GetString("BalanceMessageBuyMoney", m_BalanceMessageBuyMoney);
+ m_BalanceMessageRollBack = m_server_config.GetString("BalanceMessageRollBack", m_BalanceMessageRollBack);
+ m_BalanceMessageSendMoney = m_server_config.GetString("BalanceMessageSendMoney", m_BalanceMessageSendMoney);
+ m_BalanceMessageReceiveMoney = m_server_config.GetString("BalanceMessageReceiveMoney", m_BalanceMessageReceiveMoney);
+
+ // [Certificate] Section
+
+ // XML RPC to Region Server (Client Mode)
+ // Client Certificate
+ m_certFilename = m_cert_config.GetString("ClientCertFilename", m_certFilename);
+ m_certPassword = m_cert_config.GetString("ClientCertPassword", m_certPassword);
+ if (m_certFilename != "")
+ {
+ m_certVerify.SetPrivateCert(m_certFilename, m_certPassword);
+ m_log.Info("[MONEY XMLRPC]: Initialise: Issue Authentication of Client. Cert file is " + m_certFilename);
+ }
+
+ // Server Authentication
+ // CA : MoneyServer config for checking the server certificate of the web server for XMLRPC
+ m_checkServerCert = m_cert_config.GetBoolean("CheckServerCert", m_checkServerCert);
+ m_cacertFilename = m_cert_config.GetString("CACertFilename", m_cacertFilename);
+
+ if (m_cacertFilename != "")
+ {
+ m_certVerify.SetPrivateCA(m_cacertFilename);
+ }
+ else
+ {
+ m_checkServerCert = false;
+ }
+
+ if (m_checkServerCert)
+ {
+ m_log.Info("[MONEY XMLRPC]: Initialise: Execute Authentication of Server. CA file is " + m_cacertFilename);
+ }
+ else
+ {
+ m_log.Info("[MONEY XMLRPC]: CheckServerCert is false.");
+ }
+
+ m_moneyDBService = moneyDBService;
+ m_config = config;
+
+ // Rufe die RegisterConsoleCommands Methode auf
+ RegisterConsoleCommands(MainConsole.Instance); // Aufruf der Initialisierung der Konsolenbefehle
+
+ m_sessionDic = m_moneyCore.GetSessionDic();
+ m_secureSessionDic = m_moneyCore.GetSecureSessionDic();
+ m_webSessionDic = m_moneyCore.GetWebSessionDic();
+ RegisterHandlers();
+
+ RegisterStreamHandlers();
+ }
+
+ private void RegisterStreamHandlers()
+ {
+ m_log.Info("[MONEY XMLRPC]: Registering currency.php handlers.");
+ m_httpServer.AddSimpleStreamHandler(new CurrencyStreamHandler("/currency.php", CurrencyProcessPHP));
+
+ m_log.Info("[MONEY XMLRPC]: Registering landtool.php handlers.");
+ m_httpServer.AddSimpleStreamHandler(new LandtoolStreamHandler("/landtool.php", LandtoolProcessPHP));
+
+ m_log.InfoFormat("[MONEY MODULE]: Registered /currency.php and /landtool.php handlers on Port: {0}", m_httpServer.Port);
+ }
+
+ /// Posts the initialise.
+ public void PostInitialise()
+ {
+ }
+
+ private Dictionary m_rpcHandlers = new Dictionary();
+
+
+ /// Registers the handlers.
+ public void RegisterHandlers()
+ {
+ m_httpServer = m_moneyCore.GetHttpServer();
+ m_httpServer.AddXmlRPCHandler("ClientLogin", handleClientLogin);
+ m_httpServer.AddXmlRPCHandler("ClientLogout", handleClientLogout);
+ m_httpServer.AddXmlRPCHandler("GetBalance", handleGetBalance);
+ m_httpServer.AddXmlRPCHandler("GetTransaction", handleGetTransaction);
+
+ m_httpServer.AddXmlRPCHandler("CancelTransfer", handleCancelTransfer);
+
+ m_httpServer.AddXmlRPCHandler("TransferMoney", handleTransaction);
+ m_httpServer.AddXmlRPCHandler("ForceTransferMoney", handleForceTransaction); // added
+ m_httpServer.AddXmlRPCHandler("PayMoneyCharge", handlePayMoneyCharge); // added
+ m_httpServer.AddXmlRPCHandler("AddBankerMoney", handleAddBankerMoney); // added
+
+ m_httpServer.AddXmlRPCHandler("SendMoney", handleScriptTransaction);
+ m_httpServer.AddXmlRPCHandler("MoveMoney", handleScriptTransaction);
+
+ // this is from original DTL. not check yet.
+ m_httpServer.AddXmlRPCHandler("WebLogin", handleWebLogin);
+ m_httpServer.AddXmlRPCHandler("WebLogout", handleWebLogout);
+ m_httpServer.AddXmlRPCHandler("WebGetBalance", handleWebGetBalance);
+ m_httpServer.AddXmlRPCHandler("WebGetTransaction", handleWebGetTransaction);
+ m_httpServer.AddXmlRPCHandler("WebGetTransactionNum", handleWebGetTransactionNum);
+
+ // Land Buy Test
+ m_httpServer.AddXmlRPCHandler("preflightBuyLandPrep", preflightBuyLandPrep);
+ m_httpServer.AddXmlRPCHandler("buyLandPrep", buyLandPrep);
+
+ // Currency Buy Test
+ // getCurrencyQuote", quote_func
+ // buyCurrency", buy_func
+ m_httpServer.AddXmlRPCHandler("getCurrencyQuote", getCurrencyQuote);
+ m_httpServer.AddXmlRPCHandler("buyCurrency", buyCurrency);
+
+ // Money Transfer Test
+ m_httpServer.AddXmlRPCHandler("OnMoneyTransfered", OnMoneyTransferedHandler);
+ m_httpServer.AddXmlRPCHandler("UpdateBalance", BalanceUpdateHandler);
+ m_httpServer.AddXmlRPCHandler("UserAlert", UserAlertHandler);
+
+ // Angebot oder eine Information zu einem Kaufpreis
+ // m_httpServer.AddXmlRPCHandler("quote", getCurrencyQuote);
+ }
+ #endregion
+ // ################## Land Buy ##################
+ #region Land Buy
+
+ // Flexibilität: Die gesamte Logik wird innerhalb von LandtoolProcessPHP und ihren Hilfsfunktionen abgewickelt.
+ // Fehlerbehandlung: Umfassende Überprüfung auf fehlende Daten oder Fehler während der Verarbeitung.
+ // Unabhängigkeit: Keine Abhängigkeit von externen Funktionen oder Modulen.
+
+ private void LandtoolProcessPHP(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: LANDTOOL PROCESS PHP starting...");
+
+ if (httpRequest == null || httpResponse == null)
+ {
+ m_log.Error("[MONEY XMLRPC MODULE]: Invalid request or response object.");
+ return;
+ }
+
+ try
+ {
+ // XML-String aus Anfrage lesen
+ string requestBody;
+ using (var reader = new StreamReader(httpRequest.InputStream, Encoding.UTF8))
+ {
+ requestBody = reader.ReadToEnd();
+ }
+
+ // XML-Daten parsen
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(requestBody);
+
+ // Methode extrahieren
+ XmlNode methodNameNode = doc.SelectSingleNode("/methodCall/methodName");
+ if (methodNameNode == null)
+ {
+ throw new Exception("Missing method name in XML-RPC request.");
+ }
+
+ string methodName = methodNameNode.InnerText;
+ XmlNodeList members = doc.SelectNodes("//param/value/struct/member");
+
+ // Variablen für Landanfrage initialisieren
+ string agentId = null, secureSessionId = null, language = null;
+ int billableArea = 0, currencyBuy = 0;
+
+ // Werte aus der XML-Struktur extrahieren
+ foreach (XmlNode member in members)
+ {
+ string name = member.SelectSingleNode("name")?.InnerText;
+ string value = member.SelectSingleNode("value")?.InnerText;
+
+ if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(value)) continue;
+
+ switch (name)
+ {
+ case "agentId": agentId = value; break;
+ case "billableArea": billableArea = int.Parse(value); break;
+ case "currencyBuy": currencyBuy = int.Parse(value); break;
+ case "language": language = value; break;
+ case "secureSessionId": secureSessionId = value; break;
+ }
+ }
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: agentId ", agentId);
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: billableArea", billableArea);
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: currencyBuy", currencyBuy);
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: language", language);
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: secureSessionId", secureSessionId);
+
+ if (methodName == "preflightBuyLandPrep")
+ {
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: Processing Preflight Land Purchase Request for AgentId: {0}, BillableArea: {1}",
+ agentId, billableArea);
+
+ // Preflight-Prüfung
+ Hashtable preflightResponse = PerformPreflightLandCheck(agentId, billableArea, currencyBuy, language, secureSessionId);
+ if (!(bool)preflightResponse["success"])
+ {
+ m_log.Error("[MONEY XMLRPC MODULE]: Preflight check failed.");
+ httpResponse.StatusCode = 400;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Preflight check failed");
+ return;
+ }
+
+ // Erfolgreiche Antwort zurückgeben
+ httpResponse.StatusCode = 200;
+ XmlRpcResponse xmlResponse = new XmlRpcResponse { Value = preflightResponse };
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes(xmlResponse.ToString());
+ }
+ else if (methodName == "buyLandPrep")
+ {
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: Processing Land Purchase Request for AgentId: {0}, BillableArea: {1}",
+ agentId, billableArea);
+
+ // Landkauf durchführen
+ Hashtable purchaseResponse = ProcessLandPurchase(agentId, billableArea, currencyBuy, language, secureSessionId);
+
+ // Überprüfung der Antwort
+ if (!(bool)purchaseResponse["success"])
+ {
+ m_log.Error("[MONEY XMLRPC MODULE]: Land purchase failed.");
+ httpResponse.StatusCode = 400;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Land purchase failed");
+ return;
+ }
+
+ // Erfolgreiche Antwort zurückgeben
+ httpResponse.StatusCode = 200;
+ XmlRpcResponse xmlResponse = new XmlRpcResponse { Value = purchaseResponse };
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes(xmlResponse.ToString());
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC MODULE]: Unknown landtool method name: {0}", methodName);
+ httpResponse.StatusCode = 400;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Invalid method name");
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC MODULE]: Error processing LANDTOOL request. Error: {0}", ex.ToString());
+ httpResponse.StatusCode = 500;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Error");
+ }
+ }
+
+ // Beispiel einer Funktion zur Preflight-Prüfung
+ // PerformPreflightLandCheck:
+ // Simuliert die Preflight-Prüfung für den Landkauf.
+ // Gibt eine Erfolgsmeldung in Form einer Hashtable zurück.
+ private Hashtable PerformPreflightLandCheck(string agentId, int billableArea, int currencyBuy, string language, string secureSessionId)
+ {
+ // Beispielhafte Logik für Preflight-Prüfung
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: Preflight check for AgentId: {0}, Area: {1}, CurrencyBuy: {2}", agentId, billableArea, currencyBuy);
+
+ // Erfolg simulieren
+ return new Hashtable
+ {
+ { "success", true },
+ { "agentId", agentId },
+ { "billableArea", billableArea },
+ { "currencyBuy", currencyBuy },
+ { "message", "Preflight check passed" }
+ };
+ }
+
+ // Beispiel einer Funktion zur Bearbeitung eines Landkaufs
+ // ProcessLandPurchase:
+ // Simuliert die Durchführung des Landkaufs.
+ // Gibt ebenfalls eine Erfolgsmeldung als Hashtable zurück.
+ private Hashtable ProcessLandPurchase(string agentId, int billableArea, int currencyBuy, string language, string secureSessionId)
+ {
+ // Beispielhafte Logik für Landkauf
+ m_log.InfoFormat("[MONEY XMLRPC MODULE]: Processing land purchase for AgentId: {0}, Area: {1}, CurrencyBuy: {2}", agentId, billableArea, currencyBuy);
+
+ // Erfolg simulieren
+ return new Hashtable
+ {
+ { "success", true },
+ { "agentId", agentId },
+ { "billableArea", billableArea },
+ { "currencyBuy", currencyBuy },
+ { "message", "Land purchase completed successfully" }
+ };
+ }
+ private XmlRpcResponse preflightBuyLandPrep(XmlRpcRequest request, IPEndPoint client)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: preflightBuyLandPrep starting...");
+
+ if (request == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: preflightBuyLandPrep: request is null.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ if (client == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: preflightBuyLandPrep: client is null.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ try
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ if (requestData == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: preflightBuyLandPrep: request data is null.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ int billableArea = Convert.ToInt32(requestData["billableArea"]);
+ int currencyBuy = Convert.ToInt32(requestData["currencyBuy"]);
+
+ // Log the received data for debugging
+ m_log.InfoFormat("[MONEY XMLRPC]: Received billableArea = {0}, currencyBuy = {1}", billableArea, currencyBuy);
+
+ // Process preflight logic here
+ if (billableArea < 0 || currencyBuy < 0)
+ {
+ m_log.Error("[MONEY XMLRPC]: preflightBuyLandPrep: Invalid input values.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ // Simulate sending response
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseValue = new Hashtable
+ {
+ { "success", true },
+ { "billableArea", billableArea },
+ { "currencyBuy", currencyBuy }
+ };
+ response.Value = responseValue;
+ return response;
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: preflightBuyLandPrep: {0}", ex.Message);
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+ }
+ private XmlRpcResponse buyLandPrep(XmlRpcRequest request, IPEndPoint client)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: buyLandPrep starting...");
+
+ if (request == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: buyLandPrep: request is null.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ if (client == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: buyLandPrep: client is null.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ try
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ if (requestData == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: buyLandPrep: request data is null.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ string agentId = requestData["agentId"]?.ToString();
+ string secureSessionId = requestData["secureSessionId"]?.ToString();
+ string language = requestData["language"]?.ToString();
+ int billableArea = Convert.ToInt32(requestData["billableArea"]);
+ int currencyBuy = Convert.ToInt32(requestData["currencyBuy"]);
+
+ // Log the received data for debugging
+ m_log.InfoFormat("[MONEY XMLRPC]: Received agentId = {0}, secureSessionId = {1}, billableArea = {2}, currencyBuy = {3}",
+ agentId, secureSessionId, billableArea, currencyBuy);
+
+ if (string.IsNullOrEmpty(agentId) || string.IsNullOrEmpty(secureSessionId))
+ {
+ m_log.Error("[MONEY XMLRPC]: buyLandPrep: Missing required parameters.");
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+
+ // Process purchase logic here
+ bool purchaseSuccessful = ProcessLandPurchase(agentId, secureSessionId, billableArea, currencyBuy);
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseValue = new Hashtable
+ {
+ { "success", purchaseSuccessful }
+ };
+ response.Value = responseValue;
+ return response;
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: buyLandPrep: {0}", ex.Message);
+ return new XmlRpcResponse { Value = new Hashtable { { "success", false } } };
+ }
+ }
+
+ // Helper function to simulate land purchase logic
+ private bool ProcessLandPurchase(string agentId, string secureSessionId, int billableArea, int currencyBuy)
+ {
+ // Simulate some purchase validation
+ if (billableArea > 0 && currencyBuy >= billableArea * 10) // Example: each square meter costs 10 currency units
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: ProcessLandPurchase: Purchase successful for Agent {0}.", agentId);
+ return true;
+ }
+
+ m_log.WarnFormat("[MONEY XMLRPC]: ProcessLandPurchase: Purchase failed for Agent {0}.", agentId);
+ return false;
+ }
+ #endregion
+ // ################## Currency Buy ##################
+ #region Currency Buy
+
+ private void CurrencyProcessPHP(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
+ {
+ m_log.InfoFormat("[CURRENCY PROCESS PHP]: Currency Process Starting...");
+
+ if (httpRequest == null || httpResponse == null)
+ {
+ m_log.Error("[CURRENCY PROCESS PHP]: Invalid request or response object.");
+ httpResponse.StatusCode = 400;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Invalid request");
+ return;
+ }
+
+ try
+ {
+ string requestBody;
+ using (var reader = new StreamReader(httpRequest.InputStream, Encoding.UTF8))
+ {
+ requestBody = reader.ReadToEnd();
+ }
+
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(requestBody);
+
+ XmlNode methodNameNode = doc.SelectSingleNode("/methodCall/methodName");
+ if (methodNameNode == null)
+ {
+ throw new Exception("Missing method name in XML-RPC request.");
+ }
+
+ string methodName = methodNameNode.InnerText;
+
+ Hashtable parameters = ExtractXmlRpcParams(doc);
+
+ string agentId = parameters["agentId"]?.ToString();
+ string secureSessionId = parameters["secureSessionId"]?.ToString();
+ int currencyBuy = int.Parse(parameters["currencyBuy"]?.ToString() ?? "0");
+
+ m_log.InfoFormat("[CURRENCY PROCESS PHP]: Parsed values - AgentId: {0}, CurrencyBuy: {1}, SecureSessionId: {2}", agentId, currencyBuy, secureSessionId);
+
+ string transactionID = parameters["transactionID"]?.ToString();
+ string userID = parameters["agentId"]?.ToString();
+ int amount = int.Parse(parameters["currencyBuy"]?.ToString() ?? "0");
+
+ if (string.IsNullOrEmpty(transactionID))
+ {
+ transactionID = secureSessionId;
+ }
+
+ m_log.InfoFormat("[CURRENCY PROCESS PHP]: Parsed values - transactionID: {0}, userID: {1}, amount: {2}", transactionID, userID, amount);
+
+ if (m_CurrencyGroupOnly && m_CurrencyGroupID != "00000000-0000-0000-0000-000000000000" && !IsUserInGroup(agentId, m_CurrencyGroupID))
+ {
+ m_log.InfoFormat("[CURRENCY PROCESS PHP]: User {0} is not a member of the required group {1}.", agentId, m_CurrencyGroupID);
+ httpResponse.StatusCode = 403;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("User is not a member of the required group");
+ return;
+ }
+
+ if (m_UserMailLock && !UserMailLock(agentId))
+ {
+ m_log.InfoFormat("[CURRENCY PROCESS PHP]: User {0} does not have a registered email address.", agentId);
+ httpResponse.StatusCode = 403;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("User does not have a registered email address");
+ return;
+ }
+
+ if (m_CurrencyOnOff != "off" && !CheckGroupMoney(agentId, m_CurrencyGroupID))
+ {
+ m_log.Info("[CURRENCY PROCESS PHP]: Currency purchase is turned off for this user.");
+ httpResponse.StatusCode = 403;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Currency purchase is turned off");
+ return;
+ }
+
+ m_log.InfoFormat("[CURRENCY PROCESS PHP]: Currency Maximum loaded: {0}", m_CurrencyMaximum);
+
+ if (methodName == "getCurrencyQuote")
+ {
+ Hashtable quoteResponse = PerformGetCurrencyQuote(agentId, currencyBuy, secureSessionId);
+
+ int excessAmount = CheckMaximumMoney(agentId, m_CurrencyMaximum);
+ if (excessAmount > 0)
+ {
+ quoteResponse["message"] = $"Your balance was reduced by {excessAmount} to enforce the maximum limit.";
+ }
+
+ XmlRpcResponse xmlResponse = new XmlRpcResponse { Value = quoteResponse };
+ httpResponse.StatusCode = 200;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes(xmlResponse.ToString());
+ }
+ else if (methodName == "buyCurrency")
+ {
+ Hashtable purchaseResponse = PerformBuyCurrency(agentId, currencyBuy, secureSessionId);
+
+ if ((bool)purchaseResponse["success"])
+ {
+ m_log.Info("[CURRENCY PROCESS PHP]: Purchase successful. Proceeding to credit currency.");
+ PerformMoneyTransfer("BANKER", agentId, currencyBuy);
+ UpdateBalance(agentId, "Currency purchase successful.");
+
+ CheckMaximumMoney(agentId, m_CurrencyMaximum);
+
+ XmlRpcResponse xmlResponse = new XmlRpcResponse { Value = purchaseResponse };
+ httpResponse.StatusCode = 200;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes(xmlResponse.ToString());
+ }
+ else
+ {
+ m_log.Error("[CURRENCY PROCESS PHP]: Currency purchase failed.");
+ httpResponse.StatusCode = 400;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Currency purchase failed");
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[CURRENCY PROCESS PHP]: Unknown method name: {0}", methodName);
+ httpResponse.StatusCode = 400;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Invalid method name");
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[CURRENCY PROCESS PHP]: Error processing request. Error: {0}", ex.ToString());
+ httpResponse.StatusCode = 500;
+ httpResponse.RawBuffer = Encoding.UTF8.GetBytes("Error");
+ }
+ }
+
+
+ private bool UserMailLock(string userID)
+ {
+ m_log.InfoFormat("[USER MAIL LOCK]: Checking if user {0} has a registered email address", userID);
+
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ // SQL-Abfrage zum Überprüfen, ob der Benutzer eine hinterlegte E-Mail-Adresse hat
+ string sql = "SELECT email FROM `UserAccounts` WHERE PrincipalID = ?userID";
+ string email = null;
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ using (MySqlDataReader reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ email = reader.GetString("email");
+ m_log.InfoFormat("[USER MAIL LOCK]: User {0} email address is {1}", userID, email);
+ }
+ }
+ }
+
+ // Überprüfen, ob die E-Mail-Adresse leer ist oder null
+ if (string.IsNullOrEmpty(email))
+ {
+ m_log.InfoFormat("[USER MAIL LOCK]: User {0} does not have a registered email address", userID);
+ return false; // Benutzer hat keine hinterlegte E-Mail-Adresse
+ }
+
+ m_log.InfoFormat("[USER MAIL LOCK]: User {0} has a registered email address", userID);
+ return true; // Benutzer hat eine hinterlegte E-Mail-Adresse
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[USER MAIL LOCK]: Error checking user email: {0}", ex.Message);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ private bool CheckGroupMoney(string agentId, string groupId)
+ {
+ m_log.InfoFormat("[CHECK GROUP MONEY]: Checking group membership for agentId: {0}, groupId: {1}", agentId, groupId);
+
+ if (string.IsNullOrEmpty(groupId) || groupId == "00000000-0000-0000-0000-000000000000")
+ {
+ m_log.Info("[CHECK GROUP MONEY]: groupId is empty or set to accept all groups, returning true");
+ return true;
+ }
+
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ string sql = "SELECT COUNT(*) FROM os_groups_membership WHERE PrincipalID = ?agentId AND GroupID = ?groupId";
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?agentId", agentId);
+ cmd.Parameters.AddWithValue("?groupId", groupId);
+
+ int count = Convert.ToInt32(cmd.ExecuteScalar());
+ m_log.InfoFormat("[CHECK GROUP MONEY]: Query result: count={0}", count);
+ return count > 0;
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[CHECK GROUP MONEY]: Error checking group membership: {0}", ex.Message);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+
+ // Überprüfen, ob der Benutzer Mitglied der angegebenen Gruppe ist
+ private bool IsUserInGroup(string agentId, string groupId)
+ {
+ // IClientAPI
+ m_log.DebugFormat("[IsUserInGroup]: Checking group membership for agentId={0}, groupId={1}", agentId, groupId);
+
+ if (string.IsNullOrEmpty(groupId))
+ {
+ m_log.Debug("[IsUserInGroup]: groupId is empty, returning true");
+ return true; // Keine Einschränkung, wenn groupId nicht gesetzt ist
+ }
+
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ // SQL-Abfrage zum Überprüfen der Gruppenmitgliedschaft
+ string sql = "SELECT COUNT(*) FROM os_groups_membership WHERE PrincipalID = ?agentId AND GroupID = ?groupId";
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?agentId", agentId);
+ cmd.Parameters.AddWithValue("?groupId", groupId);
+
+ int count = Convert.ToInt32(cmd.ExecuteScalar());
+ m_log.DebugFormat("[IsUserInGroup]: Query result: count={0}", count);
+ return count > 0;
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[IsUserInGroup]: Error checking group membership: {0}", ex.Message);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ public new int CheckMaximumMoney(string userID, int m_CurrencyMaximum)
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ m_log.InfoFormat("[CHECK MAXIMUM MONEY]: Checking for userID: {0}, Currency Maximum: {1}", userID, m_CurrencyMaximum);
+
+ try
+ {
+ // Ausnahmen für SYSTEM und BANKER
+ if (userID == "SYSTEM" || userID == "BANKER" || userID == m_bankerAvatar)
+ {
+ m_log.InfoFormat("[CHECK MAXIMUM MONEY]: User {0} is SYSTEM or BANKER, skipping check.", userID);
+ return 0; // Keine Begrenzung für diese Benutzer
+ }
+
+ // Abrufen des aktuellen Guthabens des Benutzers
+ string sql = "SELECT balance FROM balances WHERE user = ?userID";
+ int currentBalance = 0;
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ using (MySqlDataReader reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ currentBalance = reader.GetInt32("balance");
+ m_log.InfoFormat("[CHECK MAXIMUM MONEY]: Current balance for user {0} is {1}", userID, currentBalance);
+ }
+ }
+ }
+
+ // Überprüfen, ob das Guthaben über dem Maximum liegt und ggf. abziehen
+ if (currentBalance > m_CurrencyMaximum)
+ {
+ int excessAmount = currentBalance - m_CurrencyMaximum;
+
+ // Guthaben auf das Maximum reduzieren
+ sql = "UPDATE balances SET balance = ?newBalance WHERE user = ?userID";
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?newBalance", m_CurrencyMaximum);
+ cmd.Parameters.AddWithValue("?userID", userID);
+ cmd.ExecuteNonQuery();
+ }
+
+ m_log.InfoFormat("[CHECK MAXIMUM MONEY]: Reduced balance for user {0} by {1} to enforce maximum limit of {2}", userID, excessAmount, m_CurrencyMaximum);
+ return excessAmount; // Rückgabe des abgezogenen Betrags
+ }
+
+ m_log.InfoFormat("[CHECK MAXIMUM MONEY]: No adjustment needed for user {0}", userID);
+ return 0; // Keine Änderung, falls das Guthaben innerhalb des Limits liegt
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[CHECK MAXIMUM MONEY]: Error checking and updating user balance: {0}", ex.Message);
+ throw;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+ private Hashtable ExtractXmlRpcParams(XmlDocument doc)
+ {
+ Hashtable parameters = new Hashtable();
+ XmlNodeList members = doc.SelectNodes("//param/value/struct/member");
+
+ foreach (XmlNode member in members)
+ {
+ string name = member.SelectSingleNode("name")?.InnerText;
+ string value = member.SelectSingleNode("value")?.InnerText;
+
+ if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value))
+ {
+ parameters[name] = value;
+ }
+ }
+ return parameters;
+ }
+
+ private Hashtable PerformGetCurrencyQuote(string agentId, int currencyBuy, string secureSessionId)
+ {
+ m_log.InfoFormat("[PERFORM GET CURRENCY QUOTE]: Generating currency quote for AgentId: {0}", agentId);
+
+ int rate = 100; // 1€ = 100L$
+ return new Hashtable
+ {
+ { "success", true },
+ { "currency", new Hashtable
+ {
+ { "estimatedCost", currencyBuy / rate }, // Kosten in €
+ { "currencyBuy", currencyBuy } // Angeforderte Spielwährung
+ }
+ },
+ { "confirm", Guid.NewGuid().ToString() }
+ };
+ }
+
+ private Hashtable PerformBuyCurrency(string agentId, int currencyBuy, string secureSessionId)
+ {
+ if (string.IsNullOrEmpty(agentId))
+ {
+ m_log.Error("[PERFORM BUY CURRENCY]: AgentId is null or empty.");
+ return new Hashtable
+ {
+ { "success", false },
+ { "message", "AgentId is required." }
+ };
+ }
+
+ if (currencyBuy <= 0)
+ {
+ m_log.Error("[PERFORM BUY CURRENCY]: Invalid currencyBuy amount.");
+ return new Hashtable
+ {
+ { "success", false },
+ { "message", "Invalid currencyBuy amount." }
+ };
+ }
+
+ m_log.InfoFormat("[PERFORM BUY CURRENCY]: Processing currency purchase for AgentId: {0}, Amount: {1}", agentId, currencyBuy);
+ return new Hashtable
+ {
+ { "success", true },
+ { "message", $"Successfully purchased {currencyBuy}L$ for {agentId}" }
+ };
+ }
+ public XmlRpcResponse getCurrencyQuote(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ int amount = 0;
+
+ // Protokolliere die eingehende XML-Anfrage
+ m_log.InfoFormat("[GET CURRENCY QUOTE]: Incoming XML Request: {0}", ToXmlString((Hashtable)request.Params[0]));
+
+ try
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ amount = (int)requestData["currencyBuy"];
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[GET CURRENCY QUOTE]: Error parsing request: {0}", ex.Message);
+ }
+
+ // Berechnung und Antwortvorbereitung
+ Hashtable currencyResponse = new Hashtable
+ {
+ { "estimatedCost", amount * 0.01 }, // Berechnung für Kosten
+ { "currencyBuy", amount }
+ };
+
+ Hashtable quoteResponse = new Hashtable
+ {
+ { "success", true },
+ { "currency", currencyResponse },
+ { "confirm", Guid.NewGuid().ToString() }
+ };
+
+ // Protokolliere die Antwort, bevor sie zurückgegeben wird
+ m_log.InfoFormat("[GET CURRENCY QUOTE]: XML Response: {0}", ToXmlString(quoteResponse));
+
+ // Erstelle die Antwort
+ XmlRpcResponse returnval = new XmlRpcResponse { Value = quoteResponse };
+
+ // Füge ein weiteres Log hinzu, um sicherzustellen, dass die Antwort korrekt erstellt wurde
+ m_log.InfoFormat("[GET CURRENCY QUOTE]: Returning response for getCurrencyQuote: {0}", ToXmlString((Hashtable)returnval.Value));
+
+ return returnval;
+ }
+ public XmlRpcResponse buyCurrency(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ string agentId = requestData["agentId"]?.ToString();
+ int amount = (int)requestData["currencyBuy"];
+
+ // Protokolliere die eingehende XML-Anfrage
+ m_log.InfoFormat("[BUY CURRENCY]: Incoming XML Request: {0}", ToXmlString((Hashtable)request.Params[0]));
+
+ // Verarbeite den Kauf und logge die Details
+ m_log.InfoFormat("[BUY CURRENCY]: Processing currency purchase for AgentId: {0}, Amount: {1}, Banker: {2}", agentId, amount, m_bankerAvatar);
+
+ // Hier erfolgt der Transfer an den Money Banker (BankerAvatar als Sender)
+ string senderID = m_bankerAvatar; // Der Sender ist der BankerAvatar
+ string receiverID = agentId; // Der Empfänger ist der Agent
+ UUID transactionUUID = UUID.Random(); // Eine eindeutige Transaktions-ID für die Transaktion
+
+ Hashtable responseData = new Hashtable();
+ responseData["success"] = false;
+
+ // Versuche, die Transaktion auszuführen
+ try
+ {
+ // Logge die Übertragung
+ m_log.InfoFormat("[MONEY XMLRPC]: handlePayMoneyCharge: Transferring money from {0} to {1}, Amount = {2}", senderID, receiverID, amount);
+
+ // Führe die tatsächliche Transaktion durch, indem die handlePayMoneyCharge-Methode aufgerufen wird
+ XmlRpcResponse transferResponse = handlePayMoneyCharge(request, remoteClient); // Übergibt die Anfrage an die tatsächliche Überweisungsmethode
+
+ // Überprüfe, ob die Antwort erfolgreich war
+ if (transferResponse != null && transferResponse.Value is Hashtable transferResult &&
+ transferResult.ContainsKey("success") && (bool)transferResult["success"])
+ {
+ // Wenn die Transaktion erfolgreich war, setze die Antwortdaten
+ responseData["success"] = true;
+ responseData["message"] = $"Successfully purchased {amount} currency for AgentId {agentId}";
+ }
+ else
+ {
+ // Fehler bei der Überweisung
+ responseData["message"] = "Currency purchase failed during money transfer.";
+ }
+ }
+ catch (Exception ex)
+ {
+ // Fehlerbehandlung
+ m_log.Error($"[BUY CURRENCY]: Error processing currency purchase: {ex.Message}");
+ responseData["message"] = "Currency purchase failed.";
+ }
+
+ // Erstelle die Antwort
+ XmlRpcResponse returnval = new XmlRpcResponse { Value = responseData };
+
+ // Protokolliere die XML-Antwort
+ m_log.InfoFormat("[BUY CURRENCY]: XML Response: {0}", ToXmlString(responseData));
+
+ return returnval;
+ }
+ public new bool PerformMoneyTransfer(string senderID, string receiverID, int amount)
+ {
+ //m_log.InfoFormat("[MONEY TRANSFER]: Transferring {0} from {1} to {2}.", amount, senderID, receiverID);
+ try
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "UPDATE balances SET balance = balance - ?amount WHERE user = ?senderID; " +
+ "UPDATE balances SET balance = balance + ?amount WHERE user = ?receiverID";
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?senderID", senderID);
+ cmd.Parameters.AddWithValue("?receiverID", receiverID);
+
+ int rowsAffected = cmd.ExecuteNonQuery();
+ bool result = (rowsAffected > 0);
+
+ if (result)
+ {
+ LogTransaction(UUID.Random(), senderID, -amount);
+ LogTransaction(UUID.Random(), receiverID, amount);
+ }
+
+ return result;
+ }
+ }
+ catch (Exception)
+ {
+ //m_log.ErrorFormat("[MONEY TRANSFER]: Error transferring money: {0}", ex.Message);
+ return false;
+ }
+ }
+
+ public new void InitializeUserCurrency(string agentId)
+ {
+ m_log.InfoFormat("[INITIALIZE USER CURRENCY]: Initializing currency for new user: {0}", agentId);
+
+ try
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "INSERT INTO balances (user, balance) VALUES (?agentId, ?realMoney), (?agentId, ?gameMoney)";
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?agentId", agentId);
+ cmd.Parameters.AddWithValue("?realMoney", m_realMoney);
+ cmd.Parameters.AddWithValue("?gameMoney", m_gameMoney);
+
+ cmd.ExecuteNonQuery();
+ }
+
+ m_log.InfoFormat("[INITIALIZE USER CURRENCY]: User {0} initialized with {1}€ and {2}L$.", agentId, m_realMoney, m_gameMoney);
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[INITIALIZE USER CURRENCY]: Error initializing user currency: {0}", ex.Message);
+ }
+ }
+
+ public new Hashtable ApplyFallbackCredit(string agentId)
+ {
+ m_log.WarnFormat("[FALLBACK CREDIT]: Applying fallback credit for user {0}", agentId);
+
+ try
+ {
+ MySQLSuperManager dbm = GetLockedConnection();
+ string sql = "UPDATE balances SET balance = balance + 100 WHERE user = ?agentId";
+
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?agentId", agentId);
+ cmd.ExecuteNonQuery();
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[FALLBACK CREDIT]: Error applying fallback credit: {0}", ex.Message);
+ }
+
+ return new Hashtable
+ {
+ { "success", true },
+ { "creditedAmount", 100 },
+ { "message", "Fallback credit applied due to transaction failure." }
+ };
+ }
+
+ public bool ValidateTransaction(UUID transactionID, string secureCode)
+ {
+ // Die Methode ValidateTransfer aus IMoneyDBService verwenden
+ bool isValid = ValidateTransfer(secureCode, transactionID);
+ if (!isValid)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Transaction validation failed for TransactionID: {0}", transactionID);
+ return false;
+ }
+ return true;
+ }
+ public bool AddMoney(UUID transactionUUID)
+ {
+ // Geld zu einer Transaktion hinzufügen
+ bool success = DoAddMoney(transactionUUID);
+ if (!success)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Failed to add money for TransactionUUID: {0}", transactionUUID);
+ return false;
+ }
+ return true;
+ }
+ public bool WithdrawMoney(UUID transactionID, string senderID, int amount)
+ {
+ bool success = withdrawMoney(transactionID, senderID, amount);
+ if (!success)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Failed to withdraw {0} for sender {1} in TransactionID: {2}", amount, senderID, transactionID);
+ return false;
+ }
+ return true;
+ }
+ public bool GiveMoney(UUID transactionID, string receiverID, int amount)
+ {
+ bool success = giveMoney(transactionID, receiverID, amount);
+ if (!success)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Failed to give {0} to receiver {1} in TransactionID: {2}", amount, receiverID, transactionID);
+ return false;
+ }
+ return true;
+ }
+ public bool UpdateTransactionStatus(UUID transactionID, int status, string description)
+ {
+ bool success = updateTransactionStatus(transactionID, status, description);
+ if (!success)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Failed to update status for TransactionID: {0} with Status: {1}", transactionID, status);
+ return false;
+ }
+ return true;
+ }
+ public bool AddUser(string userID, int balance, int status, int type)
+ {
+ bool success = addUser(userID, balance, status, type);
+ if (!success)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Failed to add user {0} with balance {1}", userID, balance);
+ return false;
+ }
+ return true;
+ }
+ public new IEnumerable GetTransactionHistory(string userID, int startTime, int endTime)
+ {
+ return GetTransactionHistory(userID, startTime, endTime);
+ }
+ public new UserInfo FetchUserInfo(string userID)
+ {
+ return FetchUserInfo(userID);
+ }
+ public new bool UserExists(string userID)
+ {
+ return UserExists(userID);
+ }
+ public bool PerformTransaction(UUID transactionUUID)
+ {
+ bool success = DoTransfer(transactionUUID);
+ if (!success)
+ {
+ m_log.ErrorFormat("[MONEY SERVER]: Failed to perform transaction for TransactionUUID: {0}", transactionUUID);
+ return false;
+ }
+ return true;
+ }
+ public static bool IsValidEmail(string email)
+ {
+ if (string.IsNullOrWhiteSpace(email))
+ {
+ return false;
+ }
+
+ // Regular Expression für gültige E-Mail-Adressen
+ const string pattern = @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
+
+ // Überprüfen, ob die E-Mail-Adresse das Muster erfüllt
+ return Regex.IsMatch(email, pattern, RegexOptions.Compiled);
+ }
+
+ private Dictionary balances = new Dictionary();
+
+ public int GetBalance(UUID uuid)
+ {
+ if (balances.TryGetValue(uuid, out int balance))
+ {
+ return balance;
+ }
+ else
+ {
+ // Return a default value if the UUID is not found
+ return 0;
+ }
+ }
+
+ public void SetBalance(UUID uuid, int balance)
+ {
+ balances[uuid] = balance;
+ }
+
+ #endregion
+ // ################## XMLRPC Pasing ##################
+ #region XMLRPC Pasing
+
+
+ public static string ToXmlStringMini(Hashtable data)
+ {
+ XmlDocument doc = new XmlDocument();
+ XmlElement root = doc.CreateElement("methodResponse");
+ doc.AppendChild(root);
+
+ XmlElement paramsElement = doc.CreateElement("params");
+ root.AppendChild(paramsElement);
+
+ foreach (DictionaryEntry entry in data)
+ {
+ XmlElement param = doc.CreateElement("param");
+ XmlElement value = doc.CreateElement("value");
+ value.InnerText = entry.Value.ToString();
+ param.AppendChild(value);
+ paramsElement.AppendChild(param);
+ }
+
+ return doc.OuterXml;
+ }
+ private string ToXmlString(Hashtable data)
+ {
+ XmlDocument doc = new XmlDocument();
+ XmlNode methodCallNode = doc.CreateElement("methodCall");
+
+ XmlNode methodNameNode = doc.CreateElement("methodName");
+ methodNameNode.InnerText = "response";
+ methodCallNode.AppendChild(methodNameNode);
+
+ XmlNode paramsNode = doc.CreateElement("params");
+ foreach (DictionaryEntry entry in data)
+ {
+ XmlNode paramNode = doc.CreateElement("param");
+
+ XmlNode valueNode = doc.CreateElement("value");
+ if (entry.Value is Hashtable)
+ {
+ XmlNode structNode = doc.CreateElement("struct");
+ foreach (DictionaryEntry structEntry in (Hashtable)entry.Value)
+ {
+ XmlNode memberNode = doc.CreateElement("member");
+ XmlNode nameNode = doc.CreateElement("name");
+ nameNode.InnerText = structEntry.Key.ToString();
+ XmlNode valueNode2 = doc.CreateElement("value");
+ valueNode2.InnerText = structEntry.Value.ToString();
+ memberNode.AppendChild(nameNode);
+ memberNode.AppendChild(valueNode2);
+ structNode.AppendChild(memberNode);
+ }
+ valueNode.AppendChild(structNode);
+ }
+ else
+ {
+ valueNode.InnerText = entry.Value.ToString();
+ }
+
+ paramNode.AppendChild(valueNode);
+ paramsNode.AppendChild(paramNode);
+ }
+
+ methodCallNode.AppendChild(paramsNode);
+ doc.AppendChild(methodCallNode);
+ return doc.OuterXml;
+ }
+
+ // Methode zur Verarbeitung und Parsing der XML-RPC-Anfrage
+ private object ParseXmlRpcRequest(string xml)
+ {
+ XmlDocument doc = new XmlDocument();
+ doc.LoadXml(xml);
+
+ XmlNode methodCallNode = doc.SelectSingleNode("/methodCall");
+ XmlNode methodNameNode = methodCallNode.SelectSingleNode("methodName");
+
+ if (methodNameNode == null)
+ throw new Exception("Missing method name");
+
+ string methodName = methodNameNode.InnerText;
+ XmlNodeList members = methodCallNode.SelectNodes("//param/value/struct/member");
+
+ if (methodName == "getCurrencyQuote")
+ {
+ CurrencyQuoteRequest request = new CurrencyQuoteRequest();
+ foreach (XmlNode member in members)
+ {
+ string name = member.SelectSingleNode("name").InnerText;
+ string value = member.SelectSingleNode("value").InnerText;
+
+ switch (name)
+ {
+ case "agentId": request.AgentId = value; break;
+ case "currencyBuy": request.CurrencyBuy = int.Parse(value); break;
+ case "language": request.Language = value; break;
+ case "secureSessionId": request.SecureSessionId = value; break;
+ case "viewerBuildVersion": request.ViewerBuildVersion = value; break;
+ case "viewerChannel": request.ViewerChannel = value; break;
+ case "viewerMajorVersion": request.ViewerMajorVersion = int.Parse(value); break;
+ case "viewerMinorVersion": request.ViewerMinorVersion = int.Parse(value); break;
+ case "viewerPatchVersion": request.ViewerPatchVersion = int.Parse(value); break;
+ }
+ }
+ m_log.InfoFormat("[MONEY XML RPC MODULE]: Processed Currency Quote Request for AgentId: {0}", request.AgentId);
+ return request;
+
+ }
+ else if (methodName == "preflightBuyLandPrep")
+ {
+ LandPurchaseRequest request = new LandPurchaseRequest();
+ foreach (XmlNode member in members)
+ {
+ string name = member.SelectSingleNode("name").InnerText;
+ string value = member.SelectSingleNode("value").InnerText;
+
+ switch (name)
+ {
+ case "agentId": request.AgentId = value; break;
+ case "billableArea": request.BillableArea = int.Parse(value); break;
+ case "currencyBuy": request.CurrencyBuy = int.Parse(value); break;
+ case "language": request.Language = value; break;
+ case "secureSessionId": request.SecureSessionId = value; break;
+ }
+ }
+ m_log.InfoFormat("[MONEY XML RPC MODULE]: Processed Land Purchase Request for AgentId: {0}, BillableArea: {1}", request.AgentId, request.BillableArea);
+ return request;
+ }
+ m_log.ErrorFormat("[MONEY XML RPC MODULE]: Unknown method name: {0}", methodName);
+ throw new Exception("Unknown method name: " + methodName);
+ }
+
+
+ private void LogXmlRpcRequestFile(IOSHttpRequest request)
+ {
+ try
+ {
+ // Erstelle einen Dateipfad für das Log
+ string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "xmlrpc_debug.log");
+
+ // Lies den Request-Body
+ string requestBody;
+ using (var reader = new StreamReader(request.InputStream, Encoding.UTF8))
+ {
+ requestBody = reader.ReadToEnd();
+ }
+
+ // Bereite den Logeintrag vor
+ string logEntry = $"{DateTime.UtcNow}: {request.RawUrl}\n{requestBody}\n\n";
+
+ // Schreibe den Logeintrag in die Datei
+ File.AppendAllText(logFilePath, logEntry);
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XML RPC MODULE DEBUG]: Error logging XML-RPC request: {0}", ex.Message);
+ }
+ }
+
+ private void LogXmlRpcRequestConsole(IOSHttpRequest request)
+ {
+ m_log.InfoFormat("[MONEY XML RPC MODULE]: {0}", new StreamReader(request.InputStream).ReadToEnd()); // TODO: test
+
+ try
+ {
+ // Lies den Request-Body
+ string requestBody;
+ using (var reader = new StreamReader(request.InputStream, Encoding.UTF8))
+ {
+ requestBody = reader.ReadToEnd();
+ }
+
+ // Bereite den Logeintrag vor
+ string logEntry = $"{DateTime.UtcNow}: {request.RawUrl}\n{requestBody}\n\n";
+
+ // Schreibe den Logeintrag in das Log
+ m_log.Info(logEntry);
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XML RPC MODULE DEBUG]: Error logging XML-RPC request: {0}", ex.Message);
+ }
+ }
+ #endregion
+ // ################## handler ##################
+ #region handler
+
+ // Spezifische Handler BalanceUpdateHandler: Verarbeitet Updates zum Kontostand.
+ public XmlRpcResponse BalanceUpdateHandler(XmlRpcRequest request, IPEndPoint client)
+ {
+ if (request == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: BalanceUpdateHandler: request is null.");
+ return new XmlRpcResponse();
+ }
+
+ if (client == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: BalanceUpdateHandler: client is null.");
+ return new XmlRpcResponse();
+ }
+
+ try
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ string balanceUpdateData = (string)requestData["balanceUpdateData"];
+
+ // Process the balance update data
+ m_log.InfoFormat("[MONEY XMLRPC]: BalanceUpdateHandler: Updating balance for user {0}", balanceUpdateData);
+
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ responseData.Add("success", true);
+ response.Value = responseData;
+ return response;
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: BalanceUpdateHandler: Exception occurred: {0}", ex.Message);
+ return new XmlRpcResponse();
+ }
+ }
+
+ // Spezifische Handler UserAlertHandler: Handhabt Benutzerbenachrichtigungen.
+ public XmlRpcResponse UserAlertHandler(XmlRpcRequest request, IPEndPoint client)
+ {
+ try
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ string alertMessage = (string)requestData["alertMessage"];
+
+ // Process the alert message
+ m_log.InfoFormat("[MONEY XMLRPC]: UserAlertHandler: Alert message received: {0}", alertMessage);
+
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ responseData.Add("success", true);
+ response.Value = responseData;
+ return response;
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: UserAlertHandler: Exception occurred: {0}", ex.Message);
+ return new XmlRpcResponse();
+ }
+ }
+
+ public XmlRpcResponse handleClientLogin(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogin: Start.");
+
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ responseData["success"] = false;
+ responseData["clientBalance"] = 0;
+
+ // Check Client Cert
+ if (m_moneyCore.IsCheckClientCert())
+ {
+ string commonName = GetSSLCommonName();
+ if (commonName == "")
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleClientLogin: Warnning: Check Client Cert is set, but SSL Common Name is empty.");
+ responseData["success"] = false;
+ responseData["description"] = "SSL Common Name is empty";
+ return response;
+ }
+ else
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogin: SSL Common Name is \"{0}\"", commonName);
+ }
+
+ }
+
+ string universalID = string.Empty;
+ string clientUUID = string.Empty;
+ string sessionID = string.Empty;
+ string secureID = string.Empty;
+ string simIP = string.Empty;
+ string userName = string.Empty;
+ int balance = 0;
+ int avatarType = (int)AvatarType.UNKNOWN_AVATAR;
+ int avatarClass = (int)AvatarType.UNKNOWN_AVATAR;
+
+ if (requestData.ContainsKey("clientUUID")) clientUUID = (string)requestData["clientUUID"];
+ if (requestData.ContainsKey("clientSessionID")) sessionID = (string)requestData["clientSessionID"];
+ if (requestData.ContainsKey("clientSecureSessionID")) secureID = (string)requestData["clientSecureSessionID"];
+ if (requestData.ContainsKey("universalID")) universalID = (string)requestData["universalID"];
+ if (requestData.ContainsKey("userName")) userName = (string)requestData["userName"];
+ if (requestData.ContainsKey("openSimServIP")) simIP = (string)requestData["openSimServIP"];
+ if (requestData.ContainsKey("avatarType")) avatarType = Convert.ToInt32(requestData["avatarType"]);
+ if (requestData.ContainsKey("avatarClass")) avatarClass = Convert.ToInt32(requestData["avatarClass"]);
+
+ string firstName = string.Empty;
+ string lastName = string.Empty;
+ string serverURL = string.Empty;
+ string securePsw = string.Empty;
+
+ if (!String.IsNullOrEmpty(universalID))
+ {
+ UUID uuid;
+ Util.ParseUniversalUserIdentifier(universalID, out uuid, out serverURL, out firstName, out lastName, out securePsw);
+ }
+ if (String.IsNullOrEmpty(userName))
+ {
+ userName = firstName + " " + lastName;
+ }
+
+ // Information from DB
+ UserInfo userInfo = m_moneyDBService.FetchUserInfo(clientUUID);
+ if (userInfo != null)
+ {
+ avatarType = userInfo.Type; // Avatar Type is not updated
+ if (avatarType == (int)AvatarType.LOCAL_AVATAR) avatarClass = (int)AvatarType.LOCAL_AVATAR;
+ if (avatarClass == (int)AvatarType.UNKNOWN_AVATAR) avatarClass = userInfo.Class;
+ if (String.IsNullOrEmpty(userName)) userName = userInfo.Avatar;
+ }
+
+ if (avatarType == (int)AvatarType.UNKNOWN_AVATAR) avatarType = avatarClass;
+ if (String.IsNullOrEmpty(serverURL)) avatarClass = (int)AvatarType.NPC_AVATAR;
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: Avatar {0} ({1}) is logged on.", userName, clientUUID);
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: Avatar Type is {0} and Avatar Class is {1}", avatarType, avatarClass);
+
+ // Check Avatar
+ if (avatarClass == (int)AvatarType.GUEST_AVATAR && !m_gst_enable)
+ {
+ responseData["description"] = "Avatar is a Guest avatar. But this Money Server does not support Guest avatars.";
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: {0}", responseData["description"]);
+ return response;
+ }
+ else if (avatarClass == (int)AvatarType.HG_AVATAR && !m_hg_enable)
+ {
+ responseData["description"] = "Avatar is a HG avatar. But this Money Server does not support HG avatars.";
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: {0}", responseData["description"]);
+ return response;
+ }
+ else if (avatarClass == (int)AvatarType.FOREIGN_AVATAR)
+ {
+ responseData["description"] = "Avatar is a Foreign avatar.";
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: {0}", responseData["description"]);
+ return response;
+ }
+ else if (avatarClass == (int)AvatarType.UNKNOWN_AVATAR)
+ {
+ responseData["description"] = "Avatar is a Unknown avatar.";
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: {0}", responseData["description"]);
+ return response;
+ }
+ // NPC
+ else if (avatarClass == (int)AvatarType.NPC_AVATAR)
+ {
+ responseData["success"] = true;
+ responseData["clientBalance"] = 0;
+ responseData["description"] = "Avatar is a NPC.";
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogon: {0}", responseData["description"]);
+ return response;
+ }
+
+ //Update the session and secure session dictionary
+ lock (m_sessionDic)
+ {
+ if (!m_sessionDic.ContainsKey(clientUUID))
+ {
+ m_sessionDic.Add(clientUUID, sessionID);
+ }
+ else m_sessionDic[clientUUID] = sessionID;
+ }
+ lock (m_secureSessionDic)
+ {
+ if (!m_secureSessionDic.ContainsKey(clientUUID))
+ {
+ m_secureSessionDic.Add(clientUUID, secureID);
+ }
+ else m_secureSessionDic[clientUUID] = secureID;
+ }
+
+ try
+ {
+ if (userInfo == null) userInfo = new UserInfo();
+ userInfo.UserID = clientUUID;
+ userInfo.SimIP = simIP;
+ userInfo.Avatar = userName;
+ userInfo.PswHash = UUID.Zero.ToString();
+ userInfo.Type = avatarType;
+ userInfo.Class = avatarClass;
+ userInfo.ServerURL = serverURL;
+ if (!String.IsNullOrEmpty(securePsw)) userInfo.PswHash = securePsw;
+
+ if (!m_moneyDBService.TryAddUserInfo(userInfo))
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleClientLogin: Unable to refresh information for user \"{0}\" in DB.", userName);
+ responseData["success"] = true; // for FireStorm
+ responseData["description"] = "Update or add user information to db failed";
+ return response;
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleClientLogin: Can't update userinfo for user {0}: {1}", clientUUID, e.ToString());
+ responseData["description"] = "Exception occured" + e.ToString();
+ return response;
+ }
+
+ try
+ {
+ balance = m_moneyDBService.getBalance(clientUUID);
+
+ //add user to balances table if not exist. (if balance is -1, it means avatar is not exist at balances table)
+ if (balance == -1)
+ {
+ int default_balance = m_defaultBalance;
+ if (avatarClass == (int)AvatarType.HG_AVATAR) default_balance = m_hg_defaultBalance;
+ if (avatarClass == (int)AvatarType.GUEST_AVATAR) default_balance = m_gst_defaultBalance;
+
+ if (m_moneyDBService.addUser(clientUUID, default_balance, 0, avatarType))
+ {
+ responseData["success"] = true;
+ responseData["description"] = "add user successfully";
+ responseData["clientBalance"] = default_balance;
+ }
+ else
+ {
+ responseData["description"] = "add user failed";
+ }
+ }
+ //Success
+ else if (balance >= 0)
+ {
+ responseData["success"] = true;
+ responseData["description"] = "get user balance successfully";
+ responseData["clientBalance"] = balance;
+ }
+
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleClientLogin: Can't get balance of user {0}: {1}", clientUUID, e.ToString());
+ responseData["description"] = "Exception occured" + e.ToString();
+ }
+
+ return response;
+ }
+
+
+ public XmlRpcResponse handleClientLogout(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string clientUUID = string.Empty;
+ if (requestData.ContainsKey("clientUUID")) clientUUID = (string)requestData["clientUUID"];
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleClientLogout: User {0} is logging off.", clientUUID);
+ try
+ {
+ lock (m_sessionDic)
+ {
+ if (m_sessionDic.ContainsKey(clientUUID))
+ {
+ m_sessionDic.Remove(clientUUID);
+ }
+ }
+
+ lock (m_secureSessionDic)
+ {
+ if (m_secureSessionDic.ContainsKey(clientUUID))
+ {
+ m_secureSessionDic.Remove(clientUUID);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleClientLogout: Failed to delete user session: " + e.ToString());
+ responseData["success"] = false;
+ return response;
+ }
+
+ responseData["success"] = true;
+ return response;
+ }
+
+ public XmlRpcResponse handleTransaction(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleTransaction:");
+
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ int amount = 0;
+ int transactionType = 0;
+ string senderID = string.Empty;
+ string receiverID = string.Empty;
+ string senderSessionID = string.Empty;
+ string senderSecureSessionID = string.Empty;
+ string objectID = string.Empty;
+ string objectName = string.Empty;
+ string regionHandle = string.Empty;
+ string regionUUID = string.Empty;
+ string description = "Newly added on";
+
+ responseData["success"] = false;
+ UUID transactionUUID = UUID.Random();
+
+ if (requestData.ContainsKey("senderID")) senderID = (string)requestData["senderID"];
+ if (requestData.ContainsKey("receiverID")) receiverID = (string)requestData["receiverID"];
+ if (requestData.ContainsKey("senderSessionID")) senderSessionID = (string)requestData["senderSessionID"];
+ if (requestData.ContainsKey("senderSecureSessionID")) senderSecureSessionID = (string)requestData["senderSecureSessionID"];
+ if (requestData.ContainsKey("amount")) amount = Convert.ToInt32(requestData["amount"]);
+ if (requestData.ContainsKey("objectID")) objectID = (string)requestData["objectID"];
+ if (requestData.ContainsKey("objectName")) objectName = (string)requestData["objectName"];
+ if (requestData.ContainsKey("regionHandle")) regionHandle = (string)requestData["regionHandle"];
+ if (requestData.ContainsKey("regionUUID")) regionUUID = (string)requestData["regionUUID"];
+ if (requestData.ContainsKey("transactionType")) transactionType = Convert.ToInt32(requestData["transactionType"]);
+ if (requestData.ContainsKey("description")) description = (string)requestData["description"];
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleTransaction: Transfering money from {0} to {1}, Amount = {2}", senderID, receiverID, amount);
+ m_log.InfoFormat("[MONEY XMLRPC]: handleTransaction: Object ID = {0}, Object Name = {1}", objectID, objectName);
+
+ if (m_sessionDic.ContainsKey(senderID) && m_secureSessionDic.ContainsKey(senderID))
+ {
+ if (m_sessionDic[senderID] == senderSessionID && m_secureSessionDic[senderID] == senderSecureSessionID)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleTransaction: Transfering money from {0} to {1}", senderID, receiverID);
+ int time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+ try
+ {
+ TransactionData transaction = new TransactionData();
+ transaction.TransUUID = transactionUUID;
+ transaction.Sender = senderID;
+ transaction.Receiver = receiverID;
+ transaction.Amount = amount;
+ transaction.ObjectUUID = objectID;
+ transaction.ObjectName = objectName;
+ transaction.RegionHandle = regionHandle;
+ transaction.RegionUUID = regionUUID;
+ transaction.Type = transactionType;
+ transaction.Time = time;
+ transaction.SecureCode = UUID.Random().ToString();
+ transaction.Status = (int)Status.PENDING_STATUS;
+ transaction.CommonName = GetSSLCommonName();
+ transaction.Description = description + " " + DateTime.UtcNow.ToString();
+
+ UserInfo rcvr = m_moneyDBService.FetchUserInfo(receiverID);
+ if (rcvr == null)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleTransaction: Receive User is not yet in DB {0}", receiverID);
+ return response;
+ }
+
+ bool result = m_moneyDBService.addTransaction(transaction);
+ if (result)
+ {
+ UserInfo user = m_moneyDBService.FetchUserInfo(senderID);
+ if (user != null)
+ {
+ if (amount > 0 || (m_enableAmountZero && amount == 0))
+ {
+ string snd_message = "";
+ string rcv_message = "";
+
+ if (transaction.Type == (int)TransactionType.Gift)
+ {
+ snd_message = m_BalanceMessageSendGift;
+ rcv_message = m_BalanceMessageReceiveGift;
+ }
+ else if (transaction.Type == (int)TransactionType.LandSale)
+ {
+ snd_message = m_BalanceMessageLandSale;
+ rcv_message = m_BalanceMessageRcvLandSale;
+ }
+ else if (transaction.Type == (int)TransactionType.PayObject)
+ {
+ snd_message = m_BalanceMessageBuyObject;
+ rcv_message = m_BalanceMessageSellObject;
+ }
+ else if (transaction.Type == (int)TransactionType.ObjectPays)
+ { // ObjectGiveMoney
+ rcv_message = m_BalanceMessageGetMoney;
+ }
+
+ responseData["success"] = NotifyTransfer(transactionUUID, snd_message, rcv_message, objectName);
+ }
+ else if (amount == 0)
+ {
+ responseData["success"] = true; // No messages for L$0 object. by Fumi.Iseki
+ }
+ return response;
+ }
+ }
+ else
+ { // add transaction failed
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleTransaction: Add transaction for user {0} failed.", senderID);
+ }
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleTransaction: Exception occurred while adding transaction: " + e.ToString());
+ }
+ return response;
+ }
+ }
+
+ m_log.Error("[MONEY XMLRPC]: handleTransaction: Session authentication failure for sender " + senderID);
+ responseData["message"] = "Session check failure, please re-login later!";
+ return response;
+ }
+
+ public XmlRpcResponse handleForceTransaction(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ int amount = 0;
+ int transactionType = 0;
+ string senderID = string.Empty;
+ string receiverID = string.Empty;
+ string objectID = string.Empty;
+ string objectName = string.Empty;
+ string regionHandle = string.Empty;
+ string regionUUID = string.Empty;
+ string description = "Newly added on";
+
+ responseData["success"] = false;
+ UUID transactionUUID = UUID.Random();
+
+ //
+ if (!m_forceTransfer)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleForceTransaction: Not allowed force transfer of Money.");
+ m_log.Error("[MONEY XMLRPC]: handleForceTransaction: Set enableForceTransfer at [MoneyServer] to true in MoneyServer.ini");
+ responseData["message"] = "not allowed force transfer of Money!";
+ return response;
+ }
+
+ if (requestData.ContainsKey("senderID")) senderID = (string)requestData["senderID"];
+ if (requestData.ContainsKey("receiverID")) receiverID = (string)requestData["receiverID"];
+ if (requestData.ContainsKey("amount")) amount = Convert.ToInt32(requestData["amount"]);
+ if (requestData.ContainsKey("objectID")) objectID = (string)requestData["objectID"];
+ if (requestData.ContainsKey("objectName")) objectName = (string)requestData["objectName"];
+ if (requestData.ContainsKey("regionHandle")) regionHandle = (string)requestData["regionHandle"];
+ if (requestData.ContainsKey("regionUUID")) regionUUID = (string)requestData["regionUUID"];
+ if (requestData.ContainsKey("transactionType")) transactionType = Convert.ToInt32(requestData["transactionType"]);
+ if (requestData.ContainsKey("description")) description = (string)requestData["description"];
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleForceTransaction: Force transfering money from {0} to {1}, Amount = {2}", senderID, receiverID, amount);
+ m_log.InfoFormat("[MONEY XMLRPC]: handleForceTransaction: Object ID = {0}, Object Name = {1}", objectID, objectName);
+
+ int time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+
+ try
+ {
+ TransactionData transaction = new TransactionData();
+ transaction.TransUUID = transactionUUID;
+ transaction.Sender = senderID;
+ transaction.Receiver = receiverID;
+ transaction.Amount = amount;
+ transaction.ObjectUUID = objectID;
+ transaction.ObjectName = objectName;
+ transaction.RegionHandle = regionHandle;
+ transaction.RegionUUID = regionUUID;
+ transaction.Type = transactionType;
+ transaction.Time = time;
+ transaction.SecureCode = UUID.Random().ToString();
+ transaction.Status = (int)Status.PENDING_STATUS;
+ transaction.CommonName = GetSSLCommonName();
+ transaction.Description = description + " " + DateTime.UtcNow.ToString();
+
+ UserInfo rcvr = m_moneyDBService.FetchUserInfo(receiverID);
+ if (rcvr == null)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleForceTransaction: Force receive User is not yet in DB {0}", receiverID);
+ return response;
+ }
+
+ bool result = m_moneyDBService.addTransaction(transaction);
+ if (result)
+ {
+ UserInfo user = m_moneyDBService.FetchUserInfo(senderID);
+ if (user != null)
+ {
+ if (amount > 0 || (m_enableAmountZero && amount == 0))
+ {
+ string snd_message = "";
+ string rcv_message = "";
+
+ if (transaction.Type == (int)TransactionType.Gift)
+ {
+ snd_message = m_BalanceMessageSendGift;
+ rcv_message = m_BalanceMessageReceiveGift;
+ }
+ else if (transaction.Type == (int)TransactionType.LandSale)
+ {
+ snd_message = m_BalanceMessageLandSale;
+ snd_message = m_BalanceMessageRcvLandSale;
+ }
+ else if (transaction.Type == (int)TransactionType.PayObject)
+ {
+ snd_message = m_BalanceMessageBuyObject;
+ rcv_message = m_BalanceMessageSellObject;
+ }
+ else if (transaction.Type == (int)TransactionType.ObjectPays)
+ { // ObjectGiveMoney
+ rcv_message = m_BalanceMessageGetMoney;
+ }
+
+ responseData["success"] = NotifyTransfer(transactionUUID, snd_message, rcv_message, objectName);
+ }
+ else if (amount == 0)
+ {
+ responseData["success"] = true; // No messages for L$0 object. by Fumi.Iseki
+ }
+ return response;
+ }
+ }
+ else
+ { // add transaction failed
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleForceTransaction: Add force transaction for user {0} failed.", senderID);
+ }
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleForceTransaction: Exception occurred while adding force transaction: " + e.ToString());
+ }
+ return response;
+ }
+
+ public XmlRpcResponse handleScriptTransaction(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleScriptTransaction:");
+
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ int amount = 0;
+ int transactionType = 0;
+ string senderID = UUID.Zero.ToString();
+ string receiverID = UUID.Zero.ToString();
+ string clientIP = remoteClient.Address.ToString();
+ string secretCode = string.Empty;
+ string description = "Scripted Send Money from/to Avatar on";
+
+ responseData["success"] = false;
+ UUID transactionUUID = UUID.Random();
+
+ if (!m_scriptSendMoney || m_scriptAccessKey == "")
+ {
+ m_log.Error("[MONEY XMLRPC]: handleScriptTransaction: Not allowed send money to avatar!!");
+ m_log.Error("[MONEY XMLRPC]: handleScriptTransaction: Set enableScriptSendMoney and MoneyScriptAccessKey at [MoneyServer] in MoneyServer.ini");
+ responseData["message"] = "not allowed set money to avatar!";
+ return response;
+ }
+
+ if (requestData.ContainsKey("senderID")) senderID = (string)requestData["senderID"];
+ if (requestData.ContainsKey("receiverID")) receiverID = (string)requestData["receiverID"];
+ if (requestData.ContainsKey("amount")) amount = Convert.ToInt32(requestData["amount"]);
+ if (requestData.ContainsKey("transactionType")) transactionType = Convert.ToInt32(requestData["transactionType"]);
+ if (requestData.ContainsKey("description")) description = (string)requestData["description"];
+ if (requestData.ContainsKey("secretAccessCode")) secretCode = (string)requestData["secretAccessCode"];
+
+ MD5 md5 = MD5.Create();
+ byte[] code = md5.ComputeHash(ASCIIEncoding.Default.GetBytes(m_scriptAccessKey + "_" + clientIP));
+ string hash = BitConverter.ToString(code).ToLower().Replace("-", "");
+ code = md5.ComputeHash(ASCIIEncoding.Default.GetBytes(hash + "_" + m_scriptIPaddress));
+ hash = BitConverter.ToString(code).ToLower().Replace("-", "");
+
+ if (secretCode.ToLower() != hash)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleScriptTransaction: Not allowed send money to avatar!!");
+ m_log.Error("[MONEY XMLRPC]: handleScriptTransaction: Not match Script Access Key.");
+ responseData["message"] = "not allowed send money to avatar! not match Script Key";
+ return response;
+ }
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleScriptTransaction: Send money from {0} to {1}", senderID, receiverID);
+ int time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+
+ try
+ {
+ TransactionData transaction = new TransactionData();
+ transaction.TransUUID = transactionUUID;
+ transaction.Sender = senderID;
+ transaction.Receiver = receiverID;
+ transaction.Amount = amount;
+ transaction.ObjectUUID = UUID.Zero.ToString();
+ transaction.RegionHandle = "0";
+ transaction.Type = transactionType;
+ transaction.Time = time;
+ transaction.SecureCode = UUID.Random().ToString();
+ transaction.Status = (int)Status.PENDING_STATUS;
+ transaction.CommonName = GetSSLCommonName();
+ transaction.Description = description + " " + DateTime.UtcNow.ToString();
+
+ UserInfo senderInfo = null;
+ UserInfo receiverInfo = null;
+ if (transaction.Sender != UUID.Zero.ToString()) senderInfo = m_moneyDBService.FetchUserInfo(transaction.Sender);
+ if (transaction.Receiver != UUID.Zero.ToString()) receiverInfo = m_moneyDBService.FetchUserInfo(transaction.Receiver);
+
+ if (senderInfo == null && receiverInfo == null)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleScriptTransaction: Sender and Receiver are not yet in DB, or both of them are System: {0}, {1}",
+ transaction.Sender, transaction.Receiver);
+ return response;
+ }
+
+ bool result = m_moneyDBService.addTransaction(transaction);
+ if (result)
+ {
+ if (amount > 0 || (m_enableAmountZero && amount == 0))
+ {
+ if (m_moneyDBService.DoTransfer(transactionUUID))
+ {
+ transaction = m_moneyDBService.FetchTransaction(transactionUUID);
+ if (transaction != null && transaction.Status == (int)Status.SUCCESS_STATUS)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleScriptTransaction: ScriptTransaction money finished successfully, now update balance {0}",
+ transactionUUID.ToString());
+ string message = string.Empty;
+ if (senderInfo != null)
+ {
+ if (receiverInfo == null) message = string.Format(m_BalanceMessageSendMoney, amount, "SYSTEM", "");
+ else message = string.Format(m_BalanceMessageSendMoney, amount, receiverInfo.Avatar, "");
+ UpdateBalance(transaction.Sender, message);
+ m_log.InfoFormat("[MONEY XMLRPC]: handleScriptTransaction: Update balance of {0}. Message = {1}", transaction.Sender, message);
+ }
+ if (receiverInfo != null)
+ {
+ if (senderInfo == null) message = string.Format(m_BalanceMessageReceiveMoney, amount, "SYSTEM", "");
+ else message = string.Format(m_BalanceMessageReceiveMoney, amount, senderInfo.Avatar, "");
+ UpdateBalance(transaction.Receiver, message);
+ m_log.InfoFormat("[MONEY XMLRPC]: handleScriptTransaction: Update balance of {0}. Message = {1}", transaction.Receiver, message);
+ }
+
+ responseData["success"] = true;
+ }
+ }
+ }
+ else if (amount == 0)
+ {
+ responseData["success"] = true; // No messages for L$0 add
+ }
+ return response;
+ }
+ else
+ { // add transaction failed
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleScriptTransaction: Add force transaction for user {0} failed.", transaction.Sender);
+ }
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleScriptTransaction: Exception occurred while adding money transaction: " + e.ToString());
+ }
+ return response;
+ }
+
+ public XmlRpcResponse handleAddBankerMoney(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ int amount = 0;
+ int transactionType = 0;
+ string senderID = UUID.Zero.ToString();
+ string bankerID = string.Empty;
+ string regionHandle = "0";
+ string regionUUID = UUID.Zero.ToString();
+ string description = "Add Money to Avatar on";
+
+ responseData["success"] = false;
+ UUID transactionUUID = UUID.Random();
+
+ if (requestData.ContainsKey("bankerID")) bankerID = (string)requestData["bankerID"];
+ if (requestData.ContainsKey("amount")) amount = Convert.ToInt32(requestData["amount"]);
+ if (requestData.ContainsKey("regionHandle")) regionHandle = (string)requestData["regionHandle"];
+ if (requestData.ContainsKey("regionUUID")) regionUUID = (string)requestData["regionUUID"];
+ if (requestData.ContainsKey("transactionType")) transactionType = Convert.ToInt32(requestData["transactionType"]);
+ if (requestData.ContainsKey("description")) description = (string)requestData["description"];
+
+ // Check Banker Avatar
+ if (m_bankerAvatar != UUID.Zero.ToString() && m_bankerAvatar != bankerID)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleAddBankerMoney: Not allowed add money to avatar!!");
+ m_log.Error("[MONEY XMLRPC]: handleAddBankerMoney: Set BankerAvatar at [MoneyServer] in MoneyServer.ini");
+ responseData["message"] = "not allowed add money to avatar!";
+ responseData["banker"] = false;
+ return response;
+ }
+ responseData["banker"] = true;
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleAddBankerMoney: Add money to avatar {0}", bankerID);
+ int time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+
+ try
+ {
+ TransactionData transaction = new TransactionData();
+ transaction.TransUUID = transactionUUID;
+ transaction.Sender = senderID;
+ transaction.Receiver = bankerID;
+ transaction.Amount = amount;
+ transaction.ObjectUUID = UUID.Zero.ToString();
+ transaction.RegionHandle = regionHandle;
+ transaction.RegionUUID = regionUUID;
+ transaction.Type = transactionType;
+ transaction.Time = time;
+ transaction.SecureCode = UUID.Random().ToString();
+ transaction.Status = (int)Status.PENDING_STATUS;
+ transaction.CommonName = GetSSLCommonName();
+ transaction.Description = description + " " + DateTime.UtcNow.ToString();
+
+ UserInfo rcvr = m_moneyDBService.FetchUserInfo(bankerID);
+ if (rcvr == null)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleAddBankerMoney: Avatar is not yet in DB {0}", bankerID);
+ return response;
+ }
+
+ bool result = m_moneyDBService.addTransaction(transaction);
+ if (result)
+ {
+ if (amount > 0 || (m_enableAmountZero && amount == 0))
+ {
+ if (m_moneyDBService.DoAddMoney(transactionUUID))
+ {
+ transaction = m_moneyDBService.FetchTransaction(transactionUUID);
+ if (transaction != null && transaction.Status == (int)Status.SUCCESS_STATUS)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleAddBankerMoney: Adding money finished successfully, now update balance: {0}",
+ transactionUUID.ToString());
+ string message = string.Format(m_BalanceMessageBuyMoney, amount, "SYSTEM", "");
+ UpdateBalance(transaction.Receiver, message);
+ responseData["success"] = true;
+ }
+ }
+ }
+ else if (amount == 0)
+ {
+ responseData["success"] = true; // No messages for L$0 add
+ }
+ return response;
+ }
+ else
+ { // add transaction failed
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleAddBankerMoney: Add force transaction for user {0} failed.", senderID);
+ }
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY XMLRPC]: handleAddBankerMoney: Exception occurred while adding money transaction: " + e.ToString());
+ }
+ return response;
+ }
+
+ public XmlRpcResponse handlePayMoneyCharge(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handlePayMoneyCharge now.");
+
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ int amount = 0;
+ int transactionType = 0;
+ string senderID = string.Empty;
+ string receiverID = UUID.Zero.ToString();
+ string senderSessionID = string.Empty;
+ string senderSecureSessionID = string.Empty;
+ string objectID = UUID.Zero.ToString();
+ string objectName = string.Empty;
+ string regionHandle = string.Empty;
+ string regionUUID = string.Empty;
+ string description = "Pay Charge on";
+
+ responseData["success"] = false;
+ UUID transactionUUID = UUID.Random();
+
+ // Parameter aus der Anfrage extrahieren
+ if (requestData.ContainsKey("senderID")) senderID = (string)requestData["senderID"];
+ if (requestData.ContainsKey("senderSessionID")) senderSessionID = (string)requestData["senderSessionID"];
+ if (requestData.ContainsKey("senderSecureSessionID")) senderSecureSessionID = (string)requestData["senderSecureSessionID"];
+ if (requestData.ContainsKey("amount")) amount = Convert.ToInt32(requestData["amount"]);
+ if (requestData.ContainsKey("regionHandle")) regionHandle = (string)requestData["regionHandle"];
+ if (requestData.ContainsKey("regionUUID")) regionUUID = (string)requestData["regionUUID"];
+ if (requestData.ContainsKey("transactionType")) transactionType = Convert.ToInt32(requestData["transactionType"]);
+ if (requestData.ContainsKey("description")) description = (string)requestData["description"];
+ if (requestData.ContainsKey("receiverID")) receiverID = (string)requestData["receiverID"];
+ if (requestData.ContainsKey("objectID")) objectID = (string)requestData["objectID"];
+ if (requestData.ContainsKey("objectName")) objectName = (string)requestData["objectName"];
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handlePayMoneyCharge: Transfering money from {0} to {1}, Amount = {2}", senderID, receiverID, amount);
+
+ // Sitzungsprüfung überspringen für SYSTEM oder Banker
+ if (senderID == m_bankerAvatar || senderID == "SYSTEM")
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handlePayMoneyCharge: Sender ist SYSTEM oder BankerAvatar. Sitzungsprüfung wird übersprungen.");
+ }
+ else if (m_sessionDic.ContainsKey(senderID) && m_secureSessionDic.ContainsKey(senderID))
+ {
+ if (m_sessionDic[senderID] != senderSessionID || m_secureSessionDic[senderID] != senderSecureSessionID)
+ {
+ m_log.Error("[MONEY XMLRPC]: handlePayMoneyCharge: Sitzungsprüfung für Sender fehlgeschlagen " + senderID);
+ responseData["message"] = "Session check failure, please re-login later!";
+ return response;
+ }
+ }
+ else
+ {
+ m_log.Error("[MONEY XMLRPC]: handlePayMoneyCharge: Sitzungsprüfung für Sender fehlgeschlagen " + senderID);
+ responseData["message"] = "Session check failure, please re-login later!";
+ return response;
+ }
+
+ int time = (int)((DateTime.UtcNow.Ticks - TicksToEpoch) / 10000000);
+
+ try
+ {
+ TransactionData transaction = new TransactionData
+ {
+ TransUUID = transactionUUID,
+ Sender = senderID,
+ Receiver = receiverID,
+ Amount = amount,
+ ObjectUUID = objectID,
+ ObjectName = objectName,
+ RegionHandle = regionHandle,
+ RegionUUID = regionUUID,
+ Type = transactionType,
+ Time = time,
+ SecureCode = UUID.Random().ToString(),
+ Status = (int)Status.PENDING_STATUS,
+ CommonName = GetSSLCommonName(),
+ Description = description + " " + DateTime.UtcNow.ToString()
+ };
+
+ bool result = m_moneyDBService.addTransaction(transaction);
+ if (result)
+ {
+ UserInfo user = m_moneyDBService.FetchUserInfo(senderID);
+ if (user != null)
+ {
+ if (amount == 0)
+ {
+ // Für L$0 keine Transferaktion, einfach Erfolg zurückgeben
+ responseData["success"] = true;
+ return response;
+ }
+
+ if (amount > 0 || (m_enableAmountZero && amount == 0))
+ {
+ if (!NotifyTransfer(transactionUUID, "", "", ""))
+ {
+ m_log.Error("[MONEY XMLRPC]: handlePayMoneyCharge: Gutschrift fehlgeschlagen, versuche manuell Geld hinzuzufügen.");
+
+ Hashtable addMoneyParams = new Hashtable();
+ addMoneyParams["bankerID"] = "SYSTEM";
+ addMoneyParams["amount"] = amount;
+ addMoneyParams["regionHandle"] = regionHandle;
+ addMoneyParams["regionUUID"] = regionUUID;
+ addMoneyParams["transactionType"] = transactionType;
+ addMoneyParams["description"] = "Manuelle Gutschrift nach Fehlermeldung";
+
+ XmlRpcResponse addMoneyResponse = handleAddBankerMoney(
+ new XmlRpcRequest("AddBankerMoney", new object[] { addMoneyParams }),
+ remoteClient
+ );
+
+ Hashtable responseValue = addMoneyResponse.Value as Hashtable;
+ bool addMoneySuccess = addMoneyResponse != null
+ && responseValue != null
+ && responseValue.ContainsKey("success")
+ && (bool)responseValue["success"];
+
+ responseData["success"] = addMoneySuccess;
+
+ if (!addMoneySuccess)
+ {
+ responseData["message"] = "Manuelles Hinzufügen des Geldes fehlgeschlagen.";
+ }
+
+ return response;
+ }
+
+ // Wenn NotifyTransfer erfolgreich war
+ responseData["success"] = true;
+ return response;
+ }
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handlePayMoneyCharge: Zahlungstransaktion für Benutzer {0} fehlgeschlagen.", senderID);
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[MONEY XMLRPC]: handlePayMoneyCharge: Ausnahme bei der Zahlungstransaktion: " + e.ToString());
+ }
+
+ return response;
+ }
+
+ public XmlRpcResponse handleCancelTransfer(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string secureCode = string.Empty;
+ string transactionID = string.Empty;
+ UUID transactionUUID = UUID.Zero;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("secureCode")) secureCode = (string)requestData["secureCode"];
+ if (requestData.ContainsKey("transactionID"))
+ {
+ transactionID = (string)requestData["transactionID"];
+ UUID.TryParse(transactionID, out transactionUUID);
+ }
+
+ if (string.IsNullOrEmpty(secureCode) || string.IsNullOrEmpty(transactionID))
+ {
+ m_log.Error("[MONEY XMLRPC]: handleCancelTransfer: secureCode and/or transactionID are empty.");
+ return response;
+ }
+
+ TransactionData transaction = m_moneyDBService.FetchTransaction(transactionUUID);
+ UserInfo user = m_moneyDBService.FetchUserInfo(transaction.Sender);
+
+ try
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleCancelTransfer: User {0} wanted to cancel the transaction.", user.Avatar);
+ if (m_moneyDBService.ValidateTransfer(secureCode, transactionUUID))
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleCancelTransfer: User {0} has canceled the transaction {1}", user.Avatar, transactionID);
+ m_moneyDBService.updateTransactionStatus(transactionUUID, (int)Status.FAILED_STATUS,
+ "User canceled the transaction on " + DateTime.UtcNow.ToString());
+ responseData["success"] = true;
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleCancelTransfer: Exception occurred when transaction {0}: {1}", transactionID, e.ToString());
+ }
+ return response;
+ }
+
+ public XmlRpcResponse handleGetTransaction(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string clientID = string.Empty;
+ string sessionID = string.Empty;
+ string secureID = string.Empty;
+ string transactionID = string.Empty;
+ UUID transactionUUID = UUID.Zero;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("clientUUID")) clientID = (string)requestData["clientUUID"];
+ if (requestData.ContainsKey("clientSessionID")) sessionID = (string)requestData["clientSessionID"];
+ if (requestData.ContainsKey("clientSecureSessionID")) secureID = (string)requestData["clientSecureSessionID"];
+
+ if (requestData.ContainsKey("transactionID"))
+ {
+ transactionID = (string)requestData["transactionID"];
+ UUID.TryParse(transactionID, out transactionUUID);
+ }
+
+ if (m_sessionDic.ContainsKey(clientID) && m_secureSessionDic.ContainsKey(clientID))
+ {
+ if (m_sessionDic[clientID] == sessionID && m_secureSessionDic[clientID] == secureID)
+ {
+ //
+ if (string.IsNullOrEmpty(transactionID))
+ {
+ responseData["description"] = "TransactionID is empty";
+ m_log.Error("[MONEY XMLRPC]: handleGetTransaction: TransactionID is empty.");
+ return response;
+ }
+
+ try
+ {
+ TransactionData transaction = m_moneyDBService.FetchTransaction(transactionUUID);
+ if (transaction != null)
+ {
+ responseData["success"] = true;
+ responseData["amount"] = transaction.Amount;
+ responseData["time"] = transaction.Time;
+ responseData["type"] = transaction.Type;
+ responseData["sender"] = transaction.Sender.ToString();
+ responseData["receiver"] = transaction.Receiver.ToString();
+ responseData["description"] = transaction.Description;
+ }
+ else
+ {
+ responseData["description"] = "Invalid Transaction UUID";
+ }
+
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleGetTransaction: {0}", e.ToString());
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleGetTransaction: Can't get transaction information for {0}", transactionUUID.ToString());
+ }
+ return response;
+ }
+ }
+
+ responseData["success"] = false;
+ responseData["description"] = "Session check failure, please re-login";
+ return response;
+ }
+
+ public XmlRpcResponse handleWebLogin(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string userID = string.Empty;
+ string webSessionID = string.Empty;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("userID")) userID = (string)requestData["userID"];
+ if (requestData.ContainsKey("sessionID")) webSessionID = (string)requestData["sessionID"];
+
+ if (string.IsNullOrEmpty(userID) || string.IsNullOrEmpty(webSessionID))
+ {
+ responseData["errorMessage"] = "userID or sessionID can`t be empty, login failed!";
+ return response;
+ }
+
+ //Update the web session dictionary
+ lock (m_webSessionDic)
+ {
+ if (!m_webSessionDic.ContainsKey(userID))
+ {
+ m_webSessionDic.Add(userID, webSessionID);
+ }
+ else m_webSessionDic[userID] = webSessionID;
+ }
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleWebLogin: User {0} has logged in from web.", userID);
+ responseData["success"] = true;
+ return response;
+ }
+
+ public XmlRpcResponse handleWebLogout(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string userID = string.Empty;
+ string webSessionID = string.Empty;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("userID")) userID = (string)requestData["userID"];
+ if (requestData.ContainsKey("sessionID")) webSessionID = (string)requestData["sessionID"];
+
+ if (string.IsNullOrEmpty(userID) || string.IsNullOrEmpty(webSessionID))
+ {
+ responseData["errorMessage"] = "userID or sessionID can`t be empty, log out failed!";
+ return response;
+ }
+
+ //Update the web session dictionary
+ lock (m_webSessionDic)
+ {
+ if (m_webSessionDic.ContainsKey(userID))
+ {
+ m_webSessionDic.Remove(userID);
+ }
+ }
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleWebLogout: User {0} has logged out from web.", userID);
+ responseData["success"] = true;
+ return response;
+ }
+
+ public XmlRpcResponse handleWebGetBalance(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string userID = string.Empty;
+ string webSessionID = string.Empty;
+ int balance = 0;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("userID")) userID = (string)requestData["userID"];
+ if (requestData.ContainsKey("sessionID")) webSessionID = (string)requestData["sessionID"];
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleWebGetBalance: Getting balance for user {0}", userID);
+
+ //perform session check
+ if (m_webSessionDic.ContainsKey(userID))
+ {
+ if (m_webSessionDic[userID] == webSessionID)
+ {
+ try
+ {
+ balance = m_moneyDBService.getBalance(userID);
+ UserInfo user = m_moneyDBService.FetchUserInfo(userID);
+ if (user != null)
+ {
+ responseData["userName"] = user.Avatar;
+ }
+ else
+ {
+ responseData["userName"] = "unknown user";
+ }
+ // User not found
+ if (balance == -1)
+ {
+ responseData["errorMessage"] = "User not found";
+ responseData["balance"] = 0;
+ }
+ else if (balance >= 0)
+ {
+ responseData["success"] = true;
+ responseData["balance"] = balance;
+ }
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleWebGetBalance: Can't get balance for user {0}, Exception {1}", userID, e.ToString());
+ responseData["errorMessage"] = "Exception occurred when getting balance";
+ return response;
+ }
+ }
+ }
+
+ m_log.Error("[MONEY XMLRPC]: handleWebLogout: Session authentication failed when getting balance for user " + userID);
+ responseData["errorMessage"] = "Session check failure, please re-login";
+ return response;
+ }
+
+ public XmlRpcResponse handleWebGetTransaction(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string userID = string.Empty;
+ string webSessionID = string.Empty;
+ int lastIndex = -1;
+ int startTime = 0;
+ int endTime = 0;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("userID")) userID = (string)requestData["userID"];
+ if (requestData.ContainsKey("sessionID")) webSessionID = (string)requestData["sessionID"];
+ if (requestData.ContainsKey("startTime")) startTime = (int)requestData["startTime"];
+ if (requestData.ContainsKey("endTime")) endTime = (int)requestData["endTime"];
+ if (requestData.ContainsKey("lastIndex")) lastIndex = (int)requestData["lastIndex"];
+
+ if (m_webSessionDic.ContainsKey(userID))
+ {
+ if (m_webSessionDic[userID] == webSessionID)
+ {
+ try
+ {
+ int total = m_moneyDBService.getTransactionNum(userID, startTime, endTime);
+ TransactionData tran = null;
+ m_log.InfoFormat("[MONEY XMLRPC]: handleWebGetTransaction: Getting transation[{0}] for user {1}", lastIndex + 1, userID);
+ if (total > lastIndex + 2)
+ {
+ responseData["isEnd"] = false;
+ }
+ else
+ {
+ responseData["isEnd"] = true;
+ }
+
+ tran = m_moneyDBService.FetchTransaction(userID, startTime, endTime, lastIndex);
+ if (tran != null)
+ {
+ UserInfo senderInfo = m_moneyDBService.FetchUserInfo(tran.Sender);
+ UserInfo receiverInfo = m_moneyDBService.FetchUserInfo(tran.Receiver);
+ if (senderInfo != null && receiverInfo != null)
+ {
+ responseData["senderName"] = senderInfo.Avatar;
+ responseData["receiverName"] = receiverInfo.Avatar;
+ }
+ else
+ {
+ responseData["senderName"] = "unknown user";
+ responseData["receiverName"] = "unknown user";
+ }
+ responseData["success"] = true;
+ responseData["transactionIndex"] = lastIndex + 1;
+ responseData["transactionUUID"] = tran.TransUUID.ToString();
+ responseData["senderID"] = tran.Sender;
+ responseData["receiverID"] = tran.Receiver;
+ responseData["amount"] = tran.Amount;
+ responseData["type"] = tran.Type;
+ responseData["time"] = tran.Time;
+ responseData["status"] = tran.Status;
+ responseData["description"] = tran.Description;
+ }
+ else
+ {
+ responseData["errorMessage"] = string.Format("Unable to fetch transaction data with the index {0}", lastIndex + 1);
+ }
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleWebGetTransaction: Can't get transaction for user {0}, Exception {1}", userID, e.ToString());
+ responseData["errorMessage"] = "Exception occurred when getting transaction";
+ return response;
+ }
+ }
+ }
+
+ m_log.Error("[MONEY XMLRPC]: handleWebGetTransaction: Session authentication failed when getting transaction for user " + userID);
+ responseData["errorMessage"] = "Session check failure, please re-login";
+ return response;
+ }
+
+ public XmlRpcResponse handleWebGetTransactionNum(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string userID = string.Empty;
+ string webSessionID = string.Empty;
+ int startTime = 0;
+ int endTime = 0;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("userID")) userID = (string)requestData["userID"];
+ if (requestData.ContainsKey("sessionID")) webSessionID = (string)requestData["sessionID"];
+ if (requestData.ContainsKey("startTime")) startTime = (int)requestData["startTime"];
+ if (requestData.ContainsKey("endTime")) endTime = (int)requestData["endTime"];
+
+ if (m_webSessionDic.ContainsKey(userID))
+ {
+ if (m_webSessionDic[userID] == webSessionID)
+ {
+ int it = m_moneyDBService.getTransactionNum(userID, startTime, endTime);
+ if (it >= 0)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: handleWebGetTransactionNum: Get {0} transactions for user {1}", it, userID);
+ responseData["success"] = true;
+ responseData["number"] = it;
+ }
+ return response;
+ }
+ }
+
+ m_log.Error("[MONEY XMLRPC]: handleWebGetTransactionNum: Session authentication failed when getting transaction number for user " + userID);
+ responseData["errorMessage"] = "Session check failure, please re-login";
+ return response;
+ }
+
+
+ #endregion
+ // ################## helper ##################
+ #region helper
+
+
+ //Spezifische Handler OnMoneyTransferedHandler: Protokolliert Details zu einer Geldüberweisung.
+ public XmlRpcResponse OnMoneyTransferedHandler(XmlRpcRequest request, IPEndPoint client)
+ {
+ if (request == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: OnMoneyTransferedHandler: request is null.");
+ return new XmlRpcResponse();
+ }
+
+ if (client == null)
+ {
+ m_log.Error("[MONEY XMLRPC]: OnMoneyTransferedHandler: client is null.");
+ return new XmlRpcResponse();
+ }
+
+ try
+ {
+ Hashtable requestData = (Hashtable)request.Params[0];
+ string transactionID = (string)requestData["transactionID"];
+ UUID transactionUUID = UUID.Zero;
+ UUID.TryParse(transactionID, out transactionUUID);
+
+ TransactionData transaction = m_moneyDBService.FetchTransaction(transactionUUID);
+ UserInfo user = m_moneyDBService.FetchUserInfo(transaction.Sender);
+
+ m_log.InfoFormat("[MONEY XMLRPC]: OnMoneyTransferedHandler: Transaction {0} from user {1} to user {2} for {3} units",
+ transactionID, user.Avatar, transaction.Receiver, transaction.Amount);
+
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ responseData.Add("success", true);
+ response.Value = responseData;
+ return response;
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: OnMoneyTransferedHandler: Exception occurred: {0}", ex.Message);
+ return new XmlRpcResponse();
+ }
+ }
+
+ public string GetSSLCommonName(XmlRpcRequest request)
+ {
+ if (request.Params.Count > 5)
+ {
+ m_sslCommonName = (string)request.Params[5];
+ }
+ else if (request.Params.Count == 5)
+ {
+ m_sslCommonName = (string)request.Params[4];
+ if (m_sslCommonName == "gridproxy") m_sslCommonName = "";
+ }
+ else
+ {
+ m_sslCommonName = "";
+ }
+ return m_sslCommonName;
+ }
+
+ /// Gets the name of the SSL common.
+ public string GetSSLCommonName()
+ {
+ return m_sslCommonName;
+ }
+
+ public bool ObjectGiveMoney(UUID objectID, UUID fromID, UUID toID, int amount, UUID txn, out string result)
+ {
+ result = String.Empty;
+ string description = String.Format("Object {0} pays {1}", resolveObjectName(objectID), resolveAgentName(toID));
+
+ bool give_result = doMoneyTransfer(fromID, toID, amount, 2, description);
+
+
+ BalanceUpdate(fromID, toID, give_result, description);
+
+ return give_result;
+ }
+
+ private string resolveObjectName(UUID objectID)
+ {
+ SceneObjectPart part = findPrim(objectID);
+ if (part != null)
+ {
+ return part.Name;
+ }
+ return String.Empty;
+ }
+
+ private string resolveAgentName(UUID agentID)
+ {
+ // try avatar username surname
+ Scene scene = GetRandomScene();
+ UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, agentID);
+ if (account != null)
+ {
+ string avatarname = account.FirstName + " " + account.LastName;
+ return avatarname;
+ }
+ else
+ {
+ m_log.ErrorFormat(
+ "[MONEY]: Could not resolve user {0}",
+ agentID);
+ }
+
+ return String.Empty;
+ }
+
+ ///
+ /// Utility function Gets a Random scene in the instance. For when which scene exactly you're doing something with doesn't matter
+ ///
+ ///
+ public Scene GetRandomScene()
+ {
+ lock (m_scenes)
+ {
+ foreach (Scene rs in m_scenes.Values)
+ return rs;
+ }
+ return null;
+ }
+
+ private SceneObjectPart findPrim(UUID objectID)
+ {
+ lock (m_scenes)
+ {
+ foreach (Scene s in m_scenes.Values)
+ {
+ SceneObjectPart part = s.GetSceneObjectPart(objectID);
+ if (part != null)
+ {
+ return part;
+ }
+ }
+ }
+ return null;
+ }
+
+ ///
+ /// Transfer money
+ ///
+ ///
+ ///
+ ///
+ ///
+ private bool doMoneyTransfer(UUID Sender, UUID Receiver, int amount, int transactiontype, string description)
+ {
+ return true;
+ }
+
+ private void BalanceUpdate(UUID senderID, UUID receiverID, bool transactionresult, string description)
+ {
+ IClientAPI sender = LocateClientObject(senderID);
+ IClientAPI receiver = LocateClientObject(receiverID);
+
+ if (senderID != receiverID)
+ {
+ if (sender != null)
+ {
+ sender.SendMoneyBalance(UUID.Random(), transactionresult, Utils.StringToBytes(description), GetFundsForAgentID(senderID), 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty);
+ }
+
+ if (receiver != null)
+ {
+ receiver.SendMoneyBalance(UUID.Random(), transactionresult, Utils.StringToBytes(description), GetFundsForAgentID(receiverID), 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty);
+ }
+ }
+ }
+
+ ///
+ /// Sends the the stored money balance to the client
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SendMoneyBalance(IClientAPI client, UUID agentID, UUID SessionID, UUID TransactionID)
+ {
+ if (client.AgentId == agentID && client.SessionId == SessionID)
+ {
+ int returnfunds = 0;
+
+ try
+ {
+ returnfunds = GetFundsForAgentID(agentID);
+ }
+ catch (Exception e)
+ {
+ client.SendAlertMessage(e.Message + " ");
+ }
+
+ client.SendMoneyBalance(TransactionID, true, Array.Empty(), returnfunds, 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty);
+ }
+ else
+ {
+ client.SendAlertMessage("Unable to send your money balance to you!");
+ }
+ }
+
+ private int GetFundsForAgentID(UUID AgentID)
+ {
+ int returnfunds = 0;
+
+ return returnfunds;
+ }
+
+ ///
+ /// Locates a IClientAPI for the client specified
+ ///
+ ///
+ ///
+ private IClientAPI LocateClientObject(UUID AgentID)
+ {
+ ScenePresence tPresence;
+ lock (m_scenes)
+ {
+ foreach (Scene _scene in m_scenes.Values)
+ {
+ tPresence = _scene.GetScenePresence(AgentID);
+ if (tPresence != null && !tPresence.IsDeleted && !tPresence.IsChildAgent)
+ return tPresence.ControllingClient;
+ }
+ }
+ return null;
+ }
+
+ public bool NotifyTransfer(UUID transactionUUID, string msg2sender, string msg2receiver, string objectName)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: NotifyTransfer: User has accepted the transaction, now continue with the transaction");
+
+ try
+ {
+ if (m_moneyDBService.DoTransfer(transactionUUID))
+ {
+ TransactionData transaction = m_moneyDBService.FetchTransaction(transactionUUID);
+ if (transaction != null && transaction.Status == (int)Status.SUCCESS_STATUS)
+ {
+ //m_log.InfoFormat("[MONEY XMLRPC]: NotifyTransfer: Transaction Type = {0}", transaction.Type);
+ //m_log.InfoFormat("[MONEY XMLRPC]: NotifyTransfer: Payment finished successfully, now update balance {0}", transactionUUID.ToString());
+
+ bool updateSender = true;
+ bool updateReceiv = true;
+ if (transaction.Sender == transaction.Receiver) updateSender = false;
+ if (transaction.Type == (int)TransactionType.UploadCharge) updateReceiv = false;
+
+ if (updateSender)
+ {
+ UserInfo receiverInfo = m_moneyDBService.FetchUserInfo(transaction.Receiver);
+ string receiverName = "unknown user";
+ if (receiverInfo != null) receiverName = receiverInfo.Avatar;
+ string snd_message = string.Format(msg2sender, transaction.Amount, receiverName, objectName);
+ UpdateBalance(transaction.Sender, snd_message);
+ }
+ if (updateReceiv)
+ {
+ UserInfo senderInfo = m_moneyDBService.FetchUserInfo(transaction.Sender);
+ string senderName = "unknown user";
+ if (senderInfo != null) senderName = senderInfo.Avatar;
+ string rcv_message = string.Format(msg2receiver, transaction.Amount, senderName, objectName);
+ UpdateBalance(transaction.Receiver, rcv_message);
+ }
+
+ // Notify to sender
+ if (transaction.Type == (int)TransactionType.PayObject)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: NotifyTransfer: Now notify opensim to give object to customer {0} ", transaction.Sender);
+ Hashtable requestTable = new Hashtable();
+ requestTable["clientUUID"] = transaction.Sender;
+ requestTable["receiverUUID"] = transaction.Receiver;
+
+ if (m_sessionDic.ContainsKey(transaction.Sender) && m_secureSessionDic.ContainsKey(transaction.Sender))
+ {
+ requestTable["clientSessionID"] = m_sessionDic[transaction.Sender];
+ requestTable["clientSecureSessionID"] = m_secureSessionDic[transaction.Sender];
+ }
+ else
+ {
+ requestTable["clientSessionID"] = UUID.Zero.ToString();
+ requestTable["clientSecureSessionID"] = UUID.Zero.ToString();
+ }
+ requestTable["transactionType"] = transaction.Type;
+ requestTable["amount"] = transaction.Amount;
+ requestTable["objectID"] = transaction.ObjectUUID;
+ requestTable["objectName"] = transaction.ObjectName;
+ requestTable["regionHandle"] = transaction.RegionHandle;
+
+ UserInfo user = m_moneyDBService.FetchUserInfo(transaction.Sender);
+ if (user != null)
+ {
+ Hashtable responseTable = genericCurrencyXMLRPCRequest(requestTable, "OnMoneyTransfered", user.SimIP);
+
+ if (responseTable != null && responseTable.ContainsKey("success"))
+ {
+ //User not online or failed to get object ?
+ if (!(bool)responseTable["success"])
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: NotifyTransfer: User {0} can't get the object, rolling back.", transaction.Sender);
+ if (RollBackTransaction(transaction))
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: NotifyTransfer: Transaction {0} failed but roll back succeeded.", transactionUUID.ToString());
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: NotifyTransfer: Transaction {0} failed and roll back failed as well.",
+ transactionUUID.ToString());
+ }
+ }
+ else
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: NotifyTransfer: Transaction {0} finished successfully.", transactionUUID.ToString());
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+ }
+ m_log.ErrorFormat("[MONEY XMLRPC]: NotifyTransfer: Transaction {0} failed.", transactionUUID.ToString());
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: NotifyTransfer: exception occurred when transaction {0}: {1}", transactionUUID.ToString(), e.ToString());
+ }
+
+ return false;
+ }
+
+ public XmlRpcResponse handleGetBalance(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ GetSSLCommonName(request);
+
+ Hashtable requestData = (Hashtable)request.Params[0];
+ XmlRpcResponse response = new XmlRpcResponse();
+ Hashtable responseData = new Hashtable();
+ response.Value = responseData;
+
+ string clientUUID = string.Empty;
+ string sessionID = string.Empty;
+ string secureID = string.Empty;
+ int balance;
+
+ responseData["success"] = false;
+
+ if (requestData.ContainsKey("clientUUID")) clientUUID = (string)requestData["clientUUID"];
+ if (requestData.ContainsKey("clientSessionID")) sessionID = (string)requestData["clientSessionID"];
+ if (requestData.ContainsKey("clientSecureSessionID")) secureID = (string)requestData["clientSecureSessionID"];
+
+ m_log.InfoFormat("[MONEY XMLRPC]: handleGetBalance: Getting balance for user {0}", clientUUID);
+
+ if (m_sessionDic.ContainsKey(clientUUID) && m_secureSessionDic.ContainsKey(clientUUID))
+ {
+ if (m_sessionDic[clientUUID] == sessionID && m_secureSessionDic[clientUUID] == secureID)
+ {
+ try
+ {
+ balance = m_moneyDBService.getBalance(clientUUID);
+ if (balance == -1) // User not found
+ {
+ responseData["description"] = "user not found";
+ responseData["clientBalance"] = 0;
+ }
+ else if (balance >= 0)
+ {
+ responseData["success"] = true;
+ responseData["clientBalance"] = balance;
+ }
+
+ return response;
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: handleGetBalance: Can't get balance for user {0}, Exception {1}", clientUUID, e.ToString());
+ }
+ return response;
+ }
+ }
+
+ m_log.Error("[MONEY XMLRPC]: handleGetBalance: Session authentication failed when getting balance for user " + clientUUID);
+ responseData["description"] = "Session check failure, please re-login";
+ return response;
+ }
+
+ private Hashtable genericCurrencyXMLRPCRequest(Hashtable reqParams, string method, string uri)
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: genericCurrencyXMLRPCRequest: to {0}", uri);
+
+ if (reqParams.Count <= 0 || string.IsNullOrEmpty(method)) return null;
+
+ if (m_checkServerCert)
+ {
+ if (!uri.StartsWith("https://"))
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: genericCurrencyXMLRPCRequest: CheckServerCert is true, but protocol is not HTTPS. Please check INI file.");
+ //return null;
+ }
+ }
+ else
+ {
+ if (!uri.StartsWith("https://") && !uri.StartsWith("http://"))
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: genericCurrencyXMLRPCRequest: Invalid Region Server URL: {0}", uri);
+ return null;
+ }
+ }
+
+ ArrayList arrayParams = new ArrayList();
+ arrayParams.Add(reqParams);
+ XmlRpcResponse moneyServResp = null;
+ try
+ {
+ NSLXmlRpcRequest moneyModuleReq = new NSLXmlRpcRequest(method, arrayParams);
+ moneyServResp = moneyModuleReq.certSend(uri, m_certVerify, m_checkServerCert, MONEYMODULE_REQUEST_TIMEOUT);
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY XMLRPC]: genericCurrencyXMLRPCRequest: Unable to connect to Region Server {0}", uri);
+ m_log.ErrorFormat("[MONEY XMLRPC]: genericCurrencyXMLRPCRequest: {0}", ex.ToString());
+
+ Hashtable ErrorHash = new Hashtable();
+ ErrorHash["success"] = false;
+ ErrorHash["errorMessage"] = "Failed to perform actions on OpenSim Server";
+ ErrorHash["errorURI"] = "";
+ return ErrorHash;
+ }
+
+ if (moneyServResp == null || moneyServResp.IsFault)
+ {
+ Hashtable ErrorHash = new Hashtable();
+ ErrorHash["success"] = false;
+ ErrorHash["errorMessage"] = "Failed to perform actions on OpenSim Server";
+ ErrorHash["errorURI"] = "";
+ return ErrorHash;
+ }
+
+ Hashtable moneyRespData = (Hashtable)moneyServResp.Value;
+ return moneyRespData;
+ }
+
+ private void UpdateBalance(string userID, string message)
+ {
+ string sessionID = string.Empty;
+ string secureID = string.Empty;
+
+ // Konfiguriere das maximale Guthaben (dieser Wert kann aus einer Konfigurationsdatei wie MoneyServer.ini geladen werden)
+ //int m_CurrencyMaximum = m_CurrencyMaximum;
+
+ if (m_sessionDic.ContainsKey(userID) && m_secureSessionDic.ContainsKey(userID))
+ {
+ sessionID = m_sessionDic[userID];
+ secureID = m_secureSessionDic[userID];
+
+ // Aktuelles Guthaben des Benutzers abrufen
+ int currentBalance = m_moneyDBService.getBalance(userID);
+
+ // Überprüfen, ob das Guthaben über dem Maximum liegt und ggf. abziehen
+ if (currentBalance > m_CurrencyMaximum)
+ {
+ int excessAmount = currentBalance - m_CurrencyMaximum;
+
+ // Guthaben auf das Maximum reduzieren
+ bool balanceUpdateSuccess = ReduceUserBalance(userID, excessAmount);
+ if (!balanceUpdateSuccess)
+ {
+ m_log.ErrorFormat("[UpdateBalance]: Error reducing user balance for user {0}", userID);
+ return;
+ }
+
+ currentBalance = m_CurrencyMaximum;
+ m_log.InfoFormat("[UpdateBalance]: Reduced balance for user {0} by {1} to enforce maximum limit of {2}", userID, excessAmount, m_CurrencyMaximum);
+ }
+
+ Hashtable requestTable = new Hashtable();
+ requestTable["clientUUID"] = userID;
+ requestTable["clientSessionID"] = sessionID;
+ requestTable["clientSecureSessionID"] = secureID;
+ requestTable["Balance"] = currentBalance;
+ if (message != "") requestTable["Message"] = message;
+
+ UserInfo user = m_moneyDBService.FetchUserInfo(userID);
+ if (user != null)
+ {
+ genericCurrencyXMLRPCRequest(requestTable, "UpdateBalance", user.SimIP);
+ m_log.InfoFormat("[MONEY XMLRPC]: UpdateBalance: Sent UpdateBalance Request to {0}", user.SimIP.ToString());
+ }
+ }
+ }
+
+ private bool ReduceUserBalance(string userID, int amount)
+ {
+ string sql = "UPDATE balances SET balance = balance - ?amount WHERE user = ?userID";
+ MySQLSuperManager dbm = GetLockedConnection();
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(sql, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?amount", amount);
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ int rowsAffected = cmd.ExecuteNonQuery();
+ return rowsAffected > 0;
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[ReduceUserBalance]: Error reducing user balance: {0}", ex.Message);
+ return false;
+ }
+ finally
+ {
+ dbm.Release();
+ }
+ }
+
+
+ protected bool RollBackTransaction(TransactionData transaction)
+ {
+ if (m_moneyDBService.withdrawMoney(transaction.TransUUID, transaction.Receiver, transaction.Amount))
+ {
+ if (m_moneyDBService.giveMoney(transaction.TransUUID, transaction.Sender, transaction.Amount))
+ {
+ m_log.InfoFormat("[MONEY XMLRPC]: RollBackTransaction: Transaction {0} is successfully.", transaction.TransUUID.ToString());
+ m_moneyDBService.updateTransactionStatus(transaction.TransUUID, (int)Status.FAILED_STATUS,
+ "The buyer failed to get the object, roll back the transaction");
+ UserInfo senderInfo = m_moneyDBService.FetchUserInfo(transaction.Sender);
+ UserInfo receiverInfo = m_moneyDBService.FetchUserInfo(transaction.Receiver);
+ string senderName = "unknown user";
+ string receiverName = "unknown user";
+ if (senderInfo != null) senderName = senderInfo.Avatar;
+ if (receiverInfo != null) receiverName = receiverInfo.Avatar;
+
+ string snd_message = string.Format(m_BalanceMessageRollBack, transaction.Amount, receiverName, transaction.ObjectName);
+ string rcv_message = string.Format(m_BalanceMessageRollBack, transaction.Amount, senderName, transaction.ObjectName);
+
+ if (transaction.Sender != transaction.Receiver) UpdateBalance(transaction.Sender, snd_message);
+ UpdateBalance(transaction.Receiver, rcv_message);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // #########################################
+ // Cashbook ausgabe Transaktionen der User.
+ // #########################################
+
+ public void GetCashbookBalance(string userID)
+ {
+ int balance = 0;
+ string query = "SELECT balance FROM balances WHERE user = ?userID";
+
+ MySQLSuperManager dbm = ((MoneyDBService)m_moneyDBService).GetLockedConnection();
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(query, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?userID", userID);
+ object result = cmd.ExecuteScalar();
+ m_log.InfoFormat("[Cashbook]: Query executed for userID: {0}, result: {1}", userID, result);
+ if (result != null)
+ {
+ balance = Convert.ToInt32(result);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[Cashbook]: Error getting balance for userID: {0}. Exception: {1}", userID, ex);
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ m_log.InfoFormat("[Cashbook]: User: {0}, Balance: {1}", userID, balance);
+ }
+
+
+ public void GetCashbookTotalSales(string userID)
+ {
+ List totalSalesList = new List();
+ string query = "SELECT objectUUID, TotalCount, TotalAmount FROM totalsales WHERE user = ?userID";
+
+ MySQLSuperManager dbm = ((MoneyDBService)m_moneyDBService).GetLockedConnection();
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(query, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ using (MySqlDataReader reader = cmd.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ CashbookTotalSalesData data = new CashbookTotalSalesData
+ {
+ ObjectUUID = reader.GetString("objectUUID"),
+ TotalCount = reader.GetInt32("TotalCount"),
+ TotalAmount = reader.GetInt32("TotalAmount")
+ };
+ totalSalesList.Add(data);
+ m_log.InfoFormat("[Cashbook]: Found sale: ObjectUUID={0}, TotalCount={1}, TotalAmount={2}", data.ObjectUUID, data.TotalCount, data.TotalAmount);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[Cashbook]: Error getting total sales for userID: {0}. Exception: {1}", userID, ex);
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ m_log.InfoFormat("[Cashbook]: User: {0}, Total Sales:", userID);
+ foreach (var sale in totalSalesList)
+ {
+ m_log.InfoFormat("ObjectUUID: {0}, TotalCount: {1}, TotalAmount: {2}", sale.ObjectUUID, sale.TotalCount, sale.TotalAmount);
+ }
+ }
+
+
+
+ public void GetCashbookTransactions(string userID)
+ {
+ List transactionList = new List();
+ string query = "SELECT receiver, amount, senderBalance, receiverBalance, objectName, commonName, description FROM transactions WHERE sender = ?userID OR receiver = ?userID";
+
+ MySQLSuperManager dbm = ((MoneyDBService)m_moneyDBService).GetLockedConnection();
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(query, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?userID", userID);
+
+ using (MySqlDataReader reader = cmd.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ CashbookTransactionData data = new CashbookTransactionData
+ {
+ Receiver = reader.GetString("receiver"),
+ Amount = reader.GetInt32("amount"),
+ SenderBalance = reader.GetInt32("senderBalance"),
+ ReceiverBalance = reader.GetInt32("receiverBalance"),
+ ObjectName = reader.GetString("objectName"),
+ CommonName = reader.GetString("commonName"),
+ Description = reader.GetString("description")
+ };
+ transactionList.Add(data);
+ m_log.InfoFormat("[Cashbook]: Found transaction: Receiver={0}, Amount={1}, SenderBalance={2}, ReceiverBalance={3}, ObjectName={4}, CommonName={5}, Description={6}",
+ data.Receiver, data.Amount, data.SenderBalance, data.ReceiverBalance, data.ObjectName, data.CommonName, data.Description);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[Cashbook]: Error getting transactions for userID: {0}. Exception: {1}", userID, ex);
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ m_log.InfoFormat("[Cashbook]: User: {0}, Transactions:", userID);
+ foreach (var transaction in transactionList)
+ {
+ m_log.InfoFormat("Receiver: {0}, Amount: {1}, SenderBalance: {2}, ReceiverBalance: {3}, ObjectName: {4}, CommonName: {5}, Description: {6}",
+ transaction.Receiver, transaction.Amount, transaction.SenderBalance, transaction.ReceiverBalance, transaction.ObjectName, transaction.CommonName, transaction.Description);
+ }
+ }
+
+
+
+ // Aktualisierte Methode zur Initialisierung der Konsolenbefehle
+ public void RegisterConsoleCommands(ICommandConsole console)
+ {
+ console.Commands.AddCommand(
+ "MoneyXmlRpcModule",
+ false,
+ "getbalance",
+ "getbalance or ",
+ "Get the balance for the specified user",
+ HandleGetCashbookBalance);
+
+ console.Commands.AddCommand(
+ "MoneyXmlRpcModule",
+ false,
+ "gettotalsales",
+ "gettotalsales or ",
+ "Get the total sales for the specified user",
+ HandleGetCashbookTotalSales);
+
+ console.Commands.AddCommand(
+ "MoneyXmlRpcModule",
+ false,
+ "gettransactions",
+ "gettransactions or ",
+ "Get the transactions for the specified user",
+ HandleGetCashbookTransactions);
+ }
+
+ private void HandleGetCashbookBalance(string module, string[] cmdparams)
+ {
+ if (cmdparams.Length < 3)
+ {
+ m_log.Info("[Cashbook]: Usage: getbalance or ");
+ return;
+ }
+
+ string userID = string.Join(" ", cmdparams, 2, cmdparams.Length - 2).Trim();
+
+ // Prüfen, ob userID eine gültige UUID ist
+ if (Guid.TryParse(userID, out Guid _))
+ {
+ m_log.InfoFormat("[Cashbook]: userID is a valid UUID: {0}", userID);
+ GetCashbookBalance(userID);
+ }
+ else
+ {
+ m_log.InfoFormat("[Cashbook]: userID is a name: {0}", userID);
+ GetCashbookBalanceByName(userID);
+ }
+ }
+
+ private void HandleGetCashbookTotalSales(string module, string[] cmdparams)
+ {
+ if (cmdparams.Length < 3)
+ {
+ m_log.Info("[Cashbook]: Usage: gettotalsales or ");
+ return;
+ }
+
+ string userID = string.Join(" ", cmdparams, 2, cmdparams.Length - 2).Trim();
+
+ // Prüfen, ob userID eine gültige UUID ist
+ if (Guid.TryParse(userID, out Guid _))
+ {
+ m_log.InfoFormat("[Cashbook]: userID is a valid UUID: {0}", userID);
+ GetCashbookTotalSales(userID);
+ }
+ else
+ {
+ m_log.InfoFormat("[Cashbook]: userID is a name: {0}", userID);
+ GetCashbookTotalSalesByName(userID);
+ }
+ }
+
+ private void HandleGetCashbookTransactions(string module, string[] cmdparams)
+ {
+ if (cmdparams.Length < 3)
+ {
+ m_log.Info("[Cashbook]: Usage: gettransactions or ");
+ return;
+ }
+
+ string userID = string.Join(" ", cmdparams, 2, cmdparams.Length - 2).Trim();
+
+ // Prüfen, ob userID eine gültige UUID ist
+ if (Guid.TryParse(userID, out Guid _))
+ {
+ m_log.InfoFormat("[Cashbook]: userID is a valid UUID: {0}", userID);
+ GetCashbookTransactions(userID);
+ }
+ else
+ {
+ m_log.InfoFormat("[Cashbook]: userID is a name: {0}", userID);
+ GetCashbookTransactionsByName(userID);
+ }
+ }
+
+ private void GetCashbookBalanceByName(string name)
+ {
+ string userID = GetUUIDFromName(name);
+ if (string.IsNullOrEmpty(userID))
+ {
+ m_log.InfoFormat("[Cashbook]: No UUID found for user: {0}", name);
+ return;
+ }
+ GetCashbookBalance(userID);
+ }
+
+ private void GetCashbookTotalSalesByName(string name)
+ {
+ string userID = GetUUIDFromName(name);
+ if (string.IsNullOrEmpty(userID))
+ {
+ m_log.InfoFormat("[Cashbook]: No UUID found for user: {0}", name);
+ return;
+ }
+ GetCashbookTotalSales(userID);
+ }
+
+ private void GetCashbookTransactionsByName(string name)
+ {
+ string userID = GetUUIDFromName(name);
+ if (string.IsNullOrEmpty(userID))
+ {
+ m_log.InfoFormat("[Cashbook]: No UUID found for user: {0}", name);
+ return;
+ }
+ GetCashbookTransactions(userID);
+ }
+
+ private string GetUUIDFromName(string name)
+ {
+ string query = "SELECT PrincipalID FROM UserAccounts WHERE CONCAT(FirstName, ' ', LastName) = ?name";
+ string userID = null;
+
+ MySQLSuperManager dbm = ((MoneyDBService)m_moneyDBService).GetLockedConnection();
+
+ try
+ {
+ using (MySqlCommand cmd = new MySqlCommand(query, dbm.Manager.dbcon))
+ {
+ cmd.Parameters.AddWithValue("?name", name);
+ using (MySqlDataReader reader = cmd.ExecuteReader())
+ {
+ if (reader.Read())
+ {
+ userID = reader.GetString("PrincipalID");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[Cashbook]: Error getting UUID for name: {0}. Exception: {1}", name, ex);
+ }
+ finally
+ {
+ dbm.Release();
+ }
+
+ return userID;
+ }
+
+
+
+ #endregion
+
+ }
+
+}
+// ################## classes ##################
+#region classes
+
+public class CashbookTotalSalesData
+{
+ public string ObjectUUID { get; set; }
+ public int TotalCount { get; set; }
+ public int TotalAmount { get; set; }
+}
+
+public class CashbookTransactionData
+{
+ public string Receiver { get; set; }
+ public int Amount { get; set; }
+ public int SenderBalance { get; set; }
+ public int ReceiverBalance { get; set; }
+ public string ObjectName { get; set; }
+ public string CommonName { get; set; }
+ public string Description { get; set; }
+}
+
+public class CurrencyQuoteRequest
+{
+ public string AgentId { get; set; }
+ public int CurrencyBuy { get; set; }
+ public string Language { get; set; }
+ public string SecureSessionId { get; set; }
+ public string ViewerBuildVersion { get; set; }
+ public string ViewerChannel { get; set; }
+ public int ViewerMajorVersion { get; set; }
+ public int ViewerMinorVersion { get; set; }
+ public int ViewerPatchVersion { get; set; }
+}
+
+public class LandPurchaseRequest
+{
+ public string AgentId { get; set; }
+ public int BillableArea { get; set; }
+ public int CurrencyBuy { get; set; }
+ public string Language { get; set; }
+ public string SecureSessionId { get; set; }
+}
+
+public class BuyCurrencyRequest
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public BuyCurrencyRequest()
+ {
+ // Initialize any default values or properties here
+ }
+ public string AgentId { get; set; }
+ public int CurrencyBuy { get; set; }
+ public string Language { get; set; }
+ public string SecureSessionId { get; set; }
+ public string ViewerBuildVersion { get; set; }
+ public string ViewerChannel { get; set; }
+ public int ViewerMajorVersion { get; set; }
+ public int ViewerMinorVersion { get; set; }
+ public int ViewerPatchVersion { get; set; }
+
+ public decimal Amount { get; set; }
+
+ public string CurrencyType { get; set; }
+
+ public bool IsValid()
+ {
+ // Add validation logic here, e.g., check for null or empty values
+ return Amount > 0 && !string.IsNullOrEmpty(CurrencyType);
+ }
+}
+#endregion
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/Program.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/Program.cs
new file mode 100644
index 0000000..74f7905
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/Program.cs
@@ -0,0 +1,74 @@
+/*
+ * 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 OpenSim 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.
+
+Funktion
+ Purpose: Dies ist der Einstiegspunkt (Main-Methode) für die Anwendung OpenSim.Grid.MoneyServer.
+ Ablauf:
+ Konfiguriert das Logging mit XmlConfigurator.Configure().
+ Erstellt eine Instanz von MoneyServerBase.
+ Startet den Server mit app.Startup() und ruft danach app.Work() auf.
+ Bei Fehlern wird eine Exception abgefangen und die Fehlermeldung auf der Konsole ausgegeben.
+
+Null-Pointer-Prüfung und Fehlerquellen
+ app != null: Nach der Instanziierung von MoneyServerBase wird geprüft, ob die Instanz erfolgreich erstellt wurde (obwohl in C# ein Konstruktor normalerweise nicht null liefern kann, außer bei sehr speziellen Fällen wie einem Factory-Pattern).
+ Exception Handling: Der gesamte Ablauf ist in einem try-catch Block gekapselt. Jede Exception (auch NullReferenceException) wird abgefangen und eine lesbare Fehlermeldung auf die Konsole ausgegeben.
+ Fehlerfall: Sollte MoneyServerBase nicht erstellt werden können, gibt es eine eigene Fehlermeldung ("Failed to create MoneyServerBase instance.").
+ Keine weiteren Ressourcenlecks: Es gibt keine expliziten Ressourcen (wie Files/DB-Connections), die in dieser Datei geöffnet und geschlossen werden müssten.
+
+Zusammenfassung
+ NullPointer: Praktisch ausgeschlossen, da alle kritischen Objekte direkt initialisiert oder auf null geprüft werden. Fehler werden sauber abgefangen.
+ Fehlerquellen: Nur wenn der Konstruktor oder nachfolgende Aufrufe von Startup() oder Work() eine Exception werfen, erscheint eine Fehlermeldung auf der Konsole.
+ Funktion: Startet den MoneyServer und hält ihn im Betrieb.
+
+Fazit:
+Der Code in Program.cs ist sehr robust gegen NullPointer und allgemeine Fehler. Die Funktion ist klar: Start und Lebenszyklusmanagement des MoneyServers.
+Es gibt keine offensichtlichen Schwachstellen oder Verbesserungsbedarf in Bezug auf NullPointer oder Fehlerbehandlung.
+ */
+
+using log4net.Config;
+
+using System;
+
+namespace OpenSim.Grid.MoneyServer
+{
+ class Program
+ {
+ /// Defines the entry point of the application.
+ /// The arguments.
+ public static void Main(string[] args)
+ {
+ try
+ {
+ XmlConfigurator.Configure();
+ MoneyServerBase app = new MoneyServerBase();
+ if (app != null)
+ {
+ app.Startup();
+ app.Work();
+ }
+ else
+ {
+ Console.WriteLine("Failed to create MoneyServerBase instance.");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("An error occurred: " + ex.Message);
+ // You can also log the exception here, e.g., using a logging framework
+ }
+ }
+ }
+}
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/Properties/AssemblyInfo.cs b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c5604a9
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/OpenSim.Grid.MoneyServer/Properties/AssemblyInfo.cs
@@ -0,0 +1,34 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Mono.Addins;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OpenSim.Grid.MoneyServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("OpenSim.Grid.MoneyServer")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f554c84a-d8c7-41ea-833d-2483cde29e0f")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/addon-modules/OpenSim-Grid-MoneyServer/prebuild-MoneyServer.xml b/addon-modules/OpenSim-Grid-MoneyServer/prebuild-MoneyServer.xml
new file mode 100644
index 0000000..c285575
--- /dev/null
+++ b/addon-modules/OpenSim-Grid-MoneyServer/prebuild-MoneyServer.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ ../../bin/
+ true
+
+
+
+
+ ../../bin/
+ true
+
+
+
+ ../../bin/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/DTLNSLMoneyModule.cs b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/DTLNSLMoneyModule.cs
new file mode 100644
index 0000000..0adea50
--- /dev/null
+++ b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/DTLNSLMoneyModule.cs
@@ -0,0 +1,2246 @@
+/* Modified by Fumi.Iseki for Unix/Linix http://www.nsl.tuis.ac.jp
+ *
+ * Copyright (c) Contributors, http://opensimulator.org/, http://www.nsl.tuis.ac.jp/ 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 OpenSim 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.
+
+Funktion
+ DTLNSLMoneyModule ist ein OpenSim-Regionmodul für die Verwaltung eines eigenen Währungssystems und die Anbindung an einen externen MoneyServer.
+ Es implementiert die Schnittstellen IMoneyModule und ISharedRegionModule.
+ Kernfunktionen:
+ Verwaltung von Guthaben (Balance), Transaktionen und Gebühren für verschiedene In-World-Aktionen (z.B. Objektkauf, Landkauf, Uploads).
+ Kommunikation mit einem externen MoneyServer via XML-RPC (Transfer, Login, Logoff, Balance-Abfrage, etc.).
+ Ereignis- und Event-Handling für Inworld-Transaktionen.
+ Zertifikatsverwaltung für sichere Kommunikation.
+
+Null Pointer Checks
+ Konfigurationswerte: Werden mit Defaultwerten initialisiert (string.Empty, false, 0 usw.). Viele Konfigurationswerte werden mit Fallback-Werten geladen.
+ Methoden mit Objektrückgabe wie GetLocateClient, GetLocateScene, GetLocatePrim:
+ Es wird geprüft, ob Rückgabewerte null sind, bevor sie genutzt werden (z.B. in ObjectGiveMoney, Transfer, etc.).
+ Client- und Scene-Objekte:
+ Wird ein Objekt nicht gefunden, wird mit return oder Fehlerwerten sauber abgebrochen.
+ RPC-Handler:
+ Prüfen, ob erforderliche Felder im Parameter-Hashtable existieren, bevor sie genutzt werden.
+ Try-Catch bei XML-RPC:
+ Netzwerkfehler und Exceptions werden sauber abgefangen und führen zu Fehler-Hash als Rückgabe.
+ Event-Handler:
+ Überall wird geprüft, ob das Event-Objekt oder der Client existiert, bevor darauf zugegriffen wird.
+ Beispiel:
+ C#
+
+ SceneObjectPart sceneObj = GetLocatePrim(objectID);
+ if (sceneObj == null) return false;
+
+ Rückgabewerte können null sein:
+ Es wird überall mit null als Fehlerfall gerechnet (besonders bei Lookups und DB/Network-Kommunikation).
+
+Fehlerquellen und deren Behandlung
+ Konfiguration:
+ Fehlende oder falsche Konfiguration wird geloggt und führt zum Abbruch der Initialisierung.
+ RPC-Fehler:
+ Bei Netzwerkproblemen oder fehlerhaften Antworten vom MoneyServer wird immer ein Fehlerobjekt erstellt und ausführlich geloggt.
+ Fehlende Objekte/Clients:
+ Fast überall wird geprüft, ob Objekte, Clients oder Rückgabewerte wirklich existieren, bevor sie verwendet werden.
+ Fehlende Berechtigungen oder unzulässige Aktionen:
+ Z.B. if (!m_sellEnabled) return; – Aktionen werden abgebrochen, wenn sie nicht erlaubt sind.
+ Transaktionsfehler:
+ Jede Transaktion prüft, ob die Übertragung erfolgreich war, und gibt false zurück oder loggt den Fehler.
+
+Typische Pattern für Sicherheit und Fehlervermeidung
+ Locking:
+ Zugriff auf die Szenenliste ist mit lock (m_sceneList) geschützt.
+ Null-Checks:
+ Überall vor der Nutzung von Rückgabewerten, Event-Objekten, Parametern.
+ Logging:
+ Fehler und Sonderfälle werden ausführlich geloggt.
+ Try/Catch:
+ Insbesondere bei externen Netzwerkzugriffen.
+
+Zusammenfassung & Bewertung
+ Null Pointer:
+ Der gesamte Code ist sehr gewissenhaft in Bezug auf Null Pointer – überall werden Objekte auf null geprüft, bevor sie verwendet werden.
+ Fehlerquellen:
+ Externe Fehler (Netzwerk, Konfiguration) werden geloggt und führen zu sauberem Abbruch. Rückgabewerte im Fehlerfall (z.B. null oder false) sind klar definiert und werden behandelt.
+ Funktion:
+ Sehr umfangreiches Modul zur sicheren Verwaltung von Währungen und Transaktionen in OpenSim, mit Anbindung an einen externen Server und vielen Schutzmechanismen.
+
+Fazit:
+Der Code ist robust gegenüber NullPointerException und typischen Fehlern. Fehlerquellen werden gut abgefangen, Logging ist umfassend.
+Die gesamte Struktur entspricht gängiger C#-Best-Practice für modulare, fehlertolerante Server-Module.
+ */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Reflection;
+using System.Net;
+using System.Security.Cryptography;
+using log4net;
+using Nini.Config;
+using Nwc.XmlRpc;
+using Mono.Addins;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Framework.Servers;
+using OpenSim.Framework.Servers.HttpServer;
+using OpenSim.Services.Interfaces;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Data.MySQL.MySQLMoneyDataWrapper;
+using NSL.Certificate.Tools;
+using NSL.Network.XmlRpc;
+using System.IO;
+
+
+[assembly: Addin("DTLNSLMoneyModule", "1.0")]
+[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
+
+namespace OpenSim.Modules.Currency
+{
+ ///
+ /// Transaction Type
+ ///
+ public enum TransactionType : int
+ {
+ None = 0,
+ // Extend
+ BirthGift = 900,
+ AwardPoints = 901,
+ // One-Time Charges
+ ObjectClaim = 1000,
+ LandClaim = 1001,
+ GroupCreate = 1002,
+ GroupJoin = 1004,
+ TeleportCharge = 1100,
+ UploadCharge = 1101,
+ LandAuction = 1102,
+ ClassifiedCharge = 1103,
+ // Recurrent Charges
+ ObjectTax = 2000,
+ LandTax = 2001,
+ LightTax = 2002,
+ ParcelDirFee = 2003,
+ GroupTax = 2004,
+ ClassifiedRenew = 2005,
+ ScheduledFee = 2900,
+ // Inventory Transactions
+ GiveInventory = 3000,
+ // Transfers Between Users
+ ObjectSale = 5000,
+ Gift = 5001,
+ LandSale = 5002,
+ ReferBonus = 5003,
+ InvntorySale = 5004,
+ RefundPurchase = 5005,
+ LandPassSale = 5006,
+ DwellBonus = 5007,
+ PayObject = 5008,
+ ObjectPays = 5009,
+ BuyMoney = 5010,
+ MoveMoney = 5011,
+ SendMoney = 5012,
+ // Group Transactions
+ GroupLandDeed = 6001,
+ GroupObjectDeed = 6002,
+ GroupLiability = 6003,
+ GroupDividend = 6004,
+ GroupMembershipDues = 6005,
+ // Stipend Credits
+ StipendBasic = 10000
+ }
+
+ [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "DTLNSLMoneyModule")]
+ public class DTLNSLMoneyModule : IMoneyModule, ISharedRegionModule
+ {
+ // Constant memebers
+ private const int MONEYMODULE_REQUEST_TIMEOUT = 10000;
+
+ // Private data members.
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ //private bool m_enabled = true;
+ private bool m_sellEnabled = false;
+ private bool m_enable_server = true; // enable Money Server
+
+ private IConfigSource m_config;
+
+ private string m_moneyServURL = string.Empty;
+ public BaseHttpServer HttpServer;
+
+ private string m_certFilename = "";
+ private string m_certPassword = "";
+ private bool m_checkServerCert = false;
+ private string m_cacertFilename = "";
+
+ private bool m_use_web_settle = false;
+ private string m_settle_url = "";
+ private string m_settle_message = "";
+ private bool m_settle_user = false;
+
+ private int m_hg_avatarClass = (int)AvatarType.HG_AVATAR;
+
+ private NSLCertificateVerify m_certVerify = new NSLCertificateVerify(); // For server authentication
+
+ private Dictionary m_sceneList = new Dictionary();
+
+ private Dictionary m_moneyServer = new Dictionary();
+
+ // Events
+ public event ObjectPaid OnObjectPaid;
+
+ // Price
+ private int ObjectCount = 0;
+ private int PriceEnergyUnit = 100;
+ private int PriceObjectClaim = 10;
+ private int PricePublicObjectDecay = 4;
+ private int PricePublicObjectDelete = 4;
+ private int PriceParcelClaim = 1;
+ private float PriceParcelClaimFactor = 1.0f;
+ private int PriceUpload = 0;
+ private int PriceRentLight = 5;
+ private float PriceObjectRent = 1.0f;
+ private float PriceObjectScaleFactor = 10.0f;
+ private int PriceParcelRent = 1;
+ private int PriceGroupCreate = 0;
+ private int TeleportMinPrice = 2;
+ private float TeleportPriceExponent = 2.0f;
+ private float EnergyEfficiency = 1.0f;
+ private int PriceLandTax = 0;
+ private int PriceLand = 0;
+ private int PriceCurrency = 0;
+
+ /// The m RPC handlers
+ private Dictionary m_rpcHandlers;
+
+ ///
+ /// Initializes the specified scene.
+ ///
+ /// The scene to initialize.
+ /// The source of the configuration.
+ ///
+ /// This method calls the Initialize method with the source parameter,
+ /// then checks if the money server URL is null or empty. If so, it sets
+ /// the enable_server flag to false. Finally, it adds the scene to the region.
+ ///
+ public void Initialise(Scene scene, IConfigSource source)
+ {
+ // Call the Initialize method with the source parameter
+ Initialise(source);
+
+ // Check if the money server URL is null or empty
+ //if (string.IsNullOrEmpty(m_moneyServURL)) m_enable_server = false;
+ if (string.IsNullOrEmpty(m_moneyServURL))
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: CurrencyServer URL not set.");
+ m_enable_server = false;
+ }
+
+ // Add the scene to the region
+ AddRegion(scene);
+ }
+
+ ///
+ /// This is called to initialize the region module. For shared modules, this is called
+ /// exactly once, after creating the single (shared) instance. For non-shared modules,
+ /// this is called once on each instance, after the instace for the region has been created.
+ ///
+ /// A
+ public void Initialise(IConfigSource source)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: Initialise started.");
+
+ // Überprüfen, ob die Konfigurationsquelle null ist.
+ if (source == null)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: Initialise aborted - source is null.");
+ return;
+ }
+
+ try
+ {
+ m_config = source;
+
+ // Economy-Konfiguration abrufen
+ IConfig economyConfig = m_config.Configs["Economy"];
+ if (economyConfig == null)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: Initialise aborted - [Economy] section is missing in configuration.");
+ return;
+ }
+
+ // Überprüfen, ob das Modul aktiviert ist
+ if (economyConfig.GetString("EconomyModule") != Name)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: Initialise - DTL/NSL MoneyModule is disabled.");
+ return;
+ }
+
+ m_log.InfoFormat("[MONEY MODULE]: Initialise - DTL/NSL MoneyModule is enabled.");
+
+ // Konfiguration für Verkauf und MoneyServer-URL
+ m_sellEnabled = economyConfig.GetBoolean("SellEnabled", m_sellEnabled);
+ m_log.InfoFormat("[MONEY MODULE]: SellEnabled set to {0}", m_sellEnabled);
+
+ m_moneyServURL = economyConfig.GetString("CurrencyServer", m_moneyServURL);
+ m_log.InfoFormat("[MONEY MODULE]: CurrencyServer set to {0}", m_moneyServURL);
+
+ // Konfiguration für Client-Zertifizierung
+ m_certFilename = economyConfig.GetString("ClientCertFilename", m_certFilename);
+ m_certPassword = economyConfig.GetString("ClientCertPassword", m_certPassword);
+ if (!string.IsNullOrEmpty(m_certFilename))
+ {
+ m_certVerify.SetPrivateCert(m_certFilename, m_certPassword);
+ m_log.InfoFormat("[MONEY MODULE]: Client certificate set from file {0}", m_certFilename);
+ }
+ else
+ {
+ m_log.Warn("[MONEY MODULE]: No client certificate filename provided.");
+ }
+
+ // Konfiguration für Server-Zertifikatüberprüfung
+ m_checkServerCert = economyConfig.GetBoolean("CheckServerCert", m_checkServerCert);
+ m_cacertFilename = economyConfig.GetString("CACertFilename", m_cacertFilename);
+
+ if (!string.IsNullOrEmpty(m_cacertFilename))
+ {
+ m_certVerify.SetPrivateCA(m_cacertFilename);
+ m_log.InfoFormat("[MONEY MODULE]: Server CA certificate loaded from {0}", m_cacertFilename);
+ }
+ else
+ {
+ m_checkServerCert = false;
+ m_log.Warn("[MONEY MODULE]: No CA certificate filename provided; server certificate check disabled.");
+ }
+
+ // Konfiguration für Settlement
+ m_use_web_settle = economyConfig.GetBoolean("SettlementByWeb", m_use_web_settle);
+ m_log.InfoFormat("[MONEY MODULE]: SettlementByWeb set to {0}", m_use_web_settle);
+
+ m_settle_url = economyConfig.GetString("SettlementURL", m_settle_url);
+ m_log.InfoFormat("[MONEY MODULE]: SettlementURL set to {0}", m_settle_url);
+
+ m_settle_message = economyConfig.GetString("SettlementMessage", m_settle_message);
+ m_log.InfoFormat("[MONEY MODULE]: SettlementMessage set to {0}", m_settle_message);
+
+ // Preise konfigurieren
+ PriceEnergyUnit = economyConfig.GetInt("PriceEnergyUnit", PriceEnergyUnit);
+ PriceObjectClaim = economyConfig.GetInt("PriceObjectClaim", PriceObjectClaim);
+ PricePublicObjectDecay = economyConfig.GetInt("PricePublicObjectDecay", PricePublicObjectDecay);
+ PricePublicObjectDelete = economyConfig.GetInt("PricePublicObjectDelete", PricePublicObjectDelete);
+ PriceParcelClaim = economyConfig.GetInt("PriceParcelClaim", PriceParcelClaim);
+ PriceParcelClaimFactor = economyConfig.GetFloat("PriceParcelClaimFactor", PriceParcelClaimFactor);
+ PriceUpload = economyConfig.GetInt("PriceUpload", PriceUpload);
+ PriceRentLight = economyConfig.GetInt("PriceRentLight", PriceRentLight);
+ PriceObjectRent = economyConfig.GetFloat("PriceObjectRent", PriceObjectRent);
+ PriceObjectScaleFactor = economyConfig.GetFloat("PriceObjectScaleFactor", PriceObjectScaleFactor);
+ PriceParcelRent = economyConfig.GetInt("PriceParcelRent", PriceParcelRent);
+
+ PriceLand = economyConfig.GetInt("PriceLand", PriceLand); // Test
+ PriceCurrency = economyConfig.GetInt("PriceCurrency", PriceCurrency); // Test
+ PriceLandTax = economyConfig.GetInt("PriceLandTax", PriceLandTax); // Test
+
+ PriceGroupCreate = economyConfig.GetInt("PriceGroupCreate", PriceGroupCreate);
+ TeleportMinPrice = economyConfig.GetInt("TeleportMinPrice", TeleportMinPrice);
+ TeleportPriceExponent = economyConfig.GetFloat("TeleportPriceExponent", TeleportPriceExponent);
+ EnergyEfficiency = economyConfig.GetFloat("EnergyEfficiency", EnergyEfficiency);
+ m_log.InfoFormat("[MONEY MODULE]: Price settings loaded successfully.");
+
+ // Konfiguration für HG-Avatar-Typ
+ string avatarClass = economyConfig.GetString("HGAvatarAs", "HGAvatar").ToLower();
+ m_hg_avatarClass = avatarClass switch
+ {
+ "localavatar" => (int)AvatarType.LOCAL_AVATAR,
+ "guestavatar" => (int)AvatarType.GUEST_AVATAR,
+ "hgavatar" => (int)AvatarType.HG_AVATAR,
+ "foreignavatar" => (int)AvatarType.FOREIGN_AVATAR,
+ _ => (int)AvatarType.UNKNOWN_AVATAR
+ };
+
+ m_log.InfoFormat("[MONEY MODULE]: Initialise - Configuration loaded successfully.");
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: Initialise - Failed to load configuration. Error: {0}", ex);
+ }
+ }
+
+ ///
+ /// This is called whenever a is added. For shared modules, this can happen several times.
+ /// For non-shared modules, this happens exactly once, after has been called.
+ ///
+ /// A
+ public void AddRegion(Scene scene)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: AddRegion:");
+
+ if (scene == null) return;
+
+ scene.RegisterModuleInterface(this); // Eliminate conflicting modules
+
+ lock (m_sceneList)
+ {
+ if (m_sceneList.Count == 0)
+ {
+ if (m_enable_server)
+ {
+ HttpServer = new BaseHttpServer(9000);
+ HttpServer.AddStreamHandler(new Region.Framework.Scenes.RegionStatsHandler(scene.RegionInfo));
+
+ HttpServer.AddXmlRPCHandler("OnMoneyTransfered", OnMoneyTransferedHandler);
+ HttpServer.AddXmlRPCHandler("UpdateBalance", BalanceUpdateHandler);
+ HttpServer.AddXmlRPCHandler("UserAlert", UserAlertHandler);
+ HttpServer.AddXmlRPCHandler("GetBalance", GetBalanceHandler);
+ HttpServer.AddXmlRPCHandler("AddBankerMoney", AddBankerMoneyHandler);
+ HttpServer.AddXmlRPCHandler("SendMoney", SendMoneyHandler);
+ HttpServer.AddXmlRPCHandler("MoveMoney", MoveMoneyHandler);
+
+ m_rpcHandlers = new Dictionary();
+
+ MainServer.Instance.AddXmlRPCHandler("OnMoneyTransfered", OnMoneyTransferedHandler);
+ MainServer.Instance.AddXmlRPCHandler("UpdateBalance", BalanceUpdateHandler);
+ MainServer.Instance.AddXmlRPCHandler("UserAlert", UserAlertHandler);
+ MainServer.Instance.AddXmlRPCHandler("GetBalance", GetBalanceHandler);
+ MainServer.Instance.AddXmlRPCHandler("AddBankerMoney", AddBankerMoneyHandler);
+ MainServer.Instance.AddXmlRPCHandler("SendMoney", SendMoneyHandler);
+ MainServer.Instance.AddXmlRPCHandler("MoveMoney", MoveMoneyHandler);
+
+ }
+ }
+
+ if (m_sceneList.ContainsKey(scene.RegionInfo.RegionHandle))
+ {
+ m_sceneList[scene.RegionInfo.RegionHandle] = scene;
+ }
+ else
+ {
+ m_sceneList.Add(scene.RegionInfo.RegionHandle, scene);
+ }
+ }
+
+ scene.EventManager.OnNewClient += OnNewClient;
+ scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
+ scene.EventManager.OnMakeChildAgent += MakeChildAgent;
+
+ // for OpenSim
+ scene.EventManager.OnMoneyTransfer += MoneyTransferAction;
+ scene.EventManager.OnValidateLandBuy += ValidateLandBuy;
+ scene.EventManager.OnLandBuy += processLandBuy;
+
+ m_log.InfoFormat("[MONEY MODULE]: AddRegion: {0}", scene.RegionInfo.RegionName);
+
+ }
+
+
+ ///
+ /// This is called whenever a is removed. For shared modules, this can happen several times.
+ /// For non-shared modules, this happens exactly once, if the scene this instance is associated with is removed.
+ ///
+ /// A
+ public void RemoveRegion(Scene scene)
+ {
+ if (scene == null) return;
+
+ lock (m_sceneList)
+ {
+ scene.EventManager.OnNewClient -= OnNewClient;
+ scene.EventManager.OnMakeRootAgent -= OnMakeRootAgent;
+ scene.EventManager.OnMakeChildAgent -= MakeChildAgent;
+
+ // for OpenSim
+ scene.EventManager.OnMoneyTransfer -= MoneyTransferAction;
+ scene.EventManager.OnValidateLandBuy -= ValidateLandBuy;
+ scene.EventManager.OnLandBuy -= processLandBuy;
+
+ m_log.InfoFormat("[MONEY MODULE]: RemoveRegion: {0}", scene.RegionInfo.RegionName);
+ }
+ }
+
+
+ ///
+ /// This will be called once for every scene loaded. In a shared module
+ /// this will be multiple times in one instance, while a nonshared
+ /// module instance will only be called once.
+ /// This method is called after AddRegion has been called in all
+ /// modules for that scene, providing an opportunity to request
+ /// another module's interface, or hook an event from another module.
+ ///
+ /// A
+ public void RegionLoaded(Scene scene)
+ {
+ m_log.InfoFormat("[MONEY MODULE] region loaded {0}", scene.RegionInfo.RegionID.ToString());
+ }
+
+
+ ///
+ /// If this returns non-null, it is the type of an interface that
+ /// this module intends to register.
+ /// This will cause the loader to defer loading of this module
+ /// until all other modules have been loaded. If no other module
+ /// has registered the interface by then, this module will be
+ /// activated, else it will remain inactive, letting the other module
+ /// take over. This should return non-null ONLY in modules that are
+ /// intended to be easily replaceable, e.g. stub implementations
+ /// that the developer expects to be replaced by third party provided
+ /// modules.
+ ///
+ public Type ReplaceableInterface
+ {
+ get { return null; }
+ }
+
+
+ /// Gets a value indicating whether this instance is shared module.
+ ///
+ /// true if this instance is shared module; otherwise, false.
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+
+
+ ///
+ ///
+ ///
+ /// The name of the module
+ public string Name
+ {
+ get { return "DTLNSLMoneyModule"; }
+ }
+
+
+ ///
+ /// This is called exactly once after all the shared region-modules have been instanciated and
+ /// d.
+ ///
+ public void PostInitialise()
+ {
+
+ }
+
+
+ ///
+ /// This is the inverse to . After a Close(), this instance won't be usable anymore.
+ ///
+ public void Close()
+ {
+
+ }
+
+ /// Objects the give money.
+ /// The object identifier.
+ /// From identifier.
+ /// To identifier.
+ /// The amount.
+ /// The TXN.
+ /// The result.
+ ///
+ ///
+ ///
+ public bool ObjectGiveMoney(UUID objectID, UUID fromID, UUID toID, int amount, UUID txn, out string result)
+ {
+ result = string.Empty;
+ if (!m_sellEnabled)
+ {
+ result = "LINDENDOLLAR_INSUFFICIENTFUNDS";
+ return false;
+ }
+
+ string objName = string.Empty;
+ string avatarName = string.Empty;
+
+ SceneObjectPart sceneObj = GetLocatePrim(objectID);
+ if (sceneObj == null)
+ {
+ result = "LINDENDOLLAR_INSUFFICIENTFUNDS";
+ return false;
+ }
+ objName = sceneObj.Name;
+
+ Scene scene = GetLocateScene(toID);
+ if (scene != null)
+ {
+ UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, toID);
+ if (account != null)
+ {
+ avatarName = account.FirstName + " " + account.LastName;
+ }
+ }
+
+ bool ret = false;
+ string description = String.Format("Object {0} pays {1}", objName, avatarName);
+
+ if (sceneObj.OwnerID == fromID)
+ {
+ ulong regionHandle = sceneObj.RegionHandle;
+ UUID regionUUID = sceneObj.RegionID;
+ if (GetLocateClient(fromID) != null)
+ {
+ ret = TransferMoney(fromID, toID, amount, (int)TransactionType.ObjectPays, objectID, regionHandle, regionUUID, description);
+ }
+ else
+ {
+ ret = ForceTransferMoney(fromID, toID, amount, (int)TransactionType.ObjectPays, objectID, regionHandle, regionUUID, description);
+ }
+ }
+
+ if (!ret) result = "LINDENDOLLAR_INSUFFICIENTFUNDS";
+
+ m_log.InfoFormat("[MONEY MODULE] ObjectGiveMoney: {0} {1} {2} {3} {4} {5} {6}", objectID, fromID, toID, amount, txn, result, ret);
+
+ return ret;
+ }
+
+
+ //
+ /// Gets the upload charge.
+ /// The upload charge.
+ public int UploadCharge
+ {
+ get { return PriceUpload; }
+ }
+
+
+ //
+ /// Gets the group creation charge.
+ /// The group creation charge.
+ public int GroupCreationCharge
+ {
+ get { return PriceGroupCreate; }
+ }
+
+
+ /// Gets the balance.
+ /// The agent identifier.
+ public int GetBalance(UUID agentID)
+ {
+ IClientAPI client = GetLocateClient(agentID);
+ return QueryBalanceFromMoneyServer(client);
+ }
+
+
+ /// Uploads the covered.
+ /// The agent identifier.
+ /// The amount.
+ public bool UploadCovered(UUID agentID, int amount)
+ {
+ IClientAPI client = GetLocateClient(agentID);
+
+ if (m_enable_server || string.IsNullOrEmpty(m_moneyServURL))
+ {
+ int balance = QueryBalanceFromMoneyServer(client);
+ if (balance >= amount) return true;
+ }
+
+ m_log.InfoFormat("[MONEY MODULE] UploadCovered: {0} {1}", agentID, amount);
+
+ return false;
+ }
+
+
+ /// Amounts the covered.
+ /// The agent identifier.
+ /// The amount.
+ public bool AmountCovered(UUID agentID, int amount)
+ {
+ IClientAPI client = GetLocateClient(agentID);
+
+ if (m_enable_server || string.IsNullOrEmpty(m_moneyServURL))
+ {
+ int balance = QueryBalanceFromMoneyServer(client);
+ if (balance >= amount) return true;
+ }
+
+ m_log.InfoFormat("[MONEY MODULE] AmountCovered: {0} {1}", agentID, amount);
+
+ return false;
+ }
+
+
+ /// Applies the upload charge.
+ /// The agent identifier.
+ /// The amount.
+ /// The text.
+ public void ApplyUploadCharge(UUID agentID, int amount, string text)
+ {
+ ulong regionHandle = GetLocateScene(agentID).RegionInfo.RegionHandle;
+ UUID regionUUID = GetLocateScene(agentID).RegionInfo.RegionID;
+ PayMoneyCharge(agentID, amount, (int)TransactionType.UploadCharge, regionHandle, regionUUID, text);
+
+ m_log.InfoFormat("[MONEY MODULE] ApplyUploadCharge: {0} {1} {2}", agentID, amount, text);
+ }
+
+
+ /// Applies the charge.
+ /// The agent identifier.
+ /// The amount.
+ /// The type.
+ public void ApplyCharge(UUID agentID, int amount, MoneyTransactionType type)
+ {
+ ApplyCharge(agentID, amount, type, string.Empty);
+
+ m_log.InfoFormat("[MONEY MODULE] ApplyCharge: {0} {1} {2}", agentID, amount, type);
+ }
+
+
+ /// Applies the charge.
+ /// The agent identifier.
+ /// The amount.
+ /// The type.
+ /// The text.
+ public void ApplyCharge(UUID agentID, int amount, MoneyTransactionType type, string text)
+ {
+ ulong regionHandle = GetLocateScene(agentID).RegionInfo.RegionHandle;
+ UUID regionUUID = GetLocateScene(agentID).RegionInfo.RegionID;
+ PayMoneyCharge(agentID, amount, (int)type, regionHandle, regionUUID, text);
+
+ m_log.InfoFormat("[MONEY MODULE] ApplyCharge: {0} {1} {2} {3}", agentID, amount, type, text);
+ }
+
+
+ /// Transfers the specified from identifier.
+ /// From identifier.
+ /// To identifier.
+ /// The region handle.
+ /// The amount.
+ /// The type.
+ /// The text.
+ ///
+ ///
+ ///
+ public bool Transfer(UUID fromID, UUID toID, int regionHandle, int amount, MoneyTransactionType type, string text)
+ {
+ return TransferMoney(fromID, toID, amount, (int)type, UUID.Zero, (ulong)regionHandle, UUID.Zero, text);
+ }
+
+
+ /// Transfers the specified from identifier.
+ /// From identifier.
+ /// To identifier.
+ /// The object identifier.
+ /// The amount.
+ /// The type.
+ /// The text.
+ ///
+ ///
+ ///
+ public bool Transfer(UUID fromID, UUID toID, UUID objectID, int amount, MoneyTransactionType type, string text)
+ {
+ SceneObjectPart sceneObj = GetLocatePrim(objectID);
+ if (sceneObj == null) return false;
+
+ ulong regionHandle = sceneObj.ParentGroup.Scene.RegionInfo.RegionHandle;
+ UUID regionUUID = sceneObj.ParentGroup.Scene.RegionInfo.RegionID;
+ return TransferMoney(fromID, toID, amount, (int)type, objectID, (ulong)regionHandle, regionUUID, text);
+ }
+
+
+ // for 0.8.3 over
+ /// Moves the money.
+ /// From agent identifier.
+ /// To agent identifier.
+ /// The amount.
+ /// The text.
+ public void MoveMoney(UUID fromAgentID, UUID toAgentID, int amount, string text)
+ {
+ ForceTransferMoney(fromAgentID, toAgentID, amount, (int)TransactionType.MoveMoney, UUID.Zero, (ulong)0, UUID.Zero, text);
+
+ m_log.InfoFormat("[MONEY MODULE] MoveMoney: {0} {1} {2}", fromAgentID, toAgentID, amount);
+ }
+
+ // for 0.9.1 over
+ /// Moves the money.
+ /// From agent identifier.
+ /// To agent identifier.
+ /// The amount.
+ /// The type.
+ /// The text.
+ ///
+ ///
+ ///
+ public bool MoveMoney(UUID fromAgentID, UUID toAgentID, int amount, MoneyTransactionType type, string text)
+ {
+ bool ret = ForceTransferMoney(fromAgentID, toAgentID, amount, (int)type, UUID.Zero, (ulong)0, UUID.Zero, text);
+
+ m_log.InfoFormat("[MONEY MODULE] MoveMoney: {0} {1} {2} {3}", fromAgentID, toAgentID, amount, type);
+
+ return ret;
+ }
+
+
+
+ /// Called when [new client].
+ /// The client.
+ private void OnNewClient(IClientAPI client)
+ {
+ client.OnEconomyDataRequest += OnEconomyDataRequest;
+ client.OnLogout += ClientClosed;
+
+ client.OnMoneyBalanceRequest += OnMoneyBalanceRequest;
+ client.OnRequestPayPrice += OnRequestPayPrice;
+ client.OnObjectBuy += OnObjectBuy;
+
+ m_log.InfoFormat("[MONEY MODULE] OnNewClient: {0}", client.AgentId);
+ }
+
+
+ /// Called when [make root agent].
+ /// The agent.
+ public void OnMakeRootAgent(ScenePresence agent)
+ {
+ int balance = 0;
+ IClientAPI client = agent.ControllingClient;
+
+ m_enable_server = LoginMoneyServer(agent, out balance);
+ client.SendMoneyBalance(UUID.Zero, true, new byte[0], balance, 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty);
+
+ m_log.InfoFormat("[MONEY MODULE] OnMakeRootAgent: {0} {1}", client.AgentId, balance);
+
+ }
+
+
+ // for OnClientClosed event
+ /// Clients the closed.
+ /// The client.
+ private void ClientClosed(IClientAPI client)
+ {
+ if (m_enable_server && client != null)
+ {
+ LogoffMoneyServer(client);
+
+ m_log.InfoFormat("[MONEY MODULE] ClientClosed: {0}", client.AgentId);
+ }
+ }
+
+
+ // for OnMakeChildAgent event
+ /// Makes the child agent.
+ /// The avatar.
+ private void MakeChildAgent(ScenePresence avatar)
+ {
+ }
+
+
+ // for OnMoneyTransfer event
+ /// Moneys the transfer action.
+ /// The sender.
+ /// The money event.
+ private void MoneyTransferAction(Object sender, EventManager.MoneyTransferArgs moneyEvent)
+ {
+ if (!m_sellEnabled) return;
+
+ // Check the money transaction is necessary.
+ if (moneyEvent.sender == moneyEvent.receiver)
+ {
+ return;
+ }
+
+ UUID receiver = moneyEvent.receiver;
+ // Pay for the object.
+ if (moneyEvent.transactiontype == (int)TransactionType.PayObject)
+ {
+ SceneObjectPart sceneObj = GetLocatePrim(moneyEvent.receiver);
+ if (sceneObj != null)
+ {
+ receiver = sceneObj.OwnerID;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // Before paying for the object, save the object local ID for current transaction.
+ UUID objectID = UUID.Zero;
+ ulong regionHandle = 0;
+ UUID regionUUID = UUID.Zero;
+
+ if (sender is Scene)
+ {
+ Scene scene = (Scene)sender;
+ regionHandle = scene.RegionInfo.RegionHandle;
+ regionUUID = scene.RegionInfo.RegionID;
+
+ if (moneyEvent.transactiontype == (int)TransactionType.PayObject)
+ {
+ objectID = scene.GetSceneObjectPart(moneyEvent.receiver).UUID;
+ }
+ }
+
+ TransferMoney(moneyEvent.sender, receiver, moneyEvent.amount, moneyEvent.transactiontype, objectID, regionHandle, regionUUID, "OnMoneyTransfer event");
+ return;
+ }
+
+
+ // for OnValidateLandBuy event
+ /// Validates the land buy.
+ /// The sender.
+ /// The land buy event.
+ private void ValidateLandBuy(Object sender, EventManager.LandBuyArgs landBuyEvent)
+ {
+ IClientAPI senderClient = GetLocateClient(landBuyEvent.agentId);
+ if (senderClient != null)
+ {
+ int balance = QueryBalanceFromMoneyServer(senderClient);
+ if (balance >= landBuyEvent.parcelPrice)
+ {
+ lock (landBuyEvent)
+ {
+ landBuyEvent.economyValidated = true;
+ }
+ }
+ }
+ return;
+ }
+
+
+ // for LandBuy even
+ /// Processes the land buy.
+ /// The sender.
+ /// The land buy event.
+ private void processLandBuy(Object sender, EventManager.LandBuyArgs landBuyEvent)
+ {
+ if (!m_sellEnabled) return;
+
+ lock (landBuyEvent)
+ {
+ if (landBuyEvent.economyValidated == true && landBuyEvent.transactionID == 0)
+ {
+ landBuyEvent.transactionID = Util.UnixTimeSinceEpoch();
+
+ ulong parcelID = (ulong)landBuyEvent.parcelLocalID;
+ UUID regionUUID = UUID.Zero;
+ if (sender is Scene) regionUUID = ((Scene)sender).RegionInfo.RegionID;
+
+ if (TransferMoney(landBuyEvent.agentId, landBuyEvent.parcelOwnerID,
+ landBuyEvent.parcelPrice, (int)TransactionType.LandSale, regionUUID, parcelID, regionUUID, "Land Purchase"))
+ {
+ landBuyEvent.amountDebited = landBuyEvent.parcelPrice;
+ }
+ }
+ }
+ return;
+ }
+
+
+ // for OnObjectBuy event
+ /// Called when [object buy].
+ /// The remote client.
+ /// The agent identifier.
+ /// The session identifier.
+ /// The group identifier.
+ /// The category identifier.
+ /// The local identifier.
+ /// Type of the sale.
+ /// The sale price.
+ public void OnObjectBuy(IClientAPI remoteClient, UUID agentID, UUID sessionID, UUID groupID, UUID categoryID, uint localID, byte saleType, int salePrice)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: OnObjectBuy: agent = {0}, {1}", agentID, remoteClient.AgentId);
+
+ // Handle the parameters error.
+ if (!m_sellEnabled) return;
+ if (remoteClient == null || salePrice < 0) return;
+
+ // Get the balance from money server.
+ int balance = QueryBalanceFromMoneyServer(remoteClient);
+ if (balance < salePrice)
+ {
+ remoteClient.SendAgentAlertMessage("Unable to buy now. You don't have sufficient funds", false);
+ m_log.InfoFormat("[MONEY MODULE]: OnObjectBuy: agent = {0}, balance = {1}, salePrice = {2}", agentID, balance, salePrice);
+ return;
+ }
+
+ Scene scene = GetLocateScene(remoteClient.AgentId);
+ if (scene != null)
+ {
+ SceneObjectPart sceneObj = scene.GetSceneObjectPart(localID);
+ if (sceneObj != null)
+ {
+ IBuySellModule mod = scene.RequestModuleInterface();
+ if (mod != null)
+ {
+ UUID receiverId = sceneObj.OwnerID;
+ ulong regionHandle = sceneObj.RegionHandle;
+ UUID regionUUID = sceneObj.RegionID;
+ bool ret = false;
+ //
+ if (salePrice >= 0)
+ {
+ if (!string.IsNullOrEmpty(m_moneyServURL))
+ {
+ ret = TransferMoney(remoteClient.AgentId, receiverId, salePrice,
+ (int)TransactionType.PayObject, sceneObj.UUID, regionHandle, regionUUID, "Object Buy");
+ }
+ else if (salePrice == 0)
+ { // amount is 0 with No Money Server
+ ret = true;
+ }
+ }
+ if (ret)
+ {
+ mod.BuyObject(remoteClient, categoryID, localID, saleType, salePrice);
+ }
+ }
+ }
+ else
+ {
+ remoteClient.SendAgentAlertMessage("Unable to buy now. The object was not found", false);
+ m_log.InfoFormat("[MONEY MODULE]: OnObjectBuy: Unable to buy now. The object was not found");
+ return;
+ }
+ }
+ return;
+ }
+
+
+ ///
+ /// Sends the the stored money balance to the client
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void OnMoneyBalanceRequest(IClientAPI client, UUID agentID, UUID SessionID, UUID TransactionID)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: OnMoneyBalanceRequest:");
+
+ if (client.AgentId == agentID && client.SessionId == SessionID)
+ {
+ int balance = 0;
+ //
+ if (m_enable_server)
+ {
+ balance = QueryBalanceFromMoneyServer(client);
+ }
+
+ client.SendMoneyBalance(TransactionID, true, new byte[0], balance, 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty);
+ }
+ else
+ {
+ client.SendAlertMessage("Unable to send your money balance");
+ }
+ }
+
+
+ /// Called when [request pay price].
+ /// The client.
+ /// The object identifier.
+ private void OnRequestPayPrice(IClientAPI client, UUID objectID)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: OnRequestPayPrice:");
+
+ Scene scene = GetLocateScene(client.AgentId);
+ if (scene == null) return;
+ SceneObjectPart sceneObj = scene.GetSceneObjectPart(objectID);
+ if (sceneObj == null) return;
+ SceneObjectGroup group = sceneObj.ParentGroup;
+ SceneObjectPart root = group.RootPart;
+
+ client.SendPayPrice(objectID, root.PayPrice);
+ }
+
+
+ //
+ //private void OnEconomyDataRequest(UUID agentId)
+ /// Called when [economy data request].
+ /// The user.
+ private void OnEconomyDataRequest(IClientAPI user)
+ {
+ if (user != null)
+ {
+ if (m_enable_server || string.IsNullOrEmpty(m_moneyServURL))
+ {
+ //Scene s = GetLocateScene(user.AgentId);
+ Scene s = (Scene)user.Scene;
+ user.SendEconomyData(EnergyEfficiency, s.RegionInfo.ObjectCapacity, ObjectCount, PriceEnergyUnit, PriceGroupCreate,
+ PriceObjectClaim, PriceObjectRent, PriceObjectScaleFactor, PriceParcelClaim, PriceParcelClaimFactor,
+ PriceParcelRent, PricePublicObjectDecay, PricePublicObjectDelete, PriceRentLight, PriceUpload,
+ TeleportMinPrice, TeleportPriceExponent);
+ }
+ }
+ }
+
+
+
+ // "OnMoneyTransfered" RPC from MoneyServer
+ /// Called when [money transfered handler].
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse OnMoneyTransferedHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: OnMoneyTransferedHandler:");
+
+ bool ret = false;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+ if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && requestParam.Contains("clientSecureSessionID"))
+ {
+ UUID clientUUID = UUID.Zero;
+ UUID.TryParse((string)requestParam["clientUUID"], out clientUUID);
+
+ if (clientUUID != UUID.Zero)
+ {
+ IClientAPI client = GetLocateClient(clientUUID);
+ string sessionid = (string)requestParam["clientSessionID"];
+ string secureid = (string)requestParam["clientSecureSessionID"];
+ if (client != null && secureid == client.SecureSessionId.ToString() && (sessionid == UUID.Zero.ToString() || sessionid == client.SessionId.ToString()))
+ {
+ if (requestParam.Contains("transactionType") && requestParam.Contains("objectID") && requestParam.Contains("amount"))
+ {
+ m_log.InfoFormat("[MONEY MODULE]: OnMoneyTransferedHandler: type = {0}", requestParam["transactionType"]);
+
+ // Pay for the object.
+ if ((int)requestParam["transactionType"] == (int)TransactionType.PayObject)
+ {
+ // Send notify to the client(viewer) for Money Event Trigger.
+ ObjectPaid handlerOnObjectPaid = OnObjectPaid;
+ if (handlerOnObjectPaid != null)
+ {
+ UUID objectID = UUID.Zero;
+ UUID.TryParse((string)requestParam["objectID"], out objectID);
+ handlerOnObjectPaid(objectID, clientUUID, (int)requestParam["amount"]); // call Script Engine for LSL money()
+ }
+ ret = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Send the response to money server.
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["success"] = ret;
+
+ if (!ret)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: OnMoneyTransferedHandler: Transaction is failed. MoneyServer will rollback");
+ }
+ resp.Value = paramTable;
+
+ return resp;
+ }
+
+
+ // "UpdateBalance" RPC from MoneyServer or Script
+ /// Balances the update handler.
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse BalanceUpdateHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+
+ bool ret = false;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+ if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && requestParam.Contains("clientSecureSessionID"))
+ {
+ UUID clientUUID = UUID.Zero;
+ UUID.TryParse((string)requestParam["clientUUID"], out clientUUID);
+ //
+ if (clientUUID != UUID.Zero)
+ {
+ IClientAPI client = GetLocateClient(clientUUID);
+ string sessionid = (string)requestParam["clientSessionID"];
+ string secureid = (string)requestParam["clientSecureSessionID"];
+ if (client != null && secureid == client.SecureSessionId.ToString() && (sessionid == UUID.Zero.ToString() || sessionid == client.SessionId.ToString()))
+ {
+ //
+ if (requestParam.Contains("Balance"))
+ {
+ // Send notify to the client.
+ string msg = "";
+ if (requestParam.Contains("Message")) msg = (string)requestParam["Message"];
+ client.SendMoneyBalance(UUID.Random(), true, Utils.StringToBytes(msg), (int)requestParam["Balance"],
+ 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty);
+ // Dialog
+ if (msg != "")
+ {
+ Scene scene = (Scene)client.Scene;
+ IDialogModule dlg = scene.RequestModuleInterface();
+ dlg.SendAlertToUser(client.AgentId, msg);
+ }
+ ret = true;
+ }
+ }
+ }
+ }
+ }
+
+
+ // Send the response to money server.
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["success"] = ret;
+
+ if (!ret)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: BalanceUpdateHandler: Cannot update client balance from MoneyServer");
+ }
+ resp.Value = paramTable;
+
+ return resp;
+ }
+
+
+ // "UserAlert" RPC from Script
+ /// Users the alert handler.
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse UserAlertHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: UserAlertHandler:");
+
+ bool ret = false;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+ if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && requestParam.Contains("clientSecureSessionID"))
+ {
+ UUID clientUUID = UUID.Zero;
+ UUID.TryParse((string)requestParam["clientUUID"], out clientUUID);
+ //
+ if (clientUUID != UUID.Zero)
+ {
+ IClientAPI client = GetLocateClient(clientUUID);
+ string sessionid = (string)requestParam["clientSessionID"];
+ string secureid = (string)requestParam["clientSecureSessionID"];
+ if (client != null && secureid == client.SecureSessionId.ToString() && (sessionid == UUID.Zero.ToString() || sessionid == client.SessionId.ToString()))
+ {
+ if (requestParam.Contains("Description"))
+ {
+ string description = (string)requestParam["Description"];
+ // Show the notice dialog with money server message.
+ GridInstantMessage gridMsg = new GridInstantMessage(null, UUID.Zero, "MonyServer", new UUID(clientUUID.ToString()),
+ (byte)InstantMessageDialog.MessageFromAgent, description, false, new Vector3());
+ client.SendInstantMessage(gridMsg);
+ ret = true;
+ }
+ }
+ }
+ }
+ }
+
+ // Send the response to money server.
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["success"] = ret;
+
+ resp.Value = paramTable;
+ return resp;
+ }
+
+
+ // "GetBalance" RPC from Script
+ /// Gets the balance handler.
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse GetBalanceHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+
+ bool ret = false;
+ int balance = -1;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+ if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && requestParam.Contains("clientSecureSessionID"))
+ {
+ UUID clientUUID = UUID.Zero;
+ UUID.TryParse((string)requestParam["clientUUID"], out clientUUID);
+ //
+ if (clientUUID != UUID.Zero)
+ {
+ IClientAPI client = GetLocateClient(clientUUID);
+ string sessionid = (string)requestParam["clientSessionID"];
+ string secureid = (string)requestParam["clientSecureSessionID"];
+ if (client != null && secureid == client.SecureSessionId.ToString() && (sessionid == UUID.Zero.ToString() || sessionid == client.SessionId.ToString()))
+ {
+ balance = QueryBalanceFromMoneyServer(client);
+ }
+ }
+ }
+ }
+
+ // Send the response to caller.
+ if (balance < 0)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: GetBalanceHandler: GetBalance transaction is failed");
+ ret = false;
+ }
+
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["success"] = ret;
+ paramTable["balance"] = balance;
+ resp.Value = paramTable;
+
+ return resp;
+ }
+
+
+ // "AddBankerMoney" RPC from Script
+ /// Adds the banker money handler.
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse AddBankerMoneyHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: AddBankerMoneyHandler:");
+
+ bool ret = false;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+
+ if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && requestParam.Contains("clientSecureSessionID"))
+ {
+ UUID bankerUUID = UUID.Zero;
+ UUID.TryParse((string)requestParam["clientUUID"], out bankerUUID);
+ //
+ if (bankerUUID != UUID.Zero)
+ {
+ IClientAPI client = GetLocateClient(bankerUUID);
+ string sessionid = (string)requestParam["clientSessionID"];
+ string secureid = (string)requestParam["clientSecureSessionID"];
+ if (client != null && secureid == client.SecureSessionId.ToString() && (sessionid == UUID.Zero.ToString() || sessionid == client.SessionId.ToString()))
+ {
+ if (requestParam.Contains("amount"))
+ {
+ Scene scene = (Scene)client.Scene;
+ int amount = (int)requestParam["amount"];
+ ulong regionHandle = scene.RegionInfo.RegionHandle;
+ UUID regionUUID = scene.RegionInfo.RegionID;
+ ret = AddBankerMoney(bankerUUID, amount, regionHandle, regionUUID);
+
+ if (m_use_web_settle && m_settle_user)
+ {
+ ret = true;
+ IDialogModule dlg = scene.RequestModuleInterface();
+ if (dlg != null)
+ {
+ dlg.SendUrlToUser(bankerUUID, "SYSTEM", UUID.Zero, UUID.Zero, false, m_settle_message, m_settle_url);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret) m_log.ErrorFormat("[MONEY MODULE]: AddBankerMoneyHandler: Add Banker Money transaction is failed");
+
+ // Send the response to caller.
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["settle"] = false;
+ paramTable["success"] = ret;
+
+ if (m_use_web_settle && m_settle_user) paramTable["settle"] = true;
+ resp.Value = paramTable;
+
+ return resp;
+ }
+
+
+ // "SendMoney" RPC from Script
+ /// Sends the money handler.
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse SendMoneyHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ bool ret = false;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+ if (requestParam.Contains("agentUUID") && requestParam.Contains("secretAccessCode"))
+ {
+ UUID agentUUID = UUID.Zero;
+ UUID.TryParse((string)requestParam["agentUUID"], out agentUUID);
+
+ if (agentUUID != UUID.Zero)
+ {
+ if (requestParam.Contains("amount"))
+ {
+ int amount = (int)requestParam["amount"];
+ int type = -1;
+ if (requestParam.Contains("type")) type = (int)requestParam["type"];
+ string secretCode = (string)requestParam["secretAccessCode"];
+ string scriptIP = remoteClient.Address.ToString();
+
+ MD5 md5 = MD5.Create();
+ byte[] code = md5.ComputeHash(ASCIIEncoding.Default.GetBytes(secretCode + "_" + scriptIP));
+ string hash = BitConverter.ToString(code).ToLower().Replace("-", "");
+ m_log.InfoFormat("[MONEY MODULE]: SendMoneyHandler: SecretCode: {0} + {1} = {2}", secretCode, scriptIP, hash);
+ ret = SendMoneyTo(agentUUID, amount, type, hash);
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: SendMoneyHandler: amount is missed");
+ }
+ }
+ else
+ {
+ if (!requestParam.Contains("agentUUID"))
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: SendMoneyHandler: agentUUID is missed");
+ }
+ if (!requestParam.Contains("secretAccessCode"))
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: SendMoneyHandler: secretAccessCode is missed");
+ }
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: SendMoneyHandler: Params count is under 0");
+ }
+
+ if (!ret) m_log.ErrorFormat("[MONEY MODULE]: SendMoneyHandler: Send Money transaction is failed");
+
+ // Send the response to caller.
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["success"] = ret;
+
+ resp.Value = paramTable;
+
+ return resp;
+ }
+
+
+ // "MoveMoney" RPC from Script
+ /// Moves the money handler.
+ /// The request.
+ /// The remote client.
+ ///
+ ///
+ ///
+ public XmlRpcResponse MoveMoneyHandler(XmlRpcRequest request, IPEndPoint remoteClient)
+ {
+ bool ret = false;
+
+ if (request.Params.Count > 0)
+ {
+ Hashtable requestParam = (Hashtable)request.Params[0];
+ if ((requestParam.Contains("fromUUID") || requestParam.Contains("toUUID")) && requestParam.Contains("secretAccessCode"))
+ {
+ UUID fromUUID = UUID.Zero;
+ UUID toUUID = UUID.Zero; // UUID.Zero means System
+ if (requestParam.Contains("fromUUID")) UUID.TryParse((string)requestParam["fromUUID"], out fromUUID);
+ if (requestParam.Contains("toUUID")) UUID.TryParse((string)requestParam["toUUID"], out toUUID);
+
+ if (requestParam.Contains("amount"))
+ {
+ int amount = (int)requestParam["amount"];
+ string secretCode = (string)requestParam["secretAccessCode"];
+ string scriptIP = remoteClient.Address.ToString();
+
+ MD5 md5 = MD5.Create();
+ byte[] code = md5.ComputeHash(ASCIIEncoding.Default.GetBytes(secretCode + "_" + scriptIP));
+ string hash = BitConverter.ToString(code).ToLower().Replace("-", "");
+ m_log.InfoFormat("[MONEY MODULE]: MoveMoneyHandler: SecretCode: {0} + {1} = {2}", secretCode, scriptIP, hash);
+ ret = MoveMoneyFromTo(fromUUID, toUUID, amount, hash);
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyHandler: amount is missed");
+ }
+ }
+ else
+ {
+ if (!requestParam.Contains("fromUUID") && !requestParam.Contains("toUUID"))
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyHandler: fromUUID and toUUID are missed");
+ }
+ if (!requestParam.Contains("secretAccessCode"))
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyHandler: secretAccessCode is missed");
+ }
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyHandler: Params count is under 0");
+ }
+
+ if (!ret) m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyHandler: Move Money transaction is failed");
+
+ // Send the response to caller.
+ XmlRpcResponse resp = new XmlRpcResponse();
+ Hashtable paramTable = new Hashtable();
+ paramTable["success"] = ret;
+
+ resp.Value = paramTable;
+
+ return resp;
+ }
+
+
+ ///
+ /// Transfer the money from one user to another. Need to notify money server to update.
+ ///
+ ///
+ /// The amount of money.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool TransferMoney(UUID sender, UUID receiver, int amount, int type, UUID objectID, ulong regionHandle, UUID regionUUID, string description)
+ {
+ bool ret = false;
+ IClientAPI senderClient = GetLocateClient(sender);
+
+ // Handle the illegal transaction.
+ // receiverClient could be null.
+ if (senderClient == null)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: TransferMoney: Client {0} not found", sender.ToString());
+ return false;
+ }
+
+ if (QueryBalanceFromMoneyServer(senderClient) < amount)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: TransferMoney: No insufficient balance in client [{0}]", sender.ToString());
+ return false;
+ }
+
+ if (m_enable_server)
+ {
+ string objName = string.Empty;
+ SceneObjectPart sceneObj = GetLocatePrim(objectID);
+ if (sceneObj != null) objName = sceneObj.Name;
+
+ // Fill parameters for money transfer XML-RPC.
+ Hashtable paramTable = new Hashtable();
+ paramTable["senderID"] = sender.ToString();
+ paramTable["receiverID"] = receiver.ToString();
+ paramTable["senderSessionID"] = senderClient.SessionId.ToString();
+ paramTable["senderSecureSessionID"] = senderClient.SecureSessionId.ToString();
+ paramTable["transactionType"] = type;
+ paramTable["objectID"] = objectID.ToString();
+ paramTable["objectName"] = objName;
+ paramTable["regionHandle"] = regionHandle.ToString();
+ paramTable["regionUUID"] = regionUUID.ToString();
+ paramTable["amount"] = amount;
+ paramTable["description"] = description;
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "TransferMoney");
+
+ // Handle the return values from Money Server.
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: TransferMoney: Can not money transfer request from [{0}] to [{1}]", sender.ToString(), receiver.ToString());
+ }
+
+ return ret;
+ }
+
+
+ ///
+ /// Force transfer the money from one user to another.
+ /// This function does not check sender login.
+ /// Need to notify money server to update.
+ ///
+ ///
+ /// The amount of money.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool ForceTransferMoney(UUID sender, UUID receiver, int amount, int type, UUID objectID, ulong regionHandle, UUID regionUUID, string description)
+ {
+ bool ret = false;
+
+ if (m_enable_server)
+ {
+ string objName = string.Empty;
+ SceneObjectPart sceneObj = GetLocatePrim(objectID);
+ if (sceneObj != null) objName = sceneObj.Name;
+
+ // Fill parameters for money transfer XML-RPC.
+ Hashtable paramTable = new Hashtable();
+ paramTable["senderID"] = sender.ToString();
+ paramTable["receiverID"] = receiver.ToString();
+ paramTable["transactionType"] = type;
+ paramTable["objectID"] = objectID.ToString();
+ paramTable["objectName"] = objName;
+ paramTable["regionHandle"] = regionHandle.ToString();
+ paramTable["regionUUID"] = regionUUID.ToString();
+ paramTable["amount"] = amount;
+ paramTable["description"] = description;
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "ForceTransferMoney");
+
+ // Handle the return values from Money Server.
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: ForceTransferMoney: Can not money force transfer request from [{0}] to [{1}]", sender.ToString(), receiver.ToString());
+ }
+
+ return ret;
+ }
+
+
+ ///
+ /// Send the money to avatar. Need to notify money server to update.
+ ///
+ ///
+ /// The amount of money.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool SendMoneyTo(UUID avatarID, int amount, int type, string secretCode)
+ {
+ bool ret = false;
+
+ if (m_enable_server)
+ {
+ // Fill parameters for money transfer XML-RPC.
+ if (type < 0) type = (int)TransactionType.ReferBonus;
+ Hashtable paramTable = new Hashtable();
+ paramTable["receiverID"] = avatarID.ToString();
+ paramTable["transactionType"] = type;
+ paramTable["amount"] = amount;
+ paramTable["secretAccessCode"] = secretCode;
+ paramTable["description"] = "Bonus to Avatar";
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "SendMoney");
+
+ // Handle the return values from Money Server.
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: SendMoneyTo: Fail Message is {0}", resultTable["message"]);
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: SendMoneyTo: Money Server is not responce");
+ }
+
+ return ret;
+ }
+
+
+ ///
+ /// Move the money from avatar to other avatar. Need to notify money server to update.
+ ///
+ ///
+ /// The amount of money.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool MoveMoneyFromTo(UUID senderID, UUID receiverID, int amount, string secretCode)
+ {
+ bool ret = false;
+
+ if (m_enable_server)
+ {
+ // Fill parameters for money transfer XML-RPC.
+ Hashtable paramTable = new Hashtable();
+ paramTable["senderID"] = senderID.ToString();
+ paramTable["receiverID"] = receiverID.ToString();
+ paramTable["transactionType"] = (int)TransactionType.MoveMoney;
+ paramTable["amount"] = amount;
+ paramTable["secretAccessCode"] = secretCode;
+ paramTable["description"] = "Move Money";
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "MoveMoney");
+
+ // Handle the return values from Money Server.
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyFromTo: Fail Message is {0}", resultTable["message"]);
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: MoveMoneyFromTo: Money Server is not responce");
+ }
+
+ return ret;
+ }
+
+
+ ///
+ /// Add the money to banker avatar. Need to notify money server to update.
+ ///
+ ///
+ /// The amount of money.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool AddBankerMoney(UUID bankerID, int amount, ulong regionHandle, UUID regionUUID)
+ {
+ bool ret = false;
+ m_settle_user = false;
+
+ if (m_enable_server)
+ {
+ // Fill parameters for money transfer XML-RPC.
+ Hashtable paramTable = new Hashtable();
+ paramTable["bankerID"] = bankerID.ToString();
+ paramTable["transactionType"] = (int)TransactionType.BuyMoney;
+ paramTable["amount"] = amount;
+ paramTable["regionHandle"] = regionHandle.ToString();
+ paramTable["regionUUID"] = regionUUID.ToString();
+ paramTable["description"] = "Add Money to Avatar";
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "AddBankerMoney");
+
+ // Handle the return values from Money Server.
+ if (resultTable != null)
+ {
+ if (resultTable.Contains("success") && (bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ else
+ {
+ if (resultTable.Contains("banker"))
+ {
+ m_settle_user = !(bool)resultTable["banker"]; // If avatar is not banker, Web Settlement is used.
+ if (m_settle_user && m_use_web_settle) m_log.ErrorFormat("[MONEY MODULE]: AddBankerMoney: Avatar is not Banker. Web Settlemrnt is used.");
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: AddBankerMoney: Fail Message {0}", resultTable["message"]);
+ }
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: AddBankerMoney: Money Server is not responce");
+ }
+
+ return ret;
+ }
+
+
+ ///
+ /// Pay the money of charge.
+ ///
+ ///
+ /// The amount of money.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool PayMoneyCharge(UUID sender, int amount, int type, ulong regionHandle, UUID regionUUID, string description)
+ {
+ bool ret = false;
+ IClientAPI senderClient = GetLocateClient(sender);
+
+ // Handle the illegal transaction.
+ // receiverClient could be null.
+ if (senderClient == null)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: PayMoneyCharge: Client {0} is not found", sender.ToString());
+ return false;
+ }
+
+ if (QueryBalanceFromMoneyServer(senderClient) < amount)
+ {
+ m_log.InfoFormat("[MONEY MODULE]: PayMoneyCharge: No insufficient balance in client [{0}]", sender.ToString());
+ return false;
+ }
+
+ if (m_enable_server)
+ {
+ // Fill parameters for money transfer XML-RPC.
+ Hashtable paramTable = new Hashtable();
+ paramTable["senderID"] = sender.ToString();
+ paramTable["senderSessionID"] = senderClient.SessionId.ToString();
+ paramTable["senderSecureSessionID"] = senderClient.SecureSessionId.ToString();
+ paramTable["transactionType"] = type;
+ paramTable["amount"] = amount;
+ paramTable["regionHandle"] = regionHandle.ToString();
+ paramTable["regionUUID"] = regionUUID.ToString();
+ paramTable["description"] = description;
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "PayMoneyCharge");
+
+ // Handle the return values from Money Server.
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: PayMoneyCharge: Can not pay money of charge request from [{0}]", sender.ToString());
+ }
+
+ return ret;
+ }
+
+
+ /// Queries the balance from money server.
+ /// The client.
+ ///
+ ///
+ ///
+ private int QueryBalanceFromMoneyServer(IClientAPI client)
+ {
+ int balance = 0;
+
+ if (client != null)
+ {
+ if (m_enable_server)
+ {
+ Hashtable paramTable = new Hashtable();
+ paramTable["clientUUID"] = client.AgentId.ToString();
+ paramTable["clientSessionID"] = client.SessionId.ToString();
+ paramTable["clientSecureSessionID"] = client.SecureSessionId.ToString();
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "GetBalance");
+
+ // Handle the return result
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ balance = (int)resultTable["clientBalance"];
+ m_log.InfoFormat("[MONEY MODULE]: QueryBalanceFromMoneyServer: Balance {0}", balance);
+ }
+ }
+ }
+ else
+ {
+ if (m_moneyServer.ContainsKey(client.AgentId))
+ {
+ balance = m_moneyServer[client.AgentId];
+ m_log.InfoFormat("[MONEY MODULE]: QueryBalanceFromMoneyServer: Balance {0}", balance);
+ }
+ }
+ }
+
+ return balance;
+ }
+
+
+ ///
+ /// Login the money server when the new client login.
+ ///
+ ///
+ /// Indicate user ID of the new client.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool LoginMoneyServer(ScenePresence avatar, out int balance)
+ {
+ balance = 0;
+ bool ret = false;
+ bool isNpc = avatar.IsNPC;
+
+ IClientAPI client = avatar.ControllingClient;
+
+ if (!string.IsNullOrEmpty(m_moneyServURL))
+ {
+ Scene scene = (Scene)client.Scene;
+ string userName = string.Empty;
+
+ // Get the username for the login user.
+ if (client.Scene is Scene)
+ {
+ if (scene != null)
+ {
+ UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, client.AgentId);
+ if (account != null)
+ {
+ userName = account.FirstName + " " + account.LastName;
+ m_log.InfoFormat("[MONEY MODULE]: LoginMoneyServer: User {0} logged in.", userName);
+ }
+ }
+ }
+
+
+ // User Universal Identifer for Grid Avatar, HG Avatar or NPC
+ string universalID = string.Empty;
+ string firstName = string.Empty;
+ string lastName = string.Empty;
+ string serverURL = string.Empty;
+ int avatarType = (int)AvatarType.LOCAL_AVATAR;
+ int avatarClass = (int)AvatarType.LOCAL_AVATAR;
+
+ AgentCircuitData agent = scene.AuthenticateHandler.GetAgentCircuitData(client.AgentId);
+
+ if (agent != null)
+ {
+ universalID = Util.ProduceUserUniversalIdentifier(agent);
+ if (!String.IsNullOrEmpty(universalID))
+ {
+ UUID uuid;
+ string tmp;
+ Util.ParseUniversalUserIdentifier(universalID, out uuid, out serverURL, out firstName, out lastName, out tmp);
+ }
+ // if serverURL is empty, avatar is a NPC
+ if (isNpc || String.IsNullOrEmpty(serverURL))
+ {
+ avatarType = (int)AvatarType.NPC_AVATAR;
+ }
+ //
+ if ((agent.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 || String.IsNullOrEmpty(userName))
+ {
+ avatarType = (int)AvatarType.HG_AVATAR;
+ }
+ }
+ if (String.IsNullOrEmpty(userName))
+ {
+ userName = firstName + " " + lastName;
+ m_log.InfoFormat("[MONEY MODULE]: LoginMoneyServer: User {0} logged in.", userName);
+ }
+
+ //
+ avatarClass = avatarType;
+ if (avatarType == (int)AvatarType.NPC_AVATAR) return false;
+ if (avatarType == (int)AvatarType.HG_AVATAR) avatarClass = m_hg_avatarClass;
+
+ //
+ // Login the Money Server.
+ Hashtable paramTable = new Hashtable();
+ paramTable["openSimServIP"] = scene.RegionInfo.ServerURI.Replace(scene.RegionInfo.InternalEndPoint.Port.ToString(),
+ scene.RegionInfo.HttpPort.ToString());
+ paramTable["avatarType"] = avatarType.ToString();
+ paramTable["avatarClass"] = avatarClass.ToString();
+ paramTable["userName"] = userName;
+ paramTable["universalID"] = universalID;
+ paramTable["clientUUID"] = client.AgentId.ToString();
+ paramTable["clientSessionID"] = client.SessionId.ToString();
+ paramTable["clientSecureSessionID"] = client.SecureSessionId.ToString();
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "ClientLogin");
+
+ // Handle the return result
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ balance = (int)resultTable["clientBalance"];
+ m_log.InfoFormat("[MONEY MODULE]: LoginMoneyServer: Client [{0}] login Money Server {1}", client.AgentId.ToString(), m_moneyServURL);
+ ret = true;
+ }
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: LoginMoneyServer: Unable to login Money Server {0} for client [{1}]", m_moneyServURL, client.AgentId.ToString());
+ }
+ else m_log.ErrorFormat("[MONEY MODULE]: LoginMoneyServer: Money Server is not available!!");
+
+
+ // Notifies the Viewer of the setting.
+ if (ret || string.IsNullOrEmpty(m_moneyServURL))
+ {
+ OnEconomyDataRequest(client);
+ }
+
+ return ret;
+ }
+
+
+ ///
+ /// Log off from the money server.
+ ///
+ ///
+ /// Indicate user ID of the new client.
+ ///
+ ///
+ /// return true, if successfully.
+ ///
+ private bool LogoffMoneyServer(IClientAPI client)
+ {
+ bool ret = false;
+
+ if (!string.IsNullOrEmpty(m_moneyServURL))
+ {
+ // Log off from the Money Server.
+ Hashtable paramTable = new Hashtable();
+ paramTable["clientUUID"] = client.AgentId.ToString();
+ paramTable["clientSessionID"] = client.SessionId.ToString();
+ paramTable["clientSecureSessionID"] = client.SecureSessionId.ToString();
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "ClientLogout");
+ // Handle the return result
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ ret = true;
+ }
+ }
+ m_log.InfoFormat("[MONEY MODULE]: LogoffMoneyServer: Client [{0}] logoff Money Server {1}", client.AgentId.ToString(), m_moneyServURL);
+ }
+
+ return ret;
+ }
+
+
+ //
+ /// Gets the transaction information.
+ /// The client.
+ /// The transaction identifier.
+ ///
+ ///
+ ///
+ private EventManager.MoneyTransferArgs GetTransactionInfo(IClientAPI client, string transactionID)
+ {
+ EventManager.MoneyTransferArgs args = null;
+
+ if (m_enable_server)
+ {
+ Hashtable paramTable = new Hashtable();
+ paramTable["clientUUID"] = client.AgentId.ToString();
+ paramTable["clientSessionID"] = client.SessionId.ToString();
+ paramTable["clientSecureSessionID"] = client.SecureSessionId.ToString();
+ paramTable["transactionID"] = transactionID;
+
+ // Generate the request for transfer.
+ Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "GetTransaction");
+
+ // Handle the return result
+ if (resultTable != null && resultTable.Contains("success"))
+ {
+ if ((bool)resultTable["success"] == true)
+ {
+ int amount = (int)resultTable["amount"];
+ int type = (int)resultTable["type"];
+ string desc = (string)resultTable["description"];
+ UUID sender = UUID.Zero;
+ UUID recver = UUID.Zero;
+ UUID.TryParse((string)resultTable["sender"], out sender);
+ UUID.TryParse((string)resultTable["receiver"], out recver);
+ args = new EventManager.MoneyTransferArgs(sender, recver, amount, type, desc);
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: GetTransactionInfo: GetTransactionInfo: Fail to Request. {0}", (string)resultTable["description"]);
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: GetTransactionInfo: Invalid Response");
+ }
+ }
+ else
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: GetTransactionInfo: Invalid Money Server URL");
+ }
+
+ return args;
+ }
+
+
+ ///
+ /// Generic XMLRPC client abstraction
+ ///
+ /// Hashtable containing parameters to the method
+ /// Method to invoke
+ /// Hashtable with success=>bool and other values
+ private Hashtable genericCurrencyXMLRPCRequest(Hashtable reqParams, string method)
+ {
+ if (reqParams.Count <= 0 || string.IsNullOrEmpty(method)) return null;
+
+ if (m_checkServerCert)
+ {
+ if (!m_moneyServURL.StartsWith("https://"))
+ {
+ m_log.InfoFormat("[MONEY MODULE]: genericCurrencyXMLRPCRequest: CheckServerCert is true, but protocol is not HTTPS. Please check INI file");
+ //return null;
+ }
+ }
+ else
+ {
+ if (!m_moneyServURL.StartsWith("https://") && !m_moneyServURL.StartsWith("http://"))
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: genericCurrencyXMLRPCRequest: Invalid Money Server URL: {0}", m_moneyServURL);
+ return null;
+ }
+ }
+
+ ArrayList arrayParams = new ArrayList();
+ arrayParams.Add(reqParams);
+ XmlRpcResponse moneyServResp = null;
+ try
+ {
+ NSLXmlRpcRequest moneyModuleReq = new NSLXmlRpcRequest(method, arrayParams);
+ //moneyServResp = moneyModuleReq.certSend(m_moneyServURL, m_cert, m_checkServerCert, MONEYMODULE_REQUEST_TIMEOUT);
+ moneyServResp = moneyModuleReq.certSend(m_moneyServURL, m_certVerify, m_checkServerCert, MONEYMODULE_REQUEST_TIMEOUT);
+ }
+ catch (Exception ex)
+ {
+ m_log.ErrorFormat("[MONEY MODULE]: genericCurrencyXMLRPCRequest: Unable to connect to Money Server {0}", m_moneyServURL);
+ m_log.ErrorFormat("[MONEY MODULE]: genericCurrencyXMLRPCRequest: {0}", ex);
+
+ Hashtable ErrorHash = new Hashtable();
+ ErrorHash["success"] = false;
+ ErrorHash["errorMessage"] = "Unable to manage your money at this time. Purchases may be unavailable";
+ ErrorHash["errorURI"] = "";
+ return ErrorHash;
+ }
+
+ if (moneyServResp == null || moneyServResp.IsFault)
+ {
+ Hashtable ErrorHash = new Hashtable();
+ ErrorHash["success"] = false;
+ ErrorHash["errorMessage"] = "Unable to manage your money at this time. Purchases may be unavailable";
+ ErrorHash["errorURI"] = "";
+ return ErrorHash;
+ }
+
+ Hashtable moneyRespData = (Hashtable)moneyServResp.Value;
+ return moneyRespData;
+ }
+
+
+ /// Locates a IClientAPI for the client specified
+ ///
+ ///
+ ///
+ private IClientAPI GetLocateClient(UUID AgentID)
+ {
+ IClientAPI client = null;
+
+ lock (m_sceneList)
+ {
+ if (m_sceneList.Count > 0)
+ {
+ foreach (Scene _scene in m_sceneList.Values)
+ {
+ ScenePresence tPresence = (ScenePresence)_scene.GetScenePresence(AgentID);
+ if (tPresence != null && !tPresence.IsChildAgent)
+ {
+ IClientAPI rclient = tPresence.ControllingClient;
+ if (rclient != null)
+ {
+ client = rclient;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ m_log.DebugFormat("[MONEY MODULE]: GetLocateClient: {0}", client);
+
+ return client;
+ }
+
+
+ /// Gets the locate scene.
+ /// The agent identifier.
+ ///
+ ///
+ ///
+ private Scene GetLocateScene(UUID AgentId)
+ {
+ Scene scene = null;
+
+ lock (m_sceneList)
+ {
+ if (m_sceneList.Count > 0)
+ {
+ foreach (Scene _scene in m_sceneList.Values)
+ {
+ ScenePresence tPresence = (ScenePresence)_scene.GetScenePresence(AgentId);
+ if (tPresence != null && !tPresence.IsChildAgent)
+ {
+ scene = _scene;
+ break;
+ }
+ }
+ }
+ }
+
+ m_log.DebugFormat("[MONEY MODULE]: GetLocateScene: {0}", scene);
+
+ return scene;
+ }
+
+
+ /// Gets the locate prim.
+ /// The object identifier.
+ ///
+ ///
+ ///
+ private SceneObjectPart GetLocatePrim(UUID objectID)
+ {
+ SceneObjectPart sceneObj = null;
+
+ lock (m_sceneList)
+ {
+ if (m_sceneList.Count > 0)
+ {
+ foreach (Scene _scene in m_sceneList.Values)
+ {
+ SceneObjectPart part = (SceneObjectPart)_scene.GetSceneObjectPart(objectID);
+ if (part != null)
+ {
+ sceneObj = part;
+ break;
+ }
+ }
+ }
+ }
+
+
+
+ return sceneObj;
+ }
+
+ }
+
+}
diff --git a/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/NSLCertificateTools.cs b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/NSLCertificateTools.cs
new file mode 100644
index 0000000..d27dd9e
--- /dev/null
+++ b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/NSLCertificateTools.cs
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) Contributors, http://www.nsl.tuis.ac.jp
+
+Funktion
+
+Diese Klasse dient zur Verwaltung und Überprüfung von SSL/TLS-Zertifikaten in C# (z. B. für einen MoneyServer). Sie bietet Methoden, um:
+ Eigene Zertifikate (CA und Client) zu laden
+ Certificate Revocation Lists (CRL) zu laden
+ Zertifikatsketten zu prüfen
+ Server- und Client-Zertifikate im Rahmen einer SSL/TLS-Verbindung zu validieren
+
+Null Pointer Checks
+
+1. Member-Initialisierung
+ Alle Felder (m_chain, m_cacert, m_mycert, m_clientcrl) sind nullable und werden mit null initialisiert.
+
+2. Set- und Get-Methoden
+ Beim Laden von Zertifikaten (SetPrivateCert, SetPrivateCA, SetPrivateCRL) wird jeder Fehler per try-catch abgefangen. Im Fehlerfall wird das entsprechende Feld auf null gesetzt und geloggt.
+
+3. Zertifikatsprüfungen
+ In CheckPrivateChain wird vor der Nutzung von m_chain und m_cacert explizit auf null geprüft:
+ C#
+
+ if (m_chain == null || m_cacert == null) return false;
+
+ In den Callback-Methoden zur Zertifikatsvalidierung (ValidateServerCertificate, ValidateClientCertificate) wird jeweils geprüft, ob ein Zertifikat vorhanden ist (certificate == null).
+
+4. CRL-Prüfung
+ Die CRL wird nur geprüft, wenn sie geladen wurde (if (m_clientcrl != null)).
+
+Fehlerquellen und ihre Behandlung
+ Dateizugriffe: Wenn Zertifikats- oder CRL-Dateien nicht lesbar oder ungültig sind, wird dies sauber abgefangen, das jeweilige Objekt auf null gesetzt und ein Fehler geloggt.
+ Chain-Validation: Wenn die Kette nicht validiert werden kann, wird dies mit Rückgabewerten behandelt (false) und geloggt.
+ SSL Policy Errors: Policy-Fehler werden geloggt und führen zum Abbruch der Validierung.
+ CRL-Eintrag: Wird ein Zertifikat als widerrufen erkannt, wird dies geloggt und die Validierung schlägt fehl.
+ Header "NoVerifyCert": Bei explizitem Wunsch per Header wird die Zertifikatsprüfung übersprungen.
+
+Zusammenfassung
+ Null Pointer: Sehr sorgfältige Behandlung. Überall werden Objekte vor Nutzung geprüft, alle Fehlerfälle führen zu Logging und setzen der Objekte auf null, Rückgabe erfolgt immer sicher.
+ Fehlerquellen: Hauptsächlich externe Faktoren wie ungültige oder fehlende Dateien, fehlerhafte Zertifikate oder widerrufene Zertifikate. Alles wird abgefangen.
+ Funktion: Die Klasse ist robust und für produktiven Serverbetrieb geeignet. Sie bietet vollständige und sichere Zertifikatsprüfung für Server- und Client-Zertifikate inkl. CRL-Unterstützung.
+
+Fazit:
+Die Klasse ist sicher gegenüber NullPointer-Exceptions und behandelt alle Fehlerfälle sauber mit Logging.
+Sie erfüllt alle Anforderungen für sichere Zertifikatsvalidierung in einem OpenSim-Serverumfeld.
+ */
+
+using System;
+using System.Net;
+using System.Net.Security;
+using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
+
+using log4net;
+
+
+namespace NSL.Certificate.Tools
+{
+ ///
+ /// class NSL Certificate Verify
+ ///
+ public class NSLCertificateVerify
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private X509Chain m_chain = null;
+ private X509Certificate2 m_cacert = null;
+ private X509Certificate2 m_mycert = null;
+
+ private Mono.Security.X509.X509Crl m_clientcrl = null;
+
+
+ /// NSL Certificate Verify
+ public NSLCertificateVerify()
+ {
+ m_chain = null;
+ m_cacert = null;
+ m_clientcrl = null;
+
+ // m_log.InfoFormat("[NSL CERT VERIFY]: NSLCertificateVerify()");
+ }
+
+
+ ///
+ /// NSL Certificate Verify
+ ///
+ ///
+ public NSLCertificateVerify(string certfile)
+ {
+ SetPrivateCA(certfile);
+
+ // m_log.InfoFormat("[NSL CERT VERIFY]: NSLCertificateVerify()");
+ }
+
+
+ ///
+ /// NSL Certificate Verify
+ ///
+ ///
+ ///
+ public NSLCertificateVerify(string certfile, string crlfile)
+ {
+ SetPrivateCA(certfile);
+ SetPrivateCRL(crlfile);
+
+ // m_log.InfoFormat("[NSL CERT VERIFY]: NSLCertificateVerify()");
+ }
+
+
+ ///
+ /// Set Private Certificate
+ ///
+ ///
+ ///
+ public void SetPrivateCert(string certfile, string passwd)
+ {
+ try
+ {
+ m_mycert = new X509Certificate2(certfile, passwd);
+ }
+ catch (Exception ex)
+ {
+ m_mycert = null;
+ m_log.ErrorFormat("[SET PRIVATE CERT]: Cert File setting error [{0}]. {1}", certfile, ex);
+ }
+ }
+
+
+ ///
+ /// Get Private Certificate
+ ///
+ public X509Certificate2 GetPrivateCert()
+ {
+ return m_mycert;
+ }
+
+
+
+ ///
+ /// Set Private CA
+ ///
+ ///
+ public void SetPrivateCA(string certfile)
+ {
+ try
+ {
+ m_cacert = new X509Certificate2(certfile);
+ }
+ catch (Exception ex)
+ {
+ m_cacert = null;
+ m_log.ErrorFormat("[SET PRIVATE CA]: CA File reading error [{0}]. {1}", certfile, ex);
+ }
+
+ if (m_cacert != null)
+ {
+ m_chain = new X509Chain();
+ m_chain.ChainPolicy.ExtraStore.Add(m_cacert);
+ m_chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
+ m_chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
+ }
+ }
+
+ /// Sets the private CRL.
+ /// The crlfile.
+ public void SetPrivateCRL(string crlfile)
+ {
+ try
+ {
+ m_clientcrl = Mono.Security.X509.X509Crl.CreateFromFile(crlfile);
+ }
+ catch (Exception ex)
+ {
+ m_clientcrl = null;
+ m_log.ErrorFormat("[SET PRIVATE CRL]: CRL File reading error [{0}]. {1}", crlfile, ex);
+ }
+ }
+
+
+ ///
+ /// Check Private Chain
+ ///
+ ///
+ ///
+ public bool CheckPrivateChain(X509Certificate2 cert)
+ {
+ if (m_chain == null || m_cacert == null)
+ {
+ return false;
+ }
+
+ bool ret = m_chain.Build((X509Certificate2)cert);
+ if (ret)
+ {
+ return true;
+ }
+
+ for (int i = 0; i < m_chain.ChainStatus.Length; i++)
+ {
+ if (m_chain.ChainStatus[i].Status == X509ChainStatusFlags.UntrustedRoot) return true;
+ }
+
+ return false;
+ }
+
+
+ ///
+ /// Validate Server Certificate Callback Function
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool ValidateServerCertificate(object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ {
+ m_log.InfoFormat("[NSL SERVER CERT VERIFY]: ValidateServerCertificate: Policy is ({0})", sslPolicyErrors);
+
+ if (obj is HttpWebRequest)
+ {
+ HttpWebRequest Request = (HttpWebRequest)obj;
+ string noVerify = Request.Headers.Get("NoVerifyCert");
+ if ((noVerify != null) && (noVerify.ToLower() == "true"))
+ {
+ m_log.InfoFormat("[NSL SERVER CERT VERIFY]: ValidateServerCertificate: No Verify Server Certificate.");
+ return true;
+ }
+ }
+
+ X509Certificate2 certificate2 = new X509Certificate2(certificate);
+ string commonname = certificate2.GetNameInfo(X509NameType.SimpleName, false);
+ m_log.InfoFormat("[NSL SERVER CERT VERIFY]: ValidateServerCertificate: Common Name is \"{0}\"", commonname);
+
+ // None, ChainErrors Error except for.
+ if ((sslPolicyErrors != SslPolicyErrors.None) && (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors))
+ {
+ m_log.InfoFormat("[NSL SERVER CERT VERIFY]: ValidateServerCertificate: Policy Error! {0}", sslPolicyErrors);
+ return false;
+ }
+
+ bool valid = CheckPrivateChain(certificate2);
+ if (valid)
+ {
+ m_log.InfoFormat("[NSL SERVER CERT VERIFY]: ValidateServerCertificate: Valid Server Certification for \"{0}\"", commonname);
+ }
+ else
+ {
+ m_log.InfoFormat("[NSL SERVER CERT VERIFY]: ValidateServerCertificate: Failed to Verify Server Certification for \"{0}\"", commonname);
+ }
+ return valid;
+ }
+
+
+ ///
+ /// Validate Client Certificate Callback Function
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public bool ValidateClientCertificate(object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ {
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: ValidateClientCertificate: Start.");
+
+ if (certificate == null)
+ {
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: ValidateClientCertificate: Client does not have a Certificate!");
+ return false;
+ }
+
+ X509Certificate2 certificate2 = new X509Certificate2(certificate);
+ string commonname = certificate2.GetNameInfo(X509NameType.SimpleName, false);
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: ValidateClientCertificate: Common Name is \"{0}\"", commonname);
+
+ // None, ChainErrors Anything other than that is an error.
+ if (sslPolicyErrors != SslPolicyErrors.None && sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
+ {
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: ValidateClientCertificate: Policy Error! {0}", sslPolicyErrors);
+ return false;
+ }
+
+ // check CRL
+ if (m_clientcrl != null)
+ {
+ Mono.Security.X509.X509Certificate monocert = new Mono.Security.X509.X509Certificate(certificate.GetRawCertData());
+ Mono.Security.X509.X509Crl.X509CrlEntry entry = m_clientcrl.GetCrlEntry(monocert);
+ if (entry != null)
+ {
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: Common Name \"{0}\" was revoked at {1}", commonname, entry.RevocationDate.ToString());
+ return false;
+ }
+ }
+
+ bool valid = CheckPrivateChain(certificate2);
+ if (valid)
+ {
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: Valid Client Certification for \"{0}\"", commonname);
+ }
+ else
+ {
+ m_log.InfoFormat("[NSL CLIENT CERT VERIFY]: Failed to Verify Client Certification for \"{0}\"", commonname);
+ }
+ return valid;
+ }
+ }
+
+}
diff --git a/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/NSLXmlRpc.cs b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/NSLXmlRpc.cs
new file mode 100644
index 0000000..36e5f6b
--- /dev/null
+++ b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/NSLXmlRpc.cs
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) Contributors, http://www.nsl.tuis.ac.jp
+ *
+
+Funktion
+Die Klasse NSLXmlRpcRequest erweitert XmlRpcRequest und implementiert eine Möglichkeit, XML-RPC-Requests (z.B. für einen MoneyServer) mit optionaler Client-Zertifikatsunterstützung und Serverzertifikatsprüfung zu senden. Sie serialisiert den Request, sendet ihn per HTTP(S) und deserialisiert die Antwort.
+Null Pointer Checks
+
+1. Konstruktoren:
+ Initialisieren immer das Parameter-Array (_params).
+ Keine Gefahr für NullPointer in der Instanz selbst.
+
+2. certSend-Methode:
+ Prüft, ob das erzeugte HttpWebRequest-Objekt null ist.
+ Holt das Zertifikat via certVerify.GetPrivateCert(). Wenn dieses null ist, wird kein Zertifikat angehängt (kein Fehler).
+ Prüft, ob ein Zertifikats-Validator gesetzt werden muss.
+ Holt den Stream für den Request im Try/Catch. Bei Fehler wird geloggt und null zurückgegeben.
+ Holt die Response im Try/Catch. Fehler werden geloggt, aber das Programm läuft weiter.
+ Rückgabe von null, wenn der Stream nicht geöffnet werden kann.
+ Liest den Response-Stream, ohne expliziten Null-Check auf response. Wenn GetResponse() fehlschlägt, bleibt das Response-Objekt null, was zu einem Fehler führen kann, falls nicht abgefangen.
+
+Potenzielle Null Pointer:
+ Nach einem Fehler bei GetResponse() bleibt response null, aber danach wird direkt response.GetResponseStream() aufgerufen.
+ → Es fehlt ein expliziter Null-Check für response nach der Fehlerbehandlung! Das ist eine Schwachstelle:
+ C#
+
+ HttpWebResponse response = null;
+ try { response = (HttpWebResponse)request.GetResponse(); }
+ catch (Exception ex) { ... }
+ // Kein Check auf response == null vor response.GetResponseStream()
+
+Fehlerquellen und Fehlerbehandlung
+ Netzwerkfehler: Werden beim Öffnen von Streams und Response sauber per Try/Catch behandelt und geloggt.
+ Serialisierung: Falls der Request-Stream nicht geöffnet werden kann, wird null zurückgegeben.
+ Fehlerhafte Zertifikate: Wenn kein Zertifikatsprüfer übergeben wird, wird die Serverzertifikatsprüfung ausgeschaltet (per Header).
+ Fehler beim Deserialisieren: Keine explizite Fehlerbehandlung beim Deserialisieren des Response-Streams, aber vorherige Fehler führen dazu, dass diese Zeile nicht ausgeführt wird.
+
+Zusammenfassung
+ Null Pointer:
+ Fast überall sicher behandelt, aber ein (kleiner) Fehler: Nach einem Fehler bei GetResponse() kann response null sein, was zu einem NullReferenceException bei response.GetResponseStream() führen kann.
+ → Empfohlen: Vor dem Streamzugriff explizit prüfen, ob response != null.
+ Fehlerquellen:
+ Netzwerk- und Zertifikatsfehler werden gut geloggt und führen zu Rückgabe von null.
+ Funktion:
+ Sichert und erweitert XML-RPC-Requests um Zertifikats- und Sicherheitsoptionen.
+
+Fazit:
+Die Klasse ist robust, bis auf den fehlenden Null-Check für response nach GetResponse(). Ansonsten werden Null Pointer und Fehlerquellen sorgfältig behandelt. Logging ist umfassend.
+
+Empfehlung:
+Füge vor dem Zugriff auf den Response-Stream einen Check ein:
+C#
+
+if (response == null) return null;
+
+ */
+
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Xml;
+using System.Net;
+using System.Text;
+using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
+using log4net;
+using Nwc.XmlRpc;
+using System.Net.Security;
+using NSL.Certificate.Tools;
+
+
+namespace NSL.Network.XmlRpc
+{
+ public class NSLXmlRpcRequest : XmlRpcRequest
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ // The encoding
+ private Encoding _encoding = new UTF8Encoding();
+ // The serializer
+ private XmlRpcRequestSerializer _serializer = new XmlRpcRequestSerializer();
+ // The deserializer
+ private XmlRpcResponseDeserializer _deserializer = new XmlRpcResponseDeserializer();
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// This constructor initializes the request with an empty parameter list.
+ ///
+ public NSLXmlRpcRequest()
+ {
+ // Initialize the parameter list as an empty ArrayList
+ _params = new ArrayList();
+ }
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Name of the method to be invoked in the XML-RPC request.
+ /// The parameters to be passed to the method.
+ public NSLXmlRpcRequest(String methodName, IList parameters)
+ {
+ // Set the method name for the XML-RPC request
+ MethodName = methodName;
+
+ // Set the parameters for the XML-RPC request
+ _params = parameters;
+ }
+
+
+ ///
+ /// Sends a certificate-based XML-RPC request to the specified URL.
+ ///
+ /// The URL of the XML-RPC server.
+ /// The certificate verification object.
+ /// Whether to check the server's certificate.
+ /// The timeout for the request in milliseconds.
+ /// The XML-RPC response from the server.
+ /// Thrown if there is an error with the request.
+ public XmlRpcResponse certSend(String url, NSLCertificateVerify certVerify, bool checkServerCert, Int32 timeout)
+ {
+ // Log the request URL
+ m_log.InfoFormat("[MONEY NSL XMLRPC]: XmlRpcResponse certSend: connect to {0}", url);
+
+ // Create a new HTTP web request
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
+ if (request == null)
+ {
+ // Throw an exception if the request cannot be created
+ throw new XmlRpcException(XmlRpcErrorCodes.TRANSPORT_ERROR, XmlRpcErrorCodes.TRANSPORT_ERROR_MSG + ": Could not create request with " + url);
+ }
+
+ // Set the request method and content type
+ request.Method = "POST";
+ request.ContentType = "text/xml";
+ request.AllowWriteStreamBuffering = true;
+ request.Timeout = timeout;
+ request.UserAgent = "NSLXmlRpcRequest";
+
+ // Add the client certificate if provided
+ X509Certificate2 clientCert = null;
+ if (certVerify != null)
+ {
+ clientCert = certVerify.GetPrivateCert();
+ if (clientCert != null) request.ClientCertificates.Add(clientCert); // Own certificate
+ request.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(certVerify.ValidateServerCertificate);
+ }
+ else
+ {
+ // Disable server certificate checking if no verification object is provided
+ checkServerCert = false;
+ }
+
+ // Disable server certificate checking if requested
+ if (!checkServerCert)
+ {
+ request.Headers.Add("NoVerifyCert", "true"); // Do not verify the certificate of the other party
+ }
+
+ // Get the request stream
+ Stream stream = null;
+ try
+ {
+ stream = request.GetRequestStream();
+ }
+ catch (Exception ex)
+ {
+ // Log any errors getting the request stream
+ m_log.ErrorFormat("[MONEY NSL XMLRPC]: GetRequestStream Error: {0}", ex);
+ stream = null;
+ }
+
+ // Return null if the request stream could not be obtained
+ if (stream == null) return null;
+
+ // Serialize the request to the stream
+ XmlTextWriter xml = new XmlTextWriter(stream, _encoding);
+ _serializer.Serialize(xml, this);
+ xml.Flush();
+ xml.Close();
+
+ // Get the response from the server
+ HttpWebResponse response = null;
+ try
+ {
+ response = (HttpWebResponse)request.GetResponse();
+ }
+ catch (Exception ex)
+ {
+ // Log any errors getting the response
+ m_log.ErrorFormat("[MONEY NSL XMLRPC]: XmlRpcResponse certSend: GetResponse Error: {0}", ex.ToString());
+ }
+
+ // Return null if the response could not be obtained
+ if (response == null)
+ return null;
+
+ // Deserialize the response from the server
+ StreamReader input = new StreamReader(response.GetResponseStream());
+ string inputXml = input.ReadToEnd();
+ XmlRpcResponse resp = (XmlRpcResponse)_deserializer.Deserialize(inputXml);
+
+ // Close the input and response streams
+ input.Close();
+ response.Close();
+
+ // Return the deserialized response
+ return resp;
+ }
+ }
+
+}
diff --git a/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/Properties/AssemblyInfo.cs b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..e11b288
--- /dev/null
+++ b/addon-modules/OpenSim-Modules-Currency/OpenSim.Modules.Currency/Properties/AssemblyInfo.cs
@@ -0,0 +1,34 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Mono.Addins;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("OpenSim.Modules.Currency")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("DTL/NSL Group")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2009")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2363e3c9-7be0-4b9b-84ac-82923d5021f4")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file
diff --git a/addon-modules/OpenSim-Modules-Currency/prebuild-OpenSimModulesCurrency.xml b/addon-modules/OpenSim-Modules-Currency/prebuild-OpenSimModulesCurrency.xml
new file mode 100644
index 0000000..8b7b71e
--- /dev/null
+++ b/addon-modules/OpenSim-Modules-Currency/prebuild-OpenSimModulesCurrency.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ ../../bin/
+ true
+
+
+
+
+ ../../bin/
+ true
+
+
+
+ ../../bin/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addon-modules/README b/addon-modules/README
new file mode 100644
index 0000000..0c722c9
--- /dev/null
+++ b/addon-modules/README
@@ -0,0 +1,9 @@
+In this directory you can place addon modules for OpenSim
+
+Each module should be in it's own tree and the root of the tree
+should contain a file named "prebuild.xml", which will be included in the
+main prebuild file.
+
+The prebuild.xml should only contain and associated child tags.
+The , , and tags should not be
+included since the add-on modules prebuild.xml will be inserted directly into the main prebuild.xml
diff --git a/bin/MoneyServer.dll.config b/bin/MoneyServer.dll.config
new file mode 100644
index 0000000..1f31302
--- /dev/null
+++ b/bin/MoneyServer.dll.config
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bin/MoneyServer.ini.example b/bin/MoneyServer.ini.example
new file mode 100644
index 0000000..db21b4a
--- /dev/null
+++ b/bin/MoneyServer.ini.example
@@ -0,0 +1,99 @@
+[Startup]
+; Path to create the PID file for identifying the process.
+PIDFile = "/tmp/money.pid"
+
+[MySql]
+; MySQL database connection settings:
+hostname = "localhost" ; Hostname of the MySQL server.
+database = "robust" ; Name of the database to use.
+username = "opensim" ; Username for database access.
+password = "opensim" ; Password for database access.
+pooling = "true" ; Enable connection pooling (true/false).
+port = "3306" ; MySQL server port (default: 3306).
+MaxConnection = "25" ; Maximum number of connections in the pool.
+
+[MoneyServer]
+; Port number on which the MoneyServer listens.
+ServerPort = 8008
+
+; Enable or disable the currency system ("on" to enable, "off" to disable).
+CurrencyOnOff = on
+
+; Maximum amount of currency a user can hold.
+CurrencyMaximum = 20000
+
+; URL or IP address where the MoneyServer can be reached.
+MoneyServerIPaddress = "http://127.0.0.1:8008"
+
+; Default starting balance for new users.
+DefaultBalance = 1000
+
+; Restrict currency purchases to members of a specific group (true/false).
+CurrencyGroupOnly = true
+; Group ID required for currency purchases (UUID format).
+CurrencyGroupID = "00000000-0000-0000-0000-000000000000"
+
+; Require email verification for currency purchases (true/false).
+UserMailLock = true
+
+; Allow transactions with an amount of zero (true/false, default: false).
+EnableAmountZero = true
+
+; Cost multiplier for each unit of currency purchased (integer value).
+CalculateCurrency = 10
+
+; UUID of the avatar designated as the "banker".
+; Use "00000000-0000-0000-0000-000000000000" to allow all avatars, or leave blank to disable the banker function.
+BankerAvatar = "00000000-0000-0000-0000-000000000000"
+
+; Allow forced money transfers via scripts (true/false).
+EnableForceTransfer = true
+; Allow scripts to send money (true/false).
+EnableScriptSendMoney = true
+
+; Default balances for HyperGrid and guest users.
+HGAvatarDefaultBalance = 500
+GuestAvatarDefaultBalance = 500
+
+; Access key and IP address for MoneyScript integration.
+MoneyScriptAccessKey = "123456789"
+MoneyScriptIPaddress = "127.0.0.1"
+
+; Enable currency support for HyperGrid and guest avatars (true/false).
+EnableHGAvatar = true
+EnableGuestAvatar = true
+
+; Messages shown to users when their balance is updated.
+; Use {0} for the amount, {1} for the other party's name, and {2} for object names.
+BalanceMessageSendGift = "Sent Gift L${0} to {1}."
+BalanceMessageReceiveGift = "Received Gift L${0} from {1}."
+BalanceMessagePayCharge = "Paid the Money L${0} for creation."
+BalanceMessageBuyObject = "Bought the Object {2} from {1} by L${0}."
+BalanceMessageSellObject = "{1} bought the Object {2} by L${0}."
+BalanceMessageLandSale = "Paid the Money L${0} for Land."
+BalanceMessageScvLandSale = "Paid the Money L${0} for Land."
+BalanceMessageGetMoney = "Got the Money L${0} from {1}."
+BalanceMessageBuyMoney = "Bought the Money L${0}."
+BalanceMessageRollBack = "RollBack the Transaction: L${0} from/to {1}."
+BalanceMessageSendMoney = "Paid the Money L${0} to {1}."
+BalanceMessageReceiveMoney = "Received L${0} from {1}."
+
+[Certificate]
+; Certificate settings for secure connections:
+
+; CA certificate for verifying client/server certificates.
+;CACertFilename = "cacert.crt"
+
+; Server SSL certificate (for HTTPS server mode).
+;ServerCertFilename = "server_cert.p12"
+;ServerCertPassword = "opensim"
+
+; Enable verification of client certificates (true/false).
+CheckClientCert = false
+
+; Enable verification of server certificates (true/false).
+CheckServerCert = false
+
+; Client certificate (for authentication with other servers).
+;ClientCertFilename = "client_cert.p12"
+;ClientCertPassword = "opensim"
diff --git a/bin/OpenSim.ini.sample b/bin/OpenSim.ini.sample
new file mode 100644
index 0000000..bb28f44
--- /dev/null
+++ b/bin/OpenSim.ini.sample
@@ -0,0 +1,55 @@
+[Economy]
+ ;; Enables selling objects in-world. Set to false to disable all selling functionality.
+ SellEnabled = true
+
+ ;CurrencyServer = "" ;; Example: "https://opensim.net:8008/" Default is ""
+ ;; The URL of your external Money Server. The placeholder "${Const|BaseURL}" will be replaced at runtime with your actual base URL.
+ EconomyModule = DTLNSLMoneyModule
+ CurrencyServer = "${Const|BaseURL}:8008/"
+ ;; The UserServer is typically on port 8002, used for avatar and user account management.
+ UserServer = "${Const|BaseURL}:8002/"
+
+ ;; Set to true to enable SSL/TLS certificate verification for the Money Server. For testing, false is safer. For production, true is recommended.
+ CheckServerCert = false
+
+ ;; The fee (in in-world currency units) to upload textures, animations etc. Default is 0 (free uploads).
+ PriceUpload = 0
+
+ ;; Mesh upload cost multipliers. Adjust these to change the price for uploading mesh models and textures.
+ ;; 1.0 means default cost, higher values increase the cost.
+ MeshModelUploadCostFactor = 1.0
+ MeshModelUploadTextureCostFactor = 1.0
+ MeshModelMinCostFactor = 1.0
+
+ ;; The fee (in in-world currency units) to create a new group. Default is 0 (creating groups is free).
+ PriceGroupCreate = 0
+
+ ;; The following values are sent to the viewer, but are not always used by OpenSim itself.
+ ;; Their impact may depend on the viewer or custom modules—change only if you know what you want to achieve!
+ ObjectCount = 0 ;; Number of allowed objects (sent to client, often ignored)
+ PriceEnergyUnit = 0 ;; Cost per energy unit (seldom used)
+ PriceObjectClaim = 0 ;; Cost to claim an object (usually zero)
+ PricePublicObjectDecay = 0 ;; Cost when a public object decays (rarely used)
+ PricePublicObjectDelete = 0 ;; Cost to delete a public object (rarely used)
+ PriceParcelClaim = 0 ;; Cost to claim a parcel of land
+ PriceParcelClaimFactor = 1 ;; Multiplier for parcel claim cost
+
+ PriceRentLight = 0 ;; Cost to rent a light (seldom used)
+ TeleportMinPrice = 0 ;; Minimum cost for teleportation (usually zero)
+ TeleportPriceExponent = 2 ;; Exponent for calculating teleport costs (rarely changed)
+ EnergyEfficiency = 1 ;; Efficiency factor, usually 1 (default)
+ PriceObjectRent = 0 ;; Cost to rent an object (seldom used)
+ PriceObjectScaleFactor = 10 ;; Scale factor for object-related costs (adjust as needed)
+ PriceParcelRent = 0 ;; Cost to rent a parcel (usually zero)
+
+ ; Mesh upload settings, these options work regardless of the economy module you use.
+
+ ; If true, textures uploaded with a mesh model are also added to the user's inventory.
+ ; Default is false (textures are not added separately).
+ ; MeshModelAllowTextureToInventory = true
+
+ ;; Avatar Class for HG Avatar:
+ ;; Possible values: ForeignAvatar, HGAvatar, GuestAvatar, LocalAvatar.
+ ;; Default is HGAvatar. This setting determines how HyperGrid avatars are handled.
+ ;; The actual processing for each avatar class depends on the configuration of your Money Server.
+ ;HGAvatarAs = "HGAvatar"
diff --git a/bin/SineWaveCert.pfx b/bin/SineWaveCert.pfx
new file mode 100644
index 0000000..11c8ae5
Binary files /dev/null and b/bin/SineWaveCert.pfx differ
diff --git a/bin/server_cert.p12 b/bin/server_cert.p12
new file mode 100644
index 0000000..16adb01
Binary files /dev/null and b/bin/server_cert.p12 differ