root / trunk / twisted / words / protocols / toc.py

Revision 25789, 49.7 kB (checked in by exarkun, 6 months ago)

Apply twisted/words/protocols/toc.py deprecation patch

Author: exarkun
Reviewer: therve
Fixes: #3580

Formally deprecate twisted.words.protocols.toc, informally deprecated for seven
years already, and largely non-working and useless since AOL disabled TOC on their
AIM network.

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