clockwerk-opensim-stable – Blame information for rev 1

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