2 /* Circ.Cil : Circ's Irc Library, main backend for Circ
3 * Copyright (C) 2007 LAVAL Jérémie
5 * This file is licensed under the terms of the LGPL.
7 * For the complete licence see the file COPYING.
13 using System
.Net
.Sockets
;
14 using System
.Threading
;
15 using System
.Collections
.Generic
;
20 public sealed class CilIrcConnection
: IrcConnection
23 IList
<IrcChannel
> channelsJoined
= new List
<IrcChannel
>();
26 //const int DefaultTime = 1000;
28 bool identified
= false;
31 TcpClient client
= new TcpClient();
39 //Timer readTimer = new Timer(DefaultTime);
41 volatile int nbTimes
= 0;
44 IParser parser
= DefaultParser
.Instance
;
46 public override void Connect()
52 this.MessageReceived
+= AutomaticPong
;
53 this.MessageReceived
+= Identification
;
56 client
.LingerState
= new LingerOption(true, 2);
58 ConnectionEventArgs e
= new ConnectionEventArgs(this.Server
, this.Info
.Port
);
62 client
.Connect(Info
.Server
, Info
.Port
);
63 } catch (Exception ex
) {
65 throw new ConnectionException("The connection to the server wasn't successful : "+ex
.Message
, ex
);
68 ns
= client
.GetStream();
69 sw
= new StreamWriter(ns
, Info
.Charset
);
70 sr
= new StreamReader(ns
, Info
.Charset
);
72 // Thread initialization
73 readThread
= new Thread(WorkerRead
);
74 readThread
.IsBackground
= true;
75 readThread
.Name
= "Read Thread";
76 readThread
.Priority
= ThreadPriority
.BelowNormal
;
79 // First identification pass try
80 if (!string.IsNullOrEmpty(Info
.Password
))
81 SendRawMessage(Rfc
.Pass(Info
.Password
));
82 SendRawMessage(Rfc
.Nick(Info
.Pseudo
));
83 SendRawMessage(Rfc
.User(Info
.Pseudo
, Info
.RealName
));
85 // Inform that we are connected. Warning : Connected doesn't mean Identified
86 // (i.e. you can't join channels until you are identified)
90 public override void Disconnect()
92 if (client
== null || (client
!= null && !client
.Connected
))
95 SendRawMessage(Rfc
.Quit("Powered by Monologue"));
99 protected override void Dispose (bool managedRes
)
101 foreach (IrcChannel chan
in JoinedChannels
)
103 // Disconnect the machine
119 public override void SendRawMessage(string message
)
121 if (string.IsNullOrEmpty(message
))
122 throw new ArgumentNullException("message");
125 // Check if the message is correctly finished with a \r\n sequence
126 if (message
.EndsWith(Rfc
.Crlf
))
129 sw
.WriteLine(message
);
134 // TODO: maybe use an event instead of this
135 throw new ConnectionException("Error, the inner socket has been shut down");
137 OnMessageSent(new MessageEventArgs(message
));
140 public override IrcChannel
JoinChannel(string channelName
)
142 IrcChannel temp
= new CilIrcChannel(channelName
, this);
143 channelsJoined
.Add(temp
);
144 OnChannelCreated(new ChannelEventArgs(temp
));
148 public override IrcChannel
[] JoinedChannels
{
150 IrcChannel
[] temp
= new IrcChannel
[channelsJoined
.Count
];
151 channelsJoined
.CopyTo(temp
, 0);
157 public override string Nick
{
158 get { return Info.Pseudo; }
160 if (string.IsNullOrEmpty(value))
168 public override string RealName
{
169 get { return Info.RealName; }
172 public override string Server
{
173 get { return Info.Server; }
176 public bool IsIdentified
{
177 get { return identified; }
181 #region private helpers methods
182 private void UpdateNickname()
184 this.SendRawMessage(Rfc
.Nick(Info
.Pseudo
));
187 private void WorkerRead()
194 if (!client
.Connected
)
197 // Sleep if the channel is idle
198 if (Info
.UsePassiveMode
&& client
.Available
== 0 && identified
) {
199 if (sleepTime
< 5000 && ++nbTimes
% 1500 == 0)
201 if (nbTimes
== int.MaxValue
) {
205 Thread
.Sleep(sleepTime
);
212 while ((temp
= sr
.ReadLine()) != null)
213 OnMessageReceived(new MessageEventArgs(temp
));
214 } catch (IOException ex
) {
215 Logger
.Error(string.Format("Exception while reading data. Inner Exception : {O} ::: Error code : {1}", ex
.InnerException
.Message
,
216 ((ex
.InnerException
is SocketException
) ? ((SocketException
)ex
.InnerException
).ErrorCode
.ToString() : "No code")), ex
);
218 // Let other things happens
223 // Big hack, but don't know why some server are reluctant to accept my Nick, User and all messages
224 // thus if they want the war so be it (I repeatidly send them the connection commands until they accept
226 private void Identification(object sender
, MessageEventArgs e
)
228 // Some server return an 451 error and other nothing
229 if (((parser
.RetrieveMessageType(e
.Message
) == MessageType
.Reply
&&
230 parser
.RetrieveNumericalReply(e
.Message
) == 451)) || ++messageCount
== 3) {
231 // Resend the connection commands
232 if (!string.IsNullOrEmpty(Info
.Password
))
233 SendRawMessage(Rfc
.Pass(Info
.Password
));
234 SendRawMessage(Rfc
.Nick(Info
.Pseudo
));
235 SendRawMessage(Rfc
.User(Info
.Pseudo
, Info
.RealName
));
237 if (parser
.RetrieveMessageType(e
.Message
) == MessageType
.Reply
238 && parser
.RetrieveNumericalReply(e
.Message
) == 1) {
239 this.MessageReceived
-= Identification
;
240 OnIdentified(new ConnectionEventArgs(this.Server
, Info
.Port
));
246 // An automatic wrapper which call PONG at each PING messages
247 private void AutomaticPong(object sender
, MessageEventArgs e
)
249 if (string.IsNullOrEmpty(e
.Message
))
252 if (parser
.RetrieveMessageType(e
.Message
) == MessageType
.Action
253 && parser
.RetrieveAction(e
.Message
) == ActionType
.Ping
) {
255 SendRawMessage(Rfc
.Pong(parser
.RetrieveParameters(e
.Message
)));