Cosmetic change, plugin work, various reaorganization (Frontend, Control, CilBackend)
[circ.git] / Circ.Backend.Cil / CilIrcConnection.cs
blob01d2e53faf429330bdc39914b6351942574dbe22
2 using System;
3 using System.IO;
4 using System.Net;
5 using System.Net.Sockets;
6 //using System.Timers;
7 //using Threads = System.Threading;
8 using System.Threading;
9 using System.Collections.Generic;
10 using Circ.Backend;
12 namespace Circ.Cil
14 public sealed class CilIrcConnection: IrcConnection
16 // State info
17 IList<IrcChannel> channelsJoined = new List<IrcChannel>();
19 // Config
20 //const int DefaultTime = 1000;
21 int messageCount = 0;
22 bool identified = false;
24 // Tcpclient
25 TcpClient client = new TcpClient();
27 // Streams access
28 NetworkStream ns;
29 StreamWriter sw;
30 StreamReader sr;
32 // Threads
33 //Timer readTimer = new Timer(DefaultTime);
34 Thread readThread;
35 volatile int nbTimes = 0;
37 // Parser
38 IParser parser = new DefaultParser();
40 public override void Connect()
42 if (client.Connected)
43 return;
45 // Event helpers
46 this.MessageReceived += AutomaticPong;
47 this.MessageReceived += Identification;
49 // Set common option
50 client.LingerState = new LingerOption(true, 2);
52 try {
53 client.Connect(Info.Server, Info.Port);
54 } catch (Exception e) {
55 client.Close();
56 throw new ConnectionException("The connection to the server wasn't successful : "+e.Message, e);
59 ns = client.GetStream();
60 sw = new StreamWriter(ns, Info.Charset);
61 sr = new StreamReader(ns, Info.Charset);
63 // Thread initialization
64 readThread = new Thread(WorkerRead);
65 readThread.IsBackground = true;
66 readThread.Name = "Read Thread";
67 readThread.Priority = ThreadPriority.BelowNormal;
68 readThread.Start();
70 // First identification pass try
71 if (!string.IsNullOrEmpty(Info.Password))
72 SendRawMessage(Rfc.Pass(Info.Password));
73 SendRawMessage(Rfc.Nick(Info.Pseudo));
74 SendRawMessage(Rfc.User(Info.Pseudo, Info.RealName));
78 public override void Disconnect()
80 if (client == null || !client.Connected)
81 return;
82 // Send quit message
83 SendRawMessage(Rfc.Quit("Powered by Monologue"));
84 sw.Flush();
87 protected override void Dispose (bool managedRes)
89 foreach (IrcChannel chan in JoinedChannels)
90 chan.CloseChannel();
91 // Disconnect the machine
92 this.Disconnect();
94 if (managedRes)
96 try {
97 if (sw != null)
98 sw.Dispose();
99 if (sr != null)
100 sr.Dispose();
101 } catch {}
103 client.Close();
107 public override void SendRawMessage(string message)
109 if (string.IsNullOrEmpty(message))
110 throw new ArgumentNullException("message");
112 OnMessageSent(new MessageEventArgs(message));
114 // Check if the message is correctly finished with a \r\n sequence
115 if (message.EndsWith(Rfc.Crlf))
116 sw.Write(message);
117 else
118 sw.WriteLine(message);
120 // Flush the data
121 sw.Flush();
124 public override IrcChannel JoinChannel(string channelName)
126 IrcChannel temp = new CilIrcChannel(channelName, this);
127 channelsJoined.Add(temp);
128 OnChannelCreated(new ChannelEventArgs(temp));
129 return temp;
132 public override IrcChannel[] JoinedChannels {
133 get {
134 IrcChannel[] temp = new IrcChannel[channelsJoined.Count];
135 channelsJoined.CopyTo(temp, 0);
136 return temp;
140 #region Properties
141 public override string Nick {
142 get { return Info.Pseudo; }
143 set {
144 if (string.IsNullOrEmpty(value))
145 return;
147 Info.Pseudo = value;
148 UpdateNickname();
152 public override string RealName {
153 get { return Info.RealName; }
156 public override string Server {
157 get { return Info.Server; }
160 public bool IsIdentified {
161 get { return identified; }
163 #endregion
165 #region private helpers methods
166 private void UpdateNickname()
168 this.SendRawMessage(Rfc.Nick(Info.Pseudo));
171 private void WorkerRead()
173 int sleepTime = 0;
174 string temp;
176 // Main loop
177 while (true) {
178 if (!client.Connected)
179 return;
181 // Sleep if the channel is idle
182 if (Info.UsePassiveMode && client.Available == 0 && identified) {
183 if (sleepTime < 5000 && ++nbTimes % 1500 == 0)
184 sleepTime += 20;
185 if (nbTimes == int.MaxValue) {
186 nbTimes = 0;
188 if (sleepTime != 0)
189 Thread.Sleep(sleepTime);
190 } else {
191 sleepTime = 0;
194 try {
195 // Process data
196 while ((temp = sr.ReadLine()) != null)
197 OnMessageReceived(new MessageEventArgs(temp));
198 } catch (IOException ex) {
199 Logger.Error(string.Format("Exception while reading data. Inner Exception : {O} ::: Error code : {1}", ex.InnerException.Message,
200 (ex.InnerException is SocketException) ? ((SocketException)ex.InnerException).ErrorCode.ToString() : "No code"), ex);
202 // Let other things happens
203 Thread.Sleep(500);
207 // Big hack, but don't know why some server are reluctant to accept my Nick, User and all messages
208 // thus if they want the war so be it (I repeatidly send them the connection commands until they accept
209 // me)
210 private void Identification(object sender, MessageEventArgs e)
212 // Some server return an 451 error and other nothing
213 if (((parser.RetrieveMessageType(e.Message) == MessageType.Reply &&
214 parser.RetrieveNumericalReply(e.Message) == 451)) || ++messageCount == 3) {
215 // Resend the connection commands
216 if (!string.IsNullOrEmpty(Info.Password))
217 SendRawMessage(Rfc.Pass(Info.Password));
218 SendRawMessage(Rfc.Nick(Info.Pseudo));
219 SendRawMessage(Rfc.User(Info.Pseudo, Info.RealName));
221 if (parser.RetrieveMessageType(e.Message) == MessageType.Reply
222 && parser.RetrieveNumericalReply(e.Message) == 1) {
223 this.MessageReceived -= Identification;
224 OnIdentified(new ConnectionEventArgs(this.Server, Info.Port));
225 messageCount = 0;
226 identified = true;
230 // An automatic wrapper which call PONG at each PING messages
231 private void AutomaticPong(object sender, MessageEventArgs e)
233 if (string.IsNullOrEmpty(e.Message))
234 return;
236 if (parser.RetrieveMessageType(e.Message) == MessageType.Action
237 && parser.RetrieveAction(e.Message) == ActionType.Ping) {
239 SendRawMessage(Rfc.Pong(parser.RetrieveParameters(e.Message)));
242 #endregion