import os import select import socket import time from funcs import * from client import Client from channel import Channel from message import PACKET_SIZE import pprint import logging class Server(object): def __init__(self, options, station): self.station = station self.irc_ports = options.irc_ports self.udp_port = options.udp_port self.channel_name = options.channel_name self.password = options.password self.motdfile = options.motd self.pp = pprint.PrettyPrinter(indent=4) self.db_path = options.db_path self.address_table_path = options.address_table_path self.irc_server_address = "127.0.0.1" if options.listen: self.udp_address = socket.gethostbyname(options.listen) else: self.udp_address = "" server_name_limit = 63 # From the RFC. self.name = socket.getfqdn(self.udp_address)[:server_name_limit] self.channels = {} # irc_lower(Channel name) --> Channel instance. self.client = None self.nicknames = {} # irc_lower(Nickname) --> Client instance. def get_client(self, nickname): return self.nicknames.get(irc_lower(nickname)) def has_channel(self, name): return irc_lower(name) in self.channels def get_channel(self, channelname): if irc_lower(channelname) in self.channels: channel = self.channels[irc_lower(channelname)] else: channel = Channel(self, channelname) self.channels[irc_lower(channelname)] = channel return channel def get_motd_lines(self): if self.motdfile: try: return open(self.motdfile).readlines() except IOError: return ["Could not read MOTD file %r." % self.motdfile] else: return [] def client_changed_nickname(self, client, oldnickname): if oldnickname: del self.nicknames[irc_lower(oldnickname)] self.nicknames[irc_lower(client.nickname)] = client def remove_member_from_channel(self, client, channelname): if irc_lower(channelname) in self.channels: channel = self.channels[irc_lower(channelname)] channel.remove_client(client) def remove_client(self, client, quitmsg): client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg)) for x in client.channels.values(): x.remove_client(client) if client.nickname \ and irc_lower(client.nickname) in self.nicknames: del self.nicknames[irc_lower(client.nickname)] self.client = None def remove_channel(self, channel): del self.channels[irc_lower(channel.name)] def start(self): # Setup UDP first self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM) self.udp_server_socket.bind((self.udp_address, self.udp_port)) self.station.socket = self.udp_server_socket logging.info("Listening for Pest packets on udp port %d." % self.udp_port) serversockets = [] for port in self.irc_ports: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) try: s.bind((self.irc_server_address, port)) except socket.error as e: logging.error("Could not bind port %s: %s." % (port, e)) sys.exit(1) s.listen(5) serversockets.append(s) del s logging.info("Listening for IRC connections on port %d." % port) # event loop setup last_aliveness_check = time.time() last_short_buffer_check = time.time() last_rubbish_dispatch = time.time() last_order_buffer_check = time.time() last_presence_check = time.time() while True: # we don't want to be listening for client connections if there's already a client connected if self.client == None: input_sockets = serversockets else: input_sockets = [self.client.socket] output_sockets = ([self.client.socket] if self.client and self.client.write_queue_size() > 0 else []) # handle tcp socket events (iwtd, owtd, ewtd) = select.select(input_sockets, output_sockets, [], .2) for x in iwtd: if self.client != None: self.client.socket_readable_notification() else: try: (conn, addr) = x.accept() self.client = Client(self, conn) self.station.client = self.client self.client.state = self.station.state self.client.long_buffer = self.station.long_buffer logging.info("Accepted connection from %s:%s." % ( addr[0], addr[1])) except socket.error as e: logging.error("Failed to accept new client connection: %s" % e) for x in owtd: if self.client and x == self.client.socket: # client may have been disconnected self.client.socket_writable_notification() # handle udp socket events (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0) for x in inputready: if x == self.udp_server_socket: bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE) self.station.handle_udp_data(bytes_address_pair) # ping pong now = time.time() if last_aliveness_check + 10 < now: if self.client: self.client.check_aliveness() last_aliveness_check = now # clear embargo queue if enough time has elapsed if last_short_buffer_check + int(self.station.state.get_knob('short_buffer_check_interval_seconds')) < now: self.station.check_short_buffer() last_short_buffer_check = now # spray rubbish if last_rubbish_dispatch + int(self.station.state.get_knob('rubbish_interval_seconds')) < now: self.station.send_rubbish() last_rubbish_dispatch = now # check order buffer if last_order_buffer_check + int(self.station.state.get_knob('order_buffer_check_seconds')) < now: self.station.check_order_buffer() last_order_buffer_check = now # check presence if last_presence_check + int(self.station.state.get_knob('presence_check_seconds')) < now: self.station.report_presence() last_presence_check = now def create_directory(path): if not os.path.isdir(path): os.makedirs(path)