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  
41 // An instance of this class exists for each unique combination of
42 // IRC chat interface characteristics, as determined by the supplied
43 // configuration file.
44  
45 internal class ChannelState
46 {
47  
48 private static readonly ILog m_log =
49 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50  
51 private static Regex arg = new Regex(@"\[[^\[\]]*\]");
52 private static int _idk_ = 0;
53 private static int DEBUG_CHANNEL = 2147483647;
54  
55 // These are the IRC Connector configurable parameters with hard-wired
56 // default values (retained for compatability).
57  
58 internal string Server = null;
59 internal string Password = null;
60 internal string IrcChannel = null;
61 internal string BaseNickname = "OSimBot";
62 internal uint Port = 6667;
63 internal string User = null;
64  
65 internal bool ClientReporting = true;
66 internal bool RelayChat = true;
67 internal bool RelayPrivateChannels = false;
68 internal int RelayChannel = 1;
69 internal List<int> ValidInWorldChannels = new List<int>();
70  
71 // Connector agnostic parameters. These values are NOT shared with the
72 // connector and do not differentiate at an IRC level
73  
74 internal string PrivateMessageFormat = "PRIVMSG {0} :<{2}> {1} {3}";
75 internal string NoticeMessageFormat = "PRIVMSG {0} :<{2}> {3}";
76 internal int RelayChannelOut = -1;
77 internal bool RandomizeNickname = true;
78 internal bool CommandsEnabled = false;
79 internal int CommandChannel = -1;
80 internal int ConnectDelay = 10;
81 internal int PingDelay = 15;
82 internal string DefaultZone = "Sim";
83  
84 internal string _accessPassword = String.Empty;
85 internal Regex AccessPasswordRegex = null;
86 internal List<string> ExcludeList = new List<string>();
87 internal string AccessPassword
88 {
89 get { return _accessPassword; }
90 set
91 {
92 _accessPassword = value;
93 AccessPasswordRegex = new Regex(String.Format(@"^{0},\s*(?<avatar>[^,]+),\s*(?<message>.+)$", _accessPassword),
94 RegexOptions.Compiled);
95 }
96 }
97  
98  
99  
100 // IRC connector reference
101  
102 internal IRCConnector irc = null;
103  
104 internal int idn = _idk_++;
105  
106 // List of regions dependent upon this connection
107  
108 internal List<RegionState> clientregions = new List<RegionState>();
109  
110 // Needed by OpenChannel
111  
112 internal ChannelState()
113 {
114 }
115  
116 // This constructor is used by the Update* methods. A copy of the
117 // existing channel state is created, and distinguishing characteristics
118 // are copied across.
119  
120 internal ChannelState(ChannelState model)
121 {
122 Server = model.Server;
123 Password = model.Password;
124 IrcChannel = model.IrcChannel;
125 Port = model.Port;
126 BaseNickname = model.BaseNickname;
127 RandomizeNickname = model.RandomizeNickname;
128 User = model.User;
129 CommandsEnabled = model.CommandsEnabled;
130 CommandChannel = model.CommandChannel;
131 RelayChat = model.RelayChat;
132 RelayPrivateChannels = model.RelayPrivateChannels;
133 RelayChannelOut = model.RelayChannelOut;
134 RelayChannel = model.RelayChannel;
135 ValidInWorldChannels = model.ValidInWorldChannels;
136 PrivateMessageFormat = model.PrivateMessageFormat;
137 NoticeMessageFormat = model.NoticeMessageFormat;
138 ClientReporting = model.ClientReporting;
139 AccessPassword = model.AccessPassword;
140 DefaultZone = model.DefaultZone;
141 ConnectDelay = model.ConnectDelay;
142 PingDelay = model.PingDelay;
143 }
144  
145 // Read the configuration file, performing variable substitution and any
146 // necessary aliasing. See accompanying documentation for how this works.
147 // If you don't need variables, then this works exactly as before.
148 // If either channel or server are not specified, the request fails.
149  
150 internal static void OpenChannel(RegionState rs, IConfig config)
151 {
152  
153 // Create a new instance of a channel. This may not actually
154 // get used if an equivalent channel already exists.
155  
156 ChannelState cs = new ChannelState();
157  
158 // Read in the configuration file and filter everything for variable
159 // subsititution.
160  
161 m_log.DebugFormat("[IRC-Channel-{0}] Initial request by Region {1} to connect to IRC", cs.idn, rs.Region);
162  
163 cs.Server = Substitute(rs, config.GetString("server", null));
164 m_log.DebugFormat("[IRC-Channel-{0}] Server : <{1}>", cs.idn, cs.Server);
165 cs.Password = Substitute(rs, config.GetString("password", null));
166 // probably not a good idea to put a password in the log file
167 cs.User = Substitute(rs, config.GetString("user", null));
168 cs.IrcChannel = Substitute(rs, config.GetString("channel", null));
169 m_log.DebugFormat("[IRC-Channel-{0}] IrcChannel : <{1}>", cs.idn, cs.IrcChannel);
170 cs.Port = Convert.ToUInt32(Substitute(rs, config.GetString("port", Convert.ToString(cs.Port))));
171 m_log.DebugFormat("[IRC-Channel-{0}] Port : <{1}>", cs.idn, cs.Port);
172 cs.BaseNickname = Substitute(rs, config.GetString("nick", cs.BaseNickname));
173 m_log.DebugFormat("[IRC-Channel-{0}] BaseNickname : <{1}>", cs.idn, cs.BaseNickname);
174 cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("randomize_nick", Convert.ToString(cs.RandomizeNickname))));
175 m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname);
176 cs.RandomizeNickname = Convert.ToBoolean(Substitute(rs, config.GetString("nicknum", Convert.ToString(cs.RandomizeNickname))));
177 m_log.DebugFormat("[IRC-Channel-{0}] RandomizeNickname : <{1}>", cs.idn, cs.RandomizeNickname);
178 cs.User = Substitute(rs, config.GetString("username", cs.User));
179 m_log.DebugFormat("[IRC-Channel-{0}] User : <{1}>", cs.idn, cs.User);
180 cs.CommandsEnabled = Convert.ToBoolean(Substitute(rs, config.GetString("commands_enabled", Convert.ToString(cs.CommandsEnabled))));
181 m_log.DebugFormat("[IRC-Channel-{0}] CommandsEnabled : <{1}>", cs.idn, cs.CommandsEnabled);
182 cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("commandchannel", Convert.ToString(cs.CommandChannel))));
183 m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel);
184 cs.CommandChannel = Convert.ToInt32(Substitute(rs, config.GetString("command_channel", Convert.ToString(cs.CommandChannel))));
185 m_log.DebugFormat("[IRC-Channel-{0}] CommandChannel : <{1}>", cs.idn, cs.CommandChannel);
186 cs.RelayChat = Convert.ToBoolean(Substitute(rs, config.GetString("relay_chat", Convert.ToString(cs.RelayChat))));
187 m_log.DebugFormat("[IRC-Channel-{0}] RelayChat : <{1}>", cs.idn, cs.RelayChat);
188 cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("relay_private_channels", Convert.ToString(cs.RelayPrivateChannels))));
189 m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels);
190 cs.RelayPrivateChannels = Convert.ToBoolean(Substitute(rs, config.GetString("useworldcomm", Convert.ToString(cs.RelayPrivateChannels))));
191 m_log.DebugFormat("[IRC-Channel-{0}] RelayPrivateChannels : <{1}>", cs.idn, cs.RelayPrivateChannels);
192 cs.RelayChannelOut = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_out", Convert.ToString(cs.RelayChannelOut))));
193 m_log.DebugFormat("[IRC-Channel-{0}] RelayChannelOut : <{1}>", cs.idn, cs.RelayChannelOut);
194 cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("relay_private_channel_in", Convert.ToString(cs.RelayChannel))));
195 m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel);
196 cs.RelayChannel = Convert.ToInt32(Substitute(rs, config.GetString("inchannel", Convert.ToString(cs.RelayChannel))));
197 m_log.DebugFormat("[IRC-Channel-{0}] RelayChannel : <{1}>", cs.idn, cs.RelayChannel);
198 cs.PrivateMessageFormat = Substitute(rs, config.GetString("msgformat", cs.PrivateMessageFormat));
199 m_log.DebugFormat("[IRC-Channel-{0}] PrivateMessageFormat : <{1}>", cs.idn, cs.PrivateMessageFormat);
200 cs.NoticeMessageFormat = Substitute(rs, config.GetString("noticeformat", cs.NoticeMessageFormat));
201 m_log.DebugFormat("[IRC-Channel-{0}] NoticeMessageFormat : <{1}>", cs.idn, cs.NoticeMessageFormat);
202 cs.ClientReporting = Convert.ToInt32(Substitute(rs, config.GetString("verbosity", cs.ClientReporting ? "1" : "0"))) > 0;
203 m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting);
204 cs.ClientReporting = Convert.ToBoolean(Substitute(rs, config.GetString("report_clients", Convert.ToString(cs.ClientReporting))));
205 m_log.DebugFormat("[IRC-Channel-{0}] ClientReporting : <{1}>", cs.idn, cs.ClientReporting);
206 cs.DefaultZone = Substitute(rs, config.GetString("fallback_region", cs.DefaultZone));
207 m_log.DebugFormat("[IRC-Channel-{0}] DefaultZone : <{1}>", cs.idn, cs.DefaultZone);
208 cs.ConnectDelay = Convert.ToInt32(Substitute(rs, config.GetString("connect_delay", Convert.ToString(cs.ConnectDelay))));
209 m_log.DebugFormat("[IRC-Channel-{0}] ConnectDelay : <{1}>", cs.idn, cs.ConnectDelay);
210 cs.PingDelay = Convert.ToInt32(Substitute(rs, config.GetString("ping_delay", Convert.ToString(cs.PingDelay))));
211 m_log.DebugFormat("[IRC-Channel-{0}] PingDelay : <{1}>", cs.idn, cs.PingDelay);
212 cs.AccessPassword = Substitute(rs, config.GetString("access_password", cs.AccessPassword));
213 m_log.DebugFormat("[IRC-Channel-{0}] AccessPassword : <{1}>", cs.idn, cs.AccessPassword);
214 string[] excludes = config.GetString("exclude_list", "").Trim().Split(new Char[] { ',' });
215 cs.ExcludeList = new List<string>(excludes.Length);
216 foreach (string name in excludes)
217 {
218 cs.ExcludeList.Add(name.Trim().ToLower());
219 }
220  
221 // Fail if fundamental information is still missing
222  
223 if (cs.Server == null)
224 throw new Exception(String.Format("[IRC-Channel-{0}] Invalid configuration for region {1}: server missing", cs.idn, rs.Region));
225 else if (cs.IrcChannel == null)
226 throw new Exception(String.Format("[IRC-Channel-{0}] Invalid configuration for region {1}: channel missing", cs.idn, rs.Region));
227 else if (cs.BaseNickname == null)
228 throw new Exception(String.Format("[IRC-Channel-{0}] Invalid configuration for region {1}: nick missing", cs.idn, rs.Region));
229 else if (cs.User == null)
230 throw new Exception(String.Format("[IRC-Channel-{0}] Invalid configuration for region {1}: user missing", cs.idn, rs.Region));
231  
232 m_log.InfoFormat("[IRC-Channel-{0}] Configuration for Region {1} is valid", cs.idn, rs.Region);
233 m_log.InfoFormat("[IRC-Channel-{0}] Server = {1}", cs.idn, cs.Server);
234 m_log.InfoFormat("[IRC-Channel-{0}] Channel = {1}", cs.idn, cs.IrcChannel);
235 m_log.InfoFormat("[IRC-Channel-{0}] Port = {1}", cs.idn, cs.Port);
236 m_log.InfoFormat("[IRC-Channel-{0}] Nickname = {1}", cs.idn, cs.BaseNickname);
237 m_log.InfoFormat("[IRC-Channel-{0}] User = {1}", cs.idn, cs.User);
238  
239 // Set the channel state for this region
240  
241 if (cs.RelayChat)
242 {
243 cs.ValidInWorldChannels.Add(0);
244 cs.ValidInWorldChannels.Add(DEBUG_CHANNEL);
245 }
246  
247 if (cs.RelayPrivateChannels)
248 cs.ValidInWorldChannels.Add(cs.RelayChannelOut);
249  
250 rs.cs = Integrate(rs, cs);
251  
252 }
253  
254 // An initialized channel state instance is passed in. If an identical
255 // channel state instance already exists, then the existing instance
256 // is used to replace the supplied value.
257 // If the instance matches with respect to IRC, then the underlying
258 // IRCConnector is assigned to the supplied channel state and the
259 // updated value is returned.
260 // If there is no match, then the supplied instance is completed by
261 // creating and assigning an instance of an IRC connector.
262  
263 private static ChannelState Integrate(RegionState rs, ChannelState p_cs)
264 {
265  
266 ChannelState cs = p_cs;
267  
268 // Check to see if we have an existing server/channel setup that can be used
269 // In the absence of variable substitution this will always resolve to the
270 // same ChannelState instance, and the table will only contains a single
271 // entry, so the performance considerations for the existing behavior are
272 // zero. Only the IRC connector is shared, the ChannelState still contains
273 // values that, while independent of the IRC connetion, do still distinguish
274 // this region's behavior.
275  
276 lock (IRCBridgeModule.m_channels)
277 {
278  
279 foreach (ChannelState xcs in IRCBridgeModule.m_channels)
280 {
281 if (cs.IsAPerfectMatchFor(xcs))
282 {
283 m_log.DebugFormat("[IRC-Channel-{0}] Channel state matched", cs.idn);
284 cs = xcs;
285 break;
286 }
287 if (cs.IsAConnectionMatchFor(xcs))
288 {
289 m_log.DebugFormat("[IRC-Channel-{0}] Channel matched", cs.idn);
290 cs.irc = xcs.irc;
291 break;
292 }
293 }
294  
295 }
296  
297 // No entry was found, so this is going to be a new entry.
298  
299 if (cs.irc == null)
300 {
301  
302 m_log.DebugFormat("[IRC-Channel-{0}] New channel required", cs.idn);
303  
304 if ((cs.irc = new IRCConnector(cs)) != null)
305 {
306  
307 IRCBridgeModule.m_channels.Add(cs);
308  
309 m_log.InfoFormat("[IRC-Channel-{0}] New channel initialized for {1}, nick: {2}, commands {3}, private channels {4}",
310 cs.idn, rs.Region, cs.DefaultZone,
311 cs.CommandsEnabled ? "enabled" : "not enabled",
312 cs.RelayPrivateChannels ? "relayed" : "not relayed");
313 }
314 else
315 {
316 string txt = String.Format("[IRC-Channel-{0}] Region {1} failed to connect to channel {2} on server {3}:{4}",
317 cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port);
318 m_log.Error(txt);
319 throw new Exception(txt);
320 }
321 }
322 else
323 {
324 m_log.InfoFormat("[IRC-Channel-{0}] Region {1} reusing existing connection to channel {2} on server {3}:{4}",
325 cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port);
326 }
327  
328 m_log.InfoFormat("[IRC-Channel-{0}] Region {1} associated with channel {2} on server {3}:{4}",
329 cs.idn, rs.Region, cs.IrcChannel, cs.Server, cs.Port);
330  
331 // We're finally ready to commit ourselves
332  
333  
334 return cs;
335  
336 }
337  
338 // These routines allow differentiating changes to
339 // the underlying channel state. If necessary, a
340 // new channel state will be created.
341  
342 internal ChannelState UpdateServer(RegionState rs, string server)
343 {
344 RemoveRegion(rs);
345 ChannelState cs = new ChannelState(this);
346 cs.Server = server;
347 cs = Integrate(rs, cs);
348 cs.AddRegion(rs);
349 return cs;
350 }
351  
352 internal ChannelState UpdatePort(RegionState rs, string port)
353 {
354 RemoveRegion(rs);
355 ChannelState cs = new ChannelState(this);
356 cs.Port = Convert.ToUInt32(port);
357 cs = Integrate(rs, cs);
358 cs.AddRegion(rs);
359 return cs;
360 }
361  
362 internal ChannelState UpdateChannel(RegionState rs, string channel)
363 {
364 RemoveRegion(rs);
365 ChannelState cs = new ChannelState(this);
366 cs.IrcChannel = channel;
367 cs = Integrate(rs, cs);
368 cs.AddRegion(rs);
369 return cs;
370 }
371  
372 internal ChannelState UpdateNickname(RegionState rs, string nickname)
373 {
374 RemoveRegion(rs);
375 ChannelState cs = new ChannelState(this);
376 cs.BaseNickname = nickname;
377 cs = Integrate(rs, cs);
378 cs.AddRegion(rs);
379 return cs;
380 }
381  
382 internal ChannelState UpdateClientReporting(RegionState rs, string cr)
383 {
384 RemoveRegion(rs);
385 ChannelState cs = new ChannelState(this);
386 cs.ClientReporting = Convert.ToBoolean(cr);
387 cs = Integrate(rs, cs);
388 cs.AddRegion(rs);
389 return cs;
390 }
391  
392 internal ChannelState UpdateRelayIn(RegionState rs, string channel)
393 {
394 RemoveRegion(rs);
395 ChannelState cs = new ChannelState(this);
396 cs.RelayChannel = Convert.ToInt32(channel);
397 cs = Integrate(rs, cs);
398 cs.AddRegion(rs);
399 return cs;
400 }
401  
402 internal ChannelState UpdateRelayOut(RegionState rs, string channel)
403 {
404 RemoveRegion(rs);
405 ChannelState cs = new ChannelState(this);
406 cs.RelayChannelOut = Convert.ToInt32(channel);
407 cs = Integrate(rs, cs);
408 cs.AddRegion(rs);
409 return cs;
410 }
411  
412 // Determine whether or not this is a 'new' channel. Only those
413 // attributes that uniquely distinguish an IRC connection should
414 // be included here (and only those attributes should really be
415 // in the ChannelState structure)
416  
417 private bool IsAConnectionMatchFor(ChannelState cs)
418 {
419 return (
420 Server == cs.Server &&
421 IrcChannel == cs.IrcChannel &&
422 Port == cs.Port &&
423 BaseNickname == cs.BaseNickname &&
424 User == cs.User
425 );
426 }
427  
428 // This level of obsessive matching allows us to produce
429 // a minimal overhead int he case of a server which does
430 // need to differentiate IRC at a region level.
431  
432 private bool IsAPerfectMatchFor(ChannelState cs)
433 {
434 return (IsAConnectionMatchFor(cs) &&
435 RelayChannelOut == cs.RelayChannelOut &&
436 PrivateMessageFormat == cs.PrivateMessageFormat &&
437 NoticeMessageFormat == cs.NoticeMessageFormat &&
438 RandomizeNickname == cs.RandomizeNickname &&
439 AccessPassword == cs.AccessPassword &&
440 CommandsEnabled == cs.CommandsEnabled &&
441 CommandChannel == cs.CommandChannel &&
442 DefaultZone == cs.DefaultZone &&
443 RelayPrivateChannels == cs.RelayPrivateChannels &&
444 RelayChannel == cs.RelayChannel &&
445 RelayChat == cs.RelayChat &&
446 ClientReporting == cs.ClientReporting
447 );
448 }
449  
450 // This function implements the variable substitution mechanism
451 // for the configuration values. Each string read from the
452 // configuration file is scanned for '[...]' enclosures. Each
453 // one that is found is replaced by either a runtime variable
454 // (%xxx) or an existing configuration key. When no further
455 // substitution is possible, the remaining string is returned
456 // to the caller. This allows for arbitrarily nested
457 // enclosures.
458  
459 private static string Substitute(RegionState rs, string instr)
460 {
461  
462 string result = instr;
463  
464 if (result == null || result.Length == 0)
465 return result;
466  
467 // Repeatedly scan the string until all possible
468 // substitutions have been performed.
469  
470 // m_log.DebugFormat("[IRC-Channel] Parse[1]: {0}", result);
471  
472 while (arg.IsMatch(result))
473 {
474  
475 string vvar = arg.Match(result).ToString();
476 string var = vvar.Substring(1, vvar.Length - 2).Trim();
477  
478 switch (var.ToLower())
479 {
480 case "%region":
481 result = result.Replace(vvar, rs.Region);
482 break;
483 case "%host":
484 result = result.Replace(vvar, rs.Host);
485 break;
486 case "%locx":
487 result = result.Replace(vvar, rs.LocX);
488 break;
489 case "%locy":
490 result = result.Replace(vvar, rs.LocY);
491 break;
492 case "%k":
493 result = result.Replace(vvar, rs.IDK);
494 break;
495 default:
496 result = result.Replace(vvar, rs.config.GetString(var, var));
497 break;
498 }
499 // m_log.DebugFormat("[IRC-Channel] Parse[2]: {0}", result);
500 }
501  
502 // m_log.DebugFormat("[IRC-Channel] Parse[3]: {0}", result);
503 return result;
504  
505 }
506  
507 public void Close()
508 {
509 m_log.InfoFormat("[IRC-Channel-{0}] Closing channel <{1}> to server <{2}:{3}>",
510 idn, IrcChannel, Server, Port);
511 m_log.InfoFormat("[IRC-Channel-{0}] There are {1} active clients",
512 idn, clientregions.Count);
513 irc.Close();
514 }
515  
516 public void Open()
517 {
518 m_log.InfoFormat("[IRC-Channel-{0}] Opening channel <{1}> to server <{2}:{3}>",
519 idn, IrcChannel, Server, Port);
520  
521 irc.Open();
522  
523 }
524  
525 // These are called by each region that attaches to this channel. The call affects
526 // only the relationship of the region with the channel. Not the channel to IRC
527 // relationship (unless it is closed and we want it open).
528  
529 public void Open(RegionState rs)
530 {
531 AddRegion(rs);
532 Open();
533 }
534  
535 // Close is called to ensure that the IRC session is terminated if this is the
536 // only client.
537  
538 public void Close(RegionState rs)
539 {
540 RemoveRegion(rs);
541 lock (IRCBridgeModule.m_channels)
542 {
543 if (clientregions.Count == 0)
544 {
545 Close();
546 IRCBridgeModule.m_channels.Remove(this);
547 m_log.InfoFormat("[IRC-Channel-{0}] Region {1} is last user of channel <{2}> to server <{3}:{4}>",
548 idn, rs.Region, IrcChannel, Server, Port);
549 m_log.InfoFormat("[IRC-Channel-{0}] Removed", idn);
550 }
551 }
552 }
553  
554 // Add a client region to this channel if it is not already known
555  
556 public void AddRegion(RegionState rs)
557 {
558 m_log.InfoFormat("[IRC-Channel-{0}] Adding region {1} to channel <{2}> to server <{3}:{4}>",
559 idn, rs.Region, IrcChannel, Server, Port);
560 if (!clientregions.Contains(rs))
561 {
562 clientregions.Add(rs);
563 lock (irc) irc.depends++;
564 }
565 }
566  
567 // Remove a client region from the channel. If this is the last
568 // region, then clean up the channel. The connector will clean itself
569 // up if it finds itself about to be GC'd.
570  
571 public void RemoveRegion(RegionState rs)
572 {
573  
574 m_log.InfoFormat("[IRC-Channel-{0}] Removing region {1} from channel <{2} to server <{3}:{4}>",
575 idn, rs.Region, IrcChannel, Server, Port);
576  
577 if (clientregions.Contains(rs))
578 {
579 clientregions.Remove(rs);
580 lock (irc) irc.depends--;
581 }
582  
583 }
584  
585 // This function is lifted from the IRCConnector because it
586 // contains information that is not differentiating from an
587 // IRC point-of-view.
588  
589 public static void OSChat(IRCConnector p_irc, OSChatMessage c, bool cmsg)
590 {
591  
592 // m_log.DebugFormat("[IRC-OSCHAT] from {0}:{1}", p_irc.Server, p_irc.IrcChannel);
593  
594 try
595 {
596  
597 // Scan through the set of unique channel configuration for those
598 // that belong to this connector. And then forward the message to
599 // all regions known to those channels.
600 // Note that this code is responsible for completing some of the
601 // settings for the inbound OSChatMessage
602  
603 lock (IRCBridgeModule.m_channels)
604 {
605 foreach (ChannelState cs in IRCBridgeModule.m_channels)
606 {
607 if (p_irc == cs.irc)
608 {
609  
610 // This non-IRC differentiator moved to here
611  
612 if (cmsg && !cs.ClientReporting)
613 continue;
614  
615 // This non-IRC differentiator moved to here
616  
617 c.Channel = (cs.RelayPrivateChannels ? cs.RelayChannel : 0);
618  
619 foreach (RegionState region in cs.clientregions)
620 {
621 region.OSChat(cs.irc, c);
622 }
623  
624 }
625 }
626 }
627 }
628 catch (Exception ex)
629 {
630 m_log.ErrorFormat("[IRC-OSCHAT]: BroadcastSim Exception: {0}", ex.Message);
631 m_log.Debug(ex);
632 }
633 }
634 }
635 }