toc.py :  » Network » Twisted » Twisted-1.0.3 » Twisted-1.0.3 » twisted » protocols » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Network » Twisted 
Twisted » Twisted 1.0.3 » Twisted 1.0.3 » twisted » protocols » toc.py

# Twisted, the Framework of Your Internet
# Copyright (C) 2001 Matthew W. Lefkowitz
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""
Implements a AOL Instant Messenger TOC server and client, using the Twisted framework.

TODO:
info,dir: see how gaim connects for this...it may never work if it tries to connect to the aim server automatically

This module is stable, but deprecated.

Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
"""

# twisted imports
from twisted.internet import reactor,protocol
from twisted.python import log

# base imports
import struct
import string
import time
import base64
import os
import StringIO

SIGNON,DATA,ERROR,SIGNOFF,KEEP_ALIVE=range(1,6)
PERMITALL,DENYALL,PERMITSOME,DENYSOME=range(1,5)

def quote(s):
    rep=['\\','$','{','}','[',']','(',')','"']
    for r in rep:
        s=string.replace(s,r,"\\"+r)
    return "\""+s+"\""

def unquote(s):
    if s=="": return ""
    if s[0]!='"': return s
    r=string.replace
    s=s[1:-1]
    s=r(s,"\\\\","\\")
    s=r(s,"\\$","$")
    s=r(s,"\\{","{")
    s=r(s,"\\}","}")
    s=r(s,"\\[","[")
    s=r(s,"\\]","]")
    s=r(s,"\\(","(")
    s=r(s,"\\)",")")
    s=r(s,"\\\"","\"")
    return s

def unquotebeg(s):
    for i in range(1,len(s)):
        if s[i]=='"' and s[i-1]!='\\':
            q=unquote(s[:i+1])
            return [q,s[i+2:]]

def unroast(pw):
    roaststring="Tic/Toc"
    pw=string.lower(pw[2:])
    r=""
    count=0
    hex=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
    while pw:
        st,pw=pw[:2],pw[2:]
        value=(16*hex.index(st[0]))+hex.index(st[1])
        xor=ord(roaststring[count])
        count=(count+1)%len(roaststring)
        r=r+chr(value^xor)
    return r

def roast(pw):
    # contributed by jemfinch on #python
    key="Tic/Toc"
    ro="0x"
    i=0
    ascii=map(ord,pw)
    for c in ascii:
        ro=ro+'%02x'%(c^ord(key[i%len(key)]))
        i=i+1
    return string.lower(ro)

def checksum(b):
    return 0xdeadbeef # do it like gaim does, since the checksum
                      # formula doesn't work
##    # used in file transfers
##    check0 = check1 = 0x00ff
##    for i in range(len(b)):
##        if i%2:
##            if ord(b[i])>check1:
##                check1=check1+0x100 # wrap
##                if check0==0:
##                    check0=0x00ff
##                    if check1==0x100:
##                        check1=check1-1
##                else:
##                    check0=check0-1
##            check1=check1-ord(b[i])
##        else:
##            if ord(b[i])>check0: # wrap
##                check0=check0+0x100
##                if check1==0:
##                    check1=0x00ff
##                    if check0==0x100:
##                        check0=check0-1
##                else:
##                    check1=check1-1
##            check0=check0-ord(b[i])
##    check0=check0 & 0xff
##    check1=check1 & 0xff
##    checksum=(long(check0)*0x1000000)+(long(check1)*0x10000)
##    return checksum

def checksum_file(f):
    return 0xdeadbeef # do it like gaim does, since the checksum
                      # formula doesn't work
##    check0=check1=0x00ff
##    i=0
##    while 1:
##        b=f.read()
##        if not b: break
##        for char in b:
##            i=not i
##            if i:
##                if ord(char)>check1:
##                    check1=check1+0x100 # wrap
##                    if check0==0:
##                        check0=0x00ff
##                        if check1==0x100:
##                            check1=check1-1
##                    else:
##                        check0=check0-1
##                check1=check1-ord(char)
##            else:
##                if ord(char)>check0: # wrap
##                    check0=check0+0x100
##                    if check1==0:
##                        check1=0x00ff
##                        if check0==0x100:
##                            check0=check0-1
##                    else:
##                        check1=check1-1
##                check0=check0-ord(char)
##    check0=check0 & 0xff
##    check1=check1 & 0xff
##    checksum=(long(check0)*0x1000000)+(long(check1)*0x10000)
##    return checksum

def normalize(s):
    s=string.lower(s)
    s=string.replace(s," ","")
    return s


class TOCParseError(ValueError):
    pass


class TOC(protocol.Protocol):
    users={}

    def connectionMade(self):
        # initialization of protocol
        self._buf=""
        self._ourseqnum=0L
        self._theirseqnum=0L
        self._mode="Flapon"
        self._onlyflaps=0
        self._laststatus={} # the last status for a user
        self.username=None
        self.permitmode=PERMITALL
        self.permitlist=[]
        self.denylist=[]
        self.buddylist=[]
        self.signontime=0
        self.idletime=0
        self.userinfo="<br>"
        self.userclass=" O"
        self.away=""
        self.saved=None

    def _debug(self,data):
        log.msg(data)

    def connectionLost(self, reason):
        self._debug("dropped connection from %s" % self.username)
        try:
            del self.factory.users[self.username]
        except:
            pass
        for k in self.factory.chatroom.keys():
            try:
                self.factory.chatroom[k].leave(self)
            except TOCParseError:
                pass
        if self.saved:
            self.factory.savedusers[self.username]=self.saved
        self.updateUsers()

    def sendFlap(self,type,data):
        """
        send a FLAP to the client
        """
        send="*"
        self._debug(data)
        if type==DATA:
            data=data+"\000"
        length=len(data)
        send=send+struct.pack("!BHH",type,self._ourseqnum,length)
        send=send+data
        self._ourseqnum=self._ourseqnum+1
        if self._ourseqnum>(256L**4):
            self._ourseqnum=0
        self.transport.write(send)

    def dataReceived(self,data):
        self._buf=self._buf+data
        try:
            func=getattr(self,"mode%s"%self._mode)
        except:
            return
        self._mode=func()
        if self._onlyflaps and self.isFlap(): self.dataReceived("")

    def isFlap(self):
        """
        tests to see if a flap is actually on the buffer
        """
        if self._buf=='': return 0
        if self._buf[0]!="*": return 0
        if len(self._buf)<6: return 0
        foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
        if type not in range(1,6): return 0
        if len(self._buf)<6+length: return 0
        return 1

    def readFlap(self):
        """
        read the first FLAP off self._buf, raising errors if it isn't in the right form.
        the FLAP is the basic TOC message format, and is logically equivilant to a packet in TCP
        """
        if self._buf=='': return None
        if self._buf[0]!="*":
            raise TOCParseError
        if len(self._buf)<6: return None
        foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
        if len(self._buf)<6+length: return None
        data=self._buf[6:6+length]
        self._buf=self._buf[6+length:]
        if data and data[-1]=="\000":
            data=data[:-1]
        self._debug([type,data])
        return [type,data]

    #def modeWeb(self):
    #    try:
    #        line,rest=string.split(self._buf,"\n",1)
    #        get,username,http=string.split(line," ",2)
    #    except:
    #        return "Web" # not enough data
    #    foo,type,username=string.split(username,"/")
    #    if type=="info":
    #        user=self.factory.users[username]
    #        text="<HTML><HEAD><TITLE>User Information for %s</TITLE></HEAD><BODY>Username: <B>%s</B><br>\nWarning Level: <B>%s%</B><br>\n Online Since: <B>%s</B><br>\nIdle Minutes: <B>%s</B><br>\n<hr><br>\n%s\n<hr><br>\n"%(user.saved.nick, user.saved.nick, user.saved.evilness, time.asctime(user.signontime), int((time.time()-user.idletime)/60), user.userinfo)
    #        self.transport.write("HTTP/1.1 200 OK\n")
    #        self.transport.write("Content-Type: text/html\n")
    #        self.transport.write("Content-Length: %s\n\n"%len(text))
    #        self.transport.write(text)
    #        self.loseConnection()

    def modeFlapon(self):
        #if self._buf[:3]=="GET": self.modeWeb() # TODO: get this working
        if len(self._buf)<10: return "Flapon" # not enough bytes
        flapon,self._buf=self._buf[:10],self._buf[10:]
        if flapon!="FLAPON\r\n\r\n":
            raise TOCParseError
        self.sendFlap(SIGNON,"\000\000\000\001")
        self._onlyflaps=1
        return "Signon"

    def modeSignon(self):
        flap=self.readFlap()
        if flap==None:
            return "Signon"
        if flap[0]!=SIGNON: raise TOCParseError
        version,tlv,unlength=struct.unpack("!LHH",flap[1][:8])
        if version!=1 or tlv!=1 or unlength+8!=len(flap[1]):
            raise TOCParseError
        self.username=normalize(flap[1][8:])
        if self.username in self.factory.savedusers.keys():
            self.saved=self.factory.savedusers[self.username]
        else:
            self.saved=SavedUser()
            self.saved.nick=self.username
        return "TocSignon"

    def modeTocSignon(self):
        flap=self.readFlap()
        if flap==None:
            return "TocSignon"
        if flap[0]!=DATA: raise TOCParseError
        data=string.split(flap[1]," ")
        if data[0]!="toc_signon": raise TOCParseError
        for i in data:
            if not i:data.remove(i)
        password=unroast(data[4])
        if not(self.authorize(data[1],int(data[2]),data[3],password)):
            self.sendError(BAD_NICKNAME)
            self.transport.loseConnection()
            return
        self.sendFlap(DATA,"SIGN_ON:TOC1.0")
        self.sendFlap(DATA,"NICK:%s"%self.saved.nick)
        self.sendFlap(DATA,"CONFIG:%s"%self.saved.config)
        # sending user configuration goes here
        return "Connected"

    def authorize(self,server,port,username,password):
        if self.saved.password=="":
            self.saved.password=password
            return 1
        else:
            return self.saved.password==password

    def modeConnected(self):
        flap=self.readFlap()
        while flap!=None:
            if flap[0] not in [DATA,KEEP_ALIVE]: raise TOCParseError
            flapdata=string.split(flap[1]," ",1)
            tocname=flapdata[0][4:]
            if len(flapdata)==2:
                data=flapdata[1]
            else:
                data=""
            func=getattr(self,"toc_"+tocname,None)
            if func!=None:
                func(data)
            else:
                self.toc_unknown(tocname,data)
            flap=self.readFlap()
        return "Connected"

    def toc_unknown(self,tocname,data):
        self._debug("unknown! %s %s" % (tocname,data))

    def toc_init_done(self,data):
        """
        called when all the setup is done.

        toc_init_done
        """
        self.signontime=int(time.time())
        self.factory.users[self.username]=self
        self.updateUsers()

    def toc_add_permit(self,data):
        """
        adds users to the permit list.  if the list is null, then set the mode to DENYALL
        """
        if data=="":
            self.permitmode=DENYALL
            self.permitlist=[]
            self.denylist=[]
        else:
            self.permitmode=PERMITSOME
            self.denylist=[]
            users=string.split(data," ")
            map(self.permitlist.append,users)
        self.updateUsers()

    def toc_add_deny(self,data):
        """
        adds users to the deny list.  if the list is null, then set the mode to PERMITALL
        """
        if data=="":
            self.permitmode=PERMITALL
            self.permitlist=[]
            self.denylist=[]
        else:
            self.permitmode=DENYSOME
            self.permitlist=[]
            users=string.split(data," ")
            map(self.denylist.append,users)
        self.updateUsers()

    def toc_evil(self,data):
        """
        warns a user.

        toc_evil <username> <anon|norm>
        """
        username,nora=string.split(data," ")
        if nora=="anon":
            user=""
        else:
            user=self.saved.nick
        if not(self.factory.users.has_key(username)):
            self.sendError(CANT_WARN,username)
            return
        if self.factory.users[username].saved.evilness>=100:
            self.sendError(CANT_WARN,username)
            return
        self.factory.users[username].evilFrom(user)

    def toc_add_buddy(self,data):
        """
        adds users to the buddy list

        toc_add_buddy <buddyname1> [<buddyname2>] [<buddyname3>]...
        """
        buddies=map(normalize,string.split(data," "))
        for b in buddies:
            if b not in self.buddylist:
                self.buddylist.append(b)
        for buddy in buddies:
            try:
                buddy=self.factory.users[buddy]
            except:
                pass
            else:
                self.buddyUpdate(buddy)

    def toc_remove_buddy(self,data):
        """
        removes users from the buddy list

        toc_remove_buddy <buddyname1> [<buddyname2>] [<buddyname3>]...
        """
        buddies=string.split(data," ")
        for buddy in buddies:
            try:
                self.buddylist.remove(normalize(buddy))
            except: pass

    def toc_send_im(self,data):
        """
        incoming instant message

        toc_send_im <screenname> <quoted message> [auto]
        """
        username,data=string.split(data," ",1)
        auto=0
        if data[-4:]=="auto":
            auto=1
            data=data[:-5]
        data=unquote(data)
        if not(self.factory.users.has_key(username)):
            self.sendError(NOT_AVAILABLE,username)
            return
        user=self.factory.users[username]
        if not(self.canContact(user)):
            self.sendError(NOT_AVAILABLE,username)
            return
        user.hearWhisper(self,data,auto)

    def toc_set_info(self,data):
        """
        set the users information, retrivable with toc_get_info

        toc_set_info <user info (quoted)>
        """
        info=unquote(data)
        self._userinfo=info

    def toc_set_idle(self,data):
        """
        set/unset idle

        toc_set_idle <seconds>
        """
        seconds=int(data)
        self.idletime=time.time()-seconds # time when they started being idle
        self.updateUsers()

    def toc_set_away(self,data):
        """
        set/unset away message

        toc_set_away [<away message>]
        """
        away=unquote(data)
        if not self.away and away: # setting an away message
            self.away=away
            self.userclass=self.userclass+'U'
            self.updateUsers()
        elif self.away and not away: # coming back
            self.away=""
            self.userclass=self.userclass[:2]
            self.updateUsers()
        else:
            raise TOCParseError

    def toc_chat_join(self,data):
        """
        joins the chat room.

        toc_chat_join <exchange> <room name>
        """
        exchange,name=string.split(data," ",1)
        self.factory.getChatroom(int(exchange),unquote(name)).join(self)

    def toc_chat_invite(self,data):
        """
        invite others to the room.

        toc_chat_invite <room id> <invite message> <buddy 1> [<buddy2>]...
        """
        id,data=string.split(data," ",1)
        id=int(id)
        message,data=unquotebeg(data)
        buddies=string.split(data," ")
        for b in buddies:
            room=self.factory.chatroom[id]
            bud=self.factory.users[b]
            bud.chatInvite(room,self,message)

    def toc_chat_accept(self,data):
        """
        accept an invitation.

        toc_chat_accept <room id>
        """
        id=int(data)
        self.factory.chatroom[id].join(self)

    def toc_chat_send(self,data):
        """
        send a message to the chat room.

        toc_chat_send <room id> <message>
        """
        id,message=string.split(data," ",1)
        id=int(id)
        message=unquote(message)
        self.factory.chatroom[id].say(self,message)

    def toc_chat_whisper(self,data):
        id,user,message=string.split(data," ",2)
        id=int(id)
        room=self.factory.chatroom[id]
        message=unquote(message)
        self.factory.users[user].chatWhisper(room,self,message)

    def toc_chat_leave(self,data):
        """
        leave the room.

        toc_chat_leave <room id>
        """
        id=int(data)
        self.factory.chatroom[id].leave(self)

    def toc_set_config(self,data):
        """
        set the saved config.  this gets send when you log in.

        toc_set_config <config>
        """
        self.saved.config=unquote(data)

    def toc_get_info(self,data):
        """
        get the user info for a user

        toc_get_info <username>
        """
        if not self.factory.users.has_key(data):
            self.sendError(901,data)
            return
        self.sendFlap(2,"GOTO_URL:TIC:info/%s"%data)

    def toc_format_nickname(self,data):
        """
        change the format of your nickname.

        toc_format_nickname <new format>
        """
        # XXX may not work
        nick=unquote(data)
        if normalize(nick)==self.username:
            self.saved.nick=nick
            self.sendFlap(2,"ADMIN_NICK_STATUS:0")
        else:
            self.sendError(BAD_INPUT)

    def toc_change_passwd(self,data):
        orig,data=unquotebeg(data)
        new=unquote(data)
        if orig==self.saved.password:
            self.saved.password=new
            self.sendFlap(2,"ADMIN_PASSWD_STATUS:0")
        else:
            self.sendError(BAD_INPUT)

    def sendError(self,code,*varargs):
        """
        send an error to the user.  listing of error messages is below.
        """
        send="ERROR:%s"%code
        for v in varargs:
            send=send+":"+v
        self.sendFlap(DATA,send)

    def updateUsers(self):
        """
        Update the users who have us on their buddylist.
        Called when the user changes anything (idle,away) so people can get updates.
        """
        for user in self.factory.users.values():
            if self.username in user.buddylist and self.canContact(user):
                user.buddyUpdate(self)

    def getStatus(self,user):
        if self.canContact(user):
            if self in self.factory.users.values():ol='T'
            else: ol='F'
            idle=0
            if self.idletime:
                idle=int((time.time()-self.idletime)/60)
            return (self.saved.nick,ol,self.saved.evilness,self.signontime,idle,self.userclass)
        else:
            return (self.saved.nick,'F',0,0,0,self.userclass)

    def canContact(self,user):
        if self.permitmode==PERMITALL: return 1
        elif self.permitmode==DENYALL: return 0
        elif self.permitmode==PERMITSOME:
            if user.username in self.permitlist: return 1
            else: return 0
        elif self.permitmode==DENYSOME:
            if user.username in self.denylist: return 0
            else: return 1
        else:
            assert 0,"bad permitmode %s" % self.permitmode

    def buddyUpdate(self,user):
        """
        Update the buddy.  Called from updateUsers()
        """
        if not self.canContact(user): return
        status=user.getStatus(self)
        if not self._laststatus.has_key(user):
            self._laststatus[user]=()
        if self._laststatus[user]!=status:
            send="UPDATE_BUDDY:%s:%s:%s:%s:%s:%s"%status
            self.sendFlap(DATA,send)
            self._laststatus[user]=status

    def hearWhisper(self,user,data,auto=0):
        """
        Called when you get an IM.  If auto=1, it's an autoreply from an away message.
        """
        if not self.canContact(user): return
        if auto: auto='T'
        else: auto='F'
        send="IM_IN:%s:%s:%s"%(user.saved.nick,auto,data)
        self.sendFlap(DATA,send)

    def evilFrom(self,user):
        if user=="":
            percent=0.03
        else:
            percent=0.1
        self.saved.evilness=self.saved.evilness+int((100-self.saved.evilness)*percent)
        self.sendFlap(2,"EVILED:%s:%s"%(self.saved.evilness,user))
        self.updateUsers()

    def chatJoin(self,room):
        self.sendFlap(2,"CHAT_JOIN:%s:%s"%(room.id,room.name))
        f="CHAT_UPDATE_BUDDY:%s:T"%room.id
        for u in room.users:
            if u!=self:
                u.chatUserUpdate(room,self)
            f=f+":"+u.saved.nick
        self.sendFlap(2,f)

    def chatInvite(self,room,user,message):
        if not self.canContact(user): return
        self.sendFlap(2,"CHAT_INVITE:%s:%s:%s:%s"%(room.name,room.id,user.saved.nick,message))

    def chatUserUpdate(self,room,user):
        if user in room.users:
            inroom='T'
        else:
            inroom='F'
        self.sendFlap(2,"CHAT_UPDATE_BUDDY:%s:%s:%s"%(room.id,inroom,user.saved.nick))

    def chatMessage(self,room,user,message):
        if not self.canContact(user): return
        self.sendFlap(2,"CHAT_IN:%s:%s:F:%s"%(room.id,user.saved.nick,message))

    def chatWhisper(self,room,user,message):
        if not self.canContact(user): return
        self.sendFlap(2,"CHAT_IN:%s:%s:T:%s"%(room.id,user.saved.nick,message))

    def chatLeave(self,room):
        self.sendFlap(2,"CHAT_LEFT:%s"%(room.id))


class Chatroom:
    def __init__(self,fac,exchange,name,id):
        self.exchange=exchange
        self.name=name
        self.id=id
        self.factory=fac
        self.users=[]

    def join(self,user):
        if user in self.users:
            return
        self.users.append(user)
        user.chatJoin(self)

    def leave(self,user):
        if user not in self.users:
            raise TOCParseError
        self.users.remove(user)
        user.chatLeave(self)
        for u in self.users:
            u.chatUserUpdate(self,user)
        if len(self.users)==0:
            self.factory.remChatroom(self)

    def say(self,user,message):
        for u in self.users:
            u.chatMessage(self,user,message)


class SavedUser:
    def __init__(self):
        self.config=""
        self.nick=""
        self.password=""
        self.evilness=0


class TOCFactory(protocol.Factory):
    def __init__(self):
        self.users={}
        self.savedusers={}
        self.chatroom={}
        self.chatroomid=0

    def buildProtocol(self,addr):
        p=TOC()
        p.factory=self
        return p

    def getChatroom(self,exchange,name):
        for i in self.chatroom.values():
            if normalize(i.name)==normalize(name):
                return i
        self.chatroom[self.chatroomid]=Chatroom(self,exchange,name,self.chatroomid)
        self.chatroomid=self.chatroomid+1
        return self.chatroom[self.chatroomid-1]

    def remChatroom(self,room):
        id=room.id
        del self.chatroom[id]

MAXARGS={}
MAXARGS["CONFIG"]=0
MAXARGS["NICK"]=0
MAXARGS["IM_IN"]=2
MAXARGS["UPDATE_BUDDY"]=5
MAXARGS["ERROR"]=-1
MAXARGS["EVILED"]=1
MAXARGS["CHAT_JOIN"]=1
MAXARGS["CHAT_IN"]=3
MAXARGS["CHAT_UPDATE_BUDDY"]=-1
MAXARGS["CHAT_INVITE"]=3
MAXARGS["CHAT_LEFT"]=0
MAXARGS["ADMIN_NICK_STATUS"]=0
MAXARGS["ADMIN_PASSWD_STATUS"]=0


class TOCClient(protocol.Protocol):
    def __init__(self,username,password,authhost="login.oscar.aol.com",authport=5190):

        self.username=normalize(username) # our username
        self._password=password # our password
        self._mode="SendNick" # current mode
        self._ourseqnum=19071 # current sequence number (for sendFlap)
        self._authhost=authhost # authorization host
        self._authport=authport # authorization port
        self._online=0 # are we online?
        self._buddies=[] # the current buddy list
        self._privacymode=PERMITALL # current privacy mode
        self._permitlist=[] # list of users on the permit list
        self._roomnames={} # the names for each of the rooms we're in
        self._receivedchatmembers={} # have we gotten who's in our room yet?
        self._denylist=[]
        self._cookies={} # for file transfers
        self._buf='' # current data buffer
        self._awaymessage=''

    def _debug(self,data):
        log.msg(data)

    def sendFlap(self,type,data):
        if type==DATA:
            data=data+"\000"
        length=len(data)
        s="*"
        s=s+struct.pack("!BHH",type,self._ourseqnum,length)
        s=s+data
        self._ourseqnum=self._ourseqnum+1
        if self._ourseqnum>(256*256+256):
            self._ourseqnum=0
        self._debug(data)
        self.transport.write(s)

    def isFlap(self):
        """
        tests to see if a flap is actually on the buffer
        """
        if self._buf=='': return 0
        if self._buf[0]!="*": return 0
        if len(self._buf)<6: return 0
        foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
        if type not in range(1,6): return 0
        if len(self._buf)<6+length: return 0
        return 1

    def readFlap(self):
        if self._buf=='': return None
        if self._buf[0]!="*":
            raise TOCParseError
        if len(self._buf)<6: return None
        foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
        if len(self._buf)<6+length: return None
        data=self._buf[6:6+length]
        self._buf=self._buf[6+length:]
        if data and data[-1]=="\000":
            data=data[:-1]
        return [type,data]

    def connectionMade(self):
        self._debug("connection made! %s" % self.transport)
        self.transport.write("FLAPON\r\n\r\n")

    def connectionLost(self, reason):
        self._debug("connection lost!")
        self._online=0

    def dataReceived(self,data):
        self._buf=self._buf+data
        while self.isFlap():
            flap=self.readFlap()
            func=getattr(self,"mode%s"%self._mode)
            func(flap)

    def modeSendNick(self,flap):
        if flap!=[1,"\000\000\000\001"]: raise TOCParseError
        s="\000\000\000\001\000\001"+struct.pack("!H",len(self.username))+self.username
        self.sendFlap(1,s)
        s="toc_signon %s %s  %s %s english \"penguin\""%(self._authhost,\
            self._authport,self.username,roast(self._password))
        self.sendFlap(2,s)
        self._mode="Data"

    def modeData(self,flap):
        if not flap[1]:
            return
        if not ':' in flap[1]:
            self._debug("bad SNAC:%s"%(flap[1]))
            return
        command,rest=string.split(flap[1],":",1)
        if MAXARGS.has_key(command):
            maxsplit=MAXARGS[command]
        else:
            maxsplit=-1
        if maxsplit==-1:
            l=tuple(string.split(rest,":"))
        elif maxsplit==0:
            l=(rest,)
        else:
            l=tuple(string.split(rest,":",maxsplit))
        self._debug("%s %s"%(command,l))
        try:
            func=getattr(self,"toc%s"%command)
            self._debug("calling %s"%func)
        except:
            self._debug("calling %s"%self.tocUNKNOWN)
            self.tocUNKNOWN(command,l)
            return
        func(l)

    def tocUNKNOWN(self,command,data):
        pass

    def tocSIGN_ON(self,data):
        if data!=("TOC1.0",): raise TOCParseError
        self._debug("Whee, signed on!")
        if self._buddies: self.add_buddy(self._buddies)
        self._online=1
        self.onLine()

    def tocNICK(self,data):
        """
        NICK:<format of nickname>
        """
        self.username=data[0]

    def tocCONFIG(self,data):
        """
        CONFIG:<config>
        format of config data:
            - g: group.  all users until next g or end of config are in this group
            - b: buddy
            - p: person on the permit list
            - d: person on the deny list
            - m: permit/deny mode (1: permit all, 2: deny all, 3: permit some, 4: deny some)
        """
        data=data[0]
        if data and data[0]=="{":data=data[1:-1]
        lines=string.split(data,"\n")
        buddylist={}
        currentgroup=""
        permit=[]
        deny=[]
        mode=1
        for l in lines:
            if l:
                code,data=l[0],l[2:]
                if code=='g': # group
                    currentgroup=data
                    buddylist[currentgroup]=[]
                elif code=='b':
                    buddylist[currentgroup].append(data)
                elif code=='p':
                    permit.append(data)
                elif code=='d':
                    deny.append(data)
                elif code=='m':
                    mode=int(data)
        self.gotConfig(mode,buddylist,permit,deny)

    def tocIM_IN(self,data):
        """
        IM_IN:<user>:<autoreply T|F>:message
        """
        user=data[0]
        autoreply=(data[1]=='T')
        message=data[2]
        self.hearMessage(user,message,autoreply)

    def tocUPDATE_BUDDY(self,data):
        """
        UPDATE_BUDDY:<username>:<online T|F>:<warning level>:<signon time>:<idle time (minutes)>:<user class>
        """
        data=list(data)
        online=(data[1]=='T')
        if len(data[5])==2:
            data[5]=data[5]+" "
        away=(data[5][-1]=='U')
        if data[5][-1]=='U':
            data[5]=data[5][:-1]
        self.updateBuddy(data[0],online,int(data[2]),int(data[3]),int(data[4]),data[5],away)

    def tocERROR(self,data):
        """
        ERROR:<error code>:<misc. data>
        """
        code,args=data[0],data[1:]
        self.hearError(int(code),args)

    def tocEVILED(self,data):
        """
        EVILED:<current warning level>:<user who warned us>
        """
        self.hearWarning(data[0],data[1])

    def tocCHAT_JOIN(self,data):
        """
        CHAT_JOIN:<room id>:<room name>
        """
        #self.chatJoined(int(data[0]),data[1])
        self._roomnames[int(data[0])]=data[1]
        self._receivedchatmembers[int(data[0])]=0

    def tocCHAT_UPDATE_BUDDY(self,data):
        """
        CHAT_UPDATE_BUDDY:<room id>:<in room? T/F>:<user 1>:<user 2>...
        """
        roomid=int(data[0])
        inroom=(data[1]=='T')
        if self._receivedchatmembers[roomid]:
            for u in data[2:]:
                self.chatUpdate(roomid,u,inroom)
        else:
            self._receivedchatmembers[roomid]=1
            self.chatJoined(roomid,self._roomnames[roomid],list(data[2:]))

    def tocCHAT_IN(self,data):
        """
        CHAT_IN:<room id>:<username>:<whisper T/F>:<message>
        whisper isn't used
        """
        whisper=(data[2]=='T')
        if whisper:
            self.chatHearWhisper(int(data[0]),data[1],data[3])
        else:
            self.chatHearMessage(int(data[0]),data[1],data[3])

    def tocCHAT_INVITE(self,data):
        """
        CHAT_INVITE:<room name>:<room id>:<username>:<message>
        """
        self.chatInvited(int(data[1]),data[0],data[2],data[3])

    def tocCHAT_LEFT(self,data):
        """
        CHAT_LEFT:<room id>
        """
        self.chatLeft(int(data[0]))
        del self._receivedchatmembers[int(data[0])]
        del self._roomnames[int(data[0])]

    def tocRVOUS_PROPOSE(self,data):
        """
        RVOUS_PROPOSE:<user>:<uuid>:<cookie>:<seq>:<rip>:<pip>:<vip>:<port>
              [:tlv tag1:tlv value1[:tlv tag2:tlv value2[:...]]]
        """
        user,uid,cookie,seq,rip,pip,vip,port=data[:8]
        cookie=base64.decodestring(cookie)
        port=int(port)
        tlvs={}
        for i in range(8,len(data),2):
            key=data[i]
            value=base64.decodestring(data[i+1])
            tlvs[key]=value
        name=UUIDS[uid]
        try:
            func=getattr(self,"toc%s"%name)
        except:
            self._debug("no function for UID %s" % uid)
            return
        func(user,cookie,seq,pip,vip,port,tlvs)

    def tocSEND_FILE(self,user,cookie,seq,pip,vip,port,tlvs):
        if tlvs.has_key('12'):
            description=tlvs['12']
        else:
            description=""
        subtype,numfiles,size=struct.unpack("!HHI",tlvs['10001'][:8])
        name=tlvs['10001'][8:-4]
        while name[-1]=='\000':
            name=name[:-1]
        self._cookies[cookie]=[user,SEND_FILE_UID,pip,port,{'name':name}]
        self.rvousProposal("send",cookie,user,vip,port,description=description,
                           name=name,files=numfiles,size=size)

    def tocGET_FILE(self,user,cookie,seq,pip,vip,port,tlvs):
        return
        # XXX add this back in
        #reactor.clientTCP(pip,port,GetFileTransfer(self,cookie,os.path.expanduser("~")))
        #self.rvous_accept(user,cookie,GET_FILE_UID)

    def onLine(self):
        """
        called when we are first online
        """
        pass

    def gotConfig(self,mode,buddylist,permit,deny):
        """
        called when we get a configuration from the server
        mode := permit/deny mode
        buddylist := current buddylist
        permit := permit list
        deny := deny list
        """
        pass

    def hearError(self,code,args):
        """
        called when an error is received
        code := error code
        args := misc. arguments (username, etc.)
        """
        pass

    def hearWarning(self,newamount,username):
        """
        called when we get warned
        newamount := the current warning level
        username := the user who warned us, or '' if it's anonymous
        """
        pass

    def hearMessage(self,username,message,autoreply):
        """
        called when you receive an IM
        username := the user who the IM is from
        message := the message
        autoreply := true if the message is an autoreply from an away message
        """
        pass

    def updateBuddy(self,username,online,evilness,signontime,idletime,userclass,away):
        """
        called when a buddy changes state
        username := the user whos state changed
        online := true if the user is online
        evilness := the users current warning level
        signontime := the time the user signed on (UNIX epoch)
        idletime := the time the user has been idle (minutes)
        away := true if the user is away
        userclass := the class of the user (generally " O")
        """
        pass

    def chatJoined(self,roomid,roomname,users):
        """
        we just joined a chat room
        roomid := the AIM id for the room
        roomname := the name for the room
        users := a list of the users already in the room
        """
        pass

    def chatUpdate(self,roomid,username,inroom):
        """
        a user has joined the room
        roomid := the AIM id for the room
        username := the username
        inroom := true if the user is in the room
        """
        pass

    def chatHearMessage(self,roomid,username,message):
        """
        a message was sent to the room
        roomid := the AIM id for the room
        username := the user who sent the message
        message := the message
        """
        pass

    def chatHearWhisper(self,roomid,username,message):
        """
        someone whispered to us in a chatroom
        roomid := the AIM for the room
        username := the user who whispered to us
        message := the message
        """
        pass

    def chatInvited(self,roomid,roomname,username,message):
        """
        we were invited to a chat room
        roomid := the AIM id for the room
        roomname := the name of the room
        username := the user who invited us
        message := the invite message
        """
        pass

    def chatLeft(self,roomid):
        """
        we left the room
        roomid := the AIM id for the room
        """
        pass

    def rvousProposal(self,type,cookie,user,vip,port,**kw):
        """
        we were asked for a rondevouz
        type := the type of rondevous.  currently, one of ["send"]
        cookie := the cookie. pass this to rvous_accept()
        user := the user who asked us
        vip := their verified_ip
        port := the port they want us to conenct to
        kw := misc. args
        """
        pass #self.rvous_accept(cookie)

    def receiveBytes(self,user,file,chunk,sofar,total):
        """
        we received part of a file from a file transfer
        file := the name of the file
        chunk := the chunk of data
        sofar := how much data we've gotten so far
        total := the total amount of data
        """
        pass #print user,file,sofar,total

    def isaway(self):
        """
        return our away status
        """
        return len(self._awaymessage)>0

    def set_config(self,mode,buddylist,permit,deny):
        """
        set the server configuration
        mode := permit mode
        buddylist := buddy list
        permit := permit list
        deny := deny list
        """
        s="m %s\n"%mode
        for g in buddylist.keys():
            s=s+"g %s\n"%g
            for u in buddylist[g]:
                s=s+"b %s\n"%u
        for p in permit:
            s=s+"p %s\n"%p
        for d in deny:
            s=s+"d %s\n"%d
        #s="{\n"+s+"\n}"
        self.sendFlap(2,"toc_set_config %s"%quote(s))

    def add_buddy(self,buddies):
        s=""
        if type(buddies)==type(""): buddies=[buddies]
        for b in buddies:
            s=s+" "+normalize(b)
        self.sendFlap(2,"toc_add_buddy%s"%s)

    def del_buddy(self,buddies):
        s=""
        if type(buddies)==type(""): buddies=[buddies]
        for b in buddies:
            s=s+" "+b
        self.sendFlap(2,"toc_remove_buddy%s"%s)

    def add_permit(self,users):
        if type(users)==type(""): users=[users]
        s=""
        if self._privacymode!=PERMITSOME:
            self._privacymode=PERMITSOME
            self._permitlist=[]
        for u in users:
            u=normalize(u)
            if u not in self._permitlist:self._permitlist.append(u)
            s=s+" "+u
        if not s:
            self._privacymode=DENYALL
            self._permitlist=[]
            self._denylist=[]
        self.sendFlap(2,"toc_add_permit"+s)

    def del_permit(self,users):
        if type(users)==type(""): users=[users]
        p=self._permitlist[:]
        for u in users:
            u=normalize(u)
            if u in p:
                p.remove(u)
        self.add_permit([])
        self.add_permit(p)

    def add_deny(self,users):
        if type(users)==type(""): users=[users]
        s=""
        if self._privacymode!=DENYSOME:
            self._privacymode=DENYSOME
            self._denylist=[]
        for u in users:
            u=normalize(u)
            if u not in self._denylist:self._denylist.append(u)
            s=s+" "+u
        if not s:
            self._privacymode=PERMITALL
            self._permitlist=[]
            self._denylist=[]
        self.sendFlap(2,"toc_add_deny"+s)

    def del_deny(self,users):
        if type(users)==type(""): users=[users]
        d=self._denylist[:]
        for u in users:
            u=normalize(u)
            if u in d:
                d.remove(u)
        self.add_deny([])
        if d:
            self.add_deny(d)

    def signon(self):
        """
        called to finish the setup, and signon to the network
        """
        self.sendFlap(2,"toc_init_done")
        self.sendFlap(2,"toc_set_caps %s" % (SEND_FILE_UID,)) # GET_FILE_UID)

    def say(self,user,message,autoreply=0):
        """
        send a message
        user := the user to send to
        message := the message
        autoreply := true if the message is an autoreply (good for away messages)
        """
        if autoreply: a=" auto"
        else: a=''
        self.sendFlap(2,"toc_send_im %s %s%s"%(normalize(user),quote(message),a))

    def idle(self,idletime=0):
        """
        change idle state
        idletime := the seconds that the user has been away, or 0 if they're back
        """
        self.sendFlap(2,"toc_set_idle %s" % int(idletime))

    def evil(self,user,anon=0):
        """
        warn a user
        user := the user to warn
        anon := if true, an anonymous warning
        """
        self.sendFlap(2,"toc_evil %s %s"%(normalize(user), (not anon and "anon") or "norm"))

    def away(self,message=''):
        """
        change away state
        message := the message, or '' to come back from awayness
        """
        self._awaymessage=message
        if message:
            message=' '+quote(message)
        self.sendFlap(2,"toc_set_away%s"%message)

    def chat_join(self,exchange,roomname):
        """
        join a chat room
        exchange := should almost always be 4
        roomname := room name
        """
        roomname=string.replace(roomname," ","")
        self.sendFlap(2,"toc_chat_join %s %s"%(int(exchange),roomname))

    def chat_say(self,roomid,message):
        """
        send a message to a chatroom
        roomid := the AIM id for the room
        message := the message to send
        """
        self.sendFlap(2,"toc_chat_send %s %s"%(int(roomid),quote(message)))

    def chat_whisper(self,roomid,user,message):
        """
        whisper to another user in a chatroom
        roomid := the AIM id for the room
        user := the user to whisper to
        message := the message to send
        """
        self.sendFlap(2,"toc_chat_whisper %s %s %s"%(int(roomid),normalize(user),quote(message)))

    def chat_leave(self,roomid):
        """
        leave a chat room.
        roomid := the AIM id for the room
        """
        self.sendFlap(2,"toc_chat_leave %s" % int(roomid))

    def chat_invite(self,roomid,usernames,message):
        """
        invite a user[s] to the chat room
        roomid := the AIM id for the room
        usernames := either a string (one username) or a list (more than one)
        message := the message to invite them with
        """
        if type(usernames)==type(""): # a string, one username
            users=usernames
        else:
            users=""
            for u in usernames:
                users=users+u+" "
            users=users[:-1]
        self.sendFlap(2,"toc_chat_invite %s %s %s" % (int(roomid),quote(message),users))

    def chat_accept(self,roomid):
        """
        accept an invite to a chat room
        roomid := the AIM id for the room
        """
        self.sendFlap(2,"toc_chat_accept %s"%int(roomid))

    def rvous_accept(self,cookie):
        user,uuid,pip,port,d=self._cookies[cookie]
        self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user),
                                                     cookie,uuid))
        if uuid==SEND_FILE_UID:
            protocol.ClientConnector(reactor, SendFileTransfer,self,cookie,user,d["name"]).connectTCP(pip,port)

    def rvous_cancel(self,cookie):
        user,uuid,pip,port,d=self._cookies[cookie]
        self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user),
                                                       cookie,uuid))
        del self._cookies[cookie]


class SendFileTransfer(protocol.Protocol):
    header_fmt="!4s2H8s6H10I32s3c69s16s2H64s"

    def __init__(self,client,cookie,user,filename):
        self.client=client
        self.cookie=cookie
        self.user=user
        self.filename=filename
        self.hdr=[0,0,0]
        self.sofar=0

    def dataReceived(self,data):
        if not self.hdr[2]==0x202:
            self.hdr=list(struct.unpack(self.header_fmt,data[:256]))
            self.hdr[2]=0x202
            self.hdr[3]=self.cookie
            self.hdr[4]=0
            self.hdr[5]=0
            self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr))
            data=data[256:]
            if self.hdr[6]==1:
                self.name=self.filename
            else:
                self.name=self.filename+self.hdr[-1]
                while self.name[-1]=="\000":
                    self.name=self.name[:-1]
        if not data: return
        self.sofar=self.sofar+len(data)
        self.client.receiveBytes(self.user,self.name,data,self.sofar,self.hdr[11])
        if self.sofar==self.hdr[11]: # end of this file
            self.hdr[2]=0x204
            self.hdr[7]=self.hdr[7]-1
            self.hdr[9]=self.hdr[9]-1
            self.hdr[19]=0xdeadbeef # XXX really calculate this
            self.hdr[18]=self.hdr[18]+1
            self.hdr[21]="\000"
            self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr))
            self.sofar=0
            if self.hdr[7]==0:
                self.transport.loseConnection()


class GetFileTransfer(protocol.Protocol):
    header_fmt="!4s 2H 8s 6H 10I 32s 3c 69s 16s 2H 64s"
    def __init__(self,client,cookie,dir):
        self.client=client
        self.cookie=cookie
        self.dir=dir
        self.buf=""

    def connectionMade(self):
        def func(f,path,names):
            names.sort(lambda x,y:cmp(string.lower(x),string.lower(y)))
            for n in names:
                name=os.path.join(path,n)
                lt=time.localtime(os.path.getmtime(name))
                size=os.path.getsize(name)
                f[1]=f[1]+size
                f.append("%02d/%02d/%4d %02d:%02d %8d %s" %
                             (lt[1],lt[2],lt[0],lt[3],lt[4],size,name[f[0]:]))
        f=[len(self.dir)+1,0]
        os.path.walk(self.dir,func,f)
        size=f[1]
        self.listing=string.join(f[2:],"\r\n")+"\r\n"
        open("\\listing.txt","w").write(self.listing)
        hdr=["OFT2",256,0x1108,self.cookie,0,0,len(f)-2,len(f)-2,1,1,size,
             len(self.listing),os.path.getmtime(self.dir),
             checksum(self.listing),0,0,0,0,0,0,"OFT_Windows ICBMFT V1.1 32",
             "\002",chr(0x1a),chr(0x10),"","",0,0,""]
        self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))

    def dataReceived(self,data):
        self.buf=self.buf+data
        while len(self.buf)>=256:
            hdr=list(struct.unpack(self.header_fmt,self.buf[:256]))
            self.buf=self.buf[256:]
            if hdr[2]==0x1209:
                self.file=StringIO.StringIO(self.listing)
                self.transport.registerProducer(self,0)
            elif hdr[2]==0x120b: pass
            elif hdr[2]==0x120c: # file request
                file=hdr[-1]
                for k,v in [["\000",""],["\001",os.sep]]:
                    file=string.replace(file,k,v)
                self.name=os.path.join(self.dir,file)
                self.file=open(self.name,'rb')
                hdr[2]=0x0101
                hdr[6]=hdr[7]=1
                hdr[10]=hdr[11]=os.path.getsize(self.name)
                hdr[12]=os.path.getmtime(self.name)
                hdr[13]=checksum_file(self.file)
                self.file.seek(0)
                hdr[18]=hdr[19]=0
                hdr[21]=chr(0x20)
                self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
                log.msg("got file request for %s"%file,hex(hdr[13]))
            elif hdr[2]==0x0202:
                log.msg("sending file")
                self.transport.registerProducer(self,0)
            elif hdr[2]==0x0204:
                log.msg("real checksum: %s"%hex(hdr[19]))
                del self.file
            elif hdr[2]==0x0205: # resume
                already=hdr[18]
                if already:
                    data=self.file.read(already)
                else:
                    data=""
                log.msg("restarting at %s"%already)
                hdr[2]=0x0106
                hdr[19]=checksum(data)
                self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
            elif hdr[2]==0x0207:
                self.transport.registerProducer(self,0)
            else:
                log.msg("don't understand 0x%04x"%hdr[2])
                log.msg(hdr)

    def resumeProducing(self):
        data=self.file.read(4096)
        log.msg(len(data))
        if not data:
            self.transport.unregisterProducer()
        self.transport.write(data)

    def pauseProducing(self): pass

    def stopProducing(self): del self.file

# UUIDs
SEND_FILE_UID = "09461343-4C7F-11D1-8222-444553540000"
GET_FILE_UID  = "09461348-4C7F-11D1-8222-444553540000"
UUIDS={
    SEND_FILE_UID:"SEND_FILE",
    GET_FILE_UID:"GET_FILE"
}

# ERRORS
# general
NOT_AVAILABLE=901
CANT_WARN=902
MESSAGES_TOO_FAST=903
# admin
BAD_INPUT=911
BAD_ACCOUNT=912
REQUEST_ERROR=913
SERVICE_UNAVAILABLE=914
# chat
NO_CHAT_IN=950
# im and info
SEND_TOO_FAST=960
MISSED_BIG_IM=961
MISSED_FAST_IM=962
# directory
DIR_FAILURE=970
TOO_MANY_MATCHES=971
NEED_MORE_QUALIFIERS=972
DIR_UNAVAILABLE=973
NO_EMAIL_LOOKUP=974
KEYWORD_IGNORED=975
NO_KEYWORDS=976
BAD_LANGUAGE=977
BAD_COUNTRY=978
DIR_FAIL_UNKNOWN=979
# authorization
BAD_NICKNAME=980
SERVICE_TEMP_UNAVAILABLE=981
WARNING_TOO_HIGH=982
CONNECTING_TOO_QUICK=983
UNKNOWN_SIGNON=989

STD_MESSAGE={}
STD_MESSAGE[NOT_AVAILABLE]="%s not currently available"
STD_MESSAGE[CANT_WARN]="Warning of %s not currently available"
STD_MESSAGE[MESSAGES_TOO_FAST]="A message has been dropped, you are exceeding the server speed limit"
STD_MESSAGE[BAD_INPUT]="Error validating input"
STD_MESSAGE[BAD_ACCOUNT]="Invalid account"
STD_MESSAGE[REQUEST_ERROR]="Error encountered while processing request"
STD_MESSAGE[SERVICE_UNAVAILABLE]="Service unavailable"
STD_MESSAGE[NO_CHAT_IN]="Chat in %s is unavailable"
STD_MESSAGE[SEND_TOO_FAST]="You are sending messages too fast to %s"
STD_MESSAGE[MISSED_BIG_IM]="You missed an IM from %s because it was too big"
STD_MESSAGE[MISSED_FAST_IM]="You missed an IM from %s because it was sent too fast"
# skipping directory for now
STD_MESSAGE[BAD_NICKNAME]="Incorrect nickname or password"
STD_MESSAGE[SERVICE_TEMP_UNAVAILABLE]="The service is temporarily unavailable"
STD_MESSAGE[WARNING_TOO_HIGH]="Your warning level is currently too high to sign on"
STD_MESSAGE[CONNECTING_TOO_QUICK]="You have been connecting and disconnecting too frequently.  Wait 10 minutes and try again.  If you continue to try, you will need to wait even longer."
STD_MESSAGE[UNKNOWN_SIGNON]="An unknown signon error has occured %s"
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.