text: #! /usr/bin/env python
  # -*-python-*-
  # Joel Rosdahl <joel@rosdahl.net>
  
  VERSION = "0.4"
  
  import os
  import re
  import select
  import socket
  import string
  import sys
  import tempfile
  import time
  from datetime import datetime
  from optparse import OptionParser
  
  
  def create_directory(path):
      if not os.path.isdir(path):
          os.makedirs(path)
  
  class Channel(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)
          timestamp = datetime.utcnow().strftime("%H:%M:%S") # %Y-%m-%d %H:%M:%S UTC
          print timestamp, client.nickname, 'joined', '('+client.host+':'+str(client.port)+')'
  
  
      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)
          timestamp = datetime.utcnow().strftime("%H:%M:%S") # %Y-%m-%d %H:%M:%S UTC
          print timestamp, client.nickname, '\tleft'
          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 Client(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
          if self.server.password:
              self.__handle_command = self.__pass_handler
          else:
              self.__handle_command = self.__registration_handler
  
      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() == server.password:
                      self.__handle_command = self.__registration_handler
                  else:
                      self.reply("464 :Password incorrect")
          elif command == "QUIT":
              self.disconnect("Client quit")
              return
  
      def __registration_handler(self, command, arguments):
          server = self.server
          if command == "NICK":
              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 :irc server running" % self.nickname)
              self.reply("002 %s :host is %s" % (self.nickname, server.name))
              self.send_lusers()
              self.send_motd()
              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 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))
                  self.reply("353 %s = %s :%s"
                             % (self.nickname,
                                channelname,
                                " ".join(sorted(x.nickname
                                                for x in channel.members))))
                  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)
                  timestamp = datetime.utcnow().strftime("%H:%M:%S")
                  print timestamp, oldnickname, 'is now:', self.nickname
                  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 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
              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:
                  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)
              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 = arguments[0]
              if server.has_channel(targetname):
                  channel = server.get_channel(targetname)
                  for member in channel.members:
                      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:
                  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,
          }
          server = self.server
          valid_channel_re = self.__valid_channelname_regexp
          try:
              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, 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, 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):
          line = ":%s %s %s" % (self.prefix, command, message)
          for client in channel.members:
              if client != self or include_self:
                  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("/", "_")
          fp = open("%s/%s.log" % (self.server.logdir, logname), "a")
          fp.write(format % (timestamp, self.nickname, message))
          fp.close()
  
      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:
              client.message(msg)
  
      def send_lusers(self):
          self.reply("251 %s :There are %d users and 0 services on 1 server"
                     % (self.nickname, len(self.server.clients)))
  
      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 Server(object):
      def __init__(self, options):
          self.ports = options.ports
          self.password = options.password
          self.motdfile = options.motd
          self.verbose = options.verbose
          self.debug = options.debug
          self.logdir = options.logdir
          self.statedir = options.statedir
          self.name = socket.getfqdn()[:63]  # Server name limit from the RFC.
          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)
  
      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 = 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 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):
          serversockets = []
          for port in self.ports:
              s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
              s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
              try:
                  s.bind(("", port))
              except socket.error, x:
                  self.print_error("Could not bind port %s: %s." % (port, x))
                  sys.exit(1)
              s.listen(5)
              serversockets.append(s)
              del s
              self.print_info("Listening on port %d." % port)
          last_aliveness_check = time.time()
          while True:
              (iwtd, owtd, ewtd) = select.select(
                  serversockets + [x.socket for x in self.clients.values()],
                  [x.socket for x in self.clients.values()
                            if x.write_queue_size() > 0],
                  [],
                  10)
              for x in iwtd:
                  if x in self.clients:
                      self.clients[x].socket_readable_notification()
                  else:
                      (conn, addr) = x.accept()
                      self.clients[conn] = Client(self, conn)
                      self.print_info("Accepted connection from %s:%s." % (
                          addr[0], addr[1]))
              for x in owtd:
                  if x in self.clients: # client may have been disconnected
                      self.clients[x].socket_writable_notification()
              now = time.time()
              if last_aliveness_check + 10 < now:
                  for client in self.clients.values():
                      client.check_aliveness()
                  last_aliveness_check = now
  
  
  _alpha = "abcdefghijklmnopqrstuvwxyz"
  _ircstring_translation = string.maketrans(
      string.upper(_alpha) + "[]\\^",
      _alpha + "{}|~")
  
  
  def irc_lower(s):
      return string.translate(s, _ircstring_translation)
  
  
  def main(argv):
      op = OptionParser(
          version=VERSION,
          description="miniircd is a small and limited IRC server.")
      op.add_option(
          "-d", "--daemon",
          action="store_true",
          help="fork and become a daemon")
      op.add_option(
          "--debug",
          action="store_true",
          help="print debug messages to stdout")
      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(
          "-p", "--password",
          metavar="X",
          help="require connection password X; default: no password")
      op.add_option(
          "--ports",
          metavar="X",
          help="listen to ports X (a list separated by comma or whitespace);"
               " default: 6667")
      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)")
      op.set_defaults(ports="6667")
      (options, args) = op.parse_args(argv[1:])
      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 = Server(options)
      if options.daemon:
          server.daemonize()
      try:
          server.start()
          timestamp = datetime.utcnow().strftime("%H:%M:%S")
          print timestamp, 'server started ...'
          #server.get_channel('mode+v')
          #server.get_channel('#mode+v')
      except KeyboardInterrupt:
          server.print_error("Interrupted.")
  
  
  main(sys.argv)
  
  # ex:et:sw=4:ts=4
  