1 /**
2  * Common IRC code.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Stéphan Kochen <stephan@kochen.nl>
12  *   Vladimir Panteleev <vladimir@thecybershadow.net>
13  *   Vincent Povirk <madewokherd@gmail.com>
14  *   Simon Arlott
15  */
16 
17 module ae.net.irc.common;
18 
19 import core.time;
20 import std.string;
21 
22 import ae.net.asockets;
23 
24 /// Types of a chat message.
25 enum IrcMessageType
26 {
27 	NORMAL,
28 	ACTION,
29 	NOTICE
30 }
31 
32 // RFC1459 case mapping
33 static assert(toLower("[") == "[" && toUpper("[") == "[");
34 static assert(toLower("]") == "]" && toUpper("]") == "]");
35 static assert(toLower("{") == "{" && toUpper("{") == "{");
36 static assert(toLower("}") == "}" && toUpper("}") == "}");
37 static assert(toLower("|") == "|" && toUpper("|") == "|");
38 static assert(toLower("\\") == "\\" && toUpper("\\") == "\\");
39 
40 string rfc1459toLower(string name)
41 {
42 	return toLower(name).tr("[]\\","{}|");
43 }
44 
45 string rfc1459toUpper(string name)
46 {
47 	return toUpper(name).tr("{}|","[]\\");
48 }
49 
50 unittest
51 {
52 	assert(rfc1459toLower("{}|[]\\") == "{}|{}|");
53 	assert(rfc1459toUpper("{}|[]\\") == "[]\\[]\\");
54 }
55 
56 class IrcSocket : LineBufferedSocket
57 {
58 	this()
59 	{
60 		super(90.seconds);
61 		init();
62 	}
63 
64 	this(Socket conn)
65 	{
66 		super.setIdleTimeout(60.seconds);
67 		super(conn);
68 		init();
69 	}
70 
71 	void init()
72 	{
73 		handleIdleTimeout = &onIdleTimeout;
74 
75 		super.delimiter = "\n";
76 		super.handleReadLine = &onReadLine;
77 	}
78 
79 	override void markNonIdle()
80 	{
81 		if (pingSent)
82 			pingSent = false;
83 		super.markNonIdle();
84 	}
85 
86 	override final void send(string line)
87 	{
88 		// Send with \r\n, but support receiving with \n
89 		import ae.sys.data;
90 		ClientSocket.send(Data(line ~ "\r\n"));
91 	}
92 
93 	void delegate (IrcSocket sender) handleInactivity;
94 	void delegate (IrcSocket sender) handleTimeout;
95 	void delegate(LineBufferedSocket sender, string line) handleReadLine; // redefine
96 
97 private:
98 	final void onReadLine(LineBufferedSocket sender, string line)
99 	{
100 		if (handleReadLine)
101 			handleReadLine(sender, line.chomp("\r"));
102 	}
103 
104 	final void onIdleTimeout(ClientSocket sender)
105 	{
106 		if (pingSent || handleInactivity is null)
107 		{
108 			if (handleTimeout)
109 				handleTimeout(this);
110 			else
111 				disconnect("Time-out", DisconnectType.Error);
112 		}
113 		else
114 		{
115 			handleInactivity(this);
116 			pingSent = true;
117 		}
118 	}
119 
120 	bool pingSent;
121 }
122 
123 alias GenericServerSocket!(IrcSocket) IrcServerSocket;
124 
125 // TODO: this is server-specific
126 const string IRC_NICK_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-`";
127 const string IRC_USER_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
128 const string IRC_HOST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.";
129 
130 enum Reply
131 {
132 	RPL_WELCOME             = 001,
133 	RPL_YOURHOST            = 002,
134 	RPL_CREATED             = 003,
135 	RPL_MYINFO              = 004,
136 	RPL_BOUNCE              = 005,
137 	RPL_TRACELINK           = 200,
138 	RPL_TRACECONNECTING     = 201,
139 	RPL_TRACEHANDSHAKE      = 202,
140 	RPL_TRACEUNKNOWN        = 203,
141 	RPL_TRACEOPERATOR       = 204,
142 	RPL_TRACEUSER           = 205,
143 	RPL_TRACESERVER         = 206,
144 	RPL_TRACESERVICE        = 207,
145 	RPL_TRACENEWTYPE        = 208,
146 	RPL_TRACECLASS          = 209,
147 	RPL_TRACERECONNECT      = 210,
148 	RPL_STATSLINKINFO       = 211,
149 	RPL_STATSCOMMANDS       = 212,
150 	RPL_STATSCLINE          = 213,
151 	RPL_STATSNLINE          = 214,
152 	RPL_STATSILINE          = 215,
153 	RPL_STATSKLINE          = 216,
154 	RPL_STATSQLINE          = 217,
155 	RPL_STATSYLINE          = 218,
156 	RPL_ENDOFSTATS          = 219,
157 	RPL_UMODEIS             = 221,
158 	RPL_SERVICEINFO         = 231,
159 	RPL_ENDOFSERVICES       = 232,
160 	RPL_SERVICE             = 233,
161 	RPL_SERVLIST            = 234,
162 	RPL_SERVLISTEND         = 235,
163 	RPL_STATSVLINE          = 240,
164 	RPL_STATSLLINE          = 241,
165 	RPL_STATSUPTIME         = 242,
166 	RPL_STATSOLINE          = 243,
167 	RPL_STATSHLINE          = 244,
168 	RPL_STATSSLINE          = 244,
169 	RPL_STATSPING           = 246,
170 	RPL_STATSBLINE          = 247,
171 	RPL_STATSDLINE          = 250,
172 	RPL_LUSERCLIENT         = 251,
173 	RPL_LUSEROP             = 252,
174 	RPL_LUSERUNKNOWN        = 253,
175 	RPL_LUSERCHANNELS       = 254,
176 	RPL_LUSERME             = 255,
177 	RPL_ADMINME             = 256,
178 	RPL_ADMINLOC1           = 257,
179 	RPL_ADMINLOC2           = 258,
180 	RPL_ADMINEMAIL          = 259,
181 	RPL_TRACELOG            = 261,
182 	RPL_TRACEEND            = 262,
183 	RPL_TRYAGAIN            = 263,
184 	RPL_NONE                = 300,
185 	RPL_AWAY                = 301,
186 	RPL_USERHOST            = 302,
187 	RPL_ISON                = 303,
188 	RPL_UNAWAY              = 305,
189 	RPL_NOWAWAY             = 306,
190 	RPL_WHOISUSER           = 311,
191 	RPL_WHOISSERVER         = 312,
192 	RPL_WHOISOPERATOR       = 313,
193 	RPL_WHOWASUSER          = 314,
194 	RPL_ENDOFWHO            = 315,
195 	RPL_WHOISCHANOP         = 316,
196 	RPL_WHOISIDLE           = 317,
197 	RPL_ENDOFWHOIS          = 318,
198 	RPL_WHOISCHANNELS       = 319,
199 	RPL_LISTSTART           = 321,
200 	RPL_LIST                = 322,
201 	RPL_LISTEND             = 323,
202 	RPL_CHANNELMODEIS       = 324,
203 	RPL_UNIQOPIS            = 325,
204 	RPL_NOTOPIC             = 331,
205 	RPL_TOPIC               = 332,
206 	RPL_INVITING            = 341,
207 	RPL_SUMMONING           = 342,
208 	RPL_INVITELIST          = 346,
209 	RPL_ENDOFINVITELIST     = 347,
210 	RPL_EXCEPTLIST          = 348,
211 	RPL_ENDOFEXCEPTLIST     = 349,
212 	RPL_VERSION             = 351,
213 	RPL_WHOREPLY            = 352,
214 	RPL_NAMREPLY            = 353,
215 	RPL_KILLDONE            = 361,
216 	RPL_CLOSING             = 362,
217 	RPL_CLOSEEND            = 363,
218 	RPL_LINKS               = 364,
219 	RPL_ENDOFLINKS          = 365,
220 	RPL_ENDOFNAMES          = 366,
221 	RPL_BANLIST             = 367,
222 	RPL_ENDOFBANLIST        = 368,
223 	RPL_ENDOFWHOWAS         = 369,
224 	RPL_INFO                = 371,
225 	RPL_MOTD                = 372,
226 	RPL_INFOSTART           = 373,
227 	RPL_ENDOFINFO           = 374,
228 	RPL_MOTDSTART           = 375,
229 	RPL_ENDOFMOTD           = 376,
230 	RPL_YOUREOPER           = 381,
231 	RPL_REHASHING           = 382,
232 	RPL_YOURESERVICE        = 383,
233 	RPL_MYPORTIS            = 384,
234 	RPL_TIME                = 391,
235 	RPL_USERSSTART          = 392,
236 	RPL_USERS               = 393,
237 	RPL_ENDOFUSERS          = 394,
238 	RPL_NOUSERS             = 395,
239 	ERR_NOSUCHNICK          = 401,
240 	ERR_NOSUCHSERVER        = 402,
241 	ERR_NOSUCHCHANNEL       = 403,
242 	ERR_CANNOTSENDTOCHAN    = 404,
243 	ERR_TOOMANYCHANNELS     = 405,
244 	ERR_WASNOSUCHNICK       = 406,
245 	ERR_TOOMANYTARGETS      = 407,
246 	ERR_NOSUCHSERVICE       = 408,
247 	ERR_NOORIGIN            = 409,
248 	ERR_NORECIPIENT         = 411,
249 	ERR_NOTEXTTOSEND        = 412,
250 	ERR_NOTOPLEVEL          = 413,
251 	ERR_WILDTOPLEVEL        = 414,
252 	ERR_BADMASK             = 415,
253 	ERR_UNKNOWNCOMMAND      = 421,
254 	ERR_NOMOTD              = 422,
255 	ERR_NOADMININFO         = 423,
256 	ERR_FILEERROR           = 424,
257 	ERR_NONICKNAMEGIVEN     = 431,
258 	ERR_ERRONEUSNICKNAME    = 432,
259 	ERR_NICKNAMEINUSE       = 433,
260 	ERR_NICKCOLLISION       = 436,
261 	ERR_UNAVAILRESOURCE     = 437,
262 	ERR_USERNOTINCHANNEL    = 441,
263 	ERR_NOTONCHANNEL        = 442,
264 	ERR_USERONCHANNEL       = 443,
265 	ERR_NOLOGIN             = 444,
266 	ERR_SUMMONDISABLED      = 445,
267 	ERR_USERSDISABLED       = 446,
268 	ERR_NOTREGISTERED       = 451,
269 	ERR_NEEDMOREPARAMS      = 461,
270 	ERR_ALREADYREGISTRED    = 462,
271 	ERR_NOPERMFORHOST       = 463,
272 	ERR_PASSWDMISMATCH      = 464,
273 	ERR_YOUREBANNEDCREEP    = 465,
274 	ERR_YOUWILLBEBANNED     = 466,
275 	ERR_KEYSET              = 467,
276 	ERR_CHANNELISFULL       = 471,
277 	ERR_UNKNOWNMODE         = 472,
278 	ERR_INVITEONLYCHAN      = 473,
279 	ERR_BANNEDFROMCHAN      = 474,
280 	ERR_BADCHANNELKEY       = 475,
281 	ERR_BADCHANMASK         = 476,
282 	ERR_NOCHANMODES         = 477,
283 	ERR_BANLISTFULL         = 478,
284 	ERR_NOPRIVILEGES        = 481,
285 	ERR_CHANOPRIVSNEEDED    = 482,
286 	ERR_CANTKILLSERVER      = 483,
287 	ERR_RESTRICTED          = 484,
288 	ERR_UNIQOPPRIVSNEEDED   = 485,
289 	ERR_NOOPERHOST          = 491,
290 	ERR_NOSERVICEHOST       = 492,
291 	ERR_UMODEUNKNOWNFLAG    = 501,
292 	ERR_USERSDONTMATCH      = 502,
293 }