OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
ChannelState.cs
Go to the documentation of this file.
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 (string.IsNullOrEmpty(result))
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  // Now we unescape the literal brackets
503  result = result.Replace(@"\[","[").Replace(@"\]","]");
504 
505  // m_log.DebugFormat("[IRC-Channel] Parse[3]: {0}", result);
506  return result;
507 
508  }
509 
510  public void Close()
511  {
512  m_log.InfoFormat("[IRC-Channel-{0}] Closing channel <{1}> to server <{2}:{3}>",
513  idn, IrcChannel, Server, Port);
514  m_log.InfoFormat("[IRC-Channel-{0}] There are {1} active clients",
515  idn, clientregions.Count);
516  irc.Close();
517  }
518 
519  public void Open()
520  {
521  m_log.InfoFormat("[IRC-Channel-{0}] Opening channel <{1}> to server <{2}:{3}>",
522  idn, IrcChannel, Server, Port);
523 
524  irc.Open();
525 
526  }
527 
528  // These are called by each region that attaches to this channel. The call affects
529  // only the relationship of the region with the channel. Not the channel to IRC
530  // relationship (unless it is closed and we want it open).
531 
532  public void Open(RegionState rs)
533  {
534  AddRegion(rs);
535  Open();
536  }
537 
538  // Close is called to ensure that the IRC session is terminated if this is the
539  // only client.
540 
541  public void Close(RegionState rs)
542  {
543  RemoveRegion(rs);
544  lock (IRCBridgeModule.m_channels)
545  {
546  if (clientregions.Count == 0)
547  {
548  Close();
549  IRCBridgeModule.m_channels.Remove(this);
550  m_log.InfoFormat("[IRC-Channel-{0}] Region {1} is last user of channel <{2}> to server <{3}:{4}>",
551  idn, rs.Region, IrcChannel, Server, Port);
552  m_log.InfoFormat("[IRC-Channel-{0}] Removed", idn);
553  }
554  }
555  }
556 
557  // Add a client region to this channel if it is not already known
558 
559  public void AddRegion(RegionState rs)
560  {
561  m_log.InfoFormat("[IRC-Channel-{0}] Adding region {1} to channel <{2}> to server <{3}:{4}>",
562  idn, rs.Region, IrcChannel, Server, Port);
563  if (!clientregions.Contains(rs))
564  {
565  clientregions.Add(rs);
566  lock (irc) irc.depends++;
567  }
568  }
569 
570  // Remove a client region from the channel. If this is the last
571  // region, then clean up the channel. The connector will clean itself
572  // up if it finds itself about to be GC'd.
573 
574  public void RemoveRegion(RegionState rs)
575  {
576 
577  m_log.InfoFormat("[IRC-Channel-{0}] Removing region {1} from channel <{2} to server <{3}:{4}>",
578  idn, rs.Region, IrcChannel, Server, Port);
579 
580  if (clientregions.Contains(rs))
581  {
582  clientregions.Remove(rs);
583  lock (irc) irc.depends--;
584  }
585 
586  }
587 
588  // This function is lifted from the IRCConnector because it
589  // contains information that is not differentiating from an
590  // IRC point-of-view.
591 
592  public static void OSChat(IRCConnector p_irc, OSChatMessage c, bool cmsg)
593  {
594 
595  // m_log.DebugFormat("[IRC-OSCHAT] from {0}:{1}", p_irc.Server, p_irc.IrcChannel);
596 
597  try
598  {
599 
600  // Scan through the set of unique channel configuration for those
601  // that belong to this connector. And then forward the message to
602  // all regions known to those channels.
603  // Note that this code is responsible for completing some of the
604  // settings for the inbound OSChatMessage
605 
606  lock (IRCBridgeModule.m_channels)
607  {
608  foreach (ChannelState cs in IRCBridgeModule.m_channels)
609  {
610  if (p_irc == cs.irc)
611  {
612 
613  // This non-IRC differentiator moved to here
614 
615  if (cmsg && !cs.ClientReporting)
616  continue;
617 
618  // This non-IRC differentiator moved to here
619 
620  c.Channel = (cs.RelayPrivateChannels ? cs.RelayChannel : 0);
621 
622  foreach (RegionState region in cs.clientregions)
623  {
624  region.OSChat(cs.irc, c);
625  }
626 
627  }
628  }
629  }
630  }
631  catch (Exception ex)
632  {
633  m_log.ErrorFormat("[IRC-OSCHAT]: BroadcastSim Exception: {0}", ex.Message);
634  m_log.Debug(ex);
635  }
636  }
637  }
638 }
ChatFromViewer Arguments
Interactive OpenSim region server
Definition: OpenSim.cs:55