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 }