diff options
-rw-r--r-- | OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs | 216 | ||||
-rw-r--r-- | bin/OpenSim.ini.example | 22 |
2 files changed, 191 insertions, 47 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs index ab60763..acc25dd 100644 --- a/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs +++ b/OpenSim/Region/Environment/Modules/Avatar/Concierge/ConciergeModule.cs | |||
@@ -26,6 +26,7 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
29 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
30 | using System.IO; | 31 | using System.IO; |
31 | using System.Net.Sockets; | 32 | using System.Net.Sockets; |
@@ -34,8 +35,10 @@ using System.Text.RegularExpressions; | |||
34 | using System.Threading; | 35 | using System.Threading; |
35 | using log4net; | 36 | using log4net; |
36 | using Nini.Config; | 37 | using Nini.Config; |
38 | using Nwc.XmlRpc; | ||
37 | using OpenMetaverse; | 39 | using OpenMetaverse; |
38 | using OpenSim.Framework; | 40 | using OpenSim.Framework; |
41 | using OpenSim.Framework.Servers; | ||
39 | using OpenSim.Region.Environment.Interfaces; | 42 | using OpenSim.Region.Environment.Interfaces; |
40 | using OpenSim.Region.Environment.Scenes; | 43 | using OpenSim.Region.Environment.Scenes; |
41 | using OpenSim.Region.Environment.Modules.Avatar.Chat; | 44 | using OpenSim.Region.Environment.Modules.Avatar.Chat; |
@@ -48,15 +51,20 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
48 | 51 | ||
49 | private const int DEBUG_CHANNEL = 2147483647; | 52 | private const int DEBUG_CHANNEL = 2147483647; |
50 | 53 | ||
51 | private int _conciergeChannel = 42; | ||
52 | private List<IScene> _scenes = new List<IScene>(); | 54 | private List<IScene> _scenes = new List<IScene>(); |
53 | private List<IScene> _conciergedScenes = new List<IScene>(); | 55 | private List<IScene> _conciergedScenes = new List<IScene>(); |
54 | private Dictionary<IScene, List<string>> _sceneAttendees = new Dictionary<IScene, List<string>>(); | 56 | private Dictionary<IScene, List<ScenePresence>> _sceneAttendees = new Dictionary<IScene, List<ScenePresence>>(); |
57 | private bool _replacingChatModule = false; | ||
58 | |||
55 | private IConfig _config; | 59 | private IConfig _config; |
60 | |||
56 | private string _whoami = "conferencier"; | 61 | private string _whoami = "conferencier"; |
57 | private bool _replacingChatModule = false; | ||
58 | private Regex _regions = null; | 62 | private Regex _regions = null; |
59 | private string _welcomes = null; | 63 | private string _welcomes = null; |
64 | private int _conciergeChannel = 42; | ||
65 | private string _announceEntering = "{0} enters {1} (now {2} visitors in this region)"; | ||
66 | private string _announceLeaving = "{0} leaves {1} (back to {2} visitors in this region)"; | ||
67 | private string _xmlRpcPassword = String.Empty; | ||
60 | 68 | ||
61 | internal object _syncy = new object(); | 69 | internal object _syncy = new object(); |
62 | 70 | ||
@@ -108,6 +116,9 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
108 | _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel); | 116 | _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel); |
109 | _whoami = _config.GetString("whoami", "conferencier"); | 117 | _whoami = _config.GetString("whoami", "conferencier"); |
110 | _welcomes = _config.GetString("welcomes", _welcomes); | 118 | _welcomes = _config.GetString("welcomes", _welcomes); |
119 | _announceEntering = _config.GetString("announce_entering", _announceEntering); | ||
120 | _announceLeaving = _config.GetString("announce_leaving", _announceLeaving); | ||
121 | _xmlRpcPassword = _config.GetString("password", _xmlRpcPassword); | ||
111 | _log.InfoFormat("[Concierge] reporting as \"{0}\" to our users", _whoami); | 122 | _log.InfoFormat("[Concierge] reporting as \"{0}\" to our users", _whoami); |
112 | 123 | ||
113 | // calculate regions Regex | 124 | // calculate regions Regex |
@@ -116,10 +127,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
116 | string regions = _config.GetString("regions", String.Empty); | 127 | string regions = _config.GetString("regions", String.Empty); |
117 | if (!String.IsNullOrEmpty(regions)) | 128 | if (!String.IsNullOrEmpty(regions)) |
118 | { | 129 | { |
119 | _regions = new Regex(regions, RegexOptions.Compiled | RegexOptions.IgnoreCase); | 130 | _regions = new Regex(@regions, RegexOptions.Compiled | RegexOptions.IgnoreCase); |
120 | } | 131 | } |
121 | } | 132 | } |
122 | 133 | ||
134 | scene.CommsManager.HttpServer.AddXmlRPCHandler("concierge_update_welcome", XmlRpcUpdateWelcomeMethod, false); | ||
135 | |||
123 | lock (_syncy) | 136 | lock (_syncy) |
124 | { | 137 | { |
125 | if (!_scenes.Contains(scene)) | 138 | if (!_scenes.Contains(scene)) |
@@ -244,17 +257,13 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
244 | { | 257 | { |
245 | client.OnLogout += OnClientLoggedOut; | 258 | client.OnLogout += OnClientLoggedOut; |
246 | client.OnConnectionClosed += OnClientLoggedOut; | 259 | client.OnConnectionClosed += OnClientLoggedOut; |
260 | |||
247 | if (_replacingChatModule) | 261 | if (_replacingChatModule) |
248 | client.OnChatFromClient += OnChatFromClient; | 262 | client.OnChatFromClient += OnChatFromClient; |
249 | |||
250 | if (_conciergedScenes.Contains(client.Scene)) | ||
251 | { | ||
252 | _log.DebugFormat("[Concierge] {0} logs on to {1}", client.Name, client.Scene.RegionInfo.RegionName); | ||
253 | AnnounceToAgentsRegion(client, String.Format("{0} logs on to {1}", client.Name, | ||
254 | client.Scene.RegionInfo.RegionName)); | ||
255 | } | ||
256 | } | 263 | } |
257 | 264 | ||
265 | |||
266 | |||
258 | public void OnClientLoggedOut(IClientAPI client) | 267 | public void OnClientLoggedOut(IClientAPI client) |
259 | { | 268 | { |
260 | client.OnLogout -= OnClientLoggedOut; | 269 | client.OnLogout -= OnClientLoggedOut; |
@@ -263,8 +272,10 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
263 | if (_conciergedScenes.Contains(client.Scene)) | 272 | if (_conciergedScenes.Contains(client.Scene)) |
264 | { | 273 | { |
265 | _log.DebugFormat("[Concierge] {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); | 274 | _log.DebugFormat("[Concierge] {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); |
266 | AnnounceToAgentsRegion(client, String.Format("{0} logs off from {1}", client.Name, | 275 | ScenePresence agent = (client.Scene as Scene).GetScenePresence(client.AgentId); |
267 | client.Scene.RegionInfo.RegionName)); | 276 | RemoveFromAttendeeList(agent, agent.Scene); |
277 | AnnounceToAgentsRegion(agent, String.Format(_announceLeaving, agent.Name, agent.Scene.RegionInfo.RegionName, | ||
278 | _sceneAttendees[agent.Scene].Count)); | ||
268 | } | 279 | } |
269 | } | 280 | } |
270 | 281 | ||
@@ -274,13 +285,69 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
274 | if (_conciergedScenes.Contains(agent.Scene)) | 285 | if (_conciergedScenes.Contains(agent.Scene)) |
275 | { | 286 | { |
276 | _log.DebugFormat("[Concierge] {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); | 287 | _log.DebugFormat("[Concierge] {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); |
288 | AddToAttendeeList(agent, agent.Scene); | ||
289 | WelcomeAvatar(agent, agent.Scene); | ||
290 | AnnounceToAgentsRegion(agent, String.Format(_announceEntering, agent.Name, agent.Scene.RegionInfo.RegionName, | ||
291 | _sceneAttendees[agent.Scene].Count)); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | |||
296 | public void OnMakeChildAgent(ScenePresence agent) | ||
297 | { | ||
298 | if (_conciergedScenes.Contains(agent.Scene)) | ||
299 | { | ||
300 | _log.DebugFormat("[Concierge] {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); | ||
301 | RemoveFromAttendeeList(agent, agent.Scene); | ||
302 | AnnounceToAgentsRegion(agent, String.Format(_announceLeaving, agent.Name, agent.Scene.RegionInfo.RegionName, | ||
303 | _sceneAttendees[agent.Scene].Count)); | ||
304 | } | ||
305 | } | ||
277 | 306 | ||
278 | // welcome mechanics: check whether we have a welcomes | 307 | protected void AddToAttendeeList(ScenePresence agent, Scene scene) |
279 | // directory set and wether there is a region specific | 308 | { |
280 | // welcome file there: if yes, send it to the agent | 309 | lock(_sceneAttendees) |
281 | if (!String.IsNullOrEmpty(_welcomes)) | 310 | { |
311 | if (!_sceneAttendees.ContainsKey(scene)) | ||
312 | _sceneAttendees[scene] = new List<ScenePresence>(); | ||
313 | List<ScenePresence> attendees = _sceneAttendees[scene]; | ||
314 | if (!attendees.Contains(agent)) | ||
315 | attendees.Add(agent); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | protected void RemoveFromAttendeeList(ScenePresence agent, Scene scene) | ||
320 | { | ||
321 | lock(_sceneAttendees) | ||
322 | { | ||
323 | if (!_sceneAttendees.ContainsKey(scene)) | ||
324 | { | ||
325 | _log.WarnFormat("[Concierge] attendee list missing for region {0}", scene.RegionInfo.RegionName); | ||
326 | return; | ||
327 | } | ||
328 | List<ScenePresence> attendees = _sceneAttendees[scene]; | ||
329 | if (!attendees.Contains(agent)) | ||
330 | { | ||
331 | _log.WarnFormat("[Concierge] avatar {0} sneaked in (not on attendee list of region {1})", | ||
332 | agent.Name, scene.RegionInfo.RegionName); | ||
333 | return; | ||
334 | } | ||
335 | attendees.Remove(agent); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | protected void WelcomeAvatar(ScenePresence agent, Scene scene) | ||
340 | { | ||
341 | // welcome mechanics: check whether we have a welcomes | ||
342 | // directory set and wether there is a region specific | ||
343 | // welcome file there: if yes, send it to the agent | ||
344 | if (!String.IsNullOrEmpty(_welcomes)) | ||
345 | { | ||
346 | string[] welcomes = new string[] { | ||
347 | Path.Combine(_welcomes, agent.Scene.RegionInfo.RegionName), | ||
348 | Path.Combine(_welcomes, "DEFAULT")}; | ||
349 | foreach (string welcome in welcomes) | ||
282 | { | 350 | { |
283 | string welcome = Path.Combine(_welcomes, agent.Scene.RegionInfo.RegionName); | ||
284 | if (File.Exists(welcome)) | 351 | if (File.Exists(welcome)) |
285 | { | 352 | { |
286 | try | 353 | try |
@@ -288,46 +355,22 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
288 | string[] welcomeLines = File.ReadAllLines(welcome); | 355 | string[] welcomeLines = File.ReadAllLines(welcome); |
289 | foreach (string l in welcomeLines) | 356 | foreach (string l in welcomeLines) |
290 | { | 357 | { |
291 | AnnounceToAgent(agent, String.Format(l, agent.Name, agent.Scene.RegionInfo.RegionName, _whoami)); | 358 | AnnounceToAgent(agent, String.Format(l, agent.Name, scene.RegionInfo.RegionName, _whoami)); |
292 | } | 359 | } |
293 | } | 360 | } |
294 | catch (IOException ioe) | 361 | catch (IOException ioe) |
295 | { | 362 | { |
296 | _log.ErrorFormat("[Concierge] run into trouble reading welcome file {0} for region {1} for avatar {2}: {3}", | 363 | _log.ErrorFormat("[Concierge] run into trouble reading welcome file {0} for region {1} for avatar {2}: {3}", |
297 | welcome, agent.Scene.RegionInfo.RegionName, agent.Name, ioe); | 364 | welcome, scene.RegionInfo.RegionName, agent.Name, ioe); |
298 | } | 365 | } |
299 | catch (FormatException fe) | 366 | catch (FormatException fe) |
300 | { | 367 | { |
301 | _log.ErrorFormat("[Concierge] welcome file {0} is malformed: {1}", welcome, fe); | 368 | _log.ErrorFormat("[Concierge] welcome file {0} is malformed: {1}", welcome, fe); |
302 | } | 369 | } |
303 | } else { | 370 | } |
304 | _log.DebugFormat("[Concierge] not playing out welcome message: {0} not found", welcome); | 371 | return; |
305 | } | ||
306 | } | 372 | } |
307 | 373 | _log.DebugFormat("[Concierge] no welcome message for region {0}", scene.RegionInfo.RegionName); | |
308 | AnnounceToAgentsRegion(agent, String.Format("{0} enters {1}", agent.Name, | ||
309 | agent.Scene.RegionInfo.RegionName)); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | |||
314 | public void OnMakeChildAgent(ScenePresence agent) | ||
315 | { | ||
316 | if (_conciergedScenes.Contains(agent.Scene)) | ||
317 | { | ||
318 | _log.DebugFormat("[Concierge] {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); | ||
319 | AnnounceToAgentsRegion(agent, String.Format("{0} leaves {1}", agent.Name, | ||
320 | agent.Scene.RegionInfo.RegionName)); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | |||
325 | public void ClientLoggedOut(IClientAPI client) | ||
326 | { | ||
327 | if (_conciergedScenes.Contains(client.Scene)) | ||
328 | { | ||
329 | _log.DebugFormat("[Concierge] {0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName); | ||
330 | AnnounceToAgentsRegion(client, String.Format("{0} logs out of {1}", client.Name, client.Scene.RegionInfo.RegionName)); | ||
331 | } | 374 | } |
332 | } | 375 | } |
333 | 376 | ||
@@ -372,5 +415,84 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Concierge | |||
372 | agent.ControllingClient.SendChatMessage(msg, (byte) ChatTypeEnum.Say, PosOfGod, _whoami, UUID.Zero, | 415 | agent.ControllingClient.SendChatMessage(msg, (byte) ChatTypeEnum.Say, PosOfGod, _whoami, UUID.Zero, |
373 | (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully); | 416 | (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully); |
374 | } | 417 | } |
418 | |||
419 | private static void checkStringParameters(XmlRpcRequest request, string[] param) | ||
420 | { | ||
421 | Hashtable requestData = (Hashtable) request.Params[0]; | ||
422 | foreach (string p in param) | ||
423 | { | ||
424 | if (!requestData.Contains(p)) | ||
425 | throw new Exception(String.Format("missing string parameter {0}", p)); | ||
426 | if (String.IsNullOrEmpty((string)requestData[p])) | ||
427 | throw new Exception(String.Format("parameter {0} is empty", p)); | ||
428 | } | ||
429 | } | ||
430 | |||
431 | private static void checkIntegerParams(XmlRpcRequest request, string[] param) | ||
432 | { | ||
433 | Hashtable requestData = (Hashtable) request.Params[0]; | ||
434 | foreach (string p in param) | ||
435 | { | ||
436 | if (!requestData.Contains(p)) | ||
437 | throw new Exception(String.Format("missing integer parameter {0}", p)); | ||
438 | } | ||
439 | } | ||
440 | |||
441 | public XmlRpcResponse XmlRpcUpdateWelcomeMethod(XmlRpcRequest request) | ||
442 | { | ||
443 | _log.Info("[Concierge]: processing UpdateWelcome request"); | ||
444 | XmlRpcResponse response = new XmlRpcResponse(); | ||
445 | Hashtable responseData = new Hashtable(); | ||
446 | |||
447 | try | ||
448 | { | ||
449 | Hashtable requestData = (Hashtable)request.Params[0]; | ||
450 | checkStringParameters(request, new string[] { "password", "region", "welcome" }); | ||
451 | |||
452 | // check password | ||
453 | if (!String.IsNullOrEmpty(_xmlRpcPassword) && | ||
454 | (string)requestData["password"] != _xmlRpcPassword) throw new Exception("wrong password"); | ||
455 | |||
456 | if (String.IsNullOrEmpty(_welcomes)) | ||
457 | throw new Exception("welcome templates are not enabled, ask your OpenSim operator to set the \"welcomes\" option in the [Concierge] section of OpenSim.ini"); | ||
458 | |||
459 | string msg = (string)requestData["welcome"]; | ||
460 | if (String.IsNullOrEmpty(msg)) | ||
461 | throw new Exception("empty parameter \"welcome\""); | ||
462 | |||
463 | string regionName = (string)requestData["region"]; | ||
464 | IScene scene = _scenes.Find(delegate(IScene s) { return s.RegionInfo.RegionName == regionName; }); | ||
465 | if (scene == null) | ||
466 | throw new Exception(String.Format("unknown region \"{0}\"", regionName)); | ||
467 | |||
468 | if (!_conciergedScenes.Contains(scene)) | ||
469 | throw new Exception(String.Format("region \"{0}\" is not a concierged region.", regionName)); | ||
470 | |||
471 | string welcome = Path.Combine(_welcomes, regionName); | ||
472 | if (File.Exists(welcome)) | ||
473 | { | ||
474 | _log.InfoFormat("[Concierge] UpdateWelcome: updating existing template \"{0}\"", welcome); | ||
475 | string welcomeBackup = String.Format("{0}~", welcome); | ||
476 | if (File.Exists(welcomeBackup)) | ||
477 | File.Delete(welcomeBackup); | ||
478 | File.Move(welcome, welcomeBackup); | ||
479 | } | ||
480 | File.WriteAllText(welcome, msg); | ||
481 | |||
482 | responseData["success"] = "true"; | ||
483 | response.Value = responseData; | ||
484 | } | ||
485 | catch (Exception e) | ||
486 | { | ||
487 | _log.InfoFormat("[Concierge] UpdateWelcome failed: {0}", e.Message); | ||
488 | |||
489 | responseData["success"] = "false"; | ||
490 | responseData["error"] = e.Message; | ||
491 | |||
492 | response.Value = responseData; | ||
493 | } | ||
494 | _log.Debug("[Concierge]: done processing UpdateWelcome request"); | ||
495 | return response; | ||
496 | } | ||
375 | } | 497 | } |
376 | } \ No newline at end of file | 498 | } \ No newline at end of file |
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 08dc27e..8a5bdd7 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example | |||
@@ -411,10 +411,29 @@ flush-on-error=true | |||
411 | ;; relay_private_channels = true: will relay IRC chat from/to private in-world channels | 411 | ;; relay_private_channels = true: will relay IRC chat from/to private in-world channels |
412 | ;; relay_private_channel_out -- channel to send messages out to the IRC bridge | 412 | ;; relay_private_channel_out -- channel to send messages out to the IRC bridge |
413 | ;; relay_private_channel_in -- channel to receive message from the IRC bridge | 413 | ;; relay_private_channel_in -- channel to receive message from the IRC bridge |
414 | ;; relay_chat = false: IRC bridge will not relay normal chat | ||
414 | ;; access_password -- simple security device | 415 | ;; access_password -- simple security device |
416 | ;; | ||
417 | ;; so, to just relay chat from an IRC channel to in-world region and vice versa: | ||
418 | ;; | ||
419 | ;; relay_private_channels = false | ||
420 | ;; relay_chat = true | ||
421 | ;; | ||
422 | ;; to relay chat only to/from private in-world channels: | ||
423 | ;; | ||
424 | ;; relay_chat = false | ||
425 | ;; relay_private_channels = true | ||
426 | ;; relay_private_channel_in = 2226 | ||
427 | ;; relay_private_channel_out = 2225 | ||
428 | ;; | ||
429 | ;; in this example, all chat coming in from IRC will be send out via | ||
430 | ;; in-world channel 2226, and all chat from in-world channel 2225 will | ||
431 | ;; be relayed to the IRC channel. | ||
432 | ;; | ||
415 | ;relay_private_channels = false | 433 | ;relay_private_channels = false |
416 | ;relay_private_channel_in = 2226 | 434 | ;relay_private_channel_in = 2226 |
417 | ;relay_private_channel_out = 2225 | 435 | ;relay_private_channel_out = 2225 |
436 | ;relay_chat = true | ||
418 | ;access_password = foobar | 437 | ;access_password = foobar |
419 | 438 | ||
420 | ;fallback_region = name of "default" region | 439 | ;fallback_region = name of "default" region |
@@ -798,6 +817,9 @@ enabled = false | |||
798 | ; name of the concierge | 817 | ; name of the concierge |
799 | whoami = "jeeves" | 818 | whoami = "jeeves" |
800 | 819 | ||
820 | ; password for updating the welcome message templates via XmlRpc | ||
821 | password = SECRET | ||
822 | |||
801 | ; regex specifying for which regions concierge service is desired; if | 823 | ; regex specifying for which regions concierge service is desired; if |
802 | ; empty, then for all | 824 | ; empty, then for all |
803 | regions = "^MeetingSpace-" | 825 | regions = "^MeetingSpace-" |