genesis 1 import socket
genesis 2 import time
genesis 3 import sys
genesis 4 import re
genesis 5 import string
genesis 6 import os
genesis 7 import base64
9990-keep-ephemer... 8 import traceback
9987-embargoing 9 import logging
9985-single-thread 10 from state import State
9983-knobs 11 from state import KNOBS
9985-single-thread 12 from message import Message
9985-single-thread 13 from server import VERSION
genesis 14 from funcs import *
9985-single-thread 15 from commands import BROADCAST
9985-single-thread 16 from commands import DIRECT
genesis 17
genesis 18 class Client(object):
genesis 19 __linesep_regexp = re.compile(r"\r?\n")
genesis 20
genesis 21 __valid_nickname_regexp = re.compile(
genesis 22 r"^[][\`_^{|}A-Za-z][][\`_^{|}A-Za-z0-9-]{0,50}$")
genesis 23 __valid_channelname_regexp = re.compile(
genesis 24 r"^[&#+!][^\x00\x07\x0a\x0d ,:]{0,50}$")
genesis 25
genesis 26 def __init__(self, server, socket):
genesis 27 self.server = server
9987-embargoing 28 self.state = State.get_instance()
genesis 29 self.socket = socket
genesis 30 self.channels = {}
genesis 31 self.nickname = None
genesis 32 self.user = None
genesis 33 self.realname = None
genesis 34 (self.host, self.port) = socket.getpeername()
genesis 35 self.__timestamp = time.time()
genesis 36 self.__readbuffer = ""
genesis 37 self.__writebuffer = ""
genesis 38 self.__sent_ping = False
genesis 39 if self.server.password:
genesis 40 self.__handle_command = self.__pass_handler
genesis 41 else:
genesis 42 self.__handle_command = self.__registration_handler
genesis 43
9987-embargoing 44 def message_from_station(self, msg):
9987-embargoing 45 targetname = self.server.channel_name if msg.command == BROADCAST else self.nickname
9987-embargoing 46 pest_prefix = msg.prefix if msg.prefix else msg.speaker
9987-embargoing 47 formatted_message = ":%s PRIVMSG %s :%s" % (pest_prefix, targetname, msg.body)
9987-embargoing 48 self.__writebuffer += formatted_message + "\r\n"
genesis 49
genesis 50 def get_prefix(self):
9989-show-wot-nicks 51 return "%s" % (self.nickname)
genesis 52 prefix = property(get_prefix)
genesis 53
genesis 54 def check_aliveness(self):
genesis 55 now = time.time()
genesis 56 if self.__timestamp + 180 < now:
genesis 57 self.disconnect("ping timeout")
genesis 58 return
genesis 59 if not self.__sent_ping and self.__timestamp + 90 < now:
genesis 60 if self.__handle_command == self.__command_handler:
genesis 61
genesis 62 self.message("PING :%s" % self.server.name)
genesis 63 self.__sent_ping = True
genesis 64 else:
genesis 65
genesis 66 self.disconnect("ping timeout")
genesis 67
genesis 68 def write_queue_size(self):
genesis 69 return len(self.__writebuffer)
genesis 70
genesis 71 def __parse_read_buffer(self):
genesis 72 lines = self.__linesep_regexp.split(self.__readbuffer)
genesis 73 self.__readbuffer = lines[-1]
genesis 74 lines = lines[:-1]
genesis 75 for line in lines:
genesis 76 if not line:
genesis 77
genesis 78 continue
genesis 79 x = line.split(" ", 1)
genesis 80 command = x[0].upper()
genesis 81 if len(x) == 1:
genesis 82 arguments = []
genesis 83 else:
genesis 84 if len(x[1]) > 0 and x[1][0] == ":":
genesis 85 arguments = [x[1][1:]]
genesis 86 else:
genesis 87 y = string.split(x[1], " :", 1)
genesis 88 arguments = string.split(y[0])
genesis 89 if len(y) == 2:
genesis 90 arguments.append(y[1])
genesis 91 self.__handle_command(command, arguments)
genesis 92
genesis 93 def __pass_handler(self, command, arguments):
genesis 94 server = self.server
genesis 95 if command == "PASS":
genesis 96 if len(arguments) == 0:
genesis 97 self.reply_461("PASS")
genesis 98 else:
genesis 99 if arguments[0].lower() == server.password:
genesis 100 self.__handle_command = self.__registration_handler
genesis 101 else:
genesis 102 self.reply("464 :Password incorrect")
genesis 103 elif command == "QUIT":
genesis 104 self.disconnect("Client quit")
genesis 105 return
genesis 106
genesis 107 def __registration_handler(self, command, arguments):
genesis 108 server = self.server
genesis 109 if command == "NICK":
genesis 110 if len(arguments) < 1:
genesis 111 self.reply("431 :No nickname given")
genesis 112 return
genesis 113 nick = arguments[0]
genesis 114 if server.get_client(nick):
genesis 115 self.reply("433 * %s :Nickname is already in use" % nick)
genesis 116 elif not self.__valid_nickname_regexp.match(nick):
genesis 117 self.reply("432 * %s :Erroneous nickname" % nick)
genesis 118 else:
genesis 119 self.nickname = nick
genesis 120 server.client_changed_nickname(self, None)
genesis 121 elif command == "USER":
genesis 122 if len(arguments) < 4:
genesis 123 self.reply_461("USER")
genesis 124 return
genesis 125 self.user = arguments[0]
genesis 126 self.realname = arguments[3]
genesis 127 elif command == "QUIT":
genesis 128 self.disconnect("Client quit")
genesis 129 return
genesis 130 if self.nickname and self.user:
9990-keep-ephemer... 131 self.reply("001 %s :Hi, welcome to Pest" % self.nickname)
genesis 132 self.reply("002 %s :Your host is %s, running version blatta-%s"
genesis 133 % (self.nickname, server.name, VERSION))
genesis 134 self.reply("003 %s :This server was created sometime"
genesis 135 % self.nickname)
genesis 136 self.reply("004 %s :%s blatta-%s o o"
genesis 137 % (self.nickname, server.name, VERSION))
genesis 138 self.send_motd()
genesis 139 self.__handle_command = self.__command_handler
genesis 140
genesis 141 def __command_handler(self, command, arguments):
genesis 142 def away_handler():
genesis 143 pass
genesis 144
genesis 145 def ison_handler():
genesis 146 if len(arguments) < 1:
genesis 147 self.reply_461("ISON")
genesis 148 return
genesis 149 nicks = arguments
genesis 150 online = [n for n in nicks if server.get_client(n)]
genesis 151 self.reply("303 %s :%s" % (self.nickname, " ".join(online)))
genesis 152
genesis 153 def join_handler():
genesis 154 if len(arguments) < 1:
genesis 155 self.reply_461("JOIN")
genesis 156 return
genesis 157 if arguments[0] == "0":
genesis 158 for (channelname, channel) in self.channels.items():
genesis 159 self.message_channel(channel, "PART", channelname, True)
genesis 160 server.remove_member_from_channel(self, channelname)
genesis 161 self.channels = {}
genesis 162 return
genesis 163 channelnames = arguments[0].split(",")
9987-embargoing 164 for channelname in channelnames:
genesis 165 if irc_lower(channelname) in self.channels:
genesis 166 continue
genesis 167 if not valid_channel_re.match(channelname):
genesis 168 self.reply_403(channelname)
genesis 169 continue
genesis 170 channel = server.get_channel(channelname)
genesis 171 channel.add_member(self)
genesis 172 self.channels[irc_lower(channelname)] = channel
genesis 173 self.message_channel(channel, "JOIN", channelname, True)
genesis 174 if channel.topic:
genesis 175 self.reply("332 %s %s :%s"
genesis 176 % (self.nickname, channel.name, channel.topic))
genesis 177 else:
genesis 178 self.reply("331 %s %s :No topic is set"
genesis 179 % (self.nickname, channel.name))
genesis 180 self.reply("353 %s = %s :%s"
genesis 181 % (self.nickname,
genesis 182 channelname,
9989-show-wot-nicks 183 " ".join(sorted(x
9987-embargoing 184 for x in self.state.get_peer_handles()))))
9985-single-thread 185 self.reply("366 %s %s :End of NAMES list" % (self.nickname, channelname))
genesis 186
genesis 187 def list_handler():
genesis 188 if len(arguments) < 1:
genesis 189 channels = server.channels.values()
genesis 190 else:
genesis 191 channels = []
genesis 192 for channelname in arguments[0].split(","):
genesis 193 if server.has_channel(channelname):
genesis 194 channels.append(server.get_channel(channelname))
genesis 195 channels.sort(key=lambda x: x.name)
genesis 196 for channel in channels:
genesis 197 self.reply("322 %s %s %d :%s"
genesis 198 % (self.nickname, channel.name,
genesis 199 len(channel.members), channel.topic))
genesis 200 self.reply("323 %s :End of LIST" % self.nickname)
genesis 201
genesis 202 def lusers_handler():
9987-embargoing 203 pass
genesis 204
genesis 205 def mode_handler():
genesis 206 if len(arguments) < 1:
genesis 207 self.reply_461("MODE")
genesis 208 return
genesis 209 targetname = arguments[0]
genesis 210 if server.has_channel(targetname):
genesis 211 channel = server.get_channel(targetname)
genesis 212 if len(arguments) < 2:
genesis 213 if channel.key:
genesis 214 modes = "+k"
genesis 215 if irc_lower(channel.name) in self.channels:
genesis 216 modes += " %s" % channel.key
genesis 217 else:
genesis 218 modes = "+"
genesis 219 self.reply("324 %s %s %s"
genesis 220 % (self.nickname, targetname, modes))
genesis 221 return
genesis 222 flag = arguments[1]
genesis 223 if flag == "+k":
genesis 224 if len(arguments) < 3:
genesis 225 self.reply_461("MODE")
genesis 226 return
genesis 227 key = arguments[2]
genesis 228 if irc_lower(channel.name) in self.channels:
genesis 229 channel.key = key
genesis 230 self.message_channel(
genesis 231 channel, "MODE", "%s +k %s" % (channel.name, key),
genesis 232 True)
genesis 233 else:
genesis 234 self.reply("442 %s :You're not on that channel"
genesis 235 % targetname)
genesis 236 elif flag == "-k":
genesis 237 if irc_lower(channel.name) in self.channels:
genesis 238 channel.key = None
genesis 239 self.message_channel(
genesis 240 channel, "MODE", "%s -k" % channel.name,
genesis 241 True)
genesis 242 else:
genesis 243 self.reply("442 %s :You're not on that channel"
genesis 244 % targetname)
genesis 245 else:
genesis 246 self.reply("472 %s %s :Unknown MODE flag"
genesis 247 % (self.nickname, flag))
genesis 248 elif targetname == self.nickname:
genesis 249 if len(arguments) == 1:
genesis 250 self.reply("221 %s +" % self.nickname)
genesis 251 else:
genesis 252 self.reply("501 %s :Unknown MODE flag" % self.nickname)
genesis 253 else:
genesis 254 self.reply_403(targetname)
genesis 255
genesis 256 def motd_handler():
genesis 257 self.send_motd()
genesis 258
genesis 259 def nick_handler():
genesis 260 if len(arguments) < 1:
genesis 261 self.reply("431 :No nickname given")
genesis 262 return
genesis 263 newnick = arguments[0]
genesis 264 client = server.get_client(newnick)
genesis 265 if newnick == self.nickname:
genesis 266 pass
genesis 267 elif client and client is not self:
genesis 268 self.reply("433 %s %s :Nickname is already in use"
genesis 269 % (self.nickname, newnick))
genesis 270 elif not self.__valid_nickname_regexp.match(newnick):
genesis 271 self.reply("432 %s %s :Erroneous Nickname"
genesis 272 % (self.nickname, newnick))
genesis 273 else:
genesis 274 oldnickname = self.nickname
genesis 275 self.nickname = newnick
genesis 276 server.client_changed_nickname(self, oldnickname)
genesis 277 self.message_related(
genesis 278 ":%s!%s@%s NICK %s"
genesis 279 % (oldnickname, self.user, self.host, self.nickname),
genesis 280 True)
genesis 281
genesis 282 def notice_and_privmsg_handler():
genesis 283 if len(arguments) == 0:
genesis 284 self.reply("411 %s :No recipient given (%s)"
genesis 285 % (self.nickname, command))
genesis 286 return
genesis 287 if len(arguments) == 1:
genesis 288 self.reply("412 %s :No text to send" % self.nickname)
genesis 289 return
genesis 290 targetname = arguments[0]
genesis 291 message = arguments[1]
genesis 292
genesis 293 if server.has_channel(targetname):
genesis 294 channel = server.get_channel(targetname)
genesis 295 self.message_channel(
genesis 296 channel, command, "%s :%s" % (channel.name, message))
9987-embargoing 297
9987-embargoing 298 self.server.station.infosec.message(
9987-embargoing 299 Message(
9987-embargoing 300 {
9987-embargoing 301 "speaker": self.nickname,
9987-embargoing 302 "command": BROADCAST,
9987-embargoing 303 "bounces": 0,
9987-embargoing 304 "body": message
9987-embargoing 305 }))
genesis 306 else:
9987-embargoing 307 self.server.station.infosec.message(Message({
genesis 308 "speaker": self.nickname,
genesis 309 "handle": targetname,
9987-embargoing 310 "body": message,
genesis 311 "bounces": 0,
genesis 312 "command": DIRECT
9987-embargoing 313 }))
genesis 314
genesis 315 def part_handler():
genesis 316 if len(arguments) < 1:
genesis 317 self.reply_461("PART")
genesis 318 return
genesis 319 if len(arguments) > 1:
genesis 320 partmsg = arguments[1]
genesis 321 else:
genesis 322 partmsg = self.nickname
genesis 323 for channelname in arguments[0].split(","):
genesis 324 if not valid_channel_re.match(channelname):
genesis 325 self.reply_403(channelname)
genesis 326 elif not irc_lower(channelname) in self.channels:
genesis 327 self.reply("442 %s %s :You're not on that channel"
genesis 328 % (self.nickname, channelname))
genesis 329 else:
genesis 330 channel = self.channels[irc_lower(channelname)]
genesis 331 self.message_channel(
genesis 332 channel, "PART", "%s :%s" % (channelname, partmsg),
genesis 333 True)
genesis 334 del self.channels[irc_lower(channelname)]
genesis 335 server.remove_member_from_channel(self, channelname)
genesis 336
genesis 337 def ping_handler():
genesis 338 if len(arguments) < 1:
genesis 339 self.reply("409 %s :No origin specified" % self.nickname)
genesis 340 return
genesis 341 self.reply("PONG %s :%s" % (server.name, arguments[0]))
genesis 342
genesis 343 def pong_handler():
genesis 344 pass
genesis 345
genesis 346 def quit_handler():
genesis 347 if len(arguments) < 1:
genesis 348 quitmsg = self.nickname
genesis 349 else:
genesis 350 quitmsg = arguments[0]
genesis 351 self.disconnect(quitmsg)
genesis 352
genesis 353 def topic_handler():
genesis 354 if len(arguments) < 1:
genesis 355 self.reply_461("TOPIC")
genesis 356 return
genesis 357 channelname = arguments[0]
genesis 358 channel = self.channels.get(irc_lower(channelname))
genesis 359 if channel:
genesis 360 if len(arguments) > 1:
genesis 361 newtopic = arguments[1]
genesis 362 channel.topic = newtopic
genesis 363 self.message_channel(
genesis 364 channel, "TOPIC", "%s :%s" % (channelname, newtopic),
genesis 365 True)
genesis 366 else:
genesis 367 if channel.topic:
genesis 368 self.reply("332 %s %s :%s"
genesis 369 % (self.nickname, channel.name,
genesis 370 channel.topic))
genesis 371 else:
genesis 372 self.reply("331 %s %s :No topic is set"
genesis 373 % (self.nickname, channel.name))
genesis 374 else:
genesis 375 self.reply("442 %s :You're not on that channel" % channelname)
genesis 376
genesis 377 def wallops_handler():
genesis 378 if len(arguments) < 1:
genesis 379 self.reply_461(command)
genesis 380 message = arguments[0]
genesis 381 for client in server.clients.values():
genesis 382 client.message(":%s NOTICE %s :Global notice: %s"
genesis 383 % (self.prefix, client.nickname, message))
genesis 384
genesis 385 def who_handler():
genesis 386 if len(arguments) < 1:
genesis 387 return
genesis 388 targetname = arguments[0]
genesis 389 if server.has_channel(targetname):
genesis 390 channel = server.get_channel(targetname)
genesis 391 for member in channel.members:
genesis 392 self.reply("352 %s %s %s %s %s %s H :0 %s"
genesis 393 % (self.nickname, targetname, member.user,
genesis 394 member.host, server.name, member.nickname,
genesis 395 member.realname))
genesis 396 self.reply("315 %s %s :End of WHO list"
genesis 397 % (self.nickname, targetname))
genesis 398
genesis 399 def whois_handler():
genesis 400 if len(arguments) < 1:
genesis 401 return
genesis 402 username = arguments[0]
genesis 403 user = server.get_client(username)
genesis 404 if user:
genesis 405 self.reply("311 %s %s %s %s * :%s"
genesis 406 % (self.nickname, user.nickname, user.user,
genesis 407 user.host, user.realname))
genesis 408 self.reply("312 %s %s %s :%s"
genesis 409 % (self.nickname, user.nickname, server.name,
genesis 410 server.name))
genesis 411 self.reply("319 %s %s :%s"
genesis 412 % (self.nickname, user.nickname,
genesis 413 " ".join(user.channels)))
genesis 414 self.reply("318 %s %s :End of WHOIS list"
genesis 415 % (self.nickname, user.nickname))
genesis 416 else:
genesis 417 self.reply("401 %s %s :No such nick"
genesis 418 % (self.nickname, username))
genesis 419
genesis 420 def wot_handler():
genesis 421 if len(arguments) < 1:
genesis 422
9987-embargoing 423 peers = self.state.get_peers()
9992-handle-edge-... 424 if len(peers) > 0:
9992-handle-edge-... 425 for peer in peers:
9987-embargoing 426 if peer.address and peer.port:
9987-embargoing 427 address = "%s:%s" % (peer.address, peer.port)
9987-embargoing 428 else:
9987-embargoing 429 address = "<address not configured>"
9987-embargoing 430 self.pest_reply("%s %s" % (string.join(peer.handles, ","), address))
9992-handle-edge-... 431 else:
9992-handle-edge-... 432 self.pest_reply("WOT is empty")
genesis 433 elif len(arguments) == 1:
genesis 434
genesis 435
genesis 436 handle = arguments[0]
9987-embargoing 437 peer = self.state.get_peer_by_handle(handle)
9992-handle-edge-... 438 if peer:
9992-handle-edge-... 439 self.pest_reply("keys:")
9992-handle-edge-... 440 for key in peer.keys:
9984-unbork-at-co... 441 self.pest_reply("'%s'" % key)
9992-handle-edge-... 442 else:
9992-handle-edge-... 443 self.pest_reply("unknown peer: %s" % handle)
genesis 444
genesis 445 else:
genesis 446 pass
genesis 447
genesis 448 def peer_handler():
genesis 449 if len(arguments) == 1:
9991-improved-log... 450 try:
9987-embargoing 451 self.state.add_peer(arguments[0])
9991-improved-log... 452 self.pest_reply("added new peer %s" % arguments[0])
9989-show-wot-nicks 453 self.message(":%s JOIN %s" % (arguments[0], self.server.channel_name))
9984-unbork-at-co... 454 except Exception, ex:
9991-improved-log... 455 self.pest_reply("error attempting to add peer %s" % arguments[0])
9984-unbork-at-co... 456 stack = traceback.format_exc()
9984-unbork-at-co... 457 logging.debug(ex)
9984-unbork-at-co... 458 logging.debug(stack)
genesis 459 else:
genesis 460 self.pest_reply("Usage: PEER <HANDLE>")
genesis 461
genesis 462 def unpeer_handler():
genesis 463 if len(arguments) == 1:
genesis 464 try:
9987-embargoing 465 self.state.remove_peer(arguments[0])
genesis 466 self.pest_reply("removed peer %s" % arguments[0])
9989-show-wot-nicks 467 self.message(":%s PART %s" % (arguments[0], self.server.channel_name))
9984-unbork-at-co... 468 except Exception, ex:
9984-unbork-at-co... 469 self.pest_reply("Error attempting to remove peer: %s" % ex)
9984-unbork-at-co... 470 stack = traceback.format_exc()
9984-unbork-at-co... 471 logging.debug(stack)
genesis 472 else:
genesis 473 self.pest_reply("Usage: UNPEER <HANDLE>")
genesis 474
genesis 475 def genkey_handler():
genesis 476 self.pest_reply(base64.b64encode(os.urandom(64)))
genesis 477
genesis 478 def key_handler():
genesis 479 if len(arguments) != 2:
genesis 480 self.pest_reply("Usage: KEY <HANDLE> <KEY>")
genesis 481 else:
genesis 482 handle = arguments[0]
genesis 483 key = arguments[1]
genesis 484 try:
9987-embargoing 485 self.state.add_key(handle, key)
9992-handle-edge-... 486 self.pest_reply("added key: %s" % key)
genesis 487 except:
genesis 488 self.pest_reply("Error attempting to add key")
genesis 489
genesis 490 def unkey_handler():
genesis 491 if len(arguments) != 1:
genesis 492 self.pest_reply("Usage: UNKEY <KEY>")
genesis 493 else:
genesis 494 try:
9987-embargoing 495 self.state.remove_key(arguments[0])
9992-handle-edge-... 496 self.pest_reply("removed key: %s" % arguments[0])
9992-handle-edge-... 497 except Exception, e:
genesis 498 self.pest_reply("Error attempting to remove key")
9987-embargoing 499 logging.debug(e)
genesis 500
genesis 501 def at_handler():
genesis 502 if len(arguments) == 0:
9987-embargoing 503 at = self.state.get_at()
genesis 504 elif len(arguments) == 1:
genesis 505 handle = arguments[0]
9987-embargoing 506 at = self.state.get_at(handle)
genesis 507 elif len(arguments) == 2:
9990-keep-ephemer... 508 try:
9990-keep-ephemer... 509 handle, address = arguments
9990-keep-ephemer... 510 address_ip, port = string.split(address, ":")
9984-unbork-at-co... 511 self.state.update_at(
9984-unbork-at-co... 512 {"handle": handle,
9984-unbork-at-co... 513 "address": address_ip,
9984-unbork-at-co... 514 "port": int(port)},
9984-unbork-at-co... 515 False)
9990-keep-ephemer... 516 self.pest_reply("updated address table: %s %s" % (handle, address))
9990-keep-ephemer... 517 except Exception as ex:
9984-unbork-at-co... 518 self.pest_reply("Error attempting to update address table: %s" % ex)
9990-keep-ephemer... 519 stack = traceback.format_exc()
9986-rebroadcast-... 520 logging.debug(stack)
genesis 521 return
genesis 522 elif len(arguments) > 2:
genesis 523 self.pest_reply("Usage: AT [<HANDLE>] [<ADDRESS>]")
genesis 524 return
9992-handle-edge-... 525 if len(at) > 0:
9992-handle-edge-... 526 for address in at:
9992-handle-edge-... 527 self.pest_reply("%s %s %s" % (address["handle"],
9992-handle-edge-... 528 address["address"],
9991-improved-log... 529 address["active_at"]))
9992-handle-edge-... 530 else:
9992-handle-edge-... 531 self.pest_reply("no results")
genesis 532
9983-knobs 533 def knob_handler():
9983-knobs 534 if len(arguments) == 0:
9983-knobs 535 knobs = self.state.get_knobs()
9983-knobs 536 if len(knobs) > 0:
9983-knobs 537 for key in knobs.keys():
9983-knobs 538 self.pest_reply("%s %s" % (key, knobs[key]))
9983-knobs 539 else:
9983-knobs 540 self.pest_reply("no knobs configured")
9983-knobs 541 elif len(arguments) == 1:
9983-knobs 542 knob_value = self.state.get_knob(arguments[0])
9983-knobs 543 if knob:
9983-knobs 544 self.pest_reply("%s %s" % (arguments[0], knob_value))
9983-knobs 545 else:
9983-knobs 546 self.pest_reply("no such knob")
9983-knobs 547 elif len(arguments) == 2:
9983-knobs 548 self.state.set_knob(arguments[0], arguments[1])
9983-knobs 549 self.pest_reply("set %s to %s" % (arguments[0], arguments[1]))
9983-knobs 550 else:
9983-knobs 551 self.pest_reply("Usage: KNOB [<NAME>] [<VALUE>]")
9983-knobs 552
genesis 553 handler_table = {
genesis 554 "AWAY": away_handler,
genesis 555 "AT": at_handler,
genesis 556 "GENKEY": genkey_handler,
genesis 557 "ISON": ison_handler,
genesis 558 "JOIN": join_handler,
genesis 559 "KEY": key_handler,
genesis 560 "LIST": list_handler,
genesis 561 "LUSERS": lusers_handler,
genesis 562 "MODE": mode_handler,
genesis 563 "MOTD": motd_handler,
genesis 564 "NICK": nick_handler,
genesis 565 "NOTICE": notice_and_privmsg_handler,
genesis 566 "PART": part_handler,
genesis 567 "PEER": peer_handler,
genesis 568 "PING": ping_handler,
genesis 569 "PONG": pong_handler,
genesis 570 "PRIVMSG": notice_and_privmsg_handler,
genesis 571 "QUIT": quit_handler,
genesis 572 "TOPIC": topic_handler,
genesis 573 "UNKEY": unkey_handler,
genesis 574 "UNPEER": unpeer_handler,
genesis 575 "WALLOPS": wallops_handler,
genesis 576 "WHO": who_handler,
genesis 577 "WHOIS": whois_handler,
9983-knobs 578 "WOT": wot_handler,
9983-knobs 579 "KNOB": knob_handler
genesis 580 }
genesis 581 server = self.server
genesis 582 valid_channel_re = self.__valid_channelname_regexp
genesis 583 try:
genesis 584 handler_table[command]()
genesis 585 except KeyError:
9985-single-thread 586 self.reply("421 %s %s :Unknown command" % (self.nickname, command))
9985-single-thread 587 stack = traceback.format_exc()
9985-single-thread 588 logging.debug(stack)
genesis 589
genesis 590 def socket_readable_notification(self):
genesis 591 try:
genesis 592 data = self.socket.recv(2 ** 10)
9987-embargoing 593 logging.debug(
genesis 594 "[%s:%d] -> %r" % (self.host, self.port, data))
genesis 595 quitmsg = "EOT"
genesis 596 except socket.error as x:
genesis 597 data = ""
genesis 598 quitmsg = x
genesis 599 if data:
genesis 600 self.__readbuffer += data
genesis 601 self.__parse_read_buffer()
genesis 602 self.__timestamp = time.time()
genesis 603 self.__sent_ping = False
genesis 604 else:
genesis 605 self.disconnect(quitmsg)
genesis 606
genesis 607 def socket_writable_notification(self):
genesis 608 try:
genesis 609 sent = self.socket.send(self.__writebuffer)
9987-embargoing 610 logging.debug(
genesis 611 "[%s:%d] <- %r" % (
genesis 612 self.host, self.port, self.__writebuffer[:sent]))
genesis 613 self.__writebuffer = self.__writebuffer[sent:]
genesis 614 except socket.error as x:
genesis 615 self.disconnect(x)
genesis 616
genesis 617 def disconnect(self, quitmsg):
genesis 618 self.message("ERROR :%s" % quitmsg)
9987-embargoing 619 logging.info(
genesis 620 "Disconnected connection from %s:%s (%s)." % (
genesis 621 self.host, self.port, quitmsg))
genesis 622 self.socket.close()
genesis 623 self.server.remove_client(self, quitmsg)
genesis 624
genesis 625 def message(self, msg):
genesis 626 self.__writebuffer += msg + "\r\n"
genesis 627
genesis 628 def reply(self, msg):
genesis 629 self.message(":%s %s" % (self.server.name, msg))
genesis 630
genesis 631 def pest_reply(self, msg):
genesis 632 self.message("NOTICE %s :%s" % (self.nickname, msg))
genesis 633
genesis 634 def reply_403(self, channel):
genesis 635 self.reply("403 %s %s :No such channel" % (self.nickname, channel))
genesis 636
genesis 637 def reply_461(self, command):
genesis 638 nickname = self.nickname or "*"
genesis 639 self.reply("461 %s %s :Not enough parameters" % (nickname, command))
genesis 640
genesis 641 def message_channel(self, channel, command, message, include_self=False):
genesis 642 line = ":%s %s %s" % (self.prefix, command, message)
9987-embargoing 643 if include_self:
9987-embargoing 644 self.message(line)
genesis 645
genesis 646 def message_related(self, msg, include_self=False):
genesis 647 clients = set()
genesis 648 if include_self:
genesis 649 clients.add(self)
genesis 650 for channel in self.channels.values():
genesis 651 clients |= channel.members
genesis 652 if not include_self:
genesis 653 clients.discard(self)
genesis 654 for client in clients:
genesis 655 client.message(msg)
genesis 656
genesis 657 def send_motd(self):
genesis 658 server = self.server
genesis 659 motdlines = server.get_motd_lines()
genesis 660 if motdlines:
genesis 661 self.reply("375 %s :- %s Message of the day -"
genesis 662 % (self.nickname, server.name))
genesis 663 for line in motdlines:
genesis 664 self.reply("372 %s :- %s" % (self.nickname, line.rstrip()))
genesis 665 self.reply("376 %s :End of /MOTD command" % self.nickname)
genesis 666 else:
genesis 667 self.reply("422 %s :MOTD File is missing" % self.nickname)