raw
genesis                 1 import os
genesis 2 import select
genesis 3 import socket
genesis 4 import time
9987-embargoing 5 from funcs import *
9985-single-thread 6 from client import Client
9985-single-thread 7 from channel import Channel
9982-getdata 8 from message import PACKET_SIZE
genesis 9 import pprint
9987-embargoing 10 import logging
genesis 11
genesis 12 class Server(object):
9982-getdata 13 def __init__(self, options, station):
9982-getdata 14 self.station = station
9991-improved-log... 15 self.irc_ports = options.irc_ports
genesis 16 self.udp_port = options.udp_port
9989-show-wot-nicks 17 self.channel_name = options.channel_name
genesis 18 self.password = options.password
genesis 19 self.motdfile = options.motd
genesis 20 self.pp = pprint.PrettyPrinter(indent=4)
9987-embargoing 21 self.db_path = options.db_path
9987-embargoing 22 self.address_table_path = options.address_table_path
9985-single-thread 23 self.irc_server_address = "127.0.0.1"
genesis 24
genesis 25 if options.listen:
9985-single-thread 26 self.udp_address = socket.gethostbyname(options.listen)
genesis 27 else:
9985-single-thread 28 self.udp_address = ""
genesis 29 server_name_limit = 63 # From the RFC.
9985-single-thread 30 self.name = socket.getfqdn(self.udp_address)[:server_name_limit]
genesis 31
genesis 32 self.channels = {} # irc_lower(Channel name) --> Channel instance.
9987-embargoing 33 self.client = None
genesis 34 self.nicknames = {} # irc_lower(Nickname) --> Client instance.
9987-embargoing 35
genesis 36 def get_client(self, nickname):
genesis 37 return self.nicknames.get(irc_lower(nickname))
genesis 38
genesis 39 def has_channel(self, name):
genesis 40 return irc_lower(name) in self.channels
genesis 41
genesis 42 def get_channel(self, channelname):
genesis 43 if irc_lower(channelname) in self.channels:
genesis 44 channel = self.channels[irc_lower(channelname)]
genesis 45 else:
genesis 46 channel = Channel(self, channelname)
genesis 47 self.channels[irc_lower(channelname)] = channel
genesis 48 return channel
genesis 49
genesis 50 def get_motd_lines(self):
genesis 51 if self.motdfile:
genesis 52 try:
genesis 53 return open(self.motdfile).readlines()
genesis 54 except IOError:
genesis 55 return ["Could not read MOTD file %r." % self.motdfile]
genesis 56 else:
genesis 57 return []
genesis 58
genesis 59 def client_changed_nickname(self, client, oldnickname):
genesis 60 if oldnickname:
genesis 61 del self.nicknames[irc_lower(oldnickname)]
genesis 62 self.nicknames[irc_lower(client.nickname)] = client
genesis 63
genesis 64 def remove_member_from_channel(self, client, channelname):
genesis 65 if irc_lower(channelname) in self.channels:
genesis 66 channel = self.channels[irc_lower(channelname)]
genesis 67 channel.remove_client(client)
genesis 68
genesis 69 def remove_client(self, client, quitmsg):
genesis 70 client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg))
genesis 71 for x in client.channels.values():
genesis 72 x.remove_client(client)
genesis 73 if client.nickname \
genesis 74 and irc_lower(client.nickname) in self.nicknames:
genesis 75 del self.nicknames[irc_lower(client.nickname)]
9987-embargoing 76 self.client = None
genesis 77
genesis 78 def remove_channel(self, channel):
genesis 79 del self.channels[irc_lower(channel.name)]
genesis 80
genesis 81 def start(self):
genesis 82 # Setup UDP first
genesis 83 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
9985-single-thread 84 self.udp_server_socket.bind((self.udp_address, self.udp_port))
9982-getdata 85 self.station.socket = self.udp_server_socket
9987-embargoing 86 logging.info("Listening for Pest packets on udp port %d." % self.udp_port)
genesis 87
genesis 88 serversockets = []
9991-improved-log... 89 for port in self.irc_ports:
genesis 90 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
genesis 91 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
genesis 92 try:
9985-single-thread 93 s.bind((self.irc_server_address, port))
genesis 94 except socket.error as e:
9987-embargoing 95 logging.error("Could not bind port %s: %s." % (port, e))
genesis 96 sys.exit(1)
genesis 97 s.listen(5)
genesis 98 serversockets.append(s)
genesis 99 del s
9987-embargoing 100 logging.info("Listening for IRC connections on port %d." % port)
9985-single-thread 101
9985-single-thread 102 # event loop setup
genesis 103 last_aliveness_check = time.time()
9982-getdata 104 last_short_buffer_check = time.time()
9985-single-thread 105 last_rubbish_dispatch = time.time()
9982-getdata 106 last_order_buffer_check = time.time()
9979-presence 107 last_presence_check = time.time()
9982-getdata 108
genesis 109 while True:
9985-single-thread 110 # we don't want to be listening for client connections if there's already a client connected
9985-single-thread 111 if self.client == None:
9985-single-thread 112 input_sockets = serversockets
9985-single-thread 113 else:
9985-single-thread 114 input_sockets = [self.client.socket]
9985-single-thread 115 output_sockets = ([self.client.socket]
9985-single-thread 116 if self.client and self.client.write_queue_size() > 0 else [])
9985-single-thread 117
9985-single-thread 118 # handle tcp socket events
9985-single-thread 119 (iwtd, owtd, ewtd) = select.select(input_sockets, output_sockets, [], .2)
genesis 120 for x in iwtd:
9987-embargoing 121 if self.client != None:
9987-embargoing 122 self.client.socket_readable_notification()
genesis 123 else:
9985-single-thread 124 try:
9985-single-thread 125 (conn, addr) = x.accept()
9985-single-thread 126 self.client = Client(self, conn)
9985-single-thread 127 self.station.client = self.client
9982-getdata 128 self.client.state = self.station.state
9982-getdata 129 self.client.long_buffer = self.station.long_buffer
9985-single-thread 130 logging.info("Accepted connection from %s:%s." % (
9985-single-thread 131 addr[0], addr[1]))
9985-single-thread 132 except socket.error as e:
9985-single-thread 133 logging.error("Failed to accept new client connection: %s" % e)
genesis 134 for x in owtd:
9987-embargoing 135 if self.client and x == self.client.socket: # client may have been disconnected
9987-embargoing 136 self.client.socket_writable_notification()
9985-single-thread 137
9985-single-thread 138 # handle udp socket events
9985-single-thread 139 (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0)
9985-single-thread 140 for x in inputready:
9985-single-thread 141 if x == self.udp_server_socket:
9985-single-thread 142 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
9985-single-thread 143 self.station.handle_udp_data(bytes_address_pair)
9985-single-thread 144
9985-single-thread 145 # ping pong
genesis 146 now = time.time()
genesis 147 if last_aliveness_check + 10 < now:
9985-single-thread 148 if self.client:
9985-single-thread 149 self.client.check_aliveness()
9987-embargoing 150 last_aliveness_check = now
genesis 151
9985-single-thread 152 # clear embargo queue if enough time has elapsed
9982-getdata 153 if last_short_buffer_check + int(self.station.state.get_knob('short_buffer_check_interval_seconds')) < now:
9982-getdata 154 self.station.check_short_buffer()
9982-getdata 155 last_short_buffer_check = now
9985-single-thread 156
9985-single-thread 157 # spray rubbish
9982-getdata 158 if last_rubbish_dispatch + int(self.station.state.get_knob('rubbish_interval_seconds')) < now:
9985-single-thread 159 self.station.send_rubbish()
9985-single-thread 160 last_rubbish_dispatch = now
9985-single-thread 161
9982-getdata 162 # check order buffer
9982-getdata 163 if last_order_buffer_check + int(self.station.state.get_knob('order_buffer_check_seconds')) < now:
9982-getdata 164 self.station.check_order_buffer()
9982-getdata 165 last_order_buffer_check = now
9982-getdata 166
9979-presence 167 # check presence
9979-presence 168 if last_presence_check + int(self.station.state.get_knob('presence_check_seconds')) < now:
9979-presence 169 self.station.report_presence()
9979-presence 170 last_presence_check = now
9979-presence 171
genesis 172 def create_directory(path):
genesis 173 if not os.path.isdir(path):
genesis 174 os.makedirs(path)
genesis 175