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