aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs393
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs702
2 files changed, 0 insertions, 1095 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
deleted file mode 100644
index 482f469..0000000
--- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
+++ /dev/null
@@ -1,393 +0,0 @@
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 OpenSim 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Net.Sockets;
32using System.Reflection;
33using System.Text.RegularExpressions;
34using System.Threading;
35using OpenMetaverse;
36using log4net;
37using Nini.Config;
38using OpenSim.Framework;
39using OpenSim.Region.Environment.Interfaces;
40using OpenSim.Region.Environment.Scenes;
41
42namespace OpenSim.Region.Environment.Modules.Avatar.Chat
43{
44 public class IRCBridgeModule : IRegionModule
45 {
46 private static readonly ILog m_log =
47 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48
49 private const int DEBUG_CHANNEL = 2147483647;
50
51
52 private IRCConnector m_irc = null;
53
54 private string m_last_leaving_user = null;
55 private string m_last_new_user = null;
56 private List<Scene> m_scenes = new List<Scene>();
57 private List<int> m_validInWorldChannels = new List<int>();
58
59 internal object m_syncInit = new object();
60 internal object m_syncLogout = new object();
61
62 private IConfig m_config;
63 private string m_defaultzone = null;
64 private bool m_commandsEnabled = false;
65 private int m_commandChannel = -1;
66 private bool m_relayPrivateChannels = false;
67 private int m_relayChannelOut = -1;
68 private bool m_clientReporting = true;
69 private bool m_relayChat = true;
70 private Regex m_accessPasswordRe = null;
71
72 #region IRegionModule Members
73
74 public void Initialise(Scene scene, IConfigSource config)
75 {
76 try
77 {
78 if ((m_config = config.Configs["OIRC"]) == null)
79 {
80 m_log.InfoFormat("[IRC] module not configured");
81 return;
82 }
83
84 if (!m_config.GetBoolean("enabled", false))
85 {
86 m_log.InfoFormat("[IRC] module disabled in configuration");
87 return;
88 }
89 }
90 catch (Exception)
91 {
92 m_log.Info("[IRC] module not configured");
93 return;
94 }
95
96 m_commandsEnabled = m_config.GetBoolean("commands_enabled", m_commandsEnabled);
97 m_commandChannel = m_config.GetInt("commandchannel", m_commandChannel); // compat
98 m_commandChannel = m_config.GetInt("command_channel", m_commandChannel);
99
100 m_relayPrivateChannels = m_config.GetBoolean("relay_private_channels", m_relayPrivateChannels);
101 m_relayChannelOut = m_config.GetInt("relay_private_channel_out", m_relayChannelOut);
102 m_relayChat = m_config.GetBoolean("relay_chat", m_relayChat);
103
104 m_clientReporting = m_config.GetBoolean("report_clients", m_clientReporting);
105
106 if (m_accessPasswordRe == null)
107 {
108 string pass = config.Configs["IRC"].GetString("access_password", String.Empty);
109 m_accessPasswordRe = new Regex(String.Format(@"^{0},(?<avatar>[^,]+),(?<message>.+)$", pass),
110 RegexOptions.Compiled);
111 }
112
113 if (m_relayChat)
114 {
115 m_validInWorldChannels.Add(0);
116 m_validInWorldChannels.Add(DEBUG_CHANNEL);
117 }
118
119 if (m_relayPrivateChannels)
120 m_validInWorldChannels.Add(m_relayChannelOut);
121
122
123 lock (m_syncInit)
124 {
125
126 if (!m_scenes.Contains(scene))
127 {
128 m_scenes.Add(scene);
129 scene.EventManager.OnNewClient += OnNewClient;
130 scene.EventManager.OnChatFromWorld += OnSimChat;
131 scene.EventManager.OnChatFromClient += OnSimChat;
132 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
133 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
134 }
135
136 try
137 {
138 m_defaultzone = config.Configs["IRC"].GetString("fallback_region", "Sim");
139 }
140 catch (Exception)
141 {
142 }
143
144 // setup IRC Relay
145 if (m_irc == null)
146 {
147 m_irc = new IRCConnector(config);
148 }
149 m_irc.AddScene(scene);
150
151 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1}, commands {2}, private channels {3}",
152 scene.RegionInfo.RegionName, m_defaultzone,
153 m_commandsEnabled ? "enabled" : "not enabled",
154 m_relayPrivateChannels ? "relayed" : "not relayed");
155 }
156 }
157
158 public void PostInitialise()
159 {
160 if (null == m_irc || !m_irc.Enabled) return;
161 m_irc.Start();
162 }
163
164 public void Close()
165 {
166 if (null != m_irc)
167 {
168 m_irc.Close();
169 m_log.Info("[IRC] closed connection to IRC server");
170 }
171 }
172
173 public string Name
174 {
175 get { return "IRCBridgeModule"; }
176 }
177
178 public bool IsSharedModule
179 {
180 get { return true; }
181 }
182
183 #endregion
184
185 #region ISimChat Members
186
187 public void OnSimChat(Object sender, OSChatMessage c)
188 {
189 // early return if nothing to forward
190 if (c.Message.Length == 0) return;
191
192 // early return if this comes from the IRC forwarder
193 if (m_irc.Equals(sender)) return;
194
195 m_log.DebugFormat("[IRC] heard on channel {0}: {1}", c.Channel, c.Message);
196
197 // check for commands coming from avatars or in-world
198 // object (if commands are enabled)
199 if (m_commandsEnabled && c.Channel == m_commandChannel)
200 {
201 string[] messages = c.Message.Split(' ');
202 string command = messages[0].ToLower();
203
204 try
205 {
206 switch (command)
207 {
208 case "channel":
209 m_irc.IrcChannel = messages[1];
210 break;
211 case "close":
212 m_irc.Close();
213 break;
214 case "connect":
215 m_irc.Connect();
216 break;
217 case "nick":
218 m_irc.Nick = messages[1];
219 break;
220 case "port":
221 m_irc.Port = Convert.ToUInt32(messages[1]);
222 break;
223 case "reconnect":
224 m_irc.Reconnect();
225 break;
226 case "server":
227 m_irc.Server = messages[1];
228 break;
229 case "client-reporting":
230 m_irc.ClientReporting = Convert.ToBoolean(messages[1]);
231
232 break;
233 case "in-channel":
234 m_irc.RelayChannel = Convert.ToInt32(messages[1]);
235 break;
236 case "out-channel":
237 m_relayChannelOut = Convert.ToInt32(messages[1]);
238 break;
239
240 default:
241 m_irc.Send(c.Message);
242 break;
243 }
244 }
245 catch (Exception ex)
246 {
247 m_log.DebugFormat("[IRC] error processing in-world command channel input: {0}", ex);
248 }
249 }
250
251 // drop messages if their channel is not on the valid
252 // in-world channel list
253 if (!m_validInWorldChannels.Contains(c.Channel))
254 {
255 m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel);
256 return;
257 }
258
259 ScenePresence avatar = null;
260 Scene scene = (Scene)c.Scene;
261
262 if (scene == null)
263 scene = m_scenes[0];
264
265 string fromName = c.From;
266
267 if (c.Sender != null)
268 {
269 avatar = scene.GetScenePresence(c.Sender.AgentId);
270 if (avatar != null) fromName = avatar.Name;
271 }
272
273 if (!m_irc.Connected)
274 {
275 m_log.WarnFormat("[IRC] IRCConnector not connected: dropping message from {0}", fromName);
276 return;
277 }
278
279 if (null != avatar && m_relayChat)
280 {
281 string msg = c.Message;
282 if (msg.StartsWith("/me "))
283 msg = String.Format("{0} {1}", fromName, c.Message.Substring(4));
284
285 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, msg);
286 return;
287 }
288
289 if (null == avatar && m_relayPrivateChannels)
290 {
291 Match m;
292 if (m_accessPasswordRe != null &&
293 (m = m_accessPasswordRe.Match(c.Message)) != null)
294 {
295 m_log.DebugFormat("[IRC] relaying message from {0}: {1}", m.Groups["avatar"].ToString(),
296 m.Groups["message"].ToString());
297 m_irc.PrivMsg(m.Groups["avatar"].ToString(), scene.RegionInfo.RegionName,
298 m.Groups["message"].ToString());
299 }
300 return;
301 }
302 }
303 #endregion
304
305 public void OnNewClient(IClientAPI client)
306 {
307 try
308 {
309 client.OnLogout += OnClientLoggedOut;
310 client.OnConnectionClosed += OnClientLoggedOut;
311
312 if (client.Name != m_last_new_user)
313 {
314 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
315 {
316 m_log.DebugFormat("[IRC] {0} logging on", client.Name);
317 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging on", client.Name));
318 }
319 m_last_new_user = client.Name;
320 }
321 }
322 catch (Exception ex)
323 {
324 m_log.Error("[IRC]: OnNewClient exception trap:" + ex.ToString());
325 }
326 }
327
328 public void OnMakeRootAgent(ScenePresence presence)
329 {
330 try
331 {
332 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
333 {
334 string regionName = presence.Scene.RegionInfo.RegionName;
335 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
336 m_log.DebugFormat("[IRC] noticing {0} in {1}", clientName, regionName);
337 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} in {1}", clientName, regionName));
338 }
339 }
340 catch (Exception)
341 {
342 }
343 }
344
345 public void OnMakeChildAgent(ScenePresence presence)
346 {
347 try
348 {
349 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
350 {
351 string regionName = presence.Scene.RegionInfo.RegionName;
352 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
353 m_log.DebugFormat("[IRC] noticing {0} in {1}", clientName, regionName);
354 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} left {1}", clientName, regionName));
355 }
356 }
357 catch (Exception)
358 {
359 }
360 }
361
362
363 public void OnClientLoggedOut(IClientAPI client)
364 {
365 lock (m_syncLogout)
366 {
367 try
368 {
369 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
370 {
371 // handles simple case. May not work for
372 // hundred connecting in per second. and
373 // OnNewClients calle getting interleaved but
374 // filters out multiple reports
375 if (client.Name != m_last_leaving_user)
376 {
377 m_last_leaving_user = client.Name;
378 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", client.Name));
379 m_log.InfoFormat("[IRC]: {0} logging out", client.Name);
380 }
381
382 if (m_last_new_user == client.Name)
383 m_last_new_user = null;
384 }
385 }
386 catch (Exception ex)
387 {
388 m_log.Error("[IRC]: ClientLoggedOut exception trap:" + ex.ToString());
389 }
390 }
391 }
392 }
393}
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs
deleted file mode 100644
index 29ba17d..0000000
--- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs
+++ /dev/null
@@ -1,702 +0,0 @@
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 OpenSim 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Net.Sockets;
32using System.Reflection;
33using System.Text.RegularExpressions;
34using System.Threading;
35using OpenMetaverse;
36using log4net;
37using Nini.Config;
38using OpenSim.Framework;
39using OpenSim.Region.Environment.Interfaces;
40using OpenSim.Region.Environment.Scenes;
41
42namespace OpenSim.Region.Environment.Modules.Avatar.Chat
43{
44 public class IRCConnector
45 {
46 #region ErrorReplies enum
47
48 public enum ErrorReplies
49 {
50 NotRegistered = 451, // ":You have not registered"
51 NicknameInUse = 433 // "<nick> :Nickname is already in use"
52 }
53
54 #endregion
55
56 #region Replies enum
57
58 public enum Replies
59 {
60 MotdStart = 375, // ":- <server> Message of the day - "
61 Motd = 372, // ":- <text>"
62 EndOfMotd = 376 // ":End of /MOTD command"
63 }
64
65 #endregion
66
67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
68
69 private Thread m_listener = null;
70 private Thread m_watchdog = null;
71 private Thread m_pinger = null;
72
73 private bool m_randomizeNick = true;
74
75 public string m_baseNick = null;
76 private string m_nick = null;
77 public string Nick
78 {
79 get { return m_baseNick; }
80 set { m_baseNick = value; }
81 }
82
83 private bool m_enabled = false;
84 public bool Enabled
85 {
86 get { return m_enabled; }
87 }
88
89 private bool m_connected = false;
90 public bool Connected
91 {
92 get { return m_connected; }
93 }
94
95 private string m_ircChannel;
96 public string IrcChannel
97 {
98 get { return m_ircChannel; }
99
100 set { m_ircChannel = value; }
101 }
102
103 private bool m_relayPrivateChannels = false;
104 public bool RelayPrivateChannels
105 {
106 get { return m_relayPrivateChannels; }
107 set { m_relayPrivateChannels = value; }
108 }
109
110 private int m_relayChannel = 0;
111 public int RelayChannel
112 {
113 get { return m_relayChannel; }
114 set { m_relayChannel = value; }
115 }
116
117 private bool m_clientReporting = true;
118 public bool ClientReporting
119 {
120 get { return m_clientReporting; }
121 set { m_clientReporting = value; }
122 }
123
124 private uint m_port = 6667;
125 public uint Port
126 {
127 get { return m_port; }
128 set { m_port = value; }
129 }
130
131 private string m_server = null;
132 public string Server
133 {
134 get { return m_server; }
135 set { m_server = value; }
136 }
137
138 private string m_privmsgformat = "PRIVMSG {0} :<{1} in {2}>: {3}";
139 private StreamReader m_reader;
140 private List<Scene> m_scenes = new List<Scene>();
141
142 private NetworkStream m_stream = null;
143 internal object m_syncConnect = new object();
144 private TcpClient m_tcp;
145 private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot";
146 private StreamWriter m_writer;
147
148
149 public IRCConnector(IConfigSource config)
150 {
151 m_tcp = null;
152 m_writer = null;
153 m_reader = null;
154
155 // configuration in OpenSim.ini
156 // [IRC]
157 // server = chat.freenode.net
158 // nick = OSimBot_mysim
159 // nicknum = true
160 // ;nicknum set to true appends a 2 digit random number to the nick
161 // ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot
162 // ; username is the IRC command line sent
163 // ; USER <irc_user> <visible=8,invisible=0> * : <IRC_realname>
164 // channel = #opensim-regions
165 // port = 6667
166 // ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message
167 // ;for <bot>:<user in region> :<message>
168 // ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"
169 // ;for <bot>:<message> - <user of region> :
170 // ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}"
171 // ;for <bot>:<message> - from <user> :
172 // ;msgformat = "PRIVMSG {0} : {3} - from {1}"
173 // Traps I/O disconnects so it does not crash the sim
174 // Trys to reconnect if disconnected and someone says something
175 // Tells IRC server "QUIT" when doing a close (just to be nice)
176 // Default port back to 6667
177
178 try
179 {
180 m_server = config.Configs["OIRC"].GetString("server");
181 m_baseNick = config.Configs["OIRC"].GetString("nick", "OSimBot");
182
183 m_randomizeNick = config.Configs["OIRC"].GetBoolean("randomize_nick", m_randomizeNick);
184 m_randomizeNick = config.Configs["OIRC"].GetBoolean("nicknum", m_randomizeNick); // compat
185 m_ircChannel = config.Configs["OIRC"].GetString("channel");
186 m_port = (uint)config.Configs["OIRC"].GetInt("port", (int)m_port);
187 m_user = config.Configs["OIRC"].GetString("username", m_user);
188 m_privmsgformat = config.Configs["OIRC"].GetString("msgformat", m_privmsgformat);
189
190 m_clientReporting = config.Configs["OIRC"].GetInt("verbosity", 2) > 0;
191 m_clientReporting = config.Configs["OIRC"].GetBoolean("report_clients", m_clientReporting);
192
193 m_relayPrivateChannels = config.Configs["OIRC"].GetBoolean("relay_private_channels", m_relayPrivateChannels);
194 m_relayPrivateChannels = config.Configs["OIRC"].GetBoolean("useworldcomm", m_relayPrivateChannels); //compat
195 m_relayChannel = config.Configs["OIRC"].GetInt("relay_private_channel_in", m_relayChannel);
196 m_relayChannel = config.Configs["OIRC"].GetInt("inchannel", m_relayChannel);
197
198 if (m_server != null && m_baseNick != null && m_ircChannel != null)
199 {
200 if (m_randomizeNick)
201 {
202 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
203 }
204 m_enabled = true;
205 }
206 }
207 catch (Exception ex)
208 {
209 m_log.Error("[IRCConnector]: Incomplete IRC configuration, skipping IRC bridge configuration");
210 m_log.DebugFormat("[IRCConnector] Incomplete IRC configuration: {0}", ex.ToString());
211 }
212
213 if (null == m_watchdog)
214 {
215 m_watchdog = new Thread(WatchdogRun);
216 m_watchdog.Name = "IRCWatchdog";
217 m_watchdog.IsBackground = true;
218 }
219 }
220
221 public void Start()
222 {
223 if (!m_watchdog.IsAlive)
224 {
225 m_watchdog.Start();
226 ThreadTracker.Add(m_watchdog);
227 }
228 }
229
230 public void AddScene(Scene scene)
231 {
232 lock (m_syncConnect) m_scenes.Add(scene);
233 }
234
235 public bool Connect()
236 {
237 lock (m_syncConnect)
238 {
239 try
240 {
241 if (m_connected) return true;
242
243 m_tcp = new TcpClient(m_server, (int)m_port);
244 m_stream = m_tcp.GetStream();
245 m_reader = new StreamReader(m_stream);
246 m_writer = new StreamWriter(m_stream);
247
248 m_log.DebugFormat("[IRCConnector]: Connected to {0}:{1}", m_server, m_port);
249
250 m_pinger = new Thread(new ThreadStart(PingRun));
251 m_pinger.Name = "PingSenderThread";
252 m_pinger.IsBackground = true;
253 m_pinger.Start();
254 ThreadTracker.Add(m_pinger);
255
256 m_listener = new Thread(new ThreadStart(ListenerRun));
257 m_listener.Name = "IRCConnectorListenerThread";
258 m_listener.IsBackground = true;
259 m_listener.Start();
260 ThreadTracker.Add(m_listener);
261
262 m_writer.WriteLine(m_user);
263 m_writer.Flush();
264 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
265 m_writer.Flush();
266 m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel));
267 m_writer.Flush();
268 m_log.Info("[IRCConnector]: Connection fully established");
269 m_connected = true;
270 }
271 catch (Exception e)
272 {
273 m_log.ErrorFormat("[IRCConnector] cannot connect to {0}:{1}: {2}",
274 m_server, m_port, e.Message);
275 }
276 m_log.Debug("[IRCConnector] Connected");
277 return m_connected;
278 }
279 }
280
281 public void Reconnect()
282 {
283 m_connected = false;
284 try
285 {
286 m_listener.Abort();
287 m_pinger.Abort();
288
289 m_writer.Close();
290 m_reader.Close();
291
292 m_tcp.Close();
293 }
294 catch (Exception)
295 {
296 }
297
298 if (m_enabled)
299 {
300 Connect();
301 }
302 }
303
304 public void PrivMsg(string from, string region, string msg)
305 {
306 m_log.DebugFormat("[IRCConnector] Sending message to IRC from {0}: {1}", from, msg);
307
308 // One message to the IRC server
309 try
310 {
311 m_writer.WriteLine(m_privmsgformat, m_ircChannel, from, region, msg);
312 m_writer.Flush();
313 m_log.InfoFormat("[IRCConnector]: PrivMsg {0} in {1}: {2}", from, region, msg);
314 }
315 catch (IOException)
316 {
317 m_log.Error("[IRCConnector]: Disconnected from IRC server.(PrivMsg)");
318 Reconnect();
319 }
320 catch (Exception ex)
321 {
322 m_log.ErrorFormat("[IRCConnector]: PrivMsg exception trap: {0}", ex.ToString());
323 }
324 }
325
326 public void Send(string msg)
327 {
328 try
329 {
330 m_writer.WriteLine(msg);
331 m_writer.Flush();
332 m_log.Info("IRC: Sent command string: " + msg);
333 }
334 catch (IOException)
335 {
336 m_log.Error("[IRCConnector]: Disconnected from IRC server.(PrivMsg)");
337 Reconnect();
338 }
339 catch (Exception ex)
340 {
341 m_log.ErrorFormat("[IRCConnector]: PrivMsg exception trap: {0}", ex.ToString());
342 }
343
344 }
345
346
347 private Dictionary<string, string> ExtractMsg(string input)
348 {
349 //examines IRC commands and extracts any private messages
350 // which will then be reboadcast in the Sim
351
352 m_log.Info("[IRCConnector]: ExtractMsg: " + input);
353 Dictionary<string, string> result = null;
354 string regex = @":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
355 Regex RE = new Regex(regex, RegexOptions.Multiline);
356 MatchCollection matches = RE.Matches(input);
357
358 // Get some direct matches $1 $4 is a
359 if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
360 {
361 // m_log.Info("[IRCConnector]: Number of matches: " + matches.Count);
362 // if (matches.Count > 0)
363 // {
364 // m_log.Info("[IRCConnector]: Number of groups: " + matches[0].Groups.Count);
365 // }
366 return null;
367 }
368
369 result = new Dictionary<string, string>();
370 result.Add("nick", matches[0].Groups[1].Value);
371 result.Add("user", matches[0].Groups[2].Value);
372 result.Add("channel", matches[0].Groups[3].Value);
373 result.Add("msg", matches[0].Groups[4].Value);
374
375 return result;
376 }
377
378 public void PingRun()
379 {
380 // IRC keep alive thread
381 // send PING ever 15 seconds
382 while (m_enabled)
383 {
384 try
385 {
386 if (m_connected == true)
387 {
388 m_writer.WriteLine(String.Format("PING :{0}", m_server));
389 m_writer.Flush();
390 Thread.Sleep(15000);
391 }
392 }
393 catch (IOException)
394 {
395 if (m_enabled)
396 {
397 m_log.Error("[IRCConnector]: Disconnected from IRC server.(PingRun)");
398 Reconnect();
399 }
400 }
401 catch (Exception ex)
402 {
403 m_log.ErrorFormat("[IRCConnector]: PingRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
404 }
405 }
406 }
407
408 static private Vector3 CenterOfRegion = new Vector3(128, 128, 20);
409 public void ListenerRun()
410 {
411 string inputLine;
412
413 while (m_enabled)
414 {
415 try
416 {
417 while ((m_connected) && ((inputLine = m_reader.ReadLine()) != null))
418 {
419 // m_log.Info("[IRCConnector]: " + inputLine);
420
421 if (inputLine.Contains(m_ircChannel))
422 {
423 Dictionary<string, string> data = ExtractMsg(inputLine);
424 // Any chat ???
425 if (data != null)
426 {
427 OSChatMessage c = new OSChatMessage();
428 c.Message = data["msg"];
429 c.Type = ChatTypeEnum.Region;
430 c.Position = CenterOfRegion;
431 c.Channel = m_relayPrivateChannels ? m_relayChannel : 0;
432 c.From = data["nick"];
433 c.Sender = null;
434 c.SenderUUID = UUID.Zero;
435
436 // is message "\001ACTION foo
437 // bar\001"? -> "/me foo bar"
438 if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION"))
439 c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9));
440
441 m_log.DebugFormat("[IRCConnector] ListenerRun from: {0}, {1}", c.From, c.Message);
442
443 foreach (Scene scene in m_scenes)
444 {
445 c.Scene = scene;
446 scene.EventManager.TriggerOnChatBroadcast(this, c);
447 }
448 }
449
450 Thread.Sleep(150);
451 continue;
452 }
453
454 ProcessIRCCommand(inputLine);
455 Thread.Sleep(150);
456 }
457 }
458 catch (IOException)
459 {
460 if (m_enabled)
461 {
462 m_log.Error("[IRCConnector]: ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)");
463 Reconnect();
464 }
465 }
466 catch (Exception ex)
467 {
468 m_log.ErrorFormat("[IRCConnector]: ListenerRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
469 }
470 }
471 }
472
473 public void BroadcastSim(string sender, string format, params string[] args)
474 {
475 try
476 {
477 OSChatMessage c = new OSChatMessage();
478 c.From = sender;
479 c.Message = String.Format(format, args);
480 c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say;
481 c.Channel = m_relayPrivateChannels ? m_relayChannel : 0;
482 c.Position = CenterOfRegion;
483 c.Sender = null;
484 c.SenderUUID = UUID.Zero;
485
486 m_log.DebugFormat("[IRCConnector] BroadcastSim from {0}: {1}", c.From, c.Message);
487
488 foreach (Scene scene in m_scenes)
489 {
490 c.Scene = scene;
491 scene.EventManager.TriggerOnChatBroadcast(this, c);
492 // // m_scene.EventManager.TriggerOnChatFromWorld(this, c);
493 // IWorldComm wComm = m_scene.RequestModuleInterface<IWorldComm>();
494 // wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message);
495 // //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
496 // //wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text);
497
498 }
499 }
500 catch (Exception ex) // IRC gate should not crash Sim
501 {
502 m_log.ErrorFormat("[IRCConnector]: BroadcastSim Exception Trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
503 }
504 }
505
506 public void ProcessIRCCommand(string command)
507 {
508 // m_log.Debug("[IRCConnector]: ProcessIRCCommand:" + command);
509
510 string[] commArgs = new string[command.Split(' ').Length];
511 string c_server = m_server;
512
513 commArgs = command.Split(' ');
514 if (commArgs[0].Substring(0, 1) == ":")
515 {
516 commArgs[0] = commArgs[0].Remove(0, 1);
517 }
518
519 if (commArgs[1] == "002")
520 {
521 // fetch the correct servername
522 // ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/...
523 // irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch
524
525 c_server = (commArgs[6].Split('['))[0];
526 m_server = c_server;
527 }
528
529 if (commArgs[0] == "ERROR")
530 {
531 m_log.ErrorFormat("[IRCConnector]: IRC SERVER ERROR: {0}", command);
532 }
533
534 if (commArgs[0] == "PING")
535 {
536 string p_reply = "";
537
538 for (int i = 1; i < commArgs.Length; i++)
539 {
540 p_reply += commArgs[i] + " ";
541 }
542
543 m_writer.WriteLine(String.Format("PONG {0}", p_reply));
544 m_writer.Flush();
545 }
546 else if (commArgs[0] == c_server)
547 {
548 // server message
549 try
550 {
551 Int32 commandCode = Int32.Parse(commArgs[1]);
552 switch (commandCode)
553 {
554 case (int)ErrorReplies.NicknameInUse:
555 // Gen a new name
556 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
557 m_log.ErrorFormat("[IRCConnector]: IRC SERVER reports NicknameInUse, trying {0}", m_nick);
558 // Retry
559 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
560 m_writer.Flush();
561 m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel));
562 m_writer.Flush();
563 break;
564 case (int)ErrorReplies.NotRegistered:
565 break;
566 case (int)Replies.EndOfMotd:
567 break;
568 }
569 }
570 catch (Exception)
571 {
572 }
573 }
574 else
575 {
576 // Normal message
577 string commAct = commArgs[1];
578 switch (commAct)
579 {
580 case "JOIN":
581 eventIrcJoin(commArgs);
582 break;
583 case "PART":
584 eventIrcPart(commArgs);
585 break;
586 case "MODE":
587 eventIrcMode(commArgs);
588 break;
589 case "NICK":
590 eventIrcNickChange(commArgs);
591 break;
592 case "KICK":
593 eventIrcKick(commArgs);
594 break;
595 case "QUIT":
596 eventIrcQuit(commArgs);
597 break;
598 case "PONG":
599 break; // that's nice
600 }
601 }
602 }
603
604 public void eventIrcJoin(string[] commArgs)
605 {
606 string IrcChannel = commArgs[2];
607 if (IrcChannel.StartsWith(":"))
608 IrcChannel = IrcChannel.Substring(1);
609 string IrcUser = commArgs[0].Split('!')[0];
610 if (m_clientReporting)
611 BroadcastSim(IrcUser, "/me joins {0}", IrcChannel);
612 }
613
614 public void eventIrcPart(string[] commArgs)
615 {
616 string IrcChannel = commArgs[2];
617 string IrcUser = commArgs[0].Split('!')[0];
618 if (m_clientReporting)
619 BroadcastSim(IrcUser, "/me parts {0}", IrcChannel);
620 }
621
622 public void eventIrcMode(string[] commArgs)
623 {
624 string UserMode = "";
625 for (int i = 3; i < commArgs.Length; i++)
626 {
627 UserMode += commArgs[i] + " ";
628 }
629
630 if (UserMode.Substring(0, 1) == ":")
631 {
632 UserMode = UserMode.Remove(0, 1);
633 }
634 }
635
636 public void eventIrcNickChange(string[] commArgs)
637 {
638 string UserOldNick = commArgs[0].Split('!')[0];
639 string UserNewNick = commArgs[2].Remove(0, 1);
640 if (m_clientReporting)
641 BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick);
642 }
643
644 public void eventIrcKick(string[] commArgs)
645 {
646 string UserKicker = commArgs[0].Split('!')[0];
647 string UserKicked = commArgs[3];
648 string IrcChannel = commArgs[2];
649 string KickMessage = "";
650 for (int i = 4; i < commArgs.Length; i++)
651 {
652 KickMessage += commArgs[i] + " ";
653 }
654 if (m_clientReporting)
655 BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
656 if (UserKicked == m_nick)
657 {
658 BroadcastSim(m_nick, "Hey, that was me!!!");
659 }
660 }
661
662 public void eventIrcQuit(string[] commArgs)
663 {
664 string IrcUser = commArgs[0].Split('!')[0];
665 string QuitMessage = "";
666
667 for (int i = 2; i < commArgs.Length; i++)
668 {
669 QuitMessage += commArgs[i] + " ";
670 }
671 if (m_clientReporting)
672 BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage);
673 }
674
675 public void Close()
676 {
677 m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing",
678 m_nick, m_ircChannel, m_server));
679 m_writer.Flush();
680
681 m_connected = false;
682 m_enabled = false;
683
684 //listener.Abort();
685 //pingSender.Abort();
686
687 m_writer.Close();
688 m_reader.Close();
689 m_stream.Close();
690 m_tcp.Close();
691 }
692
693 protected void WatchdogRun()
694 {
695 while (m_enabled)
696 {
697 if (!m_connected) Connect();
698 Thread.Sleep(15000);
699 }
700 }
701 }
702}