aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--addon-modules/OpenSimProfile/GridCommon.ini.example5
-rw-r--r--addon-modules/OpenSimProfile/Modules/ProfileModule/OpenProfile.cs982
-rw-r--r--addon-modules/OpenSimProfile/Robust.HG.ini.example3
-rw-r--r--addon-modules/OpenSimProfile/prebuild.xml40
-rw-r--r--addon-modules/OpenSimSearch/Modules/SearchModule/OpenSearch.cs753
-rw-r--r--addon-modules/OpenSimSearch/README294
-rw-r--r--addon-modules/OpenSimSearch/prebuild.xml39
8 files changed, 2116 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index f0a5487..973d1b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -78,7 +78,6 @@ TAGS
78Makefile.local 78Makefile.local
79bin/.version 79bin/.version
80compile.bat 80compile.bat
81addon-modules
82OpenSim/Data/Tests/test-results/ 81OpenSim/Data/Tests/test-results/
83OpenSim/Framework/Serialization/Tests/test-results/ 82OpenSim/Framework/Serialization/Tests/test-results/
84OpenSim/Framework/Servers/Tests/test-results/ 83OpenSim/Framework/Servers/Tests/test-results/
diff --git a/addon-modules/OpenSimProfile/GridCommon.ini.example b/addon-modules/OpenSimProfile/GridCommon.ini.example
new file mode 100644
index 0000000..8afe23d
--- /dev/null
+++ b/addon-modules/OpenSimProfile/GridCommon.ini.example
@@ -0,0 +1,5 @@
1Add the following to your own GridCommon.ini file to make the UserProfile service working
2
3[Profile]
4; Change it to your own HTTP server to have the Profile server work
5; ProfileURL = http://192.168.0.1/profile.php
diff --git a/addon-modules/OpenSimProfile/Modules/ProfileModule/OpenProfile.cs b/addon-modules/OpenSimProfile/Modules/ProfileModule/OpenProfile.cs
new file mode 100644
index 0000000..775e8b1
--- /dev/null
+++ b/addon-modules/OpenSimProfile/Modules/ProfileModule/OpenProfile.cs
@@ -0,0 +1,982 @@
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Globalization;
5using System.Net;
6using System.Net.Sockets;
7using System.Reflection;
8using System.Xml;
9using OpenMetaverse;
10using log4net;
11using Nini.Config;
12using Nwc.XmlRpc;
13using OpenSim.Framework;
14using OpenSim.Region.Framework.Interfaces;
15using OpenSim.Region.Framework.Scenes;
16using OpenSim.Services.Interfaces;
17using Mono.Addins;
18using OpenSim.Services.Connectors.Hypergrid;
19
20[assembly: Addin("OpenProfileModule", "0.1")]
21[assembly: AddinDependency("OpenSim", "0.5")]
22
23namespace OpenSimProfile.Modules.OpenProfile
24{
25 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
26 public class OpenProfileModule : IProfileModule, ISharedRegionModule
27 {
28 //
29 // Log module
30 //
31 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
32
33 //
34 // Module vars
35 //
36 private IConfigSource m_Config;
37 private List<Scene> m_Scenes = new List<Scene>();
38 private string m_ProfileServer = "";
39 private bool m_Enabled = true;
40
41 IUserManagement m_uMan;
42 IUserManagement UserManagementModule
43 {
44 get
45 {
46 if (m_uMan == null)
47 m_uMan = m_Scenes[0].RequestModuleInterface<IUserManagement>();
48 return m_uMan;
49 }
50 }
51
52 #region IRegionModuleBase implementation
53 public void Initialise(IConfigSource source)
54 {
55 m_Config = source;
56
57 IConfig profileConfig = m_Config.Configs["Profile"];
58
59 if (profileConfig == null)
60 {
61 m_Enabled = false;
62 return;
63 }
64 m_ProfileServer = profileConfig.GetString("ProfileURL", "");
65 if (m_ProfileServer == "")
66 {
67 m_Enabled = false;
68 return;
69 }
70 else
71 {
72 m_log.Info("[PROFILE] OpenProfile module is activated");
73 m_Enabled = true;
74 }
75 }
76
77 public void AddRegion(Scene scene)
78 {
79 if (!m_Enabled)
80 return;
81
82 // Hook up events
83 scene.EventManager.OnNewClient += OnNewClient;
84
85 // Take ownership of the IProfileModule service
86 scene.RegisterModuleInterface<IProfileModule>(this);
87
88 // Add our scene to our list...
89 lock(m_Scenes)
90 {
91 m_Scenes.Add(scene);
92 }
93 }
94
95 public void RemoveRegion(Scene scene)
96 {
97 if (!m_Enabled)
98 return;
99
100 scene.UnregisterModuleInterface<IProfileModule>(this);
101 m_Scenes.Remove(scene);
102 }
103
104 public void RegionLoaded(Scene scene)
105 {
106 }
107
108 public Type ReplaceableInterface
109 {
110 get { return null; }
111 }
112
113 public void PostInitialise()
114 {
115 }
116
117 public void Close()
118 {
119 }
120
121 public string Name
122 {
123 get { return "ProfileModule"; }
124 }
125 #endregion
126
127 private ScenePresence FindPresence(UUID clientID)
128 {
129 ScenePresence p;
130
131 foreach (Scene s in m_Scenes)
132 {
133 p = s.GetScenePresence(clientID);
134 if (p != null && !p.IsChildAgent)
135 return p;
136 }
137 return null;
138 }
139
140 /// New Client Event Handler
141 private void OnNewClient(IClientAPI client)
142 {
143 // Subscribe to messages
144
145 // Classifieds
146 client.AddGenericPacketHandler("avatarclassifiedsrequest", HandleAvatarClassifiedsRequest);
147 client.OnClassifiedInfoUpdate += ClassifiedInfoUpdate;
148 client.OnClassifiedDelete += ClassifiedDelete;
149
150 // Picks
151 client.AddGenericPacketHandler("avatarpicksrequest", HandleAvatarPicksRequest);
152 client.AddGenericPacketHandler("pickinforequest", HandlePickInfoRequest);
153 client.OnPickInfoUpdate += PickInfoUpdate;
154 client.OnPickDelete += PickDelete;
155
156 // Notes
157 client.AddGenericPacketHandler("avatarnotesrequest", HandleAvatarNotesRequest);
158 client.OnAvatarNotesUpdate += AvatarNotesUpdate;
159
160 //Profile
161 client.OnRequestAvatarProperties += RequestAvatarProperties;
162 client.OnUpdateAvatarProperties += UpdateAvatarProperties;
163 client.OnAvatarInterestUpdate += AvatarInterestsUpdate;
164 client.OnUserInfoRequest += UserPreferencesRequest;
165 client.OnUpdateUserInfo += UpdateUserPreferences;
166 }
167
168 //
169 // Make external XMLRPC request
170 //
171 private Hashtable GenericXMLRPCRequest(Hashtable ReqParams, string method, string server)
172 {
173 ArrayList SendParams = new ArrayList();
174 SendParams.Add(ReqParams);
175
176 // Send Request
177 XmlRpcResponse Resp;
178 try
179 {
180 XmlRpcRequest Req = new XmlRpcRequest(method, SendParams);
181 Resp = Req.Send(server, 30000);
182 }
183 catch (WebException ex)
184 {
185 m_log.ErrorFormat("[PROFILE]: Unable to connect to Profile " +
186 "Server {0}. Exception {1}", m_ProfileServer, ex);
187
188 Hashtable ErrorHash = new Hashtable();
189 ErrorHash["success"] = false;
190 ErrorHash["errorMessage"] = "Unable to fetch profile data at this time. ";
191 ErrorHash["errorURI"] = "";
192
193 return ErrorHash;
194 }
195 catch (SocketException ex)
196 {
197 m_log.ErrorFormat(
198 "[PROFILE]: Unable to connect to Profile Server {0}. Method {1}, params {2}. " +
199 "Exception {3}", m_ProfileServer, method, ReqParams, ex);
200
201 Hashtable ErrorHash = new Hashtable();
202 ErrorHash["success"] = false;
203 ErrorHash["errorMessage"] = "Unable to fetch profile data at this time. ";
204 ErrorHash["errorURI"] = "";
205
206 return ErrorHash;
207 }
208 catch (XmlException ex)
209 {
210 m_log.ErrorFormat(
211 "[PROFILE]: Unable to connect to Profile Server {0}. Method {1}, params {2}. " +
212 "Exception {3}", m_ProfileServer, method, ReqParams.ToString(), ex);
213 Hashtable ErrorHash = new Hashtable();
214 ErrorHash["success"] = false;
215 ErrorHash["errorMessage"] = "Unable to fetch profile data at this time. ";
216 ErrorHash["errorURI"] = "";
217
218 return ErrorHash;
219 }
220 if (Resp.IsFault)
221 {
222 Hashtable ErrorHash = new Hashtable();
223 ErrorHash["success"] = false;
224 ErrorHash["errorMessage"] = "Unable to fetch profile data at this time. ";
225 ErrorHash["errorURI"] = "";
226 return ErrorHash;
227 }
228 Hashtable RespData = (Hashtable)Resp.Value;
229
230 return RespData;
231 }
232
233 // Classifieds Handler
234 public void HandleAvatarClassifiedsRequest(Object sender, string method, List<String> args)
235 {
236 if (!(sender is IClientAPI))
237 return;
238
239 IClientAPI remoteClient = (IClientAPI)sender;
240
241 UUID targetID;
242 UUID.TryParse(args[0], out targetID);
243
244 // Can't handle NPC yet...
245 ScenePresence p = FindPresence(targetID);
246
247 if (null != p)
248 {
249 if (p.PresenceType == PresenceType.Npc)
250 return;
251 }
252
253 string serverURI = string.Empty;
254 bool foreign = GetUserProfileServerURI(targetID, out serverURI);
255
256 Hashtable ReqHash = new Hashtable();
257 ReqHash["uuid"] = args[0];
258
259 Hashtable result = GenericXMLRPCRequest(ReqHash,
260 method, serverURI);
261
262 if (!Convert.ToBoolean(result["success"]))
263 {
264 remoteClient.SendAgentAlertMessage(
265 result["errorMessage"].ToString(), false);
266 return;
267 }
268
269 ArrayList dataArray = (ArrayList)result["data"];
270
271 Dictionary<UUID, string> classifieds = new Dictionary<UUID, string>();
272
273 foreach (Object o in dataArray)
274 {
275 Hashtable d = (Hashtable)o;
276
277 classifieds[new UUID(d["classifiedid"].ToString())] = d["name"].ToString();
278 }
279
280 remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), classifieds);
281 }
282
283 // Classifieds Update
284 public void ClassifiedInfoUpdate(UUID queryclassifiedID, uint queryCategory, string queryName, string queryDescription, UUID queryParcelID,
285 uint queryParentEstate, UUID querySnapshotID, Vector3 queryGlobalPos, byte queryclassifiedFlags,
286 int queryclassifiedPrice, IClientAPI remoteClient)
287 {
288 Hashtable ReqHash = new Hashtable();
289
290 Scene s = (Scene) remoteClient.Scene;
291 Vector3 pos = remoteClient.SceneAgent.AbsolutePosition;
292 ILandObject land = s.LandChannel.GetLandObject(pos.X, pos.Y);
293
294 if (land == null)
295 ReqHash["parcelname"] = String.Empty;
296 else
297 ReqHash["parcelname"] = land.LandData.Name;
298
299 ReqHash["creatorUUID"] = remoteClient.AgentId.ToString();
300 ReqHash["classifiedUUID"] = queryclassifiedID.ToString();
301 ReqHash["category"] = queryCategory.ToString();
302 ReqHash["name"] = queryName;
303 ReqHash["description"] = queryDescription;
304 ReqHash["parentestate"] = queryParentEstate.ToString();
305 ReqHash["snapshotUUID"] = querySnapshotID.ToString();
306 ReqHash["sim_name"] = remoteClient.Scene.RegionInfo.RegionName;
307 ReqHash["globalpos"] = queryGlobalPos.ToString();
308 ReqHash["classifiedFlags"] = queryclassifiedFlags.ToString();
309 ReqHash["classifiedPrice"] = queryclassifiedPrice.ToString();
310
311 ScenePresence p = FindPresence(remoteClient.AgentId);
312
313
314 string serverURI = string.Empty;
315 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
316
317 Vector3 avaPos = p.AbsolutePosition;
318
319 // Getting the parceluuid for this parcel
320 ReqHash["parcelUUID"] = p.currentParcelUUID.ToString();
321
322 // Getting the global position for the Avatar
323 Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.RegionLocX * Constants.RegionSize + avaPos.X,
324 remoteClient.Scene.RegionInfo.RegionLocY * Constants.RegionSize + avaPos.Y,
325 avaPos.Z);
326
327 ReqHash["pos_global"] = posGlobal.ToString();
328
329 Hashtable result = GenericXMLRPCRequest(ReqHash,
330 "classified_update", serverURI);
331
332 if (!Convert.ToBoolean(result["success"]))
333 {
334 remoteClient.SendAgentAlertMessage(
335 result["errorMessage"].ToString(), false);
336 }
337 }
338
339 // Classifieds Delete
340 public void ClassifiedDelete(UUID queryClassifiedID, IClientAPI remoteClient)
341 {
342 Hashtable ReqHash = new Hashtable();
343
344 string serverURI = string.Empty;
345 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
346
347 ReqHash["classifiedID"] = queryClassifiedID.ToString();
348
349 Hashtable result = GenericXMLRPCRequest(ReqHash,
350 "classified_delete", serverURI);
351
352 if (!Convert.ToBoolean(result["success"]))
353 {
354 remoteClient.SendAgentAlertMessage(
355 result["errorMessage"].ToString(), false);
356 }
357 }
358
359 // Picks Handler
360 public void HandleAvatarPicksRequest(Object sender, string method, List<String> args)
361 {
362 if (!(sender is IClientAPI))
363 return;
364
365 IClientAPI remoteClient = (IClientAPI)sender;
366
367 UUID targetID;
368 UUID.TryParse(args[0], out targetID);
369
370 // Can't handle NPC yet...
371 ScenePresence p = FindPresence(targetID);
372
373 if (null != p)
374 {
375 if (p.PresenceType == PresenceType.Npc)
376 return;
377 }
378
379 string serverURI = string.Empty;
380 bool foreign = GetUserProfileServerURI(targetID, out serverURI);
381
382 Hashtable ReqHash = new Hashtable();
383 ReqHash["uuid"] = args[0];
384
385 Hashtable result = GenericXMLRPCRequest(ReqHash,
386 method, serverURI);
387
388 if (!Convert.ToBoolean(result["success"]))
389 {
390 remoteClient.SendAgentAlertMessage(
391 result["errorMessage"].ToString(), false);
392 return;
393 }
394
395 ArrayList dataArray = (ArrayList)result["data"];
396
397 Dictionary<UUID, string> picks = new Dictionary<UUID, string>();
398
399 if (dataArray != null)
400 {
401 foreach (Object o in dataArray)
402 {
403 Hashtable d = (Hashtable)o;
404
405 picks[new UUID(d["pickid"].ToString())] = d["name"].ToString();
406 }
407 }
408
409 remoteClient.SendAvatarPicksReply(new UUID(args[0]), picks);
410 }
411
412 // Picks Request
413 public void HandlePickInfoRequest(Object sender, string method, List<String> args)
414 {
415 if (!(sender is IClientAPI))
416 return;
417
418 IClientAPI remoteClient = (IClientAPI)sender;
419
420 Hashtable ReqHash = new Hashtable();
421
422 UUID targetID;
423 UUID.TryParse(args[0], out targetID);
424
425 string serverURI = string.Empty;
426 bool foreign = GetUserProfileServerURI(targetID, out serverURI);
427
428 ReqHash["avatar_id"] = args[0];
429 ReqHash["pick_id"] = args[1];
430
431 Hashtable result = GenericXMLRPCRequest(ReqHash,
432 method, serverURI);
433
434 if (!Convert.ToBoolean(result["success"]))
435 {
436 remoteClient.SendAgentAlertMessage(
437 result["errorMessage"].ToString(), false);
438 return;
439 }
440
441 ArrayList dataArray = (ArrayList)result["data"];
442
443 Hashtable d = (Hashtable)dataArray[0];
444
445 Vector3 globalPos = new Vector3();
446 Vector3.TryParse(d["posglobal"].ToString(), out globalPos);
447
448 if (d["description"] == null)
449 d["description"] = String.Empty;
450
451 remoteClient.SendPickInfoReply(
452 new UUID(d["pickuuid"].ToString()),
453 new UUID(d["creatoruuid"].ToString()),
454 Convert.ToBoolean(d["toppick"]),
455 new UUID(d["parceluuid"].ToString()),
456 d["name"].ToString(),
457 d["description"].ToString(),
458 new UUID(d["snapshotuuid"].ToString()),
459 d["user"].ToString(),
460 d["originalname"].ToString(),
461 d["simname"].ToString(),
462 globalPos,
463 Convert.ToInt32(d["sortorder"]),
464 Convert.ToBoolean(d["enabled"]));
465 }
466
467 // Picks Update
468 public void PickInfoUpdate(IClientAPI remoteClient, UUID pickID, UUID creatorID, bool topPick, string name, string desc, UUID snapshotID, int sortOrder, bool enabled)
469 {
470 Hashtable ReqHash = new Hashtable();
471
472 ReqHash["agent_id"] = remoteClient.AgentId.ToString();
473 ReqHash["pick_id"] = pickID.ToString();
474 ReqHash["creator_id"] = creatorID.ToString();
475 ReqHash["top_pick"] = topPick.ToString();
476 ReqHash["name"] = name;
477 ReqHash["desc"] = desc;
478 ReqHash["snapshot_id"] = snapshotID.ToString();
479 ReqHash["sort_order"] = sortOrder.ToString();
480 ReqHash["enabled"] = enabled.ToString();
481 ReqHash["sim_name"] = remoteClient.Scene.RegionInfo.RegionName;
482
483 ScenePresence p = FindPresence(remoteClient.AgentId);
484
485 Vector3 avaPos = p.AbsolutePosition;
486
487 // Getting the parceluuid for this parcel
488 ReqHash["parcel_uuid"] = p.currentParcelUUID.ToString();
489
490 // Getting the global position for the Avatar
491 Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.RegionLocX*Constants.RegionSize + avaPos.X,
492 remoteClient.Scene.RegionInfo.RegionLocY*Constants.RegionSize + avaPos.Y,
493 avaPos.Z);
494
495 ReqHash["pos_global"] = posGlobal.ToString();
496
497 // Getting the owner of the parcel
498 ReqHash["user"] = ""; //FIXME: Get avatar/group who owns parcel
499
500 string serverURI = string.Empty;
501 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
502
503 // Do the request
504 Hashtable result = GenericXMLRPCRequest(ReqHash,
505 "picks_update", serverURI);
506
507 if (!Convert.ToBoolean(result["success"]))
508 {
509 remoteClient.SendAgentAlertMessage(
510 result["errorMessage"].ToString(), false);
511 }
512 }
513
514 // Picks Delete
515 public void PickDelete(IClientAPI remoteClient, UUID queryPickID)
516 {
517 Hashtable ReqHash = new Hashtable();
518
519 ReqHash["pick_id"] = queryPickID.ToString();
520
521 string serverURI = string.Empty;
522 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
523
524 Hashtable result = GenericXMLRPCRequest(ReqHash,
525 "picks_delete", serverURI);
526
527 if (!Convert.ToBoolean(result["success"]))
528 {
529 remoteClient.SendAgentAlertMessage(
530 result["errorMessage"].ToString(), false);
531 }
532 }
533
534 // Notes Handler
535 public void HandleAvatarNotesRequest(Object sender, string method, List<String> args)
536 {
537 string targetid;
538 string notes = "";
539
540 if (!(sender is IClientAPI))
541 return;
542
543 IClientAPI remoteClient = (IClientAPI)sender;
544
545 Hashtable ReqHash = new Hashtable();
546
547 ReqHash["avatar_id"] = remoteClient.AgentId.ToString();
548 ReqHash["uuid"] = args[0];
549
550 string serverURI = string.Empty;
551 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
552
553 Hashtable result = GenericXMLRPCRequest(ReqHash,
554 method, serverURI);
555
556 if (!Convert.ToBoolean(result["success"]))
557 {
558 remoteClient.SendAgentAlertMessage(
559 result["errorMessage"].ToString(), false);
560 return;
561 }
562
563 ArrayList dataArray = (ArrayList)result["data"];
564
565 if (dataArray != null && dataArray[0] != null)
566 {
567 Hashtable d = (Hashtable)dataArray[0];
568
569 targetid = d["targetid"].ToString();
570 if (d["notes"] != null)
571 notes = d["notes"].ToString();
572
573 remoteClient.SendAvatarNotesReply(new UUID(targetid), notes);
574 }
575 }
576
577 // Notes Update
578 public void AvatarNotesUpdate(IClientAPI remoteClient, UUID queryTargetID, string queryNotes)
579 {
580 Hashtable ReqHash = new Hashtable();
581
582 ReqHash["avatar_id"] = remoteClient.AgentId.ToString();
583 ReqHash["target_id"] = queryTargetID.ToString();
584 ReqHash["notes"] = queryNotes;
585
586 string serverURI = string.Empty;
587 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
588
589 Hashtable result = GenericXMLRPCRequest(ReqHash,
590 "avatar_notes_update", serverURI);
591
592 if (!Convert.ToBoolean(result["success"]))
593 {
594 remoteClient.SendAgentAlertMessage(
595 result["errorMessage"].ToString(), false);
596 }
597 }
598
599 // Standard Profile bits
600 public void AvatarInterestsUpdate(IClientAPI remoteClient, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages)
601 {
602 Hashtable ReqHash = new Hashtable();
603
604 ReqHash["avatar_id"] = remoteClient.AgentId.ToString();
605 ReqHash["wantmask"] = wantmask.ToString();
606 ReqHash["wanttext"] = wanttext;
607 ReqHash["skillsmask"] = skillsmask.ToString();
608 ReqHash["skillstext"] = skillstext;
609 ReqHash["languages"] = languages;
610
611 string serverURI = string.Empty;
612 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
613
614 Hashtable result = GenericXMLRPCRequest(ReqHash,
615 "avatar_interests_update", serverURI);
616
617 if (!Convert.ToBoolean(result["success"]))
618 {
619 remoteClient.SendAgentAlertMessage(
620 result["errorMessage"].ToString(), false);
621 }
622 }
623
624 public void UserPreferencesRequest(IClientAPI remoteClient)
625 {
626 Hashtable ReqHash = new Hashtable();
627
628 ReqHash["avatar_id"] = remoteClient.AgentId.ToString();
629
630 string serverURI = string.Empty;
631 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
632
633 Hashtable result = GenericXMLRPCRequest(ReqHash,
634 "user_preferences_request", serverURI);
635
636 if (!Convert.ToBoolean(result["success"]))
637 {
638 remoteClient.SendAgentAlertMessage(
639 result["errorMessage"].ToString(), false);
640 return;
641 }
642
643 ArrayList dataArray = (ArrayList)result["data"];
644
645 if (dataArray != null && dataArray[0] != null)
646 {
647 Hashtable d = (Hashtable)dataArray[0];
648 string mail = "";
649
650 if (d["email"] != null)
651 mail = d["email"].ToString();
652
653 remoteClient.SendUserInfoReply(
654 Convert.ToBoolean(d["imviaemail"]),
655 Convert.ToBoolean(d["visible"]),
656 mail);
657 }
658 }
659
660 public void UpdateUserPreferences(bool imViaEmail, bool visible, IClientAPI remoteClient)
661 {
662 Hashtable ReqHash = new Hashtable();
663
664 ReqHash["avatar_id"] = remoteClient.AgentId.ToString();
665 ReqHash["imViaEmail"] = imViaEmail.ToString();
666 ReqHash["visible"] = visible.ToString();
667
668 string serverURI = string.Empty;
669 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
670
671 Hashtable result = GenericXMLRPCRequest(ReqHash,
672 "user_preferences_update", serverURI);
673
674 if (!Convert.ToBoolean(result["success"]))
675 {
676 remoteClient.SendAgentAlertMessage(
677 result["errorMessage"].ToString(), false);
678 }
679 }
680
681 // Profile data like the WebURL
682 private Hashtable GetProfileData(UUID userID)
683 {
684 Hashtable ReqHash = new Hashtable();
685
686 // Can't handle NPC yet...
687 ScenePresence p = FindPresence(userID);
688
689 if (null != p)
690 {
691 if (p.PresenceType == PresenceType.Npc)
692 {
693 Hashtable npc =new Hashtable();
694 npc["success"] = "false";
695 npc["errorMessage"] = "Presence is NPC. ";
696 return npc;
697 }
698 }
699
700 ReqHash["avatar_id"] = userID.ToString();
701
702 string serverURI = string.Empty;
703 bool foreign = GetUserProfileServerURI(userID, out serverURI);
704
705 // This is checking a friend on the home grid
706 // Not HG friend
707 if ( String.IsNullOrEmpty(serverURI))
708 {
709 Hashtable nop =new Hashtable();
710 nop["success"] = "false";
711 nop["errorMessage"] = "No Presence - foreign friend";
712 return nop;
713
714 }
715
716 Hashtable result = GenericXMLRPCRequest(ReqHash,
717 "avatar_properties_request", serverURI);
718
719 ArrayList dataArray = (ArrayList)result["data"];
720
721 if (dataArray != null && dataArray[0] != null)
722 {
723 Hashtable d = (Hashtable)dataArray[0];
724 return d;
725 }
726 return result;
727 }
728
729 public void RequestAvatarProperties(IClientAPI remoteClient, UUID avatarID)
730 {
731 if ( String.IsNullOrEmpty(avatarID.ToString()) || String.IsNullOrEmpty(remoteClient.AgentId.ToString()))
732 {
733 // Looking for a reason that some viewers are sending null Id's
734 m_log.InfoFormat("[PROFILE]: This should not happen remoteClient.AgentId {0} - avatarID {1}", remoteClient.AgentId, avatarID);
735 return;
736 }
737
738 // Can't handle NPC yet...
739 ScenePresence p = FindPresence(avatarID);
740
741 if (null != p)
742 {
743 if (p.PresenceType == PresenceType.Npc)
744 return;
745 }
746
747 IScene s = remoteClient.Scene;
748 if (!(s is Scene))
749 return;
750
751 Scene scene = (Scene)s;
752
753 string serverURI = string.Empty;
754 bool foreign = GetUserProfileServerURI(avatarID, out serverURI);
755
756 UserAccount account = null;
757 Dictionary<string,object> userInfo;
758
759 if (!foreign)
760 {
761 account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, avatarID);
762 }
763 else
764 {
765 userInfo = new Dictionary<string, object>();
766 }
767
768 Byte[] charterMember = new Byte[1];
769 string born = String.Empty;
770 uint flags = 0x00;
771
772 if (null != account)
773 {
774 if (account.UserTitle == "")
775 {
776 charterMember[0] = (Byte)((account.UserFlags & 0xf00) >> 8);
777 }
778 else
779 {
780 charterMember = Utils.StringToBytes(account.UserTitle);
781 }
782
783 born = Util.ToDateTime(account.Created).ToString(
784 "M/d/yyyy", CultureInfo.InvariantCulture);
785 flags = (uint)(account.UserFlags & 0xff);
786 }
787 else
788 {
789 if (GetUserProfileData(avatarID, out userInfo) == true)
790 {
791 if ((string)userInfo["user_title"] == "")
792 {
793 charterMember[0] = (Byte)(((Byte)userInfo["user_flags"] & 0xf00) >> 8);
794 }
795 else
796 {
797 charterMember = Utils.StringToBytes((string)userInfo["user_title"]);
798 }
799
800 int val_born = (int)userInfo["user_created"];
801 born = Util.ToDateTime(val_born).ToString(
802 "M/d/yyyy", CultureInfo.InvariantCulture);
803
804 // picky, picky
805 int val_flags = (int)userInfo["user_flags"];
806 flags = (uint)(val_flags & 0xff);
807 }
808 }
809
810 Hashtable profileData = GetProfileData(avatarID);
811 string profileUrl = string.Empty;
812 string aboutText = String.Empty;
813 string firstLifeAboutText = String.Empty;
814 UUID image = UUID.Zero;
815 UUID firstLifeImage = UUID.Zero;
816 UUID partner = UUID.Zero;
817 uint wantMask = 0;
818 string wantText = String.Empty;
819 uint skillsMask = 0;
820 string skillsText = String.Empty;
821 string languages = String.Empty;
822
823 if (profileData["ProfileUrl"] != null)
824 profileUrl = profileData["ProfileUrl"].ToString();
825 if (profileData["AboutText"] != null)
826 aboutText = profileData["AboutText"].ToString();
827 if (profileData["FirstLifeAboutText"] != null)
828 firstLifeAboutText = profileData["FirstLifeAboutText"].ToString();
829 if (profileData["Image"] != null)
830 image = new UUID(profileData["Image"].ToString());
831 if (profileData["FirstLifeImage"] != null)
832 firstLifeImage = new UUID(profileData["FirstLifeImage"].ToString());
833 if (profileData["Partner"] != null)
834 partner = new UUID(profileData["Partner"].ToString());
835
836 // The PROFILE information is no longer stored in the user
837 // account. It now needs to be taken from the XMLRPC
838 //
839 remoteClient.SendAvatarProperties(avatarID, aboutText,born,
840 charterMember, firstLifeAboutText,
841 flags,
842 firstLifeImage, image, profileUrl, partner);
843
844 //Viewer expects interest data when it asks for properties.
845 if (profileData["wantmask"] != null)
846 wantMask = Convert.ToUInt32(profileData["wantmask"].ToString());
847 if (profileData["wanttext"] != null)
848 wantText = profileData["wanttext"].ToString();
849
850 if (profileData["skillsmask"] != null)
851 skillsMask = Convert.ToUInt32(profileData["skillsmask"].ToString());
852 if (profileData["skillstext"] != null)
853 skillsText = profileData["skillstext"].ToString();
854
855 if (profileData["languages"] != null)
856 languages = profileData["languages"].ToString();
857
858 remoteClient.SendAvatarInterestsReply(avatarID, wantMask, wantText,
859 skillsMask, skillsText, languages);
860 }
861
862 public void UpdateAvatarProperties(IClientAPI remoteClient, UserProfileData newProfile)
863 {
864 // if it's the profile of the user requesting the update, then we change only a few things.
865 if (remoteClient.AgentId == newProfile.ID)
866 {
867 Hashtable ReqHash = new Hashtable();
868
869 ReqHash["avatar_id"] = remoteClient.AgentId.ToString();
870 ReqHash["ProfileUrl"] = newProfile.ProfileUrl;
871 ReqHash["Image"] = newProfile.Image.ToString();
872 ReqHash["AboutText"] = newProfile.AboutText;
873 ReqHash["FirstLifeImage"] = newProfile.FirstLifeImage.ToString();
874 ReqHash["FirstLifeAboutText"] = newProfile.FirstLifeAboutText;
875
876 string serverURI = string.Empty;
877 bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
878
879 Hashtable result = GenericXMLRPCRequest(ReqHash,
880 "avatar_properties_update", serverURI);
881
882 if (!Convert.ToBoolean(result["success"]))
883 {
884 remoteClient.SendAgentAlertMessage(
885 result["errorMessage"].ToString(), false);
886 }
887
888 RequestAvatarProperties(remoteClient, newProfile.ID);
889 }
890 }
891
892 private bool GetUserProfileServerURI(UUID userID, out string serverURI)
893 {
894 IUserManagement uManage = UserManagementModule;
895
896 if (!uManage.IsLocalGridUser(userID))
897 {
898 serverURI = uManage.GetUserServerURL(userID, "ProfileServerURI");
899 // Is Foreign
900 return true;
901 }
902 else
903 {
904 serverURI = m_ProfileServer;
905 // Is local
906 return false;
907 }
908 }
909
910 //
911 // Get the UserAccountBits
912 //
913 private bool GetUserProfileData(UUID userID, out Dictionary<string, object> userInfo)
914 {
915 IUserManagement uManage = UserManagementModule;
916 Dictionary<string,object> info = new Dictionary<string, object>();
917
918
919 if (!uManage.IsLocalGridUser(userID))
920 {
921 // Is Foreign
922 string home_url = uManage.GetUserServerURL(userID, "HomeURI");
923
924 if (String.IsNullOrEmpty(home_url))
925 {
926 info["user_flags"] = 0;
927 info["user_created"] = 0;
928 info["user_title"] = "Unavailable";
929
930 userInfo = info;
931 return true;
932 }
933
934 UserAgentServiceConnector uConn = new UserAgentServiceConnector(home_url);
935
936 Dictionary<string, object> account = uConn.GetUserInfo(userID);
937
938 if (account.Count > 0)
939 {
940 if (account.ContainsKey("user_flags"))
941 info["user_flags"] = account["user_flags"];
942 else
943 info["user_flags"] = "";
944
945 if (account.ContainsKey("user_created"))
946 info["user_created"] = account["user_created"];
947 else
948 info["user_created"] = "";
949
950 info["user_title"] = "HG Visitor";
951 }
952 else
953 {
954 info["user_flags"] = 0;
955 info["user_created"] = 0;
956 info["user_title"] = "HG Visitor";
957 }
958 userInfo = info;
959 return true;
960 }
961 else
962 {
963 // Is local
964 Scene scene = m_Scenes[0];
965 IUserAccountService uas = scene.UserAccountService;
966 UserAccount account = uas.GetUserAccount(scene.RegionInfo.ScopeID, userID);
967
968 info["user_flags"] = account.UserFlags;
969 info["user_created"] = account.Created;
970
971 if (!String.IsNullOrEmpty(account.UserTitle))
972 info["user_title"] = account.UserTitle;
973 else
974 info["user_title"] = "";
975
976 userInfo = info;
977
978 return false;
979 }
980 }
981 }
982}
diff --git a/addon-modules/OpenSimProfile/Robust.HG.ini.example b/addon-modules/OpenSimProfile/Robust.HG.ini.example
new file mode 100644
index 0000000..d9407cb
--- /dev/null
+++ b/addon-modules/OpenSimProfile/Robust.HG.ini.example
@@ -0,0 +1,3 @@
1;; Update [LogonService] section to point to your profile server's url
2;;
3SRV_ProfileServerURI = "http://127.0.0.1/profile.php"
diff --git a/addon-modules/OpenSimProfile/prebuild.xml b/addon-modules/OpenSimProfile/prebuild.xml
new file mode 100644
index 0000000..e653dee
--- /dev/null
+++ b/addon-modules/OpenSimProfile/prebuild.xml
@@ -0,0 +1,40 @@
1<?xml version="1.0" ?>
2<Project frameworkVersion="v3_5" name="OpenSimProfile.Modules" path="addon-modules/OpenSimProfile/Modules" type="Library">
3 <Configuration name="Debug">
4 <Options>
5 <OutputPath>../../../bin/</OutputPath>
6 </Options>
7 </Configuration>
8 <Configuration name="Release">
9 <Options>
10 <OutputPath>../../../bin/</OutputPath>
11 </Options>
12 </Configuration>
13
14 <ReferencePath>../../../bin/</ReferencePath>
15 <Reference localCopy="false" name="System"/>
16 <Reference name="System.Xml"/>
17 <Reference name="System.Drawing"/>
18 <Reference name="System.Runtime.Remoting"/>
19 <Reference name="OpenMetaverseTypes" path="../../../bin/"/>
20 <Reference name="OpenMetaverse" path="../../../bin/"/>
21 <Reference name="Axiom.MathLib" path="../../../bin/"/>
22 <Reference name="OpenSim.Framework"/>
23 <Reference name="OpenSim.Data"/>
24 <Reference name="OpenSim.Region.Framework"/>
25 <Reference name="OpenSim.Framework.Console"/>
26 <Reference name="OpenSim.Framework.Servers"/>
27 <Reference name="OpenSim.Framework.Statistics"/>
28 <Reference name="OpenSim.Framework.Communications"/>
29 <Reference name="OpenSim.Region.Physics.Manager"/>
30 <Reference name="OpenSim.Services.Interfaces"/>
31 <Reference name="OpenSim.Services.Connectors"/>
32 <Reference name="Nini" path="../../../bin/"/>
33 <Reference name="log4net" path="../../../bin/"/>
34 <Reference name="XMLRPC" path="../../../bin/"/>
35 <Reference name="Mono.Addins" path="../../../bin/"/>
36
37 <Files>
38 <Match pattern="*.cs" recurse="true"/>
39 </Files>
40</Project>
diff --git a/addon-modules/OpenSimSearch/Modules/SearchModule/OpenSearch.cs b/addon-modules/OpenSimSearch/Modules/SearchModule/OpenSearch.cs
new file mode 100644
index 0000000..2544614
--- /dev/null
+++ b/addon-modules/OpenSimSearch/Modules/SearchModule/OpenSearch.cs
@@ -0,0 +1,753 @@
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Globalization;
5using System.Net;
6using System.Net.Sockets;
7using System.Reflection;
8using System.Xml;
9using OpenMetaverse;
10using log4net;
11using Nini.Config;
12using Nwc.XmlRpc;
13using OpenSim.Framework;
14using OpenSim.Region.Framework.Interfaces;
15using OpenSim.Region.Framework.Scenes;
16using OpenSim.Services.Interfaces;
17using Mono.Addins;
18
19[assembly: Addin("OpenSearchModule", "0.1")]
20[assembly: AddinDependency("OpenSim", "0.5")]
21
22namespace OpenSimSearch.Modules.OpenSearch
23{
24 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
25 public class OpenSearchModule : ISearchModule, ISharedRegionModule
26 {
27 //
28 // Log module
29 //
30 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
31
32 //
33 // Module vars
34 //
35 private List<Scene> m_Scenes = new List<Scene>();
36 private string m_SearchServer = "";
37 private bool m_Enabled = true;
38
39 public void Initialise(IConfigSource config)
40 {
41 IConfig searchConfig = config.Configs["Search"];
42
43 if (searchConfig == null)
44 {
45 m_Enabled = false;
46 return;
47 }
48 if (searchConfig.GetString("Module", "OpenSimSearch") != "OpenSimSearch")
49 {
50 m_Enabled = false;
51 return;
52 }
53
54 m_SearchServer = searchConfig.GetString("SearchURL", "");
55 if (m_SearchServer == "")
56 {
57 m_log.Error("[SEARCH] No search server, disabling search");
58 m_Enabled = false;
59 return;
60 }
61 else
62 {
63 m_log.Info("[SEARCH] Search module is activated");
64 m_Enabled = true;
65 }
66 }
67
68 public void AddRegion(Scene scene)
69 {
70 if (!m_Enabled)
71 return;
72
73 // Hook up events
74 scene.EventManager.OnNewClient += OnNewClient;
75
76 // Take ownership of the ISearchModule service
77 scene.RegisterModuleInterface<ISearchModule>(this);
78
79 // Add our scene to our list...
80 lock(m_Scenes)
81 {
82 m_Scenes.Add(scene);
83 }
84
85 }
86
87 public void RemoveRegion(Scene scene)
88 {
89 if (!m_Enabled)
90 return;
91
92 scene.UnregisterModuleInterface<ISearchModule>(this);
93 m_Scenes.Remove(scene);
94 }
95
96 public void RegionLoaded(Scene scene)
97 {
98 }
99
100 public Type ReplaceableInterface
101 {
102 get { return null; }
103 }
104
105 public void PostInitialise()
106 {
107 }
108
109 public void Close()
110 {
111 }
112
113 public string Name
114 {
115 get { return "SearchModule"; }
116 }
117
118 public bool IsSharedModule
119 {
120 get { return true; }
121 }
122
123 /// New Client Event Handler
124 private void OnNewClient(IClientAPI client)
125 {
126 // Subscribe to messages
127 client.OnDirPlacesQuery += DirPlacesQuery;
128 client.OnDirFindQuery += DirFindQuery;
129 client.OnDirPopularQuery += DirPopularQuery;
130 client.OnDirLandQuery += DirLandQuery;
131 client.OnDirClassifiedQuery += DirClassifiedQuery;
132 // Response after Directory Queries
133 client.OnEventInfoRequest += EventInfoRequest;
134 client.OnClassifiedInfoRequest += ClassifiedInfoRequest;
135 client.OnMapItemRequest += HandleMapItemRequest;
136 }
137
138 //
139 // Make external XMLRPC request
140 //
141 private Hashtable GenericXMLRPCRequest(Hashtable ReqParams, string method)
142 {
143 ArrayList SendParams = new ArrayList();
144 SendParams.Add(ReqParams);
145
146 // Send Request
147 XmlRpcResponse Resp;
148 try
149 {
150 XmlRpcRequest Req = new XmlRpcRequest(method, SendParams);
151 Resp = Req.Send(m_SearchServer, 30000);
152 }
153 catch (WebException ex)
154 {
155 m_log.ErrorFormat("[SEARCH]: Unable to connect to Search " +
156 "Server {0}. Exception {1}", m_SearchServer, ex);
157
158 Hashtable ErrorHash = new Hashtable();
159 ErrorHash["success"] = false;
160 ErrorHash["errorMessage"] = "Unable to search at this time. ";
161 ErrorHash["errorURI"] = "";
162
163 return ErrorHash;
164 }
165 catch (SocketException ex)
166 {
167 m_log.ErrorFormat(
168 "[SEARCH]: Unable to connect to Search Server {0}. " +
169 "Exception {1}", m_SearchServer, ex);
170
171 Hashtable ErrorHash = new Hashtable();
172 ErrorHash["success"] = false;
173 ErrorHash["errorMessage"] = "Unable to search at this time. ";
174 ErrorHash["errorURI"] = "";
175
176 return ErrorHash;
177 }
178 catch (XmlException ex)
179 {
180 m_log.ErrorFormat(
181 "[SEARCH]: Unable to connect to Search Server {0}. " +
182 "Exception {1}", m_SearchServer, ex);
183
184 Hashtable ErrorHash = new Hashtable();
185 ErrorHash["success"] = false;
186 ErrorHash["errorMessage"] = "Unable to search at this time. ";
187 ErrorHash["errorURI"] = "";
188
189 return ErrorHash;
190 }
191 if (Resp.IsFault)
192 {
193 Hashtable ErrorHash = new Hashtable();
194 ErrorHash["success"] = false;
195 ErrorHash["errorMessage"] = "Unable to search at this time. ";
196 ErrorHash["errorURI"] = "";
197 return ErrorHash;
198 }
199 Hashtable RespData = (Hashtable)Resp.Value;
200
201 return RespData;
202 }
203
204 protected void DirPlacesQuery(IClientAPI remoteClient, UUID queryID,
205 string queryText, int queryFlags, int category, string simName,
206 int queryStart)
207 {
208 Hashtable ReqHash = new Hashtable();
209 ReqHash["text"] = queryText;
210 ReqHash["flags"] = queryFlags.ToString();
211 ReqHash["category"] = category.ToString();
212 ReqHash["sim_name"] = simName;
213 ReqHash["query_start"] = queryStart.ToString();
214
215 Hashtable result = GenericXMLRPCRequest(ReqHash,
216 "dir_places_query");
217
218 if (!Convert.ToBoolean(result["success"]))
219 {
220 remoteClient.SendAgentAlertMessage(
221 result["errorMessage"].ToString(), false);
222 return;
223 }
224
225 ArrayList dataArray = (ArrayList)result["data"];
226
227 int count = dataArray.Count;
228 if (count > 100)
229 count = 101;
230
231 DirPlacesReplyData[] data = new DirPlacesReplyData[count];
232
233 int i = 0;
234
235 foreach (Object o in dataArray)
236 {
237 Hashtable d = (Hashtable)o;
238
239 data[i] = new DirPlacesReplyData();
240 data[i].parcelID = new UUID(d["parcel_id"].ToString());
241 data[i].name = d["name"].ToString();
242 data[i].forSale = Convert.ToBoolean(d["for_sale"]);
243 data[i].auction = Convert.ToBoolean(d["auction"]);
244 data[i].dwell = Convert.ToSingle(d["dwell"]);
245
246 if (++i >= count)
247 break;
248 }
249
250 remoteClient.SendDirPlacesReply(queryID, data);
251 }
252
253 public void DirPopularQuery(IClientAPI remoteClient, UUID queryID, uint queryFlags)
254 {
255 Hashtable ReqHash = new Hashtable();
256 ReqHash["flags"] = queryFlags.ToString();
257
258 Hashtable result = GenericXMLRPCRequest(ReqHash,
259 "dir_popular_query");
260
261 if (!Convert.ToBoolean(result["success"]))
262 {
263 remoteClient.SendAgentAlertMessage(
264 result["errorMessage"].ToString(), false);
265 return;
266 }
267
268 ArrayList dataArray = (ArrayList)result["data"];
269
270 int count = dataArray.Count;
271 if (count > 100)
272 count = 101;
273
274 DirPopularReplyData[] data = new DirPopularReplyData[count];
275
276 int i = 0;
277
278 foreach (Object o in dataArray)
279 {
280 Hashtable d = (Hashtable)o;
281
282 data[i] = new DirPopularReplyData();
283 data[i].parcelID = new UUID(d["parcel_id"].ToString());
284 data[i].name = d["name"].ToString();
285 data[i].dwell = Convert.ToSingle(d["dwell"]);
286
287 if (++i >= count)
288 break;
289 }
290
291 remoteClient.SendDirPopularReply(queryID, data);
292 }
293
294 public void DirLandQuery(IClientAPI remoteClient, UUID queryID,
295 uint queryFlags, uint searchType, int price, int area,
296 int queryStart)
297 {
298 Hashtable ReqHash = new Hashtable();
299 ReqHash["flags"] = queryFlags.ToString();
300 ReqHash["type"] = searchType.ToString();
301 ReqHash["price"] = price.ToString();
302 ReqHash["area"] = area.ToString();
303 ReqHash["query_start"] = queryStart.ToString();
304
305 Hashtable result = GenericXMLRPCRequest(ReqHash,
306 "dir_land_query");
307
308 if (!Convert.ToBoolean(result["success"]))
309 {
310 remoteClient.SendAgentAlertMessage(
311 result["errorMessage"].ToString(), false);
312 return;
313 }
314
315 ArrayList dataArray = (ArrayList)result["data"];
316 int count = 0;
317
318 /* Count entries in dataArray with valid region name to */
319 /* prevent allocating data array with too many entries. */
320 foreach (Object o in dataArray)
321 {
322 Hashtable d = (Hashtable)o;
323
324 if (d["name"] != null)
325 ++count;
326 }
327
328 if (count > 100)
329 count = 101;
330
331 DirLandReplyData[] data = new DirLandReplyData[count];
332
333 int i = 0;
334
335 foreach (Object o in dataArray)
336 {
337 Hashtable d = (Hashtable)o;
338
339 if (d["name"] == null)
340 continue;
341
342 data[i] = new DirLandReplyData();
343 data[i].parcelID = new UUID(d["parcel_id"].ToString());
344 data[i].name = d["name"].ToString();
345 data[i].auction = Convert.ToBoolean(d["auction"]);
346 data[i].forSale = Convert.ToBoolean(d["for_sale"]);
347 data[i].salePrice = Convert.ToInt32(d["sale_price"]);
348 data[i].actualArea = Convert.ToInt32(d["area"]);
349
350 if (++i >= count)
351 break;
352 }
353
354 remoteClient.SendDirLandReply(queryID, data);
355 }
356
357 public void DirFindQuery(IClientAPI remoteClient, UUID queryID,
358 string queryText, uint queryFlags, int queryStart)
359 {
360 if ((queryFlags & 1) != 0) //People (1 << 0)
361 {
362 DirPeopleQuery(remoteClient, queryID, queryText, queryFlags,
363 queryStart);
364 return;
365 }
366 else if ((queryFlags & 32) != 0) //DateEvents (1 << 5)
367 {
368 DirEventsQuery(remoteClient, queryID, queryText, queryFlags,
369 queryStart);
370 return;
371 }
372 }
373
374 public void DirPeopleQuery(IClientAPI remoteClient, UUID queryID,
375 string queryText, uint queryFlags, int queryStart)
376 {
377 List<UserAccount> accounts = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, queryText);
378
379 DirPeopleReplyData[] data =
380 new DirPeopleReplyData[accounts.Count];
381
382 int i = 0;
383
384 foreach (UserAccount item in accounts)
385 {
386 data[i] = new DirPeopleReplyData();
387
388 data[i].agentID = item.PrincipalID;
389 data[i].firstName = item.FirstName;
390 data[i].lastName = item.LastName;
391 data[i].group = "";
392 data[i].online = false;
393 data[i].reputation = 0;
394 i++;
395 }
396
397 remoteClient.SendDirPeopleReply(queryID, data);
398 }
399
400 public void DirEventsQuery(IClientAPI remoteClient, UUID queryID,
401 string queryText, uint queryFlags, int queryStart)
402 {
403 Hashtable ReqHash = new Hashtable();
404 ReqHash["text"] = queryText;
405 ReqHash["flags"] = queryFlags.ToString();
406 ReqHash["query_start"] = queryStart.ToString();
407
408 Hashtable result = GenericXMLRPCRequest(ReqHash,
409 "dir_events_query");
410
411 if (!Convert.ToBoolean(result["success"]))
412 {
413 remoteClient.SendAgentAlertMessage(
414 result["errorMessage"].ToString(), false);
415 return;
416 }
417
418 ArrayList dataArray = (ArrayList)result["data"];
419
420 int count = dataArray.Count;
421 if (count > 100)
422 count = 101;
423
424 DirEventsReplyData[] data = new DirEventsReplyData[count];
425
426 int i = 0;
427
428 foreach (Object o in dataArray)
429 {
430 Hashtable d = (Hashtable)o;
431
432 data[i] = new DirEventsReplyData();
433 data[i].ownerID = new UUID(d["owner_id"].ToString());
434 data[i].name = d["name"].ToString();
435 data[i].eventID = Convert.ToUInt32(d["event_id"]);
436 data[i].date = d["date"].ToString();
437 data[i].unixTime = Convert.ToUInt32(d["unix_time"]);
438 data[i].eventFlags = Convert.ToUInt32(d["event_flags"]);
439
440 if (++i >= count)
441 break;
442 }
443
444 remoteClient.SendDirEventsReply(queryID, data);
445 }
446
447 public void DirClassifiedQuery(IClientAPI remoteClient, UUID queryID,
448 string queryText, uint queryFlags, uint category,
449 int queryStart)
450 {
451 Hashtable ReqHash = new Hashtable();
452 ReqHash["text"] = queryText;
453 ReqHash["flags"] = queryFlags.ToString();
454 ReqHash["category"] = category.ToString();
455 ReqHash["query_start"] = queryStart.ToString();
456
457 Hashtable result = GenericXMLRPCRequest(ReqHash,
458 "dir_classified_query");
459
460 if (!Convert.ToBoolean(result["success"]))
461 {
462 remoteClient.SendAgentAlertMessage(
463 result["errorMessage"].ToString(), false);
464 return;
465 }
466
467 ArrayList dataArray = (ArrayList)result["data"];
468
469 int count = dataArray.Count;
470 if (count > 100)
471 count = 101;
472
473 DirClassifiedReplyData[] data = new DirClassifiedReplyData[count];
474
475 int i = 0;
476
477 foreach (Object o in dataArray)
478 {
479 Hashtable d = (Hashtable)o;
480
481 data[i] = new DirClassifiedReplyData();
482 data[i].classifiedID = new UUID(d["classifiedid"].ToString());
483 data[i].name = d["name"].ToString();
484 data[i].classifiedFlags = Convert.ToByte(d["classifiedflags"]);
485 data[i].creationDate = Convert.ToUInt32(d["creation_date"]);
486 data[i].expirationDate = Convert.ToUInt32(d["expiration_date"]);
487 data[i].price = Convert.ToInt32(d["priceforlisting"]);
488
489 if (++i >= count)
490 break;
491 }
492
493 remoteClient.SendDirClassifiedReply(queryID, data);
494 }
495
496 public void EventInfoRequest(IClientAPI remoteClient, uint queryEventID)
497 {
498 Hashtable ReqHash = new Hashtable();
499 ReqHash["eventID"] = queryEventID.ToString();
500
501 Hashtable result = GenericXMLRPCRequest(ReqHash,
502 "event_info_query");
503
504 if (!Convert.ToBoolean(result["success"]))
505 {
506 remoteClient.SendAgentAlertMessage(
507 result["errorMessage"].ToString(), false);
508 return;
509 }
510
511 ArrayList dataArray = (ArrayList)result["data"];
512 if (dataArray.Count == 0)
513 {
514 // something bad happened here, if we could return an
515 // event after the search,
516 // we should be able to find it here
517 // TODO do some (more) sensible error-handling here
518 remoteClient.SendAgentAlertMessage("Couldn't find this event.",
519 false);
520 return;
521 }
522
523 Hashtable d = (Hashtable)dataArray[0];
524 EventData data = new EventData();
525 data.eventID = Convert.ToUInt32(d["event_id"]);
526 data.creator = d["creator"].ToString();
527 data.name = d["name"].ToString();
528 data.category = d["category"].ToString();
529 data.description = d["description"].ToString();
530 data.date = d["date"].ToString();
531 data.dateUTC = Convert.ToUInt32(d["dateUTC"]);
532 data.duration = Convert.ToUInt32(d["duration"]);
533 data.cover = Convert.ToUInt32(d["covercharge"]);
534 data.amount = Convert.ToUInt32(d["coveramount"]);
535 data.simName = d["simname"].ToString();
536 Vector3.TryParse(d["globalposition"].ToString(), out data.globalPos);
537 data.eventFlags = Convert.ToUInt32(d["eventflags"]);
538
539 remoteClient.SendEventInfoReply(data);
540 }
541
542 public void ClassifiedInfoRequest(UUID queryClassifiedID, IClientAPI remoteClient)
543 {
544 Hashtable ReqHash = new Hashtable();
545 ReqHash["classifiedID"] = queryClassifiedID.ToString();
546
547 Hashtable result = GenericXMLRPCRequest(ReqHash,
548 "classifieds_info_query");
549
550 if (!Convert.ToBoolean(result["success"]))
551 {
552 remoteClient.SendAgentAlertMessage(
553 result["errorMessage"].ToString(), false);
554 return;
555 }
556
557 //The viewer seems to issue an info request even when it is
558 //creating a new classified which means the data hasn't been
559 //saved to the database yet so there is no info to find.
560 ArrayList dataArray = (ArrayList)result["data"];
561 if (dataArray.Count == 0)
562 {
563 // Something bad happened here if we could not return an
564 // event after the search. We should be able to find it here.
565 // TODO do some (more) sensible error-handling here
566// remoteClient.SendAgentAlertMessage("Couldn't find data for classified ad.",
567// false);
568 return;
569 }
570
571 Hashtable d = (Hashtable)dataArray[0];
572
573 Vector3 globalPos = new Vector3();
574 Vector3.TryParse(d["posglobal"].ToString(), out globalPos);
575
576 remoteClient.SendClassifiedInfoReply(
577 new UUID(d["classifieduuid"].ToString()),
578 new UUID(d["creatoruuid"].ToString()),
579 Convert.ToUInt32(d["creationdate"]),
580 Convert.ToUInt32(d["expirationdate"]),
581 Convert.ToUInt32(d["category"]),
582 d["name"].ToString(),
583 d["description"].ToString(),
584 new UUID(d["parceluuid"].ToString()),
585 Convert.ToUInt32(d["parentestate"]),
586 new UUID(d["snapshotuuid"].ToString()),
587 d["simname"].ToString(),
588 globalPos,
589 d["parcelname"].ToString(),
590 Convert.ToByte(d["classifiedflags"]),
591 Convert.ToInt32(d["priceforlisting"]));
592 }
593
594 public void HandleMapItemRequest(IClientAPI remoteClient, uint flags,
595 uint EstateID, bool godlike,
596 uint itemtype, ulong regionhandle)
597 {
598 //The following constant appears to be from GridLayerType enum
599 //defined in OpenMetaverse/GridManager.cs of libopenmetaverse.
600 if (itemtype == (uint)OpenMetaverse.GridItemType.LandForSale)
601 {
602 Hashtable ReqHash = new Hashtable();
603
604 //The flags are: SortAsc (1 << 15), PerMeterSort (1 << 17)
605 ReqHash["flags"] = "163840";
606 ReqHash["type"] = "4294967295"; //This is -1 in 32 bits
607 ReqHash["price"] = "0";
608 ReqHash["area"] = "0";
609 ReqHash["query_start"] = "0";
610
611 Hashtable result = GenericXMLRPCRequest(ReqHash,
612 "dir_land_query");
613
614 if (!Convert.ToBoolean(result["success"]))
615 {
616 remoteClient.SendAgentAlertMessage(
617 result["errorMessage"].ToString(), false);
618 return;
619 }
620
621 ArrayList dataArray = (ArrayList)result["data"];
622
623 int count = dataArray.Count;
624 if (count > 100)
625 count = 101;
626
627 List<mapItemReply> mapitems = new List<mapItemReply>();
628 string ParcelRegionUUID;
629 string[] landingpoint;
630
631 foreach (Object o in dataArray)
632 {
633 Hashtable d = (Hashtable)o;
634
635 if (d["name"] == null)
636 continue;
637
638 mapItemReply mapitem = new mapItemReply();
639
640 ParcelRegionUUID = d["region_UUID"].ToString();
641
642 foreach (Scene scene in m_Scenes)
643 {
644 if (scene.RegionInfo.RegionID.ToString() == ParcelRegionUUID)
645 {
646 landingpoint = d["landing_point"].ToString().Split('/');
647
648 mapitem.x = (uint)((scene.RegionInfo.RegionLocX * 256) +
649 Convert.ToDecimal(landingpoint[0]));
650 mapitem.y = (uint)((scene.RegionInfo.RegionLocY * 256) +
651 Convert.ToDecimal(landingpoint[1]));
652 break;
653 }
654 }
655
656 mapitem.id = new UUID(d["parcel_id"].ToString());
657 mapitem.Extra = Convert.ToInt32(d["area"]);
658 mapitem.Extra2 = Convert.ToInt32(d["sale_price"]);
659 mapitem.name = d["name"].ToString();
660
661 mapitems.Add(mapitem);
662 }
663
664 remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
665 mapitems.Clear();
666 }
667
668 if (itemtype == (uint)OpenMetaverse.GridItemType.PgEvent ||
669 itemtype == (uint)OpenMetaverse.GridItemType.MatureEvent ||
670 itemtype == (uint)OpenMetaverse.GridItemType.AdultEvent)
671 {
672 Hashtable ReqHash = new Hashtable();
673
674 //Find the maturity level
675 int maturity = (1 << 24);
676
677 //Find the maturity level
678 if (itemtype == (uint)OpenMetaverse.GridItemType.MatureEvent)
679 maturity = (1 << 25);
680 else
681 {
682 if (itemtype == (uint)OpenMetaverse.GridItemType.AdultEvent)
683 maturity = (1 << 26);
684 }
685
686 //The flags are: SortAsc (1 << 15), PerMeterSort (1 << 17)
687 maturity |= 163840;
688
689 //Character before | is number of days before/after current date
690 //Characters after | is the number for a category
691 ReqHash["text"] = "0|0";
692 ReqHash["flags"] = maturity.ToString();
693 ReqHash["query_start"] = "0";
694
695 Hashtable result = GenericXMLRPCRequest(ReqHash,
696 "dir_events_query");
697
698 if (!Convert.ToBoolean(result["success"]))
699 {
700 remoteClient.SendAgentAlertMessage(
701 result["errorMessage"].ToString(), false);
702 return;
703 }
704
705 ArrayList dataArray = (ArrayList)result["data"];
706
707 List<mapItemReply> mapitems = new List<mapItemReply>();
708 string ParcelRegionUUID;
709 string[] landingpoint;
710
711 foreach (Object o in dataArray)
712 {
713 Hashtable d = (Hashtable)o;
714
715 if (d["name"] == null)
716 continue;
717
718 mapItemReply mapitem = new mapItemReply();
719
720 ParcelRegionUUID = d["region_UUID"].ToString();
721
722 foreach (Scene scene in m_Scenes)
723 {
724 if (scene.RegionInfo.RegionID.ToString() == ParcelRegionUUID)
725 {
726 landingpoint = d["landing_point"].ToString().Split('/');
727
728 mapitem.x = (uint)((scene.RegionInfo.RegionLocX * 256) +
729 Convert.ToDecimal(landingpoint[0]));
730 mapitem.y = (uint)((scene.RegionInfo.RegionLocY * 256) +
731 Convert.ToDecimal(landingpoint[1]));
732 break;
733 }
734 }
735
736 mapitem.id = UUID.Random();
737 mapitem.Extra = (int)Convert.ToInt32(d["unix_time"]);
738 mapitem.Extra2 = (int)Convert.ToInt32(d["event_id"]);
739 mapitem.name = d["name"].ToString();
740
741 mapitems.Add(mapitem);
742 }
743
744 remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags);
745 mapitems.Clear();
746 }
747 }
748
749 public void Refresh()
750 {
751 }
752 }
753}
diff --git a/addon-modules/OpenSimSearch/README b/addon-modules/OpenSimSearch/README
new file mode 100644
index 0000000..ce826e3
--- /dev/null
+++ b/addon-modules/OpenSimSearch/README
@@ -0,0 +1,294 @@
1OpenSimSearch add-on module for Open Simulator
2
3Requirements
4
5The webserver needs PHP support with support for CURL and XMLRPC.
6NOTE: Not all webservers may have CURL and XMLRPC support for PHP.
7
8
9About the files
10
11README - The file you are currently reading
12
13bin/OpenSimSearch.Modules.dll - A pre-compiled module you can use
14
15OpenSimSearch/ - Source code for OpenSim search module
16
17webroot/*.php - Files to install on webserver
18
19webroot/sql/ossearch.sql - This will create the needed database tables
20webroot/sql/update-*.sql - Files used to update an older installation
21
22webroot/wiredux/ - Files to add to an installation of wiredux
23
24
25How it works
26
27If you are looking for a detailed explanation of how the add-on search system
28works you should study the contents of the files which accompany this README
29file. What follows is a general overview of the search package to give you an
30idea of how the pieces fit together in case you have any problems.
31
32There are three main components to OpenSimSearch which are a database, a DLL
33file, and several PHP files.
34
35Most of the tables in the search database contain details on items which can
36appear in the results of a search. The exception is the hostsregister table.
37The hostsregister table contains the list of OpenSim instances which will be
38checked periodically for information about searchable items.
39
40When an OpenSim instance starts it accesses the register.php script (using the
41URL of the data_services entry in OpenSim.ini) to record the host address and
42port of the instance in the hostsregister table of the database. The host and
43port entries are used by parser.php to retrieve the data to be indexed from
44the running instances.
45
46Take note of the last part of the previous paragraph where it stated that
47parser.php is used to index the data from an OpenSim instance and not from a
48region. The parser retrieves data from an OpenSim instance. If the instance
49is running a single region the data for a single region will be updated. If
50the instance is running multiple regions the data for all regions hosted by
51that instance will be indexed.
52
53OpenSim instances use the data snapshot module to create an XML based record
54of all searchable items in the regions they host. The XML record is retrieved
55by parser.php using a URL created from the host, port and the query string
56"?method=collector" (eg. http://127.0.0.1:9001/?method=collector). The
57parser.php file will get one host/port pair from the hostregister table each
58time it is called. It will parse the XML data from the OpenSim instance and
59save the data to the various tables of the search database.
60
61The query.php file is the heart of the search process. It receives an XML
62message from the DLL based on the search request originating in a viewer.
63The XML message is parsed to determine the type of search being performed,
64SQL queries are built and executed to retrieve the data from the database
65tables, and finally, the results are sent to the DLL file as an XML message
66for any final processing before being passed along to the viewer for display.
67
68
69Before you begin
70
71In the information that follows there are references to settings to be made
72in the OpenSim.ini files. As of the release of the 0.7 version of OpenSim
73some of the settings may no longer be in the OpenSim.ini file but may be
74found in the OpenSimDefault.ini file. If a section of the OpenSim.ini file
75mentioned in the information which follows doesn't exist in your copy of
76the file, you will either need to add the section and settings to the main
77OpenSim.ini file or make the changes to the OpenSimDefaults.ini file.
78
79
80Compiling the module
81
82Adding the OpenSimSearch C# source file to the source tree of OpenSim so it
83will be compiled at the same time as OpenSim is compiled is the easiest way
84to create to the DLL file needed by OpenSim.
85
86Copy the OpenSimSearch directory in to the addon-modules directory of your
87OpenSim source tree. After you have done that, compile OpenSim in the usual
88way (runprebuild and nant) to create the DLL file. When nant finishes, you
89will have an OpenSimSearch.Modules.dll file in the main bin directory of
90your OpenSim source tree along with a matching .pdb (or .mdb) file. These
91two files will be copied to the bin directory of your OpenSim instances
92during the installation or update steps.
93
94
95First time installation
96
97The first installation step is the creation of a database that will hold
98the data to be searched. If you have already installed the add-on module
99osprofile you should use the same database as osprofile as it shares some
100tables in common with ossearch. If you don't have osprofile installed you
101will need to create a new database that will hold the search tables. Having
102decided on the database to be used, use ossearch.sql (located in the
103webroot/sql directory) to create the required tables in the database. The
104name of this database will be needed when configuring one of the PHP files
105in the next steps.
106
107Copy the PHP files (located in the webroot directory) to your webserver.
108Remember to set the permissions on the file so the webserver may access the
109files. Use a text editor to open databaseinfo.php and enter the name or IP
110address of the database server, the name of the database, and the user name
111and password required to access the database.
112
113The next part of the installation process is to set up and configure your
114OpenSim instances.
115
116Copy the two OpenSimSearch.Modules files created during the compile steps
117(above) in to the bin directory of each of your OpenSim instances. The next
118part of the installation process requires some changes to the OpenSim.ini in
119all of your OpenSim instances.
120
121Add the following lines to all OpenSim.ini files:
122
123 [Search]
124 ;///////////////////////////////////////////////////////////////////////////
125 ;// The SearchURL is important. It must be correct or search won't work. //
126 ;// //
127 ;// Change the URL to point to the query.php file on your own HTTP server //
128 ;SearchURL = http://192.168.0.1/query.php
129 ;// //
130 ;///////////////////////////////////////////////////////////////////////////
131
132NOTE: You do not need to indent the above lines when adding them to your own
133OpenSim.ini files. The lines were indented to offset them from the rest of the
134text in this file.
135
136Uncomment and edit the SearchURL line so it contains the correct address for
137the webserver and needed path to where the query.php file was installed on
138your webserver.
139
140To allow the Teleport and Show on Map buttons to work properly (for search
141results containing locations) the following two lines must be added to the
142[Modules] section of the OpenSim.ini file:
143
144 LandServices = "RemoteLandServicesConnector"
145 LandServiceInConnector = "true"
146
147The last changes to be made to the OpenSim.ini file are in the [DataSnapshot]
148section. Change index_sims to true. You can have data_exposure set to all but
149it is better to leave it on minimum so users can control what items can appear
150in search results by using the "Show In Search" checkboxes. You can leave
151default_snapshot_period commented out or you can uncomment it and set it to
152whatever value you wish. The section on optimizing the configuration will
153help you to decide an appropriate value for this setting.
154
155The last change for OpenSim.ini is the setting for data_services. This line
156must be uncommented and contain a valid URL for your webserver and the path
157to the register.php file which you installed earlier. If you do not enter a
158valid URL the OS instance will not get listed in the hostsregister table and
159search data for the regions hosted by the OS instance will not be recorded or
160updated. After entering the URL, exit and save the updated OpenSim.ini file.
161
162
163Updating an existing installation
164
165Updating an existing installation of ossearch is just a matter of copying a
166few files to the same places where you had previously installed the files.
167
168Copy all of the PHP files (located in the webroot directory) EXCEPT for
169databaseinfo.php to the directory on your webserver where you place the
170previous copies. If you also copy databaseinfo.php when copying the other
171PHP files you will have to edit databaseinfo.php and reset the information
172used to connect to the database.
173
174Copy the two OpenSimSearch.Modules files created during compilation to the
175bin directory of each of your OpenSim instances.
176
177Finally, execute all of the SQL files located in the webroot/sql directory
178where the filenames start with "update-". This step is very important to
179make certain your database tables are up-to-date.
180
181
182Configuration
183
184With everything in place, the next step is to set up a task on your webserver
185(or some other machine) which will invoke the URL to the parser.php file on
186your webserver on a regular basis to ensure the contents of the database are
187kept up-to-date. For a machine running Linux you can set up a cron job. For
188a machine running Windows you can use Windows Scheduler.
189
190See the section on optimizing the configuration to help you decide how often
191the parser.php should be run.
192
193
194Optimizing the configuration
195
196When you change what items are to be found in search by clicking the checkbox
197"Show In Search" or by removing an item that was set to show in search results
198there is a delay before the change is reflected in the database tables. There
199are four main factors that affect the length of this delay. They are: the
200number of OpenSim instances, the value of default_snapshot_period used by the
201DataSnapshot module, the time between calls to parser.php, and the number of
202OpenSim instances processed each time parser.php is run. You can't easily
203control the number of instances but you can control the other factors.
204
205To explain how the factors affect the delay take a grid with 100 regions. If
206you have one region per instance you have 100 instances. Using the default
207settings and calling parser once an hour it would take 100 hours, or more than
208four days, for changes to appear in search results. A more realistic setup is
209one where you have an average of 4 regions run by each OpenSim instance. This
210reduces the delay to 25 hours. This is a rather long delay for a relatively
211small number of regions and instances. The delay can be reduced substantially.
212
213A simple way to reduce the delay is to run parser.php more frequently. If
214parser.php is run once every 15 minutes instead of once an hour (60 minutes)
215the delay is reduced by a factor of 4 from 25 hours to 6.25 hours. Much better
216but still a long delay. The delay can be reduced even further but to do so
217requires a change to the parser.php file.
218
219Near the end of the parser.php file is a SQL query that ends with "LIMIT 0,1".
220It is the value "1" which is important. That value limits the number of OS
221instances that will be processed each time parser.php is run. If the value is
222changed from 1 to 3 then three OS instances will be processed on each run of
223the parser.php file. This reduces the delay by a factor of 3 to just over
2242 hours. This is a much better delay than the original value of 25 hours.
225
226For those of you who like math, the amount of delay can be expressed using
227the following simple formula:
228 delay = # instances * time between runs of parser / limit value in parser
229
230Three factors affecting the delay have been discussed but earlier it was
231stated that there are four factors. The fourth factor is the value of
232default_snapshot_period value located in the [DataSnapshot] section of the
233OpenSim.ini file. This setting is specified in seconds and controls how often
234the data used by parser.php will be updated. This setting doesn't have any
235direct impact on the delay between updates of the database but if it is set
236incorrecty it can efffectively increase the delay between database updates.
237
238The example grid setup discussed earlier was adjusted to reduce the update
239delay to just over 2 hours (2 hours and 5 minutes to be more precise). If the
240value of default_snapshot_period is set to a value slightly greater than the
241calculated delay it would be possible for parser.php to be called twice before
242the data it retrieves would have been updated. This would turn a delay of two
243hours into a delay of four hours. The proper setting is one that is *less*
244than the delay calculated from the other three factors discussed earlier.
245
246Given a delay of just over 2 hours, a good value for default_snapshot_period
247would be 7200 (2 hours expressed in seconds). By keeping the value of this
248setting close to, but less than, the delay between when parser.php is used
249to get the data for an instance will minimize any overhead imposed on the
250OpenSim instance when it creates the snapshot of searchable items while, at
251the same time, ensures the data will have been updated by the next time the
252parser.php is run to update the database.
253
254A final comment about the setting for the time between runs of parser.php and
255the value in the limit statement in that file. Avoid running parser.php too
256frequently or setting the value in the LIMIT statement too high. Doing either
257can cause unnecessary overhead or high loads on the webserver used to run
258parser.php, or on the database server while it updates all the tables with
259the latest information from the OpenSim instances.
260
261
262Additional Information
263
264A few words about event listings and the events database table.
265Support is included for events but the event listings need to be created
266using an external webpage.
267
268The category for an event is stored as a number. The numbers for the
269categories are as follows:
2700 - Any (NOTE: Event information will show "*Unspecified*")
27118- Discussion
27219- Sports
27320- Live Music
27422- Commercial
27523- Nightlife/Entertainment
27624- Games/Contests
27725- Pageants
27826- Education
27927- Arts and Culture
28028- Charity/Support Groups
28129- Miscellaneous
282
283The dateUTC field is a timestamp for the event in UTC time.
284
285The covercharge field is a boolean. Set it to 0 if there is no cover charge
286for the event. When covercharge is not 0, the amount is in the coveramount
287field. (It seems silly to require the boolean but this has been left in to
288avoid any compatability issues.)
289
290The globalPos field is the location of the event as a global grid coordinate.
291The format is "x/y/z". where x and y are the grid X and Y positions (times
292256) plus the x and y offset within the region named by the simname field.
293
294The eventflags field is 0 for a PG event, 1 for Mature, and 2 for Adult.
diff --git a/addon-modules/OpenSimSearch/prebuild.xml b/addon-modules/OpenSimSearch/prebuild.xml
new file mode 100644
index 0000000..961ebf9
--- /dev/null
+++ b/addon-modules/OpenSimSearch/prebuild.xml
@@ -0,0 +1,39 @@
1<?xml version="1.0" ?>
2<Project frameworkVersion="v3_5" name="OpenSimSearch.Modules" path="addon-modules/OpenSimSearch/Modules" type="Library">
3 <Configuration name="Debug">
4 <Options>
5 <OutputPath>../../../bin/</OutputPath>
6 </Options>
7 </Configuration>
8 <Configuration name="Release">
9 <Options>
10 <OutputPath>../../../bin/</OutputPath>
11 </Options>
12 </Configuration>
13
14 <ReferencePath>../../../bin/</ReferencePath>
15 <Reference localCopy="false" name="System"/>
16 <Reference name="System.Xml"/>
17 <Reference name="System.Drawing"/>
18 <Reference name="System.Runtime.Remoting"/>
19 <Reference name="OpenMetaverseTypes" path="../../../bin/"/>
20 <Reference name="OpenMetaverse" path="../../../bin/"/>
21 <Reference name="Axiom.MathLib" path="../../../bin/"/>
22 <Reference name="OpenSim.Framework"/>
23 <Reference name="OpenSim.Data"/>
24 <Reference name="OpenSim.Region.Framework"/>
25 <Reference name="OpenSim.Framework.Console"/>
26 <Reference name="OpenSim.Framework.Servers"/>
27 <Reference name="OpenSim.Framework.Statistics"/>
28 <Reference name="OpenSim.Framework.Communications"/>
29 <Reference name="OpenSim.Region.Physics.Manager"/>
30 <Reference name="OpenSim.Services.Interfaces"/>
31 <Reference name="Nini" path="../../../bin/"/>
32 <Reference name="log4net" path="../../../bin/"/>
33 <Reference name="XMLRPC" path="../../../bin/"/>
34 <Reference name="Mono.Addins" path="../../../bin/"/>
35
36 <Files>
37 <Match pattern="*.cs" recurse="true"/>
38 </Files>
39</Project>