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