aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Util.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Util.cs3087
1 files changed, 3087 insertions, 0 deletions
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
new file mode 100644
index 0000000..a5f798d
--- /dev/null
+++ b/OpenSim/Framework/Util.cs
@@ -0,0 +1,3087 @@
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
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Data;
32using System.Diagnostics;
33using System.Globalization;
34using System.IO;
35using System.IO.Compression;
36using System.Net;
37using System.Net.Sockets;
38using System.Reflection;
39using System.Runtime.InteropServices;
40using System.Runtime.Serialization;
41using System.Runtime.Serialization.Formatters.Binary;
42using System.Security.Cryptography;
43using System.Text;
44using System.Text.RegularExpressions;
45using System.Xml;
46using System.Threading;
47using log4net;
48using log4net.Appender;
49using Nini.Config;
50using Nwc.XmlRpc;
51using OpenMetaverse;
52using OpenMetaverse.StructuredData;
53using Amib.Threading;
54using System.Collections.Concurrent;
55using System.Collections.Specialized;
56using System.Web;
57
58namespace OpenSim.Framework
59{
60 [Flags]
61 public enum PermissionMask : uint
62 {
63 None = 0,
64 Transfer = 1 << 13,
65 Modify = 1 << 14,
66 Copy = 1 << 15,
67 Export = 1 << 16,
68 Move = 1 << 19,
69 Damage = 1 << 20,
70 // All does not contain Export, which is special and must be
71 // explicitly given
72 All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19)
73 }
74
75 /// <summary>
76 /// The method used by Util.FireAndForget for asynchronously firing events
77 /// </summary>
78 /// <remarks>
79 /// None is used to execute the method in the same thread that made the call. It should only be used by regression
80 /// test code that relies on predictable event ordering.
81 /// RegressionTest is used by regression tests. It fires the call synchronously and does not catch any exceptions.
82 /// </remarks>
83 public enum FireAndForgetMethod
84 {
85 None,
86 RegressionTest,
87 UnsafeQueueUserWorkItem,
88 QueueUserWorkItem,
89 BeginInvoke,
90 SmartThreadPool,
91 Thread,
92 }
93
94 /// <summary>
95 /// Class for delivering SmartThreadPool statistical information
96 /// </summary>
97 /// <remarks>
98 /// We do it this way so that we do not directly expose STP.
99 /// </remarks>
100 public class STPInfo
101 {
102 public string Name { get; set; }
103 public STPStartInfo STPStartInfo { get; set; }
104 public WIGStartInfo WIGStartInfo { get; set; }
105 public bool IsIdle { get; set; }
106 public bool IsShuttingDown { get; set; }
107 public int MaxThreads { get; set; }
108 public int MinThreads { get; set; }
109 public int InUseThreads { get; set; }
110 public int ActiveThreads { get; set; }
111 public int WaitingCallbacks { get; set; }
112 public int MaxConcurrentWorkItems { get; set; }
113 }
114
115 /// <summary>
116 /// Miscellaneous utility functions
117 /// </summary>
118 public static class Util
119 {
120 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
121
122 /// <summary>
123 /// Log-level for the thread pool:
124 /// 0 = no logging
125 /// 1 = only first line of stack trace; don't log common threads
126 /// 2 = full stack trace; don't log common threads
127 /// 3 = full stack trace, including common threads
128 /// </summary>
129 public static int LogThreadPool { get; set; }
130 public static bool LogOverloads { get; set; }
131
132 public static readonly int MAX_THREADPOOL_LEVEL = 3;
133
134 static Util()
135 {
136 LogThreadPool = 0;
137 LogOverloads = true;
138 }
139
140 private static uint nextXferID = 5000;
141 private static Random randomClass = new ThreadSafeRandom();
142
143 // Get a list of invalid file characters (OS dependent)
144 private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]";
145 private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]";
146 private static object XferLock = new object();
147
148 /// <summary>
149 /// Thread pool used for Util.FireAndForget if FireAndForgetMethod.SmartThreadPool is used
150 /// </summary>
151 private static SmartThreadPool m_ThreadPool;
152
153 // Watchdog timer that aborts threads that have timed-out
154 private static Timer m_threadPoolWatchdog;
155
156 // 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.
157 public static readonly DateTime UnixEpoch =
158 DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime();
159
160 private static readonly string rawUUIDPattern
161 = "[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}";
162 public static readonly Regex PermissiveUUIDPattern = new Regex(rawUUIDPattern);
163 public static readonly Regex UUIDPattern = new Regex(string.Format("^{0}$", rawUUIDPattern));
164
165 public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool;
166 public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod;
167
168 public static bool IsPlatformMono
169 {
170 get { return Type.GetType("Mono.Runtime") != null; }
171 }
172
173 /// <summary>
174 /// Gets the name of the directory where the current running executable
175 /// is located
176 /// </summary>
177 /// <returns>Filesystem path to the directory containing the current
178 /// executable</returns>
179 public static string ExecutingDirectory()
180 {
181 return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
182 }
183
184 /// <summary>
185 /// Linear interpolates B<->C using percent A
186 /// </summary>
187 /// <param name="a"></param>
188 /// <param name="b"></param>
189 /// <param name="c"></param>
190 /// <returns></returns>
191 public static double lerp(double a, double b, double c)
192 {
193 return (b*a) + (c*(1 - a));
194 }
195
196 /// <summary>
197 /// Bilinear Interpolate, see Lerp but for 2D using 'percents' X & Y.
198 /// Layout:
199 /// A B
200 /// C D
201 /// A<->C = Y
202 /// C<->D = X
203 /// </summary>
204 /// <param name="x"></param>
205 /// <param name="y"></param>
206 /// <param name="a"></param>
207 /// <param name="b"></param>
208 /// <param name="c"></param>
209 /// <param name="d"></param>
210 /// <returns></returns>
211 public static double lerp2D(double x, double y, double a, double b, double c, double d)
212 {
213 return lerp(y, lerp(x, a, b), lerp(x, c, d));
214 }
215
216 public static Encoding UTF8 = Encoding.UTF8;
217 public static Encoding UTF8NoBomEncoding = new UTF8Encoding(false);
218
219 /// <value>
220 /// Well known UUID for the blank texture used in the Linden SL viewer version 1.20 (and hopefully onwards)
221 /// </value>
222 public static UUID BLANK_TEXTURE_UUID = new UUID("5748decc-f629-461c-9a36-a35a221fe21f");
223
224 #region Vector Equations
225
226 /// <summary>
227 /// Get the distance between two 3d vectors
228 /// </summary>
229 /// <param name="a">A 3d vector</param>
230 /// <param name="b">A 3d vector</param>
231 /// <returns>The distance between the two vectors</returns>
232 public static double GetDistanceTo(Vector3 a, Vector3 b)
233 {
234 float dx = a.X - b.X;
235 float dy = a.Y - b.Y;
236 float dz = a.Z - b.Z;
237 return Math.Sqrt(dx * dx + dy * dy + dz * dz);
238 }
239
240 /// <summary>
241 /// Returns true if the distance beween A and B is less than amount. Significantly faster than GetDistanceTo since it eliminates the Sqrt.
242 /// </summary>
243 /// <param name="a"></param>
244 /// <param name="b"></param>
245 /// <param name="amount"></param>
246 /// <returns></returns>
247 public static bool DistanceLessThan(Vector3 a, Vector3 b, double amount)
248 {
249 float dx = a.X - b.X;
250 float dy = a.Y - b.Y;
251 float dz = a.Z - b.Z;
252 return (dx*dx + dy*dy + dz*dz) < (amount*amount);
253 }
254
255 /// <summary>
256 /// Get the magnitude of a 3d vector
257 /// </summary>
258 /// <param name="a">A 3d vector</param>
259 /// <returns>The magnitude of the vector</returns>
260 public static double GetMagnitude(Vector3 a)
261 {
262 return Math.Sqrt((a.X * a.X) + (a.Y * a.Y) + (a.Z * a.Z));
263 }
264
265 /// <summary>
266 /// Get a normalized form of a 3d vector
267 /// </summary>
268 /// <param name="a">A 3d vector</param>
269 /// <returns>A new vector which is normalized form of the vector</returns>
270 /// <remarks>The vector paramater cannot be <0,0,0></remarks>
271 public static Vector3 GetNormalizedVector(Vector3 a)
272 {
273 if (IsZeroVector(a))
274 throw new ArgumentException("Vector paramater cannot be a zero vector.");
275
276 float Mag = (float) GetMagnitude(a);
277 return new Vector3(a.X / Mag, a.Y / Mag, a.Z / Mag);
278 }
279
280 /// <summary>
281 /// Returns if a vector is a zero vector (has all zero components)
282 /// </summary>
283 /// <returns></returns>
284 public static bool IsZeroVector(Vector3 v)
285 {
286 if (v.X == 0 && v.Y == 0 && v.Z == 0)
287 {
288 return true;
289 }
290
291 return false;
292 }
293
294 # endregion
295
296 public static Quaternion Axes2Rot(Vector3 fwd, Vector3 left, Vector3 up)
297 {
298 float s;
299 float tr = (float) (fwd.X + left.Y + up.Z + 1.0);
300
301 if (tr >= 1.0)
302 {
303 s = (float) (0.5 / Math.Sqrt(tr));
304 return new Quaternion(
305 (left.Z - up.Y) * s,
306 (up.X - fwd.Z) * s,
307 (fwd.Y - left.X) * s,
308 (float) 0.25 / s);
309 }
310 else
311 {
312 float max = (left.Y > up.Z) ? left.Y : up.Z;
313
314 if (max < fwd.X)
315 {
316 s = (float) (Math.Sqrt(fwd.X - (left.Y + up.Z) + 1.0));
317 float x = (float) (s * 0.5);
318 s = (float) (0.5 / s);
319 return new Quaternion(
320 x,
321 (fwd.Y + left.X) * s,
322 (up.X + fwd.Z) * s,
323 (left.Z - up.Y) * s);
324 }
325 else if (max == left.Y)
326 {
327 s = (float) (Math.Sqrt(left.Y - (up.Z + fwd.X) + 1.0));
328 float y = (float) (s * 0.5);
329 s = (float) (0.5 / s);
330 return new Quaternion(
331 (fwd.Y + left.X) * s,
332 y,
333 (left.Z + up.Y) * s,
334 (up.X - fwd.Z) * s);
335 }
336 else
337 {
338 s = (float) (Math.Sqrt(up.Z - (fwd.X + left.Y) + 1.0));
339 float z = (float) (s * 0.5);
340 s = (float) (0.5 / s);
341 return new Quaternion(
342 (up.X + fwd.Z) * s,
343 (left.Z + up.Y) * s,
344 z,
345 (fwd.Y - left.X) * s);
346 }
347 }
348 }
349
350 public static Random RandomClass
351 {
352 get { return randomClass; }
353 }
354
355 public static ulong UIntsToLong(uint X, uint Y)
356 {
357 return Utils.UIntsToLong(X, Y);
358 }
359
360 // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong.
361 // Several places rely on the ability to extract a region's location from its handle.
362 // Note the location is in 'world coordinates' (see below).
363 // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0.
364 public static ulong RegionWorldLocToHandle(uint X, uint Y)
365 {
366 return Utils.UIntsToLong(X, Y);
367 }
368
369 public static ulong RegionLocToHandle(uint X, uint Y)
370 {
371 return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y));
372 }
373
374 public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y)
375 {
376 X = (uint)(handle >> 32);
377 Y = (uint)(handle & (ulong)uint.MaxValue);
378 }
379
380 public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y)
381 {
382 uint worldX, worldY;
383 RegionHandleToWorldLoc(handle, out worldX, out worldY);
384 X = WorldToRegionLoc(worldX);
385 Y = WorldToRegionLoc(worldY);
386 }
387
388 // A region location can be 'world coordinates' (meters from zero) or 'region coordinates'
389 // (number of regions from zero). This measurement of regions relies on the legacy 256 region size.
390 // These routines exist to make what is being converted explicit so the next person knows what was meant.
391 // Convert a region's 'world coordinate' to its 'region coordinate'.
392 public static uint WorldToRegionLoc(uint worldCoord)
393 {
394 return worldCoord / Constants.RegionSize;
395 }
396
397 // Convert a region's 'region coordinate' to its 'world coordinate'.
398 public static uint RegionToWorldLoc(uint regionCoord)
399 {
400 return regionCoord * Constants.RegionSize;
401 }
402
403 public static T Clamp<T>(T x, T min, T max)
404 where T : IComparable<T>
405 {
406 return x.CompareTo(max) > 0 ? max :
407 x.CompareTo(min) < 0 ? min :
408 x;
409 }
410
411 // Clamp the maximum magnitude of a vector
412 public static Vector3 ClampV(Vector3 x, float max)
413 {
414 float lenSq = x.LengthSquared();
415 if (lenSq > (max * max))
416 {
417 x = x / x.Length() * max;
418 }
419
420 return x;
421 }
422
423 /// <summary>
424 /// Check if any of the values in a Vector3 are NaN or Infinity
425 /// </summary>
426 /// <param name="v">Vector3 to check</param>
427 /// <returns></returns>
428 public static bool IsNanOrInfinity(Vector3 v)
429 {
430 if (float.IsNaN(v.X) || float.IsNaN(v.Y) || float.IsNaN(v.Z))
431 return true;
432
433 if (float.IsInfinity(v.X) || float.IsInfinity(v.Y) || float.IsNaN(v.Z))
434 return true;
435
436 return false;
437 }
438
439 // Inclusive, within range test (true if equal to the endpoints)
440 public static bool InRange<T>(T x, T min, T max)
441 where T : IComparable<T>
442 {
443 return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0;
444 }
445
446 public static uint GetNextXferID()
447 {
448 uint id = 0;
449 lock (XferLock)
450 {
451 id = nextXferID;
452 nextXferID++;
453 }
454 return id;
455 }
456
457 public static string GetFileName(string file)
458 {
459 // Return just the filename on UNIX platforms
460 // TODO: this should be customisable with a prefix, but that's something to do later.
461 if (Environment.OSVersion.Platform == PlatformID.Unix)
462 {
463 return file;
464 }
465
466 // Return %APPDATA%/OpenSim/file for 2K/XP/NT/2K3/VISTA
467 // TODO: Switch this to System.Enviroment.SpecialFolders.ApplicationData
468 if (Environment.OSVersion.Platform == PlatformID.Win32NT)
469 {
470 if (!Directory.Exists("%APPDATA%\\OpenSim\\"))
471 {
472 Directory.CreateDirectory("%APPDATA%\\OpenSim");
473 }
474
475 return "%APPDATA%\\OpenSim\\" + file;
476 }
477
478 // Catch all - covers older windows versions
479 // (but those probably wont work anyway)
480 return file;
481 }
482
483 /// <summary>
484 /// Debug utility function to convert OSD into formatted XML for debugging purposes.
485 /// </summary>
486 /// <param name="osd">
487 /// A <see cref="OSD"/>
488 /// </param>
489 /// <returns>
490 /// A <see cref="System.String"/>
491 /// </returns>
492 public static string GetFormattedXml(OSD osd)
493 {
494 return GetFormattedXml(OSDParser.SerializeLLSDXmlString(osd));
495 }
496
497 /// <summary>
498 /// Debug utility function to convert unbroken strings of XML into something human readable for occasional debugging purposes.
499 /// </summary>
500 /// <remarks>
501 /// Please don't delete me even if I appear currently unused!
502 /// </remarks>
503 /// <param name="rawXml"></param>
504 /// <returns></returns>
505 public static string GetFormattedXml(string rawXml)
506 {
507 XmlDocument xd = new XmlDocument();
508 xd.LoadXml(rawXml);
509
510 StringBuilder sb = new StringBuilder();
511 StringWriter sw = new StringWriter(sb);
512
513 XmlTextWriter xtw = new XmlTextWriter(sw);
514 xtw.Formatting = Formatting.Indented;
515
516 try
517 {
518 xd.WriteTo(xtw);
519 }
520 finally
521 {
522 xtw.Close();
523 }
524
525 return sb.ToString();
526 }
527
528 public static byte[] DocToBytes(XmlDocument doc)
529 {
530 using (MemoryStream ms = new MemoryStream())
531 using (XmlTextWriter xw = new XmlTextWriter(ms, null))
532 {
533 xw.Formatting = Formatting.Indented;
534 doc.WriteTo(xw);
535 xw.Flush();
536
537 return ms.ToArray();
538 }
539 }
540
541 /// <summary>
542 /// Is the platform Windows?
543 /// </summary>
544 /// <returns>true if so, false otherwise</returns>
545 public static bool IsWindows()
546 {
547 PlatformID platformId = Environment.OSVersion.Platform;
548
549 return (platformId == PlatformID.Win32NT
550 || platformId == PlatformID.Win32S
551 || platformId == PlatformID.Win32Windows
552 || platformId == PlatformID.WinCE);
553 }
554
555 public static bool LoadArchSpecificWindowsDll(string libraryName)
556 {
557 return LoadArchSpecificWindowsDll(libraryName, string.Empty);
558 }
559
560 public static bool LoadArchSpecificWindowsDll(string libraryName, string path)
561 {
562 // We do this so that OpenSimulator on Windows loads the correct native library depending on whether
563 // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports
564 // will find it already loaded later on.
565 //
566 // This isn't necessary for other platforms (e.g. Mac OSX and Linux) since the DLL used can be
567 // controlled in config files.
568 string nativeLibraryPath;
569
570 if (Util.Is64BitProcess())
571 nativeLibraryPath = Path.Combine(Path.Combine(path, "lib64"), libraryName);
572 else
573 nativeLibraryPath = Path.Combine(Path.Combine(path, "lib32"), libraryName);
574
575 m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath);
576
577 if (Util.LoadLibrary(nativeLibraryPath) == IntPtr.Zero)
578 {
579 m_log.ErrorFormat(
580 "[UTIL]: Couldn't find native Windows library at {0}", nativeLibraryPath);
581
582 return false;
583 }
584 else
585 {
586 return true;
587 }
588 }
589
590 public static bool IsEnvironmentSupported(ref string reason)
591 {
592 // Must have .NET 2.0 (Generics / libsl)
593 if (Environment.Version.Major < 2)
594 {
595 reason = ".NET 1.0/1.1 lacks components that is used by OpenSim";
596 return false;
597 }
598
599 // Windows 95/98/ME are unsupported
600 if (Environment.OSVersion.Platform == PlatformID.Win32Windows &&
601 Environment.OSVersion.Platform != PlatformID.Win32NT)
602 {
603 reason = "Windows 95/98/ME will not run OpenSim";
604 return false;
605 }
606
607 // Windows 2000 / Pre-SP2 XP
608 if (Environment.OSVersion.Version.Major == 5 &&
609 Environment.OSVersion.Version.Minor == 0)
610 {
611 reason = "Please update to Windows XP Service Pack 2 or Server2003";
612 return false;
613 }
614
615 return true;
616 }
617
618 public static int UnixTimeSinceEpoch()
619 {
620 return ToUnixTime(DateTime.UtcNow);
621 }
622
623 public static int ToUnixTime(DateTime stamp)
624 {
625 TimeSpan t = stamp.ToUniversalTime() - UnixEpoch;
626 return (int)t.TotalSeconds;
627 }
628
629 public static DateTime ToDateTime(ulong seconds)
630 {
631 return UnixEpoch.AddSeconds(seconds);
632 }
633
634 public static DateTime ToDateTime(int seconds)
635 {
636 return UnixEpoch.AddSeconds(seconds);
637 }
638
639 /// <summary>
640 /// Return an md5 hash of the given string
641 /// </summary>
642 /// <param name="data"></param>
643 /// <returns></returns>
644 public static string Md5Hash(string data)
645 {
646 byte[] dataMd5 = ComputeMD5Hash(data);
647 StringBuilder sb = new StringBuilder();
648 for (int i = 0; i < dataMd5.Length; i++)
649 sb.AppendFormat("{0:x2}", dataMd5[i]);
650 return sb.ToString();
651 }
652
653 private static byte[] ComputeMD5Hash(string data)
654 {
655 MD5 md5 = MD5.Create();
656 return md5.ComputeHash(Encoding.Default.GetBytes(data));
657 }
658
659 /// <summary>
660 /// Return an SHA1 hash
661 /// </summary>
662 /// <param name="data"></param>
663 /// <returns></returns>
664 public static string SHA1Hash(string data)
665 {
666 return SHA1Hash(Encoding.Default.GetBytes(data));
667 }
668
669 /// <summary>
670 /// Return an SHA1 hash
671 /// </summary>
672 /// <param name="data"></param>
673 /// <returns></returns>
674 public static string SHA1Hash(byte[] data)
675 {
676 byte[] hash = ComputeSHA1Hash(data);
677 return BitConverter.ToString(hash).Replace("-", String.Empty);
678 }
679
680 private static byte[] ComputeSHA1Hash(byte[] src)
681 {
682 SHA1CryptoServiceProvider SHA1 = new SHA1CryptoServiceProvider();
683 return SHA1.ComputeHash(src);
684 }
685
686 public static int fast_distance2d(int x, int y)
687 {
688 x = Math.Abs(x);
689 y = Math.Abs(y);
690
691 int min = Math.Min(x, y);
692
693 return (x + y - (min >> 1) - (min >> 2) + (min >> 4));
694 }
695
696 /// <summary>
697 /// Determines whether a point is inside a bounding box.
698 /// </summary>
699 /// <param name='v'></param>
700 /// <param name='min'></param>
701 /// <param name='max'></param>
702 /// <returns></returns>
703 public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max)
704 {
705 return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z
706 && v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z;
707 }
708
709 /// <summary>
710 /// Are the co-ordinates of the new region visible from the old region?
711 /// </summary>
712 /// <param name="oldx">Old region x-coord</param>
713 /// <param name="newx">New region x-coord</param>
714 /// <param name="oldy">Old region y-coord</param>
715 /// <param name="newy">New region y-coord</param>
716 /// <returns></returns>
717 public static bool IsOutsideView(float drawdist, uint oldx, uint newx, uint oldy, uint newy)
718 {
719 int dd = (int)((drawdist + Constants.RegionSize - 1) / Constants.RegionSize);
720
721 int startX = (int)oldx - dd;
722 int startY = (int)oldy - dd;
723
724 int endX = (int)oldx + dd;
725 int endY = (int)oldy + dd;
726
727 return (newx < startX || endX < newx || newy < startY || endY < newy);
728 }
729
730 public static string FieldToString(byte[] bytes)
731 {
732 return FieldToString(bytes, String.Empty);
733 }
734
735 /// <summary>
736 /// Convert a variable length field (byte array) to a string, with a
737 /// field name prepended to each line of the output
738 /// </summary>
739 /// <remarks>If the byte array has unprintable characters in it, a
740 /// hex dump will be put in the string instead</remarks>
741 /// <param name="bytes">The byte array to convert to a string</param>
742 /// <param name="fieldName">A field name to prepend to each line of output</param>
743 /// <returns>An ASCII string or a string containing a hex dump, minus
744 /// the null terminator</returns>
745 public static string FieldToString(byte[] bytes, string fieldName)
746 {
747 // Check for a common case
748 if (bytes.Length == 0) return String.Empty;
749
750 StringBuilder output = new StringBuilder();
751 bool printable = true;
752
753 for (int i = 0; i < bytes.Length; ++i)
754 {
755 // Check if there are any unprintable characters in the array
756 if ((bytes[i] < 0x20 || bytes[i] > 0x7E) && bytes[i] != 0x09
757 && bytes[i] != 0x0D && bytes[i] != 0x0A && bytes[i] != 0x00)
758 {
759 printable = false;
760 break;
761 }
762 }
763
764 if (printable)
765 {
766 if (fieldName.Length > 0)
767 {
768 output.Append(fieldName);
769 output.Append(": ");
770 }
771
772 output.Append(CleanString(Util.UTF8.GetString(bytes, 0, bytes.Length - 1)));
773 }
774 else
775 {
776 for (int i = 0; i < bytes.Length; i += 16)
777 {
778 if (i != 0)
779 output.Append(Environment.NewLine);
780 if (fieldName.Length > 0)
781 {
782 output.Append(fieldName);
783 output.Append(": ");
784 }
785
786 for (int j = 0; j < 16; j++)
787 {
788 if ((i + j) < bytes.Length)
789 output.Append(String.Format("{0:X2} ", bytes[i + j]));
790 else
791 output.Append(" ");
792 }
793
794 for (int j = 0; j < 16 && (i + j) < bytes.Length; j++)
795 {
796 if (bytes[i + j] >= 0x20 && bytes[i + j] < 0x7E)
797 output.Append((char) bytes[i + j]);
798 else
799 output.Append(".");
800 }
801 }
802 }
803
804 return output.ToString();
805 }
806
807 /// <summary>
808 /// Returns a IP address from a specified DNS, favouring IPv4 addresses.
809 /// </summary>
810 /// <param name="dnsAddress">DNS Hostname</param>
811 /// <returns>An IP address, or null</returns>
812 public static IPAddress GetHostFromDNS(string dnsAddress)
813 {
814 // Is it already a valid IP? No need to look it up.
815 IPAddress ipa;
816 if (IPAddress.TryParse(dnsAddress, out ipa))
817 return ipa;
818
819 IPAddress[] hosts = null;
820
821 // Not an IP, lookup required
822 try
823 {
824 hosts = Dns.GetHostEntry(dnsAddress).AddressList;
825 }
826 catch (Exception e)
827 {
828 m_log.WarnFormat("[UTIL]: An error occurred while resolving host name {0}, {1}", dnsAddress, e);
829
830 // Still going to throw the exception on for now, since this was what was happening in the first place
831 throw e;
832 }
833
834 foreach (IPAddress host in hosts)
835 {
836 if (host.AddressFamily == AddressFamily.InterNetwork)
837 {
838 return host;
839 }
840 }
841
842 if (hosts.Length > 0)
843 return hosts[0];
844
845 return null;
846 }
847
848 public static Uri GetURI(string protocol, string hostname, int port, string path)
849 {
850 return new UriBuilder(protocol, hostname, port, path).Uri;
851 }
852
853 /// <summary>
854 /// Gets a list of all local system IP addresses
855 /// </summary>
856 /// <returns></returns>
857 public static IPAddress[] GetLocalHosts()
858 {
859 return Dns.GetHostAddresses(Dns.GetHostName());
860 }
861
862 public static IPAddress GetLocalHost()
863 {
864 IPAddress[] iplist = GetLocalHosts();
865
866 if (iplist.Length == 0) // No accessible external interfaces
867 {
868 IPAddress[] loopback = Dns.GetHostAddresses("localhost");
869 IPAddress localhost = loopback[0];
870
871 return localhost;
872 }
873
874 foreach (IPAddress host in iplist)
875 {
876 if (!IPAddress.IsLoopback(host) && host.AddressFamily == AddressFamily.InterNetwork)
877 {
878 return host;
879 }
880 }
881
882 if (iplist.Length > 0)
883 {
884 foreach (IPAddress host in iplist)
885 {
886 if (host.AddressFamily == AddressFamily.InterNetwork)
887 return host;
888 }
889 // Well all else failed...
890 return iplist[0];
891 }
892
893 return null;
894 }
895
896 /// <summary>
897 /// Parses a foreign asset ID.
898 /// </summary>
899 /// <param name="id">A possibly-foreign asset ID: http://grid.example.com:8002/00000000-0000-0000-0000-000000000000 </param>
900 /// <param name="url">The URL: http://grid.example.com:8002</param>
901 /// <param name="assetID">The asset ID: 00000000-0000-0000-0000-000000000000. Returned even if 'id' isn't foreign.</param>
902 /// <returns>True: this is a foreign asset ID; False: it isn't</returns>
903 public static bool ParseForeignAssetID(string id, out string url, out string assetID)
904 {
905 url = String.Empty;
906 assetID = String.Empty;
907
908 UUID uuid;
909 if (UUID.TryParse(id, out uuid))
910 {
911 assetID = uuid.ToString();
912 return false;
913 }
914
915 if ((id.Length == 0) || (id[0] != 'h' && id[0] != 'H'))
916 return false;
917
918 Uri assetUri;
919 if (!Uri.TryCreate(id, UriKind.Absolute, out assetUri) || assetUri.Scheme != Uri.UriSchemeHttp)
920 return false;
921
922 // Simian
923 if (assetUri.Query != string.Empty)
924 {
925 NameValueCollection qscoll = HttpUtility.ParseQueryString(assetUri.Query);
926 assetID = qscoll["id"];
927 if (assetID != null)
928 url = id.Replace(assetID, ""); // Malformed again, as simian expects
929 else
930 url = id; // !!! best effort
931 }
932 else // robust
933 {
934 url = "http://" + assetUri.Authority;
935 assetID = assetUri.LocalPath.Trim(new char[] { '/' });
936 }
937
938 if (!UUID.TryParse(assetID, out uuid))
939 return false;
940
941 return true;
942 }
943
944 /// <summary>
945 /// Removes all invalid path chars (OS dependent)
946 /// </summary>
947 /// <param name="path">path</param>
948 /// <returns>safe path</returns>
949 public static string safePath(string path)
950 {
951 return Regex.Replace(path, regexInvalidPathChars, String.Empty);
952 }
953
954 /// <summary>
955 /// Removes all invalid filename chars (OS dependent)
956 /// </summary>
957 /// <param name="path">filename</param>
958 /// <returns>safe filename</returns>
959 public static string safeFileName(string filename)
960 {
961 return Regex.Replace(filename, regexInvalidFileChars, String.Empty);
962 ;
963 }
964
965 //
966 // directory locations
967 //
968
969 public static string homeDir()
970 {
971 string temp;
972 // string personal=(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
973 // temp = Path.Combine(personal,".OpenSim");
974 temp = ".";
975 return temp;
976 }
977
978 public static string assetsDir()
979 {
980 return Path.Combine(configDir(), "assets");
981 }
982
983 public static string inventoryDir()
984 {
985 return Path.Combine(configDir(), "inventory");
986 }
987
988 public static string configDir()
989 {
990 return ".";
991 }
992
993 public static string dataDir()
994 {
995 return ".";
996 }
997
998 public static string logFile()
999 {
1000 foreach (IAppender appender in LogManager.GetRepository().GetAppenders())
1001 {
1002 if (appender is FileAppender)
1003 {
1004 return ((FileAppender)appender).File;
1005 }
1006 }
1007
1008 return "./OpenSim.log";
1009 }
1010
1011 public static string logDir()
1012 {
1013 return Path.GetDirectoryName(logFile());
1014 }
1015
1016 // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html
1017 public static string GetUniqueFilename(string FileName)
1018 {
1019 int count = 0;
1020 string Name;
1021
1022 if (File.Exists(FileName))
1023 {
1024 FileInfo f = new FileInfo(FileName);
1025
1026 if (!String.IsNullOrEmpty(f.Extension))
1027 {
1028 Name = f.FullName.Substring(0, f.FullName.LastIndexOf('.'));
1029 }
1030 else
1031 {
1032 Name = f.FullName;
1033 }
1034
1035 while (File.Exists(FileName))
1036 {
1037 count++;
1038 FileName = Name + count + f.Extension;
1039 }
1040 }
1041 return FileName;
1042 }
1043
1044 #region Nini (config) related Methods
1045
1046 public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName)
1047 {
1048 if (!File.Exists(fileName))
1049 {
1050 // create new file
1051 }
1052 XmlConfigSource config = new XmlConfigSource(fileName);
1053 AddDataRowToConfig(config, row);
1054 config.Save();
1055
1056 return config;
1057 }
1058
1059 public static void AddDataRowToConfig(IConfigSource config, DataRow row)
1060 {
1061 config.Configs.Add((string) row[0]);
1062 for (int i = 0; i < row.Table.Columns.Count; i++)
1063 {
1064 config.Configs[(string) row[0]].Set(row.Table.Columns[i].ColumnName, row[i]);
1065 }
1066 }
1067
1068 /// <summary>
1069 /// Gets the value of a configuration variable by looking into
1070 /// multiple sections in order. The latter sections overwrite
1071 /// any values previously found.
1072 /// </summary>
1073 /// <typeparam name="T">Type of the variable</typeparam>
1074 /// <param name="config">The configuration object</param>
1075 /// <param name="varname">The configuration variable</param>
1076 /// <param name="sections">Ordered sequence of sections to look at</param>
1077 /// <returns></returns>
1078 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections)
1079 {
1080 return GetConfigVarFromSections<T>(config, varname, sections, default(T));
1081 }
1082
1083 /// <summary>
1084 /// Gets the value of a configuration variable by looking into
1085 /// multiple sections in order. The latter sections overwrite
1086 /// any values previously found.
1087 /// </summary>
1088 /// <remarks>
1089 /// If no value is found then the given default value is returned
1090 /// </remarks>
1091 /// <typeparam name="T">Type of the variable</typeparam>
1092 /// <param name="config">The configuration object</param>
1093 /// <param name="varname">The configuration variable</param>
1094 /// <param name="sections">Ordered sequence of sections to look at</param>
1095 /// <param name="val">Default value</param>
1096 /// <returns></returns>
1097 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections, object val)
1098 {
1099 foreach (string section in sections)
1100 {
1101 IConfig cnf = config.Configs[section];
1102 if (cnf == null)
1103 continue;
1104
1105 if (typeof(T) == typeof(String))
1106 val = cnf.GetString(varname, (string)val);
1107 else if (typeof(T) == typeof(Boolean))
1108 val = cnf.GetBoolean(varname, (bool)val);
1109 else if (typeof(T) == typeof(Int32))
1110 val = cnf.GetInt(varname, (int)val);
1111 else if (typeof(T) == typeof(float))
1112 val = cnf.GetFloat(varname, (float)val);
1113 else
1114 m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T));
1115 }
1116
1117 return (T)val;
1118 }
1119
1120 public static void MergeEnvironmentToConfig(IConfigSource ConfigSource)
1121 {
1122 IConfig enVars = ConfigSource.Configs["Environment"];
1123 // if section does not exist then user isn't expecting them, so don't bother.
1124 if( enVars != null )
1125 {
1126 // load the values from the environment
1127 EnvConfigSource envConfigSource = new EnvConfigSource();
1128 // add the requested keys
1129 string[] env_keys = enVars.GetKeys();
1130 foreach ( string key in env_keys )
1131 {
1132 envConfigSource.AddEnv(key, string.Empty);
1133 }
1134 // load the values from environment
1135 envConfigSource.LoadEnv();
1136 // add them in to the master
1137 ConfigSource.Merge(envConfigSource);
1138 ConfigSource.ExpandKeyValues();
1139 }
1140 }
1141
1142 public static T ReadSettingsFromIniFile<T>(IConfig config, T settingsClass)
1143 {
1144 Type settingsType = settingsClass.GetType();
1145
1146 FieldInfo[] fieldInfos = settingsType.GetFields();
1147 foreach (FieldInfo fieldInfo in fieldInfos)
1148 {
1149 if (!fieldInfo.IsStatic)
1150 {
1151 if (fieldInfo.FieldType == typeof(System.String))
1152 {
1153 fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass)));
1154 }
1155 else if (fieldInfo.FieldType == typeof(System.Boolean))
1156 {
1157 fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass)));
1158 }
1159 else if (fieldInfo.FieldType == typeof(System.Int32))
1160 {
1161 fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass)));
1162 }
1163 else if (fieldInfo.FieldType == typeof(System.Single))
1164 {
1165 fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass)));
1166 }
1167 else if (fieldInfo.FieldType == typeof(System.UInt32))
1168 {
1169 fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString())));
1170 }
1171 }
1172 }
1173
1174 PropertyInfo[] propertyInfos = settingsType.GetProperties();
1175 foreach (PropertyInfo propInfo in propertyInfos)
1176 {
1177 if ((propInfo.CanRead) && (propInfo.CanWrite))
1178 {
1179 if (propInfo.PropertyType == typeof(System.String))
1180 {
1181 propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null);
1182 }
1183 else if (propInfo.PropertyType == typeof(System.Boolean))
1184 {
1185 propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null);
1186 }
1187 else if (propInfo.PropertyType == typeof(System.Int32))
1188 {
1189 propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null);
1190 }
1191 else if (propInfo.PropertyType == typeof(System.Single))
1192 {
1193 propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null);
1194 }
1195 if (propInfo.PropertyType == typeof(System.UInt32))
1196 {
1197 propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null);
1198 }
1199 }
1200 }
1201
1202 return settingsClass;
1203 }
1204
1205 /// <summary>
1206 /// Reads a configuration file, configFile, merging it with the main configuration, config.
1207 /// If the file doesn't exist, it copies a given exampleConfigFile onto configFile, and then
1208 /// merges it.
1209 /// </summary>
1210 /// <param name="config">The main configuration data</param>
1211 /// <param name="configFileName">The name of a configuration file in ConfigDirectory variable, no path</param>
1212 /// <param name="exampleConfigFile">Full path to an example configuration file</param>
1213 /// <param name="configFilePath">Full path ConfigDirectory/configFileName</param>
1214 /// <param name="created">True if the file was created in ConfigDirectory, false if it existed</param>
1215 /// <returns>True if success</returns>
1216 public static bool MergeConfigurationFile(IConfigSource config, string configFileName, string exampleConfigFile, out string configFilePath, out bool created)
1217 {
1218 created = false;
1219 configFilePath = string.Empty;
1220
1221 IConfig cnf = config.Configs["Startup"];
1222 if (cnf == null)
1223 {
1224 m_log.WarnFormat("[UTILS]: Startup section doesn't exist");
1225 return false;
1226 }
1227
1228 string configDirectory = cnf.GetString("ConfigDirectory", ".");
1229 string configFile = Path.Combine(configDirectory, configFileName);
1230
1231 if (!File.Exists(configFile) && !string.IsNullOrEmpty(exampleConfigFile))
1232 {
1233 // We need to copy the example onto it
1234
1235 if (!Directory.Exists(configDirectory))
1236 Directory.CreateDirectory(configDirectory);
1237
1238 try
1239 {
1240 File.Copy(exampleConfigFile, configFile);
1241 created = true;
1242 }
1243 catch (Exception e)
1244 {
1245 m_log.WarnFormat("[UTILS]: Exception copying configuration file {0} to {1}: {2}", configFile, exampleConfigFile, e.Message);
1246 return false;
1247 }
1248 }
1249
1250 if (File.Exists(configFile))
1251 {
1252 // Merge
1253 config.Merge(new IniConfigSource(configFile));
1254 config.ExpandKeyValues();
1255 configFilePath = configFile;
1256 return true;
1257 }
1258 else
1259 return false;
1260 }
1261
1262 #endregion
1263
1264 public static float Clip(float x, float min, float max)
1265 {
1266 return Math.Min(Math.Max(x, min), max);
1267 }
1268
1269 public static int Clip(int x, int min, int max)
1270 {
1271 return Math.Min(Math.Max(x, min), max);
1272 }
1273
1274 public static Vector3 Clip(Vector3 vec, float min, float max)
1275 {
1276 return new Vector3(Clip(vec.X, min, max), Clip(vec.Y, min, max),
1277 Clip(vec.Z, min, max));
1278 }
1279
1280 /// <summary>
1281 /// Convert an UUID to a raw uuid string. Right now this is a string without hyphens.
1282 /// </summary>
1283 /// <param name="UUID"></param>
1284 /// <returns></returns>
1285 public static String ToRawUuidString(UUID UUID)
1286 {
1287 return UUID.Guid.ToString("n");
1288 }
1289
1290 public static string CleanString(string input)
1291 {
1292 if (input.Length == 0)
1293 return input;
1294
1295 int clip = input.Length;
1296
1297 // Test for ++ string terminator
1298 int pos = input.IndexOf("\0");
1299 if (pos != -1 && pos < clip)
1300 clip = pos;
1301
1302 // Test for CR
1303 pos = input.IndexOf("\r");
1304 if (pos != -1 && pos < clip)
1305 clip = pos;
1306
1307 // Test for LF
1308 pos = input.IndexOf("\n");
1309 if (pos != -1 && pos < clip)
1310 clip = pos;
1311
1312 // Truncate string before first end-of-line character found
1313 return input.Substring(0, clip);
1314 }
1315
1316 /// <summary>
1317 /// returns the contents of /etc/issue on Unix Systems
1318 /// Use this for where it's absolutely necessary to implement platform specific stuff
1319 /// </summary>
1320 /// <returns></returns>
1321 public static string ReadEtcIssue()
1322 {
1323 try
1324 {
1325 StreamReader sr = new StreamReader("/etc/issue.net");
1326 string issue = sr.ReadToEnd();
1327 sr.Close();
1328 return issue;
1329 }
1330 catch (Exception)
1331 {
1332 return "";
1333 }
1334 }
1335
1336 public static void SerializeToFile(string filename, Object obj)
1337 {
1338 IFormatter formatter = new BinaryFormatter();
1339 Stream stream = null;
1340
1341 try
1342 {
1343 stream = new FileStream(
1344 filename, FileMode.Create,
1345 FileAccess.Write, FileShare.None);
1346
1347 formatter.Serialize(stream, obj);
1348 }
1349 catch (Exception e)
1350 {
1351 m_log.Error(e.ToString());
1352 }
1353 finally
1354 {
1355 if (stream != null)
1356 {
1357 stream.Close();
1358 }
1359 }
1360 }
1361
1362 public static Object DeserializeFromFile(string filename)
1363 {
1364 IFormatter formatter = new BinaryFormatter();
1365 Stream stream = null;
1366 Object ret = null;
1367
1368 try
1369 {
1370 stream = new FileStream(
1371 filename, FileMode.Open,
1372 FileAccess.Read, FileShare.None);
1373
1374 ret = formatter.Deserialize(stream);
1375 }
1376 catch (Exception e)
1377 {
1378 m_log.Error(e.ToString());
1379 }
1380 finally
1381 {
1382 if (stream != null)
1383 {
1384 stream.Close();
1385 }
1386 }
1387
1388 return ret;
1389 }
1390
1391 /// <summary>
1392 /// Copy data from one stream to another, leaving the read position of both streams at the beginning.
1393 /// </summary>
1394 /// <param name='inputStream'>
1395 /// Input stream. Must be seekable.
1396 /// </param>
1397 /// <exception cref='ArgumentException'>
1398 /// Thrown if the input stream is not seekable.
1399 /// </exception>
1400 public static Stream Copy(Stream inputStream)
1401 {
1402 if (!inputStream.CanSeek)
1403 throw new ArgumentException("Util.Copy(Stream inputStream) must receive an inputStream that can seek");
1404
1405 const int readSize = 256;
1406 byte[] buffer = new byte[readSize];
1407 MemoryStream ms = new MemoryStream();
1408
1409 int count = inputStream.Read(buffer, 0, readSize);
1410
1411 while (count > 0)
1412 {
1413 ms.Write(buffer, 0, count);
1414 count = inputStream.Read(buffer, 0, readSize);
1415 }
1416
1417 ms.Position = 0;
1418 inputStream.Position = 0;
1419
1420 return ms;
1421 }
1422
1423 public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args)
1424 {
1425 return SendXmlRpcCommand(url, methodName, args);
1426 }
1427
1428 public static XmlRpcResponse SendXmlRpcCommand(string url, string methodName, object[] args)
1429 {
1430 XmlRpcRequest client = new XmlRpcRequest(methodName, args);
1431 return client.Send(url, 6000);
1432 }
1433
1434 /// <summary>
1435 /// Returns an error message that the user could not be found in the database
1436 /// </summary>
1437 /// <returns>XML string consisting of a error element containing individual error(s)</returns>
1438 public static XmlRpcResponse CreateUnknownUserErrorResponse()
1439 {
1440 XmlRpcResponse response = new XmlRpcResponse();
1441 Hashtable responseData = new Hashtable();
1442 responseData["error_type"] = "unknown_user";
1443 responseData["error_desc"] = "The user requested is not in the database";
1444
1445 response.Value = responseData;
1446 return response;
1447 }
1448
1449 /// <summary>
1450 /// Converts a byte array in big endian order into an ulong.
1451 /// </summary>
1452 /// <param name="bytes">
1453 /// The array of bytes
1454 /// </param>
1455 /// <returns>
1456 /// The extracted ulong
1457 /// </returns>
1458 public static ulong BytesToUInt64Big(byte[] bytes)
1459 {
1460 if (bytes.Length < 8) return 0;
1461 return ((ulong)bytes[0] << 56) | ((ulong)bytes[1] << 48) | ((ulong)bytes[2] << 40) | ((ulong)bytes[3] << 32) |
1462 ((ulong)bytes[4] << 24) | ((ulong)bytes[5] << 16) | ((ulong)bytes[6] << 8) | (ulong)bytes[7];
1463 }
1464
1465 // used for RemoteParcelRequest (for "About Landmark")
1466 public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y)
1467 {
1468 byte[] bytes =
1469 {
1470 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
1471 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
1472 (byte)x, (byte)(x >> 8), 0, 0,
1473 (byte)y, (byte)(y >> 8), 0, 0 };
1474 return new UUID(bytes, 0);
1475 }
1476
1477 public static UUID BuildFakeParcelID(ulong regionHandle, uint x, uint y, uint z)
1478 {
1479 byte[] bytes =
1480 {
1481 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
1482 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
1483 (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8),
1484 (byte)y, (byte)(y >> 8), 0, 0 };
1485 return new UUID(bytes, 0);
1486 }
1487
1488 public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y)
1489 {
1490 byte[] bytes = parcelID.GetBytes();
1491 regionHandle = Utils.BytesToUInt64(bytes);
1492 x = Utils.BytesToUInt(bytes, 8) & 0xffff;
1493 y = Utils.BytesToUInt(bytes, 12) & 0xffff;
1494 }
1495
1496 public static void ParseFakeParcelID(UUID parcelID, out ulong regionHandle, out uint x, out uint y, out uint z)
1497 {
1498 byte[] bytes = parcelID.GetBytes();
1499 regionHandle = Utils.BytesToUInt64(bytes);
1500 x = Utils.BytesToUInt(bytes, 8) & 0xffff;
1501 z = (Utils.BytesToUInt(bytes, 8) & 0xffff0000) >> 16;
1502 y = Utils.BytesToUInt(bytes, 12) & 0xffff;
1503 }
1504
1505 public static void FakeParcelIDToGlobalPosition(UUID parcelID, out uint x, out uint y)
1506 {
1507 ulong regionHandle;
1508 uint rx, ry;
1509
1510 ParseFakeParcelID(parcelID, out regionHandle, out x, out y);
1511 Utils.LongToUInts(regionHandle, out rx, out ry);
1512
1513 x += rx;
1514 y += ry;
1515 }
1516
1517 /// <summary>
1518 /// Get operating system information if available. Returns only the first 45 characters of information
1519 /// </summary>
1520 /// <returns>
1521 /// Operating system information. Returns an empty string if none was available.
1522 /// </returns>
1523 public static string GetOperatingSystemInformation()
1524 {
1525 string os = String.Empty;
1526
1527 if (Environment.OSVersion.Platform != PlatformID.Unix)
1528 {
1529 os = Environment.OSVersion.ToString();
1530 }
1531 else
1532 {
1533 os = ReadEtcIssue();
1534 }
1535
1536 if (os.Length > 45)
1537 {
1538 os = os.Substring(0, 45);
1539 }
1540
1541 return os;
1542 }
1543
1544 public static string GetRuntimeInformation()
1545 {
1546 string ru = String.Empty;
1547
1548 if (Environment.OSVersion.Platform == PlatformID.Unix)
1549 ru = "Unix/Mono";
1550 else
1551 if (Environment.OSVersion.Platform == PlatformID.MacOSX)
1552 ru = "OSX/Mono";
1553 else
1554 {
1555 if (IsPlatformMono)
1556 ru = "Win/Mono";
1557 else
1558 ru = "Win/.NET";
1559 }
1560
1561 return ru;
1562 }
1563
1564 /// <summary>
1565 /// Is the given string a UUID?
1566 /// </summary>
1567 /// <param name="s"></param>
1568 /// <returns></returns>
1569 public static bool isUUID(string s)
1570 {
1571 return UUIDPattern.IsMatch(s);
1572 }
1573
1574 public static string GetDisplayConnectionString(string connectionString)
1575 {
1576 int passPosition = 0;
1577 int passEndPosition = 0;
1578 string displayConnectionString = null;
1579
1580 // hide the password in the connection string
1581 passPosition = connectionString.IndexOf("password", StringComparison.OrdinalIgnoreCase);
1582 passPosition = connectionString.IndexOf("=", passPosition);
1583 if (passPosition < connectionString.Length)
1584 passPosition += 1;
1585 passEndPosition = connectionString.IndexOf(";", passPosition);
1586
1587 displayConnectionString = connectionString.Substring(0, passPosition);
1588 displayConnectionString += "***";
1589 displayConnectionString += connectionString.Substring(passEndPosition, connectionString.Length - passEndPosition);
1590
1591 return displayConnectionString;
1592 }
1593
1594 public static string Base64ToString(string str)
1595 {
1596 Decoder utf8Decode = Encoding.UTF8.GetDecoder();
1597
1598 byte[] todecode_byte = Convert.FromBase64String(str);
1599 int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);
1600 char[] decoded_char = new char[charCount];
1601 utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);
1602 string result = new String(decoded_char);
1603 return result;
1604 }
1605
1606 public static void BinaryToASCII(char[] chars)
1607 {
1608 for (int i = 0; i < chars.Length; i++)
1609 {
1610 char ch = chars[i];
1611 if (ch < 32 || ch > 127)
1612 chars[i] = '.';
1613 }
1614 }
1615
1616 public static string BinaryToASCII(string src)
1617 {
1618 char[] chars = src.ToCharArray();
1619 BinaryToASCII(chars);
1620 return new String(chars);
1621 }
1622
1623 /// <summary>
1624 /// Reads a known number of bytes from a stream.
1625 /// Throws EndOfStreamException if the stream doesn't contain enough data.
1626 /// </summary>
1627 /// <param name="stream">The stream to read data from</param>
1628 /// <param name="data">The array to write bytes into. The array
1629 /// will be completely filled from the stream, so an appropriate
1630 /// size must be given.</param>
1631 public static void ReadStream(Stream stream, byte[] data)
1632 {
1633 int offset = 0;
1634 int remaining = data.Length;
1635
1636 while (remaining > 0)
1637 {
1638 int read = stream.Read(data, offset, remaining);
1639 if (read <= 0)
1640 throw new EndOfStreamException(String.Format("End of stream reached with {0} bytes left to read", remaining));
1641 remaining -= read;
1642 offset += read;
1643 }
1644 }
1645
1646 public static Guid GetHashGuid(string data, string salt)
1647 {
1648 byte[] hash = ComputeMD5Hash(data + salt);
1649
1650 //string s = BitConverter.ToString(hash);
1651
1652 Guid guid = new Guid(hash);
1653
1654 return guid;
1655 }
1656
1657 public static byte ConvertMaturityToAccessLevel(uint maturity)
1658 {
1659 byte retVal = 0;
1660 switch (maturity)
1661 {
1662 case 0: //PG
1663 retVal = 13;
1664 break;
1665 case 1: //Mature
1666 retVal = 21;
1667 break;
1668 case 2: // Adult
1669 retVal = 42;
1670 break;
1671 }
1672
1673 return retVal;
1674
1675 }
1676
1677 public static uint ConvertAccessLevelToMaturity(byte maturity)
1678 {
1679 if (maturity <= 13)
1680 return 0;
1681 else if (maturity <= 21)
1682 return 1;
1683 else
1684 return 2;
1685 }
1686
1687 /// <summary>
1688 /// Produces an OSDMap from its string representation on a stream
1689 /// </summary>
1690 /// <param name="data">The stream</param>
1691 /// <param name="length">The size of the data on the stream</param>
1692 /// <returns>The OSDMap or an exception</returns>
1693 public static OSDMap GetOSDMap(Stream stream, int length)
1694 {
1695 byte[] data = new byte[length];
1696 stream.Read(data, 0, length);
1697 string strdata = Util.UTF8.GetString(data);
1698 OSDMap args = null;
1699 OSD buffer;
1700 buffer = OSDParser.DeserializeJson(strdata);
1701 if (buffer.Type == OSDType.Map)
1702 {
1703 args = (OSDMap)buffer;
1704 return args;
1705 }
1706 return null;
1707 }
1708
1709 public static OSDMap GetOSDMap(string data)
1710 {
1711 OSDMap args = null;
1712 try
1713 {
1714 OSD buffer;
1715 // We should pay attention to the content-type, but let's assume we know it's Json
1716 buffer = OSDParser.DeserializeJson(data);
1717 if (buffer.Type == OSDType.Map)
1718 {
1719 args = (OSDMap)buffer;
1720 return args;
1721 }
1722 else
1723 {
1724 // uh?
1725 m_log.Debug(("[UTILS]: Got OSD of unexpected type " + buffer.Type.ToString()));
1726 return null;
1727 }
1728 }
1729 catch (Exception ex)
1730 {
1731 m_log.Debug("[UTILS]: exception on GetOSDMap " + ex.Message);
1732 return null;
1733 }
1734 }
1735
1736 public static string[] Glob(string path)
1737 {
1738 string vol=String.Empty;
1739
1740 if (Path.VolumeSeparatorChar != Path.DirectorySeparatorChar)
1741 {
1742 string[] vcomps = path.Split(new char[] {Path.VolumeSeparatorChar}, 2, StringSplitOptions.RemoveEmptyEntries);
1743
1744 if (vcomps.Length > 1)
1745 {
1746 path = vcomps[1];
1747 vol = vcomps[0];
1748 }
1749 }
1750
1751 string[] comps = path.Split(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries);
1752
1753 // Glob
1754
1755 path = vol;
1756 if (vol != String.Empty)
1757 path += new String(new char[] {Path.VolumeSeparatorChar, Path.DirectorySeparatorChar});
1758 else
1759 path = new String(new char[] {Path.DirectorySeparatorChar});
1760
1761 List<string> paths = new List<string>();
1762 List<string> found = new List<string>();
1763 paths.Add(path);
1764
1765 int compIndex = -1;
1766 foreach (string c in comps)
1767 {
1768 compIndex++;
1769
1770 List<string> addpaths = new List<string>();
1771 foreach (string p in paths)
1772 {
1773 string[] dirs = Directory.GetDirectories(p, c);
1774
1775 if (dirs.Length != 0)
1776 {
1777 foreach (string dir in dirs)
1778 addpaths.Add(Path.Combine(path, dir));
1779 }
1780
1781 // Only add files if that is the last path component
1782 if (compIndex == comps.Length - 1)
1783 {
1784 string[] files = Directory.GetFiles(p, c);
1785 foreach (string f in files)
1786 found.Add(f);
1787 }
1788 }
1789 paths = addpaths;
1790 }
1791
1792 return found.ToArray();
1793 }
1794
1795 /// <summary>
1796 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary.
1797 /// </summary>
1798 /// <param name="str">
1799 /// If null or empty, then an bytes[0] is returned.
1800 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1801 /// </param>
1802 /// <param name="args">
1803 /// Arguments to substitute into the string via the {} mechanism.
1804 /// </param>
1805 /// <returns></returns>
1806 public static byte[] StringToBytes256(string str, params object[] args)
1807 {
1808 return StringToBytes256(string.Format(str, args));
1809 }
1810
1811 /// <summary>
1812 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary.
1813 /// </summary>
1814 /// <param name="str">
1815 /// If null or empty, then an bytes[0] is returned.
1816 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1817 /// </param>
1818 /// <returns></returns>
1819 public static byte[] StringToBytes256(string str)
1820 {
1821 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1822 if (str.Length > 254) str = str.Remove(254);
1823 if (!str.EndsWith("\0")) { str += "\0"; }
1824
1825 // Because this is UTF-8 encoding and not ASCII, it's possible we
1826 // might have gotten an oversized array even after the string trim
1827 byte[] data = UTF8.GetBytes(str);
1828 if (data.Length > 256)
1829 {
1830 Array.Resize<byte>(ref data, 256);
1831 data[255] = 0;
1832 }
1833
1834 return data;
1835 }
1836
1837 /// <summary>
1838 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary.
1839 /// </summary>
1840 /// <param name="str">
1841 /// If null or empty, then an bytes[0] is returned.
1842 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1843 /// </param>
1844 /// <param name="args">
1845 /// Arguments to substitute into the string via the {} mechanism.
1846 /// </param>
1847 /// <returns></returns>
1848 public static byte[] StringToBytes1024(string str, params object[] args)
1849 {
1850 return StringToBytes1024(string.Format(str, args));
1851 }
1852
1853 /// <summary>
1854 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 1024 bytes if necessary.
1855 /// </summary>
1856 /// <param name="str">
1857 /// If null or empty, then an bytes[0] is returned.
1858 /// Using "\0" will return a conversion of the null character to a byte. This is not the same as bytes[0]
1859 /// </param>
1860 /// <returns></returns>
1861 public static byte[] StringToBytes1024(string str)
1862 {
1863 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1864 if (str.Length > 1023) str = str.Remove(1023);
1865 if (!str.EndsWith("\0")) { str += "\0"; }
1866
1867 // Because this is UTF-8 encoding and not ASCII, it's possible we
1868 // might have gotten an oversized array even after the string trim
1869 byte[] data = UTF8.GetBytes(str);
1870 if (data.Length > 1024)
1871 {
1872 Array.Resize<byte>(ref data, 1024);
1873 data[1023] = 0;
1874 }
1875
1876 return data;
1877 }
1878
1879 /// <summary>
1880 /// Pretty format the hashtable contents to a single line.
1881 /// </summary>
1882 /// <remarks>
1883 /// Used for debugging output.
1884 /// </remarks>
1885 /// <param name='ht'></param>
1886 public static string PrettyFormatToSingleLine(Hashtable ht)
1887 {
1888 StringBuilder sb = new StringBuilder();
1889
1890 int i = 0;
1891
1892 foreach (string key in ht.Keys)
1893 {
1894 sb.AppendFormat("{0}:{1}", key, ht[key]);
1895
1896 if (++i < ht.Count)
1897 sb.AppendFormat(", ");
1898 }
1899
1900 return sb.ToString();
1901 }
1902
1903 /// <summary>
1904 /// Used to trigger an early library load on Windows systems.
1905 /// </summary>
1906 /// <remarks>
1907 /// Required to get 32-bit and 64-bit processes to automatically use the
1908 /// appropriate native library.
1909 /// </remarks>
1910 /// <param name="dllToLoad"></param>
1911 /// <returns></returns>
1912 [DllImport("kernel32.dll")]
1913 public static extern IntPtr LoadLibrary(string dllToLoad);
1914
1915 /// <summary>
1916 /// Determine whether the current process is 64 bit
1917 /// </summary>
1918 /// <returns>true if so, false if not</returns>
1919 public static bool Is64BitProcess()
1920 {
1921 return IntPtr.Size == 8;
1922 }
1923
1924 #region FireAndForget Threading Pattern
1925
1926 /// <summary>
1927 /// Created to work around a limitation in Mono with nested delegates
1928 /// </summary>
1929 private sealed class FireAndForgetWrapper
1930 {
1931 private static volatile FireAndForgetWrapper instance;
1932 private static object syncRoot = new Object();
1933
1934 public static FireAndForgetWrapper Instance {
1935 get {
1936
1937 if (instance == null)
1938 {
1939 lock (syncRoot)
1940 {
1941 if (instance == null)
1942 {
1943 instance = new FireAndForgetWrapper();
1944 }
1945 }
1946 }
1947
1948 return instance;
1949 }
1950 }
1951
1952 public void FireAndForget(System.Threading.WaitCallback callback)
1953 {
1954 callback.BeginInvoke(null, EndFireAndForget, callback);
1955 }
1956
1957 public void FireAndForget(System.Threading.WaitCallback callback, object obj)
1958 {
1959 callback.BeginInvoke(obj, EndFireAndForget, callback);
1960 }
1961
1962 private static void EndFireAndForget(IAsyncResult ar)
1963 {
1964 System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState;
1965
1966 try { callback.EndInvoke(ar); }
1967 catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); }
1968
1969 ar.AsyncWaitHandle.Close();
1970 }
1971 }
1972
1973 public static void InitThreadPool(int minThreads, int maxThreads)
1974 {
1975 if (maxThreads < 2)
1976 throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2");
1977
1978 if (minThreads > maxThreads || minThreads < 2)
1979 throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads");
1980
1981 if (m_ThreadPool != null)
1982 {
1983 m_log.Warn("SmartThreadPool is already initialized. Ignoring request.");
1984 return;
1985 }
1986
1987 STPStartInfo startInfo = new STPStartInfo();
1988 startInfo.ThreadPoolName = "Util";
1989 startInfo.IdleTimeout = 2000;
1990 startInfo.MaxWorkerThreads = maxThreads;
1991 startInfo.MinWorkerThreads = minThreads;
1992
1993 m_ThreadPool = new SmartThreadPool(startInfo);
1994 m_threadPoolWatchdog = new Timer(ThreadPoolWatchdog, null, 0, 1000);
1995 }
1996
1997 public static int FireAndForgetCount()
1998 {
1999 const int MAX_SYSTEM_THREADS = 200;
2000
2001 switch (FireAndForgetMethod)
2002 {
2003 case FireAndForgetMethod.UnsafeQueueUserWorkItem:
2004 case FireAndForgetMethod.QueueUserWorkItem:
2005 case FireAndForgetMethod.BeginInvoke:
2006 int workerThreads, iocpThreads;
2007 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
2008 return workerThreads;
2009 case FireAndForgetMethod.SmartThreadPool:
2010 return m_ThreadPool.MaxThreads - m_ThreadPool.InUseThreads;
2011 case FireAndForgetMethod.Thread:
2012 return MAX_SYSTEM_THREADS - System.Diagnostics.Process.GetCurrentProcess().Threads.Count;
2013 default:
2014 throw new NotImplementedException();
2015 }
2016 }
2017
2018 /// <summary>
2019 /// Additional information about threads in the main thread pool. Used to time how long the
2020 /// thread has been running, and abort it if it has timed-out.
2021 /// </summary>
2022 private class ThreadInfo
2023 {
2024 public long ThreadFuncNum { get; set; }
2025 public string StackTrace { get; set; }
2026 private string context;
2027 public bool LogThread { get; set; }
2028
2029 public IWorkItemResult WorkItem { get; set; }
2030 public Thread Thread { get; set; }
2031 public bool Running { get; set; }
2032 public bool Aborted { get; set; }
2033 private int started;
2034
2035 public ThreadInfo(long threadFuncNum, string context)
2036 {
2037 ThreadFuncNum = threadFuncNum;
2038 this.context = context;
2039 LogThread = false;
2040 Thread = null;
2041 Running = false;
2042 Aborted = false;
2043 }
2044
2045 public void Started()
2046 {
2047 Thread = Thread.CurrentThread;
2048 started = EnvironmentTickCount();
2049 Running = true;
2050 }
2051
2052 public void Ended()
2053 {
2054 Running = false;
2055 }
2056
2057 public int Elapsed()
2058 {
2059 return EnvironmentTickCountSubtract(started);
2060 }
2061
2062 public void Abort()
2063 {
2064 Aborted = true;
2065 WorkItem.Cancel(true);
2066 }
2067
2068 /// <summary>
2069 /// Returns the thread's stack trace.
2070 /// </summary>
2071 /// <remarks>
2072 /// May return one of two stack traces. First, tries to get the thread's active stack
2073 /// trace. But this can fail, so as a fallback this method will return the stack
2074 /// trace that was active when the task was queued.
2075 /// </remarks>
2076 public string GetStackTrace()
2077 {
2078 string ret = (context == null) ? "" : ("(" + context + ") ");
2079
2080 StackTrace activeStackTrace = Util.GetStackTrace(Thread);
2081 if (activeStackTrace != null)
2082 ret += activeStackTrace.ToString();
2083 else if (StackTrace != null)
2084 ret += "(Stack trace when queued) " + StackTrace;
2085 // else, no stack trace available
2086
2087 return ret;
2088 }
2089 }
2090
2091 private static long nextThreadFuncNum = 0;
2092 private static long numQueuedThreadFuncs = 0;
2093 private static long numRunningThreadFuncs = 0;
2094 private static long numTotalThreadFuncsCalled = 0;
2095 private static Int32 threadFuncOverloadMode = 0;
2096
2097 public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } }
2098 public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } }
2099
2100 // Maps (ThreadFunc number -> Thread)
2101 private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>();
2102
2103 private static readonly int THREAD_TIMEOUT = 10 * 60 * 1000; // 10 minutes
2104
2105 /// <summary>
2106 /// Finds threads in the main thread pool that have timed-out, and aborts them.
2107 /// </summary>
2108 private static void ThreadPoolWatchdog(object state)
2109 {
2110 foreach (KeyValuePair<long, ThreadInfo> entry in activeThreads)
2111 {
2112 ThreadInfo t = entry.Value;
2113 if (t.Running && !t.Aborted && (t.Elapsed() >= THREAD_TIMEOUT))
2114 {
2115 m_log.WarnFormat("Timeout in threadfunc {0} ({1}) {2}", t.ThreadFuncNum, t.Thread.Name, t.GetStackTrace());
2116 t.Abort();
2117
2118 ThreadInfo dummy;
2119 activeThreads.TryRemove(entry.Key, out dummy);
2120
2121 // It's possible that the thread won't abort. To make sure the thread pool isn't
2122 // depleted, increase the pool size.
2123 m_ThreadPool.MaxThreads++;
2124 }
2125 }
2126 }
2127
2128 public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } }
2129
2130 public static Dictionary<string, int> GetFireAndForgetCallsMade()
2131 {
2132 return new Dictionary<string, int>(m_fireAndForgetCallsMade);
2133 }
2134
2135 private static Dictionary<string, int> m_fireAndForgetCallsMade = new Dictionary<string, int>();
2136
2137 public static Dictionary<string, int> GetFireAndForgetCallsInProgress()
2138 {
2139 return new Dictionary<string, int>(m_fireAndForgetCallsInProgress);
2140 }
2141
2142 private static Dictionary<string, int> m_fireAndForgetCallsInProgress = new Dictionary<string, int>();
2143
2144 public static void FireAndForget(System.Threading.WaitCallback callback)
2145 {
2146 FireAndForget(callback, null, null);
2147 }
2148
2149 public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
2150 {
2151 FireAndForget(callback, obj, null);
2152 }
2153
2154 public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context)
2155 {
2156 Interlocked.Increment(ref numTotalThreadFuncsCalled);
2157
2158 if (context != null)
2159 {
2160 if (!m_fireAndForgetCallsMade.ContainsKey(context))
2161 m_fireAndForgetCallsMade[context] = 1;
2162 else
2163 m_fireAndForgetCallsMade[context]++;
2164
2165 if (!m_fireAndForgetCallsInProgress.ContainsKey(context))
2166 m_fireAndForgetCallsInProgress[context] = 1;
2167 else
2168 m_fireAndForgetCallsInProgress[context]++;
2169 }
2170
2171 WaitCallback realCallback;
2172
2173 bool loggingEnabled = LogThreadPool > 0;
2174
2175 long threadFuncNum = Interlocked.Increment(ref nextThreadFuncNum);
2176 ThreadInfo threadInfo = new ThreadInfo(threadFuncNum, context);
2177
2178 if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
2179 {
2180 // If we're running regression tests, then we want any exceptions to rise up to the test code.
2181 realCallback =
2182 o =>
2183 {
2184 Culture.SetCurrentCulture();
2185 callback(o);
2186
2187 if (context != null)
2188 m_fireAndForgetCallsInProgress[context]--;
2189 };
2190 }
2191 else
2192 {
2193 // When OpenSim interacts with a database or sends data over the wire, it must send this in en_US culture
2194 // so that we don't encounter problems where, for instance, data is saved with a culture that uses commas
2195 // for decimals places but is read by a culture that treats commas as number seperators.
2196 realCallback = o =>
2197 {
2198 long numQueued1 = Interlocked.Decrement(ref numQueuedThreadFuncs);
2199 long numRunning1 = Interlocked.Increment(ref numRunningThreadFuncs);
2200 threadInfo.Started();
2201 activeThreads[threadFuncNum] = threadInfo;
2202
2203 try
2204 {
2205 if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
2206 m_log.DebugFormat("Run threadfunc {0} (Queued {1}, Running {2})", threadFuncNum, numQueued1, numRunning1);
2207
2208 Culture.SetCurrentCulture();
2209
2210 callback(o);
2211 }
2212 catch (ThreadAbortException e)
2213 {
2214 m_log.Error(string.Format("Aborted threadfunc {0} ", threadFuncNum), e);
2215 }
2216 catch (Exception e)
2217 {
2218 m_log.Error(string.Format("[UTIL]: Util STP threadfunc {0} terminated with error ", threadFuncNum), e);
2219 }
2220 finally
2221 {
2222 Interlocked.Decrement(ref numRunningThreadFuncs);
2223 threadInfo.Ended();
2224 ThreadInfo dummy;
2225 activeThreads.TryRemove(threadFuncNum, out dummy);
2226 if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
2227 m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed()));
2228
2229 if (context != null)
2230 m_fireAndForgetCallsInProgress[context]--;
2231 }
2232 };
2233 }
2234
2235 long numQueued = Interlocked.Increment(ref numQueuedThreadFuncs);
2236 try
2237 {
2238 long numRunning = numRunningThreadFuncs;
2239
2240 if (m_ThreadPool != null && LogOverloads)
2241 {
2242 if ((threadFuncOverloadMode == 0) && (numRunning >= m_ThreadPool.MaxThreads))
2243 {
2244 if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 1, 0) == 0)
2245 m_log.DebugFormat("Threadfunc: enable overload mode (Queued {0}, Running {1})", numQueued, numRunning);
2246 }
2247 else if ((threadFuncOverloadMode == 1) && (numRunning <= (m_ThreadPool.MaxThreads * 2) / 3))
2248 {
2249 if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 0, 1) == 1)
2250 m_log.DebugFormat("Threadfunc: disable overload mode (Queued {0}, Running {1})", numQueued, numRunning);
2251 }
2252 }
2253
2254 if (loggingEnabled || (threadFuncOverloadMode == 1))
2255 {
2256 string full, partial;
2257 GetFireAndForgetStackTrace(out full, out partial);
2258 threadInfo.StackTrace = full;
2259 threadInfo.LogThread = ShouldLogThread(partial);
2260
2261 if (threadInfo.LogThread)
2262 {
2263 m_log.DebugFormat("Queue threadfunc {0} (Queued {1}, Running {2}) {3}{4}",
2264 threadFuncNum, numQueued, numRunningThreadFuncs,
2265 (context == null) ? "" : ("(" + context + ") "),
2266 (LogThreadPool >= 2) ? full : partial);
2267 }
2268 }
2269 else
2270 {
2271 // Since we didn't log "Queue threadfunc", don't log "Run threadfunc" or "End threadfunc" either.
2272 // Those log lines aren't useful when we don't know which function is running in the thread.
2273 threadInfo.LogThread = false;
2274 }
2275
2276 switch (FireAndForgetMethod)
2277 {
2278 case FireAndForgetMethod.RegressionTest:
2279 case FireAndForgetMethod.None:
2280 realCallback.Invoke(obj);
2281 break;
2282 case FireAndForgetMethod.UnsafeQueueUserWorkItem:
2283 ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj);
2284 break;
2285 case FireAndForgetMethod.QueueUserWorkItem:
2286 ThreadPool.QueueUserWorkItem(realCallback, obj);
2287 break;
2288 case FireAndForgetMethod.BeginInvoke:
2289 FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance;
2290 wrapper.FireAndForget(realCallback, obj);
2291 break;
2292 case FireAndForgetMethod.SmartThreadPool:
2293 if (m_ThreadPool == null)
2294 InitThreadPool(2, 15);
2295 threadInfo.WorkItem = m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
2296 break;
2297 case FireAndForgetMethod.Thread:
2298 Thread thread = new Thread(delegate(object o) { realCallback(o); });
2299 thread.Start(obj);
2300 break;
2301 default:
2302 throw new NotImplementedException();
2303 }
2304 }
2305 catch (Exception)
2306 {
2307 Interlocked.Decrement(ref numQueuedThreadFuncs);
2308 ThreadInfo dummy;
2309 activeThreads.TryRemove(threadFuncNum, out dummy);
2310 throw;
2311 }
2312 }
2313
2314 /// <summary>
2315 /// Returns whether the thread should be logged. Some very common threads aren't logged,
2316 /// to avoid filling up the log.
2317 /// </summary>
2318 /// <param name="stackTrace">A partial stack trace of where the thread was queued</param>
2319 /// <returns>Whether to log this thread</returns>
2320 private static bool ShouldLogThread(string stackTrace)
2321 {
2322 if (LogThreadPool < 3)
2323 {
2324 if (stackTrace.Contains("BeginFireQueueEmpty"))
2325 return false;
2326 }
2327
2328 return true;
2329 }
2330
2331 /// <summary>
2332 /// Returns a stack trace for a thread added using FireAndForget().
2333 /// </summary>
2334 /// <param name="full">Will contain the full stack trace</param>
2335 /// <param name="partial">Will contain only the first frame of the stack trace</param>
2336 private static void GetFireAndForgetStackTrace(out string full, out string partial)
2337 {
2338 string src = Environment.StackTrace;
2339 string[] lines = src.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
2340
2341 StringBuilder dest = new StringBuilder(src.Length);
2342
2343 bool started = false;
2344 bool first = true;
2345 partial = "";
2346
2347 for (int i = 0; i < lines.Length; i++)
2348 {
2349 string line = lines[i];
2350
2351 if (!started)
2352 {
2353 // Skip the initial stack frames, because they're of no interest for debugging
2354 if (line.Contains("StackTrace") || line.Contains("FireAndForget"))
2355 continue;
2356 started = true;
2357 }
2358
2359 if (first)
2360 {
2361 line = line.TrimStart();
2362 first = false;
2363 partial = line;
2364 }
2365
2366 bool last = (i == lines.Length - 1);
2367 if (last)
2368 dest.Append(line);
2369 else
2370 dest.AppendLine(line);
2371 }
2372
2373 full = dest.ToString();
2374 }
2375
2376#pragma warning disable 0618
2377 /// <summary>
2378 /// Return the stack trace of a different thread.
2379 /// </summary>
2380 /// <remarks>
2381 /// This is complicated because the thread needs to be paused in order to get its stack
2382 /// trace. And pausing another thread can cause a deadlock. This method attempts to
2383 /// avoid deadlock by using a short timeout (200ms), after which it gives up and
2384 /// returns 'null' instead of the stack trace.
2385 ///
2386 /// Take from: http://stackoverflow.com/a/14935378
2387 ///
2388 /// WARNING: this doesn't work in Mono. See https://bugzilla.novell.com/show_bug.cgi?id=571691
2389 ///
2390 /// </remarks>
2391 /// <returns>The stack trace, or null if failed to get it</returns>
2392 private static StackTrace GetStackTrace(Thread targetThread)
2393 {
2394 if (IsPlatformMono)
2395 {
2396 // This doesn't work in Mono
2397 return null;
2398 }
2399
2400 ManualResetEventSlim fallbackThreadReady = new ManualResetEventSlim();
2401 ManualResetEventSlim exitedSafely = new ManualResetEventSlim();
2402
2403 try
2404 {
2405 new Thread(delegate()
2406 {
2407 fallbackThreadReady.Set();
2408 while (!exitedSafely.Wait(200))
2409 {
2410 try
2411 {
2412 targetThread.Resume();
2413 }
2414 catch (Exception)
2415 {
2416 // Whatever happens, do never stop to resume the main-thread regularly until the main-thread has exited safely.
2417 }
2418 }
2419 }).Start();
2420
2421 fallbackThreadReady.Wait();
2422 // From here, you have about 200ms to get the stack-trace
2423
2424 targetThread.Suspend();
2425
2426 StackTrace trace = null;
2427 try
2428 {
2429 trace = new StackTrace(targetThread, true);
2430 }
2431 catch (ThreadStateException)
2432 {
2433 //failed to get stack trace, since the fallback-thread resumed the thread
2434 //possible reasons:
2435 //1.) This thread was just too slow
2436 //2.) A deadlock ocurred
2437 //Automatic retry seems too risky here, so just return null.
2438 }
2439
2440 try
2441 {
2442 targetThread.Resume();
2443 }
2444 catch (ThreadStateException)
2445 {
2446 // Thread is running again already
2447 }
2448
2449 return trace;
2450 }
2451 finally
2452 {
2453 // Signal the fallack-thread to stop
2454 exitedSafely.Set();
2455 }
2456 }
2457#pragma warning restore 0618
2458
2459 /// <summary>
2460 /// Get information about the current state of the smart thread pool.
2461 /// </summary>
2462 /// <returns>
2463 /// null if this isn't the pool being used for non-scriptengine threads.
2464 /// </returns>
2465 public static STPInfo GetSmartThreadPoolInfo()
2466 {
2467 if (m_ThreadPool == null)
2468 return null;
2469
2470 STPInfo stpi = new STPInfo();
2471 stpi.Name = m_ThreadPool.Name;
2472 stpi.STPStartInfo = m_ThreadPool.STPStartInfo;
2473 stpi.IsIdle = m_ThreadPool.IsIdle;
2474 stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown;
2475 stpi.MaxThreads = m_ThreadPool.MaxThreads;
2476 stpi.MinThreads = m_ThreadPool.MinThreads;
2477 stpi.InUseThreads = m_ThreadPool.InUseThreads;
2478 stpi.ActiveThreads = m_ThreadPool.ActiveThreads;
2479 stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks;
2480 stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency;
2481
2482 return stpi;
2483 }
2484
2485 #endregion FireAndForget Threading Pattern
2486
2487 /// <summary>
2488 /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
2489 /// and negative every 24.9 days. This trims down TickCount so it doesn't wrap
2490 /// for the callers.
2491 /// This trims it to a 12 day interval so don't let your frame time get too long.
2492 /// </summary>
2493 /// <returns></returns>
2494 public static Int32 EnvironmentTickCount()
2495 {
2496 return Environment.TickCount & EnvironmentTickCountMask;
2497 }
2498 const Int32 EnvironmentTickCountMask = 0x3fffffff;
2499
2500 /// <summary>
2501 /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
2502 /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
2503 /// 'EnvironmentTickCount()') and accounts for any wrapping.
2504 /// </summary>
2505 /// <param name="newValue"></param>
2506 /// <param name="prevValue"></param>
2507 /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
2508 public static Int32 EnvironmentTickCountSubtract(Int32 newValue, Int32 prevValue)
2509 {
2510 Int32 diff = newValue - prevValue;
2511 return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
2512 }
2513
2514 /// <summary>
2515 /// Environment.TickCount is an int but it counts all 32 bits so it goes positive
2516 /// and negative every 24.9 days. Subtracts the passed value (previously fetched by
2517 /// 'EnvironmentTickCount()') and accounts for any wrapping.
2518 /// </summary>
2519 /// <returns>subtraction of passed prevValue from current Environment.TickCount</returns>
2520 public static Int32 EnvironmentTickCountSubtract(Int32 prevValue)
2521 {
2522 return EnvironmentTickCountSubtract(EnvironmentTickCount(), prevValue);
2523 }
2524
2525 // Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount
2526 // Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount().
2527 // A positive return value indicates A occured later than B
2528 public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB)
2529 {
2530 // A, B and TC are all between 0 and 0x3fffffff
2531 int tc = EnvironmentTickCount();
2532
2533 if (tc - tcA >= 0)
2534 tcA += EnvironmentTickCountMask + 1;
2535
2536 if (tc - tcB >= 0)
2537 tcB += EnvironmentTickCountMask + 1;
2538
2539 return tcA - tcB;
2540 }
2541
2542 /// <summary>
2543 /// Formats a duration (given in milliseconds).
2544 /// </summary>
2545 public static string FormatDuration(int ms)
2546 {
2547 TimeSpan span = new TimeSpan(ms * TimeSpan.TicksPerMillisecond);
2548
2549 string str = "";
2550 string suffix = null;
2551
2552 int hours = (int)span.TotalHours;
2553 if (hours > 0)
2554 {
2555 str += hours.ToString(str.Length == 0 ? "0" : "00");
2556 suffix = "hours";
2557 }
2558
2559 if ((hours > 0) || (span.Minutes > 0))
2560 {
2561 if (str.Length > 0)
2562 str += ":";
2563 str += span.Minutes.ToString(str.Length == 0 ? "0" : "00");
2564 if (suffix == null)
2565 suffix = "min";
2566 }
2567
2568 if ((hours > 0) || (span.Minutes > 0) || (span.Seconds > 0))
2569 {
2570 if (str.Length > 0)
2571 str += ":";
2572 str += span.Seconds.ToString(str.Length == 0 ? "0" : "00");
2573 if (suffix == null)
2574 suffix = "sec";
2575 }
2576
2577 if (suffix == null)
2578 suffix = "ms";
2579
2580 if (span.TotalMinutes < 1)
2581 {
2582 int ms1 = span.Milliseconds;
2583 if (str.Length > 0)
2584 {
2585 ms1 /= 100;
2586 str += ".";
2587 }
2588 str += ms1.ToString("0");
2589 }
2590
2591 str += " " + suffix;
2592
2593 return str;
2594 }
2595
2596 /// <summary>
2597 /// Prints the call stack at any given point. Useful for debugging.
2598 /// </summary>
2599 public static void PrintCallStack()
2600 {
2601 PrintCallStack(m_log.DebugFormat);
2602 }
2603
2604 public delegate void DebugPrinter(string msg, params Object[] parm);
2605 public static void PrintCallStack(DebugPrinter printer)
2606 {
2607 StackTrace stackTrace = new StackTrace(true); // get call stack
2608 StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
2609
2610 // write call stack method names
2611 foreach (StackFrame stackFrame in stackFrames)
2612 {
2613 MethodBase mb = stackFrame.GetMethod();
2614 printer("{0}.{1}:{2}", mb.DeclaringType, mb.Name, stackFrame.GetFileLineNumber()); // write method name
2615 }
2616 }
2617
2618 /// <summary>
2619 /// Gets the client IP address
2620 /// </summary>
2621 /// <param name="xff"></param>
2622 /// <returns></returns>
2623 public static IPEndPoint GetClientIPFromXFF(string xff)
2624 {
2625 if (xff == string.Empty)
2626 return null;
2627
2628 string[] parts = xff.Split(new char[] { ',' });
2629 if (parts.Length > 0)
2630 {
2631 try
2632 {
2633 return new IPEndPoint(IPAddress.Parse(parts[0]), 0);
2634 }
2635 catch (Exception e)
2636 {
2637 m_log.WarnFormat("[UTIL]: Exception parsing XFF header {0}: {1}", xff, e.Message);
2638 }
2639 }
2640
2641 return null;
2642 }
2643
2644 public static string GetCallerIP(Hashtable req)
2645 {
2646 if (req.ContainsKey("headers"))
2647 {
2648 try
2649 {
2650 Hashtable headers = (Hashtable)req["headers"];
2651 if (headers.ContainsKey("remote_addr") && headers["remote_addr"] != null)
2652 return headers["remote_addr"].ToString();
2653 }
2654 catch (Exception e)
2655 {
2656 m_log.WarnFormat("[UTIL]: exception in GetCallerIP: {0}", e.Message);
2657 }
2658 }
2659 return string.Empty;
2660 }
2661
2662 #region Xml Serialization Utilities
2663 public static bool ReadBoolean(XmlReader reader)
2664 {
2665 // AuroraSim uses "int" for some fields that are boolean in OpenSim, e.g. "PassCollisions". Don't fail because of this.
2666 reader.ReadStartElement();
2667 string val = reader.ReadContentAsString().ToLower();
2668 bool result = val.Equals("true") || val.Equals("1");
2669 reader.ReadEndElement();
2670
2671 return result;
2672 }
2673
2674 public static UUID ReadUUID(XmlReader reader, string name)
2675 {
2676 UUID id;
2677 string idStr;
2678
2679 reader.ReadStartElement(name);
2680
2681 if (reader.Name == "Guid")
2682 idStr = reader.ReadElementString("Guid");
2683 else if (reader.Name == "UUID")
2684 idStr = reader.ReadElementString("UUID");
2685 else // no leading tag
2686 idStr = reader.ReadContentAsString();
2687 UUID.TryParse(idStr, out id);
2688 reader.ReadEndElement();
2689
2690 return id;
2691 }
2692
2693 public static Vector3 ReadVector(XmlReader reader, string name)
2694 {
2695 Vector3 vec;
2696
2697 reader.ReadStartElement(name);
2698 vec.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // X or x
2699 vec.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Y or y
2700 vec.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty); // Z or z
2701 reader.ReadEndElement();
2702
2703 return vec;
2704 }
2705
2706 public static Quaternion ReadQuaternion(XmlReader reader, string name)
2707 {
2708 Quaternion quat = new Quaternion();
2709
2710 reader.ReadStartElement(name);
2711 while (reader.NodeType != XmlNodeType.EndElement)
2712 {
2713 switch (reader.Name.ToLower())
2714 {
2715 case "x":
2716 quat.X = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2717 break;
2718 case "y":
2719 quat.Y = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2720 break;
2721 case "z":
2722 quat.Z = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2723 break;
2724 case "w":
2725 quat.W = reader.ReadElementContentAsFloat(reader.Name, String.Empty);
2726 break;
2727 }
2728 }
2729
2730 reader.ReadEndElement();
2731
2732 return quat;
2733 }
2734
2735 public static T ReadEnum<T>(XmlReader reader, string name)
2736 {
2737 string value = reader.ReadElementContentAsString(name, String.Empty);
2738 // !!!!! to deal with flags without commas
2739 if (value.Contains(" ") && !value.Contains(","))
2740 value = value.Replace(" ", ", ");
2741
2742 return (T)Enum.Parse(typeof(T), value); ;
2743 }
2744 #endregion
2745
2746 #region Universal User Identifiers
2747
2748 /// <summary>
2749 /// Attempts to parse a UUI into its components: UUID, name and URL.
2750 /// </summary>
2751 /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param>
2752 /// <param name="uuid">the uuid part</param>
2753 /// <param name="url">the endpoint part (e.g. http://foo.com)</param>
2754 /// <param name="firstname">the first name part (e.g. Test)</param>
2755 /// <param name="lastname">the last name part (e.g User)</param>
2756 /// <param name="secret">the secret part</param>
2757 public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret)
2758 {
2759 uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty;
2760
2761 string[] parts = value.Split(';');
2762 if (parts.Length >= 1)
2763 if (!UUID.TryParse(parts[0], out uuid))
2764 return false;
2765
2766 if (parts.Length >= 2)
2767 url = parts[1];
2768
2769 if (parts.Length >= 3)
2770 {
2771 string[] name = parts[2].Split();
2772 if (name.Length == 2)
2773 {
2774 firstname = name[0];
2775 lastname = name[1];
2776 }
2777 }
2778 if (parts.Length >= 4)
2779 secret = parts[3];
2780
2781 return true;
2782 }
2783
2784 /// <summary>
2785 /// For foreign avatars, extracts their original name and Server URL from their First Name and Last Name.
2786 /// </summary>
2787 public static bool ParseForeignAvatarName(string firstname, string lastname,
2788 out string realFirstName, out string realLastName, out string serverURI)
2789 {
2790 realFirstName = realLastName = serverURI = string.Empty;
2791
2792 if (!lastname.Contains("@"))
2793 return false;
2794
2795 if (!firstname.Contains("."))
2796 return false;
2797
2798 realFirstName = firstname.Split('.')[0];
2799 realLastName = firstname.Split('.')[1];
2800 serverURI = new Uri("http://" + lastname.Replace("@", "")).ToString();
2801
2802 return true;
2803 }
2804
2805 /// <summary>
2806 /// Produces a universal (HG) system-facing identifier given the information
2807 /// </summary>
2808 /// <param name="acircuit"></param>
2809 /// <returns>uuid[;homeURI[;first last]]</returns>
2810 public static string ProduceUserUniversalIdentifier(AgentCircuitData acircuit)
2811 {
2812 if (acircuit.ServiceURLs.ContainsKey("HomeURI"))
2813 return UniversalIdentifier(acircuit.AgentID, acircuit.firstname, acircuit.lastname, acircuit.ServiceURLs["HomeURI"].ToString());
2814 else
2815 return acircuit.AgentID.ToString();
2816 }
2817
2818 /// <summary>
2819 /// Produces a universal (HG) system-facing identifier given the information
2820 /// </summary>
2821 /// <param name="id">UUID of the user</param>
2822 /// <param name="firstName">first name (e.g Test)</param>
2823 /// <param name="lastName">last name (e.g. User)</param>
2824 /// <param name="homeURI">homeURI (e.g. http://foo.com)</param>
2825 /// <returns>a string of the form uuid[;homeURI[;first last]]</returns>
2826 public static string UniversalIdentifier(UUID id, String firstName, String lastName, String homeURI)
2827 {
2828 string agentsURI = homeURI;
2829 if (!agentsURI.EndsWith("/"))
2830 agentsURI += "/";
2831
2832 // This is ugly, but there's no other way, given that the name is changed
2833 // in the agent circuit data for foreigners
2834 if (lastName.Contains("@"))
2835 {
2836 string[] parts = firstName.Split(new char[] { '.' });
2837 if (parts.Length == 2)
2838 return CalcUniversalIdentifier(id, agentsURI, parts[0] + " " + parts[1]);
2839 }
2840
2841 return CalcUniversalIdentifier(id, agentsURI, firstName + " " + lastName);
2842 }
2843
2844 private static string CalcUniversalIdentifier(UUID id, string agentsURI, string name)
2845 {
2846 return id.ToString() + ";" + agentsURI + ";" + name;
2847 }
2848
2849 /// <summary>
2850 /// Produces a universal (HG) user-facing name given the information
2851 /// </summary>
2852 /// <param name="firstName"></param>
2853 /// <param name="lastName"></param>
2854 /// <param name="homeURI"></param>
2855 /// <returns>string of the form first.last @foo.com or first last</returns>
2856 public static string UniversalName(String firstName, String lastName, String homeURI)
2857 {
2858 Uri uri = null;
2859 try
2860 {
2861 uri = new Uri(homeURI);
2862 }
2863 catch (UriFormatException)
2864 {
2865 return firstName + " " + lastName;
2866 }
2867 return firstName + "." + lastName + " " + "@" + uri.Authority;
2868 }
2869 #endregion
2870
2871 /// <summary>
2872 /// Escapes the special characters used in "LIKE".
2873 /// </summary>
2874 /// <remarks>
2875 /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz"
2876 /// </remarks>
2877 public static string EscapeForLike(string str)
2878 {
2879 return str.Replace("_", "\\_").Replace("%", "\\%");
2880 }
2881
2882 /// <summary>
2883 /// Returns the name of the user's viewer.
2884 /// </summary>
2885 /// <remarks>
2886 /// This method handles two ways that viewers specify their name:
2887 /// 1. Viewer = "Firestorm-Release 4.4.2.34167", Channel = "(don't care)" -> "Firestorm-Release 4.4.2.34167"
2888 /// 2. Viewer = "4.5.1.38838", Channel = "Firestorm-Beta" -> "Firestorm-Beta 4.5.1.38838"
2889 /// </remarks>
2890 public static string GetViewerName(AgentCircuitData agent)
2891 {
2892 string name = agent.Viewer;
2893 if (name == null)
2894 name = "";
2895 else
2896 name = name.Trim();
2897
2898 // Check if 'Viewer' is just a version number. If it's *not*, then we
2899 // assume that it contains the real viewer name, and we return it.
2900 foreach (char c in name)
2901 {
2902 if (Char.IsLetter(c))
2903 return name;
2904 }
2905
2906 // The 'Viewer' string contains just a version number. If there's anything in
2907 // 'Channel' then assume that it's the viewer name.
2908 if ((agent.Channel != null) && (agent.Channel.Length > 0))
2909 name = agent.Channel.Trim() + " " + name;
2910
2911 return name;
2912 }
2913
2914 public static void LogFailedXML(string message, string xml)
2915 {
2916 int length = xml.Length;
2917 if (length > 2000)
2918 xml = xml.Substring(0, 2000) + "...";
2919
2920 m_log.ErrorFormat("{0} Failed XML ({1} bytes) = {2}", message, length, xml);
2921 }
2922
2923 }
2924
2925 public class DoubleQueue<T> where T:class
2926 {
2927 private Queue<T> m_lowQueue = new Queue<T>();
2928 private Queue<T> m_highQueue = new Queue<T>();
2929
2930 private object m_syncRoot = new object();
2931 private Semaphore m_s = new Semaphore(0, 1);
2932
2933 public DoubleQueue()
2934 {
2935 }
2936
2937 public virtual int Count
2938 {
2939 get
2940 {
2941 lock (m_syncRoot)
2942 return m_highQueue.Count + m_lowQueue.Count;
2943 }
2944 }
2945
2946 public virtual void Enqueue(T data)
2947 {
2948 Enqueue(m_lowQueue, data);
2949 }
2950
2951 public virtual void EnqueueLow(T data)
2952 {
2953 Enqueue(m_lowQueue, data);
2954 }
2955
2956 public virtual void EnqueueHigh(T data)
2957 {
2958 Enqueue(m_highQueue, data);
2959 }
2960
2961 private void Enqueue(Queue<T> q, T data)
2962 {
2963 lock (m_syncRoot)
2964 {
2965 q.Enqueue(data);
2966 m_s.WaitOne(0);
2967 m_s.Release();
2968 }
2969 }
2970
2971 public virtual T Dequeue()
2972 {
2973 return Dequeue(Timeout.Infinite);
2974 }
2975
2976 public virtual T Dequeue(int tmo)
2977 {
2978 return Dequeue(TimeSpan.FromMilliseconds(tmo));
2979 }
2980
2981 public virtual T Dequeue(TimeSpan wait)
2982 {
2983 T res = null;
2984
2985 if (!Dequeue(wait, ref res))
2986 return null;
2987
2988 return res;
2989 }
2990
2991 public bool Dequeue(int timeout, ref T res)
2992 {
2993 return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res);
2994 }
2995
2996 public bool Dequeue(TimeSpan wait, ref T res)
2997 {
2998 if (!m_s.WaitOne(wait))
2999 return false;
3000
3001 lock (m_syncRoot)
3002 {
3003 if (m_highQueue.Count > 0)
3004 res = m_highQueue.Dequeue();
3005 else if (m_lowQueue.Count > 0)
3006 res = m_lowQueue.Dequeue();
3007
3008 if (m_highQueue.Count == 0 && m_lowQueue.Count == 0)
3009 return true;
3010
3011 try
3012 {
3013 m_s.Release();
3014 }
3015 catch
3016 {
3017 }
3018
3019 return true;
3020 }
3021 }
3022
3023 public virtual void Clear()
3024 {
3025
3026 lock (m_syncRoot)
3027 {
3028 // Make sure sem count is 0
3029 m_s.WaitOne(0);
3030
3031 m_lowQueue.Clear();
3032 m_highQueue.Clear();
3033 }
3034 }
3035 }
3036
3037 public class BetterRandom
3038 {
3039 private const int BufferSize = 1024; // must be a multiple of 4
3040 private byte[] RandomBuffer;
3041 private int BufferOffset;
3042 private RNGCryptoServiceProvider rng;
3043 public BetterRandom()
3044 {
3045 RandomBuffer = new byte[BufferSize];
3046 rng = new RNGCryptoServiceProvider();
3047 BufferOffset = RandomBuffer.Length;
3048 }
3049 private void FillBuffer()
3050 {
3051 rng.GetBytes(RandomBuffer);
3052 BufferOffset = 0;
3053 }
3054 public int Next()
3055 {
3056 if (BufferOffset >= RandomBuffer.Length)
3057 {
3058 FillBuffer();
3059 }
3060 int val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff;
3061 BufferOffset += sizeof(int);
3062 return val;
3063 }
3064 public int Next(int maxValue)
3065 {
3066 return Next() % maxValue;
3067 }
3068 public int Next(int minValue, int maxValue)
3069 {
3070 if (maxValue < minValue)
3071 {
3072 throw new ArgumentOutOfRangeException("maxValue must be greater than or equal to minValue");
3073 }
3074 int range = maxValue - minValue;
3075 return minValue + Next(range);
3076 }
3077 public double NextDouble()
3078 {
3079 int val = Next();
3080 return (double)val / int.MaxValue;
3081 }
3082 public void GetBytes(byte[] buff)
3083 {
3084 rng.GetBytes(buff);
3085 }
3086 }
3087}