OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.words.test -*- | |
2 # Copyright (c) 2001-2005 Twisted Matrix Laboratories. | |
3 # See LICENSE for details. | |
4 | |
5 | |
6 """ | |
7 Implements a AOL Instant Messenger TOC server and client, using the Twisted | |
8 framework. | |
9 | |
10 TODO: | |
11 info,dir: see how gaim connects for this...it may never work if it tries to | |
12 connect to the aim server automatically | |
13 | |
14 This module is deprecated. | |
15 | |
16 Maintainer: U{Paul Swartz<mailto:z3p@twistedmatrix.com>} | |
17 """ | |
18 | |
19 # twisted imports | |
20 from twisted.internet import reactor, protocol | |
21 from twisted.python import log | |
22 | |
23 # base imports | |
24 import struct | |
25 import string | |
26 import time | |
27 import base64 | |
28 import os | |
29 import StringIO | |
30 | |
31 SIGNON,DATA,ERROR,SIGNOFF,KEEP_ALIVE=range(1,6) | |
32 PERMITALL,DENYALL,PERMITSOME,DENYSOME=range(1,5) | |
33 | |
34 DUMMY_CHECKSUM = -559038737 # 0xdeadbeef | |
35 | |
36 def quote(s): | |
37 rep=['\\','$','{','}','[',']','(',')','"'] | |
38 for r in rep: | |
39 s=string.replace(s,r,"\\"+r) | |
40 return "\""+s+"\"" | |
41 | |
42 def unquote(s): | |
43 if s=="": return "" | |
44 if s[0]!='"': return s | |
45 r=string.replace | |
46 s=s[1:-1] | |
47 s=r(s,"\\\\","\\") | |
48 s=r(s,"\\$","$") | |
49 s=r(s,"\\{","{") | |
50 s=r(s,"\\}","}") | |
51 s=r(s,"\\[","[") | |
52 s=r(s,"\\]","]") | |
53 s=r(s,"\\(","(") | |
54 s=r(s,"\\)",")") | |
55 s=r(s,"\\\"","\"") | |
56 return s | |
57 | |
58 def unquotebeg(s): | |
59 for i in range(1,len(s)): | |
60 if s[i]=='"' and s[i-1]!='\\': | |
61 q=unquote(s[:i+1]) | |
62 return [q,s[i+2:]] | |
63 | |
64 def unroast(pw): | |
65 roaststring="Tic/Toc" | |
66 pw=string.lower(pw[2:]) | |
67 r="" | |
68 count=0 | |
69 hex=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"] | |
70 while pw: | |
71 st,pw=pw[:2],pw[2:] | |
72 value=(16*hex.index(st[0]))+hex.index(st[1]) | |
73 xor=ord(roaststring[count]) | |
74 count=(count+1)%len(roaststring) | |
75 r=r+chr(value^xor) | |
76 return r | |
77 | |
78 def roast(pw): | |
79 # contributed by jemfinch on #python | |
80 key="Tic/Toc" | |
81 ro="0x" | |
82 i=0 | |
83 ascii=map(ord,pw) | |
84 for c in ascii: | |
85 ro=ro+'%02x'%(c^ord(key[i%len(key)])) | |
86 i=i+1 | |
87 return string.lower(ro) | |
88 | |
89 def checksum(b): | |
90 return DUMMY_CHECKSUM # do it like gaim does, since the checksum | |
91 # formula doesn't work | |
92 ## # used in file transfers | |
93 ## check0 = check1 = 0x00ff | |
94 ## for i in range(len(b)): | |
95 ## if i%2: | |
96 ## if ord(b[i])>check1: | |
97 ## check1=check1+0x100 # wrap | |
98 ## if check0==0: | |
99 ## check0=0x00ff | |
100 ## if check1==0x100: | |
101 ## check1=check1-1 | |
102 ## else: | |
103 ## check0=check0-1 | |
104 ## check1=check1-ord(b[i]) | |
105 ## else: | |
106 ## if ord(b[i])>check0: # wrap | |
107 ## check0=check0+0x100 | |
108 ## if check1==0: | |
109 ## check1=0x00ff | |
110 ## if check0==0x100: | |
111 ## check0=check0-1 | |
112 ## else: | |
113 ## check1=check1-1 | |
114 ## check0=check0-ord(b[i]) | |
115 ## check0=check0 & 0xff | |
116 ## check1=check1 & 0xff | |
117 ## checksum=(long(check0)*0x1000000)+(long(check1)*0x10000) | |
118 ## return checksum | |
119 | |
120 def checksum_file(f): | |
121 return DUMMY_CHECKSUM # do it like gaim does, since the checksum | |
122 # formula doesn't work | |
123 ## check0=check1=0x00ff | |
124 ## i=0 | |
125 ## while 1: | |
126 ## b=f.read() | |
127 ## if not b: break | |
128 ## for char in b: | |
129 ## i=not i | |
130 ## if i: | |
131 ## if ord(char)>check1: | |
132 ## check1=check1+0x100 # wrap | |
133 ## if check0==0: | |
134 ## check0=0x00ff | |
135 ## if check1==0x100: | |
136 ## check1=check1-1 | |
137 ## else: | |
138 ## check0=check0-1 | |
139 ## check1=check1-ord(char) | |
140 ## else: | |
141 ## if ord(char)>check0: # wrap | |
142 ## check0=check0+0x100 | |
143 ## if check1==0: | |
144 ## check1=0x00ff | |
145 ## if check0==0x100: | |
146 ## check0=check0-1 | |
147 ## else: | |
148 ## check1=check1-1 | |
149 ## check0=check0-ord(char) | |
150 ## check0=check0 & 0xff | |
151 ## check1=check1 & 0xff | |
152 ## checksum=(long(check0)*0x1000000)+(long(check1)*0x10000) | |
153 ## return checksum | |
154 | |
155 def normalize(s): | |
156 s=string.lower(s) | |
157 s=string.replace(s," ","") | |
158 return s | |
159 | |
160 | |
161 class TOCParseError(ValueError): | |
162 pass | |
163 | |
164 | |
165 class TOC(protocol.Protocol): | |
166 users={} | |
167 | |
168 def connectionMade(self): | |
169 # initialization of protocol | |
170 self._buf="" | |
171 self._ourseqnum=0L | |
172 self._theirseqnum=0L | |
173 self._mode="Flapon" | |
174 self._onlyflaps=0 | |
175 self._laststatus={} # the last status for a user | |
176 self.username=None | |
177 self.permitmode=PERMITALL | |
178 self.permitlist=[] | |
179 self.denylist=[] | |
180 self.buddylist=[] | |
181 self.signontime=0 | |
182 self.idletime=0 | |
183 self.userinfo="<br>" | |
184 self.userclass=" O" | |
185 self.away="" | |
186 self.saved=None | |
187 | |
188 def _debug(self,data): | |
189 log.msg(data) | |
190 | |
191 def connectionLost(self, reason): | |
192 self._debug("dropped connection from %s" % self.username) | |
193 try: | |
194 del self.factory.users[self.username] | |
195 except: | |
196 pass | |
197 for k in self.factory.chatroom.keys(): | |
198 try: | |
199 self.factory.chatroom[k].leave(self) | |
200 except TOCParseError: | |
201 pass | |
202 if self.saved: | |
203 self.factory.savedusers[self.username]=self.saved | |
204 self.updateUsers() | |
205 | |
206 def sendFlap(self,type,data): | |
207 """ | |
208 send a FLAP to the client | |
209 """ | |
210 send="*" | |
211 self._debug(data) | |
212 if type==DATA: | |
213 data=data+"\000" | |
214 length=len(data) | |
215 send=send+struct.pack("!BHH",type,self._ourseqnum,length) | |
216 send=send+data | |
217 self._ourseqnum=self._ourseqnum+1 | |
218 if self._ourseqnum>(256L**4): | |
219 self._ourseqnum=0 | |
220 self.transport.write(send) | |
221 | |
222 def dataReceived(self,data): | |
223 self._buf=self._buf+data | |
224 try: | |
225 func=getattr(self,"mode%s"%self._mode) | |
226 except: | |
227 return | |
228 self._mode=func() | |
229 if self._onlyflaps and self.isFlap(): self.dataReceived("") | |
230 | |
231 def isFlap(self): | |
232 """ | |
233 tests to see if a flap is actually on the buffer | |
234 """ | |
235 if self._buf=='': return 0 | |
236 if self._buf[0]!="*": return 0 | |
237 if len(self._buf)<6: return 0 | |
238 foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6]) | |
239 if type not in range(1,6): return 0 | |
240 if len(self._buf)<6+length: return 0 | |
241 return 1 | |
242 | |
243 def readFlap(self): | |
244 """ | |
245 read the first FLAP off self._buf, raising errors if it isn't in the rig
ht form. | |
246 the FLAP is the basic TOC message format, and is logically equivilant to
a packet in TCP | |
247 """ | |
248 if self._buf=='': return None | |
249 if self._buf[0]!="*": | |
250 raise TOCParseError | |
251 if len(self._buf)<6: return None | |
252 foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6]) | |
253 if len(self._buf)<6+length: return None | |
254 data=self._buf[6:6+length] | |
255 self._buf=self._buf[6+length:] | |
256 if data and data[-1]=="\000": | |
257 data=data[:-1] | |
258 self._debug([type,data]) | |
259 return [type,data] | |
260 | |
261 #def modeWeb(self): | |
262 # try: | |
263 # line,rest=string.split(self._buf,"\n",1) | |
264 # get,username,http=string.split(line," ",2) | |
265 # except: | |
266 # return "Web" # not enough data | |
267 # foo,type,username=string.split(username,"/") | |
268 # if type=="info": | |
269 # user=self.factory.users[username] | |
270 # text="<HTML><HEAD><TITLE>User Information for %s</TITLE></HEAD><BOD
Y>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) | |
271 # self.transport.write("HTTP/1.1 200 OK\n") | |
272 # self.transport.write("Content-Type: text/html\n") | |
273 # self.transport.write("Content-Length: %s\n\n"%len(text)) | |
274 # self.transport.write(text) | |
275 # self.loseConnection() | |
276 | |
277 def modeFlapon(self): | |
278 #if self._buf[:3]=="GET": self.modeWeb() # TODO: get this working | |
279 if len(self._buf)<10: return "Flapon" # not enough bytes | |
280 flapon,self._buf=self._buf[:10],self._buf[10:] | |
281 if flapon!="FLAPON\r\n\r\n": | |
282 raise TOCParseError | |
283 self.sendFlap(SIGNON,"\000\000\000\001") | |
284 self._onlyflaps=1 | |
285 return "Signon" | |
286 | |
287 def modeSignon(self): | |
288 flap=self.readFlap() | |
289 if flap==None: | |
290 return "Signon" | |
291 if flap[0]!=SIGNON: raise TOCParseError | |
292 version,tlv,unlength=struct.unpack("!LHH",flap[1][:8]) | |
293 if version!=1 or tlv!=1 or unlength+8!=len(flap[1]): | |
294 raise TOCParseError | |
295 self.username=normalize(flap[1][8:]) | |
296 if self.username in self.factory.savedusers.keys(): | |
297 self.saved=self.factory.savedusers[self.username] | |
298 else: | |
299 self.saved=SavedUser() | |
300 self.saved.nick=self.username | |
301 return "TocSignon" | |
302 | |
303 def modeTocSignon(self): | |
304 flap=self.readFlap() | |
305 if flap==None: | |
306 return "TocSignon" | |
307 if flap[0]!=DATA: raise TOCParseError | |
308 data=string.split(flap[1]," ") | |
309 if data[0]!="toc_signon": raise TOCParseError | |
310 for i in data: | |
311 if not i:data.remove(i) | |
312 password=unroast(data[4]) | |
313 if not(self.authorize(data[1],int(data[2]),data[3],password)): | |
314 self.sendError(BAD_NICKNAME) | |
315 self.transport.loseConnection() | |
316 return | |
317 self.sendFlap(DATA,"SIGN_ON:TOC1.0") | |
318 self.sendFlap(DATA,"NICK:%s"%self.saved.nick) | |
319 self.sendFlap(DATA,"CONFIG:%s"%self.saved.config) | |
320 # sending user configuration goes here | |
321 return "Connected" | |
322 | |
323 def authorize(self,server,port,username,password): | |
324 if self.saved.password=="": | |
325 self.saved.password=password | |
326 return 1 | |
327 else: | |
328 return self.saved.password==password | |
329 | |
330 def modeConnected(self): | |
331 flap=self.readFlap() | |
332 while flap!=None: | |
333 if flap[0] not in [DATA,KEEP_ALIVE]: raise TOCParseError | |
334 flapdata=string.split(flap[1]," ",1) | |
335 tocname=flapdata[0][4:] | |
336 if len(flapdata)==2: | |
337 data=flapdata[1] | |
338 else: | |
339 data="" | |
340 func=getattr(self,"toc_"+tocname,None) | |
341 if func!=None: | |
342 func(data) | |
343 else: | |
344 self.toc_unknown(tocname,data) | |
345 flap=self.readFlap() | |
346 return "Connected" | |
347 | |
348 def toc_unknown(self,tocname,data): | |
349 self._debug("unknown! %s %s" % (tocname,data)) | |
350 | |
351 def toc_init_done(self,data): | |
352 """ | |
353 called when all the setup is done. | |
354 | |
355 toc_init_done | |
356 """ | |
357 self.signontime=int(time.time()) | |
358 self.factory.users[self.username]=self | |
359 self.updateUsers() | |
360 | |
361 def toc_add_permit(self,data): | |
362 """ | |
363 adds users to the permit list. if the list is null, then set the mode t
o DENYALL | |
364 """ | |
365 if data=="": | |
366 self.permitmode=DENYALL | |
367 self.permitlist=[] | |
368 self.denylist=[] | |
369 else: | |
370 self.permitmode=PERMITSOME | |
371 self.denylist=[] | |
372 users=string.split(data," ") | |
373 map(self.permitlist.append,users) | |
374 self.updateUsers() | |
375 | |
376 def toc_add_deny(self,data): | |
377 """ | |
378 adds users to the deny list. if the list is null, then set the mode to
PERMITALL | |
379 """ | |
380 if data=="": | |
381 self.permitmode=PERMITALL | |
382 self.permitlist=[] | |
383 self.denylist=[] | |
384 else: | |
385 self.permitmode=DENYSOME | |
386 self.permitlist=[] | |
387 users=string.split(data," ") | |
388 map(self.denylist.append,users) | |
389 self.updateUsers() | |
390 | |
391 def toc_evil(self,data): | |
392 """ | |
393 warns a user. | |
394 | |
395 toc_evil <username> <anon|norm> | |
396 """ | |
397 username,nora=string.split(data," ") | |
398 if nora=="anon": | |
399 user="" | |
400 else: | |
401 user=self.saved.nick | |
402 if not(self.factory.users.has_key(username)): | |
403 self.sendError(CANT_WARN,username) | |
404 return | |
405 if self.factory.users[username].saved.evilness>=100: | |
406 self.sendError(CANT_WARN,username) | |
407 return | |
408 self.factory.users[username].evilFrom(user) | |
409 | |
410 def toc_add_buddy(self,data): | |
411 """ | |
412 adds users to the buddy list | |
413 | |
414 toc_add_buddy <buddyname1> [<buddyname2>] [<buddyname3>]... | |
415 """ | |
416 buddies=map(normalize,string.split(data," ")) | |
417 for b in buddies: | |
418 if b not in self.buddylist: | |
419 self.buddylist.append(b) | |
420 for buddy in buddies: | |
421 try: | |
422 buddy=self.factory.users[buddy] | |
423 except: | |
424 pass | |
425 else: | |
426 self.buddyUpdate(buddy) | |
427 | |
428 def toc_remove_buddy(self,data): | |
429 """ | |
430 removes users from the buddy list | |
431 | |
432 toc_remove_buddy <buddyname1> [<buddyname2>] [<buddyname3>]... | |
433 """ | |
434 buddies=string.split(data," ") | |
435 for buddy in buddies: | |
436 try: | |
437 self.buddylist.remove(normalize(buddy)) | |
438 except: pass | |
439 | |
440 def toc_send_im(self,data): | |
441 """ | |
442 incoming instant message | |
443 | |
444 toc_send_im <screenname> <quoted message> [auto] | |
445 """ | |
446 username,data=string.split(data," ",1) | |
447 auto=0 | |
448 if data[-4:]=="auto": | |
449 auto=1 | |
450 data=data[:-5] | |
451 data=unquote(data) | |
452 if not(self.factory.users.has_key(username)): | |
453 self.sendError(NOT_AVAILABLE,username) | |
454 return | |
455 user=self.factory.users[username] | |
456 if not(self.canContact(user)): | |
457 self.sendError(NOT_AVAILABLE,username) | |
458 return | |
459 user.hearWhisper(self,data,auto) | |
460 | |
461 def toc_set_info(self,data): | |
462 """ | |
463 set the users information, retrivable with toc_get_info | |
464 | |
465 toc_set_info <user info (quoted)> | |
466 """ | |
467 info=unquote(data) | |
468 self._userinfo=info | |
469 | |
470 def toc_set_idle(self,data): | |
471 """ | |
472 set/unset idle | |
473 | |
474 toc_set_idle <seconds> | |
475 """ | |
476 seconds=int(data) | |
477 self.idletime=time.time()-seconds # time when they started being idle | |
478 self.updateUsers() | |
479 | |
480 def toc_set_away(self,data): | |
481 """ | |
482 set/unset away message | |
483 | |
484 toc_set_away [<away message>] | |
485 """ | |
486 away=unquote(data) | |
487 if not self.away and away: # setting an away message | |
488 self.away=away | |
489 self.userclass=self.userclass+'U' | |
490 self.updateUsers() | |
491 elif self.away and not away: # coming back | |
492 self.away="" | |
493 self.userclass=self.userclass[:2] | |
494 self.updateUsers() | |
495 else: | |
496 raise TOCParseError | |
497 | |
498 def toc_chat_join(self,data): | |
499 """ | |
500 joins the chat room. | |
501 | |
502 toc_chat_join <exchange> <room name> | |
503 """ | |
504 exchange,name=string.split(data," ",1) | |
505 self.factory.getChatroom(int(exchange),unquote(name)).join(self) | |
506 | |
507 def toc_chat_invite(self,data): | |
508 """ | |
509 invite others to the room. | |
510 | |
511 toc_chat_invite <room id> <invite message> <buddy 1> [<buddy2>]... | |
512 """ | |
513 id,data=string.split(data," ",1) | |
514 id=int(id) | |
515 message,data=unquotebeg(data) | |
516 buddies=string.split(data," ") | |
517 for b in buddies: | |
518 room=self.factory.chatroom[id] | |
519 bud=self.factory.users[b] | |
520 bud.chatInvite(room,self,message) | |
521 | |
522 def toc_chat_accept(self,data): | |
523 """ | |
524 accept an invitation. | |
525 | |
526 toc_chat_accept <room id> | |
527 """ | |
528 id=int(data) | |
529 self.factory.chatroom[id].join(self) | |
530 | |
531 def toc_chat_send(self,data): | |
532 """ | |
533 send a message to the chat room. | |
534 | |
535 toc_chat_send <room id> <message> | |
536 """ | |
537 id,message=string.split(data," ",1) | |
538 id=int(id) | |
539 message=unquote(message) | |
540 self.factory.chatroom[id].say(self,message) | |
541 | |
542 def toc_chat_whisper(self,data): | |
543 id,user,message=string.split(data," ",2) | |
544 id=int(id) | |
545 room=self.factory.chatroom[id] | |
546 message=unquote(message) | |
547 self.factory.users[user].chatWhisper(room,self,message) | |
548 | |
549 def toc_chat_leave(self,data): | |
550 """ | |
551 leave the room. | |
552 | |
553 toc_chat_leave <room id> | |
554 """ | |
555 id=int(data) | |
556 self.factory.chatroom[id].leave(self) | |
557 | |
558 def toc_set_config(self,data): | |
559 """ | |
560 set the saved config. this gets send when you log in. | |
561 | |
562 toc_set_config <config> | |
563 """ | |
564 self.saved.config=unquote(data) | |
565 | |
566 def toc_get_info(self,data): | |
567 """ | |
568 get the user info for a user | |
569 | |
570 toc_get_info <username> | |
571 """ | |
572 if not self.factory.users.has_key(data): | |
573 self.sendError(901,data) | |
574 return | |
575 self.sendFlap(2,"GOTO_URL:TIC:info/%s"%data) | |
576 | |
577 def toc_format_nickname(self,data): | |
578 """ | |
579 change the format of your nickname. | |
580 | |
581 toc_format_nickname <new format> | |
582 """ | |
583 # XXX may not work | |
584 nick=unquote(data) | |
585 if normalize(nick)==self.username: | |
586 self.saved.nick=nick | |
587 self.sendFlap(2,"ADMIN_NICK_STATUS:0") | |
588 else: | |
589 self.sendError(BAD_INPUT) | |
590 | |
591 def toc_change_passwd(self,data): | |
592 orig,data=unquotebeg(data) | |
593 new=unquote(data) | |
594 if orig==self.saved.password: | |
595 self.saved.password=new | |
596 self.sendFlap(2,"ADMIN_PASSWD_STATUS:0") | |
597 else: | |
598 self.sendError(BAD_INPUT) | |
599 | |
600 def sendError(self,code,*varargs): | |
601 """ | |
602 send an error to the user. listing of error messages is below. | |
603 """ | |
604 send="ERROR:%s"%code | |
605 for v in varargs: | |
606 send=send+":"+v | |
607 self.sendFlap(DATA,send) | |
608 | |
609 def updateUsers(self): | |
610 """ | |
611 Update the users who have us on their buddylist. | |
612 Called when the user changes anything (idle,away) so people can get upda
tes. | |
613 """ | |
614 for user in self.factory.users.values(): | |
615 if self.username in user.buddylist and self.canContact(user): | |
616 user.buddyUpdate(self) | |
617 | |
618 def getStatus(self,user): | |
619 if self.canContact(user): | |
620 if self in self.factory.users.values():ol='T' | |
621 else: ol='F' | |
622 idle=0 | |
623 if self.idletime: | |
624 idle=int((time.time()-self.idletime)/60) | |
625 return (self.saved.nick,ol,self.saved.evilness,self.signontime,idle,
self.userclass) | |
626 else: | |
627 return (self.saved.nick,'F',0,0,0,self.userclass) | |
628 | |
629 def canContact(self,user): | |
630 if self.permitmode==PERMITALL: return 1 | |
631 elif self.permitmode==DENYALL: return 0 | |
632 elif self.permitmode==PERMITSOME: | |
633 if user.username in self.permitlist: return 1 | |
634 else: return 0 | |
635 elif self.permitmode==DENYSOME: | |
636 if user.username in self.denylist: return 0 | |
637 else: return 1 | |
638 else: | |
639 assert 0,"bad permitmode %s" % self.permitmode | |
640 | |
641 def buddyUpdate(self,user): | |
642 """ | |
643 Update the buddy. Called from updateUsers() | |
644 """ | |
645 if not self.canContact(user): return | |
646 status=user.getStatus(self) | |
647 if not self._laststatus.has_key(user): | |
648 self._laststatus[user]=() | |
649 if self._laststatus[user]!=status: | |
650 send="UPDATE_BUDDY:%s:%s:%s:%s:%s:%s"%status | |
651 self.sendFlap(DATA,send) | |
652 self._laststatus[user]=status | |
653 | |
654 def hearWhisper(self,user,data,auto=0): | |
655 """ | |
656 Called when you get an IM. If auto=1, it's an autoreply from an away me
ssage. | |
657 """ | |
658 if not self.canContact(user): return | |
659 if auto: auto='T' | |
660 else: auto='F' | |
661 send="IM_IN:%s:%s:%s"%(user.saved.nick,auto,data) | |
662 self.sendFlap(DATA,send) | |
663 | |
664 def evilFrom(self,user): | |
665 if user=="": | |
666 percent=0.03 | |
667 else: | |
668 percent=0.1 | |
669 self.saved.evilness=self.saved.evilness+int((100-self.saved.evilness)*pe
rcent) | |
670 self.sendFlap(2,"EVILED:%s:%s"%(self.saved.evilness,user)) | |
671 self.updateUsers() | |
672 | |
673 def chatJoin(self,room): | |
674 self.sendFlap(2,"CHAT_JOIN:%s:%s"%(room.id,room.name)) | |
675 f="CHAT_UPDATE_BUDDY:%s:T"%room.id | |
676 for u in room.users: | |
677 if u!=self: | |
678 u.chatUserUpdate(room,self) | |
679 f=f+":"+u.saved.nick | |
680 self.sendFlap(2,f) | |
681 | |
682 def chatInvite(self,room,user,message): | |
683 if not self.canContact(user): return | |
684 self.sendFlap(2,"CHAT_INVITE:%s:%s:%s:%s"%(room.name,room.id,user.saved.
nick,message)) | |
685 | |
686 def chatUserUpdate(self,room,user): | |
687 if user in room.users: | |
688 inroom='T' | |
689 else: | |
690 inroom='F' | |
691 self.sendFlap(2,"CHAT_UPDATE_BUDDY:%s:%s:%s"%(room.id,inroom,user.saved.
nick)) | |
692 | |
693 def chatMessage(self,room,user,message): | |
694 if not self.canContact(user): return | |
695 self.sendFlap(2,"CHAT_IN:%s:%s:F:%s"%(room.id,user.saved.nick,message)) | |
696 | |
697 def chatWhisper(self,room,user,message): | |
698 if not self.canContact(user): return | |
699 self.sendFlap(2,"CHAT_IN:%s:%s:T:%s"%(room.id,user.saved.nick,message)) | |
700 | |
701 def chatLeave(self,room): | |
702 self.sendFlap(2,"CHAT_LEFT:%s"%(room.id)) | |
703 | |
704 | |
705 class Chatroom: | |
706 def __init__(self,fac,exchange,name,id): | |
707 self.exchange=exchange | |
708 self.name=name | |
709 self.id=id | |
710 self.factory=fac | |
711 self.users=[] | |
712 | |
713 def join(self,user): | |
714 if user in self.users: | |
715 return | |
716 self.users.append(user) | |
717 user.chatJoin(self) | |
718 | |
719 def leave(self,user): | |
720 if user not in self.users: | |
721 raise TOCParseError | |
722 self.users.remove(user) | |
723 user.chatLeave(self) | |
724 for u in self.users: | |
725 u.chatUserUpdate(self,user) | |
726 if len(self.users)==0: | |
727 self.factory.remChatroom(self) | |
728 | |
729 def say(self,user,message): | |
730 for u in self.users: | |
731 u.chatMessage(self,user,message) | |
732 | |
733 | |
734 class SavedUser: | |
735 def __init__(self): | |
736 self.config="" | |
737 self.nick="" | |
738 self.password="" | |
739 self.evilness=0 | |
740 | |
741 | |
742 class TOCFactory(protocol.Factory): | |
743 def __init__(self): | |
744 self.users={} | |
745 self.savedusers={} | |
746 self.chatroom={} | |
747 self.chatroomid=0 | |
748 | |
749 def buildProtocol(self,addr): | |
750 p=TOC() | |
751 p.factory=self | |
752 return p | |
753 | |
754 def getChatroom(self,exchange,name): | |
755 for i in self.chatroom.values(): | |
756 if normalize(i.name)==normalize(name): | |
757 return i | |
758 self.chatroom[self.chatroomid]=Chatroom(self,exchange,name,self.chatroom
id) | |
759 self.chatroomid=self.chatroomid+1 | |
760 return self.chatroom[self.chatroomid-1] | |
761 | |
762 def remChatroom(self,room): | |
763 id=room.id | |
764 del self.chatroom[id] | |
765 | |
766 MAXARGS={} | |
767 MAXARGS["CONFIG"]=0 | |
768 MAXARGS["NICK"]=0 | |
769 MAXARGS["IM_IN"]=2 | |
770 MAXARGS["UPDATE_BUDDY"]=5 | |
771 MAXARGS["ERROR"]=-1 | |
772 MAXARGS["EVILED"]=1 | |
773 MAXARGS["CHAT_JOIN"]=1 | |
774 MAXARGS["CHAT_IN"]=3 | |
775 MAXARGS["CHAT_UPDATE_BUDDY"]=-1 | |
776 MAXARGS["CHAT_INVITE"]=3 | |
777 MAXARGS["CHAT_LEFT"]=0 | |
778 MAXARGS["ADMIN_NICK_STATUS"]=0 | |
779 MAXARGS["ADMIN_PASSWD_STATUS"]=0 | |
780 | |
781 | |
782 class TOCClient(protocol.Protocol): | |
783 def __init__(self,username,password,authhost="login.oscar.aol.com",authport=
5190): | |
784 | |
785 self.username=normalize(username) # our username | |
786 self._password=password # our password | |
787 self._mode="SendNick" # current mode | |
788 self._ourseqnum=19071 # current sequence number (for sendFlap) | |
789 self._authhost=authhost # authorization host | |
790 self._authport=authport # authorization port | |
791 self._online=0 # are we online? | |
792 self._buddies=[] # the current buddy list | |
793 self._privacymode=PERMITALL # current privacy mode | |
794 self._permitlist=[] # list of users on the permit list | |
795 self._roomnames={} # the names for each of the rooms we're in | |
796 self._receivedchatmembers={} # have we gotten who's in our room yet? | |
797 self._denylist=[] | |
798 self._cookies={} # for file transfers | |
799 self._buf='' # current data buffer | |
800 self._awaymessage='' | |
801 | |
802 def _debug(self,data): | |
803 log.msg(data) | |
804 | |
805 def sendFlap(self,type,data): | |
806 if type==DATA: | |
807 data=data+"\000" | |
808 length=len(data) | |
809 s="*" | |
810 s=s+struct.pack("!BHH",type,self._ourseqnum,length) | |
811 s=s+data | |
812 self._ourseqnum=self._ourseqnum+1 | |
813 if self._ourseqnum>(256*256+256): | |
814 self._ourseqnum=0 | |
815 self._debug(data) | |
816 self.transport.write(s) | |
817 | |
818 def isFlap(self): | |
819 """ | |
820 tests to see if a flap is actually on the buffer | |
821 """ | |
822 if self._buf=='': return 0 | |
823 if self._buf[0]!="*": return 0 | |
824 if len(self._buf)<6: return 0 | |
825 foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6]) | |
826 if type not in range(1,6): return 0 | |
827 if len(self._buf)<6+length: return 0 | |
828 return 1 | |
829 | |
830 def readFlap(self): | |
831 if self._buf=='': return None | |
832 if self._buf[0]!="*": | |
833 raise TOCParseError | |
834 if len(self._buf)<6: return None | |
835 foo,type,seqnum,length=struct.unpack("!BBHH",self._buf[:6]) | |
836 if len(self._buf)<6+length: return None | |
837 data=self._buf[6:6+length] | |
838 self._buf=self._buf[6+length:] | |
839 if data and data[-1]=="\000": | |
840 data=data[:-1] | |
841 return [type,data] | |
842 | |
843 def connectionMade(self): | |
844 self._debug("connection made! %s" % self.transport) | |
845 self.transport.write("FLAPON\r\n\r\n") | |
846 | |
847 def connectionLost(self, reason): | |
848 self._debug("connection lost!") | |
849 self._online=0 | |
850 | |
851 def dataReceived(self,data): | |
852 self._buf=self._buf+data | |
853 while self.isFlap(): | |
854 flap=self.readFlap() | |
855 func=getattr(self,"mode%s"%self._mode) | |
856 func(flap) | |
857 | |
858 def modeSendNick(self,flap): | |
859 if flap!=[1,"\000\000\000\001"]: raise TOCParseError | |
860 s="\000\000\000\001\000\001"+struct.pack("!H",len(self.username))+self.u
sername | |
861 self.sendFlap(1,s) | |
862 s="toc_signon %s %s %s %s english \"penguin\""%(self._authhost,\ | |
863 self._authport,self.username,roast(self._password)) | |
864 self.sendFlap(2,s) | |
865 self._mode="Data" | |
866 | |
867 def modeData(self,flap): | |
868 if not flap[1]: | |
869 return | |
870 if not ':' in flap[1]: | |
871 self._debug("bad SNAC:%s"%(flap[1])) | |
872 return | |
873 command,rest=string.split(flap[1],":",1) | |
874 if MAXARGS.has_key(command): | |
875 maxsplit=MAXARGS[command] | |
876 else: | |
877 maxsplit=-1 | |
878 if maxsplit==-1: | |
879 l=tuple(string.split(rest,":")) | |
880 elif maxsplit==0: | |
881 l=(rest,) | |
882 else: | |
883 l=tuple(string.split(rest,":",maxsplit)) | |
884 self._debug("%s %s"%(command,l)) | |
885 try: | |
886 func=getattr(self,"toc%s"%command) | |
887 self._debug("calling %s"%func) | |
888 except: | |
889 self._debug("calling %s"%self.tocUNKNOWN) | |
890 self.tocUNKNOWN(command,l) | |
891 return | |
892 func(l) | |
893 | |
894 def tocUNKNOWN(self,command,data): | |
895 pass | |
896 | |
897 def tocSIGN_ON(self,data): | |
898 if data!=("TOC1.0",): raise TOCParseError | |
899 self._debug("Whee, signed on!") | |
900 if self._buddies: self.add_buddy(self._buddies) | |
901 self._online=1 | |
902 self.onLine() | |
903 | |
904 def tocNICK(self,data): | |
905 """ | |
906 Handle a message that looks like:: | |
907 | |
908 NICK:<format of nickname> | |
909 """ | |
910 self.username=data[0] | |
911 | |
912 def tocCONFIG(self,data): | |
913 """ | |
914 Handle a message that looks like:: | |
915 | |
916 CONFIG:<config> | |
917 | |
918 Format of config data: | |
919 | |
920 - g: group. all users until next g or end of config are in this gro
up | |
921 - b: buddy | |
922 - p: person on the permit list | |
923 - d: person on the deny list | |
924 - m: permit/deny mode (1: permit all, 2: deny all, 3: permit some, 4
: deny some) | |
925 """ | |
926 data=data[0] | |
927 if data and data[0]=="{":data=data[1:-1] | |
928 lines=string.split(data,"\n") | |
929 buddylist={} | |
930 currentgroup="" | |
931 permit=[] | |
932 deny=[] | |
933 mode=1 | |
934 for l in lines: | |
935 if l: | |
936 code,data=l[0],l[2:] | |
937 if code=='g': # group | |
938 currentgroup=data | |
939 buddylist[currentgroup]=[] | |
940 elif code=='b': | |
941 buddylist[currentgroup].append(data) | |
942 elif code=='p': | |
943 permit.append(data) | |
944 elif code=='d': | |
945 deny.append(data) | |
946 elif code=='m': | |
947 mode=int(data) | |
948 self.gotConfig(mode,buddylist,permit,deny) | |
949 | |
950 def tocIM_IN(self,data): | |
951 """ | |
952 Handle a message that looks like:: | |
953 | |
954 IM_IN:<user>:<autoreply T|F>:message | |
955 """ | |
956 user=data[0] | |
957 autoreply=(data[1]=='T') | |
958 message=data[2] | |
959 self.hearMessage(user,message,autoreply) | |
960 | |
961 def tocUPDATE_BUDDY(self,data): | |
962 """ | |
963 Handle a message that looks like:: | |
964 | |
965 UPDATE_BUDDY:<username>:<online T|F>:<warning level>:<signon time>:<
idle time (minutes)>:<user class> | |
966 """ | |
967 data=list(data) | |
968 online=(data[1]=='T') | |
969 if len(data[5])==2: | |
970 data[5]=data[5]+" " | |
971 away=(data[5][-1]=='U') | |
972 if data[5][-1]=='U': | |
973 data[5]=data[5][:-1] | |
974 self.updateBuddy(data[0],online,int(data[2]),int(data[3]),int(data[4]),d
ata[5],away) | |
975 | |
976 def tocERROR(self,data): | |
977 """ | |
978 Handle a message that looks like:: | |
979 | |
980 ERROR:<error code>:<misc. data> | |
981 """ | |
982 code,args=data[0],data[1:] | |
983 self.hearError(int(code),args) | |
984 | |
985 def tocEVILED(self,data): | |
986 """ | |
987 Handle a message that looks like:: | |
988 | |
989 EVILED:<current warning level>:<user who warned us> | |
990 """ | |
991 self.hearWarning(data[0],data[1]) | |
992 | |
993 def tocCHAT_JOIN(self,data): | |
994 """ | |
995 Handle a message that looks like:: | |
996 | |
997 CHAT_JOIN:<room id>:<room name> | |
998 """ | |
999 #self.chatJoined(int(data[0]),data[1]) | |
1000 self._roomnames[int(data[0])]=data[1] | |
1001 self._receivedchatmembers[int(data[0])]=0 | |
1002 | |
1003 def tocCHAT_UPDATE_BUDDY(self,data): | |
1004 """ | |
1005 Handle a message that looks like:: | |
1006 | |
1007 CHAT_UPDATE_BUDDY:<room id>:<in room? T/F>:<user 1>:<user 2>... | |
1008 """ | |
1009 roomid=int(data[0]) | |
1010 inroom=(data[1]=='T') | |
1011 if self._receivedchatmembers[roomid]: | |
1012 for u in data[2:]: | |
1013 self.chatUpdate(roomid,u,inroom) | |
1014 else: | |
1015 self._receivedchatmembers[roomid]=1 | |
1016 self.chatJoined(roomid,self._roomnames[roomid],list(data[2:])) | |
1017 | |
1018 def tocCHAT_IN(self,data): | |
1019 """ | |
1020 Handle a message that looks like:: | |
1021 | |
1022 CHAT_IN:<room id>:<username>:<whisper T/F>:<message> | |
1023 | |
1024 whisper isn't used | |
1025 """ | |
1026 whisper=(data[2]=='T') | |
1027 if whisper: | |
1028 self.chatHearWhisper(int(data[0]),data[1],data[3]) | |
1029 else: | |
1030 self.chatHearMessage(int(data[0]),data[1],data[3]) | |
1031 | |
1032 def tocCHAT_INVITE(self,data): | |
1033 """ | |
1034 Handle a message that looks like:: | |
1035 | |
1036 CHAT_INVITE:<room name>:<room id>:<username>:<message> | |
1037 """ | |
1038 self.chatInvited(int(data[1]),data[0],data[2],data[3]) | |
1039 | |
1040 def tocCHAT_LEFT(self,data): | |
1041 """ | |
1042 Handle a message that looks like:: | |
1043 | |
1044 CHAT_LEFT:<room id> | |
1045 """ | |
1046 self.chatLeft(int(data[0])) | |
1047 del self._receivedchatmembers[int(data[0])] | |
1048 del self._roomnames[int(data[0])] | |
1049 | |
1050 def tocRVOUS_PROPOSE(self,data): | |
1051 """ | |
1052 Handle a message that looks like:: | |
1053 | |
1054 RVOUS_PROPOSE:<user>:<uuid>:<cookie>:<seq>:<rip>:<pip>:<vip>:<port> | |
1055 [:tlv tag1:tlv value1[:tlv tag2:tlv value2[:...]]] | |
1056 """ | |
1057 user,uid,cookie,seq,rip,pip,vip,port=data[:8] | |
1058 cookie=base64.decodestring(cookie) | |
1059 port=int(port) | |
1060 tlvs={} | |
1061 for i in range(8,len(data),2): | |
1062 key=data[i] | |
1063 value=base64.decodestring(data[i+1]) | |
1064 tlvs[key]=value | |
1065 name=UUIDS[uid] | |
1066 try: | |
1067 func=getattr(self,"toc%s"%name) | |
1068 except: | |
1069 self._debug("no function for UID %s" % uid) | |
1070 return | |
1071 func(user,cookie,seq,pip,vip,port,tlvs) | |
1072 | |
1073 def tocSEND_FILE(self,user,cookie,seq,pip,vip,port,tlvs): | |
1074 if tlvs.has_key('12'): | |
1075 description=tlvs['12'] | |
1076 else: | |
1077 description="" | |
1078 subtype,numfiles,size=struct.unpack("!HHI",tlvs['10001'][:8]) | |
1079 name=tlvs['10001'][8:-4] | |
1080 while name[-1]=='\000': | |
1081 name=name[:-1] | |
1082 self._cookies[cookie]=[user,SEND_FILE_UID,pip,port,{'name':name}] | |
1083 self.rvousProposal("send",cookie,user,vip,port,description=description, | |
1084 name=name,files=numfiles,size=size) | |
1085 | |
1086 def tocGET_FILE(self,user,cookie,seq,pip,vip,port,tlvs): | |
1087 return | |
1088 # XXX add this back in | |
1089 #reactor.clientTCP(pip,port,GetFileTransfer(self,cookie,os.path.expandus
er("~"))) | |
1090 #self.rvous_accept(user,cookie,GET_FILE_UID) | |
1091 | |
1092 def onLine(self): | |
1093 """ | |
1094 called when we are first online | |
1095 """ | |
1096 pass | |
1097 | |
1098 def gotConfig(self,mode,buddylist,permit,deny): | |
1099 """ | |
1100 called when we get a configuration from the server | |
1101 mode := permit/deny mode | |
1102 buddylist := current buddylist | |
1103 permit := permit list | |
1104 deny := deny list | |
1105 """ | |
1106 pass | |
1107 | |
1108 def hearError(self,code,args): | |
1109 """ | |
1110 called when an error is received | |
1111 code := error code | |
1112 args := misc. arguments (username, etc.) | |
1113 """ | |
1114 pass | |
1115 | |
1116 def hearWarning(self,newamount,username): | |
1117 """ | |
1118 called when we get warned | |
1119 newamount := the current warning level | |
1120 username := the user who warned us, or '' if it's anonymous | |
1121 """ | |
1122 pass | |
1123 | |
1124 def hearMessage(self,username,message,autoreply): | |
1125 """ | |
1126 called when you receive an IM | |
1127 username := the user who the IM is from | |
1128 message := the message | |
1129 autoreply := true if the message is an autoreply from an away message | |
1130 """ | |
1131 pass | |
1132 | |
1133 def updateBuddy(self,username,online,evilness,signontime,idletime,userclass,
away): | |
1134 """ | |
1135 called when a buddy changes state | |
1136 username := the user whos state changed | |
1137 online := true if the user is online | |
1138 evilness := the users current warning level | |
1139 signontime := the time the user signed on (UNIX epoch) | |
1140 idletime := the time the user has been idle (minutes) | |
1141 away := true if the user is away | |
1142 userclass := the class of the user (generally " O") | |
1143 """ | |
1144 pass | |
1145 | |
1146 def chatJoined(self,roomid,roomname,users): | |
1147 """ | |
1148 we just joined a chat room | |
1149 roomid := the AIM id for the room | |
1150 roomname := the name for the room | |
1151 users := a list of the users already in the room | |
1152 """ | |
1153 pass | |
1154 | |
1155 def chatUpdate(self,roomid,username,inroom): | |
1156 """ | |
1157 a user has joined the room | |
1158 roomid := the AIM id for the room | |
1159 username := the username | |
1160 inroom := true if the user is in the room | |
1161 """ | |
1162 pass | |
1163 | |
1164 def chatHearMessage(self,roomid,username,message): | |
1165 """ | |
1166 a message was sent to the room | |
1167 roomid := the AIM id for the room | |
1168 username := the user who sent the message | |
1169 message := the message | |
1170 """ | |
1171 pass | |
1172 | |
1173 def chatHearWhisper(self,roomid,username,message): | |
1174 """ | |
1175 someone whispered to us in a chatroom | |
1176 roomid := the AIM for the room | |
1177 username := the user who whispered to us | |
1178 message := the message | |
1179 """ | |
1180 pass | |
1181 | |
1182 def chatInvited(self,roomid,roomname,username,message): | |
1183 """ | |
1184 we were invited to a chat room | |
1185 roomid := the AIM id for the room | |
1186 roomname := the name of the room | |
1187 username := the user who invited us | |
1188 message := the invite message | |
1189 """ | |
1190 pass | |
1191 | |
1192 def chatLeft(self,roomid): | |
1193 """ | |
1194 we left the room | |
1195 roomid := the AIM id for the room | |
1196 """ | |
1197 pass | |
1198 | |
1199 def rvousProposal(self,type,cookie,user,vip,port,**kw): | |
1200 """ | |
1201 we were asked for a rondevouz | |
1202 type := the type of rondevous. currently, one of ["send"] | |
1203 cookie := the cookie. pass this to rvous_accept() | |
1204 user := the user who asked us | |
1205 vip := their verified_ip | |
1206 port := the port they want us to conenct to | |
1207 kw := misc. args | |
1208 """ | |
1209 pass #self.rvous_accept(cookie) | |
1210 | |
1211 def receiveBytes(self,user,file,chunk,sofar,total): | |
1212 """ | |
1213 we received part of a file from a file transfer | |
1214 file := the name of the file | |
1215 chunk := the chunk of data | |
1216 sofar := how much data we've gotten so far | |
1217 total := the total amount of data | |
1218 """ | |
1219 pass #print user,file,sofar,total | |
1220 | |
1221 def isaway(self): | |
1222 """ | |
1223 return our away status | |
1224 """ | |
1225 return len(self._awaymessage)>0 | |
1226 | |
1227 def set_config(self,mode,buddylist,permit,deny): | |
1228 """ | |
1229 set the server configuration | |
1230 mode := permit mode | |
1231 buddylist := buddy list | |
1232 permit := permit list | |
1233 deny := deny list | |
1234 """ | |
1235 s="m %s\n"%mode | |
1236 for g in buddylist.keys(): | |
1237 s=s+"g %s\n"%g | |
1238 for u in buddylist[g]: | |
1239 s=s+"b %s\n"%u | |
1240 for p in permit: | |
1241 s=s+"p %s\n"%p | |
1242 for d in deny: | |
1243 s=s+"d %s\n"%d | |
1244 #s="{\n"+s+"\n}" | |
1245 self.sendFlap(2,"toc_set_config %s"%quote(s)) | |
1246 | |
1247 def add_buddy(self,buddies): | |
1248 s="" | |
1249 if type(buddies)==type(""): buddies=[buddies] | |
1250 for b in buddies: | |
1251 s=s+" "+normalize(b) | |
1252 self.sendFlap(2,"toc_add_buddy%s"%s) | |
1253 | |
1254 def del_buddy(self,buddies): | |
1255 s="" | |
1256 if type(buddies)==type(""): buddies=[buddies] | |
1257 for b in buddies: | |
1258 s=s+" "+b | |
1259 self.sendFlap(2,"toc_remove_buddy%s"%s) | |
1260 | |
1261 def add_permit(self,users): | |
1262 if type(users)==type(""): users=[users] | |
1263 s="" | |
1264 if self._privacymode!=PERMITSOME: | |
1265 self._privacymode=PERMITSOME | |
1266 self._permitlist=[] | |
1267 for u in users: | |
1268 u=normalize(u) | |
1269 if u not in self._permitlist:self._permitlist.append(u) | |
1270 s=s+" "+u | |
1271 if not s: | |
1272 self._privacymode=DENYALL | |
1273 self._permitlist=[] | |
1274 self._denylist=[] | |
1275 self.sendFlap(2,"toc_add_permit"+s) | |
1276 | |
1277 def del_permit(self,users): | |
1278 if type(users)==type(""): users=[users] | |
1279 p=self._permitlist[:] | |
1280 for u in users: | |
1281 u=normalize(u) | |
1282 if u in p: | |
1283 p.remove(u) | |
1284 self.add_permit([]) | |
1285 self.add_permit(p) | |
1286 | |
1287 def add_deny(self,users): | |
1288 if type(users)==type(""): users=[users] | |
1289 s="" | |
1290 if self._privacymode!=DENYSOME: | |
1291 self._privacymode=DENYSOME | |
1292 self._denylist=[] | |
1293 for u in users: | |
1294 u=normalize(u) | |
1295 if u not in self._denylist:self._denylist.append(u) | |
1296 s=s+" "+u | |
1297 if not s: | |
1298 self._privacymode=PERMITALL | |
1299 self._permitlist=[] | |
1300 self._denylist=[] | |
1301 self.sendFlap(2,"toc_add_deny"+s) | |
1302 | |
1303 def del_deny(self,users): | |
1304 if type(users)==type(""): users=[users] | |
1305 d=self._denylist[:] | |
1306 for u in users: | |
1307 u=normalize(u) | |
1308 if u in d: | |
1309 d.remove(u) | |
1310 self.add_deny([]) | |
1311 if d: | |
1312 self.add_deny(d) | |
1313 | |
1314 def signon(self): | |
1315 """ | |
1316 called to finish the setup, and signon to the network | |
1317 """ | |
1318 self.sendFlap(2,"toc_init_done") | |
1319 self.sendFlap(2,"toc_set_caps %s" % (SEND_FILE_UID,)) # GET_FILE_UID) | |
1320 | |
1321 def say(self,user,message,autoreply=0): | |
1322 """ | |
1323 send a message | |
1324 user := the user to send to | |
1325 message := the message | |
1326 autoreply := true if the message is an autoreply (good for away messages
) | |
1327 """ | |
1328 if autoreply: a=" auto" | |
1329 else: a='' | |
1330 self.sendFlap(2,"toc_send_im %s %s%s"%(normalize(user),quote(message),a)
) | |
1331 | |
1332 def idle(self,idletime=0): | |
1333 """ | |
1334 change idle state | |
1335 idletime := the seconds that the user has been away, or 0 if they're bac
k | |
1336 """ | |
1337 self.sendFlap(2,"toc_set_idle %s" % int(idletime)) | |
1338 | |
1339 def evil(self,user,anon=0): | |
1340 """ | |
1341 warn a user | |
1342 user := the user to warn | |
1343 anon := if true, an anonymous warning | |
1344 """ | |
1345 self.sendFlap(2,"toc_evil %s %s"%(normalize(user), (not anon and "anon")
or "norm")) | |
1346 | |
1347 def away(self,message=''): | |
1348 """ | |
1349 change away state | |
1350 message := the message, or '' to come back from awayness | |
1351 """ | |
1352 self._awaymessage=message | |
1353 if message: | |
1354 message=' '+quote(message) | |
1355 self.sendFlap(2,"toc_set_away%s"%message) | |
1356 | |
1357 def chat_join(self,exchange,roomname): | |
1358 """ | |
1359 join a chat room | |
1360 exchange := should almost always be 4 | |
1361 roomname := room name | |
1362 """ | |
1363 roomname=string.replace(roomname," ","") | |
1364 self.sendFlap(2,"toc_chat_join %s %s"%(int(exchange),roomname)) | |
1365 | |
1366 def chat_say(self,roomid,message): | |
1367 """ | |
1368 send a message to a chatroom | |
1369 roomid := the AIM id for the room | |
1370 message := the message to send | |
1371 """ | |
1372 self.sendFlap(2,"toc_chat_send %s %s"%(int(roomid),quote(message))) | |
1373 | |
1374 def chat_whisper(self,roomid,user,message): | |
1375 """ | |
1376 whisper to another user in a chatroom | |
1377 roomid := the AIM id for the room | |
1378 user := the user to whisper to | |
1379 message := the message to send | |
1380 """ | |
1381 self.sendFlap(2,"toc_chat_whisper %s %s %s"%(int(roomid),normalize(user)
,quote(message))) | |
1382 | |
1383 def chat_leave(self,roomid): | |
1384 """ | |
1385 leave a chat room. | |
1386 roomid := the AIM id for the room | |
1387 """ | |
1388 self.sendFlap(2,"toc_chat_leave %s" % int(roomid)) | |
1389 | |
1390 def chat_invite(self,roomid,usernames,message): | |
1391 """ | |
1392 invite a user[s] to the chat room | |
1393 roomid := the AIM id for the room | |
1394 usernames := either a string (one username) or a list (more than one) | |
1395 message := the message to invite them with | |
1396 """ | |
1397 if type(usernames)==type(""): # a string, one username | |
1398 users=usernames | |
1399 else: | |
1400 users="" | |
1401 for u in usernames: | |
1402 users=users+u+" " | |
1403 users=users[:-1] | |
1404 self.sendFlap(2,"toc_chat_invite %s %s %s" % (int(roomid),quote(message)
,users)) | |
1405 | |
1406 def chat_accept(self,roomid): | |
1407 """ | |
1408 accept an invite to a chat room | |
1409 roomid := the AIM id for the room | |
1410 """ | |
1411 self.sendFlap(2,"toc_chat_accept %s"%int(roomid)) | |
1412 | |
1413 def rvous_accept(self,cookie): | |
1414 user,uuid,pip,port,d=self._cookies[cookie] | |
1415 self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user), | |
1416 cookie,uuid)) | |
1417 if uuid==SEND_FILE_UID: | |
1418 protocol.ClientCreator(reactor, SendFileTransfer,self,cookie,user,d[
"name"]).connectTCP(pip,port) | |
1419 | |
1420 def rvous_cancel(self,cookie): | |
1421 user,uuid,pip,port,d=self._cookies[cookie] | |
1422 self.sendFlap(2,"toc_rvous_accept %s %s %s" % (normalize(user), | |
1423 cookie,uuid)) | |
1424 del self._cookies[cookie] | |
1425 | |
1426 | |
1427 class SendFileTransfer(protocol.Protocol): | |
1428 header_fmt="!4s2H8s6H10I32s3c69s16s2H64s" | |
1429 | |
1430 def __init__(self,client,cookie,user,filename): | |
1431 self.client=client | |
1432 self.cookie=cookie | |
1433 self.user=user | |
1434 self.filename=filename | |
1435 self.hdr=[0,0,0] | |
1436 self.sofar=0 | |
1437 | |
1438 def dataReceived(self,data): | |
1439 if not self.hdr[2]==0x202: | |
1440 self.hdr=list(struct.unpack(self.header_fmt,data[:256])) | |
1441 self.hdr[2]=0x202 | |
1442 self.hdr[3]=self.cookie | |
1443 self.hdr[4]=0 | |
1444 self.hdr[5]=0 | |
1445 self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr)) | |
1446 data=data[256:] | |
1447 if self.hdr[6]==1: | |
1448 self.name=self.filename | |
1449 else: | |
1450 self.name=self.filename+self.hdr[-1] | |
1451 while self.name[-1]=="\000": | |
1452 self.name=self.name[:-1] | |
1453 if not data: return | |
1454 self.sofar=self.sofar+len(data) | |
1455 self.client.receiveBytes(self.user,self.name,data,self.sofar,self.hdr[11
]) | |
1456 if self.sofar==self.hdr[11]: # end of this file | |
1457 self.hdr[2]=0x204 | |
1458 self.hdr[7]=self.hdr[7]-1 | |
1459 self.hdr[9]=self.hdr[9]-1 | |
1460 self.hdr[19]=DUMMY_CHECKSUM # XXX really calculate this | |
1461 self.hdr[18]=self.hdr[18]+1 | |
1462 self.hdr[21]="\000" | |
1463 self.transport.write(apply(struct.pack,[self.header_fmt]+self.hdr)) | |
1464 self.sofar=0 | |
1465 if self.hdr[7]==0: | |
1466 self.transport.loseConnection() | |
1467 | |
1468 | |
1469 class GetFileTransfer(protocol.Protocol): | |
1470 header_fmt="!4s 2H 8s 6H 10I 32s 3c 69s 16s 2H 64s" | |
1471 def __init__(self,client,cookie,dir): | |
1472 self.client=client | |
1473 self.cookie=cookie | |
1474 self.dir=dir | |
1475 self.buf="" | |
1476 | |
1477 def connectionMade(self): | |
1478 def func(f,path,names): | |
1479 names.sort(lambda x,y:cmp(string.lower(x),string.lower(y))) | |
1480 for n in names: | |
1481 name=os.path.join(path,n) | |
1482 lt=time.localtime(os.path.getmtime(name)) | |
1483 size=os.path.getsize(name) | |
1484 f[1]=f[1]+size | |
1485 f.append("%02d/%02d/%4d %02d:%02d %8d %s" % | |
1486 (lt[1],lt[2],lt[0],lt[3],lt[4],size,name[f[0]:])) | |
1487 f=[len(self.dir)+1,0] | |
1488 os.path.walk(self.dir,func,f) | |
1489 size=f[1] | |
1490 self.listing=string.join(f[2:],"\r\n")+"\r\n" | |
1491 open("\\listing.txt","w").write(self.listing) | |
1492 hdr=["OFT2",256,0x1108,self.cookie,0,0,len(f)-2,len(f)-2,1,1,size, | |
1493 len(self.listing),os.path.getmtime(self.dir), | |
1494 checksum(self.listing),0,0,0,0,0,0,"OFT_Windows ICBMFT V1.1 32", | |
1495 "\002",chr(0x1a),chr(0x10),"","",0,0,""] | |
1496 self.transport.write(apply(struct.pack,[self.header_fmt]+hdr)) | |
1497 | |
1498 def dataReceived(self,data): | |
1499 self.buf=self.buf+data | |
1500 while len(self.buf)>=256: | |
1501 hdr=list(struct.unpack(self.header_fmt,self.buf[:256])) | |
1502 self.buf=self.buf[256:] | |
1503 if hdr[2]==0x1209: | |
1504 self.file=StringIO.StringIO(self.listing) | |
1505 self.transport.registerProducer(self,0) | |
1506 elif hdr[2]==0x120b: pass | |
1507 elif hdr[2]==0x120c: # file request | |
1508 file=hdr[-1] | |
1509 for k,v in [["\000",""],["\001",os.sep]]: | |
1510 file=string.replace(file,k,v) | |
1511 self.name=os.path.join(self.dir,file) | |
1512 self.file=open(self.name,'rb') | |
1513 hdr[2]=0x0101 | |
1514 hdr[6]=hdr[7]=1 | |
1515 hdr[10]=hdr[11]=os.path.getsize(self.name) | |
1516 hdr[12]=os.path.getmtime(self.name) | |
1517 hdr[13]=checksum_file(self.file) | |
1518 self.file.seek(0) | |
1519 hdr[18]=hdr[19]=0 | |
1520 hdr[21]=chr(0x20) | |
1521 self.transport.write(apply(struct.pack,[self.header_fmt]+hdr)) | |
1522 log.msg("got file request for %s"%file,hex(hdr[13])) | |
1523 elif hdr[2]==0x0202: | |
1524 log.msg("sending file") | |
1525 self.transport.registerProducer(self,0) | |
1526 elif hdr[2]==0x0204: | |
1527 log.msg("real checksum: %s"%hex(hdr[19])) | |
1528 del self.file | |
1529 elif hdr[2]==0x0205: # resume | |
1530 already=hdr[18] | |
1531 if already: | |
1532 data=self.file.read(already) | |
1533 else: | |
1534 data="" | |
1535 log.msg("restarting at %s"%already) | |
1536 hdr[2]=0x0106 | |
1537 hdr[19]=checksum(data) | |
1538 self.transport.write(apply(struct.pack,[self.header_fmt]+hdr)) | |
1539 elif hdr[2]==0x0207: | |
1540 self.transport.registerProducer(self,0) | |
1541 else: | |
1542 log.msg("don't understand 0x%04x"%hdr[2]) | |
1543 log.msg(hdr) | |
1544 | |
1545 def resumeProducing(self): | |
1546 data=self.file.read(4096) | |
1547 log.msg(len(data)) | |
1548 if not data: | |
1549 self.transport.unregisterProducer() | |
1550 self.transport.write(data) | |
1551 | |
1552 def pauseProducing(self): pass | |
1553 | |
1554 def stopProducing(self): del self.file | |
1555 | |
1556 # UUIDs | |
1557 SEND_FILE_UID = "09461343-4C7F-11D1-8222-444553540000" | |
1558 GET_FILE_UID = "09461348-4C7F-11D1-8222-444553540000" | |
1559 UUIDS={ | |
1560 SEND_FILE_UID:"SEND_FILE", | |
1561 GET_FILE_UID:"GET_FILE" | |
1562 } | |
1563 | |
1564 # ERRORS | |
1565 # general | |
1566 NOT_AVAILABLE=901 | |
1567 CANT_WARN=902 | |
1568 MESSAGES_TOO_FAST=903 | |
1569 # admin | |
1570 BAD_INPUT=911 | |
1571 BAD_ACCOUNT=912 | |
1572 REQUEST_ERROR=913 | |
1573 SERVICE_UNAVAILABLE=914 | |
1574 # chat | |
1575 NO_CHAT_IN=950 | |
1576 # im and info | |
1577 SEND_TOO_FAST=960 | |
1578 MISSED_BIG_IM=961 | |
1579 MISSED_FAST_IM=962 | |
1580 # directory | |
1581 DIR_FAILURE=970 | |
1582 TOO_MANY_MATCHES=971 | |
1583 NEED_MORE_QUALIFIERS=972 | |
1584 DIR_UNAVAILABLE=973 | |
1585 NO_EMAIL_LOOKUP=974 | |
1586 KEYWORD_IGNORED=975 | |
1587 NO_KEYWORDS=976 | |
1588 BAD_LANGUAGE=977 | |
1589 BAD_COUNTRY=978 | |
1590 DIR_FAIL_UNKNOWN=979 | |
1591 # authorization | |
1592 BAD_NICKNAME=980 | |
1593 SERVICE_TEMP_UNAVAILABLE=981 | |
1594 WARNING_TOO_HIGH=982 | |
1595 CONNECTING_TOO_QUICK=983 | |
1596 UNKNOWN_SIGNON=989 | |
1597 | |
1598 STD_MESSAGE={} | |
1599 STD_MESSAGE[NOT_AVAILABLE]="%s not currently available" | |
1600 STD_MESSAGE[CANT_WARN]="Warning of %s not currently available" | |
1601 STD_MESSAGE[MESSAGES_TOO_FAST]="A message has been dropped, you are exceeding th
e server speed limit" | |
1602 STD_MESSAGE[BAD_INPUT]="Error validating input" | |
1603 STD_MESSAGE[BAD_ACCOUNT]="Invalid account" | |
1604 STD_MESSAGE[REQUEST_ERROR]="Error encountered while processing request" | |
1605 STD_MESSAGE[SERVICE_UNAVAILABLE]="Service unavailable" | |
1606 STD_MESSAGE[NO_CHAT_IN]="Chat in %s is unavailable" | |
1607 STD_MESSAGE[SEND_TOO_FAST]="You are sending messages too fast to %s" | |
1608 STD_MESSAGE[MISSED_BIG_IM]="You missed an IM from %s because it was too big" | |
1609 STD_MESSAGE[MISSED_FAST_IM]="You missed an IM from %s because it was sent too fa
st" | |
1610 # skipping directory for now | |
1611 STD_MESSAGE[BAD_NICKNAME]="Incorrect nickname or password" | |
1612 STD_MESSAGE[SERVICE_TEMP_UNAVAILABLE]="The service is temporarily unavailable" | |
1613 STD_MESSAGE[WARNING_TOO_HIGH]="Your warning level is currently too high to sign
on" | |
1614 STD_MESSAGE[CONNECTING_TOO_QUICK]="You have been connecting and disconnecting to
o frequently. Wait 10 minutes and try again. If you continue to try, you will
need to wait even longer." | |
1615 STD_MESSAGE[UNKNOWN_SIGNON]="An unknown signon error has occurred %s" | |
OLD | NEW |