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