raw
9987-embargoing         1 VERSION = "9987"
genesis 2
genesis 3 import os
genesis 4 import select
genesis 5 import socket
genesis 6 import sys
genesis 7 import sys
genesis 8 import tempfile
genesis 9 import time
genesis 10 import string
9991-improved-log... 11 import datetime
9987-embargoing 12 import sqlite3
genesis 13 from datetime import datetime
9987-embargoing 14 from funcs import *
genesis 15 from lib.client import Client
genesis 16 from lib.channel import Channel
9987-embargoing 17 from lib.station import Station
9990-keep-ephemer... 18 from lib.message import Message
9987-embargoing 19 from lib.infosec import PACKET_SIZE
genesis 20 import imp
genesis 21 import pprint
9987-embargoing 22 import logging
genesis 23
genesis 24 class Server(object):
genesis 25 def __init__(self, options):
9991-improved-log... 26 self.irc_ports = options.irc_ports
genesis 27 self.udp_port = options.udp_port
9989-show-wot-nicks 28 self.channel_name = options.channel_name
genesis 29 self.password = options.password
genesis 30 self.motdfile = options.motd
genesis 31 self.verbose = options.verbose
genesis 32 self.logdir = options.logdir
genesis 33 self.chroot = options.chroot
genesis 34 self.setuid = options.setuid
genesis 35 self.statedir = options.statedir
genesis 36 self.config_file_path = options.config_file_path
genesis 37 self.pp = pprint.PrettyPrinter(indent=4)
9987-embargoing 38 self.db_path = options.db_path
9987-embargoing 39 self.address_table_path = options.address_table_path
genesis 40
genesis 41 if options.listen:
genesis 42 self.address = socket.gethostbyname(options.listen)
genesis 43 else:
genesis 44 self.address = ""
genesis 45 server_name_limit = 63 # From the RFC.
genesis 46 self.name = socket.getfqdn(self.address)[:server_name_limit]
genesis 47
genesis 48 self.channels = {} # irc_lower(Channel name) --> Channel instance.
9987-embargoing 49 self.client = None
genesis 50 self.nicknames = {} # irc_lower(Nickname) --> Client instance.
9987-embargoing 51
genesis 52 if self.logdir:
genesis 53 create_directory(self.logdir)
genesis 54 if self.statedir:
genesis 55 create_directory(self.statedir)
genesis 56
genesis 57 def daemonize(self):
genesis 58 try:
genesis 59 pid = os.fork()
genesis 60 if pid > 0:
genesis 61 sys.exit(0)
genesis 62 except OSError:
genesis 63 sys.exit(1)
genesis 64 os.setsid()
genesis 65 try:
genesis 66 pid = os.fork()
genesis 67 if pid > 0:
9987-embargoing 68 logging.info("PID: %d" % pid)
genesis 69 sys.exit(0)
genesis 70 except OSError:
genesis 71 sys.exit(1)
genesis 72 os.chdir("/")
genesis 73 os.umask(0)
genesis 74 dev_null = open("/dev/null", "r+")
genesis 75 os.dup2(dev_null.fileno(), sys.stdout.fileno())
genesis 76 os.dup2(dev_null.fileno(), sys.stderr.fileno())
genesis 77 os.dup2(dev_null.fileno(), sys.stdin.fileno())
genesis 78
genesis 79 def get_client(self, nickname):
genesis 80 return self.nicknames.get(irc_lower(nickname))
genesis 81
genesis 82 def has_channel(self, name):
genesis 83 return irc_lower(name) in self.channels
genesis 84
genesis 85 def get_channel(self, channelname):
genesis 86 if irc_lower(channelname) in self.channels:
genesis 87 channel = self.channels[irc_lower(channelname)]
genesis 88 else:
genesis 89 channel = Channel(self, channelname)
genesis 90 self.channels[irc_lower(channelname)] = channel
genesis 91 return channel
genesis 92
genesis 93 def get_motd_lines(self):
genesis 94 if self.motdfile:
genesis 95 try:
genesis 96 return open(self.motdfile).readlines()
genesis 97 except IOError:
genesis 98 return ["Could not read MOTD file %r." % self.motdfile]
genesis 99 else:
genesis 100 return []
genesis 101
genesis 102 def client_changed_nickname(self, client, oldnickname):
genesis 103 if oldnickname:
genesis 104 del self.nicknames[irc_lower(oldnickname)]
genesis 105 self.nicknames[irc_lower(client.nickname)] = client
genesis 106
genesis 107 def remove_member_from_channel(self, client, channelname):
genesis 108 if irc_lower(channelname) in self.channels:
genesis 109 channel = self.channels[irc_lower(channelname)]
genesis 110 channel.remove_client(client)
genesis 111
genesis 112 def remove_client(self, client, quitmsg):
genesis 113 client.message_related(":%s QUIT :%s" % (client.prefix, quitmsg))
genesis 114 for x in client.channels.values():
genesis 115 x.remove_client(client)
genesis 116 if client.nickname \
genesis 117 and irc_lower(client.nickname) in self.nicknames:
genesis 118 del self.nicknames[irc_lower(client.nickname)]
9987-embargoing 119 self.client = None
genesis 120
genesis 121 def remove_channel(self, channel):
genesis 122 del self.channels[irc_lower(channel.name)]
genesis 123
genesis 124 def start(self):
genesis 125 # Setup UDP first
genesis 126 self.udp_server_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
genesis 127 self.udp_server_socket.bind((self.address, self.udp_port))
9987-embargoing 128 self.station = Station({ "socket": self.udp_server_socket,
9987-embargoing 129 "db_path": self.db_path,
9987-embargoing 130 "address_table_path": self.address_table_path
9987-embargoing 131 })
9987-embargoing 132 self.station.start_embargo_queue_checking()
9987-embargoing 133 self.station.start_rubbish()
9987-embargoing 134 logging.info("Listening for Pest packets on udp port %d." % self.udp_port)
genesis 135
genesis 136 serversockets = []
9991-improved-log... 137 for port in self.irc_ports:
genesis 138 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
genesis 139 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
genesis 140 try:
genesis 141 s.bind((self.address, port))
genesis 142 except socket.error as e:
9987-embargoing 143 logging.error("Could not bind port %s: %s." % (port, e))
genesis 144 sys.exit(1)
genesis 145 s.listen(5)
genesis 146 serversockets.append(s)
genesis 147 del s
9987-embargoing 148 logging.info("Listening for IRC connections on port %d." % port)
genesis 149 if self.chroot:
genesis 150 os.chdir(self.chroot)
genesis 151 os.chroot(self.chroot)
9987-embargoing 152 logging.info("Changed root directory to %s" % self.chroot)
genesis 153 if self.setuid:
genesis 154 os.setgid(self.setuid[1])
genesis 155 os.setuid(self.setuid[0])
9987-embargoing 156 logging.info("Setting uid:gid to %s:%s"
genesis 157 % (self.setuid[0], self.setuid[1]))
genesis 158 last_aliveness_check = time.time()
genesis 159 while True:
genesis 160 (inputready,outputready,exceptready) = select.select([self.udp_server_socket],[],[],0)
genesis 161 (iwtd, owtd, ewtd) = select.select(
9987-embargoing 162 serversockets + ([self.client.socket] if self.client else []),
9987-embargoing 163 [self.client.socket] if self.client and self.client.write_queue_size() > 0 else [],
genesis 164 [],
9990-keep-ephemer... 165 .2)
genesis 166 for x in inputready:
9987-embargoing 167 if x == self.udp_server_socket:
9987-embargoing 168 bytes_address_pair = self.udp_server_socket.recvfrom(PACKET_SIZE)
9987-embargoing 169 self.station.embargo_queue_lock.acquire()
9987-embargoing 170 try:
9987-embargoing 171 self.station.handle_udp_data(bytes_address_pair)
9987-embargoing 172 except sqlite3.ProgrammingError as ex:
9987-embargoing 173 logging.error("sqlite3 concurrency problem")
9987-embargoing 174 self.station.embargo_queue_lock.release()
genesis 175 for x in iwtd:
9987-embargoing 176 if self.client != None:
9987-embargoing 177 self.client.socket_readable_notification()
genesis 178 else:
genesis 179 (conn, addr) = x.accept()
9987-embargoing 180 self.client = Client(self, conn)
9987-embargoing 181 self.station.client = self.client
9987-embargoing 182 logging.info("Accepted connection from %s:%s." % (
genesis 183 addr[0], addr[1]))
genesis 184 for x in owtd:
9987-embargoing 185 if self.client and x == self.client.socket: # client may have been disconnected
9987-embargoing 186 self.client.socket_writable_notification()
genesis 187 now = time.time()
genesis 188 if last_aliveness_check + 10 < now:
9987-embargoing 189 if self.client:
9987-embargoing 190 self.client.check_aliveness()
9987-embargoing 191 last_aliveness_check = now
genesis 192
genesis 193 def create_directory(path):
genesis 194 if not os.path.isdir(path):
genesis 195 os.makedirs(path)
genesis 196