aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs841
1 files changed, 104 insertions, 737 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
index e992862..05718d8 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
@@ -48,10 +48,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
48 48
49 private const int DEBUG_CHANNEL = 2147483647; 49 private const int DEBUG_CHANNEL = 2147483647;
50 50
51 private string m_defaultzone = null;
52 51
53 private IRCChatModule m_irc = null; 52 private IRCConnector m_irc = null;
54 private Thread m_irc_connector = null;
55 53
56 private string m_last_leaving_user = null; 54 private string m_last_leaving_user = null;
57 private string m_last_new_user = null; 55 private string m_last_new_user = null;
@@ -61,6 +59,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
61 internal object m_syncLogout = new object(); 59 internal object m_syncLogout = new object();
62 60
63 private IConfig m_config; 61 private IConfig m_config;
62 private string m_defaultzone = null;
63 private bool m_commandsEnabled = false;
64 private int m_commandChannel = -1;
65 private bool m_relayPrivateChannels = false;
66 private int m_relayChannelOut = -1;
67 private bool m_clientReporting = true;
64 68
65 #region IRegionModule Members 69 #region IRegionModule Members
66 70
@@ -86,6 +90,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
86 return; 90 return;
87 } 91 }
88 92
93 m_commandsEnabled = m_config.GetBoolean("commands_enabled", m_commandsEnabled);
94 m_commandChannel = m_config.GetInt("commandchannel", m_commandChannel); // compat
95 m_commandChannel = m_config.GetInt("command_channel", m_commandChannel);
96
97 m_relayPrivateChannels = m_config.GetBoolean("relay_private_channels", m_relayPrivateChannels);
98 m_relayChannelOut = m_config.GetInt("relay_private_channel_out", m_relayChannelOut);
99
100 m_clientReporting = m_config.GetBoolean("report_clients", m_clientReporting);
101
89 lock (m_syncInit) 102 lock (m_syncInit)
90 { 103 {
91 104
@@ -110,43 +123,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
110 // setup IRC Relay 123 // setup IRC Relay
111 if (m_irc == null) 124 if (m_irc == null)
112 { 125 {
113 m_irc = new IRCChatModule(config); 126 m_irc = new IRCConnector(config);
114 } 127 }
115 128 m_irc.AddScene(scene);
116 if (m_irc_connector == null) 129
117 { 130 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1}, commands {2}, private channels {3}",
118 m_irc_connector = new Thread(IRCConnectRun); 131 scene.RegionInfo.RegionName, m_defaultzone,
119 m_irc_connector.Name = "IRCConnectorThread"; 132 m_commandsEnabled ? "enabled" : "not enabled",
120 m_irc_connector.IsBackground = true; 133 m_relayPrivateChannels ? "relayed" : "not relayed");
121 }
122 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1} ", scene.RegionInfo.RegionName,
123 m_defaultzone);
124 } 134 }
125 } 135 }
126 136
127 public void PostInitialise() 137 public void PostInitialise()
128 { 138 {
129 if (null == m_irc || !m_irc.Enabled) return; 139 if (null == m_irc || !m_irc.Enabled) return;
130 140 m_irc.Start();
131 try
132 {
133 //m_irc.Connect(m_scenes);
134 if (m_irc_connector == null)
135 {
136 m_irc_connector = new Thread(IRCConnectRun);
137 m_irc_connector.Name = "IRCConnectorThread";
138 m_irc_connector.IsBackground = true;
139 }
140
141 if (!m_irc_connector.IsAlive)
142 {
143 m_irc_connector.Start();
144 ThreadTracker.Add(m_irc_connector);
145 }
146 }
147 catch (Exception)
148 {
149 }
150 } 141 }
151 142
152 public void Close() 143 public void Close()
@@ -172,14 +163,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
172 163
173 #region ISimChat Members 164 #region ISimChat Members
174 165
175 public void OnSimChat(Object sender, OSChatMessage e) 166 public void OnSimChat(Object sender, OSChatMessage c)
176 { 167 {
177 m_log.DebugFormat("[IRC] heard on channel {0}: {1}", e.Channel.ToString(), e.Message); 168 // early return if nothing to forward
169 if (c.Message.Length == 0) return;
170
171 // early return if this comes from the IRC forwarder
172 if (m_irc.Equals(sender)) return;
173
174 m_log.DebugFormat("[IRC] heard on channel {0}: {1}", c.Channel, c.Message);
178 175
179 // We only want to relay stuff on channel 0 176 // check for commands coming from avatars or in-world
180 if (e.Channel == m_irc.m_commandChannel) 177 // object (if commands are enabled)
178 if (m_commandsEnabled && c.Channel == m_commandChannel)
181 { 179 {
182 string[] messages = e.Message.Split(' '); 180 string[] messages = c.Message.Split(' ');
183 string command = messages[0].ToLower(); 181 string command = messages[0].ToLower();
184 182
185 try 183 try
@@ -187,146 +185,135 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
187 switch (command) 185 switch (command)
188 { 186 {
189 case "channel": 187 case "channel":
190 m_irc.m_channel = messages[1]; 188 m_irc.IrcChannel = messages[1];
191 break; 189 break;
192 case "close": 190 case "close":
193 m_irc.Close(); 191 m_irc.Close();
194 break; 192 break;
195 case "connect": 193 case "connect":
196 m_irc.Connect(m_scenes); 194 m_irc.Connect();
197 break; 195 break;
198 case "nick": 196 case "nick":
199 m_irc.m_nick = messages[1]; 197 m_irc.Nick = messages[1];
200 break; 198 break;
201 case "port": 199 case "port":
202 m_irc.m_port = Convert.ToUInt32(messages[1]); 200 m_irc.Port = Convert.ToUInt32(messages[1]);
203 break; 201 break;
204 case "reconnect": 202 case "reconnect":
205 m_irc.Reconnect(); 203 m_irc.Reconnect();
206 break; 204 break;
207 case "server": 205 case "server":
208 m_irc.m_server = messages[1]; 206 m_irc.Server = messages[1];
209 break; 207 break;
210 case "verbosity": 208 case "client-reporting":
211 m_irc.m_verbosity = Convert.ToInt32(messages[1]); 209 m_irc.ClientReporting = Convert.ToBoolean(messages[1]);
210
212 break; 211 break;
213 case "in-channel": 212 case "in-channel":
214 m_irc.m_messageOutChannel = Convert.ToInt32(messages[1]); 213 m_irc.RelayChannel = Convert.ToInt32(messages[1]);
215 break; 214 break;
216 case "out-channel": 215 case "out-channel":
217 m_irc.m_messageInChannel = Convert.ToInt32(messages[1]); 216 m_relayChannelOut = Convert.ToInt32(messages[1]);
218 break; 217 break;
219 218
220 default: 219 default:
221 m_irc.Send(e.Message); 220 m_irc.Send(c.Message);
222 break; 221 break;
223 } 222 }
224 } 223 }
225 catch 224 catch(Exception ex)
226 { } 225 {
226 m_log.DebugFormat("[IRC] error processing in-world command channel input: {0}", ex);
227 }
227 } 228 }
228 229
229 if (e.Channel != m_irc.m_messageOutChannel) return; 230 // drop all messages coming in on a private channel,
230 if (e.Message.Length == 0) return; 231 // except if we are relaying private channels, in which
231 232 // case we drop if the private channel is not the
232 // not interested in our own babblings 233 // configured m_relayChannelOut
233 if (m_irc.Equals(sender)) return; 234 if (m_relayPrivateChannels)
235 {
236 if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL && c.Channel != m_relayChannelOut)
237 {
238 m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel);
239 return;
240 }
241 }
242 else if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL)
243 {
244 m_log.DebugFormat("[IRC] dropping message {0} on channel {1}", c, c.Channel);
245 return;
246 }
234 247
235 ScenePresence avatar = null; 248 ScenePresence avatar = null;
236 Scene scene = (Scene)e.Scene; 249 Scene scene = (Scene)c.Scene;
237 250
238 if (scene == null) 251 if (scene == null)
239 scene = m_scenes[0]; 252 scene = m_scenes[0];
240 253
241 // Filled in since it's easier than rewriting right now. 254 string fromName = c.From;
242 string fromName = e.From;
243 255
244 if (e.Sender != null) 256 if (c.Sender != null)
245 { 257 {
246 avatar = scene.GetScenePresence(e.Sender.AgentId); 258 avatar = scene.GetScenePresence(c.Sender.AgentId);
259 if (avatar != null) fromName = avatar.Name;
247 } 260 }
248 261
249 if (avatar != null) 262 if (!m_irc.Connected)
250 { 263 {
251 fromName = avatar.Name; 264 m_log.WarnFormat("[IRC] IRCConnector not connected: dropping message from {0}", fromName);
265 return;
252 } 266 }
253 267
254 // Try to reconnect to server if not connected 268 if (null != avatar)
255 if (m_irc.Enabled && !m_irc.Connected)
256 { 269 {
257 // In a non-blocking way. Eventually the connector will get it started 270 string msg = c.Message;
258 try 271 if (msg.StartsWith("/me "))
272 msg = String.Format("{0} {1}", fromName, c.Message.Substring(4));
273
274 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, msg);
275 }
276 else
277 {
278 //Message came from an object
279 char[] splits = { ',' };
280 string[] tokens = c.Message.Split(splits,3); // This is certainly wrong
281 if (tokens.Length == 3)
259 { 282 {
260 if (m_irc_connector == null) 283 if (tokens[0] == m_irc.m_accessPassword) // This is my really simple check
261 { 284 {
262 m_irc_connector = new Thread(IRCConnectRun); 285 m_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]);
263 m_irc_connector.Name = "IRCConnectorThread"; 286 m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]);
264 m_irc_connector.IsBackground = true;
265 } 287 }
266 288 else
267 if (!m_irc_connector.IsAlive)
268 { 289 {
269 m_irc_connector.Start(); 290 m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword);
270 ThreadTracker.Add(m_irc_connector);
271 } 291 }
272 } 292 }
273 catch (Exception)
274 {
275 }
276 } 293 }
277
278 if (e.Message.StartsWith("/me ") && (null != avatar))
279 e.Message = String.Format("{0} {1}", fromName, e.Message.Substring(4));
280
281 // this is to keep objects from talking to IRC
282 if (m_irc.Connected && (avatar != null))
283 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, e.Message);
284
285 // Handle messages from objects
286 if (m_irc.Connected && (null == avatar))
287 {
288 //Message came from an object
289 char[] splits = { ',' };
290 string[] tokens = e.Message.Split(splits,3); // This is certainly wrong
291 if (tokens.Length == 3)
292 {
293 if (tokens[0] == m_irc.m_accessPassword) // This is my really simple check
294 {
295 m_log.DebugFormat("[IRC] message from object {0}, {1}", tokens[0], tokens[1]);
296 m_irc.PrivMsg(tokens[1], scene.RegionInfo.RegionName, tokens[2]);
297 }
298 else
299 {
300 m_log.WarnFormat("[IRC] prim security key mismatch <{0}> not <{1}>", tokens[0], m_irc.m_accessPassword);
301 }
302 }
303 }
304 } 294 }
305
306 #endregion 295 #endregion
307 296
308 public void OnNewClient(IClientAPI client) 297 public void OnNewClient(IClientAPI client)
309 { 298 {
310 try 299 try
311 { 300 {
312 // client.OnChatFromViewer += OnSimChat;
313 client.OnLogout += OnClientLoggedOut; 301 client.OnLogout += OnClientLoggedOut;
314 client.OnConnectionClosed += OnClientLoggedOut; 302 client.OnConnectionClosed += OnClientLoggedOut;
315 303
316 if (client.Name != m_last_new_user) 304 if (client.Name != m_last_new_user)
317 { 305 {
318 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 1)) 306 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
319 { 307 {
320 m_log.DebugFormat("[IRC] {0} logging on", client.Name); 308 m_log.DebugFormat("[IRC] {0} logging on", client.Name);
321 m_irc.PrivMsg(m_irc.Nick, "Sim", 309 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging on", client.Name));
322 String.Format("notices {0} logging on", client.Name));
323 } 310 }
324 m_last_new_user = client.Name; 311 m_last_new_user = client.Name;
325 } 312 }
326 } 313 }
327 catch (Exception ex) 314 catch (Exception ex)
328 { 315 {
329 m_log.Error("[IRC]: NewClient exception trap:" + ex.ToString()); 316 m_log.Error("[IRC]: OnNewClient exception trap:" + ex.ToString());
330 } 317 }
331 } 318 }
332 319
@@ -334,7 +321,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
334 { 321 {
335 try 322 try
336 { 323 {
337 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 2)) 324 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
338 { 325 {
339 string regionName = presence.Scene.RegionInfo.RegionName; 326 string regionName = presence.Scene.RegionInfo.RegionName;
340 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); 327 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
@@ -351,7 +338,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
351 { 338 {
352 try 339 try
353 { 340 {
354 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 2)) 341 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
355 { 342 {
356 string regionName = presence.Scene.RegionInfo.RegionName; 343 string regionName = presence.Scene.RegionInfo.RegionName;
357 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname); 344 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
@@ -371,16 +358,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
371 { 358 {
372 try 359 try
373 { 360 {
374 if ((m_irc.Enabled) && (m_irc.Connected) && (m_irc.m_verbosity >= 1)) 361 if ((m_irc.Enabled) && (m_irc.Connected) && (m_clientReporting))
375 { 362 {
376 // handles simple case. May not work for hundred connecting in per second. 363 // handles simple case. May not work for
377 // and the NewClients calles getting interleved 364 // hundred connecting in per second. and
378 // but filters out multiple reports 365 // OnNewClients calle getting interleaved but
366 // filters out multiple reports
379 if (client.Name != m_last_leaving_user) 367 if (client.Name != m_last_leaving_user)
380 { 368 {
381 // Console.WriteLine("Avatar was seen logging out.");
382 // Console.ReadLine();
383 // Console.WriteLine();
384 m_last_leaving_user = client.Name; 369 m_last_leaving_user = client.Name;
385 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", client.Name)); 370 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} logging out", client.Name));
386 m_log.InfoFormat("[IRC]: {0} logging out", client.Name); 371 m_log.InfoFormat("[IRC]: {0} logging out", client.Name);
@@ -396,623 +381,5 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
396 } 381 }
397 } 382 }
398 } 383 }
399
400 // if IRC is enabled then just keep trying using a monitor thread
401 public void IRCConnectRun()
402 {
403 while (m_irc.Enabled)
404 {
405 if (!m_irc.Connected)
406 {
407 m_irc.Connect(m_scenes);
408 }
409 Thread.Sleep(15000);
410 }
411 }
412 }
413
414 internal class IRCChatModule
415 {
416 #region ErrorReplies enum
417
418 public enum ErrorReplies
419 {
420 NotRegistered = 451, // ":You have not registered"
421 NicknameInUse = 433 // "<nick> :Nickname is already in use"
422 }
423
424 #endregion
425
426 #region Replies enum
427
428 public enum Replies
429 {
430 MotdStart = 375, // ":- <server> Message of the day - "
431 Motd = 372, // ":- <text>"
432 EndOfMotd = 376 // ":End of /MOTD command"
433 }
434
435 #endregion
436
437 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
438 private Thread listener;
439
440 private string m_basenick = null;
441 public string m_channel = null;
442 private bool m_nrnick = false;
443 private bool m_connected = false;
444 private bool m_enabled = false;
445 public int m_commandChannel = -1;
446
447 public int m_verbosity = 1;
448 public int m_messageOutChannel = 0; // Will be the message channel we listen to
449 public int m_messageInChannel = 0; // Will be the message channel where we forward msgs
450 public string m_accessPassword = "badkitty";
451 public bool m_useWorldComm = false; // true if we want chat to be localized, false if we want to broadcast to the entire region
452
453 public List<Scene> m_last_scenes = null;
454 public string m_nick = null;
455 public uint m_port = 6668;
456 private string m_privmsgformat = "PRIVMSG {0} :<{1} in {2}>: {3}";
457 private StreamReader m_reader;
458 private List<Scene> m_scenes = null;
459 public string m_server = null;
460
461 private NetworkStream m_stream = null;
462 internal object m_syncConnect = new object();
463 private TcpClient m_tcp;
464 private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot";
465 private StreamWriter m_writer;
466
467 private Thread pingSender;
468
469 public IRCChatModule(IConfigSource config)
470 {
471 m_nick = "OSimBot" + Util.RandomClass.Next(1, 99);
472 m_tcp = null;
473 m_writer = null;
474 m_reader = null;
475
476 // configuration in OpenSim.ini
477 // [IRC]
478 // server = chat.freenode.net
479 // nick = OSimBot_mysim
480 // nicknum = true
481 // ;nicknum set to true appends a 2 digit random number to the nick
482 // ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot
483 // ; username is the IRC command line sent
484 // ; USER <irc_user> <visible=8,invisible=0> * : <IRC_realname>
485 // channel = #opensim-regions
486 // port = 6667
487 // ;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message
488 // ;for <bot>:<user in region> :<message>
489 // ;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}"
490 // ;for <bot>:<message> - <user of region> :
491 // ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}"
492 // ;for <bot>:<message> - from <user> :
493 // ;msgformat = "PRIVMSG {0} : {3} - from {1}"
494 // Traps I/O disconnects so it does not crash the sim
495 // Trys to reconnect if disconnected and someone says something
496 // Tells IRC server "QUIT" when doing a close (just to be nice)
497 // Default port back to 6667
498
499 try
500 {
501 m_server = config.Configs["IRC"].GetString("server");
502 m_nick = config.Configs["IRC"].GetString("nick");
503 m_basenick = m_nick;
504 m_nrnick = config.Configs["IRC"].GetBoolean("nicknum", true);
505 m_channel = config.Configs["IRC"].GetString("channel");
506 m_port = (uint)config.Configs["IRC"].GetInt("port", (int)m_port);
507 m_user = config.Configs["IRC"].GetString("username", m_user);
508 m_privmsgformat = config.Configs["IRC"].GetString("msgformat", m_privmsgformat);
509 m_commandChannel = config.Configs["IRC"].GetInt("commandchannel", m_commandChannel);
510
511 m_verbosity = config.Configs["IRC"].GetInt("verbosity", m_verbosity);
512 m_messageOutChannel = config.Configs["IRC"].GetInt("outchannel", m_messageOutChannel);
513 m_messageInChannel = config.Configs["IRC"].GetInt("inchannel", m_messageInChannel);
514 m_accessPassword = config.Configs["IRC"].GetString("access_password",m_accessPassword);
515 m_useWorldComm = config.Configs["IRC"].GetBoolean("useworldcomm", m_useWorldComm);
516
517 if (m_server != null && m_nick != null && m_channel != null)
518 {
519 if (m_nrnick == true)
520 {
521 m_nick = m_nick + Util.RandomClass.Next(1, 99);
522 }
523 m_enabled = true;
524 }
525 }
526 catch (Exception ex)
527 {
528 m_log.Error("[IRC]: Incomplete IRC configuration, skipping IRC bridge configuration");
529 m_log.DebugFormat("[IRC] Incomplete IRC configuration: {0}", ex.ToString());
530 }
531 }
532
533 public bool Enabled
534 {
535 get { return m_enabled; }
536 }
537
538 public bool Connected
539 {
540 get { return m_connected; }
541 }
542
543 public string Nick
544 {
545 get { return m_nick; }
546 }
547
548 public bool Connect(List<Scene> scenes)
549 {
550 lock (m_syncConnect)
551 {
552 try
553 {
554 if (m_connected) return true;
555
556 m_scenes = scenes;
557 if (m_last_scenes == null)
558 {
559 m_last_scenes = scenes;
560 }
561
562 m_tcp = new TcpClient(m_server, (int)m_port);
563 m_stream = m_tcp.GetStream();
564 m_reader = new StreamReader(m_stream);
565 m_writer = new StreamWriter(m_stream);
566
567 m_log.DebugFormat("[IRC]: Connected to {0}:{1}", m_server, m_port);
568
569 pingSender = new Thread(new ThreadStart(PingRun));
570 pingSender.Name = "PingSenderThread";
571 pingSender.IsBackground = true;
572 pingSender.Start();
573 ThreadTracker.Add(pingSender);
574
575 listener = new Thread(new ThreadStart(ListenerRun));
576 listener.Name = "IRCChatModuleListenerThread";
577 listener.IsBackground = true;
578 listener.Start();
579 ThreadTracker.Add(listener);
580
581 m_writer.WriteLine(m_user);
582 m_writer.Flush();
583 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
584 m_writer.Flush();
585 m_writer.WriteLine(String.Format("JOIN {0}", m_channel));
586 m_writer.Flush();
587 m_log.Info("[IRC]: Connection fully established");
588 m_connected = true;
589 }
590 catch (Exception e)
591 {
592 m_log.ErrorFormat("[IRC] cannot connect to {0}:{1}: {2}",
593 m_server, m_port, e.Message);
594 }
595 m_log.Debug("[IRC] Connected");
596 return m_connected;
597 }
598 }
599
600 public void Reconnect()
601 {
602 m_connected = false;
603 try
604 {
605 listener.Abort();
606 pingSender.Abort();
607 m_writer.Close();
608 m_reader.Close();
609 m_tcp.Close();
610 }
611 catch (Exception)
612 {
613 }
614
615 if (m_enabled)
616 {
617 Connect(m_last_scenes);
618 }
619 }
620
621 public void PrivMsg(string from, string region, string msg)
622 {
623 m_log.DebugFormat("[IRC] Sending message to IRC from {0}: {1}", from, msg);
624
625 // One message to the IRC server
626 try
627 {
628 m_writer.WriteLine(m_privmsgformat, m_channel, from, region, msg);
629 m_writer.Flush();
630 m_log.InfoFormat("[IRC]: PrivMsg {0} in {1}: {2}", from, region, msg);
631 }
632 catch (IOException)
633 {
634 m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)");
635 Reconnect();
636 }
637 catch (Exception ex)
638 {
639 m_log.ErrorFormat("[IRC]: PrivMsg exception trap: {0}", ex.ToString());
640 }
641 }
642
643 public void Send(string msg)
644 {
645 try
646 {
647 m_writer.WriteLine(msg);
648 m_writer.Flush();
649 m_log.Info("IRC: Sent command string: " + msg);
650 }
651 catch (IOException)
652 {
653 m_log.Error("[IRC]: Disconnected from IRC server.(PrivMsg)");
654 Reconnect();
655 }
656 catch (Exception ex)
657 {
658 m_log.ErrorFormat("[IRC]: PrivMsg exception trap: {0}", ex.ToString());
659 }
660
661 }
662
663
664 private Dictionary<string, string> ExtractMsg(string input)
665 {
666 //examines IRC commands and extracts any private messages
667 // which will then be reboadcast in the Sim
668
669 m_log.Info("[IRC]: ExtractMsg: " + input);
670 Dictionary<string, string> result = null;
671 //string regex = @":(?<nick>\w*)!~(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
672 string regex = @":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
673 Regex RE = new Regex(regex, RegexOptions.Multiline);
674 MatchCollection matches = RE.Matches(input);
675
676 // Get some direct matches $1 $4 is a
677 if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
678 {
679 m_log.Info("[IRC]: Number of matches: " + matches.Count);
680 if (matches.Count > 0)
681 {
682 m_log.Info("[IRC]: Number of groups: " + matches[0].Groups.Count);
683 }
684 return null;
685 }
686
687 result = new Dictionary<string, string>();
688 result.Add("nick", matches[0].Groups[1].Value);
689 result.Add("user", matches[0].Groups[2].Value);
690 result.Add("channel", matches[0].Groups[3].Value);
691 result.Add("msg", matches[0].Groups[4].Value);
692
693 return result;
694 }
695
696 public void PingRun()
697 {
698 // IRC keep alive thread
699 // send PING ever 15 seconds
700 while (m_enabled)
701 {
702 try
703 {
704 if (m_connected == true)
705 {
706 m_writer.WriteLine(String.Format("PING :{0}", m_server));
707 m_writer.Flush();
708 Thread.Sleep(15000);
709 }
710 }
711 catch (IOException)
712 {
713 if (m_enabled)
714 {
715 m_log.Error("[IRC]: Disconnected from IRC server.(PingRun)");
716 Reconnect();
717 }
718 }
719 catch (Exception ex)
720 {
721 m_log.ErrorFormat("[IRC]: PingRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
722 }
723 }
724 }
725
726 static private Vector3 pos = new Vector3(128, 128, 20);
727 public void ListenerRun()
728 {
729 string inputLine;
730
731 while (m_enabled)
732 {
733 try
734 {
735 while ((m_connected == true) && ((inputLine = m_reader.ReadLine()) != null))
736 {
737 m_log.Info("[IRC]: " + inputLine);
738 if (inputLine.Contains(m_channel))
739 {
740 Dictionary<string, string> data = ExtractMsg(inputLine);
741 // Any chat ???
742 if (data != null)
743 {
744 OSChatMessage c = new OSChatMessage();
745 c.Message = data["msg"];
746 c.Type = ChatTypeEnum.Say;
747 c.Channel = m_messageInChannel;
748 c.Position = pos;
749 c.From = data["nick"];
750 c.Sender = null;
751 c.SenderUUID = UUID.Zero;
752
753 // is message "\001ACTION foo
754 // bar\001"? -> "/me foo bar"
755 if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION"))
756 c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9));
757
758 m_log.DebugFormat("[IRC] ListenerRun from: {0}, {1}", c.From, c.Message);
759
760 foreach (Scene scene in m_scenes)
761 {
762 if (m_useWorldComm)
763 {
764 IWorldComm wComm = scene.RequestModuleInterface<IWorldComm>();
765 wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, c.From, UUID.Zero, c.Message);
766 }
767 else
768 {
769 c.Scene = scene;
770 scene.EventManager.TriggerOnChatBroadcast(this, c);
771 }
772 }
773 }
774
775 Thread.Sleep(150);
776 continue;
777 }
778
779 ProcessIRCCommand(inputLine);
780 Thread.Sleep(150);
781 }
782 }
783 catch (IOException)
784 {
785 if (m_enabled)
786 {
787 m_log.Error("[IRC]: ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)");
788 Reconnect();
789 }
790 }
791 catch (Exception ex)
792 {
793 m_log.ErrorFormat("[IRC]: ListenerRun exception trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
794 }
795 }
796 }
797
798 public void BroadcastSim(string sender, string format, params string[] args)
799 {
800 try
801 {
802 OSChatMessage c = new OSChatMessage();
803 c.From = sender;
804 c.Message = String.Format(format, args);
805 c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say;
806 c.Channel = m_messageInChannel;
807 c.Position = new Vector3(128, 128, 20);
808 c.Sender = null;
809 c.SenderUUID = UUID.Zero;
810
811 m_log.DebugFormat("[IRC] BroadcastSim from {0}: {1}", c.From, c.Message);
812
813 foreach (Scene m_scene in m_scenes)
814 {
815 c.Scene = m_scene;
816 // m_scene.EventManager.TriggerOnChatBroadcast(this, c);
817 // m_scene.EventManager.TriggerOnChatFromWorld(this, c);
818 IWorldComm wComm = m_scene.RequestModuleInterface<IWorldComm>();
819 wComm.DeliverMessage(ChatTypeEnum.Region, m_messageInChannel, sender, UUID.Zero, c.Message);
820 //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
821 //wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text);
822
823 }
824 }
825 catch (Exception ex) // IRC gate should not crash Sim
826 {
827 m_log.ErrorFormat("[IRC]: BroadcastSim Exception Trap: {0}\n{1}", ex.ToString(), ex.StackTrace);
828 }
829 }
830
831 public void ProcessIRCCommand(string command)
832 {
833 m_log.Debug("[IRC]: ProcessIRCCommand:" + command);
834
835 string[] commArgs = new string[command.Split(' ').Length];
836 string c_server = m_server;
837
838 commArgs = command.Split(' ');
839 if (commArgs[0].Substring(0, 1) == ":")
840 {
841 commArgs[0] = commArgs[0].Remove(0, 1);
842 }
843
844 if (commArgs[1] == "002")
845 {
846 // fetch the correct servername
847 // ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/...
848 // irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch
849
850 c_server = (commArgs[6].Split('['))[0];
851 m_server = c_server;
852 }
853
854 if (commArgs[0] == "ERROR")
855 {
856 m_log.ErrorFormat("[IRC]: IRC SERVER ERROR: {0}", command);
857 }
858
859 if (commArgs[0] == "PING")
860 {
861 string p_reply = "";
862
863 for (int i = 1; i < commArgs.Length; i++)
864 {
865 p_reply += commArgs[i] + " ";
866 }
867
868 m_writer.WriteLine(String.Format("PONG {0}", p_reply));
869 m_writer.Flush();
870 }
871 else if (commArgs[0] == c_server)
872 {
873 // server message
874 try
875 {
876 Int32 commandCode = Int32.Parse(commArgs[1]);
877 switch (commandCode)
878 {
879 case (int)ErrorReplies.NicknameInUse:
880 // Gen a new name
881 m_nick = m_basenick + Util.RandomClass.Next(1, 99);
882 m_log.ErrorFormat("[IRC]: IRC SERVER reports NicknameInUse, trying {0}", m_nick);
883 // Retry
884 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
885 m_writer.Flush();
886 m_writer.WriteLine(String.Format("JOIN {0}", m_channel));
887 m_writer.Flush();
888 break;
889 case (int)ErrorReplies.NotRegistered:
890 break;
891 case (int)Replies.EndOfMotd:
892 break;
893 }
894 }
895 catch (Exception)
896 {
897 }
898 }
899 else
900 {
901 // Normal message
902 string commAct = commArgs[1];
903 switch (commAct)
904 {
905 case "JOIN":
906 eventIrcJoin(commArgs);
907 break;
908 case "PART":
909 eventIrcPart(commArgs);
910 break;
911 case "MODE":
912 eventIrcMode(commArgs);
913 break;
914 case "NICK":
915 eventIrcNickChange(commArgs);
916 break;
917 case "KICK":
918 eventIrcKick(commArgs);
919 break;
920 case "QUIT":
921 eventIrcQuit(commArgs);
922 break;
923 case "PONG":
924 break; // that's nice
925 }
926 }
927 }
928
929 public void eventIrcJoin(string[] commArgs)
930 {
931 string IrcChannel = commArgs[2];
932 if (IrcChannel.StartsWith(":"))
933 IrcChannel = IrcChannel.Substring(1);
934 string IrcUser = commArgs[0].Split('!')[0];
935 if (m_verbosity >= 1)
936 BroadcastSim(IrcUser, "/me joins {0}", IrcChannel);
937 }
938
939 public void eventIrcPart(string[] commArgs)
940 {
941 string IrcChannel = commArgs[2];
942 string IrcUser = commArgs[0].Split('!')[0];
943 if (m_verbosity >= 2)
944 BroadcastSim(IrcUser, "/me parts {0}", IrcChannel);
945 }
946
947 public void eventIrcMode(string[] commArgs)
948 {
949 string UserMode = "";
950 for (int i = 3; i < commArgs.Length; i++)
951 {
952 UserMode += commArgs[i] + " ";
953 }
954
955 if (UserMode.Substring(0, 1) == ":")
956 {
957 UserMode = UserMode.Remove(0, 1);
958 }
959 }
960
961 public void eventIrcNickChange(string[] commArgs)
962 {
963 string UserOldNick = commArgs[0].Split('!')[0];
964 string UserNewNick = commArgs[2].Remove(0, 1);
965 if (m_verbosity >= 2)
966 BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick);
967 }
968
969 public void eventIrcKick(string[] commArgs)
970 {
971 string UserKicker = commArgs[0].Split('!')[0];
972 string UserKicked = commArgs[3];
973 string IrcChannel = commArgs[2];
974 string KickMessage = "";
975 for (int i = 4; i < commArgs.Length; i++)
976 {
977 KickMessage += commArgs[i] + " ";
978 }
979 if (m_verbosity >= 1)
980 BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
981 if (UserKicked == m_nick)
982 {
983 BroadcastSim(m_nick, "Hey, that was me!!!");
984 }
985 }
986
987 public void eventIrcQuit(string[] commArgs)
988 {
989 string IrcUser = commArgs[0].Split('!')[0];
990 string QuitMessage = "";
991
992 for (int i = 2; i < commArgs.Length; i++)
993 {
994 QuitMessage += commArgs[i] + " ";
995 }
996 if (m_verbosity >= 1)
997 BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage);
998 }
999
1000 public void Close()
1001 {
1002 m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing",
1003 m_nick, m_channel, m_server));
1004 m_writer.Flush();
1005
1006 m_connected = false;
1007 m_enabled = false;
1008
1009 //listener.Abort();
1010 //pingSender.Abort();
1011
1012 m_writer.Close();
1013 m_reader.Close();
1014 m_stream.Close();
1015 m_tcp.Close();
1016 }
1017 } 384 }
1018} 385}