// * 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. // */ using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Reflection; using System.Net; using System.Net.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; 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; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using NSL.Certificate.Tools; using NSL.Network.XmlRpc; [assembly: Addin("DTLNSLMoneyModule", "0.1")] [assembly: AddinDependency("OpenSim", "0.5")] namespace OpenSim.Modules.Currency { // public enum TransactionType : int { // One-Time Charges GroupCreate = 1002, GroupJoin = 1004, UploadCharge = 1101, LandAuction = 1102, ClassifiedCharge= 1103, // Recurrent Charges ParcelDirFee = 2003, 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, // Group Transactions GroupLiability = 6003, GroupDividend = 6004, // Stipend Credits StipendPayment = 10000 } /* public enum OpenMetaverse.MoneyTransactionType : int { None = 0, FailSimulatorTimeout = 1, FailDataserverTimeout = 2, ObjectClaim = 1000, LandClaim = 1001, GroupCreate = 1002, ObjectPublicClaim = 1003, GroupJoin = 1004, TeleportCharge = 1100, UploadCharge = 1101, LandAuction = 1102, ClassifiedCharge = 1103, ObjectTax = 2000, LandTax = 2001, LightTax = 2002, ParcelDirFee = 2003, GroupTax = 2004, ClassifiedRenew = 2005, GiveInventory = 3000, ObjectSale = 5000, Gift = 5001, LandSale = 5002, ReferBonus = 5003, InventorySale = 5004, RefundPurchase = 5005, LandPassSale = 5006, DwellBonus = 5007, PayObject = 5008, ObjectPays = 5009, GroupLandDeed = 6001, GroupObjectDeed = 6002, GroupLiability = 6003, GroupDividend = 6004, GroupMembershipDues = 6005, ObjectRelease = 8000, LandRelease = 8001, ObjectDelete = 8002, ObjectPublicDecay = 8003, ObjectPublicDelete = 8004, LindenAdjustment = 9000, LindenGrant = 9001, LindenPenalty = 9002, EventFee = 9003, EventPrize = 9004, StipendBasic = 10000, StipendDeveloper = 10001, StipendAlways = 10002, StipendDaily = 10003, StipendRating = 10004, StipendDelta = 10005 } */ // [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MoneyModule")] public class MoneyModule : IMoneyModule, ISharedRegionModule { #region Constant numbers and members. // 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 IConfigSource m_config; private string m_moneyServURL = string.Empty; private string m_userServIP = string.Empty; public BaseHttpServer HttpServer; private string m_certFilename = ""; private string m_certPassword = ""; private bool m_checkServerCert = false; private string m_cacertFilename = ""; private X509Certificate2 m_cert = null; // private bool m_use_web_settle = false; private string m_settle_url = ""; private string m_settle_message = ""; private bool m_settle_user = false; private NSLCertificateVerify m_certVerify = new NSLCertificateVerify(); // サーバ認証用 /// /// Scene dictionary indexed by Region Handle /// private Dictionary m_sceneList = new Dictionary(); /// /// To cache the balance data while the money server is not available. /// private Dictionary m_moneyServer = new Dictionary(); // Events public event ObjectPaid OnObjectPaid; // Price private int ObjectCount = 0; private int PriceEnergyUnit = 0; private int PriceGroupCreate = 0; private int PriceObjectClaim = 0; private float PriceObjectRent = 0f; private float PriceObjectScaleFactor = 0f; private int PriceParcelClaim = 0; private int PriceParcelRent = 0; private float PriceParcelClaimFactor = 0f; private int PricePublicObjectDecay = 0; private int PricePublicObjectDelete = 0; private int PriceRentLight = 0; private int PriceUpload = 0; private int TeleportMinPrice = 0; private float TeleportPriceExponent = 0f; private float EnergyEfficiency = 0f; #endregion // public void Initialise(Scene scene, IConfigSource source) { Initialise(source); AddRegion(scene); } #region ISharedRegionModule interface public void Initialise(IConfigSource source) { //m_log.InfoFormat("[MONEY]: Initialise:"); // Handle the parameters errors. if (source==null) return; try { m_config = source; // [Startup] secion IConfig networkConfig = m_config.Configs["Network"]; m_userServIP = ""; if (networkConfig.Contains("user_server_url")) { m_userServIP = Util.GetHostFromURL(networkConfig.GetString("user_server_url")).ToString(); } // [Economy] section IConfig economyConfig = m_config.Configs["Economy"]; if (economyConfig.GetString("EconomyModule")!=Name) { m_enabled = false; m_log.InfoFormat("[MONEY]: The DTL/NSL MoneyModule is disabled"); return; } else { m_log.InfoFormat("[MONEY]: The DTL/NSL MoneyModule is enabled"); } m_sellEnabled = economyConfig.GetBoolean("SellEnabled", false); if (m_userServIP=="") { m_userServIP = Util.GetHostFromURL(economyConfig.GetString("UserServer")).ToString(); } m_moneyServURL = economyConfig.GetString("CurrencyServer"); // クライアント証明書 m_certFilename = economyConfig.GetString("ClientCertFilename", ""); m_certPassword = economyConfig.GetString("ClientCertPassword", ""); if (m_certFilename!="") { m_cert = new X509Certificate2(m_certFilename, m_certPassword); m_log.InfoFormat("[MONEY]: Issue Authentication of Client. Cert File is " + m_certFilename); } // サーバ認証 string checkcert = economyConfig.GetString("CheckServerCert", "false"); if (checkcert.ToLower()=="true") m_checkServerCert = true; m_cacertFilename = economyConfig.GetString("CACertFilename", ""); if (m_cacertFilename!="") { m_certVerify.SetPrivateCA(m_cacertFilename); ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(m_certVerify.ValidateServerCertificate); m_log.InfoFormat("[MONEY]: Execute Authentication of Server. CA Cert File is " + m_cacertFilename); } else { m_checkServerCert = false; ServicePointManager.ServerCertificateValidationCallback = null; } // Settlement m_use_web_settle = economyConfig.GetBoolean("SettlementByWeb", false); m_settle_url = economyConfig.GetString ("SettlementURL", ""); m_settle_message = economyConfig.GetString ("SettlementMessage", ""); // Price PriceEnergyUnit = economyConfig.GetInt ("PriceEnergyUnit", 100); PriceObjectClaim = economyConfig.GetInt ("PriceObjectClaim", 10); PricePublicObjectDecay = economyConfig.GetInt ("PricePublicObjectDecay", 4); PricePublicObjectDelete = economyConfig.GetInt ("PricePublicObjectDelete", 4); PriceParcelClaim = economyConfig.GetInt ("PriceParcelClaim", 1); PriceParcelClaimFactor = economyConfig.GetFloat("PriceParcelClaimFactor", 1f); PriceUpload = economyConfig.GetInt ("PriceUpload", 0); PriceRentLight = economyConfig.GetInt ("PriceRentLight", 5); PriceObjectRent = economyConfig.GetFloat("PriceObjectRent", 1); PriceObjectScaleFactor = economyConfig.GetFloat("PriceObjectScaleFactor", 10); PriceParcelRent = economyConfig.GetInt ("PriceParcelRent", 1); PriceGroupCreate = economyConfig.GetInt ("PriceGroupCreate", 0); TeleportMinPrice = economyConfig.GetInt ("TeleportMinPrice", 2); TeleportPriceExponent = economyConfig.GetFloat("TeleportPriceExponent", 2f); EnergyEfficiency = economyConfig.GetFloat("EnergyEfficiency", 1); } catch { m_log.ErrorFormat("[MONEY]: Initialise: Faile to read configuration file"); } } public void AddRegion(Scene scene) { //m_log.InfoFormat("[MONEY]: AddRegion:"); if (scene==null) return; scene.RegisterModuleInterface(this); // 競合するモジュールの排除 lock (m_sceneList) { if (m_sceneList.Count==0) { if (!string.IsNullOrEmpty(m_moneyServURL)) { 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); // added HttpServer.AddXmlRPCHandler("AddBankerMoney", AddBankerMoneyHandler); // added HttpServer.AddXmlRPCHandler("SendMoneyBalance", SendMoneyBalanceHandler); // added //HttpServer.AddXmlRPCHandler("UploadCovered", UploadCoveredHandler); // added for Aurora-Sim //HttpServer.AddXmlRPCHandler("UploadCharge", UploadChargeHandler); // added for Aurora-Sim MainServer.Instance.AddXmlRPCHandler("OnMoneyTransfered", OnMoneyTransferedHandler); MainServer.Instance.AddXmlRPCHandler("UpdateBalance", BalanceUpdateHandler); MainServer.Instance.AddXmlRPCHandler("UserAlert", UserAlertHandler); MainServer.Instance.AddXmlRPCHandler("GetBalance", GetBalanceHandler); // added MainServer.Instance.AddXmlRPCHandler("AddBankerMoney", AddBankerMoneyHandler); // added MainServer.Instance.AddXmlRPCHandler("SendMoneyBalance", SendMoneyBalanceHandler); // added //MainServer.Instance.AddXmlRPCHandler("UploadCovered", UploadCoveredHandler); // added for Aurora-Sim //MainServer.Instance.AddXmlRPCHandler("UploadCharge", UploadChargeHandler); // added for Aurora-Sim } } 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; } public void RemoveRegion(Scene scene) { } public void RegionLoaded(Scene scene) { } public Type ReplaceableInterface { //get { return typeof(IMoneyModule); } get { return null; } } public bool IsSharedModule { get { return true; } } public string Name { get { return "DTLNSLMoneyModule"; } } public void PostInitialise() { } public void Close() { } #endregion #region IMoneyModule interface. // for LSL llGiveMoney() function public bool ObjectGiveMoney(UUID objectID, UUID fromID, UUID toID, int amount) { //m_log.InfoFormat("[MONEY]: ObjectGiveMoney: LSL ObjectGiveMoney. UUID = {0}", objectID.ToString()); if (!m_sellEnabled) return false; string objName = string.Empty; string avatarName = string.Empty; SceneObjectPart sceneObj = GetLocatePrim(objectID); if (sceneObj!=null) { 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; if (GetLocateClient(fromID)!=null) { //ret = TransferMoney(fromID, toID, amount, (int)TransactionType.ObjectPays, objectID, regionHandle, description); ret = TransferMoney(fromID, toID, amount, (int)MoneyTransactionType.ObjectPays, objectID, regionHandle, description); } else { //ret = ForceTransferMoney(fromID, toID, amount, (int)TransactionType.ObjectPays, objectID, regionHandle, description); ret = ForceTransferMoney(fromID, toID, amount, (int)MoneyTransactionType.ObjectPays, objectID, regionHandle, description); } } return ret; } // public int UploadCharge { get { return PriceUpload; } } // public int GroupCreationCharge { get { return PriceGroupCreate; } } public int GetBalance(UUID agentID) { IClientAPI client = GetLocateClient(agentID); return QueryBalanceFromMoneyServer(client); } public bool UploadCovered(UUID agentID, int amount) { IClientAPI client = GetLocateClient(agentID); int balance = QueryBalanceFromMoneyServer(client); if (balance= landBuyEvent.parcelPrice) { lock(landBuyEvent) { landBuyEvent.economyValidated = true; } } } } /**/ // for LandBuy even // For Aurora-Sim, OnParcelBuy event function is already defined // in OpenSim/Region/CoreModules/World/Land/ParcelManagementModule.cs private void processLandBuy(Object sender, EventManager.LandBuyArgs landBuyEvent) { //m_log.InfoFormat("[MONEY]: processLandBuy:"); if (!m_sellEnabled) return; lock(landBuyEvent) { if (landBuyEvent.economyValidated==true && landBuyEvent.transactionID==0) { landBuyEvent.transactionID = Util.UnixTimeSinceEpoch(); ulong parcelID = (ulong)landBuyEvent.parcelLocalID; UUID regionID = UUID.Zero; if (sender is Scene) regionID = ((Scene)sender).RegionInfo.RegionID; if (TransferMoney(landBuyEvent.agentId, landBuyEvent.parcelOwnerID, landBuyEvent.parcelPrice, (int)MoneyTransactionType.LandSale, regionID, parcelID, "Land Purchase")) { landBuyEvent.amountDebited = landBuyEvent.parcelPrice; } } } } /**/ // for OnObjectBuy event // For Aurora-Sim, OnObjectBuy event function is already defined // in OpenSim/Region/CoreModules/World/Objects/BuySell/BuySellModule.cs public void OnObjectBuy(IClientAPI remoteClient, UUID agentID, UUID sessionID, UUID groupID, UUID categoryID, uint localID, byte saleType, int salePrice) { //m_log.InfoFormat("[MONEY]: OnObjectBuy: agent = {0}, {1}", agentID, remoteClient.AgentId); // Handle the parameters error. if (!m_sellEnabled) return; if (remoteClient==null || salePrice<0) return; // for L$0 Sell // 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); 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; bool ret = TransferMoney(remoteClient.AgentId, receiverId, salePrice, (int)MoneyTransactionType.PayObject, sceneObj.UUID, regionHandle, "Object Buy"); if (ret) { mod.BuyObject(remoteClient, categoryID, localID, saleType, salePrice); } } } else { remoteClient.SendAgentAlertMessage("Unable to buy now. The object was not found", false); 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]: OnMoneyBalanceRequest:"); if (client.AgentId==agentID && client.SessionId==SessionID) { int balance = -1; if (!string.IsNullOrEmpty(m_moneyServURL)) { balance = QueryBalanceFromMoneyServer(client); } if (balance < 0) { client.SendAlertMessage("Fail to query the balance"); } else { //client.SendMoneyBalance(TransactionID, true, new byte[0], balance); 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"); } } private void OnRequestPayPrice(IClientAPI client, UUID objectID) { //m_log.InfoFormat("[MONEY]: 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) private void OnEconomyDataRequest(IClientAPI user) { //m_log.InfoFormat("[MONEY]: OnEconomyDataRequest:"); //IClientAPI user = GetLocateClient(agentId); if (user!=null) { //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); } } #endregion #region MoneyModule XML-RPC Handler // "OnMoneyTransfered" RPC from MoneyServer public XmlRpcResponse OnMoneyTransferedHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: OnMoneyTransferedHandler:"); bool ret = false; if (request.Params.Count>0) { Hashtable requestParam = (Hashtable)request.Params[0]; if (requestParam.Contains("clientUUID") && //requestParam.Contains("receiverUUID") && requestParam.Contains("clientSessionID") && requestParam.Contains("clientSecureSessionID")) { UUID clientUUID = UUID.Zero; UUID.TryParse((string)requestParam["clientUUID"], out clientUUID); //UUID receiverUUID = UUID.Zero; //UUID.TryParse((string)requestParam["receiverUUID"], out receiverUUID); if (clientUUID!=UUID.Zero) { IClientAPI client = GetLocateClient(clientUUID); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { if (requestParam.Contains("transactionType") && requestParam.Contains("objectID") && requestParam.Contains("amount")) { //m_log.InfoFormat("[MONEY]: OnMoneyTransferedHandler: type = {0}", requestParam["transactionType"]); if ((int)requestParam["transactionType"]==(int)MoneyTransactionType.PayObject) // Pay for the object. { // 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]: OnMoneyTransferedHandler: Transaction is failed. MoneyServer will rollback"); } resp.Value = paramTable; return resp; } // "UpdateBalance" RPC from MoneyServer or Script public XmlRpcResponse BalanceUpdateHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: BalanceUpdateHandler:"); bool ret = false; #region Update the balance from money server. if (request.Params.Count>0) { Hashtable requestParam = (Hashtable)request.Params[0]; if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && // unable for Aurora-Sim requestParam.Contains("clientSecureSessionID")) { UUID clientUUID = UUID.Zero; UUID.TryParse((string)requestParam["clientUUID"], out clientUUID); if (clientUUID!=UUID.Zero) { IClientAPI client = GetLocateClient(clientUUID); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && // unable for Aurora-Sim client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { 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"]); client.SendMoneyBalance(UUID.Random(), true, Utils.StringToBytes(msg), (int)requestParam["Balance"], 0, UUID.Zero, false, UUID.Zero, false, 0, String.Empty); ret = true; } } } } } #endregion // Send the response to money server. XmlRpcResponse resp = new XmlRpcResponse(); Hashtable paramTable = new Hashtable(); paramTable["success"] = ret; if (!ret) { m_log.ErrorFormat("[MONEY]: BalanceUpdateHandler: Cannot update client balance from MoneyServer"); } resp.Value = paramTable; return resp; } // "UserAlert" RPC from Script public XmlRpcResponse UserAlertHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: UserAlertHandler:"); bool ret = false; #region confirm the request and show the notice from money server. if (request.Params.Count>0) { Hashtable requestParam = (Hashtable)request.Params[0]; if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && // unable for Aurora-Sim requestParam.Contains("clientSecureSessionID")) { UUID clientUUID = UUID.Zero; UUID.TryParse((string)requestParam["clientUUID"], out clientUUID); if (clientUUID!=UUID.Zero) { IClientAPI client = GetLocateClient(clientUUID); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && // unable for Aurora-Sim client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { 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; } } } } } // #endregion // 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 public XmlRpcResponse GetBalanceHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: GetBalanceHandler:"); bool ret = false; int balance = -1; if (request.Params.Count>0) { Hashtable requestParam = (Hashtable)request.Params[0]; if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && // unable for Aurora-Sim requestParam.Contains("clientSecureSessionID")) { UUID clientUUID = UUID.Zero; UUID.TryParse((string)requestParam["clientUUID"], out clientUUID); if (clientUUID!=UUID.Zero) { IClientAPI client = GetLocateClient(clientUUID); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && // unable for Aurora-Sim client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { balance = QueryBalanceFromMoneyServer(client); } } } } // Send the response to caller. if (balance<0) { m_log.ErrorFormat("[MONEY]: 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 public XmlRpcResponse AddBankerMoneyHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: AddBankerMoneyHandler:"); bool ret = false; if (request.Params.Count>0) { Hashtable requestParam = (Hashtable)request.Params[0]; if (requestParam.Contains("clientUUID") && requestParam.Contains("clientSessionID") && // unable for Aurora-Sim requestParam.Contains("clientSecureSessionID")) { UUID bankerUUID = UUID.Zero; UUID.TryParse((string)requestParam["clientUUID"], out bankerUUID); if (bankerUUID!=UUID.Zero) { IClientAPI client = GetLocateClient(bankerUUID); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && // unable for Aurora-Sim client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { if (requestParam.Contains("amount")) { Scene scene = (Scene)client.Scene; int amount = (int)requestParam["amount"]; ret = AddBankerMoney(bankerUUID, amount, scene.RegionInfo.RegionHandle); 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]: 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; } // "SendMoneyBalance" RPC from Script public XmlRpcResponse SendMoneyBalanceHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: SendMoneyBalanceHandler:"); bool ret = false; if (request.Params.Count>0) { Hashtable requestParam = (Hashtable)request.Params[0]; if (requestParam.Contains("clientUUID") && requestParam.Contains("secretAccessCode")) { UUID clientUUID = UUID.Zero; UUID.TryParse((string)requestParam["clientUUID"], out clientUUID); if (clientUUID!=UUID.Zero) { 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]: SendMoneyBalanceHandler: SecretCode: {0} + {1} = {2}", secretCode, scriptIP, hash); ret = SendMoneyBalance(clientUUID, amount, hash); } } else { m_log.ErrorFormat("[MONEY]: SendMoneyBalanceHandler: amount is missed"); } } else { if (!requestParam.Contains("clientUUID")) { m_log.ErrorFormat("[MONEY]: SendMoneyBalanceHandler: clientUUID is missed"); } if (!requestParam.Contains("secretAccessCode")) { m_log.ErrorFormat("[MONEY]: SendMoneyBalanceHandler: secretAccessCode is missed"); } } } else { m_log.ErrorFormat("[MONEY]: SendMoneyBalanceHandler: Count is under 0"); } if (!ret) m_log.ErrorFormat("[MONEY]: SendMoneyBalanceHandler: 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; } /* // "UploadCovered" RPC for Aurora-Sim public XmlRpcResponse UploadCoveredHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: UploadCoveredHandler:"); bool ret = false; if (request.Params.Count>0 && m_userServIP==remoteClient.Address.ToString()) { 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); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { ret = UploadCovered(client, UploadCharge); } } } } // Send the response to caller. XmlRpcResponse resp = new XmlRpcResponse(); Hashtable paramTable = new Hashtable(); paramTable["success"] = ret; resp.Value = paramTable; return resp; } */ /* // "UploadCovered" RPC for Aurora-Sim public XmlRpcResponse UploadChargeHandler(XmlRpcRequest request, IPEndPoint remoteClient) { //m_log.InfoFormat("[MONEY]: UploadChargeHandler:"); bool ret = false; if (request.Params.Count>0 && m_userServIP==remoteClient.Address.ToString()) { 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); if (client!=null && client.SessionId.ToString()==(string)requestParam["clientSessionID"] && client.SecureSessionId.ToString()==(string)requestParam["clientSecureSessionID"]) { ApplyUploadCharge(clientUUID, UploadCharge, "Upload Asset"); ret = true; } } } } // Send the response to caller. XmlRpcResponse resp = new XmlRpcResponse(); Hashtable paramTable = new Hashtable(); paramTable["success"] = ret; resp.Value = paramTable; return resp; } */ #endregion #region MoneyModule private help functions /// /// 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, string description) { //m_log.InfoFormat("[MONEY]: TransferMoney:"); bool ret = false; IClientAPI senderClient = GetLocateClient(sender); // Handle the illegal transaction. if (senderClient==null) // receiverClient could be null. { m_log.InfoFormat("[MONEY]: TransferMoney: Client {0} not found", sender.ToString()); return false; } if (QueryBalanceFromMoneyServer(senderClient) /// 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, string description) { //m_log.InfoFormat("[MONEY]: ForceTransferMoney:"); bool ret = false; #region Force send transaction request to money server and parse the resultes. if (!string.IsNullOrEmpty(m_moneyServURL)) { // Fill parameters for money transfer XML-RPC. Hashtable paramTable = new Hashtable(); paramTable["senderUserServIP"] = m_userServIP; paramTable["senderID"] = sender.ToString(); paramTable["receiverUserServIP"] = m_userServIP; paramTable["receiverID"] = receiver.ToString(); paramTable["transactionType"] = type; paramTable["objectID"] = objectID.ToString(); paramTable["regionHandle"] = regionHandle.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]: ForceTransferMoney: Can not money force transfer request from [{0}] to [{1}]", sender.ToString(), receiver.ToString()); } else m_log.ErrorFormat("[MONEY]: ForceTransferMoney: Money Server is not available!!"); #endregion 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) { //m_log.InfoFormat("[MONEY]: AddBankerMoney:"); bool ret = false; m_settle_user = false; if (!string.IsNullOrEmpty(m_moneyServURL)) { // Fill parameters for money transfer XML-RPC. Hashtable paramTable = new Hashtable(); paramTable["bankerUserServIP"] = m_userServIP; paramTable["bankerID"] = bankerID.ToString(); paramTable["transactionType"] = (int)TransactionType.BuyMoney; paramTable["amount"] = amount; paramTable["regionHandle"] = regionHandle.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]: AddBankerMoney: Avatar is not Banker. Web Settlemrnt is used."); } else m_log.ErrorFormat("[MONEY]: AddBankerMoney: Fail Message {0}", resultTable["message"]); } } else m_log.ErrorFormat("[MONEY]: AddBankerMoney: Money Server is not responce"); } else m_log.ErrorFormat("[MONEY]: AddBankerMoney: Money Server is not available!!"); return ret; } /// /// Send the money to avatar. Need to notify money server to update. /// /// /// The amount of money. /// /// /// return true, if successfully. /// private bool SendMoneyBalance(UUID avatarID, int amount, string secretCode) { //m_log.InfoFormat("[MONEY]: SendMoneyBalance:"); bool ret = false; if (!string.IsNullOrEmpty(m_moneyServURL)) { // Fill parameters for money transfer XML-RPC. Hashtable paramTable = new Hashtable(); paramTable["avatarUserServIP"] = m_userServIP; paramTable["avatarID"] = avatarID.ToString(); paramTable["transactionType"] = (int)MoneyTransactionType.ReferBonus; paramTable["amount"] = amount; paramTable["secretAccessCode"] = secretCode; paramTable["description"] = "Bonus to Avatar"; // Generate the request for transfer. Hashtable resultTable = genericCurrencyXMLRPCRequest(paramTable, "SendMoneyBalance"); // 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]: SendMoneyBalance: Fail Message is {0}", resultTable["message"]); } else m_log.ErrorFormat("[MONEY]: SendMoneyBalance: Money Server is not responce"); } else m_log.ErrorFormat("[MONEY]: SendMoneyBalance: Money Server is not available!!"); 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, string description) { //m_log.InfoFormat("[MONEY]: PayMoneyCharge:"); bool ret = false; IClientAPI senderClient = GetLocateClient(sender); // Handle the illegal transaction. if (senderClient==null) // receiverClient could be null. { m_log.InfoFormat("[MONEY]: PayMoneyCharge: Client {0} is not found", sender.ToString()); return false; } if (QueryBalanceFromMoneyServer(senderClient) /// Login the money server when the new client login. /// /// /// Indicate user ID of the new client. /// /// /// return true, if successfully. /// private bool LoginMoneyServer(IClientAPI client, out int balance) { //m_log.InfoFormat("[MONEY]: LoginMoneyServer:"); bool ret = false; balance = 0; #region Send money server the client info for login. Scene scene = (Scene)client.Scene; string userName = string.Empty; if (!string.IsNullOrEmpty(m_moneyServURL)) { // 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; } } } // Login the Money Server. Hashtable paramTable = new Hashtable(); paramTable["userServIP"] = m_userServIP; paramTable["openSimServIP"] = scene.RegionInfo.ServerURI.Replace(scene.RegionInfo.InternalEndPoint.Port.ToString(), scene.RegionInfo.HttpPort.ToString()); paramTable["userName"] = userName; 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]: LoginMoneyServer: Client [{0}] login Money Server {1}", client.AgentId.ToString(), m_moneyServURL); ret = true; } } else m_log.ErrorFormat("[MONEY]: LoginMoneyServer: Unable to login Money Server {0} for client [{1}]", m_moneyServURL, client.AgentId.ToString()); } else m_log.ErrorFormat("[MONEY]: LoginMoneyServer: Money Server is not available!!"); #endregion return ret; } /// /// Log off from the money server. /// /// /// Indicate user ID of the new client. /// /// /// return true, if successfully. /// private bool LogoffMoneyServer(IClientAPI client) { //m_log.InfoFormat("[MONEY]: LogoffMoneyServer:"); bool ret = false; if (!string.IsNullOrEmpty(m_moneyServURL)) { // Log off from the Money Server. Hashtable paramTable = new Hashtable(); paramTable["userServIP"] = m_userServIP; 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; } } } return ret; } /// /// 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) { //m_log.InfoFormat("[MONEY]: genericCurrencyXMLRPCRequest:"); if (reqParams.Count<=0 || string.IsNullOrEmpty(method)) return null; if (m_checkServerCert) { if (!m_moneyServURL.StartsWith("https://")) { m_log.InfoFormat("[MONEY]: 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]: 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); } catch (Exception ex) { m_log.ErrorFormat("[MONEY]: genericCurrencyXMLRPCRequest: Unable to connect to Money Server {0}", m_moneyServURL); m_log.ErrorFormat("[MONEY]: 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.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; } private int QueryBalanceFromMoneyServer(IClientAPI client) { //m_log.InfoFormat("[MONEY]: QueryBalanceFromMoneyServer:"); int ret = -1; #region Send the request to get the balance from money server for cilent. if (client!=null) { if (!string.IsNullOrEmpty(m_moneyServURL)) { Hashtable paramTable = new Hashtable(); paramTable["userServIP"] = m_userServIP; 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) { ret = (int)resultTable["clientBalance"]; } } } else { if (m_moneyServer.ContainsKey(client.AgentId)) { ret = m_moneyServer[client.AgentId]; } } if (ret < 0) { m_log.ErrorFormat("[MONEY]: QueryBalanceFromMoneyServer: Unable to query balance from Money Server {0} for client [{1}]", m_moneyServURL, client.AgentId.ToString()); } } #endregion return ret; } // private EventManager.MoneyTransferArgs GetTransactionInfo(IClientAPI client, string transactionID) { //m_log.InfoFormat("[MONEY]: GetTransactionInfo:"); EventManager.MoneyTransferArgs args = null; if (!string.IsNullOrEmpty(m_moneyServURL)) { Hashtable paramTable = new Hashtable(); paramTable["userServIP"] = m_userServIP; 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]: GetTransactionInfo: GetTransactionInfo: Fail to Request. {0}", (string)resultTable["description"]); } } else { m_log.ErrorFormat("[MONEY]: GetTransactionInfo: Invalid Response"); } } else { m_log.ErrorFormat("[MONEY]: GetTransactionInfo: Invalid Money Server URL"); } return args; } /// 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; } } } } } return client; } 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; } } } } return scene; } 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; } #endregion } }