1
# -*- test-case-name: twisted.words.test -*-
2
# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
3
# See LICENSE for details.
6
Implements a AOL Instant Messenger TOC server and client, using the Twisted
10
info,dir: see how gaim connects for this...it may never work if it tries to
11
connect to the aim server automatically
13
This module is deprecated.
15
Maintainer: Paul Swartz
20
"twisted.words.protocols.toc is deprecated since Twisted 9.0. "
21
"Use twisted.words.protocols.oscar instead.",
22
category=DeprecationWarning,
27
from twisted.internet import reactor, protocol
28
from twisted.python import log
38
SIGNON,DATA,ERROR,SIGNOFF,KEEP_ALIVE=range(1,6)
39
PERMITALL,DENYALL,PERMITSOME,DENYSOME=range(1,5)
41
DUMMY_CHECKSUM = -559038737 # 0xdeadbeef
44
rep=['\\','$','{','}','[',']','(',')','"']
46
s=string.replace(s,r,"\\"+r)
51
if s[0]!='"': return s
66
for i in range(1,len(s)):
67
if s[i]=='"' and s[i-1]!='\\':
73
pw=string.lower(pw[2:])
76
hex=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
79
value=(16*hex.index(st[0]))+hex.index(st[1])
80
xor=ord(roaststring[count])
81
count=(count+1)%len(roaststring)
86
# contributed by jemfinch on #python
92
ro=ro+'%02x'%(c^ord(key[i%len(key)]))
94
return string.lower(ro)
97
return DUMMY_CHECKSUM # do it like gaim does, since the checksum
98
# formula doesn't work
99
## # used in file transfers
100
## check0 = check1 = 0x00ff
101
## for i in range(len(b)):
103
## if ord(b[i])>check1:
104
## check1=check1+0x100 # wrap
111
## check1=check1-ord(b[i])
113
## if ord(b[i])>check0: # wrap
114
## check0=check0+0x100
121
## check0=check0-ord(b[i])
122
## check0=check0 & 0xff
123
## check1=check1 & 0xff
124
## checksum=(long(check0)*0x1000000)+(long(check1)*0x10000)
127
def checksum_file(f):
128
return DUMMY_CHECKSUM # do it like gaim does, since the checksum
129
# formula doesn't work
130
## check0=check1=0x00ff
138
## if ord(char)>check1:
139
## check1=check1+0x100 # wrap
146
## check1=check1-ord(char)
148
## if ord(char)>check0: # wrap
149
## check0=check0+0x100
156
## check0=check0-ord(char)
157
## check0=check0 & 0xff
158
## check1=check1 & 0xff
159
## checksum=(long(check0)*0x1000000)+(long(check1)*0x10000)
164
s=string.replace(s," ","")
168
class TOCParseError(ValueError):
172
class TOC(protocol.Protocol):
175
def connectionMade(self):
176
# initialization of protocol
182
self._laststatus={} # the last status for a user
184
self.permitmode=PERMITALL
195
def _debug(self,data):
198
def connectionLost(self, reason):
199
self._debug("dropped connection from %s" % self.username)
201
del self.factory.users[self.username]
204
for k in self.factory.chatroom.keys():
206
self.factory.chatroom[k].leave(self)
207
except TOCParseError:
210
self.factory.savedusers[self.username]=self.saved
213
def sendFlap(self,type,data):
215
send a FLAP to the client
222
send=send+struct.pack("!BHH",type,self._ourseqnum,length)
224
self._ourseqnum=self._ourseqnum+1
225
if self._ourseqnum>(256L**4):
227
self.transport.write(send)
229
def dataReceived(self,data):
230
self._buf=self._buf+data
232
func=getattr(self,"mode%s"%self._mode)
236
if self._onlyflaps and self.isFlap(): self.dataReceived("")
240
tests to see if a flap is actually on the buffer
242
if self._buf=='': return 0
243
if self._buf[0]!="*": return 0
244
if len(self._buf)<6: return 0
245
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
246
if type not in range(1,6): return 0
247
if len(self._buf)<6+length: return 0
252
read the first FLAP off self._buf, raising errors if it isn't in the right form.
253
the FLAP is the basic TOC message format, and is logically equivilant to a packet in TCP
255
if self._buf=='': return None
256
if self._buf[0]!="*":
258
if len(self._buf)<6: return None
259
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
260
if len(self._buf)<6+length: return None
261
data=self._buf[6:6+length]
262
self._buf=self._buf[6+length:]
263
if data and data[-1]=="\000":
265
self._debug([type,data])
270
# line,rest=string.split(self._buf,"\n",1)
271
# get,username,http=string.split(line," ",2)
273
# return "Web" # not enough data
274
# foo,type,username=string.split(username,"/")
276
# user=self.factory.users[username]
277
# 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)
278
# self.transport.write("HTTP/1.1 200 OK\n")
279
# self.transport.write("Content-Type: text/html\n")
280
# self.transport.write("Content-Length: %s\n\n"%len(text))
281
# self.transport.write(text)
282
# self.loseConnection()
284
def modeFlapon(self):
285
#if self._buf[:3]=="GET": self.modeWeb() # TODO: get this working
286
if len(self._buf)<10: return "Flapon" # not enough bytes
287
flapon,self._buf=self._buf[:10],self._buf[10:]
288
if flapon!="FLAPON\r\n\r\n":
290
self.sendFlap(SIGNON,"\000\000\000\001")
294
def modeSignon(self):
298
if flap[0]!=SIGNON: raise TOCParseError
299
version,tlv,unlength=struct.unpack("!LHH",flap[1][:8])
300
if version!=1 or tlv!=1 or unlength+8!=len(flap[1]):
302
self.username=normalize(flap[1][8:])
303
if self.username in self.factory.savedusers.keys():
304
self.saved=self.factory.savedusers[self.username]
306
self.saved=SavedUser()
307
self.saved.nick=self.username
310
def modeTocSignon(self):
314
if flap[0]!=DATA: raise TOCParseError
315
data=string.split(flap[1]," ")
316
if data[0]!="toc_signon": raise TOCParseError
318
if not i:data.remove(i)
319
password=unroast(data[4])
320
if not(self.authorize(data[1],int(data[2]),data[3],password)):
321
self.sendError(BAD_NICKNAME)
322
self.transport.loseConnection()
324
self.sendFlap(DATA,"SIGN_ON:TOC1.0")
325
self.sendFlap(DATA,"NICK:%s"%self.saved.nick)
326
self.sendFlap(DATA,"CONFIG:%s"%self.saved.config)
327
# sending user configuration goes here
330
def authorize(self,server,port,username,password):
331
if self.saved.password=="":
332
self.saved.password=password
335
return self.saved.password==password
337
def modeConnected(self):
340
if flap[0] not in [DATA,KEEP_ALIVE]: raise TOCParseError
341
flapdata=string.split(flap[1]," ",1)
342
tocname=flapdata[0][4:]
347
func=getattr(self,"toc_"+tocname,None)
351
self.toc_unknown(tocname,data)
355
def toc_unknown(self,tocname,data):
356
self._debug("unknown! %s %s" % (tocname,data))
358
def toc_init_done(self,data):
360
called when all the setup is done.
364
self.signontime=int(time.time())
365
self.factory.users[self.username]=self
368
def toc_add_permit(self,data):
370
adds users to the permit list. if the list is null, then set the mode to DENYALL
373
self.permitmode=DENYALL
377
self.permitmode=PERMITSOME
379
users=string.split(data," ")
380
map(self.permitlist.append,users)
383
def toc_add_deny(self,data):
385
adds users to the deny list. if the list is null, then set the mode to PERMITALL
388
self.permitmode=PERMITALL
392
self.permitmode=DENYSOME
394
users=string.split(data," ")
395
map(self.denylist.append,users)
398
def toc_evil(self,data):
402
toc_evil <username> <anon|norm>
404
username,nora=string.split(data," ")
409
if not(self.factory.users.has_key(username)):
410
self.sendError(CANT_WARN,username)
412
if self.factory.users[username].saved.evilness>=100:
413
self.sendError(CANT_WARN,username)
415
self.factory.users[username].evilFrom(user)
417
def toc_add_buddy(self,data):
419
adds users to the buddy list
421
toc_add_buddy <buddyname1> [<buddyname2>] [<buddyname3>]...
423
buddies=map(normalize,string.split(data," "))
425
if b not in self.buddylist:
426
self.buddylist.append(b)
427
for buddy in buddies:
429
buddy=self.factory.users[buddy]
433
self.buddyUpdate(buddy)
435
def toc_remove_buddy(self,data):
437
removes users from the buddy list
439
toc_remove_buddy <buddyname1> [<buddyname2>] [<buddyname3>]...
441
buddies=string.split(data," ")
442
for buddy in buddies:
444
self.buddylist.remove(normalize(buddy))
447
def toc_send_im(self,data):
449
incoming instant message
451
toc_send_im <screenname> <quoted message> [auto]
453
username,data=string.split(data," ",1)
455
if data[-4:]=="auto":
459
if not(self.factory.users.has_key(username)):
460
self.sendError(NOT_AVAILABLE,username)
462
user=self.factory.users[username]
463
if not(self.canContact(user)):
464
self.sendError(NOT_AVAILABLE,username)
466
user.hearWhisper(self,data,auto)
468
def toc_set_info(self,data):
470
set the users information, retrivable with toc_get_info
472
toc_set_info <user info (quoted)>
477
def toc_set_idle(self,data):
481
toc_set_idle <seconds>
484
self.idletime=time.time()-seconds # time when they started being idle
487
def toc_set_away(self,data):
489
set/unset away message
491
toc_set_away [<away message>]
494
if not self.away and away: # setting an away message
496
self.userclass=self.userclass+'U'
498
elif self.away and not away: # coming back
500
self.userclass=self.userclass[:2]
505
def toc_chat_join(self,data):
509
toc_chat_join <exchange> <room name>
511
exchange,name=string.split(data," ",1)
512
self.factory.getChatroom(int(exchange),unquote(name)).join(self)
514
def toc_chat_invite(self,data):
516
invite others to the room.
518
toc_chat_invite <room id> <invite message> <buddy 1> [<buddy2>]...
520
id,data=string.split(data," ",1)
522
message,data=unquotebeg(data)
523
buddies=string.split(data," ")
525
room=self.factory.chatroom[id]
526
bud=self.factory.users[b]
527
bud.chatInvite(room,self,message)
529
def toc_chat_accept(self,data):
531
accept an invitation.
533
toc_chat_accept <room id>
536
self.factory.chatroom[id].join(self)
538
def toc_chat_send(self,data):
540
send a message to the chat room.
542
toc_chat_send <room id> <message>
544
id,message=string.split(data," ",1)
546
message=unquote(message)
547
self.factory.chatroom[id].say(self,message)
549
def toc_chat_whisper(self,data):
550
id,user,message=string.split(data," ",2)
552
room=self.factory.chatroom[id]
553
message=unquote(message)
554
self.factory.users[user].chatWhisper(room,self,message)
556
def toc_chat_leave(self,data):
560
toc_chat_leave <room id>
563
self.factory.chatroom[id].leave(self)
565
def toc_set_config(self,data):
567
set the saved config. this gets send when you log in.
569
toc_set_config <config>
571
self.saved.config=unquote(data)
573
def toc_get_info(self,data):
575
get the user info for a user
577
toc_get_info <username>
579
if not self.factory.users.has_key(data):
580
self.sendError(901,data)
582
self.sendFlap(2,"GOTO_URL:TIC:info/%s"%data)
584
def toc_format_nickname(self,data):
586
change the format of your nickname.
588
toc_format_nickname <new format>
592
if normalize(nick)==self.username:
594
self.sendFlap(2,"ADMIN_NICK_STATUS:0")
596
self.sendError(BAD_INPUT)
598
def toc_change_passwd(self,data):
599
orig,data=unquotebeg(data)
601
if orig==self.saved.password:
602
self.saved.password=new
603
self.sendFlap(2,"ADMIN_PASSWD_STATUS:0")
605
self.sendError(BAD_INPUT)
607
def sendError(self,code,*varargs):
609
send an error to the user. listing of error messages is below.
614
self.sendFlap(DATA,send)
616
def updateUsers(self):
618
Update the users who have us on their buddylist.
619
Called when the user changes anything (idle,away) so people can get updates.
621
for user in self.factory.users.values():
622
if self.username in user.buddylist and self.canContact(user):
623
user.buddyUpdate(self)
625
def getStatus(self,user):
626
if self.canContact(user):
627
if self in self.factory.users.values():ol='T'
631
idle=int((time.time()-self.idletime)/60)
632
return (self.saved.nick,ol,self.saved.evilness,self.signontime,idle,self.userclass)
634
return (self.saved.nick,'F',0,0,0,self.userclass)
636
def canContact(self,user):
637
if self.permitmode==PERMITALL: return 1
638
elif self.permitmode==DENYALL: return 0
639
elif self.permitmode==PERMITSOME:
640
if user.username in self.permitlist: return 1
642
elif self.permitmode==DENYSOME:
643
if user.username in self.denylist: return 0
646
assert 0,"bad permitmode %s" % self.permitmode
648
def buddyUpdate(self,user):
650
Update the buddy. Called from updateUsers()
652
if not self.canContact(user): return
653
status=user.getStatus(self)
654
if not self._laststatus.has_key(user):
655
self._laststatus[user]=()
656
if self._laststatus[user]!=status:
657
send="UPDATE_BUDDY:%s:%s:%s:%s:%s:%s"%status
658
self.sendFlap(DATA,send)
659
self._laststatus[user]=status
661
def hearWhisper(self,user,data,auto=0):
663
Called when you get an IM. If auto=1, it's an autoreply from an away message.
665
if not self.canContact(user): return
668
send="IM_IN:%s:%s:%s"%(user.saved.nick,auto,data)
669
self.sendFlap(DATA,send)
671
def evilFrom(self,user):
676
self.saved.evilness=self.saved.evilness+int((100-self.saved.evilness)*percent)
677
self.sendFlap(2,"EVILED:%s:%s"%(self.saved.evilness,user))
680
def chatJoin(self,room):
681
self.sendFlap(2,"CHAT_JOIN:%s:%s"%(room.id,room.name))
682
f="CHAT_UPDATE_BUDDY:%s:T"%room.id
685
u.chatUserUpdate(room,self)
689
def chatInvite(self,room,user,message):
690
if not self.canContact(user): return
691
self.sendFlap(2,"CHAT_INVITE:%s:%s:%s:%s"%(room.name,room.id,user.saved.nick,message))
693
def chatUserUpdate(self,room,user):
694
if user in room.users:
698
self.sendFlap(2,"CHAT_UPDATE_BUDDY:%s:%s:%s"%(room.id,inroom,user.saved.nick))
700
def chatMessage(self,room,user,message):
701
if not self.canContact(user): return
702
self.sendFlap(2,"CHAT_IN:%s:%s:F:%s"%(room.id,user.saved.nick,message))
704
def chatWhisper(self,room,user,message):
705
if not self.canContact(user): return
706
self.sendFlap(2,"CHAT_IN:%s:%s:T:%s"%(room.id,user.saved.nick,message))
708
def chatLeave(self,room):
709
self.sendFlap(2,"CHAT_LEFT:%s"%(room.id))
713
def __init__(self,fac,exchange,name,id):
714
self.exchange=exchange
721
if user in self.users:
723
self.users.append(user)
726
def leave(self,user):
727
if user not in self.users:
729
self.users.remove(user)
732
u.chatUserUpdate(self,user)
733
if len(self.users)==0:
734
self.factory.remChatroom(self)
736
def say(self,user,message):
738
u.chatMessage(self,user,message)
749
class TOCFactory(protocol.Factory):
756
def buildProtocol(self,addr):
761
def getChatroom(self,exchange,name):
762
for i in self.chatroom.values():
763
if normalize(i.name)==normalize(name):
765
self.chatroom[self.chatroomid]=Chatroom(self,exchange,name,self.chatroomid)
766
self.chatroomid=self.chatroomid+1
767
return self.chatroom[self.chatroomid-1]
769
def remChatroom(self,room):
771
del self.chatroom[id]
777
MAXARGS["UPDATE_BUDDY"]=5
780
MAXARGS["CHAT_JOIN"]=1
782
MAXARGS["CHAT_UPDATE_BUDDY"]=-1
783
MAXARGS["CHAT_INVITE"]=3
784
MAXARGS["CHAT_LEFT"]=0
785
MAXARGS["ADMIN_NICK_STATUS"]=0
786
MAXARGS["ADMIN_PASSWD_STATUS"]=0
789
class TOCClient(protocol.Protocol):
790
def __init__(self,username,password,authhost="login.oscar.aol.com",authport=5190):
792
self.username=normalize(username) # our username
793
self._password=password # our password
794
self._mode="SendNick" # current mode
795
self._ourseqnum=19071 # current sequence number (for sendFlap)
796
self._authhost=authhost # authorization host
797
self._authport=authport # authorization port
798
self._online=0 # are we online?
799
self._buddies=[] # the current buddy list
800
self._privacymode=PERMITALL # current privacy mode
801
self._permitlist=[] # list of users on the permit list
802
self._roomnames={} # the names for each of the rooms we're in
803
self._receivedchatmembers={} # have we gotten who's in our room yet?
805
self._cookies={} # for file transfers
806
self._buf='' # current data buffer
809
def _debug(self,data):
812
def sendFlap(self,type,data):
817
s=s+struct.pack("!BHH",type,self._ourseqnum,length)
819
self._ourseqnum=self._ourseqnum+1
820
if self._ourseqnum>(256*256+256):
823
self.transport.write(s)
827
tests to see if a flap is actually on the buffer
829
if self._buf=='': return 0
830
if self._buf[0]!="*": return 0
831
if len(self._buf)<6: return 0
832
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
833
if type not in range(1,6): return 0
834
if len(self._buf)<6+length: return 0
838
if self._buf=='': return None
839
if self._buf[0]!="*":
841
if len(self._buf)<6: return None
842
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
843
if len(self._buf)<6+length: return None
844
data=self._buf[6:6+length]
845
self._buf=self._buf[6+length:]
846
if data and data[-1]=="\000":
850
def connectionMade(self):
851
self._debug("connection made! %s" % self.transport)
852
self.transport.write("FLAPON\r\n\r\n")
854
def connectionLost(self, reason):
855
self._debug("connection lost!")
858
def dataReceived(self,data):
859
self._buf=self._buf+data
862
func=getattr(self,"mode%s"%self._mode)
865
def modeSendNick(self,flap):
866
if flap!=[1,"\000\000\000\001"]: raise TOCParseError
867
s="\000\000\000\001\000\001"+struct.pack("!H",len(self.username))+self.username
869
s="toc_signon %s %s %s %s english \"penguin\""%(self._authhost,\
870
self._authport,self.username,roast(self._password))
874
def modeData(self,flap):
877
if not ':' in flap[1]:
878
self._debug("bad SNAC:%s"%(flap[1]))
880
command,rest=string.split(flap[1],":",1)
881
if MAXARGS.has_key(command):
882
maxsplit=MAXARGS[command]
886
l=tuple(string.split(rest,":"))
890
l=tuple(string.split(rest,":",maxsplit))
891
self._debug("%s %s"%(command,l))
893
func=getattr(self,"toc%s"%command)
894
self._debug("calling %s"%func)
896
self._debug("calling %s"%self.tocUNKNOWN)
897
self.tocUNKNOWN(command,l)
901
def tocUNKNOWN(self,command,data):
904
def tocSIGN_ON(self,data):
905
if data!=("TOC1.0",): raise TOCParseError
906
self._debug("Whee, signed on!")
907
if self._buddies: self.add_buddy(self._buddies)
911
def tocNICK(self,data):
913
Handle a message that looks like::
915
NICK:<format of nickname>
917
self.username=data[0]
919
def tocCONFIG(self,data):
921
Handle a message that looks like::
925
Format of config data:
927
- g: group. all users until next g or end of config are in this group
929
- p: person on the permit list
930
- d: person on the deny list
931
- m: permit/deny mode (1: permit all, 2: deny all, 3: permit some, 4: deny some)
934
if data and data[0]=="{":data=data[1:-1]
935
lines=string.split(data,"\n")
944
if code=='g': # group
946
buddylist[currentgroup]=[]
948
buddylist[currentgroup].append(data)
955
self.gotConfig(mode,buddylist,permit,deny)
957
def tocIM_IN(self,data):
959
Handle a message that looks like::
961
IM_IN:<user>:<autoreply T|F>:message
964
autoreply=(data[1]=='T')
966
self.hearMessage(user,message,autoreply)
968
def tocUPDATE_BUDDY(self,data):
970
Handle a message that looks like::
972
UPDATE_BUDDY:<username>:<online T|F>:<warning level>:<signon time>:<idle time (minutes)>:<user class>
975
online=(data[1]=='T')
978
away=(data[5][-1]=='U')
981
self.updateBuddy(data[0],online,int(data[2]),int(data[3]),int(data[4]),data[5],away)
983
def tocERROR(self,data):
985
Handle a message that looks like::
987
ERROR:<error code>:<misc. data>
989
code,args=data[0],data[1:]
990
self.hearError(int(code),args)
992
def tocEVILED(self,data):
994
Handle a message that looks like::
996
EVILED:<current warning level>:<user who warned us>
998
self.hearWarning(data[0],data[1])
1000
def tocCHAT_JOIN(self,data):
1002
Handle a message that looks like::
1004
CHAT_JOIN:<room id>:<room name>
1006
#self.chatJoined(int(data[0]),data[1])
1007
self._roomnames[int(data[0])]=data[1]
1008
self._receivedchatmembers[int(data[0])]=0
1010
def tocCHAT_UPDATE_BUDDY(self,data):
1012
Handle a message that looks like::
1014
CHAT_UPDATE_BUDDY:<room id>:<in room? T/F>:<user 1>:<user 2>...
1017
inroom=(data[1]=='T')
1018
if self._receivedchatmembers[roomid]:
1020
self.chatUpdate(roomid,u,inroom)
1022
self._receivedchatmembers[roomid]=1
1023
self.chatJoined(roomid,self._roomnames[roomid],list(data[2:]))
1025
def tocCHAT_IN(self,data):
1027
Handle a message that looks like::
1029
CHAT_IN:<room id>:<username>:<whisper T/F>:<message>
1033
whisper=(data[2]=='T')
1035
self.chatHearWhisper(int(data[0]),data[1],data[3])
1037
self.chatHearMessage(int(data[0]),data[1],data[3])
1039
def tocCHAT_INVITE(self,data):
1041
Handle a message that looks like::
1043
CHAT_INVITE:<room name>:<room id>:<username>:<message>
1045
self.chatInvited(int(data[1]),data[0],data[2],data[3])
1047
def tocCHAT_LEFT(self,data):
1049
Handle a message that looks like::
1053
self.chatLeft(int(data[0]))
1054
del self._receivedchatmembers[int(data[0])]
1055
del self._roomnames[int(data[0])]
1057
def tocRVOUS_PROPOSE(self,data):
1059
Handle a message that looks like::
1061
RVOUS_PROPOSE:<user>:<uuid>:<cookie>:<seq>:<rip>:<pip>:<vip>:<port>
1062
[:tlv tag1:tlv value1[:tlv tag2:tlv value2[:...]]]
1064
user,uid,cookie,seq,rip,pip,vip,port=data[:8]
1065
cookie=base64.decodestring(cookie)
1068
for i in range(8,len(data),2):
1070
value=base64.decodestring(data[i+1])
1074
func=getattr(self,"toc%s"%name)
1076
self._debug("no function for UID %s" % uid)
1078
func(user,cookie,seq,pip,vip,port,tlvs)
1080
def tocSEND_FILE(self,user,cookie,seq,pip,vip,port,tlvs):
1081
if tlvs.has_key('12'):
1082
description=tlvs['12']
1085
subtype,numfiles,size=struct.unpack("!HHI",tlvs['10001'][:8])
1086
name=tlvs['10001'][8:-4]
1087
while name[-1]=='\000':
1089
self._cookies[cookie]=[user,SEND_FILE_UID,pip,port,{'name':name}]
1090
self.rvousProposal("send",cookie,user,vip,port,description=description,
1091
name=name,files=numfiles,size=size)
1093
def tocGET_FILE(self,user,cookie,seq,pip,vip,port,tlvs):
1095
# XXX add this back in
1096
#reactor.clientTCP(pip,port,GetFileTransfer(self,cookie,os.path.expanduser("~")))
1097
#self.rvous_accept(user,cookie,GET_FILE_UID)
1101
called when we are first online
1105
def gotConfig(self,mode,buddylist,permit,deny):
1107
called when we get a configuration from the server
1108
mode := permit/deny mode
1109
buddylist := current buddylist
1110
permit := permit list
1115
def hearError(self,code,args):
1117
called when an error is received
1119
args := misc. arguments (username, etc.)
1123
def hearWarning(self,newamount,username):
1125
called when we get warned
1126
newamount := the current warning level
1127
username := the user who warned us, or '' if it's anonymous
1131
def hearMessage(self,username,message,autoreply):
1133
called when you receive an IM
1134
username := the user who the IM is from
1135
message := the message
1136
autoreply := true if the message is an autoreply from an away message
1140
def updateBuddy(self,username,online,evilness,signontime,idletime,userclass,away):
1142
called when a buddy changes state
1143
username := the user whos state changed
1144
online := true if the user is online
1145
evilness := the users current warning level
1146
signontime := the time the user signed on (UNIX epoch)
1147
idletime := the time the user has been idle (minutes)
1148
away := true if the user is away
1149
userclass := the class of the user (generally " O")
1153
def chatJoined(self,roomid,roomname,users):
1155
we just joined a chat room
1156
roomid := the AIM id for the room
1157
roomname := the name for the room
1158
users := a list of the users already in the room
1162
def chatUpdate(self,roomid,username,inroom):
1164
a user has joined the room
1165
roomid := the AIM id for the room
1166
username := the username
1167
inroom := true if the user is in the room
1171
def chatHearMessage(self,roomid,username,message):
1173
a message was sent to the room
1174
roomid := the AIM id for the room
1175
username := the user who sent the message
1176
message := the message
1180
def chatHearWhisper(self,roomid,username,message):
1182
someone whispered to us in a chatroom
1183
roomid := the AIM for the room
1184
username := the user who whispered to us
1185
message := the message
1189
def chatInvited(self,roomid,roomname,username,message):
1191
we were invited to a chat room
1192
roomid := the AIM id for the room
1193
roomname := the name of the room
1194
username := the user who invited us
1195
message := the invite message
1199
def chatLeft(self,roomid):
1202
roomid := the AIM id for the room
1206
def rvousProposal(self,type,cookie,user,vip,port,**kw):
1208
we were asked for a rondevouz
1209
type := the type of rondevous. currently, one of ["send"]
1210
cookie := the cookie. pass this to rvous_accept()
1211
user := the user who asked us
1212
vip := their verified_ip
1213
port := the port they want us to conenct to
1216
pass #self.rvous_accept(cookie)
1218
def receiveBytes(self,user,file,chunk,sofar,total):
1220
we received part of a file from a file transfer
1221
file := the name of the file
1222
chunk := the chunk of data
1223
sofar := how much data we've gotten so far
1224
total := the total amount of data
1226
pass #print user,file,sofar,total
1230
return our away status
1232
return len(self._awaymessage)>0
1234
def set_config(self,mode,buddylist,permit,deny):
1236
set the server configuration
1238
buddylist := buddy list
1239
permit := permit list
1243
for g in buddylist.keys():
1245
for u in buddylist[g]:
1252
self.sendFlap(2,"toc_set_config %s"%quote(s))
1254
def add_buddy(self,buddies):
1256
if type(buddies)==type(""): buddies=[buddies]
1258
s=s+" "+normalize(b)
1259
self.sendFlap(2,"toc_add_buddy%s"%s)
1261
def del_buddy(self,buddies):
1263
if type(buddies)==type(""): buddies=[buddies]
1266
self.sendFlap(2,"toc_remove_buddy%s"%s)
1268
def add_permit(self,users):
1269
if type(users)==type(""): users=[users]
1271
if self._privacymode!=PERMITSOME:
1272
self._privacymode=PERMITSOME
1276
if u not in self._permitlist:self._permitlist.append(u)
1279
self._privacymode=DENYALL
1282
self.sendFlap(2,"toc_add_permit"+s)
1284
def del_permit(self,users):
1285
if type(users)==type(""): users=[users]
1286
p=self._permitlist[:]
1294
def add_deny(self,users):
1295
if type(users)==type(""): users=[users]
1297
if self._privacymode!=DENYSOME:
1298
self._privacymode=DENYSOME
1302
if u not in self._denylist:self._denylist.append(u)
1305
self._privacymode=PERMITALL
1308
self.sendFlap(2,"toc_add_deny"+s)
1310
def del_deny(self,users):
1311
if type(users)==type(""): users=[users]
1323
called to finish the setup, and signon to the network
1325
self.sendFlap(2,"toc_init_done")
1326
self.sendFlap(2,"toc_set_caps %s" % (SEND_FILE_UID,)) # GET_FILE_UID)
1328
def say(self,user,message,autoreply=0):
1331
user := the user to send to
1332
message := the message
1333
autoreply := true if the message is an autoreply (good for away messages)
1335
if autoreply: a=" auto"
1337
self.sendFlap(2,"toc_send_im %s %s%s"%(normalize(user),quote(message),a))
1339
def idle(self,idletime=0):
1342
idletime := the seconds that the user has been away, or 0 if they're back
1344
self.sendFlap(2,"toc_set_idle %s" % int(idletime))
1346
def evil(self,user,anon=0):
1349
user := the user to warn
1350
anon := if true, an anonymous warning
1352
self.sendFlap(2,"toc_evil %s %s"%(normalize(user), (not anon and "anon") or "norm"))
1354
def away(self,message=''):
1357
message := the message, or '' to come back from awayness
1359
self._awaymessage=message
1361
message=' '+quote(message)
1362
self.sendFlap(2,"toc_set_away%s"%message)
1364
def chat_join(self,exchange,roomname):
1367
exchange := should almost always be 4
1368
roomname := room name
1370
roomname=string.replace(roomname," ","")
1371
self.sendFlap(2,"toc_chat_join %s %s"%(int(exchange),roomname))
1373
def chat_say(self,roomid,message):
1375
send a message to a chatroom
1376
roomid := the AIM id for the room
1377
message := the message to send
1379
self.sendFlap(2,"toc_chat_send %s %s"%(int(roomid),quote(message)))
1381
def chat_whisper(self,roomid,user,message):
1383
whisper to another user in a chatroom
1384
roomid := the AIM id for the room
1385
user := the user to whisper to
1386
message := the message to send
1388
self.sendFlap(2,"toc_chat_whisper %s %s %s"%(int(roomid),normalize(user),quote(message)))
1390
def chat_leave(self,roomid):
1393
roomid := the AIM id for the room
1395
self.sendFlap(2,"toc_chat_leave %s" % int(roomid))
1397
def chat_invite(self,roomid,usernames,message):
1399
invite a user[s] to the chat room
1400
roomid := the AIM id for the room
1401
usernames := either a string (one username) or a list (more than one)
1402
message := the message to invite them with
1404
if type(usernames)==type(""): # a string, one username
1411
self.sendFlap(2,"toc_chat_invite %s %s %s" % (int(roomid),quote(message),users))
1413
def chat_accept(self,roomid):
1415
accept an invite to a chat room
1416
roomid := the AIM id for the room
1418
self.sendFlap(2,"toc_chat_accept %s"%int(roomid))
1420
def rvous_accept(self,cookie):
1421
user,uuid,pip,port,d=self._cookies[cookie]
1422
self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user),
1424
if uuid==SEND_FILE_UID:
1425
protocol.ClientCreator(reactor, SendFileTransfer,self,cookie,user,d["name"]).connectTCP(pip,port)
1427
def rvous_cancel(self,cookie):
1428
user,uuid,pip,port,d=self._cookies[cookie]
1429
self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user),
1431
del self._cookies[cookie]
1434
class SendFileTransfer(protocol.Protocol):
1435
header_fmt="!4s2H8s6H10I32s3c69s16s2H64s"
1437
def __init__(self,client,cookie,user,filename):
1441
self.filename=filename
1445
def dataReceived(self,data):
1446
if not self.hdr[2]==0x202:
1447
self.hdr=list(struct.unpack(self.header_fmt,data[:256]))
1449
self.hdr[3]=self.cookie
1452
self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr))
1455
self.name=self.filename
1457
self.name=self.filename+self.hdr[-1]
1458
while self.name[-1]=="\000":
1459
self.name=self.name[:-1]
1461
self.sofar=self.sofar+len(data)
1462
self.client.receiveBytes(self.user,self.name,data,self.sofar,self.hdr[11])
1463
if self.sofar==self.hdr[11]: # end of this file
1465
self.hdr[7]=self.hdr[7]-1
1466
self.hdr[9]=self.hdr[9]-1
1467
self.hdr[19]=DUMMY_CHECKSUM # XXX really calculate this
1468
self.hdr[18]=self.hdr[18]+1
1470
self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr))
1473
self.transport.loseConnection()
1476
class GetFileTransfer(protocol.Protocol):
1477
header_fmt="!4s 2H 8s 6H 10I 32s 3c 69s 16s 2H 64s"
1478
def __init__(self,client,cookie,dir):
1484
def connectionMade(self):
1485
def func(f,path,names):
1486
names.sort(lambda x,y:cmp(string.lower(x),string.lower(y)))
1488
name=os.path.join(path,n)
1489
lt=time.localtime(os.path.getmtime(name))
1490
size=os.path.getsize(name)
1492
f.append("%02d/%02d/%4d %02d:%02d %8d %s" %
1493
(lt[1],lt[2],lt[0],lt[3],lt[4],size,name[f[0]:]))
1494
f=[len(self.dir)+1,0]
1495
os.path.walk(self.dir,func,f)
1497
self.listing=string.join(f[2:],"\r\n")+"\r\n"
1498
open("\\listing.txt","w").write(self.listing)
1499
hdr=["OFT2",256,0x1108,self.cookie,0,0,len(f)-2,len(f)-2,1,1,size,
1500
len(self.listing),os.path.getmtime(self.dir),
1501
checksum(self.listing),0,0,0,0,0,0,"OFT_Windows ICBMFT V1.1 32",
1502
"\002",chr(0x1a),chr(0x10),"","",0,0,""]
1503
self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
1505
def dataReceived(self,data):
1506
self.buf=self.buf+data
1507
while len(self.buf)>=256:
1508
hdr=list(struct.unpack(self.header_fmt,self.buf[:256]))
1509
self.buf=self.buf[256:]
1511
self.file=StringIO.StringIO(self.listing)
1512
self.transport.registerProducer(self,0)
1513
elif hdr[2]==0x120b: pass
1514
elif hdr[2]==0x120c: # file request
1516
for k,v in [["\000",""],["\001",os.sep]]:
1517
file=string.replace(file,k,v)
1518
self.name=os.path.join(self.dir,file)
1519
self.file=open(self.name,'rb')
1522
hdr[10]=hdr[11]=os.path.getsize(self.name)
1523
hdr[12]=os.path.getmtime(self.name)
1524
hdr[13]=checksum_file(self.file)
1528
self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
1529
log.msg("got file request for %s"%file,hex(hdr[13]))
1530
elif hdr[2]==0x0202:
1531
log.msg("sending file")
1532
self.transport.registerProducer(self,0)
1533
elif hdr[2]==0x0204:
1534
log.msg("real checksum: %s"%hex(hdr[19]))
1536
elif hdr[2]==0x0205: # resume
1539
data=self.file.read(already)
1542
log.msg("restarting at %s"%already)
1544
hdr[19]=checksum(data)
1545
self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
1546
elif hdr[2]==0x0207:
1547
self.transport.registerProducer(self,0)
1549
log.msg("don't understand 0x%04x"%hdr[2])
1552
def resumeProducing(self):
1553
data=self.file.read(4096)
1556
self.transport.unregisterProducer()
1557
self.transport.write(data)
1559
def pauseProducing(self): pass
1561
def stopProducing(self): del self.file
1564
SEND_FILE_UID = "09461343-4C7F-11D1-8222-444553540000"
1565
GET_FILE_UID = "09461348-4C7F-11D1-8222-444553540000"
1567
SEND_FILE_UID:"SEND_FILE",
1568
GET_FILE_UID:"GET_FILE"
1575
MESSAGES_TOO_FAST=903
1580
SERVICE_UNAVAILABLE=914
1589
TOO_MANY_MATCHES=971
1590
NEED_MORE_QUALIFIERS=972
1597
DIR_FAIL_UNKNOWN=979
1600
SERVICE_TEMP_UNAVAILABLE=981
1601
WARNING_TOO_HIGH=982
1602
CONNECTING_TOO_QUICK=983
1606
STD_MESSAGE[NOT_AVAILABLE]="%s not currently available"
1607
STD_MESSAGE[CANT_WARN]="Warning of %s not currently available"
1608
STD_MESSAGE[MESSAGES_TOO_FAST]="A message has been dropped, you are exceeding the server speed limit"
1609
STD_MESSAGE[BAD_INPUT]="Error validating input"
1610
STD_MESSAGE[BAD_ACCOUNT]="Invalid account"
1611
STD_MESSAGE[REQUEST_ERROR]="Error encountered while processing request"
1612
STD_MESSAGE[SERVICE_UNAVAILABLE]="Service unavailable"
1613
STD_MESSAGE[NO_CHAT_IN]="Chat in %s is unavailable"
1614
STD_MESSAGE[SEND_TOO_FAST]="You are sending messages too fast to %s"
1615
STD_MESSAGE[MISSED_BIG_IM]="You missed an IM from %s because it was too big"
1616
STD_MESSAGE[MISSED_FAST_IM]="You missed an IM from %s because it was sent too fast"
1617
# skipping directory for now
1618
STD_MESSAGE[BAD_NICKNAME]="Incorrect nickname or password"
1619
STD_MESSAGE[SERVICE_TEMP_UNAVAILABLE]="The service is temporarily unavailable"
1620
STD_MESSAGE[WARNING_TOO_HIGH]="Your warning level is currently too high to sign on"
1621
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."
1622
STD_MESSAGE[UNKNOWN_SIGNON]="An unknown signon error has occurred %s"