/* * 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. */ using System; using System.Data; using System.IO; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml; using libsecondlife; using log4net; using Nini.Config; using Nwc.XmlRpc; namespace OpenSim.Framework { /// <summary> /// Miscellaneous utility functions /// </summary> public class Util { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static uint nextXferID = 5000; private static Random randomClass = new Random(); // Get a list of invalid file characters (OS dependent) private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]"; private static object XferLock = new object(); #region Vector Equations /// <summary> /// Get the distance between two 3d vectors /// </summary> /// <param name="a">A 3d vector</param> /// <param name="b">A 3d vector</param> /// <returns>The distance between the two vectors</returns> public static double GetDistanceTo(LLVector3 a, LLVector3 b) { float dx = a.X - b.X; float dy = a.Y - b.Y; float dz = a.Z - b.Z; return Math.Sqrt(dx * dx + dy * dy + dz * dz); } /// <summary> /// Get the magnitude of a 3d vector /// </summary> /// <param name="a">A 3d vector</param> /// <returns>The magnitude of the vector</returns> public static double GetMagnitude(LLVector3 a) { return Math.Sqrt((a.X * a.X) + (a.Y * a.Y) + (a.Z * a.Z)); } /// <summary> /// Get a normalized form of a 3d vector /// </summary> /// <param name="a">A 3d vector</param> /// <returns>A new vector which is normalized form of the vector</returns> /// <remarks>The vector paramater cannot be <0,0,0></remarks> public static LLVector3 GetNormalizedVector(LLVector3 a) { if (IsZeroVector(a)) throw new ArgumentException("Vector paramater cannot be a zero vector."); float Mag = (float) GetMagnitude(a); return new LLVector3(a.X / Mag, a.Y / Mag, a.Z / Mag); } /// <summary> /// Returns if a vector is a zero vector (has all zero components) /// </summary> /// <returns></returns> public static bool IsZeroVector(LLVector3 v) { if (v.X == 0 && v.Y == 0 && v.Z == 0) { return true; } return false; } # endregion public Util() { } public static Random RandomClass { get { return randomClass; } } public static ulong UIntsToLong(uint X, uint Y) { return Helpers.UIntsToLong(X, Y); } public static uint GetNextXferID() { uint id = 0; lock (XferLock) { id = nextXferID; nextXferID++; } return id; } public static string GetFileName(string file) { // Return just the filename on UNIX platforms // TODO: this should be customisable with a prefix, but that's something to do later. if (Environment.OSVersion.Platform == PlatformID.Unix) { return file; } // Return %APPDATA%/OpenSim/file for 2K/XP/NT/2K3/VISTA // TODO: Switch this to System.Enviroment.SpecialFolders.ApplicationData if (Environment.OSVersion.Platform == PlatformID.Win32NT) { if (!Directory.Exists("%APPDATA%\\OpenSim\\")) { Directory.CreateDirectory("%APPDATA%\\OpenSim"); } return "%APPDATA%\\OpenSim\\" + file; } // Catch all - covers older windows versions // (but those probably wont work anyway) return file; } /// <summary> /// Debug utility function to convert unbroken strings of XML into something human readable for occasional debugging purposes. /// /// Please don't delete me even if I appear currently unused! /// </summary> /// <param name="rawXml"></param> /// <returns></returns> public static string GetFormattedXml(string rawXml) { XmlDocument xd = new XmlDocument(); xd.LoadXml(rawXml); StringBuilder sb = new StringBuilder(); StringWriter sw = new StringWriter(sb); XmlTextWriter xtw = new XmlTextWriter(sw); xtw.Formatting = Formatting.Indented; try { xd.WriteTo(xtw); } finally { xtw.Close(); } return sb.ToString(); } public static bool IsEnvironmentSupported(ref string reason) { // Must have .NET 2.0 (Generics / libsl) if (Environment.Version.Major < 2) { reason = ".NET 1.0/1.1 lacks components that is used by OpenSim"; return false; } // Windows 95/98/ME are unsupported if (Environment.OSVersion.Platform == PlatformID.Win32Windows && Environment.OSVersion.Platform != PlatformID.Win32NT) { reason = "Windows 95/98/ME will not run OpenSim"; return false; } // Windows 2000 / Pre-SP2 XP if (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0) { reason = "Please update to Windows XP Service Pack 2 or Server2003"; return false; } return true; } public static int UnixTimeSinceEpoch() { return ToUnixTime(DateTime.UtcNow); } public static int ToUnixTime(DateTime stamp) { TimeSpan t = (stamp.ToUniversalTime() - Convert.ToDateTime("1/1/1970 8:00:00 AM")); return (int) t.TotalSeconds; } public static DateTime ToDateTime(ulong seconds) { DateTime epoch = Convert.ToDateTime("1/1/1970 8:00:00 AM"); return epoch.AddSeconds(seconds); } public static DateTime ToDateTime(int seconds) { DateTime epoch = Convert.ToDateTime("1/1/1970 8:00:00 AM"); return epoch.AddSeconds(seconds); } public static string Md5Hash(string pass) { MD5 md5 = MD5CryptoServiceProvider.Create(); byte[] dataMd5 = md5.ComputeHash(Encoding.Default.GetBytes(pass)); StringBuilder sb = new StringBuilder(); for (int i = 0; i < dataMd5.Length; i++) sb.AppendFormat("{0:x2}", dataMd5[i]); return sb.ToString(); } public static string GetRandomCapsPath() { LLUUID caps = LLUUID.Random(); string capsPath = caps.ToString(); capsPath = capsPath.Remove(capsPath.Length - 4, 4); return capsPath; } public static int fast_distance2d(int x, int y) { x = Math.Abs(x); y = Math.Abs(y); int min = Math.Min(x, y); return (x + y - (min >> 1) - (min >> 2) + (min >> 4)); } public static string FieldToString(byte[] bytes) { return FieldToString(bytes, String.Empty); } /// <summary> /// Convert a variable length field (byte array) to a string, with a /// field name prepended to each line of the output /// </summary> /// <remarks>If the byte array has unprintable characters in it, a /// hex dump will be put in the string instead</remarks> /// <param name="bytes">The byte array to convert to a string</param> /// <param name="fieldName">A field name to prepend to each line of output</param> /// <returns>An ASCII string or a string containing a hex dump, minus /// the null terminator</returns> public static string FieldToString(byte[] bytes, string fieldName) { // Check for a common case if (bytes.Length == 0) return String.Empty; StringBuilder output = new StringBuilder(); bool printable = true; for (int i = 0; i < bytes.Length; ++i) { // Check if there are any unprintable characters in the array if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09 && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00) { printable = false; break; } } if (printable) { if (fieldName.Length > 0) { output.Append(fieldName); output.Append(": "); } output.Append(CleanString(UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length - 1))); } else { for (int i = 0; i < bytes.Length; i += 16) { if (i != 0) output.Append(Environment.NewLine); if (fieldName.Length > 0) { output.Append(fieldName); output.Append(": "); } for (int j = 0; j < 16; j++) { if ((i + j) < bytes.Length) output.Append(String.Format("{0:X2} ", bytes[i + j])); else output.Append(" "); } for (int j = 0; j < 16 && (i + j) < bytes.Length; j++) { if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E) output.Append((char) bytes[i + j]); else output.Append("."); } } } return output.ToString(); } /// <summary> /// Returns a IP address from a specified DNS, favouring IPv4 addresses. /// </summary> /// <param name="dnsAddress">DNS Hostname</param> /// <returns>An IP address, or null</returns> public static IPAddress GetHostFromDNS(string dnsAddress) { // Is it already a valid IP? No need to look it up. IPAddress ipa; if (IPAddress.TryParse(dnsAddress, out ipa)) return ipa; IPAddress[] hosts = null; // Not an IP, lookup required try { hosts = Dns.GetHostEntry(dnsAddress).AddressList; } catch (Exception e) { m_log.ErrorFormat("[UTIL]: An error occurred while resolving {0}, {1}", dnsAddress, e); // Still going to throw the exception on for now, since this was what was happening in the first place throw e; } foreach (IPAddress host in hosts) { if (host.AddressFamily == AddressFamily.InterNetwork) { return host; } } if (hosts.Length > 0) return hosts[0]; return null; } public static IPAddress GetLocalHost() { string dnsAddress = "localhost"; IPAddress[] hosts = Dns.GetHostEntry(dnsAddress).AddressList; foreach (IPAddress host in hosts) { if (!IPAddress.IsLoopback(host) && host.AddressFamily == AddressFamily.InterNetwork) { return host; } } if (hosts.Length > 0) return hosts[0]; return null; } /// <summary> /// Removes all invalid path chars (OS dependent) /// </summary> /// <param name="path">path</param> /// <returns>safe path</returns> public static string safePath(string path) { return Regex.Replace(path, @regexInvalidPathChars, string.Empty); } /// <summary> /// Removes all invalid filename chars (OS dependent) /// </summary> /// <param name="path">filename</param> /// <returns>safe filename</returns> public static string safeFileName(string filename) { return Regex.Replace(filename, @regexInvalidFileChars, string.Empty); ; } // // directory locations // public static string homeDir() { string temp; // string personal=(Environment.GetFolderPath(Environment.SpecialFolder.Personal)); // temp = Path.Combine(personal,".OpenSim"); temp = "."; return temp; } public static string assetsDir() { return Path.Combine(configDir(), "assets"); } public static string inventoryDir() { return Path.Combine(configDir(), "inventory"); } public static string configDir() { return "."; } public static string dataDir() { return "."; } public static string logDir() { return "."; } // Nini (config) related Methods public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) { if (!File.Exists(fileName)) { //create new file } XmlConfigSource config = new XmlConfigSource(fileName); AddDataRowToConfig(config, row); config.Save(); return config; } public static void AddDataRowToConfig(IConfigSource config, DataRow row) { config.Configs.Add((string) row[0]); for (int i = 0; i < row.Table.Columns.Count; i++) { config.Configs[(string) row[0]].Set(row.Table.Columns[i].ColumnName, row[i]); } } public static float Clip(float x, float min, float max) { return Math.Min(Math.Max(x, min), max); } public static int Clip(int x, int min, int max) { return Math.Min(Math.Max(x, min), max); } /// <summary> /// Convert an LLUUID to a raw uuid string. Right now this is a string without hyphens. /// </summary> /// <param name="lluuid"></param> /// <returns></returns> public static String ToRawUuidString(LLUUID lluuid) { return lluuid.UUID.ToString("n"); } public static string CleanString(string input) { if (input.Length == 0) return input; int clip = input.Length; // Test for ++ string terminator int pos = input.IndexOf("\0"); if (pos != -1 && pos < clip) clip = pos; // Test for CR pos = input.IndexOf("\r"); if (pos != -1 && pos < clip) clip = pos; // Test for LF pos = input.IndexOf("\n"); if (pos != -1 && pos < clip) clip = pos; // Truncate string before first end-of-line character found return input.Substring(0, clip); } /// <summary> /// returns the contents of /etc/issue on Unix Systems /// Use this for where it's absolutely necessary to implement platform specific stuff /// </summary> /// <returns></returns> public static string ReadEtcIssue() { try { StreamReader sr = new StreamReader("/etc/issue.net"); string issue = sr.ReadToEnd(); sr.Close(); return issue; } catch (Exception) { return ""; } } public static void SerializeToFile(string filename, Object obj) { IFormatter formatter = new BinaryFormatter(); Stream stream = null; try { stream = new FileStream( filename, FileMode.Create, FileAccess.Write, FileShare.None); formatter.Serialize(stream, obj); } catch (Exception e) { System.Console.WriteLine(e.Message); System.Console.WriteLine(e.StackTrace); } finally { if (stream != null) { stream.Close(); } } } public static Object DeserializeFromFile(string filename) { IFormatter formatter = new BinaryFormatter(); Stream stream = null; Object ret = null; try { stream = new FileStream( filename, FileMode.Open, FileAccess.Read, FileShare.None); ret = formatter.Deserialize(stream); } catch (Exception e) { System.Console.WriteLine(e.Message); System.Console.WriteLine(e.StackTrace); } finally { if (stream != null) { stream.Close(); } } return ret; } public static string[] ParseStartLocationRequest(string startLocationRequest) { string[] returnstring = new string[4]; // format uri:RegionName&X&Y&Z returnstring[0] = "last"; returnstring[1] = "127"; returnstring[2] = "127"; returnstring[3] = "0"; // This is the crappy way of doing it. if (startLocationRequest.Contains(":") && startLocationRequest.Contains("&")) { //System.Console.WriteLine("StartLocationRequest Contains proper elements"); string[] splitstr = startLocationRequest.Split(':'); //,2,StringSplitOptions.RemoveEmptyEntries); //System.Console.WriteLine("Found " + splitstr.GetLength(0) + " elements in 1st split result"); if (splitstr.GetLength(0) == 2) { string[] splitstr2 = splitstr[1].Split('&'); //, 4, StringSplitOptions.RemoveEmptyEntries); //System.Console.WriteLine("Found " + splitstr2.GetLength(0) + " elements in 2nd split result"); int len = Math.Min(splitstr2.GetLength(0), 4); for (int i = 0; i < 4; ++i) { if (len > i) { returnstring[i] = splitstr2[i]; } } } } return returnstring; } public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args) { return SendXmlRpcCommand(url, methodName, args); } public static XmlRpcResponse SendXmlRpcCommand(string url, string methodName, object[] args) { XmlRpcRequest client = new XmlRpcRequest(methodName, args); return client.Send(url, 6000); } } }