opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.Text.RegularExpressions;
32 using log4net;
33 using Nini.Config;
34 using OpenSim.Framework;
35 using OpenSim.Region.Framework.Interfaces;
36 using OpenSim.Region.Framework.Scenes;
37  
38 namespace OpenSim.Region.OptionalModules.Avatar.Chat
39 {
40 // An instance of this class exists for every active region
41  
42 internal class RegionState
43 {
44 private static readonly ILog m_log =
45 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46  
47 // This computation is not the real region center if the region is larger than 256.
48 // This computation isn't fixed because there is not a handle back to the region.
49 private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 20);
50 private const int DEBUG_CHANNEL = 2147483647;
51  
52 private static int _idk_ = 0;
53  
54 // Runtime variables; these values are assigned when the
55 // IrcState is created and remain constant thereafter.
56  
57 internal string Region = String.Empty;
58 internal string Host = String.Empty;
59 internal string LocX = String.Empty;
60 internal string LocY = String.Empty;
61 internal string IDK = String.Empty;
62  
63 // System values - used only be the IRC classes themselves
64  
65 internal ChannelState cs = null; // associated IRC configuration
66 internal Scene scene = null; // associated scene
67 internal IConfig config = null; // configuration file reference
68 internal bool enabled = true;
69  
70 //AgentAlert
71 internal bool showAlert = false;
72 internal string alertMessage = String.Empty;
73 internal IDialogModule dialogModule = null;
74  
75 // This list is used to keep track of who is here, and by
76 // implication, who is not.
77  
78 internal List<IClientAPI> clients = new List<IClientAPI>();
79  
80 // Setup runtime variable values
81  
82 public RegionState(Scene p_scene, IConfig p_config)
83 {
84 scene = p_scene;
85 config = p_config;
86  
87 Region = scene.RegionInfo.RegionName;
88 Host = scene.RegionInfo.ExternalHostName;
89 LocX = Convert.ToString(scene.RegionInfo.RegionLocX);
90 LocY = Convert.ToString(scene.RegionInfo.RegionLocY);
91 IDK = Convert.ToString(_idk_++);
92  
93 showAlert = config.GetBoolean("alert_show", false);
94 string alertServerInfo = String.Empty;
95  
96 if (showAlert)
97 {
98 bool showAlertServerInfo = config.GetBoolean("alert_show_serverinfo", true);
99  
100 if (showAlertServerInfo)
101 alertServerInfo = String.Format("\nServer: {0}\nPort: {1}\nChannel: {2}\n\n",
102 config.GetString("server", ""), config.GetString("port", ""), config.GetString("channel", ""));
103  
104 string alertPreMessage = config.GetString("alert_msg_pre", "This region is linked to Irc.");
105 string alertPostMessage = config.GetString("alert_msg_post", "Everything you say in public chat can be listened.");
106  
107 alertMessage = String.Format("{0}\n{1}{2}", alertPreMessage, alertServerInfo, alertPostMessage);
108  
109 dialogModule = scene.RequestModuleInterface<IDialogModule>();
110 }
111  
112 // OpenChannel conditionally establishes a connection to the
113 // IRC server. The request will either succeed, or it will
114 // throw an exception.
115  
116 ChannelState.OpenChannel(this, config);
117  
118 // Connect channel to world events
119  
120 scene.EventManager.OnChatFromWorld += OnSimChat;
121 scene.EventManager.OnChatFromClient += OnSimChat;
122 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
123 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
124  
125 m_log.InfoFormat("[IRC-Region {0}] Initialization complete", Region);
126  
127 }
128  
129 // Auto cleanup when abandoned
130  
131 ~RegionState()
132 {
133 if (cs != null)
134 cs.RemoveRegion(this);
135 }
136  
137 // Called by PostInitialize after all regions have been created
138  
139 public void Open()
140 {
141 cs.Open(this);
142 enabled = true;
143 }
144  
145 // Called by IRCBridgeModule.Close immediately prior to unload
146 // of the module for this region. This happens when the region
147 // is being removed or the server is terminating. The IRC
148 // BridgeModule will remove the region from the region list
149 // when control returns.
150  
151 public void Close()
152 {
153 enabled = false;
154 cs.Close(this);
155 }
156  
157 // The agent has disconnected, cleanup associated resources
158  
159 private void OnClientLoggedOut(IClientAPI client)
160 {
161 try
162 {
163 if (clients.Contains(client))
164 {
165 if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
166 {
167 m_log.InfoFormat("[IRC-Region {0}]: {1} has left", Region, client.Name);
168 //Check if this person is excluded from IRC
169 if (!cs.ExcludeList.Contains(client.Name.ToLower()))
170 {
171 cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", client.Name));
172 }
173 }
174 client.OnLogout -= OnClientLoggedOut;
175 client.OnConnectionClosed -= OnClientLoggedOut;
176 clients.Remove(client);
177 }
178 }
179 catch (Exception ex)
180 {
181 m_log.ErrorFormat("[IRC-Region {0}]: ClientLoggedOut exception: {1}", Region, ex.Message);
182 m_log.Debug(ex);
183 }
184 }
185  
186 // This event indicates that the agent has left the building. We should treat that the same
187 // as if the agent has logged out (we don't want cross-region noise - or do we?)
188  
189 private void OnMakeChildAgent(ScenePresence presence)
190 {
191  
192 IClientAPI client = presence.ControllingClient;
193  
194 try
195 {
196 if (clients.Contains(client))
197 {
198 if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
199 {
200 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
201 m_log.DebugFormat("[IRC-Region {0}] {1} has left", Region, clientName);
202 cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", clientName));
203 }
204 client.OnLogout -= OnClientLoggedOut;
205 client.OnConnectionClosed -= OnClientLoggedOut;
206 clients.Remove(client);
207 }
208 }
209 catch (Exception ex)
210 {
211 m_log.ErrorFormat("[IRC-Region {0}]: MakeChildAgent exception: {1}", Region, ex.Message);
212 m_log.Debug(ex);
213 }
214  
215 }
216  
217 // An agent has entered the region (from another region). Add the client to the locally
218 // known clients list
219  
220 private void OnMakeRootAgent(ScenePresence presence)
221 {
222 IClientAPI client = presence.ControllingClient;
223  
224 try
225 {
226 if (!clients.Contains(client))
227 {
228 client.OnLogout += OnClientLoggedOut;
229 client.OnConnectionClosed += OnClientLoggedOut;
230 clients.Add(client);
231 if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
232 {
233 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
234 m_log.DebugFormat("[IRC-Region {0}] {1} has arrived", Region, clientName);
235 //Check if this person is excluded from IRC
236 if (!cs.ExcludeList.Contains(clientName.ToLower()))
237 {
238 cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has arrived", clientName));
239 }
240 }
241 }
242  
243 if (dialogModule != null && showAlert)
244 dialogModule.SendAlertToUser(client, alertMessage, true);
245 }
246 catch (Exception ex)
247 {
248 m_log.ErrorFormat("[IRC-Region {0}]: MakeRootAgent exception: {1}", Region, ex.Message);
249 m_log.Debug(ex);
250 }
251 }
252  
253 // This handler detects chat events int he virtual world.
254 public void OnSimChat(Object sender, OSChatMessage msg)
255 {
256  
257 // early return if this comes from the IRC forwarder
258  
259 if (cs.irc.Equals(sender)) return;
260  
261 // early return if nothing to forward
262  
263 if (msg.Message.Length == 0) return;
264  
265 // check for commands coming from avatars or in-world
266 // object (if commands are enabled)
267  
268 if (cs.CommandsEnabled && msg.Channel == cs.CommandChannel)
269 {
270  
271 m_log.DebugFormat("[IRC-Region {0}] command on channel {1}: {2}", Region, msg.Channel, msg.Message);
272  
273 string[] messages = msg.Message.Split(' ');
274 string command = messages[0].ToLower();
275  
276 try
277 {
278 switch (command)
279 {
280  
281 // These commands potentially require a change in the
282 // underlying ChannelState.
283  
284 case "server":
285 cs.Close(this);
286 cs = cs.UpdateServer(this, messages[1]);
287 cs.Open(this);
288 break;
289 case "port":
290 cs.Close(this);
291 cs = cs.UpdatePort(this, messages[1]);
292 cs.Open(this);
293 break;
294 case "channel":
295 cs.Close(this);
296 cs = cs.UpdateChannel(this, messages[1]);
297 cs.Open(this);
298 break;
299 case "nick":
300 cs.Close(this);
301 cs = cs.UpdateNickname(this, messages[1]);
302 cs.Open(this);
303 break;
304  
305 // These may also (but are less likely) to require a
306 // change in ChannelState.
307  
308 case "client-reporting":
309 cs = cs.UpdateClientReporting(this, messages[1]);
310 break;
311 case "in-channel":
312 cs = cs.UpdateRelayIn(this, messages[1]);
313 break;
314 case "out-channel":
315 cs = cs.UpdateRelayOut(this, messages[1]);
316 break;
317  
318 // These are all taken to be temporary changes in state
319 // so the underlying connector remains intact. But note
320 // that with regions sharing a connector, there could
321 // be interference.
322  
323 case "close":
324 enabled = false;
325 cs.Close(this);
326 break;
327  
328 case "connect":
329 enabled = true;
330 cs.Open(this);
331 break;
332  
333 case "reconnect":
334 enabled = true;
335 cs.Close(this);
336 cs.Open(this);
337 break;
338  
339 // This one is harmless as far as we can judge from here.
340 // If it is not, then the complaints will eventually make
341 // that evident.
342  
343 default:
344 m_log.DebugFormat("[IRC-Region {0}] Forwarding unrecognized command to IRC : {1}",
345 Region, msg.Message);
346 cs.irc.Send(msg.Message);
347 break;
348 }
349 }
350 catch (Exception ex)
351 {
352 m_log.WarnFormat("[IRC-Region {0}] error processing in-world command channel input: {1}",
353 Region, ex.Message);
354 m_log.Debug(ex);
355 }
356  
357 return;
358  
359 }
360  
361 // The command channel remains enabled, even if we have otherwise disabled the IRC
362 // interface.
363  
364 if (!enabled)
365 return;
366  
367 // drop messages unless they are on a valid in-world
368 // channel as configured in the ChannelState
369  
370 if (!cs.ValidInWorldChannels.Contains(msg.Channel))
371 {
372 m_log.DebugFormat("[IRC-Region {0}] dropping message {1} on channel {2}", Region, msg, msg.Channel);
373 return;
374 }
375  
376 ScenePresence avatar = null;
377 string fromName = msg.From;
378  
379 if (msg.Sender != null)
380 {
381 avatar = scene.GetScenePresence(msg.Sender.AgentId);
382 if (avatar != null) fromName = avatar.Name;
383 }
384  
385 if (!cs.irc.Connected)
386 {
387 m_log.WarnFormat("[IRC-Region {0}] IRCConnector not connected: dropping message from {1}", Region, fromName);
388 return;
389 }
390  
391 m_log.DebugFormat("[IRC-Region {0}] heard on channel {1} : {2}", Region, msg.Channel, msg.Message);
392  
393 if (null != avatar && cs.RelayChat && (msg.Channel == 0 || msg.Channel == DEBUG_CHANNEL))
394 {
395 string txt = msg.Message;
396 if (txt.StartsWith("/me "))
397 txt = String.Format("{0} {1}", fromName, msg.Message.Substring(4));
398  
399 cs.irc.PrivMsg(cs.PrivateMessageFormat, fromName, Region, txt);
400 return;
401 }
402  
403 if (null == avatar && cs.RelayPrivateChannels && null != cs.AccessPassword &&
404 msg.Channel == cs.RelayChannelOut)
405 {
406 Match m = cs.AccessPasswordRegex.Match(msg.Message);
407 if (null != m)
408 {
409 m_log.DebugFormat("[IRC] relaying message from {0}: {1}", m.Groups["avatar"].ToString(),
410 m.Groups["message"].ToString());
411 cs.irc.PrivMsg(cs.PrivateMessageFormat, m.Groups["avatar"].ToString(),
412 scene.RegionInfo.RegionName, m.Groups["message"].ToString());
413 }
414 }
415 }
416  
417 // This method gives the region an opportunity to interfere with
418 // message delivery. For now we just enforce the enable/disable
419 // flag.
420  
421 internal void OSChat(Object irc, OSChatMessage msg)
422 {
423 if (enabled)
424 {
425 // m_log.DebugFormat("[IRC-OSCHAT] Region {0} being sent message", region.Region);
426 msg.Scene = scene;
427 scene.EventManager.TriggerOnChatBroadcast(irc, msg);
428 }
429 }
430  
431 // This supports any local message traffic that might be needed in
432 // support of command processing. At present there is none.
433  
434 internal void LocalChat(string msg)
435 {
436 if (enabled)
437 {
438 OSChatMessage osm = new OSChatMessage();
439 osm.From = "IRC Agent";
440 osm.Message = msg;
441 osm.Type = ChatTypeEnum.Region;
442 osm.Position = CenterOfRegion;
443 osm.Sender = null;
444 osm.SenderUUID = OpenMetaverse.UUID.Zero; // Hmph! Still?
445 osm.Channel = 0;
446 OSChat(this, osm);
447 }
448 }
449  
450 }
451  
452 }