aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs
diff options
context:
space:
mode:
authorDr Scofield2008-11-03 17:11:28 +0000
committerDr Scofield2008-11-03 17:11:28 +0000
commit69ae33db1ad6a5111211d6ccc6c3b2ac2467f4d0 (patch)
tree1e14a6c9a6fae960cab80632d93708b19fa5503e /OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs
parent* Use the UUID regex sitting in utils for detection of uuids embedded in scripts (diff)
downloadopensim-SC_OLD-69ae33db1ad6a5111211d6ccc6c3b2ac2467f4d0.zip
opensim-SC_OLD-69ae33db1ad6a5111211d6ccc6c3b2ac2467f4d0.tar.gz
opensim-SC_OLD-69ae33db1ad6a5111211d6ccc6c3b2ac2467f4d0.tar.bz2
opensim-SC_OLD-69ae33db1ad6a5111211d6ccc6c3b2ac2467f4d0.tar.xz
dropping old IRCBridgeModule.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/IRCConnector.cs702
1 files changed, 0 insertions, 702 deletions
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}