| 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 |