opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Data;
32 using System.Diagnostics;
33 using System.Globalization;
34 using System.IO;
35 using System.IO.Compression;
36 using System.Net;
37 using System.Net.Sockets;
38 using System.Reflection;
39 using System.Runtime.InteropServices;
40 using System.Runtime.Serialization;
41 using System.Runtime.Serialization.Formatters.Binary;
42 using System.Security.Cryptography;
43 using System.Text;
44 using System.Text.RegularExpressions;
45 using System.Xml;
46 using System.Threading;
47 using log4net;
48 using log4net.Appender;
49 using Nini.Config;
50 using Nwc.XmlRpc;
51 using OpenMetaverse;
52 using OpenMetaverse.StructuredData;
53 using Amib.Threading;
54  
55 namespace OpenSim.Framework
56 {
57 [Flags]
58 public enum PermissionMask : uint
59 {
60 None = 0,
61 Transfer = 1 << 13,
62 Modify = 1 << 14,
63 Copy = 1 << 15,
64 Export = 1 << 16,
65 Move = 1 << 19,
66 Damage = 1 << 20,
67 // All does not contain Export, which is special and must be
68 // explicitly given
69 All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19)
70 }
71  
72 /// <summary>
73 /// The method used by Util.FireAndForget for asynchronously firing events
74 /// </summary>
75 /// <remarks>
76 /// None is used to execute the method in the same thread that made the call. It should only be used by regression
77 /// test code that relies on predictable event ordering.
78 /// RegressionTest is used by regression tests. It fires the call synchronously and does not catch any exceptions.
79 /// </remarks>
80 public enum FireAndForgetMethod
81 {
82 None,
83 RegressionTest,
84 UnsafeQueueUserWorkItem,
85 QueueUserWorkItem,
86 BeginInvoke,
87 SmartThreadPool,
88 Thread,
89 }
90  
91 /// <summary>
92 /// Class for delivering SmartThreadPool statistical information
93 /// </summary>
94 /// <remarks>
95 /// We do it this way so that we do not directly expose STP.
96 /// </remarks>
97 public class STPInfo
98 {
99 public string Name { get; set; }
100 public STPStartInfo STPStartInfo { get; set; }
101 public WIGStartInfo WIGStartInfo { get; set; }
102 public bool IsIdle { get; set; }
103 public bool IsShuttingDown { get; set; }
104 public int MaxThreads { get; set; }
105 public int MinThreads { get; set; }
106 public int InUseThreads { get; set; }
107 public int ActiveThreads { get; set; }
108 public int WaitingCallbacks { get; set; }
109 public int MaxConcurrentWorkItems { get; set; }
110 }
111  
112 /// <summary>
113 /// Miscellaneous utility functions
114 /// </summary>
115 public static class Util
116 {
117 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
118  
119 private static uint nextXferID = 5000;
120 private static Random randomClass = new Random();
121  
122 // Get a list of invalid file characters (OS dependent)
123 private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]";
124 private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]";
125 private static object XferLock = new object();
126  
127 /// <summary>
128 /// Thread pool used for Util.FireAndForget if FireAndForgetMethod.SmartThreadPool is used
129 /// </summary>
130 private static SmartThreadPool m_ThreadPool;
131  
132 // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC.
133 public static readonly DateTime UnixEpoch =
134 DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime();
135  
136 private static readonly string rawUUIDPattern
137 = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
138 public static readonly Regex PermissiveUUIDPattern = new Regex(rawUUIDPattern);
139 public static readonly Regex UUIDPattern = new Regex(string.Format("^{0}$", rawUUIDPattern));
140  
141 public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool;
142 public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod;
143  
144 public static bool IsPlatformMono
145 {
146 get { return Type.GetType("Mono.Runtime") != null; }
147 }
148  
149 /// <summary>
150 /// Gets the name of the directory where the current running executable
151 /// is located
152 /// </summary>
153 /// <returns>Filesystem path to the directory containing the current
154 /// executable</returns>
155 public static string ExecutingDirectory()
156 {
157 return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
158 }
159  
160 /// <summary>
161 /// Linear interpolates B<->C using percent A
162 /// </summary>
163 /// <param name="a"></param>
164 /// <param name="b"></param>
165 /// <param name="c"></param>
166 /// <returns></returns>
167 public static double lerp(double a, double b, double c)
168 {
169 return (b*a) + (c*(1 - a));
170 }
171  
172 /// <summary>
173 /// Bilinear Interpolate, see Lerp but for 2D using 'percents' X & Y.
174 /// Layout:
175 /// A B
176 /// C D
177 /// A<->C = Y
178 /// C<->D = X
179 /// </summary>
180 /// <param name="x"></param>
181 /// <param name="y"></param>
182 /// <param name="a"></param>
183 /// <param name="b"></param>
184 /// <param name="c"></param>
185 /// <param name="d"></param>
186 /// <returns></returns>
187 public static double lerp2D(double x, double y, double a, double b, double c, double d)
188 {
189 return lerp(y, lerp(x, a, b), lerp(x, c, d));
190 }
191  
192 public static Encoding UTF8 = Encoding.UTF8;
193 public static Encoding UTF8NoBomEncoding = new UTF8Encoding(false);
194  
195 /// <value>
196 /// Well known UUID for the blank texture used in the Linden SL viewer version 1.20 (and hopefully onwards)
197 /// </value>
198 public static UUID BLANK_TEXTURE_UUID = new UUID("5748decc-f629-461c-9a36-a35a221fe21f");
199  
200 #region Vector Equations
201  
202 /// <summary>
203 /// Get the distance between two 3d vectors
204 /// </summary>
205 /// <param name="a">A 3d vector</param>
206 /// <param name="b">A 3d vector</param>
207 /// <returns>The distance between the two vectors</returns>
208 public static double GetDistanceTo(Vector3 a, Vector3 b)
209 {
210 float dx = a.X - b.X;
211 float dy = a.Y - b.Y;
212 float dz = a.Z - b.Z;
213 return Math.Sqrt(dx * dx + dy * dy + dz * dz);
214 }
215  
216 /// <summary>
217 /// Returns true if the distance beween A and B is less than amount. Significantly faster than GetDistanceTo since it eliminates the Sqrt.
218 /// </summary>
219 /// <param name="a"></param>
220 /// <param name="b"></param>
221 /// <param name="amount"></param>
222 /// <returns></returns>
223 public static bool DistanceLessThan(Vector3 a, Vector3 b, double amount)
224 {
225 float dx = a.X - b.X;
226 float dy = a.Y - b.Y;
227 float dz = a.Z - b.Z;
228 return (dx*dx + dy*dy + dz*dz) < (amount*amount);
229 }
230  
231 /// <summary>
232 /// Get the magnitude of a 3d vector
233 /// </summary>
234 /// <param name="a">A 3d vector</param>
235 /// <returns>The magnitude of the vector</returns>
236 public static double GetMagnitude(Vector3 a)
237 {
238 return Math.Sqrt((a.X * a.X) + (a.Y * a.Y) + (a.Z * a.Z));
239 }
240  
241 /// <summary>
242 /// Get a normalized form of a 3d vector
243 /// </summary>
244 /// <param name="a">A 3d vector</param>
245 /// <returns>A new vector which is normalized form of the vector</returns>
246 /// <remarks>The vector paramater cannot be <0,0,0></remarks>
247 public static Vector3 GetNormalizedVector(Vector3 a)
248 {
249 if (IsZeroVector(a))
250 throw new ArgumentException("Vector paramater cannot be a zero vector.");
251  
252 float Mag = (float) GetMagnitude(a);
253 return new Vector3(a.X / Mag, a.Y / Mag, a.Z / Mag);
254 }
255  
256 /// <summary>
257 /// Returns if a vector is a zero vector (has all zero components)
258 /// </summary>
259 /// <returns></returns>
260 public static bool IsZeroVector(Vector3 v)
261 {
262 if (v.X == 0 && v.Y == 0 && v.Z == 0)
263 {
264 return true;
265 }
266  
267 return false;
268 }
269  
270 # endregion
271  
272 public static Quaternion Axes2Rot(Vector3 fwd, Vector3 left, Vector3 up)
273 {
274 float s;
275 float tr = (float) (fwd.X + left.Y + up.Z + 1.0);
276  
277 if (tr >= 1.0)
278 {
279 s = (float) (0.5 / Math.Sqrt(tr));
280 return new Quaternion(
281 (left.Z - up.Y) * s,
282 (up.X - fwd.Z) * s,
283 (fwd.Y - left.X) * s,
284 (float) 0.25 / s);
285 }
286 else
287 {
288 float max = (left.Y > up.Z) ? left.Y : up.Z;
289  
290 if (max < fwd.X)
291 {
292 s = (float) (Math.Sqrt(fwd.X - (left.Y + up.Z) + 1.0));
293 float x = (float) (s * 0.5);
294 s = (float) (0.5 / s);
295 return new Quaternion(
296 x,
297 (fwd.Y + left.X) * s,
298 (up.X + fwd.Z) * s,
299 (left.Z - up.Y) * s);
300 }
301 else if (max == left.Y)
302 {
303 s = (float) (Math.Sqrt(left.Y - (up.Z + fwd.X) + 1.0));
304 float y = (float) (s * 0.5);
305 s = (float) (0.5 / s);
306 return new Quaternion(
307 (fwd.Y + left.X) * s,
308 y,
309 (left.Z + up.Y) * s,
310 (up.X - fwd.Z) * s);
311 }
312 else
313 {
314 s = (float) (Math.Sqrt(up.Z - (fwd.X + left.Y) + 1.0));
315 float z = (float) (s * 0.5);
316 s = (float) (0.5 / s);
317 return new Quaternion(
318 (up.X + fwd.Z) * s,
319 (left.Z + up.Y) * s,
320 z,
321 (fwd.Y - left.X) * s);
322 }
323 }
324 }
325  
326 public static Random RandomClass
327 {
328 get { return randomClass; }
329 }
330  
331 public static ulong UIntsToLong(uint X, uint Y)
332 {
333 return Utils.UIntsToLong(X, Y);
334 }
335  
336 // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong.
337 // Several places rely on the ability to extract a region's location from its handle.
338 // Note the location is in 'world coordinates' (see below).
339 // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0.
340 public static ulong RegionWorldLocToHandle(uint X, uint Y)
341 {
342 return Utils.UIntsToLong(X, Y);
343 }
344  
345 public static ulong RegionLocToHandle(uint X, uint Y)
346 {
347 return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y));
348 }
349  
350 public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y)
351 {
352 X = (uint)(handle >> 32);
353 Y = (uint)(handle & (ulong)uint.MaxValue);
354 }
355  
356 public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y)
357 {
358 uint worldX, worldY;
359 RegionHandleToWorldLoc(handle, out worldX, out worldY);
360 X = WorldToRegionLoc(worldX);
361 Y = WorldToRegionLoc(worldY);
362 }
363  
364 // A region location can be 'world coordinates' (meters from zero) or 'region coordinates'
365 // (number of regions from zero). This measurement of regions relies on the legacy 256 region size.
366 // These routines exist to make what is being converted explicit so the next person knows what was meant.
367 // Convert a region's 'world coordinate' to its 'region coordinate'.
368 public static uint WorldToRegionLoc(uint worldCoord)
369 {
370 return worldCoord / Constants.RegionSize;
371 }
372  
373 // Convert a region's 'region coordinate' to its 'world coordinate'.
374 public static uint RegionToWorldLoc(uint regionCoord)
375 {
376 return regionCoord * Constants.RegionSize;
377 }
378  
379 public static T Clamp<T>(T x, T min, T max)
380 where T : IComparable<T>
381 {
382 return x.CompareTo(max) > 0 ? max :
383 x.CompareTo(min) < 0 ? min :
384 x;
385 }
386  
387 // Clamp the maximum magnitude of a vector
388 public static Vector3 ClampV(Vector3 x, float max)
389 {
390 float lenSq = x.LengthSquared();
391 if (lenSq > (max * max))
392 {
393 x = x / x.Length() * max;
394 }
395  
396 return x;
397 }
398  
399 // Inclusive, within range test (true if equal to the endpoints)
400 public static bool InRange<T>(T x, T min, T max)
401 where T : IComparable<T>
402 {
403 return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0;
404 }
405  
406 public static uint GetNextXferID()
407 {
408 uint id = 0;
409 lock (XferLock)
410 {
411 id = nextXferID;
412 nextXferID++;
413 }
414 return id;
415 }
416  
417 public static string GetFileName(string file)
418 {
419 // Return just the filename on UNIX platforms
420 // TODO: this should be customisable with a prefix, but that's something to do later.
421 if (Environment.OSVersion.Platform == PlatformID.Unix)
422 {
423 return file;
424 }
425  
426 // Return %APPDATA%/OpenSim/file for 2K/XP/NT/2K3/VISTA
427 // TODO: Switch this to System.Enviroment.SpecialFolders.ApplicationData
428 if (Environment.OSVersion.Platform == PlatformID.Win32NT)
429 {
430 if (!Directory.Exists("%APPDATA%\\OpenSim\\"))
431 {
432 Directory.CreateDirectory("%APPDATA%\\OpenSim");
433 }
434  
435 return "%APPDATA%\\OpenSim\\" + file;
436 }
437  
438 // Catch all - covers older windows versions
439 // (but those probably wont work anyway)
440 return file;
441 }
442  
443 /// <summary>
444 /// Debug utility function to convert OSD into formatted XML for debugging purposes.
445 /// </summary>
446 /// <param name="osd">
447 /// A <see cref="OSD"/>
448 /// </param>
449 /// <returns>
450 /// A <see cref="System.String"/>
451 /// </returns>
452 public static string GetFormattedXml(OSD osd)
453 {
454 return GetFormattedXml(OSDParser.SerializeLLSDXmlString(osd));
455 }
456  
457 /// <summary>
458 /// Debug utility function to convert unbroken strings of XML into something human readable for occasional debugging purposes.
459 /// </summary>
460 /// <remarks>
461 /// Please don't delete me even if I appear currently unused!
462 /// </remarks>
463 /// <param name="rawXml"></param>
464 /// <returns></returns>
465 public static string GetFormattedXml(string rawXml)
466 {
467 XmlDocument xd = new XmlDocument();
468 xd.LoadXml(rawXml);
469  
470 StringBuilder sb = new StringBuilder();
471 StringWriter sw = new StringWriter(sb);
472  
473 XmlTextWriter xtw = new XmlTextWriter(sw);
474 xtw.Formatting = Formatting.Indented;
475  
476 try
477 {
478 xd.WriteTo(xtw);
479 }
480 finally
481 {
482 xtw.Close();
483 }
484  
485 return sb.ToString();
486 }
487  
488 /// <summary>
489 /// Is the platform Windows?
490 /// </summary>
491 /// <returns>true if so, false otherwise</returns>
492 public static bool IsWindows()
493 {
494 PlatformID platformId = Environment.OSVersion.Platform;
495  
496 return (platformId == PlatformID.Win32NT
497 || platformId == PlatformID.Win32S
498 || platformId == PlatformID.Win32Windows
499 || platformId == PlatformID.WinCE);
500 }
501  
502 public static bool LoadArchSpecificWindowsDll(string libraryName)
503 {
504 // We do this so that OpenSimulator on Windows loads the correct native library depending on whether
505 // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports
506 // will find it already loaded later on.
507 //
508 // This isn't necessary for other platforms (e.g. Mac OSX and Linux) since the DLL used can be
509 // controlled in config files.
510 string nativeLibraryPath;
511  
512 if (Util.Is64BitProcess())
513 nativeLibraryPath = "lib64/" + libraryName;
514 else
515 nativeLibraryPath = "lib32/" + libraryName;
516  
517 m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath);
518  
519 if (Util.LoadLibrary(nativeLibraryPath) == IntPtr.Zero)
520 {
521 m_log.ErrorFormat(
522 "[UTIL]: Couldn't find native Windows library at {0}", nativeLibraryPath);
523  
524 return false;
525 }
526 else
527 {
528 return true;
529 }
530 }
531  
532 public static bool IsEnvironmentSupported(ref string reason)
533 {
534 // Must have .NET 2.0 (Generics / libsl)
535 if (Environment.Version.Major < 2)
536 {
537 reason = ".NET 1.0/1.1 lacks components that is used by OpenSim";
538 return false;
539 }
540  
541 // Windows 95/98/ME are unsupported
542 if (Environment.OSVersion.Platform == PlatformID.Win32Windows &&
543 Environment.OSVersion.Platform != PlatformID.Win32NT)
544 {
545 reason = "Windows 95/98/ME will not run OpenSim";
546 return false;
547 }
548  
549 // Windows 2000 / Pre-SP2 XP
550 if (Environment.OSVersion.Version.Major == 5 &&
551 Environment.OSVersion.Version.Minor == 0)
552 {
553 reason = "Please update to Windows XP Service Pack 2 or Server2003";
554 return false;
555 }
556  
557 return true;
558 }
559  
560 public static int UnixTimeSinceEpoch()
561 {
562 return ToUnixTime(DateTime.UtcNow);
563 }
564  
565 public static int ToUnixTime(DateTime stamp)
566 {
567 TimeSpan t = stamp.ToUniversalTime() - UnixEpoch;
568 return (int)t.TotalSeconds;
569 }
570  
571 public static DateTime ToDateTime(ulong seconds)
572 {
573 return UnixEpoch.AddSeconds(seconds);
574 }
575  
576 public static DateTime ToDateTime(int seconds)
577 {
578 return UnixEpoch.AddSeconds(seconds);
579 }
580  
581 /// <summary>
582 /// Return an md5 hash of the given string
583 /// </summary>
584 /// <param name="data"></param>
585 /// <returns></returns>
586 public static string Md5Hash(string data)
587 {
588 byte[] dataMd5 = ComputeMD5Hash(data);
589 StringBuilder sb = new StringBuilder();
590 for (int i = 0; i < dataMd5.Length; i++)
591 sb.AppendFormat("{0:x2}", dataMd5[i]);
592 return sb.ToString();
593 }
594  
595 private static byte[] ComputeMD5Hash(string data)
596 {
597 MD5 md5 = MD5.Create();
598 return md5.ComputeHash(Encoding.Default.GetBytes(data));
599 }
600  
601 /// <summary>
602 /// Return an SHA1 hash
603 /// </summary>
604 /// <param name="data"></param>
605 /// <returns></returns>
606 public static string SHA1Hash(string data)
607 {
608 return SHA1Hash(Encoding.Default.GetBytes(data));
609 }
610  
611 /// <summary>
612 /// Return an SHA1 hash
613 /// </summary>
614 /// <param name="data"></param>
615 /// <returns></returns>
616 public static string SHA1Hash(byte[] data)
617 {
618 byte[] hash = ComputeSHA1Hash(data);
619 return BitConverter.ToString(hash).Replace("-", String.Empty);
620 }
621  
622 private static byte[] ComputeSHA1Hash(byte[] src)
623 {
624 SHA1CryptoServiceProvider SHA1 = new SHA1CryptoServiceProvider();
625 return SHA1.ComputeHash(src);
626 }
627  
628 public static int fast_distance2d(int x, int y)
629 {
630 x = Math.Abs(x);
631 y = Math.Abs(y);
632  
633 int min = Math.Min(x, y);
634  
635 return (x + y - (min >> 1) - (min >> 2) + (min >> 4));
636 }
637  
638 /// <summary>
639 /// Determines whether a point is inside a bounding box.
640 /// </summary>
641 /// <param name='v'></param>
642 /// <param name='min'></param>
643 /// <param name='max'></param>
644 /// <returns></returns>
645 public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max)
646 {
647 return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z
648 && v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z;
649 }
650  
651 /// <summary>
652 /// Are the co-ordinates of the new region visible from the old region?
653 /// </summary>
654 /// <param name="oldx">Old region x-coord</param>
655 /// <param name="newx">New region x-coord</param>
656 /// <param name="oldy">Old region y-coord</param>
657 /// <param name="newy">New region y-coord</param>
658 /// <returns></returns>
659 public static bool IsOutsideView(float drawdist, uint oldx, uint newx, uint oldy, uint newy)
660 {
661 int dd = (int)((drawdist + Constants.RegionSize - 1) / Constants.RegionSize);
662  
663 int startX = (int)oldx - dd;
664 int startY = (int)oldy - dd;
665  
666 int endX = (int)oldx + dd;
667 int endY = (int)oldy + dd;
668  
669 return (newx < startX || endX < newx || newy < startY || endY < newy);
670 }
671  
672 public static string FieldToString(byte[] bytes)
673 {
674 return FieldToString(bytes, String.Empty);
675 }
676  
677 /// <summary>
678 /// Convert a variable length field (byte array) to a string, with a
679 /// field name prepended to each line of the output
680 /// </summary>
681 /// <remarks>If the byte array has unprintable characters in it, a
682 /// hex dump will be put in the string instead</remarks>
683 /// <param name="bytes">The byte array to convert to a string</param>
684 /// <param name="fieldName">A field name to prepend to each line of output</param>
685 /// <returns>An ASCII string or a string containing a hex dump, minus
686 /// the null terminator</returns>
687 public static string FieldToString(byte[] bytes, string fieldName)
688 {
689 // Check for a common case
690 if (bytes.Length == 0) return String.Empty;
691  
692 StringBuilder output = new StringBuilder();
693 bool printable = true;
694  
695 for (int i = 0; i < bytes.Length; ++i)
696 {
697 // Check if there are any unprintable characters in the array
698 if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
699 && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
700 {
701 printable = false;
702 break;
703 }
704 }
705  
706 if (printable)
707 {
708 if (fieldName.Length > 0)
709 {
710 output.Append(fieldName);
711 output.Append(": ");
712 }
713  
714 output.Append(CleanString(Util.UTF8.GetString(bytes, 0, bytes.Length - 1)));
715 }
716 else
717 {
718 for (int i = 0; i < bytes.Length; i += 16)
719 {
720 if (i != 0)
721 output.Append(Environment.NewLine);
722 if (fieldName.Length > 0)
723 {
724 output.Append(fieldName);
725 output.Append(": ");
726 }
727  
728 for (int j = 0; j < 16; j++)
729 {
730 if ((i + j) < bytes.Length)
731 output.Append(String.Format("{0:X2} ", bytes[i + j]));
732 else
733 output.Append(" ");
734 }
735  
736 for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
737 {
738 if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
739 output.Append((char) bytes[i + j]);
740 else
741 output.Append(".");
742 }
743 }
744 }
745  
746 return output.ToString();
747 }
748  
749 /// <summary>
750 /// Converts a URL to a IPAddress
751 /// </summary>
752 /// <param name="url">URL Standard Format</param>
753 /// <returns>A resolved IP Address</returns>
754 public static IPAddress GetHostFromURL(string url)
755 {
756 return GetHostFromDNS(url.Split(new char[] {'/', ':'})[3]);
757 }
758  
759 /// <summary>
760 /// Returns a IP address from a specified DNS, favouring IPv4 addresses.
761 /// </summary>
762 /// <param name="dnsAddress">DNS Hostname</param>
763 /// <returns>An IP address, or null</returns>
764 public static IPAddress GetHostFromDNS(string dnsAddress)
765 {
766 // Is it already a valid IP? No need to look it up.
767 IPAddress ipa;
768 if (IPAddress.TryParse(dnsAddress, out ipa))
769 return ipa;
770  
771 IPAddress[] hosts = null;
772  
773 // Not an IP, lookup required
774 try
775 {
776 hosts = Dns.GetHostEntry(dnsAddress).AddressList;
777 }
778 catch (Exception e)
779 {
780 m_log.WarnFormat("[UTIL]: An error occurred while resolving host name {0}, {1}", dnsAddress, e);
781  
782 // Still going to throw the exception on for now, since this was what was happening in the first place
783 throw e;
784 }
785  
786 foreach (IPAddress host in hosts)
787 {
788 if (host.AddressFamily == AddressFamily.InterNetwork)
789 {
790 return host;
791 }
792 }
793  
794 if (hosts.Length > 0)
795 return hosts[0];
796  
797 return null;
798 }
799  
800 public static Uri GetURI(string protocol, string hostname, int port, string path)
801 {
802 return new UriBuilder(protocol, hostname, port, path).Uri;
803 }
804  
805 /// <summary>
806 /// Gets a list of all local system IP addresses
807 /// </summary>
808 /// <returns></returns>
809 public static IPAddress[] GetLocalHosts()
810 {
811 return Dns.GetHostAddresses(Dns.GetHostName());
812 }
813  
814 public static IPAddress GetLocalHost()
815 {
816 IPAddress[] iplist = GetLocalHosts();
817  
818 if (iplist.Length == 0) // No accessible external interfaces
819 {
820 IPAddress[] loopback = Dns.GetHostAddresses("localhost");
821 IPAddress localhost = loopback[0];
822  
823 return localhost;
824 }
825  
826 foreach (IPAddress host in iplist)
827 {
828 if (!IPAddress.IsLoopback(host) && host.AddressFamily == AddressFamily.InterNetwork)
829 {
830 return host;
831 }
832 }
833  
834 if (iplist.Length > 0)
835 {
836 foreach (IPAddress host in iplist)
837 {
838 if (host.AddressFamily == AddressFamily.InterNetwork)
839 return host;
840 }
841 // Well all else failed...
842 return iplist[0];
843 }
844  
845 return null;
846 }
847  
848 /// <summary>
849 /// Removes all invalid path chars (OS dependent)
850 /// </summary>
851 /// <param name="path">path</param>
852 /// <returns>safe path</returns>
853 public static string safePath(string path)
854 {
855 return Regex.Replace(path, regexInvalidPathChars, String.Empty);
856 }
857  
858 /// <summary>
859 /// Removes all invalid filename chars (OS dependent)
860 /// </summary>
861 /// <param name="path">filename</param>
862 /// <returns>safe filename</returns>
863 public static string safeFileName(string filename)
864 {
865 return Regex.Replace(filename, regexInvalidFileChars, String.Empty);
866 ;
867 }
868  
869 //
870 // directory locations
871 //
872  
873 public static string homeDir()
874 {
875 string temp;
876 // string personal=(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
877 // temp = Path.Combine(personal,".OpenSim");
878 temp = ".";
879 return temp;
880 }
881  
882 public static string assetsDir()
883 {
884 return Path.Combine(configDir(), "assets");
885 }
886  
887 public static string inventoryDir()
888 {
889 return Path.Combine(configDir(), "inventory");
890 }
891  
892 public static string configDir()
893 {
894 return ".";
895 }
896  
897 public static string dataDir()
898 {
899 return ".";
900 }
901  
902 public static string logFile()
903 {
904 foreach (IAppender appender in LogManager.GetRepository().GetAppenders())
905 {
906 if (appender is FileAppender)
907 {
908 return ((FileAppender)appender).File;
909 }
910 }
911  
912 return "./OpenSim.log";
913 }
914  
915 public static string logDir()
916 {
917 return Path.GetDirectoryName(logFile());
918 }
919  
920 // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html
921 public static string GetUniqueFilename(string FileName)
922 {
923 int count = 0;
924 string Name;
925  
926 if (File.Exists(FileName))
927 {
928 FileInfo f = new FileInfo(FileName);
929  
930 if (!String.IsNullOrEmpty(f.Extension))
931 {
932 Name = f.FullName.Substring(0, f.FullName.LastIndexOf('.'));
933 }
934 else
935 {
936 Name = f.FullName;
937 }
938  
939 while (File.Exists(FileName))
940 {
941 count++;
942 FileName = Name + count + f.Extension;
943 }
944 }
945 return FileName;
946 }
947  
948 #region Nini (config) related Methods
949 public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName)
950 {
951 if (!File.Exists(fileName))
952 {
953 //create new file
954 }
955 XmlConfigSource config = new XmlConfigSource(fileName);
956 AddDataRowToConfig(config, row);
957 config.Save();
958  
959 return config;
960 }
961  
962 public static void AddDataRowToConfig(IConfigSource config, DataRow row)
963 {
964 config.Configs.Add((string) row[0]);
965 for (int i = 0; i < row.Table.Columns.Count; i++)
966 {
967 config.Configs[(string) row[0]].Set(row.Table.Columns[i].ColumnName, row[i]);
968 }
969 }
970  
971 public static string GetConfigVarWithDefaultSection(IConfigSource config, string varname, string section)
972 {
973 // First, check the Startup section, the default section
974 IConfig cnf = config.Configs["Startup"];
975 if (cnf == null)
976 return string.Empty;
977 string val = cnf.GetString(varname, string.Empty);
978  
979 // Then check for an overwrite of the default in the given section
980 if (!string.IsNullOrEmpty(section))
981 {
982 cnf = config.Configs[section];
983 if (cnf != null)
984 val = cnf.GetString(varname, val);
985 }
986  
987 return val;
988 }
989  
990 /// <summary>
991 /// Gets the value of a configuration variable by looking into
992 /// multiple sections in order. The latter sections overwrite
993 /// any values previously found.
994 /// </summary>
995 /// <typeparam name="T">Type of the variable</typeparam>
996 /// <param name="config">The configuration object</param>
997 /// <param name="varname">The configuration variable</param>
998 /// <param name="sections">Ordered sequence of sections to look at</param>
999 /// <returns></returns>
1000 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections)
1001 {
1002 return GetConfigVarFromSections<T>(config, varname, sections, default(T));
1003 }
1004  
1005 /// <summary>
1006 /// Gets the value of a configuration variable by looking into
1007 /// multiple sections in order. The latter sections overwrite
1008 /// any values previously found.
1009 /// </summary>
1010 /// <remarks>
1011 /// If no value is found then the given default value is returned
1012 /// </remarks>
1013 /// <typeparam name="T">Type of the variable</typeparam>
1014 /// <param name="config">The configuration object</param>
1015 /// <param name="varname">The configuration variable</param>
1016 /// <param name="sections">Ordered sequence of sections to look at</param>
1017 /// <param name="val">Default value</param>
1018 /// <returns></returns>
1019 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections, object val)
1020 {
1021 foreach (string section in sections)
1022 {
1023 IConfig cnf = config.Configs[section];
1024 if (cnf == null)
1025 continue;
1026  
1027 if (typeof(T) == typeof(String))
1028 val = cnf.GetString(varname, (string)val);
1029 else if (typeof(T) == typeof(Boolean))
1030 val = cnf.GetBoolean(varname, (bool)val);
1031 else if (typeof(T) == typeof(Int32))
1032 val = cnf.GetInt(varname, (int)val);
1033 else if (typeof(T) == typeof(float))
1034 val = cnf.GetFloat(varname, (float)val);
1035 else
1036 m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T));
1037 }
1038  
1039 return (T)val;
1040 }
1041  
1042 #endregion
1043  
1044 public static float Clip(float x, float min, float max)
1045 {
1046 return Math.Min(Math.Max(x, min), max);
1047 }
1048  
1049 public static int Clip(int x, int min, int max)
1050 {
1051 return Math.Min(Math.Max(x, min), max);
1052 }
1053  
1054 public static Vector3 Clip(Vector3 vec, float min, float max)
1055 {
1056 return new Vector3(Clip(vec.X, min, max), Clip(vec.Y, min, max),
1057 Clip(vec.Z, min, max));
1058 }
1059  
1060 /// <summary>
1061 /// Convert an UUID to a raw uuid string. Right now this is a string without hyphens.
1062 /// </summary>
1063 /// <param name="UUID"></param>
1064 /// <returns></returns>
1065 public static String ToRawUuidString(UUID UUID)
1066 {
1067 return UUID.Guid.ToString("n");
1068 }
1069  
1070 public static string CleanString(string input)
1071 {
1072 if (input.Length == 0)
1073 return input;
1074  
1075 int clip = input.Length;
1076  
1077 // Test for ++ string terminator
1078 int pos = input.IndexOf("\0");
1079 if (pos != -1 && pos < clip)
1080 clip = pos;
1081  
1082 // Test for CR
1083 pos = input.IndexOf("\r");
1084 if (pos != -1 && pos < clip)
1085 clip = pos;
1086  
1087 // Test for LF
1088 pos = input.IndexOf("\n");
1089 if (pos != -1 && pos < clip)
1090 clip = pos;
1091  
1092 // Truncate string before first end-of-line character found
1093 return input.Substring(0, clip);
1094 }
1095  
1096 /// <summary>
1097 /// returns the contents of /etc/issue on Unix Systems
1098 /// Use this for where it's absolutely necessary to implement platform specific stuff
1099 /// </summary>
1100 /// <returns></returns>
1101 public static string ReadEtcIssue()
1102 {
1103 try
1104 {
1105 StreamReader sr = new StreamReader("/etc/issue.net");
1106 string issue = sr.ReadToEnd();
1107 sr.Close();
1108 return issue;
1109 }
1110 catch (Exception)
1111 {
1112 return "";
1113 }
1114 }
1115  
1116 public static void SerializeToFile(string filename, Object obj)
1117 {
1118 IFormatter formatter = new BinaryFormatter();
1119 Stream stream = null;
1120  
1121 try
1122 {
1123 stream = new FileStream(
1124 filename, FileMode.Create,
1125 FileAccess.Write, FileShare.None);
1126  
1127 formatter.Serialize(stream, obj);
1128 }
1129 catch (Exception e)
1130 {
1131 m_log.Error(e.ToString());
1132 }
1133 finally
1134 {
1135 if (stream != null)
1136 {
1137 stream.Close();
1138 }
1139 }
1140 }
1141  
1142 public static Object DeserializeFromFile(string filename)
1143 {
1144 IFormatter formatter = new BinaryFormatter();
1145 Stream stream = null;
1146 Object ret = null;
1147  
1148 try
1149 {
1150 stream = new FileStream(
1151 filename, FileMode.Open,
1152 FileAccess.Read, FileShare.None);
1153  
1154 ret = formatter.Deserialize(stream);
1155 }
1156 catch (Exception e)
1157 {
1158 m_log.Error(e.ToString());
1159 }
1160 finally
1161 {
1162 if (stream != null)
1163 {
1164 stream.Close();
1165 }
1166 }
1167  
1168 return ret;
1169 }
1170  
1171 public static string Compress(string text)
1172 {
1173 byte[] buffer = Util.UTF8.GetBytes(text);
1174 MemoryStream memory = new MemoryStream();
1175 using (GZipStream compressor = new GZipStream(memory, CompressionMode.Compress, true))
1176 {
1177 compressor.Write(buffer, 0, buffer.Length);
1178 }
1179  
1180 memory.Position = 0;
1181  
1182 byte[] compressed = new byte[memory.Length];
1183 memory.Read(compressed, 0, compressed.Length);
1184  
1185 byte[] compressedBuffer = new byte[compressed.Length + 4];
1186 Buffer.BlockCopy(compressed, 0, compressedBuffer, 4, compressed.Length);
1187 Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, compressedBuffer, 0, 4);
1188 return Convert.ToBase64String(compressedBuffer);
1189 }
1190  
1191 public static string Decompress(string compressedText)
1192 {
1193 byte[] compressedBuffer = Convert.FromBase64String(compressedText);
1194 using (MemoryStream memory = new MemoryStream())
1195 {
1196 int msgLength = BitConverter.ToInt32(compressedBuffer, 0);
1197 memory.Write(compressedBuffer, 4, compressedBuffer.Length - 4);
1198  
1199 byte[] buffer = new byte[msgLength];
1200  
1201 memory.Position = 0;
1202 using (GZipStream decompressor = new GZipStream(memory, CompressionMode.Decompress))
1203 {
1204 decompressor.Read(buffer, 0, buffer.Length);
1205 }
1206  
1207 return Util.UTF8.GetString(buffer);
1208 }
1209 }
1210  
1211 /// <summary>
1212 /// Copy data from one stream to another, leaving the read position of both streams at the beginning.
1213 /// </summary>
1214 /// <param name='inputStream'>
1215 /// Input stream. Must be seekable.
1216 /// </param>
1217 /// <exception cref='ArgumentException'>
1218 /// Thrown if the input stream is not seekable.
1219 /// </exception>
1220 public static Stream Copy(Stream inputStream)
1221 {
1222 if (!inputStream.CanSeek)
1223 throw new ArgumentException("Util.Copy(Stream inputStream) must receive an inputStream that can seek");
1224  
1225 const int readSize = 256;
1226 byte[] buffer = new byte[readSize];
1227 MemoryStream ms = new MemoryStream();
1228  
1229 int count = inputStream.Read(buffer, 0, readSize);
1230  
1231 while (count > 0)
1232 {
1233 ms.Write(buffer, 0, count);
1234 count = inputStream.Read(buffer, 0, readSize);
1235 }
1236  
1237 ms.Position = 0;
1238 inputStream.Position = 0;
1239  
1240 return ms;
1241 }
1242  
1243 public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args)
1244 {
1245 return SendXmlRpcCommand(url, methodName, args);
1246 }
1247  
1248 public static XmlRpcResponse SendXmlRpcCommand(string url, string methodName, object[] args)
1249 {
1250 XmlRpcRequest client = new XmlRpcRequest(methodName, args);
1251 return client.Send(url, 6000);
1252 }
1253  
1254 /// <summary>
1255 /// Returns an error message that the user could not be found in the database
1256 /// </summary>
1257 /// <returns>XML string consisting of a error element containing individual error(s)</returns>
1258 public static XmlRpcResponse CreateUnknownUserErrorResponse()
1259 {
1260 XmlRpcResponse response = new XmlRpcResponse();
1261 Hashtable responseData = new Hashtable();
1262 responseData["error_type"] = "unknown_user";
1263 responseData["error_desc"] = "The user requested is not in the database";
1264  
1265 response.Value = responseData;
1266 return response;
1267 }
1268  
1269 /// <summary>
1270 /// Converts a byte array in big endian order into an ulong.
1271 /// </summary>
1272 /// <param name="bytes">
1273 /// The array of bytes
1274 /// </param>
1275 /// <returns>
1276 /// The extracted ulong
1277 /// </returns>
1278 public static ulong BytesToUInt64Big(byte[] bytes)
1279 {
1280 if (bytes.Length < 8) return 0;
1281 return ((ulong)bytes[0] << 56) | ((ulong)bytes[1] << 48) | ((ulong)bytes[2] << 40) | ((ulong)bytes[3] << 32) |
1282 ((ulong)bytes[4] << 24) | ((ulong)bytes[5] << 16) | ((ulong)bytes[6] << 8) | (ulong)bytes[7];
1283 }
1284  
1285 // used for RemoteParcelRequest (for "About Landmark")
1286 public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y)
1287 {
1288 byte[] bytes =
1289 {
1290 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
1291 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
1292 (byte)x, (byte)(x >> 8), 0, 0,
1293 (byte)y, (byte)(y >> 8), 0, 0 };
1294 return new UUID(bytes, 0);
1295 }
1296  
1297 public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y, uint z)
1298 {
1299 byte[] bytes =
1300 {
1301 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
1302 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
1303 (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8),
1304 (byte)y, (byte)(y >> 8), 0, 0 };
1305 return new UUID(bytes, 0);
1306 }
1307  
1308 public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y)
1309 {
1310 byte[] bytes = parcelID.GetBytes();
1311 regionHandle = Utils.BytesToUInt64(bytes);
1312 x = Utils.BytesToUInt(bytes, 8) & 0xffff;
1313 y = Utils.BytesToUInt(bytes, 12) & 0xffff;
1314 }
1315  
1316 public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y, out uint z)
1317 {
1318 byte[] bytes = parcelID.GetBytes();
1319 regionHandle = Utils.BytesToUInt64(bytes);
1320 x = Utils.BytesToUInt(bytes, 8) & 0xffff;
1321 z = (Utils.BytesToUInt(bytes, 8) & 0xffff0000) >> 16;
1322 y = Utils.BytesToUInt(bytes, 12) & 0xffff;
1323 }
1324  
1325 public static void FakeParcelIDToGlobalPosition(UUID parcelID, out uint x, out uint y)
1326 {
1327 ulong regionHandle;
1328 uint rx, ry;
1329  
1330 ParseFakeParcelID(parcelID, out regionHandle, out x, out y);
1331 Utils.LongToUInts(regionHandle, out rx, out ry);
1332  
1333 x += rx;
1334 y += ry;
1335 }
1336  
1337 /// <summary>
1338 /// Get operating system information if available. Returns only the first 45 characters of information
1339 /// </summary>
1340 /// <returns>
1341 /// Operating system information. Returns an empty string if none was available.
1342 /// </returns>
1343 public static string GetOperatingSystemInformation()
1344 {
1345 string os = String.Empty;
1346  
1347 if (Environment.OSVersion.Platform != PlatformID.Unix)
1348 {
1349 os = Environment.OSVersion.ToString();
1350 }
1351 else
1352 {
1353 os = ReadEtcIssue();
1354 }
1355  
1356 if (os.Length > 45)
1357 {
1358 os = os.Substring(0, 45);
1359 }
1360  
1361 return os;
1362 }
1363  
1364 public static string GetRuntimeInformation()
1365 {
1366 string ru = String.Empty;
1367  
1368 if (Environment.OSVersion.Platform == PlatformID.Unix)
1369 ru = "Unix/Mono";
1370 else
1371 if (Environment.OSVersion.Platform == PlatformID.MacOSX)
1372 ru = "OSX/Mono";
1373 else
1374 {
1375 if (IsPlatformMono)
1376 ru = "Win/Mono";
1377 else
1378 ru = "Win/.NET";
1379 }
1380  
1381 return ru;
1382 }
1383  
1384 /// <summary>
1385 /// Is the given string a UUID?
1386 /// </summary>
1387 /// <param name="s"></param>
1388 /// <returns></returns>
1389 public static bool isUUID(string s)
1390 {
1391 return UUIDPattern.IsMatch(s);
1392 }
1393  
1394 public static string GetDisplayConnectionString(string connectionString)
1395 {
1396 int passPosition = 0;
1397 int passEndPosition = 0;
1398 string displayConnectionString = null;
1399  
1400 // hide the password in the connection string
1401 passPosition = connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase);
1402 passPosition = connectionString.IndexOf("=", passPosition);
1403 if (passPosition < connectionString.Length)
1404 passPosition += 1;
1405 passEndPosition = connectionString.IndexOf(";", passPosition);
1406  
1407 displayConnectionString = connectionString.Substring(0, passPosition);
1408 displayConnectionString += "***";
1409 displayConnectionString += connectionString.Substring(passEndPosition, connectionString.Length - passEndPosition);
1410  
1411 return displayConnectionString;
1412 }
1413  
1414 public static T ReadSettingsFromIniFile<T>(IConfig config, T settingsClass)
1415 {
1416 Type settingsType = settingsClass.GetType();
1417  
1418 FieldInfo[] fieldInfos = settingsType.GetFields();
1419 foreach (FieldInfo fieldInfo in fieldInfos)
1420 {
1421 if (!fieldInfo.IsStatic)
1422 {
1423 if (fieldInfo.FieldType == typeof(System.String))
1424 {
1425 fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass)));
1426 }
1427 else if (fieldInfo.FieldType == typeof(System.Boolean))
1428 {
1429 fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass)));
1430 }
1431 else if (fieldInfo.FieldType == typeof(System.Int32))
1432 {
1433 fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass)));
1434 }
1435 else if (fieldInfo.FieldType == typeof(System.Single))
1436 {
1437 fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass)));
1438 }
1439 else if (fieldInfo.FieldType == typeof(System.UInt32))
1440 {
1441 fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString())));
1442 }
1443 }
1444 }
1445  
1446 PropertyInfo[] propertyInfos = settingsType.GetProperties();
1447 foreach (PropertyInfo propInfo in propertyInfos)
1448 {
1449 if ((propInfo.CanRead) && (propInfo.CanWrite))
1450 {
1451 if (propInfo.PropertyType == typeof(System.String))
1452 {
1453 propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null);
1454 }
1455 else if (propInfo.PropertyType == typeof(System.Boolean))
1456 {
1457 propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null);
1458 }
1459 else if (propInfo.PropertyType == typeof(System.Int32))
1460 {
1461 propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null);
1462 }
1463 else if (propInfo.PropertyType == typeof(System.Single))
1464 {
1465 propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null);
1466 }
1467 if (propInfo.PropertyType == typeof(System.UInt32))
1468 {
1469 propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null);
1470 }
1471 }
1472 }
1473  
1474 return settingsClass;
1475 }
1476  
1477 public static string Base64ToString(string str)
1478 {
1479 Decoder utf8Decode = Encoding.UTF8.GetDecoder();
1480  
1481 byte[] todecode_byte = Convert.FromBase64String(str);
1482 int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
1483 char[] decoded_char = new char[charCount];
1484 utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
1485 string result = new String(decoded_char);
1486 return result;
1487 }
1488  
1489 public static Guid GetHashGuid(string data, string salt)
1490 {
1491 byte[] hash = ComputeMD5Hash(data + salt);
1492  
1493 //string s = BitConverter.ToString(hash);
1494  
1495 Guid guid = new Guid(hash);
1496  
1497 return guid;
1498 }
1499  
1500 public static byte ConvertMaturityToAccessLevel(uint maturity)
1501 {
1502 byte retVal = 0;
1503 switch (maturity)
1504 {
1505 case 0: //PG
1506 retVal = 13;
1507 break;
1508 case 1: //Mature
1509 retVal = 21;
1510 break;
1511 case 2: // Adult
1512 retVal = 42;
1513 break;
1514 }
1515  
1516 return retVal;
1517  
1518 }
1519  
1520 public static uint ConvertAccessLevelToMaturity(byte maturity)
1521 {
1522 if (maturity <= 13)
1523 return 0;
1524 else if (maturity <= 21)
1525 return 1;
1526 else
1527 return 2;
1528 }
1529  
1530 /// <summary>
1531 /// Produces an OSDMap from its string representation on a stream
1532 /// </summary>
1533 /// <param name="data">The stream</param>
1534 /// <param name="length">The size of the data on the stream</param>
1535 /// <returns>The OSDMap or an exception</returns>
1536 public static OSDMap GetOSDMap(Stream stream, int length)
1537 {
1538 byte[] data = new byte[length];
1539 stream.Read(data, 0, length);
1540 string strdata = Util.UTF8.GetString(data);
1541 OSDMap args = null;
1542 OSD buffer;
1543 buffer = OSDParser.DeserializeJson(strdata);
1544 if (buffer.Type == OSDType.Map)
1545 {
1546 args = (OSDMap)buffer;
1547 return args;
1548 }
1549 return null;
1550 }
1551  
1552 public static OSDMap GetOSDMap(string data)
1553 {
1554 OSDMap args = null;
1555 try
1556 {
1557 OSD buffer;
1558 // We should pay attention to the content-type, but let's assume we know it's Json
1559 buffer = OSDParser.DeserializeJson(data);
1560 if (buffer.Type == OSDType.Map)
1561 {
1562 args = (OSDMap)buffer;
1563 return args;
1564 }
1565 else
1566 {
1567 // uh?
1568 m_log.Debug(("[UTILS]: Got OSD of unexpected type " + buffer.Type.ToString()));
1569 return null;
1570 }
1571 }
1572 catch (Exception ex)
1573 {
1574 m_log.Debug("[UTILS]: exception on GetOSDMap " + ex.Message);
1575 return null;
1576 }
1577 }
1578  
1579 public static string[] Glob(string path)
1580 {
1581 string vol=String.Empty;
1582  
1583 if (Path.VolumeSeparatorChar != Path.DirectorySeparatorChar)
1584 {
1585 string[] vcomps = path.Split(new char[] {Path.VolumeSeparatorChar}, 2, StringSplitOptions.RemoveEmptyEntries);
1586  
1587 if (vcomps.Length > 1)
1588 {
1589 path = vcomps[1];
1590 vol = vcomps[0];
1591 }
1592 }
1593  
1594 string[] comps = path.Split(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries);
1595  
1596 // Glob
1597  
1598 path = vol;
1599 if (vol != String.Empty)
1600 path += new String(new char[] {Path.VolumeSeparatorChar, Path.DirectorySeparatorChar});
1601 else
1602 path = new String(new char[] {Path.DirectorySeparatorChar});
1603  
1604 List<string> paths = new List<string>();
1605 List<string> found = new List<string>();
1606 paths.Add(path);
1607  
1608 int compIndex = -1;
1609 foreach (string c in comps)
1610 {
1611 compIndex++;
1612  
1613 List<string> addpaths = new List<string>();
1614 foreach (string p in paths)
1615 {
1616 string[] dirs = Directory.GetDirectories(p, c);
1617  
1618 if (dirs.Length != 0)
1619 {
1620 foreach (string dir in dirs)
1621 addpaths.Add(Path.Combine(path, dir));
1622 }
1623  
1624 // Only add files if that is the last path component
1625 if (compIndex == comps.Length - 1)
1626 {
1627 string[] files = Directory.GetFiles(p, c);
1628 foreach (string f in files)
1629 found.Add(f);
1630 }
1631 }
1632 paths = addpaths;
1633 }
1634  
1635 return found.ToArray();
1636 }
1637  
1638 public static string ServerURI(string uri)
1639 {
1640 if (uri == string.Empty)
1641 return string.Empty;
1642  
1643 // Get rid of eventual slashes at the end
1644 uri = uri.TrimEnd('/');
1645  
1646 IPAddress ipaddr1 = null;
1647 string port1 = "";
1648 try
1649 {
1650 ipaddr1 = Util.GetHostFromURL(uri);
1651 }
1652 catch { }
1653  
1654 try
1655 {
1656 port1 = uri.Split(new char[] { ':' })[2];
1657 }
1658 catch { }
1659  
1660 // We tried our best to convert the domain names to IP addresses
1661 return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri;
1662 }
1663  
1664 /// <summary>
1665 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary.
1666 /// </summary>
1667 /// <param name="str">
1668 /// If null or empty, then an bytes[0] is returned.
1669 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1670 /// </param>
1671 /// <param name="args">
1672 /// Arguments to substitute into the string via the {} mechanism.
1673 /// </param>
1674 /// <returns></returns>
1675 public static byte[] StringToBytes256(string str, params object[] args)
1676 {
1677 return StringToBytes256(string.Format(str, args));
1678 }
1679  
1680 /// <summary>
1681 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary.
1682 /// </summary>
1683 /// <param name="str">
1684 /// If null or empty, then an bytes[0] is returned.
1685 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1686 /// </param>
1687 /// <returns></returns>
1688 public static byte[] StringToBytes256(string str)
1689 {
1690 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1691 if (str.Length > 254) str = str.Remove(254);
1692 if (!str.EndsWith("\0")) { str += "\0"; }
1693  
1694 // Because this is UTF-8 encoding and not ASCII, it's possible we
1695 // might have gotten an oversized array even after the string trim
1696 byte[] data = UTF8.GetBytes(str);
1697 if (data.Length > 256)
1698 {
1699 Array.Resize<byte>(ref data, 256);
1700 data[255] = 0;
1701 }
1702  
1703 return data;
1704 }
1705  
1706 /// <summary>
1707 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary.
1708 /// </summary>
1709 /// <param name="str">
1710 /// If null or empty, then an bytes[0] is returned.
1711 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1712 /// </param>
1713 /// <param name="args">
1714 /// Arguments to substitute into the string via the {} mechanism.
1715 /// </param>
1716 /// <returns></returns>
1717 public static byte[] StringToBytes1024(string str, params object[] args)
1718 {
1719 return StringToBytes1024(string.Format(str, args));
1720 }
1721  
1722 /// <summary>
1723 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary.
1724 /// </summary>
1725 /// <param name="str">
1726 /// If null or empty, then an bytes[0] is returned.
1727 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1728 /// </param>
1729 /// <returns></returns>
1730 public static byte[] StringToBytes1024(string str)
1731 {
1732 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1733 if (str.Length > 1023) str = str.Remove(1023);
1734 if (!str.EndsWith("\0")) { str += "\0"; }
1735  
1736 // Because this is UTF-8 encoding and not ASCII, it's possible we
1737 // might have gotten an oversized array even after the string trim
1738 byte[] data = UTF8.GetBytes(str);
1739 if (data.Length > 1024)
1740 {
1741 Array.Resize<byte>(ref data, 1024);
1742 data[1023] = 0;
1743 }
1744  
1745 return data;
1746 }
1747  
1748 /// <summary>
1749 /// Used to trigger an early library load on Windows systems.
1750 /// </summary>
1751 /// <remarks>
1752 /// Required to get 32-bit and 64-bit processes to automatically use the
1753 /// appropriate native library.
1754 /// </remarks>
1755 /// <param name="dllToLoad"></param>
1756 /// <returns></returns>
1757 [DllImport("kernel32.dll")]
1758 public static extern IntPtr LoadLibrary(string dllToLoad);
1759  
1760 /// <summary>
1761 /// Determine whether the current process is 64 bit
1762 /// </summary>
1763 /// <returns>true if so, false if not</returns>
1764 public static bool Is64BitProcess()
1765 {
1766 return IntPtr.Size == 8;
1767 }
1768  
1769 #region FireAndForget Threading Pattern
1770  
1771 /// <summary>
1772 /// Created to work around a limitation in Mono with nested delegates
1773 /// </summary>
1774 private sealed class FireAndForgetWrapper
1775 {
1776 private static volatile FireAndForgetWrapper instance;
1777 private static object syncRoot = new Object();
1778  
1779 public static FireAndForgetWrapper Instance {
1780 get {
1781  
1782 if (instance == null)
1783 {
1784 lock (syncRoot)
1785 {
1786 if (instance == null)
1787 {
1788 instance = new FireAndForgetWrapper();
1789 }
1790 }
1791 }
1792  
1793 return instance;
1794 }
1795 }
1796  
1797 public void FireAndForget(System.Threading.WaitCallback callback)
1798 {
1799 callback.BeginInvoke(null, EndFireAndForget, callback);
1800 }
1801  
1802 public void FireAndForget(System.Threading.WaitCallback callback, object obj)
1803 {
1804 callback.BeginInvoke(obj, EndFireAndForget, callback);
1805 }
1806  
1807 private static void EndFireAndForget(IAsyncResult ar)
1808 {
1809 System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState;
1810  
1811 try { callback.EndInvoke(ar); }
1812 catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); }
1813  
1814 ar.AsyncWaitHandle.Close();
1815 }
1816 }
1817  
1818 public static void FireAndForget(System.Threading.WaitCallback callback)
1819 {
1820 FireAndForget(callback, null);
1821 }
1822  
1823 public static void InitThreadPool(int minThreads, int maxThreads)
1824 {
1825 if (maxThreads < 2)
1826 throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2");
1827 if (minThreads > maxThreads || minThreads < 2)
1828 throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads");
1829 if (m_ThreadPool != null)
1830 throw new InvalidOperationException("SmartThreadPool is already initialized");
1831  
1832 STPStartInfo startInfo = new STPStartInfo();
1833 startInfo.ThreadPoolName = "Util";
1834 startInfo.IdleTimeout = 2000;
1835 startInfo.MaxWorkerThreads = maxThreads;
1836 startInfo.MinWorkerThreads = minThreads;
1837  
1838 m_ThreadPool = new SmartThreadPool(startInfo);
1839 }
1840  
1841 public static int FireAndForgetCount()
1842 {
1843 const int MAX_SYSTEM_THREADS = 200;
1844  
1845 switch (FireAndForgetMethod)
1846 {
1847 case FireAndForgetMethod.UnsafeQueueUserWorkItem:
1848 case FireAndForgetMethod.QueueUserWorkItem:
1849 case FireAndForgetMethod.BeginInvoke:
1850 int workerThreads, iocpThreads;
1851 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
1852 return workerThreads;
1853 case FireAndForgetMethod.SmartThreadPool:
1854 return m_ThreadPool.MaxThreads - m_ThreadPool.InUseThreads;
1855 case FireAndForgetMethod.Thread:
1856 return MAX_SYSTEM_THREADS - System.Diagnostics.Process.GetCurrentProcess().Threads.Count;
1857 default:
1858 throw new NotImplementedException();
1859 }
1860 }
1861  
1862 public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
1863 {
1864 WaitCallback realCallback;
1865  
1866 if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
1867 {
1868 // If we're running regression tests, then we want any exceptions to rise up to the test code.
1869 realCallback = o => { Culture.SetCurrentCulture(); callback(o); };
1870 }
1871 else
1872 {
1873 // When OpenSim interacts with a database or sends data over the wire, it must send this in en_US culture
1874 // so that we don't encounter problems where, for instance, data is saved with a culture that uses commas
1875 // for decimals places but is read by a culture that treats commas as number seperators.
1876 realCallback = o =>
1877 {
1878 Culture.SetCurrentCulture();
1879  
1880 try
1881 {
1882 callback(o);
1883 }
1884 catch (Exception e)
1885 {
1886 m_log.ErrorFormat(
1887 "[UTIL]: Continuing after async_call_method thread terminated with exception {0}{1}",
1888 e.Message, e.StackTrace);
1889 }
1890 };
1891 }
1892  
1893 switch (FireAndForgetMethod)
1894 {
1895 case FireAndForgetMethod.RegressionTest:
1896 case FireAndForgetMethod.None:
1897 realCallback.Invoke(obj);
1898 break;
1899 case FireAndForgetMethod.UnsafeQueueUserWorkItem:
1900 ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj);
1901 break;
1902 case FireAndForgetMethod.QueueUserWorkItem:
1903 ThreadPool.QueueUserWorkItem(realCallback, obj);
1904 break;
1905 case FireAndForgetMethod.BeginInvoke:
1906 FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance;
1907 wrapper.FireAndForget(realCallback, obj);
1908 break;
1909 case FireAndForgetMethod.SmartThreadPool:
1910 if (m_ThreadPool == null)
1911 InitThreadPool(2, 15);
1912 m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
1913 break;
1914 case FireAndForgetMethod.Thread:
1915 Thread thread = new Thread(delegate(object o) { realCallback(o); });
1916 thread.Start(obj);
1917 break;
1918 default:
1919 throw new NotImplementedException();
1920 }
1921 }
1922  
1923 /// <summary>
1924 /// Get information about the current state of the smart thread pool.
1925 /// </summary>
1926 /// <returns>
1927 /// null if this isn't the pool being used for non-scriptengine threads.
1928 /// </returns>
1929 public static STPInfo GetSmartThreadPoolInfo()
1930 {
1931 if (m_ThreadPool == null)
1932 return null;
1933  
1934 STPInfo stpi = new STPInfo();
1935 stpi.Name = m_ThreadPool.Name;
1936 stpi.STPStartInfo = m_ThreadPool.STPStartInfo;
1937 stpi.IsIdle = m_ThreadPool.IsIdle;
1938 stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown;
1939 stpi.MaxThreads = m_ThreadPool.MaxThreads;
1940 stpi.MinThreads = m_ThreadPool.MinThreads;
1941 stpi.InUseThreads = m_ThreadPool.InUseThreads;
1942 stpi.ActiveThreads = m_ThreadPool.ActiveThreads;
1943 stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks;
1944 stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency;
1945  
1946 return stpi;
1947 }
1948  
1949 #endregion FireAndForget Threading Pattern
1950  
1951 /// <summary>
1952 /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
1953 /// and negative every 24.9 days. This trims down TickCount so it doesn't wrap
1954 /// for the callers.
1955 /// This trims it to a 12 day interval so don't let your frame time get too long.
1956 /// </summary>
1957 /// <returns></returns>
1958 public static Int32 EnvironmentTickCount()
1959 {
1960 return Environment.TickCount & EnvironmentTickCountMask;
1961 }
1962 const Int32 EnvironmentTickCountMask = 0x3fffffff;
1963  
1964 /// <summary>
1965 /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
1966 /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
1967 /// 'EnvironmentTickCount()') and accounts for any wrapping.
1968 /// </summary>
1969 /// <param name="newValue"></param>
1970 /// <param name="prevValue"></param>
1971 /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
1972 public static Int32 EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue)
1973 {
1974 Int32 diff = newValue - prevValue;
1975 return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
1976 }
1977  
1978 /// <summary>
1979 /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
1980 /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
1981 /// 'EnvironmentTickCount()') and accounts for any wrapping.
1982 /// </summary>
1983 /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
1984 public static Int32 EnvironmentTickCountSubtract(Int32 prevValue)
1985 {
1986 return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue);
1987 }
1988  
1989 // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount
1990 // Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount().
1991 // A positive return value indicates A occured later than B
1992 public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB)
1993 {
1994 // A, B and TC are all between 0 and 0x3fffffff
1995 int tc = EnvironmentTickCount();
1996  
1997 if (tc - tcA >= 0)
1998 tcA += EnvironmentTickCountMask + 1;
1999  
2000 if (tc - tcB >= 0)
2001 tcB += EnvironmentTickCountMask + 1;
2002  
2003 return tcA - tcB;
2004 }
2005  
2006 /// <summary>
2007 /// Prints the call stack at any given point. Useful for debugging.
2008 /// </summary>
2009 public static void PrintCallStack()
2010 {
2011 PrintCallStack(m_log.DebugFormat);
2012 }
2013  
2014 public delegate void DebugPrinter(string msg, params Object[] parm);
2015 public static void PrintCallStack(DebugPrinter printer)
2016 {
2017 StackTrace stackTrace = new StackTrace(true); // get call stack
2018 StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
2019  
2020 // write call stack method names
2021 foreach (StackFrame stackFrame in stackFrames)
2022 {
2023 MethodBase mb = stackFrame.GetMethod();
2024 printer("{0}.{1}:{2}", mb.DeclaringType, mb.Name, stackFrame.GetFileLineNumber()); // write method name
2025 }
2026 }
2027  
2028 /// <summary>
2029 /// Gets the client IP address
2030 /// </summary>
2031 /// <param name="xff"></param>
2032 /// <returns></returns>
2033 public static IPEndPoint GetClientIPFromXFF(string xff)
2034 {
2035 if (xff == string.Empty)
2036 return null;
2037  
2038 string[] parts = xff.Split(new char[] { ',' });
2039 if (parts.Length > 0)
2040 {
2041 try
2042 {
2043 return new IPEndPoint(IPAddress.Parse(parts[0]), 0);
2044 }
2045 catch (Exception e)
2046 {
2047 m_log.WarnFormat("[UTIL]: Exception parsing XFF header {0}: {1}", xff, e.Message);
2048 }
2049 }
2050  
2051 return null;
2052 }
2053  
2054 public static string GetCallerIP(Hashtable req)
2055 {
2056 if (req.ContainsKey("headers"))
2057 {
2058 try
2059 {
2060 Hashtable headers = (Hashtable)req["headers"];
2061 if (headers.ContainsKey("remote_addr") && headers["remote_addr"] != null)
2062 return headers["remote_addr"].ToString();
2063 }
2064 catch (Exception e)
2065 {
2066 m_log.WarnFormat("[UTIL]: exception in GetCallerIP: {0}", e.Message);
2067 }
2068 }
2069 return string.Empty;
2070 }
2071  
2072 #region Xml Serialization Utilities
2073 public static bool ReadBoolean(XmlTextReader reader)
2074 {
2075 // AuroraSim uses "int" for some fields that are boolean in OpenSim, e.g. "PassCollisions". Don't fail because of this.
2076 reader.ReadStartElement();
2077 string val = reader.ReadContentAsString().ToLower();
2078 bool result = val.Equals("true") || val.Equals("1");
2079 reader.ReadEndElement();
2080  
2081 return result;
2082 }
2083  
2084 public static UUID ReadUUID(XmlTextReader reader, string name)
2085 {
2086 UUID id;
2087 string idStr;
2088  
2089 reader.ReadStartElement(name);
2090  
2091 if (reader.Name == "Guid")
2092 idStr = reader.ReadElementString("Guid");
2093 else if (reader.Name == "UUID")
2094 idStr = reader.ReadElementString("UUID");
2095 else // no leading tag
2096 idStr = reader.ReadContentAsString();
2097 UUID.TryParse(idStr, out id);
2098 reader.ReadEndElement();
2099  
2100 return id;
2101 }
2102  
2103 public static Vector3 ReadVector(XmlTextReader reader, string name)
2104 {
2105 Vector3 vec;
2106  
2107 reader.ReadStartElement(name);
2108 vec.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // X or x
2109 vec.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Y or y
2110 vec.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Z or z
2111 reader.ReadEndElement();
2112  
2113 return vec;
2114 }
2115  
2116 public static Quaternion ReadQuaternion(XmlTextReader reader, string name)
2117 {
2118 Quaternion quat = new Quaternion();
2119  
2120 reader.ReadStartElement(name);
2121 while (reader.NodeType != XmlNodeType.EndElement)
2122 {
2123 switch (reader.Name.ToLower())
2124 {
2125 case "x":
2126 quat.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2127 break;
2128 case "y":
2129 quat.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2130 break;
2131 case "z":
2132 quat.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2133 break;
2134 case "w":
2135 quat.W = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2136 break;
2137 }
2138 }
2139  
2140 reader.ReadEndElement();
2141  
2142 return quat;
2143 }
2144  
2145 public static T ReadEnum<T>(XmlTextReader reader, string name)
2146 {
2147 string value = reader.ReadElementContentAsString(name, String.Empty);
2148 // !!!!! to deal with flags without commas
2149 if (value.Contains(" ") && !value.Contains(","))
2150 value = value.Replace(" ", ", ");
2151  
2152 return (T)Enum.Parse(typeof(T), value); ;
2153 }
2154 #endregion
2155  
2156 #region Universal User Identifiers
2157 /// <summary>
2158 /// </summary>
2159 /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param>
2160 /// <param name="uuid">the uuid part</param>
2161 /// <param name="url">the endpoint part (e.g. http://foo.com)</param>
2162 /// <param name="firstname">the first name part (e.g. Test)</param>
2163 /// <param name="lastname">the last name part (e.g User)</param>
2164 /// <param name="secret">the secret part</param>
2165 public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret)
2166 {
2167 uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty;
2168  
2169 string[] parts = value.Split(';');
2170 if (parts.Length >= 1)
2171 if (!UUID.TryParse(parts[0], out uuid))
2172 return false;
2173  
2174 if (parts.Length >= 2)
2175 url = parts[1];
2176  
2177 if (parts.Length >= 3)
2178 {
2179 string[] name = parts[2].Split();
2180 if (name.Length == 2)
2181 {
2182 firstname = name[0];
2183 lastname = name[1];
2184 }
2185 }
2186 if (parts.Length >= 4)
2187 secret = parts[3];
2188  
2189 return true;
2190 }
2191  
2192 /// <summary>
2193 /// Produces a universal (HG) system-facing identifier given the information
2194 /// </summary>
2195 /// <param name="acircuit"></param>
2196 /// <returns>uuid[;homeURI[;first last]]</returns>
2197 public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit)
2198 {
2199 if (acircuit.ServiceURLs.ContainsKey("HomeURI"))
2200 return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString());
2201 else
2202 return acircuit.AgentID.ToString();
2203 }
2204  
2205 /// <summary>
2206 /// Produces a universal (HG) system-facing identifier given the information
2207 /// </summary>
2208 /// <param name="id">UUID of the user</param>
2209 /// <param name="firstName">first name (e.g Test)</param>
2210 /// <param name="lastName">last name (e.g. User)</param>
2211 /// <param name="homeURI">homeURI (e.g. http://foo.com)</param>
2212 /// <returns>a string of the form uuid[;homeURI[;first last]]</returns>
2213 public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI)
2214 {
2215 string agentsURI = homeURI;
2216 if (!agentsURI.EndsWith("/"))
2217 agentsURI += "/";
2218  
2219 // This is ugly, but there's no other way, given that the name is changed
2220 // in the agent circuit data for foreigners
2221 if (lastName.Contains("@"))
2222 {
2223 string[] parts = firstName.Split(new char[] { '.' });
2224 if (parts.Length == 2)
2225 return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1];
2226 }
2227 return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName;
2228  
2229 }
2230  
2231 /// <summary>
2232 /// Produces a universal (HG) user-facing name given the information
2233 /// </summary>
2234 /// <param name="firstName"></param>
2235 /// <param name="lastName"></param>
2236 /// <param name="homeURI"></param>
2237 /// <returns>string of the form first.last @foo.com or first last</returns>
2238 public static string UniversalName(String firstName, String lastName, String homeURI)
2239 {
2240 Uri uri = null;
2241 try
2242 {
2243 uri = new Uri(homeURI);
2244 }
2245 catch (UriFormatException)
2246 {
2247 return firstName + " " + lastName;
2248 }
2249 return firstName + "." + lastName + " " + "@" + uri.Authority;
2250 }
2251 #endregion
2252  
2253 /// <summary>
2254 /// Escapes the special characters used in "LIKE".
2255 /// </summary>
2256 /// <remarks>
2257 /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz"
2258 /// </remarks>
2259 public static string EscapeForLike(string str)
2260 {
2261 return str.Replace("_", "\\_").Replace("%", "\\%");
2262 }
2263 }
2264  
2265 public class DoubleQueue<T> where T:class
2266 {
2267 private Queue<T> m_lowQueue = new Queue<T>();
2268 private Queue<T> m_highQueue = new Queue<T>();
2269  
2270 private object m_syncRoot = new object();
2271 private Semaphore m_s = new Semaphore(0, 1);
2272  
2273 public DoubleQueue()
2274 {
2275 }
2276  
2277 public virtual int Count
2278 {
2279 get { return m_highQueue.Count + m_lowQueue.Count; }
2280 }
2281  
2282 public virtual void Enqueue(T data)
2283 {
2284 Enqueue(m_lowQueue, data);
2285 }
2286  
2287 public virtual void EnqueueLow(T data)
2288 {
2289 Enqueue(m_lowQueue, data);
2290 }
2291  
2292 public virtual void EnqueueHigh(T data)
2293 {
2294 Enqueue(m_highQueue, data);
2295 }
2296  
2297 private void Enqueue(Queue<T> q, T data)
2298 {
2299 lock (m_syncRoot)
2300 {
2301 q.Enqueue(data);
2302 m_s.WaitOne(0);
2303 m_s.Release();
2304 }
2305 }
2306  
2307 public virtual T Dequeue()
2308 {
2309 return Dequeue(Timeout.Infinite);
2310 }
2311  
2312 public virtual T Dequeue(int tmo)
2313 {
2314 return Dequeue(TimeSpan.FromMilliseconds(tmo));
2315 }
2316  
2317 public virtual T Dequeue(TimeSpan wait)
2318 {
2319 T res = null;
2320  
2321 if (!Dequeue(wait, ref res))
2322 return null;
2323  
2324 return res;
2325 }
2326  
2327 public bool Dequeue(int timeout, ref T res)
2328 {
2329 return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res);
2330 }
2331  
2332 public bool Dequeue(TimeSpan wait, ref T res)
2333 {
2334 if (!m_s.WaitOne(wait))
2335 return false;
2336  
2337 lock (m_syncRoot)
2338 {
2339 if (m_highQueue.Count > 0)
2340 res = m_highQueue.Dequeue();
2341 else if (m_lowQueue.Count > 0)
2342 res = m_lowQueue.Dequeue();
2343  
2344 if (m_highQueue.Count == 0 && m_lowQueue.Count == 0)
2345 return true;
2346  
2347 try
2348 {
2349 m_s.Release();
2350 }
2351 catch
2352 {
2353 }
2354  
2355 return true;
2356 }
2357 }
2358  
2359 public virtual void Clear()
2360 {
2361  
2362 lock (m_syncRoot)
2363 {
2364 // Make sure sem count is 0
2365 m_s.WaitOne(0);
2366  
2367 m_lowQueue.Clear();
2368 m_highQueue.Clear();
2369 }
2370 }
2371 }
2372 }