30 using System.Collections.Generic;
 
   32 using System.Net.Sockets;
 
   33 using System.Reflection;
 
   34 using System.Text.RegularExpressions;
 
   35 using System.Threading;
 
   39 using OpenSim.Framework;
 
   40 using OpenSim.Framework.Monitoring;
 
   41 using OpenSim.Region.Framework.Interfaces;
 
   42 using OpenSim.Region.Framework.Scenes;
 
   49         #region Global (static) state 
   51         private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
 
   58         private static readonly 
char[] CS_SPACE = { 
' ' };
 
   60         private const int WD_INTERVAL = 1000;     
 
   61         private static int PING_PERIOD = 15;       
 
   62         private static int ICCD_PERIOD = 10;       
 
   63         private static int L_TIMEOUT = 25;       
 
   65         private static int _idk_ = 0;        
 
   66         private static int _pdk_ = 0;        
 
   67         private static int _icc_ = ICCD_PERIOD; 
 
   71         private static List<IRCConnector> m_connectors = 
new List<IRCConnector>();
 
   75         private static System.Timers.Timer m_watchdog = null;
 
   82             m_log.DebugFormat(
"[IRC-Connector]: Static initialization started");
 
   83             m_watchdog = 
new System.Timers.Timer(WD_INTERVAL);
 
   84             m_watchdog.Elapsed += 
new ElapsedEventHandler(WatchdogHandler);
 
   85             m_watchdog.AutoReset = 
true;
 
   87             m_log.DebugFormat(
"[IRC-Connector]: Static initialization complete");
 
   92         #region Instance state 
   96         internal int idn = _idk_++;
 
  103         internal int depends = 0;
 
  110         internal int m_resetk = 0;
 
  114         internal bool m_randomizeNick = 
true; 
 
  115         internal string m_baseNick = null;      
 
  116         internal string m_nick = null;          
 
  120             get { 
return m_nick; }
 
  121             set { m_nick = value; }
 
  124         private bool m_enabled = 
false;            
 
  127             get { 
return m_enabled; }
 
  130         private bool m_connected = 
false;        
 
  131         private bool m_pending = 
false;        
 
  132         private int m_timeout = L_TIMEOUT;    
 
  133         public bool Connected
 
  135             get { 
return m_connected; }
 
  138         private string m_ircChannel;            
 
  139         public string IrcChannel
 
  141             get { 
return m_ircChannel; }
 
  142             set { m_ircChannel = value; }
 
  145         private uint m_port = 6667;                
 
  148             get { 
return m_port; }
 
  149             set { m_port = value; }
 
  152         private string m_server = null;            
 
  155             get { 
return m_server; }
 
  156             set { m_server = value; }
 
  158         private string m_password = null;
 
  159         public string Password
 
  161             get { 
return m_password; }
 
  162             set { m_password = value; }
 
  165         private string m_user = 
"USER OpenSimBot 8 * :I'm an OpenSim to IRC bot";
 
  168             get { 
return m_user; }
 
  173         private TcpClient m_tcp;
 
  174         private NetworkStream m_stream = null;
 
  175         private StreamReader m_reader;
 
  176         private StreamWriter m_writer;
 
  180         internal string usermod = String.Empty;
 
  181         internal string chanmod = String.Empty;
 
  182         internal string version = String.Empty;
 
  183         internal bool motd = 
false;
 
  187         #region connector instance management 
  200             m_server = cs.Server;
 
  201             m_password = cs.Password;
 
  202             m_baseNick = cs.BaseNickname;
 
  203             m_randomizeNick = cs.RandomizeNickname;
 
  204             m_ircChannel = cs.IrcChannel;
 
  208             if (m_watchdog == null)
 
  212                 ICCD_PERIOD = cs.ConnectDelay;
 
  213                 PING_PERIOD = cs.PingDelay;
 
  229             if (m_server == null || m_baseNick == null || m_ircChannel == null || m_user == null)
 
  230                 throw new Exception(
"Invalid connector configuration");
 
  235                 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
 
  239             m_log.InfoFormat(
"[IRC-Connector-{0}]: Initialization complete", idn);
 
  262                     m_connectors.Add(
this);
 
  274             m_log.InfoFormat(
"[IRC-Connector-{0}] Closing", idn);
 
  279                 if ((depends == 0) && Enabled)
 
  286                         m_log.DebugFormat(
"[IRC-Connector-{0}] Closing interface", idn);
 
  292                             m_writer.WriteLine(String.Format(
"QUIT :{0} to {1} wormhole to {2} closing",
 
  293                                 m_nick, m_ircChannel, m_server));
 
  296                         catch (Exception) { }
 
  301                         try { m_writer.Close(); }
 
  302                         catch (Exception) { }
 
  303                         try { m_reader.Close(); }
 
  304                         catch (Exception) { }
 
  305                         try { m_stream.Close(); }
 
  306                         catch (Exception) { }
 
  307                         try { m_tcp.Close(); }
 
  308                         catch (Exception) { }
 
  313                         m_connectors.Remove(
this);
 
  318             m_log.InfoFormat(
"[IRC-Connector-{0}] Closed", idn);
 
  324         #region session management 
  336             while (_icc_ < ICCD_PERIOD)
 
  339             m_log.DebugFormat(
"[IRC-Connector-{0}]: Connection request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel);
 
  349                     if (m_connected) 
return;
 
  353                     m_timeout = L_TIMEOUT;
 
  355                     m_tcp = 
new TcpClient(m_server, (
int)m_port);
 
  356                     m_stream = m_tcp.GetStream();
 
  357                     m_reader = 
new StreamReader(m_stream);
 
  358                     m_writer = 
new StreamWriter(m_stream);
 
  360                     m_log.InfoFormat(
"[IRC-Connector-{0}]: Connected to {1}:{2}", idn, m_server, m_port);
 
  362                     WorkManager.StartThread(ListenerRun, 
"IRCConnectionListenerThread", ThreadPriority.Normal, 
true, 
false);
 
  365                     if (m_password != null)
 
  366                         m_writer.WriteLine(String.Format(
"PASS {0}", m_password));
 
  367                     m_writer.WriteLine(String.Format(
"NICK {0}", m_nick));
 
  369                     m_writer.WriteLine(m_user);
 
  374                     m_log.ErrorFormat(
"[IRC-Connector-{0}] cannot connect {1} to {2}:{3}: {4}",
 
  375                                       idn, m_nick, m_server, m_port, e.Message);
 
  394             m_log.DebugFormat(
"[IRC-Connector-{0}]: Reconnect request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel);
 
  404                     m_log.InfoFormat(
"[IRC-Connector-{0}] Resetting connector", idn);
 
  415                     try { m_writer.Close(); }
 
  416                     catch (Exception) { }
 
  417                     try { m_reader.Close(); }
 
  418                     catch (Exception) { }
 
  419                     try { m_tcp.Close(); }
 
  420                     catch (Exception) { }
 
  436         #region Outbound (to-IRC) message handlers 
  438         public void PrivMsg(
string pattern, 
string from, 
string region, 
string msg)
 
  448                 m_writer.WriteLine(pattern, m_ircChannel, from, region, msg);
 
  454                 m_log.ErrorFormat(
"[IRC-Connector-{0}]: PrivMsg I/O Error: disconnected from IRC server", idn);
 
  459                 m_log.ErrorFormat(
"[IRC-Connector-{0}]: PrivMsg exception : {1}", idn, ex.Message);
 
  472                 m_writer.WriteLine(msg);
 
  478                 m_log.ErrorFormat(
"[IRC-Connector-{0}] Disconnected from IRC server.(Send)", idn);
 
  483                 m_log.ErrorFormat(
"[IRC-Connector-{0}] Send exception trap: {0}", idn, ex.Message);
 
  495             int resetk = m_resetk;
 
  499                 while (m_enabled && m_connected)
 
  501                     if ((inputLine = m_reader.ReadLine()) == null)
 
  502                         throw new Exception(
"Listener input socket closed");
 
  504                     Watchdog.UpdateThread();
 
  508                     if (inputLine.Contains(
"PRIVMSG"))
 
  510                         Dictionary<string, string> data = ExtractMsg(inputLine);
 
  516                             c.Message = data[
"msg"];
 
  517                             c.Type = ChatTypeEnum.Region;
 
  518                             c.Position = CenterOfRegion;
 
  519                             c.From =  data[
"nick"] + 
"@IRC";
 
  521                             c.SenderUUID = UUID.Zero;
 
  526                             if ((1 == c.
Message[0]) && c.
Message.Substring(1).StartsWith(
"ACTION"))
 
  527                                 c.Message = String.Format(
"/me {0}", c.Message.Substring(8, c.Message.Length - 9));
 
  529                             ChannelState.OSChat(
this, c, 
false);
 
  534                         ProcessIRCCommand(inputLine);
 
  548             if (m_enabled && (m_resetk == resetk))
 
  551             Watchdog.RemoveThread();
 
  554         private Regex RE = 
new Regex(
@":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)",
 
  555                                      RegexOptions.Multiline);
 
  557         private Dictionary<string, string> ExtractMsg(
string input)
 
  564             Dictionary<string, string> result = null;
 
  565             MatchCollection matches = RE.Matches(input);
 
  568             if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
 
  578             result = 
new Dictionary<string, string>();
 
  579             result.Add(
"nick", matches[0].Groups[1].Value);
 
  580             result.Add(
"user", matches[0].Groups[2].Value);
 
  581             result.Add(
"channel", matches[0].Groups[3].Value);
 
  582             result.Add(
"msg", matches[0].Groups[4].Value);
 
  593                 c.Message = String.Format(
format, args);
 
  594                 c.Type = ChatTypeEnum.Region; 
 
  595                 c.Position = CenterOfRegion;
 
  597                 c.SenderUUID = UUID.Zero;
 
  599                 ChannelState.OSChat(
this, c, 
true);
 
  604                 m_log.ErrorFormat(
"[IRC-Connector-{0}]: BroadcastSim Exception Trap: {1}\n{2}", idn, ex.Message, ex.StackTrace);
 
  608         #region IRC Command Handlers 
  614             string c_server = m_server;
 
  616             string pfx = String.Empty;
 
  617             string cmd = String.Empty;
 
  618             string parms = String.Empty;
 
  626             commArgs = command.Split(CS_SPACE, 2);
 
  628             if (commArgs[0].StartsWith(
":"))
 
  630                 pfx = commArgs[0].Substring(1);
 
  631                 commArgs = commArgs[1].Split(CS_SPACE, 2);
 
  650                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  651                     commArgs = parms.Split(CS_SPACE);
 
  652                     c_server = commArgs[1];
 
  654                     version = commArgs[2];
 
  655                     usermod = commArgs[3];
 
  656                     chanmod = commArgs[4];
 
  658                     m_writer.WriteLine(String.Format(
"JOIN {0}", m_ircChannel));
 
  660                     m_log.InfoFormat(
"[IRC-Connector-{0}]: sent request to join {1} ", idn, m_ircChannel);
 
  689                     m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
 
  690                     m_log.ErrorFormat(
"[IRC-Connector-{0}]: [{1}] IRC SERVER reports NicknameInUse, trying {2}", idn, cmd, m_nick);
 
  692                     m_writer.WriteLine(String.Format(
"NICK {0}", m_nick));
 
  694                     m_writer.WriteLine(m_user);
 
  696                     m_writer.WriteLine(String.Format(
"JOIN {0}", m_ircChannel));
 
  700                     m_log.ErrorFormat(
"[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
 
  701                     m_log.ErrorFormat(
"[IRC-Connector-{0}] [{1}] Connector disabled", idn, cmd);
 
  710                     m_log.ErrorFormat(
"[IRC-Connector-{0}] [{1}] {2}", idn, cmd, parms.Split(CS_SPACE, 2)[1]);
 
  711                     if (parms.Contains(
"reconnect too fast"))
 
  717                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  718                     m_writer.WriteLine(String.Format(
"PONG {0}", parms));
 
  725                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  726                     eventIrcJoin(pfx, cmd, parms);
 
  729                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  730                     eventIrcPart(pfx, cmd, parms);
 
  733                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  734                     eventIrcMode(pfx, cmd, parms);
 
  737                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  738                     eventIrcNickChange(pfx, cmd, parms);
 
  741                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  742                     eventIrcKick(pfx, cmd, parms);
 
  745                     m_log.DebugFormat(
"[IRC-Connector-{0}] [{1}] parms = <{2}>", idn, cmd, parms);
 
  746                     eventIrcQuit(pfx, cmd, parms);
 
  749                     m_log.DebugFormat(
"[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms);
 
  759             string[] args = parms.Split(CS_SPACE, 2);
 
  760             string IrcUser = prefix.Split(
'!')[0];
 
  761             string IrcChannel = args[0];
 
  763             if (IrcChannel.StartsWith(
":"))
 
  764                 IrcChannel = IrcChannel.Substring(1);
 
  766             if(IrcChannel == m_ircChannel)
 
  768                 m_log.InfoFormat(
"[IRC-Connector-{0}] Joined requested channel {1} at {2}", idn, IrcChannel,m_server);
 
  772                 m_log.InfoFormat(
"[IRC-Connector-{0}] Joined unknown channel {1} at {2}", idn, IrcChannel,m_server);
 
  773             BroadcastSim(IrcUser, 
"/me joins {0}", IrcChannel);
 
  778             string[] args = parms.Split(CS_SPACE, 2);
 
  779             string IrcUser = prefix.Split(
'!')[0];
 
  780             string IrcChannel = args[0];
 
  782             m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCPart {1}:{2}", idn, m_server, m_ircChannel);
 
  783             BroadcastSim(IrcUser, 
"/me parts {0}", IrcChannel);
 
  788             string[] args = parms.Split(CS_SPACE, 2);
 
  791             m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCMode {1}:{2}", idn, m_server, m_ircChannel);
 
  792             if (UserMode.Substring(0, 1) == 
":")
 
  794                 UserMode = UserMode.Remove(0, 1);
 
  800             string[] args = parms.Split(CS_SPACE, 2);
 
  801             string UserOldNick = prefix.Split(
'!')[0];
 
  802             string UserNewNick = args[0].Remove(0, 1);
 
  804             m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCNickChange {1}:{2}", idn, m_server, m_ircChannel);
 
  805             BroadcastSim(UserOldNick, 
"/me is now known as {0}", UserNewNick);
 
  810             string[] args = parms.Split(CS_SPACE, 3);
 
  811             string UserKicker = prefix.Split(
'!')[0];
 
  812             string IrcChannel = args[0];
 
  813             string UserKicked = args[1];
 
  814             string KickMessage = args[2];
 
  816             m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCKick {1}:{2}", idn, m_server, m_ircChannel);
 
  817             BroadcastSim(UserKicker, 
"/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
 
  819             if (UserKicked == m_nick)
 
  821                 BroadcastSim(m_nick, 
"Hey, that was me!!!");
 
  828             string IrcUser = prefix.Split(
'!')[0];
 
  829             string QuitMessage = parms;
 
  831             m_log.DebugFormat(
"[IRC-Connector-{0}] Event: IRCQuit {1}:{2}", idn, m_server, m_ircChannel);
 
  832             BroadcastSim(IrcUser, 
"/me quits saying \"{0}\"", QuitMessage);
 
  837         #region Connector Watch Dog 
  848             _pdk_ = (_pdk_ + 1) % PING_PERIOD;    
 
  868                                 m_log.ErrorFormat(
"[IRC-Watchdog] Exception on connector {0}: {1} ", connector.idn, e.Message);
 
  874                             if (connector.m_pending)
 
  876                                 if (connector.m_timeout == 0)
 
  878                                     m_log.ErrorFormat(
"[IRC-Watchdog] Login timed-out for connector {0}, reconnecting", connector.idn);
 
  879                                     connector.Reconnect();
 
  882                                     connector.m_timeout--;
 
  893                                     connector.m_writer.WriteLine(String.Format(
"PING :{0}", connector.m_server));
 
  894                                     connector.m_writer.Flush();
 
  898                                     m_log.ErrorFormat(
"[IRC-PingRun] Exception on connector {0}: {1} ", connector.idn, e.Message);
 
  900                                     connector.Reconnect();
 
void PrivMsg(string pattern, string from, string region, string msg)
void eventIrcKick(string prefix, string command, string parms)
static void WatchdogHandler(Object source, ElapsedEventArgs args)
void eventIrcQuit(string prefix, string command, string parms)
string Message
The message sent by the user 
void eventIrcJoin(string prefix, string command, string parms)
void eventIrcMode(string prefix, string command, string parms)
void eventIrcPart(string prefix, string command, string parms)
void BroadcastSim(string sender, string format, params string[] args)
void ProcessIRCCommand(string command)
void eventIrcNickChange(string prefix, string command, string parms)