diff options
author | Dr Scofield | 2009-02-10 13:10:57 +0000 |
---|---|---|
committer | Dr Scofield | 2009-02-10 13:10:57 +0000 |
commit | 180be7de07014aa33bc6066f12a0819b731c1c9d (patch) | |
tree | 3aa13af3cda4b808fa9453655875327699b61311 /OpenSim/Region/OptionalModules/Avatar/Concierge | |
parent | Stopgap measure: To use gridlaunch, or GUI, start opensim with (diff) | |
download | opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.zip opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.gz opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.bz2 opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.xz |
this is step 2 of 2 of the OpenSim.Region.Environment refactor.
NOTHING has been deleted or moved off to forge at this point. what
has happened is that OpenSim.Region.Environment.Modules has been split
in two:
- OpenSim.Region.CoreModules: all those modules that are either
directly or indirectly referenced from other OpenSim packages, or
that provide functionality that the OpenSim developer community
considers core functionality:
CoreModules/Agent/AssetTransaction
CoreModules/Agent/Capabilities
CoreModules/Agent/TextureDownload
CoreModules/Agent/TextureSender
CoreModules/Agent/TextureSender/Tests
CoreModules/Agent/Xfer
CoreModules/Avatar/AvatarFactory
CoreModules/Avatar/Chat/ChatModule
CoreModules/Avatar/Combat
CoreModules/Avatar/Currency/SampleMoney
CoreModules/Avatar/Dialog
CoreModules/Avatar/Friends
CoreModules/Avatar/Gestures
CoreModules/Avatar/Groups
CoreModules/Avatar/InstantMessage
CoreModules/Avatar/Inventory
CoreModules/Avatar/Inventory/Archiver
CoreModules/Avatar/Inventory/Transfer
CoreModules/Avatar/Lure
CoreModules/Avatar/ObjectCaps
CoreModules/Avatar/Profiles
CoreModules/Communications/Local
CoreModules/Communications/REST
CoreModules/Framework/EventQueue
CoreModules/Framework/InterfaceCommander
CoreModules/Hypergrid
CoreModules/InterGrid
CoreModules/Scripting/DynamicTexture
CoreModules/Scripting/EMailModules
CoreModules/Scripting/HttpRequest
CoreModules/Scripting/LoadImageURL
CoreModules/Scripting/VectorRender
CoreModules/Scripting/WorldComm
CoreModules/Scripting/XMLRPC
CoreModules/World/Archiver
CoreModules/World/Archiver/Tests
CoreModules/World/Estate
CoreModules/World/Land
CoreModules/World/Permissions
CoreModules/World/Serialiser
CoreModules/World/Sound
CoreModules/World/Sun
CoreModules/World/Terrain
CoreModules/World/Terrain/DefaultEffects
CoreModules/World/Terrain/DefaultEffects/bin
CoreModules/World/Terrain/DefaultEffects/bin/Debug
CoreModules/World/Terrain/Effects
CoreModules/World/Terrain/FileLoaders
CoreModules/World/Terrain/FloodBrushes
CoreModules/World/Terrain/PaintBrushes
CoreModules/World/Terrain/Tests
CoreModules/World/Vegetation
CoreModules/World/Wind
CoreModules/World/WorldMap
- OpenSim.Region.OptionalModules: all those modules that are not core
modules:
OptionalModules/Avatar/Chat/IRC-stuff
OptionalModules/Avatar/Concierge
OptionalModules/Avatar/Voice/AsterixVoice
OptionalModules/Avatar/Voice/SIPVoice
OptionalModules/ContentManagementSystem
OptionalModules/Grid/Interregion
OptionalModules/Python
OptionalModules/SvnSerialiser
OptionalModules/World/NPC
OptionalModules/World/TreePopulator
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/Concierge')
-rw-r--r-- | OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs | 604 | ||||
-rwxr-xr-x | OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py | 130 |
2 files changed, 734 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs new file mode 100644 index 0000000..bb46b11 --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs | |||
@@ -0,0 +1,604 @@ | |||
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 OpenSim 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.IO; | ||
32 | using System.Net; | ||
33 | using System.Net.Sockets; | ||
34 | using System.Reflection; | ||
35 | using System.Text; | ||
36 | using System.Text.RegularExpressions; | ||
37 | using System.Threading; | ||
38 | using log4net; | ||
39 | using Nini.Config; | ||
40 | using Nwc.XmlRpc; | ||
41 | using OpenMetaverse; | ||
42 | using OpenSim.Framework; | ||
43 | using OpenSim.Framework.Servers; | ||
44 | using OpenSim.Region.Framework.Interfaces; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using OpenSim.Region.CoreModules.Avatar.Chat; | ||
47 | |||
48 | namespace OpenSim.Region.OptionalModules.Avatar.Concierge | ||
49 | { | ||
50 | public class ConciergeModule : ChatModule, IRegionModule | ||
51 | { | ||
52 | private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | private const int DEBUG_CHANNEL = 2147483647; | ||
55 | |||
56 | private List<IScene> _scenes = new List<IScene>(); | ||
57 | private List<IScene> _conciergedScenes = new List<IScene>(); | ||
58 | private Dictionary<IScene, List<UUID>> _sceneAttendees = | ||
59 | new Dictionary<IScene, List<UUID>>(); | ||
60 | private Dictionary<UUID, string> _attendeeNames = | ||
61 | new Dictionary<UUID, string>(); | ||
62 | |||
63 | private bool _replacingChatModule = false; | ||
64 | |||
65 | private IConfig _config; | ||
66 | |||
67 | private string _whoami = "conferencier"; | ||
68 | private Regex _regions = null; | ||
69 | private string _welcomes = null; | ||
70 | private int _conciergeChannel = 42; | ||
71 | private string _announceEntering = "{0} enters {1} (now {2} visitors in this region)"; | ||
72 | private string _announceLeaving = "{0} leaves {1} (back to {2} visitors in this region)"; | ||
73 | private string _xmlRpcPassword = String.Empty; | ||
74 | private string _brokerURI = String.Empty; | ||
75 | |||
76 | internal object _syncy = new object(); | ||
77 | |||
78 | #region IRegionModule Members | ||
79 | public override void Initialise(Scene scene, IConfigSource config) | ||
80 | { | ||
81 | try | ||
82 | { | ||
83 | if ((_config = config.Configs["Concierge"]) == null) | ||
84 | { | ||
85 | //_log.InfoFormat("[Concierge]: no configuration section [Concierge] in OpenSim.ini: module not configured"); | ||
86 | return; | ||
87 | } | ||
88 | |||
89 | if (!_config.GetBoolean("enabled", false)) | ||
90 | { | ||
91 | //_log.InfoFormat("[Concierge]: module disabled by OpenSim.ini configuration"); | ||
92 | return; | ||
93 | } | ||
94 | } | ||
95 | catch (Exception) | ||
96 | { | ||
97 | _log.Info("[Concierge]: module not configured"); | ||
98 | return; | ||
99 | } | ||
100 | |||
101 | // check whether ChatModule has been disabled: if yes, | ||
102 | // then we'll "stand in" | ||
103 | try | ||
104 | { | ||
105 | if (config.Configs["Chat"] == null) | ||
106 | { | ||
107 | _replacingChatModule = false; | ||
108 | } | ||
109 | else | ||
110 | { | ||
111 | _replacingChatModule = !config.Configs["Chat"].GetBoolean("enabled", true); | ||
112 | } | ||
113 | } | ||
114 | catch (Exception) | ||
115 | { | ||
116 | _replacingChatModule = false; | ||
117 | } | ||
118 | _log.InfoFormat("[Concierge] {0} ChatModule", _replacingChatModule ? "replacing" : "not replacing"); | ||
119 | |||
120 | |||
121 | // take note of concierge channel and of identity | ||
122 | _conciergeChannel = config.Configs["Concierge"].GetInt("concierge_channel", _conciergeChannel); | ||
123 | _whoami = _config.GetString("whoami", "conferencier"); | ||
124 | _welcomes = _config.GetString("welcomes", _welcomes); | ||
125 | _announceEntering = _config.GetString("announce_entering", _announceEntering); | ||
126 | _announceLeaving = _config.GetString("announce_leaving", _announceLeaving); | ||
127 | _xmlRpcPassword = _config.GetString("password", _xmlRpcPassword); | ||
128 | _brokerURI = _config.GetString("broker", _brokerURI); | ||
129 | |||
130 | _log.InfoFormat("[Concierge] reporting as \"{0}\" to our users", _whoami); | ||
131 | |||
132 | // calculate regions Regex | ||
133 | if (_regions == null) | ||
134 | { | ||
135 | string regions = _config.GetString("regions", String.Empty); | ||
136 | if (!String.IsNullOrEmpty(regions)) | ||
137 | { | ||
138 | _regions = new Regex(@regions, RegexOptions.Compiled | RegexOptions.IgnoreCase); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | scene.CommsManager.HttpServer.AddXmlRPCHandler("concierge_update_welcome", XmlRpcUpdateWelcomeMethod, false); | ||
143 | |||
144 | lock (_syncy) | ||
145 | { | ||
146 | if (!_scenes.Contains(scene)) | ||
147 | { | ||
148 | _scenes.Add(scene); | ||
149 | |||
150 | if (_regions == null || _regions.IsMatch(scene.RegionInfo.RegionName)) | ||
151 | _conciergedScenes.Add(scene); | ||
152 | |||
153 | // subscribe to NewClient events | ||
154 | scene.EventManager.OnNewClient += OnNewClient; | ||
155 | |||
156 | // subscribe to *Chat events | ||
157 | scene.EventManager.OnChatFromWorld += OnChatFromWorld; | ||
158 | if (!_replacingChatModule) | ||
159 | scene.EventManager.OnChatFromClient += OnChatFromClient; | ||
160 | scene.EventManager.OnChatBroadcast += OnChatBroadcast; | ||
161 | |||
162 | // subscribe to agent change events | ||
163 | scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; | ||
164 | scene.EventManager.OnMakeChildAgent += OnMakeChildAgent; | ||
165 | } | ||
166 | } | ||
167 | _log.InfoFormat("[Concierge]: initialized for {0}", scene.RegionInfo.RegionName); | ||
168 | } | ||
169 | |||
170 | public override void PostInitialise() | ||
171 | { | ||
172 | } | ||
173 | |||
174 | public override void Close() | ||
175 | { | ||
176 | } | ||
177 | |||
178 | public override string Name | ||
179 | { | ||
180 | get { return "ConciergeModule"; } | ||
181 | } | ||
182 | |||
183 | public override bool IsSharedModule | ||
184 | { | ||
185 | get { return true; } | ||
186 | } | ||
187 | |||
188 | #endregion | ||
189 | |||
190 | #region ISimChat Members | ||
191 | public override void OnChatBroadcast(Object sender, OSChatMessage c) | ||
192 | { | ||
193 | if (_replacingChatModule) | ||
194 | { | ||
195 | // distribute chat message to each and every avatar in | ||
196 | // the region | ||
197 | base.OnChatBroadcast(sender, c); | ||
198 | } | ||
199 | |||
200 | // TODO: capture logic | ||
201 | return; | ||
202 | } | ||
203 | |||
204 | public override void OnChatFromClient(Object sender, OSChatMessage c) | ||
205 | { | ||
206 | if (_replacingChatModule) | ||
207 | { | ||
208 | // replacing ChatModule: need to redistribute | ||
209 | // ChatFromClient to interested subscribers | ||
210 | c = FixPositionOfChatMessage(c); | ||
211 | |||
212 | Scene scene = (Scene)c.Scene; | ||
213 | scene.EventManager.TriggerOnChatFromClient(sender, c); | ||
214 | |||
215 | if (_conciergedScenes.Contains(c.Scene)) | ||
216 | { | ||
217 | // when we are replacing ChatModule, we treat | ||
218 | // OnChatFromClient like OnChatBroadcast for | ||
219 | // concierged regions, effectively extending the | ||
220 | // range of chat to cover the whole | ||
221 | // region. however, we don't do this for whisper | ||
222 | // (got to have some privacy) | ||
223 | if (c.Type != ChatTypeEnum.Whisper) | ||
224 | { | ||
225 | base.OnChatBroadcast(sender, c); | ||
226 | return; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | // redistribution will be done by base class | ||
231 | base.OnChatFromClient(sender, c); | ||
232 | } | ||
233 | |||
234 | // TODO: capture chat | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | public override void OnChatFromWorld(Object sender, OSChatMessage c) | ||
239 | { | ||
240 | if (_replacingChatModule) | ||
241 | { | ||
242 | if (_conciergedScenes.Contains(c.Scene)) | ||
243 | { | ||
244 | // when we are replacing ChatModule, we treat | ||
245 | // OnChatFromClient like OnChatBroadcast for | ||
246 | // concierged regions, effectively extending the | ||
247 | // range of chat to cover the whole | ||
248 | // region. however, we don't do this for whisper | ||
249 | // (got to have some privacy) | ||
250 | if (c.Type != ChatTypeEnum.Whisper) | ||
251 | { | ||
252 | base.OnChatBroadcast(sender, c); | ||
253 | return; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | base.OnChatFromWorld(sender, c); | ||
258 | } | ||
259 | return; | ||
260 | } | ||
261 | #endregion | ||
262 | |||
263 | |||
264 | public override void OnNewClient(IClientAPI client) | ||
265 | { | ||
266 | client.OnLogout += OnClientLoggedOut; | ||
267 | |||
268 | if (_replacingChatModule) | ||
269 | client.OnChatFromClient += OnChatFromClient; | ||
270 | } | ||
271 | |||
272 | |||
273 | |||
274 | public void OnClientLoggedOut(IClientAPI client) | ||
275 | { | ||
276 | client.OnLogout -= OnClientLoggedOut; | ||
277 | client.OnConnectionClosed -= OnClientLoggedOut; | ||
278 | |||
279 | if (_conciergedScenes.Contains(client.Scene)) | ||
280 | { | ||
281 | _log.DebugFormat("[Concierge]: {0} logs off from {1}", client.Name, client.Scene.RegionInfo.RegionName); | ||
282 | RemoveFromAttendeeList(client.AgentId, client.Name, client.Scene); | ||
283 | AnnounceToAgentsRegion(client.Scene, String.Format(_announceLeaving, client.Name, client.Scene.RegionInfo.RegionName, | ||
284 | _sceneAttendees[client.Scene].Count)); | ||
285 | UpdateBroker(client.Scene); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | |||
290 | public void OnMakeRootAgent(ScenePresence agent) | ||
291 | { | ||
292 | if (_conciergedScenes.Contains(agent.Scene)) | ||
293 | { | ||
294 | _log.DebugFormat("[Concierge]: {0} enters {1}", agent.Name, agent.Scene.RegionInfo.RegionName); | ||
295 | AddToAttendeeList(agent.UUID, agent.Name, agent.Scene); | ||
296 | WelcomeAvatar(agent, agent.Scene); | ||
297 | AnnounceToAgentsRegion(agent.Scene, String.Format(_announceEntering, agent.Name, agent.Scene.RegionInfo.RegionName, | ||
298 | _sceneAttendees[agent.Scene].Count)); | ||
299 | UpdateBroker(agent.Scene); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | |||
304 | public void OnMakeChildAgent(ScenePresence agent) | ||
305 | { | ||
306 | if (_conciergedScenes.Contains(agent.Scene)) | ||
307 | { | ||
308 | _log.DebugFormat("[Concierge]: {0} leaves {1}", agent.Name, agent.Scene.RegionInfo.RegionName); | ||
309 | RemoveFromAttendeeList(agent.UUID, agent.Name, agent.Scene); | ||
310 | AnnounceToAgentsRegion(agent.Scene, String.Format(_announceLeaving, agent.Name, agent.Scene.RegionInfo.RegionName, | ||
311 | _sceneAttendees[agent.Scene].Count)); | ||
312 | UpdateBroker(agent.Scene); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | protected void AddToAttendeeList(UUID agentID, string name, Scene scene) | ||
317 | { | ||
318 | lock (_sceneAttendees) | ||
319 | { | ||
320 | if (!_sceneAttendees.ContainsKey(scene)) | ||
321 | _sceneAttendees[scene] = new List<UUID>(); | ||
322 | |||
323 | List<UUID> attendees = _sceneAttendees[scene]; | ||
324 | if (!attendees.Contains(agentID)) | ||
325 | { | ||
326 | attendees.Add(agentID); | ||
327 | _attendeeNames[agentID] = name; | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | |||
332 | protected void RemoveFromAttendeeList(UUID agentID, String name, IScene scene) | ||
333 | { | ||
334 | lock (_sceneAttendees) | ||
335 | { | ||
336 | if (!_sceneAttendees.ContainsKey(scene)) | ||
337 | { | ||
338 | _log.WarnFormat("[Concierge]: attendee list missing for region {0}", scene.RegionInfo.RegionName); | ||
339 | return; | ||
340 | } | ||
341 | |||
342 | List<UUID> attendees = _sceneAttendees[scene]; | ||
343 | if (!attendees.Contains(agentID)) | ||
344 | { | ||
345 | _log.WarnFormat("[Concierge]: avatar {0} must have sneaked in to region {1} earlier", | ||
346 | name, scene.RegionInfo.RegionName); | ||
347 | return; | ||
348 | } | ||
349 | |||
350 | attendees.Remove(agentID); | ||
351 | _attendeeNames.Remove(agentID); | ||
352 | } | ||
353 | } | ||
354 | |||
355 | protected void UpdateBroker(IScene scene) | ||
356 | { | ||
357 | if (String.IsNullOrEmpty(_brokerURI)) | ||
358 | return; | ||
359 | |||
360 | string uri = String.Format(_brokerURI, scene.RegionInfo.RegionName, scene.RegionInfo.RegionID); | ||
361 | |||
362 | // get attendee list for the scene | ||
363 | List<UUID> attendees; | ||
364 | lock (_sceneAttendees) | ||
365 | { | ||
366 | if (!_sceneAttendees.ContainsKey(scene)) | ||
367 | { | ||
368 | _log.DebugFormat("[Concierge]: attendee list missing for region {0}", scene.RegionInfo.RegionName); | ||
369 | return; | ||
370 | } | ||
371 | |||
372 | attendees = _sceneAttendees[scene]; | ||
373 | } | ||
374 | |||
375 | // create XML sniplet | ||
376 | StringBuilder list = new StringBuilder(); | ||
377 | if (0 == attendees.Count) | ||
378 | { | ||
379 | list.Append(String.Format("<avatars count=\"0\" region_name=\"{0}\" region_uuid=\"{1}\" timestamp=\"{2}\" />", | ||
380 | scene.RegionInfo.RegionName, scene.RegionInfo.RegionID, | ||
381 | DateTime.UtcNow.ToString("s"))); | ||
382 | } | ||
383 | else | ||
384 | { | ||
385 | list.Append(String.Format("<avatars count=\"{0}\" region_name=\"{1}\" region_uuid=\"{2}\" timestamp=\"{3}\">\n", | ||
386 | attendees.Count, scene.RegionInfo.RegionName, | ||
387 | scene.RegionInfo.RegionID, | ||
388 | DateTime.UtcNow.ToString("s"))); | ||
389 | foreach (UUID uuid in attendees) | ||
390 | { | ||
391 | string name = _attendeeNames[uuid]; | ||
392 | list.Append(String.Format(" <avatar name=\"{0}\" uuid=\"{1}\" />\n", name, uuid)); | ||
393 | } | ||
394 | list.Append("</avatars>"); | ||
395 | } | ||
396 | string payload = list.ToString(); | ||
397 | |||
398 | // post via REST to broker | ||
399 | HttpWebRequest updatePost = WebRequest.Create(uri) as HttpWebRequest; | ||
400 | updatePost.Method = "POST"; | ||
401 | updatePost.ContentType = "text/xml"; | ||
402 | updatePost.ContentLength = payload.Length; | ||
403 | updatePost.UserAgent = "OpenSim.Concierge"; | ||
404 | |||
405 | try | ||
406 | { | ||
407 | StreamWriter payloadStream = new StreamWriter(updatePost.GetRequestStream()); | ||
408 | payloadStream.Write(payload); | ||
409 | payloadStream.Close(); | ||
410 | |||
411 | updatePost.BeginGetResponse(UpdateBrokerDone, updatePost); | ||
412 | _log.DebugFormat("[Concierge] async broker POST to {0} started", uri); | ||
413 | } | ||
414 | catch (WebException we) | ||
415 | { | ||
416 | _log.ErrorFormat("[Concierge] async broker POST to {0} failed: {1}", uri, we.Status); | ||
417 | } | ||
418 | } | ||
419 | |||
420 | private void UpdateBrokerDone(IAsyncResult result) | ||
421 | { | ||
422 | HttpWebRequest updatePost = null; | ||
423 | try | ||
424 | { | ||
425 | updatePost = result.AsyncState as HttpWebRequest; | ||
426 | using (HttpWebResponse response = updatePost.EndGetResponse(result) as HttpWebResponse) | ||
427 | { | ||
428 | _log.DebugFormat("[Concierge] broker update: status {0}", response.StatusCode); | ||
429 | } | ||
430 | } | ||
431 | catch (WebException we) | ||
432 | { | ||
433 | string uri = updatePost.RequestUri.OriginalString; | ||
434 | _log.ErrorFormat("[Concierge] broker update to {0} failed with status {1}", uri, we.Status); | ||
435 | if (null != we.Response) | ||
436 | { | ||
437 | using (HttpWebResponse resp = we.Response as HttpWebResponse) | ||
438 | { | ||
439 | _log.ErrorFormat("[Concierge] response from {0} status code: {1}", uri, resp.StatusCode); | ||
440 | _log.ErrorFormat("[Concierge] response from {0} status desc: {1}", uri, resp.StatusDescription); | ||
441 | _log.ErrorFormat("[Concierge] response from {0} server: {1}", uri, resp.Server); | ||
442 | |||
443 | if (resp.ContentLength > 0) | ||
444 | { | ||
445 | StreamReader content = new StreamReader(resp.GetResponseStream()); | ||
446 | _log.ErrorFormat("[Concierge] response from {0} content: {1}", uri, content.ReadToEnd()); | ||
447 | content.Close(); | ||
448 | } | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | } | ||
453 | |||
454 | protected void WelcomeAvatar(ScenePresence agent, Scene scene) | ||
455 | { | ||
456 | // welcome mechanics: check whether we have a welcomes | ||
457 | // directory set and wether there is a region specific | ||
458 | // welcome file there: if yes, send it to the agent | ||
459 | if (!String.IsNullOrEmpty(_welcomes)) | ||
460 | { | ||
461 | string[] welcomes = new string[] { | ||
462 | Path.Combine(_welcomes, agent.Scene.RegionInfo.RegionName), | ||
463 | Path.Combine(_welcomes, "DEFAULT")}; | ||
464 | foreach (string welcome in welcomes) | ||
465 | { | ||
466 | if (File.Exists(welcome)) | ||
467 | { | ||
468 | try | ||
469 | { | ||
470 | string[] welcomeLines = File.ReadAllLines(welcome); | ||
471 | foreach (string l in welcomeLines) | ||
472 | { | ||
473 | AnnounceToAgent(agent, String.Format(l, agent.Name, scene.RegionInfo.RegionName, _whoami)); | ||
474 | } | ||
475 | } | ||
476 | catch (IOException ioe) | ||
477 | { | ||
478 | _log.ErrorFormat("[Concierge]: run into trouble reading welcome file {0} for region {1} for avatar {2}: {3}", | ||
479 | welcome, scene.RegionInfo.RegionName, agent.Name, ioe); | ||
480 | } | ||
481 | catch (FormatException fe) | ||
482 | { | ||
483 | _log.ErrorFormat("[Concierge]: welcome file {0} is malformed: {1}", welcome, fe); | ||
484 | } | ||
485 | } | ||
486 | return; | ||
487 | } | ||
488 | _log.DebugFormat("[Concierge]: no welcome message for region {0}", scene.RegionInfo.RegionName); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | static private Vector3 PosOfGod = new Vector3(128, 128, 9999); | ||
493 | |||
494 | // protected void AnnounceToAgentsRegion(Scene scene, string msg) | ||
495 | // { | ||
496 | // ScenePresence agent = null; | ||
497 | // if ((client.Scene is Scene) && (client.Scene as Scene).TryGetAvatar(client.AgentId, out agent)) | ||
498 | // AnnounceToAgentsRegion(agent, msg); | ||
499 | // else | ||
500 | // _log.DebugFormat("[Concierge]: could not find an agent for client {0}", client.Name); | ||
501 | // } | ||
502 | |||
503 | protected void AnnounceToAgentsRegion(IScene scene, string msg) | ||
504 | { | ||
505 | OSChatMessage c = new OSChatMessage(); | ||
506 | c.Message = msg; | ||
507 | c.Type = ChatTypeEnum.Say; | ||
508 | c.Channel = 0; | ||
509 | c.Position = PosOfGod; | ||
510 | c.From = _whoami; | ||
511 | c.Sender = null; | ||
512 | c.SenderUUID = UUID.Zero; | ||
513 | c.Scene = scene; | ||
514 | |||
515 | if (scene is Scene) | ||
516 | (scene as Scene).EventManager.TriggerOnChatBroadcast(this, c); | ||
517 | } | ||
518 | |||
519 | protected void AnnounceToAgent(ScenePresence agent, string msg) | ||
520 | { | ||
521 | OSChatMessage c = new OSChatMessage(); | ||
522 | c.Message = msg; | ||
523 | c.Type = ChatTypeEnum.Say; | ||
524 | c.Channel = 0; | ||
525 | c.Position = PosOfGod; | ||
526 | c.From = _whoami; | ||
527 | c.Sender = null; | ||
528 | c.SenderUUID = UUID.Zero; | ||
529 | c.Scene = agent.Scene; | ||
530 | |||
531 | agent.ControllingClient.SendChatMessage(msg, (byte) ChatTypeEnum.Say, PosOfGod, _whoami, UUID.Zero, | ||
532 | (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully); | ||
533 | } | ||
534 | |||
535 | private static void checkStringParameters(XmlRpcRequest request, string[] param) | ||
536 | { | ||
537 | Hashtable requestData = (Hashtable) request.Params[0]; | ||
538 | foreach (string p in param) | ||
539 | { | ||
540 | if (!requestData.Contains(p)) | ||
541 | throw new Exception(String.Format("missing string parameter {0}", p)); | ||
542 | if (String.IsNullOrEmpty((string)requestData[p])) | ||
543 | throw new Exception(String.Format("parameter {0} is empty", p)); | ||
544 | } | ||
545 | } | ||
546 | |||
547 | public XmlRpcResponse XmlRpcUpdateWelcomeMethod(XmlRpcRequest request) | ||
548 | { | ||
549 | _log.Info("[Concierge]: processing UpdateWelcome request"); | ||
550 | XmlRpcResponse response = new XmlRpcResponse(); | ||
551 | Hashtable responseData = new Hashtable(); | ||
552 | |||
553 | try | ||
554 | { | ||
555 | Hashtable requestData = (Hashtable)request.Params[0]; | ||
556 | checkStringParameters(request, new string[] { "password", "region", "welcome" }); | ||
557 | |||
558 | // check password | ||
559 | if (!String.IsNullOrEmpty(_xmlRpcPassword) && | ||
560 | (string)requestData["password"] != _xmlRpcPassword) throw new Exception("wrong password"); | ||
561 | |||
562 | if (String.IsNullOrEmpty(_welcomes)) | ||
563 | throw new Exception("welcome templates are not enabled, ask your OpenSim operator to set the \"welcomes\" option in the [Concierge] section of OpenSim.ini"); | ||
564 | |||
565 | string msg = (string)requestData["welcome"]; | ||
566 | if (String.IsNullOrEmpty(msg)) | ||
567 | throw new Exception("empty parameter \"welcome\""); | ||
568 | |||
569 | string regionName = (string)requestData["region"]; | ||
570 | IScene scene = _scenes.Find(delegate(IScene s) { return s.RegionInfo.RegionName == regionName; }); | ||
571 | if (scene == null) | ||
572 | throw new Exception(String.Format("unknown region \"{0}\"", regionName)); | ||
573 | |||
574 | if (!_conciergedScenes.Contains(scene)) | ||
575 | throw new Exception(String.Format("region \"{0}\" is not a concierged region.", regionName)); | ||
576 | |||
577 | string welcome = Path.Combine(_welcomes, regionName); | ||
578 | if (File.Exists(welcome)) | ||
579 | { | ||
580 | _log.InfoFormat("[Concierge]: UpdateWelcome: updating existing template \"{0}\"", welcome); | ||
581 | string welcomeBackup = String.Format("{0}~", welcome); | ||
582 | if (File.Exists(welcomeBackup)) | ||
583 | File.Delete(welcomeBackup); | ||
584 | File.Move(welcome, welcomeBackup); | ||
585 | } | ||
586 | File.WriteAllText(welcome, msg); | ||
587 | |||
588 | responseData["success"] = "true"; | ||
589 | response.Value = responseData; | ||
590 | } | ||
591 | catch (Exception e) | ||
592 | { | ||
593 | _log.InfoFormat("[Concierge]: UpdateWelcome failed: {0}", e.Message); | ||
594 | |||
595 | responseData["success"] = "false"; | ||
596 | responseData["error"] = e.Message; | ||
597 | |||
598 | response.Value = responseData; | ||
599 | } | ||
600 | _log.Debug("[Concierge]: done processing UpdateWelcome request"); | ||
601 | return response; | ||
602 | } | ||
603 | } | ||
604 | } | ||
diff --git a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py new file mode 100755 index 0000000..1c088fb --- /dev/null +++ b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeServer.py | |||
@@ -0,0 +1,130 @@ | |||
1 | #!/usr/bin/env python | ||
2 | # -*- encoding: utf-8 -*- | ||
3 | # | ||
4 | # Copyright (c) Contributors, http://opensimulator.org/ | ||
5 | # See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
6 | # | ||
7 | # Redistribution and use in source and binary forms, with or without | ||
8 | # modification, are permitted provided that the following conditions are met: | ||
9 | # * Redistributions of source code must retain the above copyright | ||
10 | # notice, this list of conditions and the following disclaimer. | ||
11 | # * Redistributions in binary form must reproduce the above copyright | ||
12 | # notice, this list of conditions and the following disclaimer in the | ||
13 | # documentation and/or other materials provided with the distribution. | ||
14 | # * Neither the name of the OpenSim Project nor the | ||
15 | # names of its contributors may be used to endorse or promote products | ||
16 | # derived from this software without specific prior written permission. | ||
17 | # | ||
18 | # THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
19 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
20 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
21 | # DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
22 | # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
23 | # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
24 | # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
25 | # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
27 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | # | ||
29 | |||
30 | import logging | ||
31 | import BaseHTTPServer | ||
32 | import optparse | ||
33 | import xml.etree.ElementTree as ET | ||
34 | import xml.parsers.expat | ||
35 | |||
36 | |||
37 | # enable debug level logging | ||
38 | logging.basicConfig(level = logging.DEBUG, | ||
39 | format='%(asctime)s %(levelname)s %(message)s') | ||
40 | |||
41 | options = None | ||
42 | |||
43 | # subclassed HTTPRequestHandler | ||
44 | class ConciergeHandler(BaseHTTPServer.BaseHTTPRequestHandler): | ||
45 | def logRequest(self): | ||
46 | logging.info('[ConciergeHandler] %(command)s request: %(host)s:%(port)d --- %(path)s', | ||
47 | dict(command = self.command, | ||
48 | host = self.client_address[0], | ||
49 | port = self.client_address[1], | ||
50 | path = self.path)) | ||
51 | |||
52 | def logResponse(self, status): | ||
53 | logging.info('[ConciergeHandler] %(command)s returned %(status)d', | ||
54 | dict(command = self.command, | ||
55 | status = status)) | ||
56 | |||
57 | |||
58 | def do_HEAD(self): | ||
59 | self.logRequest() | ||
60 | |||
61 | self.send_response(200) | ||
62 | self.send_header('Content-type', 'text/html') | ||
63 | self.end_headers() | ||
64 | |||
65 | self.logResponse(200) | ||
66 | |||
67 | def dumpXml(self, xml): | ||
68 | logging.debug('[ConciergeHandler] %s', xml.tag) | ||
69 | for attr in xml.attrib: | ||
70 | logging.debug('[ConciergeHandler] %s [%s] %s', xml.tag, attr, xml.attrib[attr]) | ||
71 | for kid in xml.getchildren(): | ||
72 | self.dumpXml(kid) | ||
73 | |||
74 | def do_POST(self): | ||
75 | self.logRequest() | ||
76 | hdrs = {} | ||
77 | for hdr in self.headers.headers: | ||
78 | logging.debug('[ConciergeHandler] POST: header: %s', hdr.rstrip()) | ||
79 | |||
80 | length = int(self.headers.getheader('Content-Length')) | ||
81 | content = self.rfile.read(length) | ||
82 | self.rfile.close() | ||
83 | |||
84 | logging.debug('[ConciergeHandler] POST: content: %s', content) | ||
85 | try: | ||
86 | postXml = ET.fromstring(content) | ||
87 | self.dumpXml(postXml) | ||
88 | except xml.parsers.expat.ExpatError, xmlError: | ||
89 | logging.error('[ConciergeHandler] POST illformed:%s', xmlError) | ||
90 | self.send_response(500) | ||
91 | return | ||
92 | |||
93 | if not options.fail: | ||
94 | self.send_response(200) | ||
95 | self.send_header('Content-Type', 'text/html') | ||
96 | self.send_header('Content-Length', len('<success/>')) | ||
97 | self.end_headers() | ||
98 | self.logResponse(200) | ||
99 | self.wfile.write('<success/>') | ||
100 | self.wfile.close() | ||
101 | else: | ||
102 | self.send_response(500) | ||
103 | self.send_header('Content-Type', 'text/html') | ||
104 | self.send_header('Content-Length', len('<error>gotcha!</error>')) | ||
105 | self.end_headers() | ||
106 | self.wfile.write('<error>gotcha!</error>') | ||
107 | self.wfile.close() | ||
108 | |||
109 | self.logResponse(500) | ||
110 | |||
111 | def log_request(code, size): | ||
112 | pass | ||
113 | |||
114 | if __name__ == '__main__': | ||
115 | |||
116 | logging.info('[ConciergeServer] Concierge Broker Test Server starting') | ||
117 | |||
118 | parser = optparse.OptionParser() | ||
119 | parser.add_option('-p', '--port', dest = 'port', help = 'port to listen on', metavar = 'PORT') | ||
120 | parser.add_option('-f', '--fail', dest = 'fail', action = 'store_true', help = 'always fail POST requests') | ||
121 | |||
122 | (options, args) = parser.parse_args() | ||
123 | |||
124 | httpServer = BaseHTTPServer.HTTPServer(('', 8080), ConciergeHandler) | ||
125 | try: | ||
126 | httpServer.serve_forever() | ||
127 | except KeyboardInterrupt: | ||
128 | logging.info('[ConciergeServer] terminating') | ||
129 | |||
130 | httpServer.server_close() | ||