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