1
# -*- test-case-name: twisted.words.test -*-
2
# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
3
# See LICENSE for details.
7
Implements a AOL Instant Messenger TOC server and client, using the Twisted framework.
10
info,dir: see how gaim connects for this...it may never work if it tries to connect to the aim server automatically
12
This module is stable, but deprecated.
14
Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>}
18
from twisted.internet import reactor, protocol
19
from twisted.python import log
29
SIGNON,DATA,ERROR,SIGNOFF,KEEP_ALIVE=range(1,6)
30
PERMITALL,DENYALL,PERMITSOME,DENYSOME=range(1,5)
32
DUMMY_CHECKSUM = -559038737 # 0xdeadbeef
35
rep=['\\','$','{','}','[',']','(',')','"']
37
s=string.replace(s,r,"\\"+r)
42
if s[0]!='"': return s
57
for i in range(1,len(s)):
58
if s[i]=='"' and s[i-1]!='\\':
64
pw=string.lower(pw[2:])
67
hex=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]
70
value=(16*hex.index(st[0]))+hex.index(st[1])
71
xor=ord(roaststring[count])
72
count=(count+1)%len(roaststring)
77
# contributed by jemfinch on #python
83
ro=ro+'%02x'%(c^ord(key[i%len(key)]))
85
return string.lower(ro)
88
return DUMMY_CHECKSUM # do it like gaim does, since the checksum
89
# formula doesn't work
90
## # used in file transfers
91
## check0 = check1 = 0x00ff
92
## for i in range(len(b)):
94
## if ord(b[i])>check1:
95
## check1=check1+0x100 # wrap
102
## check1=check1-ord(b[i])
104
## if ord(b[i])>check0: # wrap
105
## check0=check0+0x100
112
## check0=check0-ord(b[i])
113
## check0=check0 & 0xff
114
## check1=check1 & 0xff
115
## checksum=(long(check0)*0x1000000)+(long(check1)*0x10000)
118
def checksum_file(f):
119
return DUMMY_CHECKSUM # do it like gaim does, since the checksum
120
# formula doesn't work
121
## check0=check1=0x00ff
129
## if ord(char)>check1:
130
## check1=check1+0x100 # wrap
137
## check1=check1-ord(char)
139
## if ord(char)>check0: # wrap
140
## check0=check0+0x100
147
## check0=check0-ord(char)
148
## check0=check0 & 0xff
149
## check1=check1 & 0xff
150
## checksum=(long(check0)*0x1000000)+(long(check1)*0x10000)
155
s=string.replace(s," ","")
159
class TOCParseError(ValueError):
163
class TOC(protocol.Protocol):
166
def connectionMade(self):
167
# initialization of protocol
173
self._laststatus={} # the last status for a user
175
self.permitmode=PERMITALL
186
def _debug(self,data):
189
def connectionLost(self, reason):
190
self._debug("dropped connection from %s" % self.username)
192
del self.factory.users[self.username]
195
for k in self.factory.chatroom.keys():
197
self.factory.chatroom[k].leave(self)
198
except TOCParseError:
201
self.factory.savedusers[self.username]=self.saved
204
def sendFlap(self,type,data):
206
send a FLAP to the client
213
send=send+struct.pack("!BHH",type,self._ourseqnum,length)
215
self._ourseqnum=self._ourseqnum+1
216
if self._ourseqnum>(256L**4):
218
self.transport.write(send)
220
def dataReceived(self,data):
221
self._buf=self._buf+data
223
func=getattr(self,"mode%s"%self._mode)
227
if self._onlyflaps and self.isFlap(): self.dataReceived("")
231
tests to see if a flap is actually on the buffer
233
if self._buf=='': return 0
234
if self._buf[0]!="*": return 0
235
if len(self._buf)<6: return 0
236
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
237
if type not in range(1,6): return 0
238
if len(self._buf)<6+length: return 0
243
read the first FLAP off self._buf, raising errors if it isn't in the right form.
244
the FLAP is the basic TOC message format, and is logically equivilant to a packet in TCP
246
if self._buf=='': return None
247
if self._buf[0]!="*":
249
if len(self._buf)<6: return None
250
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
251
if len(self._buf)<6+length: return None
252
data=self._buf[6:6+length]
253
self._buf=self._buf[6+length:]
254
if data and data[-1]=="\000":
256
self._debug([type,data])
261
# line,rest=string.split(self._buf,"\n",1)
262
# get,username,http=string.split(line," ",2)
264
# return "Web" # not enough data
265
# foo,type,username=string.split(username,"/")
267
# user=self.factory.users[username]
268
# 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)
269
# self.transport.write("HTTP/1.1 200 OK\n")
270
# self.transport.write("Content-Type: text/html\n")
271
# self.transport.write("Content-Length: %s\n\n"%len(text))
272
# self.transport.write(text)
273
# self.loseConnection()
275
def modeFlapon(self):
276
#if self._buf[:3]=="GET": self.modeWeb() # TODO: get this working
277
if len(self._buf)<10: return "Flapon" # not enough bytes
278
flapon,self._buf=self._buf[:10],self._buf[10:]
279
if flapon!="FLAPON\r\n\r\n":
281
self.sendFlap(SIGNON,"\000\000\000\001")
285
def modeSignon(self):
289
if flap[0]!=SIGNON: raise TOCParseError
290
version,tlv,unlength=struct.unpack("!LHH",flap[1][:8])
291
if version!=1 or tlv!=1 or unlength+8!=len(flap[1]):
293
self.username=normalize(flap[1][8:])
294
if self.username in self.factory.savedusers.keys():
295
self.saved=self.factory.savedusers[self.username]
297
self.saved=SavedUser()
298
self.saved.nick=self.username
301
def modeTocSignon(self):
305
if flap[0]!=DATA: raise TOCParseError
306
data=string.split(flap[1]," ")
307
if data[0]!="toc_signon": raise TOCParseError
309
if not i:data.remove(i)
310
password=unroast(data[4])
311
if not(self.authorize(data[1],int(data[2]),data[3],password)):
312
self.sendError(BAD_NICKNAME)
313
self.transport.loseConnection()
315
self.sendFlap(DATA,"SIGN_ON:TOC1.0")
316
self.sendFlap(DATA,"NICK:%s"%self.saved.nick)
317
self.sendFlap(DATA,"CONFIG:%s"%self.saved.config)
318
# sending user configuration goes here
321
def authorize(self,server,port,username,password):
322
if self.saved.password=="":
323
self.saved.password=password
326
return self.saved.password==password
328
def modeConnected(self):
331
if flap[0] not in [DATA,KEEP_ALIVE]: raise TOCParseError
332
flapdata=string.split(flap[1]," ",1)
333
tocname=flapdata[0][4:]
338
func=getattr(self,"toc_"+tocname,None)
342
self.toc_unknown(tocname,data)
346
def toc_unknown(self,tocname,data):
347
self._debug("unknown! %s %s" % (tocname,data))
349
def toc_init_done(self,data):
351
called when all the setup is done.
355
self.signontime=int(time.time())
356
self.factory.users[self.username]=self
359
def toc_add_permit(self,data):
361
adds users to the permit list. if the list is null, then set the mode to DENYALL
364
self.permitmode=DENYALL
368
self.permitmode=PERMITSOME
370
users=string.split(data," ")
371
map(self.permitlist.append,users)
374
def toc_add_deny(self,data):
376
adds users to the deny list. if the list is null, then set the mode to PERMITALL
379
self.permitmode=PERMITALL
383
self.permitmode=DENYSOME
385
users=string.split(data," ")
386
map(self.denylist.append,users)
389
def toc_evil(self,data):
393
toc_evil <username> <anon|norm>
395
username,nora=string.split(data," ")
400
if not(self.factory.users.has_key(username)):
401
self.sendError(CANT_WARN,username)
403
if self.factory.users[username].saved.evilness>=100:
404
self.sendError(CANT_WARN,username)
406
self.factory.users[username].evilFrom(user)
408
def toc_add_buddy(self,data):
410
adds users to the buddy list
412
toc_add_buddy <buddyname1> [<buddyname2>] [<buddyname3>]...
414
buddies=map(normalize,string.split(data," "))
416
if b not in self.buddylist:
417
self.buddylist.append(b)
418
for buddy in buddies:
420
buddy=self.factory.users[buddy]
424
self.buddyUpdate(buddy)
426
def toc_remove_buddy(self,data):
428
removes users from the buddy list
430
toc_remove_buddy <buddyname1> [<buddyname2>] [<buddyname3>]...
432
buddies=string.split(data," ")
433
for buddy in buddies:
435
self.buddylist.remove(normalize(buddy))
438
def toc_send_im(self,data):
440
incoming instant message
442
toc_send_im <screenname> <quoted message> [auto]
444
username,data=string.split(data," ",1)
446
if data[-4:]=="auto":
450
if not(self.factory.users.has_key(username)):
451
self.sendError(NOT_AVAILABLE,username)
453
user=self.factory.users[username]
454
if not(self.canContact(user)):
455
self.sendError(NOT_AVAILABLE,username)
457
user.hearWhisper(self,data,auto)
459
def toc_set_info(self,data):
461
set the users information, retrivable with toc_get_info
463
toc_set_info <user info (quoted)>
468
def toc_set_idle(self,data):
472
toc_set_idle <seconds>
475
self.idletime=time.time()-seconds # time when they started being idle
478
def toc_set_away(self,data):
480
set/unset away message
482
toc_set_away [<away message>]
485
if not self.away and away: # setting an away message
487
self.userclass=self.userclass+'U'
489
elif self.away and not away: # coming back
491
self.userclass=self.userclass[:2]
496
def toc_chat_join(self,data):
500
toc_chat_join <exchange> <room name>
502
exchange,name=string.split(data," ",1)
503
self.factory.getChatroom(int(exchange),unquote(name)).join(self)
505
def toc_chat_invite(self,data):
507
invite others to the room.
509
toc_chat_invite <room id> <invite message> <buddy 1> [<buddy2>]...
511
id,data=string.split(data," ",1)
513
message,data=unquotebeg(data)
514
buddies=string.split(data," ")
516
room=self.factory.chatroom[id]
517
bud=self.factory.users[b]
518
bud.chatInvite(room,self,message)
520
def toc_chat_accept(self,data):
522
accept an invitation.
524
toc_chat_accept <room id>
527
self.factory.chatroom[id].join(self)
529
def toc_chat_send(self,data):
531
send a message to the chat room.
533
toc_chat_send <room id> <message>
535
id,message=string.split(data," ",1)
537
message=unquote(message)
538
self.factory.chatroom[id].say(self,message)
540
def toc_chat_whisper(self,data):
541
id,user,message=string.split(data," ",2)
543
room=self.factory.chatroom[id]
544
message=unquote(message)
545
self.factory.users[user].chatWhisper(room,self,message)
547
def toc_chat_leave(self,data):
551
toc_chat_leave <room id>
554
self.factory.chatroom[id].leave(self)
556
def toc_set_config(self,data):
558
set the saved config. this gets send when you log in.
560
toc_set_config <config>
562
self.saved.config=unquote(data)
564
def toc_get_info(self,data):
566
get the user info for a user
568
toc_get_info <username>
570
if not self.factory.users.has_key(data):
571
self.sendError(901,data)
573
self.sendFlap(2,"GOTO_URL:TIC:info/%s"%data)
575
def toc_format_nickname(self,data):
577
change the format of your nickname.
579
toc_format_nickname <new format>
583
if normalize(nick)==self.username:
585
self.sendFlap(2,"ADMIN_NICK_STATUS:0")
587
self.sendError(BAD_INPUT)
589
def toc_change_passwd(self,data):
590
orig,data=unquotebeg(data)
592
if orig==self.saved.password:
593
self.saved.password=new
594
self.sendFlap(2,"ADMIN_PASSWD_STATUS:0")
596
self.sendError(BAD_INPUT)
598
def sendError(self,code,*varargs):
600
send an error to the user. listing of error messages is below.
605
self.sendFlap(DATA,send)
607
def updateUsers(self):
609
Update the users who have us on their buddylist.
610
Called when the user changes anything (idle,away) so people can get updates.
612
for user in self.factory.users.values():
613
if self.username in user.buddylist and self.canContact(user):
614
user.buddyUpdate(self)
616
def getStatus(self,user):
617
if self.canContact(user):
618
if self in self.factory.users.values():ol='T'
622
idle=int((time.time()-self.idletime)/60)
623
return (self.saved.nick,ol,self.saved.evilness,self.signontime,idle,self.userclass)
625
return (self.saved.nick,'F',0,0,0,self.userclass)
627
def canContact(self,user):
628
if self.permitmode==PERMITALL: return 1
629
elif self.permitmode==DENYALL: return 0
630
elif self.permitmode==PERMITSOME:
631
if user.username in self.permitlist: return 1
633
elif self.permitmode==DENYSOME:
634
if user.username in self.denylist: return 0
637
assert 0,"bad permitmode %s" % self.permitmode
639
def buddyUpdate(self,user):
641
Update the buddy. Called from updateUsers()
643
if not self.canContact(user): return
644
status=user.getStatus(self)
645
if not self._laststatus.has_key(user):
646
self._laststatus[user]=()
647
if self._laststatus[user]!=status:
648
send="UPDATE_BUDDY:%s:%s:%s:%s:%s:%s"%status
649
self.sendFlap(DATA,send)
650
self._laststatus[user]=status
652
def hearWhisper(self,user,data,auto=0):
654
Called when you get an IM. If auto=1, it's an autoreply from an away message.
656
if not self.canContact(user): return
659
send="IM_IN:%s:%s:%s"%(user.saved.nick,auto,data)
660
self.sendFlap(DATA,send)
662
def evilFrom(self,user):
667
self.saved.evilness=self.saved.evilness+int((100-self.saved.evilness)*percent)
668
self.sendFlap(2,"EVILED:%s:%s"%(self.saved.evilness,user))
671
def chatJoin(self,room):
672
self.sendFlap(2,"CHAT_JOIN:%s:%s"%(room.id,room.name))
673
f="CHAT_UPDATE_BUDDY:%s:T"%room.id
676
u.chatUserUpdate(room,self)
680
def chatInvite(self,room,user,message):
681
if not self.canContact(user): return
682
self.sendFlap(2,"CHAT_INVITE:%s:%s:%s:%s"%(room.name,room.id,user.saved.nick,message))
684
def chatUserUpdate(self,room,user):
685
if user in room.users:
689
self.sendFlap(2,"CHAT_UPDATE_BUDDY:%s:%s:%s"%(room.id,inroom,user.saved.nick))
691
def chatMessage(self,room,user,message):
692
if not self.canContact(user): return
693
self.sendFlap(2,"CHAT_IN:%s:%s:F:%s"%(room.id,user.saved.nick,message))
695
def chatWhisper(self,room,user,message):
696
if not self.canContact(user): return
697
self.sendFlap(2,"CHAT_IN:%s:%s:T:%s"%(room.id,user.saved.nick,message))
699
def chatLeave(self,room):
700
self.sendFlap(2,"CHAT_LEFT:%s"%(room.id))
704
def __init__(self,fac,exchange,name,id):
705
self.exchange=exchange
712
if user in self.users:
714
self.users.append(user)
717
def leave(self,user):
718
if user not in self.users:
720
self.users.remove(user)
723
u.chatUserUpdate(self,user)
724
if len(self.users)==0:
725
self.factory.remChatroom(self)
727
def say(self,user,message):
729
u.chatMessage(self,user,message)
740
class TOCFactory(protocol.Factory):
747
def buildProtocol(self,addr):
752
def getChatroom(self,exchange,name):
753
for i in self.chatroom.values():
754
if normalize(i.name)==normalize(name):
756
self.chatroom[self.chatroomid]=Chatroom(self,exchange,name,self.chatroomid)
757
self.chatroomid=self.chatroomid+1
758
return self.chatroom[self.chatroomid-1]
760
def remChatroom(self,room):
762
del self.chatroom[id]
768
MAXARGS["UPDATE_BUDDY"]=5
771
MAXARGS["CHAT_JOIN"]=1
773
MAXARGS["CHAT_UPDATE_BUDDY"]=-1
774
MAXARGS["CHAT_INVITE"]=3
775
MAXARGS["CHAT_LEFT"]=0
776
MAXARGS["ADMIN_NICK_STATUS"]=0
777
MAXARGS["ADMIN_PASSWD_STATUS"]=0
780
class TOCClient(protocol.Protocol):
781
def __init__(self,username,password,authhost="login.oscar.aol.com",authport=5190):
783
self.username=normalize(username) # our username
784
self._password=password # our password
785
self._mode="SendNick" # current mode
786
self._ourseqnum=19071 # current sequence number (for sendFlap)
787
self._authhost=authhost # authorization host
788
self._authport=authport # authorization port
789
self._online=0 # are we online?
790
self._buddies=[] # the current buddy list
791
self._privacymode=PERMITALL # current privacy mode
792
self._permitlist=[] # list of users on the permit list
793
self._roomnames={} # the names for each of the rooms we're in
794
self._receivedchatmembers={} # have we gotten who's in our room yet?
796
self._cookies={} # for file transfers
797
self._buf='' # current data buffer
800
def _debug(self,data):
803
def sendFlap(self,type,data):
808
s=s+struct.pack("!BHH",type,self._ourseqnum,length)
810
self._ourseqnum=self._ourseqnum+1
811
if self._ourseqnum>(256*256+256):
814
self.transport.write(s)
818
tests to see if a flap is actually on the buffer
820
if self._buf=='': return 0
821
if self._buf[0]!="*": return 0
822
if len(self._buf)<6: return 0
823
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
824
if type not in range(1,6): return 0
825
if len(self._buf)<6+length: return 0
829
if self._buf=='': return None
830
if self._buf[0]!="*":
832
if len(self._buf)<6: return None
833
foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6])
834
if len(self._buf)<6+length: return None
835
data=self._buf[6:6+length]
836
self._buf=self._buf[6+length:]
837
if data and data[-1]=="\000":
841
def connectionMade(self):
842
self._debug("connection made! %s" % self.transport)
843
self.transport.write("FLAPON\r\n\r\n")
845
def connectionLost(self, reason):
846
self._debug("connection lost!")
849
def dataReceived(self,data):
850
self._buf=self._buf+data
853
func=getattr(self,"mode%s"%self._mode)
856
def modeSendNick(self,flap):
857
if flap!=[1,"\000\000\000\001"]: raise TOCParseError
858
s="\000\000\000\001\000\001"+struct.pack("!H",len(self.username))+self.username
860
s="toc_signon %s %s %s %s english \"penguin\""%(self._authhost,\
861
self._authport,self.username,roast(self._password))
865
def modeData(self,flap):
868
if not ':' in flap[1]:
869
self._debug("bad SNAC:%s"%(flap[1]))
871
command,rest=string.split(flap[1],":",1)
872
if MAXARGS.has_key(command):
873
maxsplit=MAXARGS[command]
877
l=tuple(string.split(rest,":"))
881
l=tuple(string.split(rest,":",maxsplit))
882
self._debug("%s %s"%(command,l))
884
func=getattr(self,"toc%s"%command)
885
self._debug("calling %s"%func)
887
self._debug("calling %s"%self.tocUNKNOWN)
888
self.tocUNKNOWN(command,l)
892
def tocUNKNOWN(self,command,data):
895
def tocSIGN_ON(self,data):
896
if data!=("TOC1.0",): raise TOCParseError
897
self._debug("Whee, signed on!")
898
if self._buddies: self.add_buddy(self._buddies)
902
def tocNICK(self,data):
904
Handle a message that looks like::
906
NICK:<format of nickname>
908
self.username=data[0]
910
def tocCONFIG(self,data):
912
Handle a message that looks like::
916
Format of config data:
918
- g: group. all users until next g or end of config are in this group
920
- p: person on the permit list
921
- d: person on the deny list
922
- m: permit/deny mode (1: permit all, 2: deny all, 3: permit some, 4: deny some)
925
if data and data[0]=="{":data=data[1:-1]
926
lines=string.split(data,"\n")
935
if code=='g': # group
937
buddylist[currentgroup]=[]
939
buddylist[currentgroup].append(data)
946
self.gotConfig(mode,buddylist,permit,deny)
948
def tocIM_IN(self,data):
950
Handle a message that looks like::
952
IM_IN:<user>:<autoreply T|F>:message
955
autoreply=(data[1]=='T')
957
self.hearMessage(user,message,autoreply)
959
def tocUPDATE_BUDDY(self,data):
961
Handle a message that looks like::
963
UPDATE_BUDDY:<username>:<online T|F>:<warning level>:<signon time>:<idle time (minutes)>:<user class>
966
online=(data[1]=='T')
969
away=(data[5][-1]=='U')
972
self.updateBuddy(data[0],online,int(data[2]),int(data[3]),int(data[4]),data[5],away)
974
def tocERROR(self,data):
976
Handle a message that looks like::
978
ERROR:<error code>:<misc. data>
980
code,args=data[0],data[1:]
981
self.hearError(int(code),args)
983
def tocEVILED(self,data):
985
Handle a message that looks like::
987
EVILED:<current warning level>:<user who warned us>
989
self.hearWarning(data[0],data[1])
991
def tocCHAT_JOIN(self,data):
993
Handle a message that looks like::
995
CHAT_JOIN:<room id>:<room name>
997
#self.chatJoined(int(data[0]),data[1])
998
self._roomnames[int(data[0])]=data[1]
999
self._receivedchatmembers[int(data[0])]=0
1001
def tocCHAT_UPDATE_BUDDY(self,data):
1003
Handle a message that looks like::
1005
CHAT_UPDATE_BUDDY:<room id>:<in room? T/F>:<user 1>:<user 2>...
1008
inroom=(data[1]=='T')
1009
if self._receivedchatmembers[roomid]:
1011
self.chatUpdate(roomid,u,inroom)
1013
self._receivedchatmembers[roomid]=1
1014
self.chatJoined(roomid,self._roomnames[roomid],list(data[2:]))
1016
def tocCHAT_IN(self,data):
1018
Handle a message that looks like::
1020
CHAT_IN:<room id>:<username>:<whisper T/F>:<message>
1024
whisper=(data[2]=='T')
1026
self.chatHearWhisper(int(data[0]),data[1],data[3])
1028
self.chatHearMessage(int(data[0]),data[1],data[3])
1030
def tocCHAT_INVITE(self,data):
1032
Handle a message that looks like::
1034
CHAT_INVITE:<room name>:<room id>:<username>:<message>
1036
self.chatInvited(int(data[1]),data[0],data[2],data[3])
1038
def tocCHAT_LEFT(self,data):
1040
Handle a message that looks like::
1044
self.chatLeft(int(data[0]))
1045
del self._receivedchatmembers[int(data[0])]
1046
del self._roomnames[int(data[0])]
1048
def tocRVOUS_PROPOSE(self,data):
1050
Handle a message that looks like::
1052
RVOUS_PROPOSE:<user>:<uuid>:<cookie>:<seq>:<rip>:<pip>:<vip>:<port>
1053
[:tlv tag1:tlv value1[:tlv tag2:tlv value2[:...]]]
1055
user,uid,cookie,seq,rip,pip,vip,port=data[:8]
1056
cookie=base64.decodestring(cookie)
1059
for i in range(8,len(data),2):
1061
value=base64.decodestring(data[i+1])
1065
func=getattr(self,"toc%s"%name)
1067
self._debug("no function for UID %s" % uid)
1069
func(user,cookie,seq,pip,vip,port,tlvs)
1071
def tocSEND_FILE(self,user,cookie,seq,pip,vip,port,tlvs):
1072
if tlvs.has_key('12'):
1073
description=tlvs['12']
1076
subtype,numfiles,size=struct.unpack("!HHI",tlvs['10001'][:8])
1077
name=tlvs['10001'][8:-4]
1078
while name[-1]=='\000':
1080
self._cookies[cookie]=[user,SEND_FILE_UID,pip,port,{'name':name}]
1081
self.rvousProposal("send",cookie,user,vip,port,description=description,
1082
name=name,files=numfiles,size=size)
1084
def tocGET_FILE(self,user,cookie,seq,pip,vip,port,tlvs):
1086
# XXX add this back in
1087
#reactor.clientTCP(pip,port,GetFileTransfer(self,cookie,os.path.expanduser("~")))
1088
#self.rvous_accept(user,cookie,GET_FILE_UID)
1092
called when we are first online
1096
def gotConfig(self,mode,buddylist,permit,deny):
1098
called when we get a configuration from the server
1099
mode := permit/deny mode
1100
buddylist := current buddylist
1101
permit := permit list
1106
def hearError(self,code,args):
1108
called when an error is received
1110
args := misc. arguments (username, etc.)
1114
def hearWarning(self,newamount,username):
1116
called when we get warned
1117
newamount := the current warning level
1118
username := the user who warned us, or '' if it's anonymous
1122
def hearMessage(self,username,message,autoreply):
1124
called when you receive an IM
1125
username := the user who the IM is from
1126
message := the message
1127
autoreply := true if the message is an autoreply from an away message
1131
def updateBuddy(self,username,online,evilness,signontime,idletime,userclass,away):
1133
called when a buddy changes state
1134
username := the user whos state changed
1135
online := true if the user is online
1136
evilness := the users current warning level
1137
signontime := the time the user signed on (UNIX epoch)
1138
idletime := the time the user has been idle (minutes)
1139
away := true if the user is away
1140
userclass := the class of the user (generally " O")
1144
def chatJoined(self,roomid,roomname,users):
1146
we just joined a chat room
1147
roomid := the AIM id for the room
1148
roomname := the name for the room
1149
users := a list of the users already in the room
1153
def chatUpdate(self,roomid,username,inroom):
1155
a user has joined the room
1156
roomid := the AIM id for the room
1157
username := the username
1158
inroom := true if the user is in the room
1162
def chatHearMessage(self,roomid,username,message):
1164
a message was sent to the room
1165
roomid := the AIM id for the room
1166
username := the user who sent the message
1167
message := the message
1171
def chatHearWhisper(self,roomid,username,message):
1173
someone whispered to us in a chatroom
1174
roomid := the AIM for the room
1175
username := the user who whispered to us
1176
message := the message
1180
def chatInvited(self,roomid,roomname,username,message):
1182
we were invited to a chat room
1183
roomid := the AIM id for the room
1184
roomname := the name of the room
1185
username := the user who invited us
1186
message := the invite message
1190
def chatLeft(self,roomid):
1193
roomid := the AIM id for the room
1197
def rvousProposal(self,type,cookie,user,vip,port,**kw):
1199
we were asked for a rondevouz
1200
type := the type of rondevous. currently, one of ["send"]
1201
cookie := the cookie. pass this to rvous_accept()
1202
user := the user who asked us
1203
vip := their verified_ip
1204
port := the port they want us to conenct to
1207
pass #self.rvous_accept(cookie)
1209
def receiveBytes(self,user,file,chunk,sofar,total):
1211
we received part of a file from a file transfer
1212
file := the name of the file
1213
chunk := the chunk of data
1214
sofar := how much data we've gotten so far
1215
total := the total amount of data
1217
pass #print user,file,sofar,total
1221
return our away status
1223
return len(self._awaymessage)>0
1225
def set_config(self,mode,buddylist,permit,deny):
1227
set the server configuration
1229
buddylist := buddy list
1230
permit := permit list
1234
for g in buddylist.keys():
1236
for u in buddylist[g]:
1243
self.sendFlap(2,"toc_set_config %s"%quote(s))
1245
def add_buddy(self,buddies):
1247
if type(buddies)==type(""): buddies=[buddies]
1249
s=s+" "+normalize(b)
1250
self.sendFlap(2,"toc_add_buddy%s"%s)
1252
def del_buddy(self,buddies):
1254
if type(buddies)==type(""): buddies=[buddies]
1257
self.sendFlap(2,"toc_remove_buddy%s"%s)
1259
def add_permit(self,users):
1260
if type(users)==type(""): users=[users]
1262
if self._privacymode!=PERMITSOME:
1263
self._privacymode=PERMITSOME
1267
if u not in self._permitlist:self._permitlist.append(u)
1270
self._privacymode=DENYALL
1273
self.sendFlap(2,"toc_add_permit"+s)
1275
def del_permit(self,users):
1276
if type(users)==type(""): users=[users]
1277
p=self._permitlist[:]
1285
def add_deny(self,users):
1286
if type(users)==type(""): users=[users]
1288
if self._privacymode!=DENYSOME:
1289
self._privacymode=DENYSOME
1293
if u not in self._denylist:self._denylist.append(u)
1296
self._privacymode=PERMITALL
1299
self.sendFlap(2,"toc_add_deny"+s)
1301
def del_deny(self,users):
1302
if type(users)==type(""): users=[users]
1314
called to finish the setup, and signon to the network
1316
self.sendFlap(2,"toc_init_done")
1317
self.sendFlap(2,"toc_set_caps %s" % (SEND_FILE_UID,)) # GET_FILE_UID)
1319
def say(self,user,message,autoreply=0):
1322
user := the user to send to
1323
message := the message
1324
autoreply := true if the message is an autoreply (good for away messages)
1326
if autoreply: a=" auto"
1328
self.sendFlap(2,"toc_send_im %s %s%s"%(normalize(user),quote(message),a))
1330
def idle(self,idletime=0):
1333
idletime := the seconds that the user has been away, or 0 if they're back
1335
self.sendFlap(2,"toc_set_idle %s" % int(idletime))
1337
def evil(self,user,anon=0):
1340
user := the user to warn
1341
anon := if true, an anonymous warning
1343
self.sendFlap(2,"toc_evil %s %s"%(normalize(user), (not anon and "anon") or "norm"))
1345
def away(self,message=''):
1348
message := the message, or '' to come back from awayness
1350
self._awaymessage=message
1352
message=' '+quote(message)
1353
self.sendFlap(2,"toc_set_away%s"%message)
1355
def chat_join(self,exchange,roomname):
1358
exchange := should almost always be 4
1359
roomname := room name
1361
roomname=string.replace(roomname," ","")
1362
self.sendFlap(2,"toc_chat_join %s %s"%(int(exchange),roomname))
1364
def chat_say(self,roomid,message):
1366
send a message to a chatroom
1367
roomid := the AIM id for the room
1368
message := the message to send
1370
self.sendFlap(2,"toc_chat_send %s %s"%(int(roomid),quote(message)))
1372
def chat_whisper(self,roomid,user,message):
1374
whisper to another user in a chatroom
1375
roomid := the AIM id for the room
1376
user := the user to whisper to
1377
message := the message to send
1379
self.sendFlap(2,"toc_chat_whisper %s %s %s"%(int(roomid),normalize(user),quote(message)))
1381
def chat_leave(self,roomid):
1384
roomid := the AIM id for the room
1386
self.sendFlap(2,"toc_chat_leave %s" % int(roomid))
1388
def chat_invite(self,roomid,usernames,message):
1390
invite a user[s] to the chat room
1391
roomid := the AIM id for the room
1392
usernames := either a string (one username) or a list (more than one)
1393
message := the message to invite them with
1395
if type(usernames)==type(""): # a string, one username
1402
self.sendFlap(2,"toc_chat_invite %s %s %s" % (int(roomid),quote(message),users))
1404
def chat_accept(self,roomid):
1406
accept an invite to a chat room
1407
roomid := the AIM id for the room
1409
self.sendFlap(2,"toc_chat_accept %s"%int(roomid))
1411
def rvous_accept(self,cookie):
1412
user,uuid,pip,port,d=self._cookies[cookie]
1413
self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user),
1415
if uuid==SEND_FILE_UID:
1416
protocol.ClientCreator(reactor, SendFileTransfer,self,cookie,user,d["name"]).connectTCP(pip,port)
1418
def rvous_cancel(self,cookie):
1419
user,uuid,pip,port,d=self._cookies[cookie]
1420
self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user),
1422
del self._cookies[cookie]
1425
class SendFileTransfer(protocol.Protocol):
1426
header_fmt="!4s2H8s6H10I32s3c69s16s2H64s"
1428
def __init__(self,client,cookie,user,filename):
1432
self.filename=filename
1436
def dataReceived(self,data):
1437
if not self.hdr[2]==0x202:
1438
self.hdr=list(struct.unpack(self.header_fmt,data[:256]))
1440
self.hdr[3]=self.cookie
1443
self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr))
1446
self.name=self.filename
1448
self.name=self.filename+self.hdr[-1]
1449
while self.name[-1]=="\000":
1450
self.name=self.name[:-1]
1452
self.sofar=self.sofar+len(data)
1453
self.client.receiveBytes(self.user,self.name,data,self.sofar,self.hdr[11])
1454
if self.sofar==self.hdr[11]: # end of this file
1456
self.hdr[7]=self.hdr[7]-1
1457
self.hdr[9]=self.hdr[9]-1
1458
self.hdr[19]=DUMMY_CHECKSUM # XXX really calculate this
1459
self.hdr[18]=self.hdr[18]+1
1461
self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr))
1464
self.transport.loseConnection()
1467
class GetFileTransfer(protocol.Protocol):
1468
header_fmt="!4s 2H 8s 6H 10I 32s 3c 69s 16s 2H 64s"
1469
def __init__(self,client,cookie,dir):
1475
def connectionMade(self):
1476
def func(f,path,names):
1477
names.sort(lambda x,y:cmp(string.lower(x),string.lower(y)))
1479
name=os.path.join(path,n)
1480
lt=time.localtime(os.path.getmtime(name))
1481
size=os.path.getsize(name)
1483
f.append("%02d/%02d/%4d %02d:%02d %8d %s" %
1484
(lt[1],lt[2],lt[0],lt[3],lt[4],size,name[f[0]:]))
1485
f=[len(self.dir)+1,0]
1486
os.path.walk(self.dir,func,f)
1488
self.listing=string.join(f[2:],"\r\n")+"\r\n"
1489
open("\\listing.txt","w").write(self.listing)
1490
hdr=["OFT2",256,0x1108,self.cookie,0,0,len(f)-2,len(f)-2,1,1,size,
1491
len(self.listing),os.path.getmtime(self.dir),
1492
checksum(self.listing),0,0,0,0,0,0,"OFT_Windows ICBMFT V1.1 32",
1493
"\002",chr(0x1a),chr(0x10),"","",0,0,""]
1494
self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
1496
def dataReceived(self,data):
1497
self.buf=self.buf+data
1498
while len(self.buf)>=256:
1499
hdr=list(struct.unpack(self.header_fmt,self.buf[:256]))
1500
self.buf=self.buf[256:]
1502
self.file=StringIO.StringIO(self.listing)
1503
self.transport.registerProducer(self,0)
1504
elif hdr[2]==0x120b: pass
1505
elif hdr[2]==0x120c: # file request
1507
for k,v in [["\000",""],["\001",os.sep]]:
1508
file=string.replace(file,k,v)
1509
self.name=os.path.join(self.dir,file)
1510
self.file=open(self.name,'rb')
1513
hdr[10]=hdr[11]=os.path.getsize(self.name)
1514
hdr[12]=os.path.getmtime(self.name)
1515
hdr[13]=checksum_file(self.file)
1519
self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
1520
log.msg("got file request for %s"%file,hex(hdr[13]))
1521
elif hdr[2]==0x0202:
1522
log.msg("sending file")
1523
self.transport.registerProducer(self,0)
1524
elif hdr[2]==0x0204:
1525
log.msg("real checksum: %s"%hex(hdr[19]))
1527
elif hdr[2]==0x0205: # resume
1530
data=self.file.read(already)
1533
log.msg("restarting at %s"%already)
1535
hdr[19]=checksum(data)
1536
self.transport.write(apply(struct.pack,[self.header_fmt]+hdr))
1537
elif hdr[2]==0x0207:
1538
self.transport.registerProducer(self,0)
1540
log.msg("don't understand 0x%04x"%hdr[2])
1543
def resumeProducing(self):
1544
data=self.file.read(4096)
1547
self.transport.unregisterProducer()
1548
self.transport.write(data)
1550
def pauseProducing(self): pass
1552
def stopProducing(self): del self.file
1555
SEND_FILE_UID = "09461343-4C7F-11D1-8222-444553540000"
1556
GET_FILE_UID = "09461348-4C7F-11D1-8222-444553540000"
1558
SEND_FILE_UID:"SEND_FILE",
1559
GET_FILE_UID:"GET_FILE"
1566
MESSAGES_TOO_FAST=903
1571
SERVICE_UNAVAILABLE=914
1580
TOO_MANY_MATCHES=971
1581
NEED_MORE_QUALIFIERS=972
1588
DIR_FAIL_UNKNOWN=979
1591
SERVICE_TEMP_UNAVAILABLE=981
1592
WARNING_TOO_HIGH=982
1593
CONNECTING_TOO_QUICK=983
1597
STD_MESSAGE[NOT_AVAILABLE]="%s not currently available"
1598
STD_MESSAGE[CANT_WARN]="Warning of %s not currently available"
1599
STD_MESSAGE[MESSAGES_TOO_FAST]="A message has been dropped, you are exceeding the server speed limit"
1600
STD_MESSAGE[BAD_INPUT]="Error validating input"
1601
STD_MESSAGE[BAD_ACCOUNT]="Invalid account"
1602
STD_MESSAGE[REQUEST_ERROR]="Error encountered while processing request"
1603
STD_MESSAGE[SERVICE_UNAVAILABLE]="Service unavailable"
1604
STD_MESSAGE[NO_CHAT_IN]="Chat in %s is unavailable"
1605
STD_MESSAGE[SEND_TOO_FAST]="You are sending messages too fast to %s"
1606
STD_MESSAGE[MISSED_BIG_IM]="You missed an IM from %s because it was too big"
1607
STD_MESSAGE[MISSED_FAST_IM]="You missed an IM from %s because it was sent too fast"
1608
# skipping directory for now
1609
STD_MESSAGE[BAD_NICKNAME]="Incorrect nickname or password"
1610
STD_MESSAGE[SERVICE_TEMP_UNAVAILABLE]="The service is temporarily unavailable"
1611
STD_MESSAGE[WARNING_TOO_HIGH]="Your warning level is currently too high to sign on"
1612
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."
1613
STD_MESSAGE[UNKNOWN_SIGNON]="An unknown signon error has occurred %s"