##
## server.py -- IRC server interface.
##
## Copyleft 1997 Teemu Kalvas <chery@s2.org>
##

## +class Server             (p,       persistent storage object
##                            server)  configuration data
##  -method read             (p)       persistent storage object
##  -method write            (p)       persistent storage object
##  -method handle_XXX       (p,       persistent storage object
##                            nick,    nickname or server name of sender
##                            addr,    address of sender
##                            args)    command tail
##  -method handle_ctcp_XXX  (p,       persistent storage object
##                            nick,    nickname or server name of sender
##                            addr,    address of sender
##                            chan,    target channel or nickname
##                            args)    ctcp command arguments
##  -method sock
##  -method out

import regex
import socket
import string

low_table = string.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]',
			     'abcdefghijklmnopqrstuvwxyz{|}')

def low(str):
    return string.translate(str, low_table)

class Server:
    def __init__(self, p, server):
	self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	self.sock.connect(server[:2])
	self.nicks = server[2]
	self.out = ['USER ' + p.login + ' * * :' + p.realname,
		    'NICK ' + self.nicks[0]]
	for chan in server[3]:
	    self.out.append('JOIN ' + chan)
	self.ind = ''
	self.window = p.screen.new_window('', self)
	self.channels = {}
	self.rcline = regex.compile(':\([^ !]*\)\(!\([^ ]*\)\)? \([a-zA-Z0-9]*\) \(.*\)')
	self.rcctcp = regex.compile('\001\([^ ]+\)\( \(.*\)\)?\001')
	self.connected = 1

    def read(self, p):
	ind = self.sock.recv(768)
	if ind == '':
	    self.disconnected = 1
	    return
	self.ind = self.ind + ind
	f = string.find(self.ind, '\r\n')
	while f <> -1:
	    line, self.ind = self.ind[:f], self.ind[f + 2:]
	    if regex.match('PING', line) > 0:
		self.out.append('PONG' + line[4:])
	    elif self.rcline.match(line) > 0:
		nick, addr, command, tail = self.rcline.group(1, 3, 4, 5)
		command = string.lower(command)
		args = []
		while 1:
		    if len(tail) == 0:
			break
		    if tail[0] == ':':
			args.append(tail[1:])
			break
		    f = string.find(tail, ' ')
		    if f == -1:
			break
		    args.append(tail[:f])
		    tail = tail[f + 1:]
		if Server.__dict__.has_key('handle_' + command):
		    Server.__dict__['handle_' + command](self, p, nick, addr,
		    args)
	    f = string.find(self.ind, '\r\n')

    def write(self, p):
	self.sock.send(self.out[0] + '\r\n')
	del self.out[0]

    ## :irc-2.cs.hut.fi 001 ilciin :Welcome to the Internet Relay Network \
    ## ilciin!chery@a31b.mtalo.ton.tut.fi
    def handle_001(self, p, nick, addr, args):
	self.name = nick
	self.window.set_name(nick)
	self.nick = args[0]

    ## :irc-2.cs.hut.fi 353 ilciin = #qwer :@ilciin 
    def handle_353(self, p, nick, addr, args):
	chan = low(args[2])
	self.channels[chan][0].log('*** users on channel ' + args[2] + ': '
				      + args[3])
	for nick in string.split(args[3]):
	    if nick[0] == '@' or nick[0] == '+':
		lnick = low(nick[1:])
	    else:
		lnick = low(nick)
	    self.channels[chan][1][lnick] = nick

    ## :irc-2.cs.hut.fi 433 ilciin cliini :Nickname is already in use.
    def handle_433(self, p, nick, addr, args):
	self.nicks = self.nicks[1:] + self.nicks[:1]
	self.out.append('NICK ' + self.nicks[0])

    ## :ilciin!chery@a31b.mtalo.ton.tut.fi JOIN :#qwer
    def handle_join(self, p, nick, addr, args):
	chan = low(args[0])
	if nick == self.nick:
	    self.channels[chan] = p.screen.new_window(args[0], self), {}
	self.channels[chan][0].log('*** ' + nick + ' (' + addr +
				   ') has joined channel ' + args[0])
	self.channels[chan][1][low(nick)] = ''

    ## :ilciin!chery@a31b.mtalo.ton.tut.fi KICK #qwer ilciin :bleh
    def handle_kick(self, p, nick, addr, args):
	chan = low(args[0])
	self.channels[chan][0].log('*** ' + nick + ' has kicked ' + args[1] +
				   ' from channel ' + args[0] + ' (' +
				   args[2] + ')')
	del self.channels[chan][1][low(args[1])]

    ## :ilciin!chery@a31b.mtalo.ton.tut.fi MODE #qwer +snt 
    def handle_mode(self, p, nick, addr, args):
	chan = low(args[0])
	if self.channels.has_key(chan):
	    self.channels[chan][0].log('*** ' + nick + ' has set mode ' +
				       reduce(lambda x, y: x + ' ' + y, args))
	else:
	    self.window.log('*** ' + nick + ' has set mode ' +
			    reduce(lambda x, y: x + ' ' + y, args))

    ## :ilciin!chery@a31b.mtalo.ton.tut.fi NICK :niicli
    def handle_nick(self, p, nick, addr, args):
	lnick = low(nick)
	for chan in self.channels.values():
	    if chan[1].has_key(lnick):
		chan[0].log('*** ' + nick + ' is now known as ' + args[0])
		if lnick <> low(args[0]):
		    chan[1][low(args[0])] = chan[1][lnick]
		    del chan[1][lnick]

    ## :ilciin!chery@a31b.mtalo.ton.tut.fi PART #qwer :Nymmä lähren enkä \
    ## takasin tuu!
    def handle_part(self, p, nick, addr, args):
	chan = low(args[0])
	self.channels[chan][0].log('*** ' + nick + ' has left channel ' +
				   args[0] + ' (' + args[1] + ')')
	del self.channels[chan][1][low(nick)]

    ## :cliini!chery@tjtk2.trin.cam.ac.uk PRIVMSG ilciin :bleh
    def handle_privmsg(self, p, nick, addr, args):
	chan = low(args[0])
	if self.rcctcp.match(args[1]) > 0:
	    command, tail = self.rcctcp.group(1, 3)
	    command = string.lower(command)
	    if Server.__dict__.has_key('handle_ctcp_' + command):
		Server.__dict__['handle_ctcp_' + command](self, p, nick, addr,
							  chan, tail)
	elif self.channels.has_key(chan):
	    self.channels[chan][0].log('<' + nick + '> ' + args[1])
	else:
	    self.window.log('*' + nick + '* ' + args[1])

    ## ERROR :Closing Link: ilciin[chery@a31b.mtalo.ton.tut.fi] (ör.)
    def handle_quit(self, p, nick, addr, args):
	lnick = low(nick)
	for chan in self.channels.values():
	    if chan[1].has_key(lnick):
		chan[0].log('*** ' + nick + ' has quit irc (' + args[0] + ')')
		del chan[1][lnick]

    def handle_ctcp_action(self, p, nick, addr, chan, args):
	if self.channels.has_key(chan):
	    self.channels[chan][0].log('* ' + nick + ' ' + args)
	else:
	    self.window.log('* ' + nick + ' ' + args)

    def handle_ctcp_ping(self, p, nick, addr, chan, args):
	self.out.append('NOTICE ' + nick + ' :\001PING ' + args + '\001')

    def handle_ctcp_version(self, p, nick, addr, chan, args):
	self.out.append('NOTICE ' + nick + ' :\001VERSION clirc 0.0.0\001')

## End. ##
