/* * Copyright (c) 2021-2022, Sjofn LLC * All rights reserved. * * - 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. * - Neither the name of the openmetaverse.co 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using OpenMetaverse; using OpenMetaverse.Http; using OpenMetaverse.Packets; using OpenMetaverse.StructuredData; namespace LibreMetaverse { class UserReport { public enum ReportType : byte { NullReport = 0, // don't use me. UnknownReport = 1, BugReport = 2, // obsolete ComplaintReport = 3, CsRequestReport = 4 } /// A reference to the current instance private GridClient Client; /// /// Construct a new instance of the UserReport class /// /// A reference to the current instance UserReport(GridClient client) { Client = client; } /// /// Fetch a list of Abuse Report categories from the simulator /// /// Returns Dictionary of Abuse Report categories from the server public Task> FetchAbuseReportCategories() { return FetchAbuseReportCategoriesAsync(null); } /// /// Fetch a list of Abuse Report categories from the simulator /// /// language to return categories in /// Returns Dictionary of Abuse Report categories from the server public async Task> FetchAbuseReportCategoriesAsync(string lang) { Dictionary reportCategories = null; Uri abuseCategoriesCap = Client.Network.CurrentSim.Caps.CapabilityURI("AbuseCategories"); if (abuseCategoriesCap == null) { Logger.Log("AbuseCategories capability does not exist. Could not fetch categories list.", Helpers.LogLevel.Info); } if (lang != null) { // shite C# nonsense UriBuilder builder = new UriBuilder(abuseCategoriesCap) { Query = $"lc={lang}" }; abuseCategoriesCap = builder.Uri; } await Client.HttpCapsClient.GetRequestAsync(abuseCategoriesCap, CancellationToken.None, (response, data, error) => { if (error != null) { Logger.Log($"Could not fetch abuse categories from cap. ({error.Message}", Helpers.LogLevel.Info); return; } OSD result = OSDParser.Deserialize(data); if (result is OSDMap respMap && respMap.ContainsKey("categories")) { if (respMap["categories"] is OSDArray categories) { reportCategories = categories.Cast().ToDictionary( row => row["description_localized"].AsString(), row => row["category"].AsString()); } } }); return reportCategories; } /// /// Sends user report first by trying SendUserReport sim cap, falling back to legacy /// /// /// /// /// /// /// /// /// /// /// public void SendUserReport(ReportType reportType, int category, UUID screenshotId, UUID objectId, UUID abuserId, string abuseRegionName, UUID abuseRegionId, Vector3 pos, string summary, string details) { OSDMap report = new OSDMap { ["report-type"] = (byte)reportType, ["category"] = (byte)category, ["check-flags"] = (byte)0u, // this is not used ["screenshot-id"] = screenshotId, ["object-id"] = objectId, ["abuser-id"] = abuserId, ["abuse-region-name"] = "", ["abuse-region-id"] = UUID.Zero, ["position"] = pos, ["summary"] = summary, ["version-string"] = "<3 LibreMetaverse", ["details"] = details }; Uri userReportCap = (screenshotId != UUID.Zero) ? Client.Network.CurrentSim.Caps.CapabilityURI("SendUserReportWithScreenshot") : Client.Network.CurrentSim.Caps.CapabilityURI("SendUserReport"); if (userReportCap != null) { _ = Client.HttpCapsClient.PostRequestAsync(userReportCap, OSDFormat.Xml, report, CancellationToken.None, (response, data, error) => { if (error != null) { Logger.Log($"Failed to send abuse report via {userReportCap}. " + $"({error.Message}) Falling back to legacy protocol.", Helpers.LogLevel.Warning); SendUserReportLegacy(reportType, category, screenshotId, objectId, abuserId, abuseRegionName, abuseRegionId, pos, summary, details); } }); } else { SendUserReportLegacy(reportType, category, screenshotId, objectId, abuserId, abuseRegionName, abuseRegionId, pos, summary, details); } } /// /// Sends user report using legacy lludp packet /// /// /// /// /// /// /// /// /// /// /// public void SendUserReportLegacy(ReportType reportType, int category, UUID screenshotId, UUID objectId, UUID abuserId, string abuseRegionName, UUID abuseRegionId, Vector3 pos, string summary, string details) { UserReportPacket urp = new UserReportPacket() { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID }, ReportData = { ReportType = (byte)reportType, Category = (byte)category, CheckFlags = (byte)0u, // unused ScreenshotID = screenshotId, ObjectID = objectId, AbuserID = abuserId, AbuseRegionName = Utils.StringToBytes(abuseRegionName), AbuseRegionID = abuseRegionId, Position = pos, Summary = Utils.StringToBytes(summary), Details = Utils.StringToBytes(details), VersionString = Utils.StringToBytes("<3 LibreMetaverse") } }; Client.Network.SendPacket(urp); } } }