aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/Chat/XIRCConnector.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar/Chat/XIRCConnector.cs')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/XIRCConnector.cs842
1 files changed, 842 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/XIRCConnector.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/XIRCConnector.cs
new file mode 100644
index 0000000..b9203a8
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/XIRCConnector.cs
@@ -0,0 +1,842 @@
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.Timers;
30using System.Collections.Generic;
31using System.IO;
32using System.Net.Sockets;
33using System.Reflection;
34using System.Text.RegularExpressions;
35using System.Threading;
36using OpenMetaverse;
37using log4net;
38using Nini.Config;
39using OpenSim.Framework;
40using OpenSim.Region.Environment.Interfaces;
41using OpenSim.Region.Environment.Scenes;
42
43namespace OpenSim.Region.Environment.Modules.Avatar.Chat
44{
45 public class XIRCConnector
46 {
47
48 #region Global (static) state
49
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51
52 // Local constants
53
54 private static readonly Vector3 CenterOfRegion = new Vector3(128, 128, 20);
55 private static readonly char[] CS_SPACE = { ' ' };
56
57 private const int WD_INTERVAL = 1000; // base watchdog interval
58 private static int PING_PERIOD = 15; // WD intervals per PING
59 private static int ICCD_PERIOD = 10; // WD intervals between Connects
60
61 private static int _idk_ = 0; // core connector identifier
62 private static int _pdk_ = 0; // ping interval counter
63 private static int _icc_ = 0; // IRC connect counter
64
65 // List of configured connectors
66
67 private static List<XIRCConnector> m_connectors = new List<XIRCConnector>();
68
69 // Watchdog state
70
71 private static System.Timers.Timer m_watchdog = null;
72
73 static XIRCConnector()
74 {
75 m_log.DebugFormat("[IRC-Connector]: Static initialization started");
76 m_watchdog = new System.Timers.Timer(WD_INTERVAL);
77 m_watchdog.Elapsed += new ElapsedEventHandler(WatchdogHandler);
78 m_watchdog.AutoReset = true;
79 m_watchdog.Start();
80 m_log.DebugFormat("[IRC-Connector]: Static initialization complete");
81 }
82
83 #endregion
84
85 #region Instance state
86
87 // Connector identity
88
89 internal int idn = _idk_++;
90
91 // How many regions depend upon this connection
92 // This count is updated by the ChannelState object and reflects the sum
93 // of the region clients associated with the set of associated channel
94 // state instances. That's why it cannot be managed here.
95
96 internal int depends = 0;
97
98 // Working threads
99
100 private Thread m_listener = null;
101
102 private Object msyncConnect = new Object();
103
104 internal bool m_randomizeNick = true; // add random suffix
105 internal string m_baseNick = null; // base name for randomizing
106 internal string m_nick = null; // effective nickname
107
108 public string Nick // Public property
109 {
110 get { return m_nick; }
111 set { m_nick = value; }
112 }
113
114 private bool m_enabled = false; // connector enablement
115 public bool Enabled
116 {
117 get { return m_enabled; }
118 }
119
120 private bool m_connected = false; // connection status
121 public bool Connected
122 {
123 get { return m_connected; }
124 }
125
126 private string m_ircChannel; // associated channel id
127 public string IrcChannel
128 {
129 get { return m_ircChannel; }
130 set { m_ircChannel = value; }
131 }
132
133 private uint m_port = 6667; // session port
134 public uint Port
135 {
136 get { return m_port; }
137 set { m_port = value; }
138 }
139
140 private string m_server = null; // IRC server name
141 public string Server
142 {
143 get { return m_server; }
144 set { m_server = value; }
145 }
146
147 private string m_user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot";
148 public string User
149 {
150 get { return m_user; }
151 }
152
153 // Network interface
154
155 private TcpClient m_tcp;
156 private NetworkStream m_stream = null;
157 private StreamReader m_reader;
158 private StreamWriter m_writer;
159
160 // Channel characteristic info (if available)
161
162 internal string usermod = String.Empty;
163 internal string chanmod = String.Empty;
164 internal string version = String.Empty;
165 internal bool motd = false;
166
167 #endregion
168
169 #region connector instance management
170
171 internal XIRCConnector(ChannelState cs)
172 {
173
174 // Prepare network interface
175
176 m_tcp = null;
177 m_writer = null;
178 m_reader = null;
179
180 // Setup IRC session parameters
181
182 m_server = cs.Server;
183 m_baseNick = cs.BaseNickname;
184 m_randomizeNick = cs.RandomizeNickname;
185 m_ircChannel = cs.IrcChannel;
186 m_port = (uint) cs.Port;
187 m_user = cs.User;
188
189 if (m_watchdog == null)
190 {
191 // Non-differentiating
192
193 ICCD_PERIOD = cs.ConnectDelay;
194 PING_PERIOD = cs.PingDelay;
195
196 // Smaller values are not reasonable
197
198 if (ICCD_PERIOD < 5)
199 ICCD_PERIOD = 5;
200
201 if (PING_PERIOD < 5)
202 PING_PERIOD = 5;
203
204 _icc_ = ICCD_PERIOD; // get started right away!
205
206 }
207
208 // The last line of defense
209
210 if (m_server == null || m_baseNick == null || m_ircChannel == null || m_user == null)
211 throw new Exception("Invalid connector configuration");
212
213 // Generate an initial nickname if randomizing is enabled
214
215 if (m_randomizeNick)
216 {
217 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
218 }
219
220 // Add the newly created connector to the known connectors list
221
222 m_connectors.Add(this);
223
224 m_log.InfoFormat("[IRC-Connector-{0}]: Initialization complete", idn);
225
226 }
227
228 ~XIRCConnector()
229 {
230 m_watchdog.Stop();
231 Close();
232 }
233
234 // Mark the connector as connectable. Harmless if already enabled.
235
236 public void Open()
237 {
238 if (!m_enabled)
239 {
240
241 m_connectors.Add(this);
242 m_enabled = true;
243
244 if (!Connected)
245 {
246 Connect();
247 }
248
249 }
250 }
251
252 // Only close the connector if the dependency count is zero.
253
254 public void Close()
255 {
256
257 m_log.InfoFormat("[IRC-Connector-{0}] Closing", idn);
258
259 lock (msyncConnect)
260 {
261
262 if ((depends == 0) && Enabled)
263 {
264
265 m_enabled = false;
266
267 if (Connected)
268 {
269 m_log.DebugFormat("[IRC-Connector-{0}] Closing interface", idn);
270
271 // Cleanup the IRC session
272
273 try
274 {
275 m_writer.WriteLine(String.Format("QUIT :{0} to {1} wormhole to {2} closing",
276 m_nick, m_ircChannel, m_server));
277 m_writer.Flush();
278 }
279 catch (Exception) {}
280
281
282 m_connected = false;
283
284 try { m_writer.Close(); } catch (Exception) {}
285 try { m_reader.Close(); } catch (Exception) {}
286 try { m_stream.Close(); } catch (Exception) {}
287 try { m_tcp.Close(); } catch (Exception) {}
288
289 }
290
291 m_connectors.Remove(this);
292
293 }
294 }
295
296 m_log.InfoFormat("[IRC-Connector-{0}] Closed", idn);
297
298 }
299
300 #endregion
301
302 #region session management
303
304 // Connect to the IRC server. A connector should always be connected, once enabled
305
306 public void Connect()
307 {
308
309 if (!m_enabled)
310 return;
311
312 // Delay until next WD cycle if this is too close to the last start attempt
313
314 while (_icc_ < ICCD_PERIOD)
315 return;
316
317 m_log.DebugFormat("[IRC-Connector-{0}]: Connection request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel);
318
319 lock (msyncConnect)
320 {
321
322 _icc_ = 0;
323
324 try
325 {
326 if (m_connected) return;
327
328 m_connected = true;
329
330 m_tcp = new TcpClient(m_server, (int)m_port);
331 m_stream = m_tcp.GetStream();
332 m_reader = new StreamReader(m_stream);
333 m_writer = new StreamWriter(m_stream);
334
335 m_log.InfoFormat("[IRC-Connector-{0}]: Connected to {1}:{2}", idn, m_server, m_port);
336
337 m_listener = new Thread(new ThreadStart(ListenerRun));
338 m_listener.Name = "IRCConnectorListenerThread";
339 m_listener.IsBackground = true;
340 m_listener.Start();
341 ThreadTracker.Add(m_listener);
342
343 // This is the message order recommended by RFC 2812
344
345 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
346 m_writer.Flush();
347 m_writer.WriteLine(m_user);
348 m_writer.Flush();
349 m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel));
350 m_writer.Flush();
351
352 m_log.InfoFormat("[IRC-Connector-{0}]: {1} has joined {2}", idn, m_nick, m_ircChannel);
353 m_log.InfoFormat("[IRC-Connector-{0}] Connected", idn);
354
355 }
356 catch (Exception e)
357 {
358 m_log.ErrorFormat("[IRC-Connector-{0}] cannot connect {1} to {2}:{3}: {4}",
359 idn, m_nick, m_server, m_port, e.Message);
360 m_connected = false;
361 }
362
363 }
364
365 return;
366
367 }
368
369 // Reconnect is used to force a re-cycle of the IRC connection. Should generally
370 // be a transparent event
371
372 public void Reconnect()
373 {
374
375 m_log.DebugFormat("[IRC-Connector-{0}]: Reconnect request for {1} on {2}:{3}", idn, m_nick, m_server, m_ircChannel);
376
377 // Don't do this if a Connect is in progress...
378
379 lock (msyncConnect)
380 {
381
382 if (m_connected)
383 {
384 m_log.InfoFormat("[IRC-Connector-{0}] Resetting connector", idn);
385
386 // Mark as disconnected. This will allow the listener thread
387 // to exit if still in-flight.
388
389
390 // The listener thread is not aborted - it *might* actually be
391 // the thread that is running the Reconnect! Instead just close
392 // the socket and it will disappear of its own accord, once this
393 // processing is completed.
394
395 try { m_writer.Close(); } catch (Exception) {}
396 try { m_reader.Close(); } catch (Exception) {}
397 try { m_tcp.Close(); } catch (Exception) {}
398
399 m_connected = false;
400
401 }
402
403 }
404
405 Connect();
406
407 }
408
409 #endregion
410
411 #region Outbound (to-IRC) message handlers
412
413 public void PrivMsg(string pattern, string from, string region, string msg)
414 {
415
416 m_log.DebugFormat("[IRC-Connector-{0}] PrivMsg to IRC from {1}: <{2}>", idn, from,
417 String.Format(pattern, m_ircChannel, from, region, msg));
418
419 // One message to the IRC server
420
421 try
422 {
423 m_writer.WriteLine(pattern, m_ircChannel, from, region, msg);
424 m_writer.Flush();
425 m_log.DebugFormat("[IRC-Connector-{0}]: PrivMsg from {1} in {2}: {3}", idn, from, region, msg);
426 }
427 catch (IOException)
428 {
429 m_log.ErrorFormat("[IRC-Connector-{0}]: PrivMsg I/O Error: disconnected from IRC server", idn);
430 Reconnect();
431 }
432 catch (Exception ex)
433 {
434 m_log.ErrorFormat("[IRC-Connector-{0}]: PrivMsg exception : {1}", idn, ex.Message);
435 m_log.Debug(ex);
436 }
437
438 }
439
440 public void Send(string msg)
441 {
442
443 m_log.DebugFormat("[IRC-Connector-{0}] Send to IRC : <{1}>", idn, msg);
444
445 try
446 {
447 m_writer.WriteLine(msg);
448 m_writer.Flush();
449 m_log.DebugFormat("[IRC-Connector-{0}] Sent command string: {1}", idn, msg);
450 }
451 catch (IOException)
452 {
453 m_log.ErrorFormat("[IRC-Connector-{0}] Disconnected from IRC server.(Send)", idn);
454 Reconnect();
455 }
456 catch (Exception ex)
457 {
458 m_log.ErrorFormat("[IRC-Connector-{0}] Send exception trap: {0}", idn, ex.Message);
459 m_log.Debug(ex);
460 }
461
462 }
463
464 #endregion
465
466 public void ListenerRun()
467 {
468 string inputLine;
469
470 try
471 {
472 while (m_enabled && m_connected)
473 {
474
475 if ((inputLine = m_reader.ReadLine()) == null)
476 throw new Exception("Listener input socket closed");
477
478 // m_log.Info("[IRCConnector]: " + inputLine);
479
480 if (inputLine.Contains("PRIVMSG"))
481 {
482
483 Dictionary<string, string> data = ExtractMsg(inputLine);
484
485 // Any chat ???
486 if (data != null)
487 {
488
489 OSChatMessage c = new OSChatMessage();
490 c.Message = data["msg"];
491 c.Type = ChatTypeEnum.Region;
492 c.Position = CenterOfRegion;
493 c.From = data["nick"];
494 c.Sender = null;
495 c.SenderUUID = UUID.Zero;
496
497 // Is message "\001ACTION foo bar\001"?
498 // Then change to: "/me foo bar"
499
500 if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION"))
501 c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9));
502
503 ChannelState.OSChat(this, c, false);
504
505 }
506
507 }
508 else
509 {
510 ProcessIRCCommand(inputLine);
511 }
512 }
513 }
514 catch (Exception /*e*/)
515 {
516 // m_log.ErrorFormat("[IRC-Connector-{0}]: ListenerRun exception trap: {1}", idn, e.Message);
517 // m_log.Debug(e);
518 }
519
520 if (m_enabled) Reconnect();
521
522 }
523
524 private Dictionary<string, string> ExtractMsg(string input)
525 {
526 //examines IRC commands and extracts any private messages
527 // which will then be reboadcast in the Sim
528
529 // m_log.InfoFormat("[IRC-Connector-{0}]: ExtractMsg: {1}", idn, input);
530
531 Dictionary<string, string> result = null;
532 string regex = @":(?<nick>[\w-]*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
533 Regex RE = new Regex(regex, RegexOptions.Multiline);
534 MatchCollection matches = RE.Matches(input);
535
536 // Get some direct matches $1 $4 is a
537 if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
538 {
539 // m_log.Info("[IRCConnector]: Number of matches: " + matches.Count);
540 // if (matches.Count > 0)
541 // {
542 // m_log.Info("[IRCConnector]: Number of groups: " + matches[0].Groups.Count);
543 // }
544 return null;
545 }
546
547 result = new Dictionary<string, string>();
548 result.Add("nick", matches[0].Groups[1].Value);
549 result.Add("user", matches[0].Groups[2].Value);
550 result.Add("channel", matches[0].Groups[3].Value);
551 result.Add("msg", matches[0].Groups[4].Value);
552
553 return result;
554 }
555
556 public void BroadcastSim(string sender, string format, params string[] args)
557 {
558 try
559 {
560 OSChatMessage c = new OSChatMessage();
561 c.From = sender;
562 c.Message = String.Format(format, args);
563 c.Type = ChatTypeEnum.Region; // ChatTypeEnum.Say;
564 c.Position = CenterOfRegion;
565 c.Sender = null;
566 c.SenderUUID = UUID.Zero;
567
568 ChannelState.OSChat(this, c, true);
569
570 }
571 catch (Exception ex) // IRC gate should not crash Sim
572 {
573 m_log.ErrorFormat("[IRC-Connector-{0}]: BroadcastSim Exception Trap: {1}\n{2}", idn, ex.Message, ex.StackTrace);
574 }
575 }
576
577 #region IRC Command Handlers
578
579 public void ProcessIRCCommand(string command)
580 {
581
582 string[] commArgs;
583 string c_server = m_server;
584
585 string pfx = String.Empty;
586 string cmd = String.Empty;
587 string parms = String.Empty;
588
589 // ":" indicates that a prefix is present
590 // There are NEVER more than 17 real
591 // fields. A parameter that starts with
592 // ":" indicates that the remainder of the
593 // line is a single parameter value.
594
595 commArgs = command.Split(CS_SPACE,2);
596
597 if (commArgs[0].StartsWith(":"))
598 {
599 pfx = commArgs[0].Substring(1);
600 commArgs = commArgs[1].Split(CS_SPACE,2);
601 }
602
603 cmd = commArgs[0];
604 parms = commArgs[1];
605
606 // m_log.DebugFormat("[IRC-Connector-{0}] prefix = <{1}> cmd = <{2}>", idn, pfx, cmd);
607
608 switch (cmd)
609 {
610
611 // Messages 001-004 are always sent
612 // following signon.
613
614 case "001" : // Welcome ...
615 case "002" : // Server information
616 case "003" : // Welcome ...
617 break;
618 case "004" : // Server information
619 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
620 commArgs = parms.Split(CS_SPACE);
621 c_server = commArgs[1];
622 m_server = c_server;
623 version = commArgs[2];
624 usermod = commArgs[3];
625 chanmod = commArgs[4];
626 break;
627 case "005" : // Server information
628 break;
629 case "042" :
630 case "250" :
631 case "251" :
632 case "252" :
633 case "254" :
634 case "255" :
635 case "265" :
636 case "266" :
637 case "332" : // Subject
638 case "333" : // Subject owner (?)
639 case "353" : // Name list
640 case "366" : // End-of-Name list marker
641 case "372" : // MOTD body
642 case "375" : // MOTD start
643 m_log.InfoFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]);
644 break;
645 case "376" : // MOTD end
646 m_log.InfoFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]);
647 motd = true;
648 break;
649 case "451" : // Not registered
650 break;
651 case "433" : // Nickname in use
652 // Gen a new name
653 m_nick = m_baseNick + Util.RandomClass.Next(1, 99);
654 m_log.ErrorFormat("[IRC-Connector-{0}]: IRC SERVER reports NicknameInUse, trying {1}", idn, m_nick);
655 // Retry
656 m_writer.WriteLine(String.Format("NICK {0}", m_nick));
657 m_writer.Flush();
658 m_writer.WriteLine(m_user);
659 m_writer.Flush();
660 m_writer.WriteLine(String.Format("JOIN {0}", m_ircChannel));
661 m_writer.Flush();
662 break;
663 case "NOTICE" :
664 m_log.WarnFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]);
665 break;
666 case "ERROR" :
667 m_log.ErrorFormat("[IRC-Connector-{0}] {1}", idn, parms.Split(CS_SPACE,2)[1]);
668 if (parms.Contains("reconnect too fast"))
669 ICCD_PERIOD++;
670 break;
671 case "PING" :
672 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
673 m_writer.WriteLine(String.Format("PONG {0}", parms));
674 m_writer.Flush();
675 break;
676 case "PONG" :
677 break;
678 case "JOIN":
679 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
680 eventIrcJoin(pfx, cmd, parms);
681 break;
682 case "PART":
683 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
684 eventIrcPart(pfx, cmd, parms);
685 break;
686 case "MODE":
687 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
688 eventIrcMode(pfx, cmd, parms);
689 break;
690 case "NICK":
691 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
692 eventIrcNickChange(pfx, cmd, parms);
693 break;
694 case "KICK":
695 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
696 eventIrcKick(pfx, cmd, parms);
697 break;
698 case "QUIT":
699 m_log.DebugFormat("[IRC-Connector-{0}] parms = <{1}>", idn, parms);
700 eventIrcQuit(pfx, cmd, parms);
701 break;
702 default :
703 m_log.DebugFormat("[IRC-Connector-{0}] Command '{1}' ignored, parms = {2}", idn, cmd, parms);
704 break;
705 }
706
707 // m_log.DebugFormat("[IRC-Connector-{0}] prefix = <{1}> cmd = <{2}> complete", idn, pfx, cmd);
708
709 }
710
711 public void eventIrcJoin(string prefix, string command, string parms)
712 {
713 string[] args = parms.Split(CS_SPACE,2);
714 string IrcUser = prefix.Split('!')[0];
715 string IrcChannel = args[0];
716
717 if (IrcChannel.StartsWith(":"))
718 IrcChannel = IrcChannel.Substring(1);
719
720 m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCJoin {1}:{2}", idn, m_server, m_ircChannel);
721 BroadcastSim(IrcUser, "/me joins {0}", IrcChannel);
722 }
723
724 public void eventIrcPart(string prefix, string command, string parms)
725 {
726 string[] args = parms.Split(CS_SPACE,2);
727 string IrcUser = prefix.Split('!')[0];
728 string IrcChannel = args[0];
729
730 m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCPart {1}:{2}", idn, m_server, m_ircChannel);
731 BroadcastSim(IrcUser, "/me parts {0}", IrcChannel);
732 }
733
734 public void eventIrcMode(string prefix, string command, string parms)
735 {
736 string[] args = parms.Split(CS_SPACE,2);
737 string UserMode = args[1];
738
739 m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCMode {1}:{2}", idn, m_server, m_ircChannel);
740 if (UserMode.Substring(0, 1) == ":")
741 {
742 UserMode = UserMode.Remove(0, 1);
743 }
744 }
745
746 public void eventIrcNickChange(string prefix, string command, string parms)
747 {
748 string[] args = parms.Split(CS_SPACE,2);
749 string UserOldNick = prefix.Split('!')[0];
750 string UserNewNick = args[0].Remove(0, 1);
751
752 m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCNickChange {1}:{2}", idn, m_server, m_ircChannel);
753 BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick);
754 }
755
756 public void eventIrcKick(string prefix, string command, string parms)
757 {
758 string[] args = parms.Split(CS_SPACE,3);
759 string UserKicker = prefix.Split('!')[0];
760 string IrcChannel = args[0];
761 string UserKicked = args[1];
762 string KickMessage = args[2];
763
764 m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCKick {1}:{2}", idn, m_server, m_ircChannel);
765 BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
766
767 if (UserKicked == m_nick)
768 {
769 BroadcastSim(m_nick, "Hey, that was me!!!");
770 }
771
772 }
773
774 public void eventIrcQuit(string prefix, string command, string parms)
775 {
776 string IrcUser = prefix.Split('!')[0];
777 string QuitMessage = parms;
778
779 m_log.DebugFormat("[IRC-Connector-{0}] Event: IRCQuit {1}:{2}", idn, m_server, m_ircChannel);
780 BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage);
781 }
782
783 #endregion
784
785 #region Connector Watch Dog
786
787 // A single watch dog monitors extant connectors and makes sure that they
788 // are re-connected as necessary. If a connector IS connected, then it is
789 // pinged, but only if a PING period has elapsed.
790
791 protected static void WatchdogHandler(Object source, ElapsedEventArgs args)
792 {
793
794 // m_log.InfoFormat("[IRC-Watchdog] Status scan");
795
796 _pdk_ = (_pdk_+1)%PING_PERIOD; // cycle the ping trigger
797 _icc_++; // increment the inter-consecutive-connect-delay counter
798
799 foreach (XIRCConnector connector in m_connectors)
800 {
801 if (connector.Enabled)
802 {
803 if (!connector.Connected)
804 {
805 try
806 {
807 // m_log.DebugFormat("[IRC-Watchdog] Connecting {1}:{2}", connector.idn, connector.m_server, connector.m_ircChannel);
808 connector.Connect();
809 }
810 catch (Exception e)
811 {
812 m_log.ErrorFormat("[IRC-Watchdog] Exception on connector {0}: {1} ", connector.idn, e.Message);
813 }
814 }
815 else
816 {
817 if (_pdk_ == 0)
818 {
819 try
820 {
821 connector.m_writer.WriteLine(String.Format("PING :{0}", connector.m_server));
822 connector.m_writer.Flush();
823 }
824 catch (Exception /*e*/)
825 {
826 // m_log.ErrorFormat("[IRC-PingRun] Exception on connector {0}: {1} ", connector.idn, e.Message);
827 // m_log.Debug(e);
828 connector.Reconnect();
829 }
830 }
831 }
832 }
833 }
834
835 // m_log.InfoFormat("[IRC-Watchdog] Status scan completed");
836
837 }
838
839 #endregion
840
841 }
842}