~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/words/protocols/oscar.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.words.test -*-
 
2
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
 
3
# See LICENSE for details.
 
4
 
 
5
 
 
6
"""
 
7
An implementation of the OSCAR protocol, which AIM and ICQ use to communcate.
 
8
 
 
9
Maintainer: Paul Swartz
 
10
"""
 
11
 
 
12
import struct
 
13
import string
 
14
import socket
 
15
import random
 
16
import types
 
17
import re
 
18
 
 
19
from twisted.internet import reactor, defer, protocol
 
20
from twisted.python import log
 
21
from twisted.python.hashlib import md5
 
22
 
 
23
def logPacketData(data):
 
24
    lines = len(data)/16
 
25
    if lines*16 != len(data): lines=lines+1
 
26
    for i in range(lines):
 
27
        d = tuple(data[16*i:16*i+16])
 
28
        hex = map(lambda x: "%02X"%ord(x),d)
 
29
        text = map(lambda x: (len(repr(x))>3 and '.') or x, d)
 
30
        log.msg(' '.join(hex)+ ' '*3*(16-len(d)) +''.join(text))
 
31
    log.msg('')
 
32
 
 
33
def SNAC(fam,sub,id,data,flags=[0,0]):
 
34
    header="!HHBBL"
 
35
    head=struct.pack(header,fam,sub,
 
36
                     flags[0],flags[1],
 
37
                     id)
 
38
    return head+str(data)
 
39
 
 
40
def readSNAC(data):
 
41
    header="!HHBBL"
 
42
    head=list(struct.unpack(header,data[:10]))
 
43
    return head+[data[10:]]
 
44
 
 
45
def TLV(type,value):
 
46
    header="!HH"
 
47
    head=struct.pack(header,type,len(value))
 
48
    return head+str(value)
 
49
 
 
50
def readTLVs(data,count=None):
 
51
    header="!HH"
 
52
    dict={}
 
53
    while data and len(dict)!=count:
 
54
        head=struct.unpack(header,data[:4])
 
55
        dict[head[0]]=data[4:4+head[1]]
 
56
        data=data[4+head[1]:]
 
57
    if not count:
 
58
        return dict
 
59
    return dict,data
 
60
 
 
61
def encryptPasswordMD5(password,key):
 
62
    m=md5()
 
63
    m.update(key)
 
64
    m.update(md5(password).digest())
 
65
    m.update("AOL Instant Messenger (SM)")
 
66
    return m.digest()
 
67
 
 
68
def encryptPasswordICQ(password):
 
69
    key=[0xF3,0x26,0x81,0xC4,0x39,0x86,0xDB,0x92,0x71,0xA3,0xB9,0xE6,0x53,0x7A,0x95,0x7C]
 
70
    bytes=map(ord,password)
 
71
    r=""
 
72
    for i in range(len(bytes)):
 
73
        r=r+chr(bytes[i]^key[i%len(key)])
 
74
    return r
 
75
 
 
76
def dehtml(text):
 
77
    text=string.replace(text,"<br>","\n")
 
78
    text=string.replace(text,"<BR>","\n")
 
79
    text=string.replace(text,"<Br>","\n") # XXX make this a regexp
 
80
    text=string.replace(text,"<bR>","\n")
 
81
    text=re.sub('<.*?>','',text)
 
82
    text=string.replace(text,'&gt;','>')
 
83
    text=string.replace(text,'&lt;','<')
 
84
    text=string.replace(text,'&nbsp;',' ')
 
85
    text=string.replace(text,'&#34;','"')
 
86
    text=string.replace(text,'&amp;','&')
 
87
    return text
 
88
 
 
89
def html(text):
 
90
    text=string.replace(text,'"','&#34;')
 
91
    text=string.replace(text,'&','&amp;')
 
92
    text=string.replace(text,'<','&lt;')
 
93
    text=string.replace(text,'>','&gt;')
 
94
    text=string.replace(text,"\n","<br>")
 
95
    return '<html><body bgcolor="white"><font color="black">%s</font></body></html>'%text
 
96
 
 
97
class OSCARUser:
 
98
    def __init__(self, name, warn, tlvs):
 
99
        self.name = name
 
100
        self.warning = warn
 
101
        self.flags = []
 
102
        self.caps = []
 
103
        for k,v in tlvs.items():
 
104
            if k == 1: # user flags
 
105
                v=struct.unpack('!H',v)[0]
 
106
                for o, f in [(1,'trial'),
 
107
                             (2,'unknown bit 2'),
 
108
                             (4,'aol'),
 
109
                             (8,'unknown bit 4'),
 
110
                             (16,'aim'),
 
111
                             (32,'away'),
 
112
                             (1024,'activebuddy')]:
 
113
                    if v&o: self.flags.append(f)
 
114
            elif k == 2: # member since date
 
115
                self.memberSince = struct.unpack('!L',v)[0]
 
116
            elif k == 3: # on-since
 
117
                self.onSince = struct.unpack('!L',v)[0]
 
118
            elif k == 4: # idle time
 
119
                self.idleTime = struct.unpack('!H',v)[0]
 
120
            elif k == 5: # unknown
 
121
                pass
 
122
            elif k == 6: # icq online status
 
123
                if v[2] == '\x00':
 
124
                    self.icqStatus = 'online'
 
125
                elif v[2] == '\x01':
 
126
                    self.icqStatus = 'away'
 
127
                elif v[2] == '\x02':
 
128
                    self.icqStatus = 'dnd'
 
129
                elif v[2] == '\x04':
 
130
                    self.icqStatus = 'out'
 
131
                elif v[2] == '\x10':
 
132
                    self.icqStatus = 'busy'
 
133
                else:
 
134
                    self.icqStatus = 'unknown'
 
135
            elif k == 10: # icq ip address
 
136
                self.icqIPaddy = socket.inet_ntoa(v)
 
137
            elif k == 12: # icq random stuff
 
138
                self.icqRandom = v
 
139
            elif k == 13: # capabilities
 
140
                caps=[]
 
141
                while v:
 
142
                    c=v[:16]
 
143
                    if c==CAP_ICON: caps.append("icon")
 
144
                    elif c==CAP_IMAGE: caps.append("image")
 
145
                    elif c==CAP_VOICE: caps.append("voice")
 
146
                    elif c==CAP_CHAT: caps.append("chat")
 
147
                    elif c==CAP_GET_FILE: caps.append("getfile")
 
148
                    elif c==CAP_SEND_FILE: caps.append("sendfile")
 
149
                    elif c==CAP_SEND_LIST: caps.append("sendlist")
 
150
                    elif c==CAP_GAMES: caps.append("games")
 
151
                    else: caps.append(("unknown",c))
 
152
                    v=v[16:]
 
153
                caps.sort()
 
154
                self.caps=caps
 
155
            elif k == 14: pass
 
156
            elif k == 15: # session length (aim)
 
157
                self.sessionLength = struct.unpack('!L',v)[0]
 
158
            elif k == 16: # session length (aol)
 
159
                self.sessionLength = struct.unpack('!L',v)[0]
 
160
            elif k == 30: # no idea
 
161
                pass
 
162
            else:
 
163
                log.msg("unknown tlv for user %s\nt: %s\nv: %s"%(self.name,k,repr(v)))
 
164
 
 
165
    def __str__(self):
 
166
        s = '<OSCARUser %s' % self.name
 
167
        o = []
 
168
        if self.warning!=0: o.append('warning level %s'%self.warning)
 
169
        if hasattr(self, 'flags'): o.append('flags %s'%self.flags)
 
170
        if hasattr(self, 'sessionLength'): o.append('online for %i minutes' % (self.sessionLength/60,))
 
171
        if hasattr(self, 'idleTime'): o.append('idle for %i minutes' % self.idleTime)
 
172
        if self.caps: o.append('caps %s'%self.caps)
 
173
        if o:
 
174
            s=s+', '+', '.join(o)
 
175
        s=s+'>'
 
176
        return s
 
177
 
 
178
 
 
179
class SSIGroup:
 
180
    def __init__(self, name, tlvs = {}):
 
181
        self.name = name
 
182
        #self.tlvs = []
 
183
        #self.userIDs = []
 
184
        self.usersToID = {}
 
185
        self.users = []
 
186
        #if not tlvs.has_key(0xC8): return
 
187
        #buddyIDs = tlvs[0xC8]
 
188
        #while buddyIDs:
 
189
        #    bid = struct.unpack('!H',buddyIDs[:2])[0]
 
190
        #    buddyIDs = buddyIDs[2:]
 
191
        #    self.users.append(bid)
 
192
 
 
193
    def findIDFor(self, user):
 
194
        return self.usersToID[user]
 
195
 
 
196
    def addUser(self, buddyID, user):
 
197
        self.usersToID[user] = buddyID
 
198
        self.users.append(user)
 
199
        user.group = self
 
200
 
 
201
    def oscarRep(self, groupID, buddyID):
 
202
        tlvData = TLV(0xc8, reduce(lambda x,y:x+y, [struct.pack('!H',self.usersToID[x]) for x in self.users]))
 
203
        return struct.pack('!H', len(self.name)) + self.name + \
 
204
               struct.pack('!HH', groupID, buddyID) + '\000\001' + tlvData
 
205
 
 
206
 
 
207
class SSIBuddy:
 
208
    def __init__(self, name, tlvs = {}):
 
209
        self.name = name
 
210
        self.tlvs = tlvs
 
211
        for k,v in tlvs.items():
 
212
            if k == 0x013c: # buddy comment
 
213
                self.buddyComment = v
 
214
            elif k == 0x013d: # buddy alerts
 
215
                actionFlag = ord(v[0])
 
216
                whenFlag = ord(v[1])
 
217
                self.alertActions = []
 
218
                self.alertWhen = []
 
219
                if actionFlag&1:
 
220
                    self.alertActions.append('popup')
 
221
                if actionFlag&2:
 
222
                    self.alertActions.append('sound')
 
223
                if whenFlag&1:
 
224
                    self.alertWhen.append('online')
 
225
                if whenFlag&2:
 
226
                    self.alertWhen.append('unidle')
 
227
                if whenFlag&4:
 
228
                    self.alertWhen.append('unaway')
 
229
            elif k == 0x013e:
 
230
                self.alertSound = v
 
231
 
 
232
    def oscarRep(self, groupID, buddyID):
 
233
        tlvData = reduce(lambda x,y: x+y, map(lambda (k,v):TLV(k,v), self.tlvs.items()), '\000\000')
 
234
        return struct.pack('!H', len(self.name)) + self.name + \
 
235
               struct.pack('!HH', groupID, buddyID) + '\000\000' + tlvData
 
236
 
 
237
 
 
238
class OscarConnection(protocol.Protocol):
 
239
    def connectionMade(self):
 
240
        self.state=""
 
241
        self.seqnum=0
 
242
        self.buf=''
 
243
        self.stopKeepAliveID = None
 
244
        self.setKeepAlive(4*60) # 4 minutes
 
245
 
 
246
    def connectionLost(self, reason):
 
247
        log.msg("Connection Lost! %s" % self)
 
248
        self.stopKeepAlive()
 
249
 
 
250
#    def connectionFailed(self):
 
251
#        log.msg("Connection Failed! %s" % self)
 
252
#        self.stopKeepAlive()
 
253
 
 
254
    def sendFLAP(self,data,channel = 0x02):
 
255
        header="!cBHH"
 
256
        self.seqnum=(self.seqnum+1)%0xFFFF
 
257
        seqnum=self.seqnum
 
258
        head=struct.pack(header,'*', channel,
 
259
                         seqnum, len(data))
 
260
        self.transport.write(head+str(data))
 
261
#        if isinstance(self, ChatService):
 
262
#            logPacketData(head+str(data))
 
263
 
 
264
    def readFlap(self):
 
265
        header="!cBHH"
 
266
        if len(self.buf)<6: return
 
267
        flap=struct.unpack(header,self.buf[:6])
 
268
        if len(self.buf)<6+flap[3]: return
 
269
        data,self.buf=self.buf[6:6+flap[3]],self.buf[6+flap[3]:]
 
270
        return [flap[1],data]
 
271
 
 
272
    def dataReceived(self,data):
 
273
#        if isinstance(self, ChatService):
 
274
#            logPacketData(data)
 
275
        self.buf=self.buf+data
 
276
        flap=self.readFlap()
 
277
        while flap:
 
278
            func=getattr(self,"oscar_%s"%self.state,None)
 
279
            if not func:
 
280
                log.msg("no func for state: %s" % self.state)
 
281
            state=func(flap)
 
282
            if state:
 
283
                self.state=state
 
284
            flap=self.readFlap()
 
285
 
 
286
    def setKeepAlive(self,t):
 
287
        self.keepAliveDelay=t
 
288
        self.stopKeepAlive()
 
289
        self.stopKeepAliveID = reactor.callLater(t, self.sendKeepAlive)
 
290
 
 
291
    def sendKeepAlive(self):
 
292
        self.sendFLAP("",0x05)
 
293
        self.stopKeepAliveID = reactor.callLater(self.keepAliveDelay, self.sendKeepAlive)
 
294
 
 
295
    def stopKeepAlive(self):
 
296
        if self.stopKeepAliveID:
 
297
            self.stopKeepAliveID.cancel()
 
298
            self.stopKeepAliveID = None
 
299
 
 
300
    def disconnect(self):
 
301
        """
 
302
        send the disconnect flap, and sever the connection
 
303
        """
 
304
        self.sendFLAP('', 0x04)
 
305
        def f(reason): pass
 
306
        self.connectionLost = f
 
307
        self.transport.loseConnection()
 
308
 
 
309
 
 
310
class SNACBased(OscarConnection):
 
311
    snacFamilies = {
 
312
        # family : (version, toolID, toolVersion)
 
313
    }
 
314
    def __init__(self,cookie):
 
315
        self.cookie=cookie
 
316
        self.lastID=0
 
317
        self.supportedFamilies = ()
 
318
        self.requestCallbacks={} # request id:Deferred
 
319
 
 
320
    def sendSNAC(self,fam,sub,data,flags=[0,0]):
 
321
        """
 
322
        send a snac and wait for the response by returning a Deferred.
 
323
        """
 
324
        reqid=self.lastID
 
325
        self.lastID=reqid+1
 
326
        d = defer.Deferred()
 
327
        d.reqid = reqid
 
328
 
 
329
        #d.addErrback(self._ebDeferredError,fam,sub,data) # XXX for testing
 
330
 
 
331
        self.requestCallbacks[reqid] = d
 
332
        self.sendFLAP(SNAC(fam,sub,reqid,data))
 
333
        return d
 
334
 
 
335
    def _ebDeferredError(self, error, fam, sub, data):
 
336
        log.msg('ERROR IN DEFERRED %s' % error)
 
337
        log.msg('on sending of message, family 0x%02x, subtype 0x%02x' % (fam, sub))
 
338
        log.msg('data: %s' % repr(data))
 
339
 
 
340
    def sendSNACnr(self,fam,sub,data,flags=[0,0]):
 
341
        """
 
342
        send a snac, but don't bother adding a deferred, we don't care.
 
343
        """
 
344
        self.sendFLAP(SNAC(fam,sub,0x10000*fam+sub,data))
 
345
 
 
346
    def oscar_(self,data):
 
347
        self.sendFLAP("\000\000\000\001"+TLV(6,self.cookie), 0x01)
 
348
        return "Data"
 
349
 
 
350
    def oscar_Data(self,data):
 
351
        snac=readSNAC(data[1])
 
352
        if self.requestCallbacks.has_key(snac[4]):
 
353
            d = self.requestCallbacks[snac[4]]
 
354
            del self.requestCallbacks[snac[4]]
 
355
            if snac[1]!=1:
 
356
                d.callback(snac)
 
357
            else:
 
358
                d.errback(snac)
 
359
            return
 
360
        func=getattr(self,'oscar_%02X_%02X'%(snac[0],snac[1]),None)
 
361
        if not func:
 
362
            self.oscar_unknown(snac)
 
363
        else:
 
364
            func(snac[2:])
 
365
        return "Data"
 
366
 
 
367
    def oscar_unknown(self,snac):
 
368
        log.msg("unknown for %s" % self)
 
369
        log.msg(snac)
 
370
 
 
371
 
 
372
    def oscar_01_03(self, snac):
 
373
        numFamilies = len(snac[3])/2
 
374
        self.supportedFamilies = struct.unpack("!"+str(numFamilies)+'H', snac[3])
 
375
        d = ''
 
376
        for fam in self.supportedFamilies:
 
377
            if self.snacFamilies.has_key(fam):
 
378
                d=d+struct.pack('!2H',fam,self.snacFamilies[fam][0])
 
379
        self.sendSNACnr(0x01,0x17, d)
 
380
 
 
381
    def oscar_01_0A(self,snac):
 
382
        """
 
383
        change of rate information.
 
384
        """
 
385
        # this can be parsed, maybe we can even work it in
 
386
        pass
 
387
 
 
388
    def oscar_01_18(self,snac):
 
389
        """
 
390
        host versions, in the same format as we sent
 
391
        """
 
392
        self.sendSNACnr(0x01,0x06,"") #pass
 
393
 
 
394
    def clientReady(self):
 
395
        """
 
396
        called when the client is ready to be online
 
397
        """
 
398
        d = ''
 
399
        for fam in self.supportedFamilies:
 
400
            if self.snacFamilies.has_key(fam):
 
401
                version, toolID, toolVersion = self.snacFamilies[fam]
 
402
                d = d + struct.pack('!4H',fam,version,toolID,toolVersion)
 
403
        self.sendSNACnr(0x01,0x02,d)
 
404
 
 
405
class BOSConnection(SNACBased):
 
406
    snacFamilies = {
 
407
        0x01:(3, 0x0110, 0x059b),
 
408
        0x13:(3, 0x0110, 0x059b),
 
409
        0x02:(1, 0x0110, 0x059b),
 
410
        0x03:(1, 0x0110, 0x059b),
 
411
        0x04:(1, 0x0110, 0x059b),
 
412
        0x06:(1, 0x0110, 0x059b),
 
413
        0x08:(1, 0x0104, 0x0001),
 
414
        0x09:(1, 0x0110, 0x059b),
 
415
        0x0a:(1, 0x0110, 0x059b),
 
416
        0x0b:(1, 0x0104, 0x0001),
 
417
        0x0c:(1, 0x0104, 0x0001)
 
418
    }
 
419
 
 
420
    capabilities = None
 
421
 
 
422
    def __init__(self,username,cookie):
 
423
        SNACBased.__init__(self,cookie)
 
424
        self.username=username
 
425
        self.profile = None
 
426
        self.awayMessage = None
 
427
        self.services = {}
 
428
 
 
429
        if not self.capabilities:
 
430
            self.capabilities = [CAP_CHAT]
 
431
 
 
432
    def parseUser(self,data,count=None):
 
433
        l=ord(data[0])
 
434
        name=data[1:1+l]
 
435
        warn,foo=struct.unpack("!HH",data[1+l:5+l])
 
436
        warn=int(warn/10)
 
437
        tlvs=data[5+l:]
 
438
        if count:
 
439
            tlvs,rest = readTLVs(tlvs,foo)
 
440
        else:
 
441
            tlvs,rest = readTLVs(tlvs), None
 
442
        u = OSCARUser(name, warn, tlvs)
 
443
        if rest == None:
 
444
            return u
 
445
        else:
 
446
            return u, rest
 
447
 
 
448
    def oscar_01_05(self, snac, d = None):
 
449
        """
 
450
        data for a new service connection
 
451
        d might be a deferred to be called back when the service is ready
 
452
        """
 
453
        tlvs = readTLVs(snac[3][2:])
 
454
        service = struct.unpack('!H',tlvs[0x0d])[0]
 
455
        ip = tlvs[5]
 
456
        cookie = tlvs[6]
 
457
        #c = serviceClasses[service](self, cookie, d)
 
458
        c = protocol.ClientCreator(reactor, serviceClasses[service], self, cookie, d)
 
459
        def addService(x):
 
460
            self.services[service] = x
 
461
        c.connectTCP(ip, 5190).addCallback(addService)
 
462
        #self.services[service] = c
 
463
 
 
464
    def oscar_01_07(self,snac):
 
465
        """
 
466
        rate paramaters
 
467
        """
 
468
        self.sendSNACnr(0x01,0x08,"\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05") # ack
 
469
        self.initDone()
 
470
        self.sendSNACnr(0x13,0x02,'') # SSI rights info
 
471
        self.sendSNACnr(0x02,0x02,'') # location rights info
 
472
        self.sendSNACnr(0x03,0x02,'') # buddy list rights
 
473
        self.sendSNACnr(0x04,0x04,'') # ICBM parms
 
474
        self.sendSNACnr(0x09,0x02,'') # BOS rights
 
475
 
 
476
    def oscar_01_10(self,snac):
 
477
        """
 
478
        we've been warned
 
479
        """
 
480
        skip = struct.unpack('!H',snac[3][:2])[0]
 
481
        newLevel = struct.unpack('!H',snac[3][2+skip:4+skip])[0]/10
 
482
        if len(snac[3])>4+skip:
 
483
            by = self.parseUser(snac[3][4+skip:])
 
484
        else:
 
485
            by = None
 
486
        self.receiveWarning(newLevel, by)
 
487
 
 
488
    def oscar_01_13(self,snac):
 
489
        """
 
490
        MOTD
 
491
        """
 
492
        pass # we don't care for now
 
493
 
 
494
    def oscar_02_03(self, snac):
 
495
        """
 
496
        location rights response
 
497
        """
 
498
        tlvs = readTLVs(snac[3])
 
499
        self.maxProfileLength = tlvs[1]
 
500
 
 
501
    def oscar_03_03(self, snac):
 
502
        """
 
503
        buddy list rights response
 
504
        """
 
505
        tlvs = readTLVs(snac[3])
 
506
        self.maxBuddies = tlvs[1]
 
507
        self.maxWatchers = tlvs[2]
 
508
 
 
509
    def oscar_03_0B(self, snac):
 
510
        """
 
511
        buddy update
 
512
        """
 
513
        self.updateBuddy(self.parseUser(snac[3]))
 
514
 
 
515
    def oscar_03_0C(self, snac):
 
516
        """
 
517
        buddy offline
 
518
        """
 
519
        self.offlineBuddy(self.parseUser(snac[3]))
 
520
 
 
521
#    def oscar_04_03(self, snac):
 
522
 
 
523
    def oscar_04_05(self, snac):
 
524
        """
 
525
        ICBM parms response
 
526
        """
 
527
        self.sendSNACnr(0x04,0x02,'\x00\x00\x00\x00\x00\x0b\x1f@\x03\xe7\x03\xe7\x00\x00\x00\x00') # IM rights
 
528
 
 
529
    def oscar_04_07(self, snac):
 
530
        """
 
531
        ICBM message (instant message)
 
532
        """
 
533
        data = snac[3]
 
534
        cookie, data = data[:8], data[8:]
 
535
        channel = struct.unpack('!H',data[:2])[0]
 
536
        data = data[2:]
 
537
        user, data = self.parseUser(data, 1)
 
538
        tlvs = readTLVs(data)
 
539
        if channel == 1: # message
 
540
            flags = []
 
541
            multiparts = []
 
542
            for k, v in tlvs.items():
 
543
                if k == 2:
 
544
                    while v:
 
545
                        v = v[2:] # skip bad data
 
546
                        messageLength, charSet, charSubSet = struct.unpack('!3H', v[:6])
 
547
                        messageLength -= 4
 
548
                        message = [v[6:6+messageLength]]
 
549
                        if charSet == 0:
 
550
                            pass # don't add anything special
 
551
                        elif charSet == 2:
 
552
                            message.append('unicode')
 
553
                        elif charSet == 3:
 
554
                            message.append('iso-8859-1')
 
555
                        elif charSet == 0xffff:
 
556
                            message.append('none')
 
557
                        if charSubSet == 0xb:
 
558
                            message.append('macintosh')
 
559
                        if messageLength > 0: multiparts.append(tuple(message))
 
560
                        v = v[6+messageLength:]
 
561
                elif k == 3:
 
562
                    flags.append('acknowledge')
 
563
                elif k == 4:
 
564
                    flags.append('auto')
 
565
                elif k == 6:
 
566
                    flags.append('offline')
 
567
                elif k == 8:
 
568
                    iconLength, foo, iconSum, iconStamp = struct.unpack('!LHHL',v)
 
569
                    if iconLength:
 
570
                        flags.append('icon')
 
571
                        flags.append((iconLength, iconSum, iconStamp))
 
572
                elif k == 9:
 
573
                    flags.append('buddyrequest')
 
574
                elif k == 0xb: # unknown
 
575
                    pass
 
576
                elif k == 0x17:
 
577
                    flags.append('extradata')
 
578
                    flags.append(v)
 
579
                else:
 
580
                    log.msg('unknown TLV for incoming IM, %04x, %s' % (k,repr(v)))
 
581
 
 
582
#  unknown tlv for user SNewdorf
 
583
#  t: 29
 
584
#  v: '\x00\x00\x00\x05\x02\x01\xd2\x04r\x00\x01\x01\x10/\x8c\x8b\x8a\x1e\x94*\xbc\x80}\x8d\xc4;\x1dEM'
 
585
# XXX what is this?
 
586
            self.receiveMessage(user, multiparts, flags)
 
587
        elif channel == 2: # rondevouz
 
588
            status = struct.unpack('!H',tlvs[5][:2])[0]
 
589
            requestClass = tlvs[5][10:26]
 
590
            moreTLVs = readTLVs(tlvs[5][26:])
 
591
            if requestClass == CAP_CHAT: # a chat request
 
592
                exchange = struct.unpack('!H',moreTLVs[10001][:2])[0]
 
593
                name = moreTLVs[10001][3:-2]
 
594
                instance = struct.unpack('!H',moreTLVs[10001][-2:])[0]
 
595
                if not self.services.has_key(SERVICE_CHATNAV):
 
596
                    self.connectService(SERVICE_CHATNAV,1).addCallback(lambda x: self.services[SERVICE_CHATNAV].getChatInfo(exchange, name, instance).\
 
597
                        addCallback(self._cbGetChatInfoForInvite, user, moreTLVs[12]))
 
598
                else:
 
599
                    self.services[SERVICE_CHATNAV].getChatInfo(exchange, name, instance).\
 
600
                        addCallback(self._cbGetChatInfoForInvite, user, moreTLVs[12])
 
601
            elif requestClass == CAP_SEND_FILE:
 
602
                if moreTLVs.has_key(11): # cancel
 
603
                    log.msg('cancelled file request')
 
604
                    log.msg(status)
 
605
                    return # handle this later
 
606
                name = moreTLVs[10001][9:-7]
 
607
                desc = moreTLVs[12]
 
608
                log.msg('file request from %s, %s, %s' % (user, name, desc))
 
609
                self.receiveSendFileRequest(user, name, desc, cookie)
 
610
            else:
 
611
                log.msg('unsupported rondevouz: %s' % requestClass)
 
612
                log.msg(repr(moreTLVs))
 
613
        else:
 
614
            log.msg('unknown channel %02x' % channel)
 
615
            log.msg(tlvs)
 
616
 
 
617
    def _cbGetChatInfoForInvite(self, info, user, message):
 
618
        apply(self.receiveChatInvite, (user,message)+info)
 
619
 
 
620
    def oscar_09_03(self, snac):
 
621
        """
 
622
        BOS rights response
 
623
        """
 
624
        tlvs = readTLVs(snac[3])
 
625
        self.maxPermitList = tlvs[1]
 
626
        self.maxDenyList = tlvs[2]
 
627
 
 
628
    def oscar_0B_02(self, snac):
 
629
        """
 
630
        stats reporting interval
 
631
        """
 
632
        self.reportingInterval = struct.unpack('!H',snac[3][:2])[0]
 
633
 
 
634
    def oscar_13_03(self, snac):
 
635
        """
 
636
        SSI rights response
 
637
        """
 
638
        #tlvs = readTLVs(snac[3])
 
639
        pass # we don't know how to parse this
 
640
 
 
641
    # methods to be called by the client, and their support methods
 
642
    def requestSelfInfo(self):
 
643
        """
 
644
        ask for the OSCARUser for ourselves
 
645
        """
 
646
        d = defer.Deferred()
 
647
        self.sendSNAC(0x01, 0x0E, '').addCallback(self._cbRequestSelfInfo, d)
 
648
        return d
 
649
 
 
650
    def _cbRequestSelfInfo(self, snac, d):
 
651
        d.callback(self.parseUser(snac[5]))
 
652
 
 
653
    def initSSI(self):
 
654
        """
 
655
        this sends the rate request for family 0x13 (Server Side Information)
 
656
        so we can then use it
 
657
        """
 
658
        return self.sendSNAC(0x13, 0x02, '').addCallback(self._cbInitSSI)
 
659
 
 
660
    def _cbInitSSI(self, snac, d):
 
661
        return {} # don't even bother parsing this
 
662
 
 
663
    def requestSSI(self, timestamp = 0, revision = 0):
 
664
        """
 
665
        request the server side information
 
666
        if the deferred gets None, it means the SSI is the same
 
667
        """
 
668
        return self.sendSNAC(0x13, 0x05,
 
669
            struct.pack('!LH',timestamp,revision)).addCallback(self._cbRequestSSI)
 
670
 
 
671
    def _cbRequestSSI(self, snac, args = ()):
 
672
        if snac[1] == 0x0f: # same SSI as we have
 
673
            return
 
674
        itemdata = snac[5][3:]
 
675
        if args:
 
676
            revision, groups, permit, deny, permitMode, visibility = args
 
677
        else:
 
678
            version, revision = struct.unpack('!BH', snac[5][:3])
 
679
            groups = {}
 
680
            permit = []
 
681
            deny = []
 
682
            permitMode = None
 
683
            visibility = None
 
684
        while len(itemdata)>4:
 
685
            nameLength = struct.unpack('!H', itemdata[:2])[0]
 
686
            name = itemdata[2:2+nameLength]
 
687
            groupID, buddyID, itemType, restLength = \
 
688
                struct.unpack('!4H', itemdata[2+nameLength:10+nameLength])
 
689
            tlvs = readTLVs(itemdata[10+nameLength:10+nameLength+restLength])
 
690
            itemdata = itemdata[10+nameLength+restLength:]
 
691
            if itemType == 0: # buddies
 
692
                groups[groupID].addUser(buddyID, SSIBuddy(name, tlvs))
 
693
            elif itemType == 1: # group
 
694
                g = SSIGroup(name, tlvs)
 
695
                if groups.has_key(0): groups[0].addUser(groupID, g)
 
696
                groups[groupID] = g
 
697
            elif itemType == 2: # permit
 
698
                permit.append(name)
 
699
            elif itemType == 3: # deny
 
700
                deny.append(name)
 
701
            elif itemType == 4: # permit deny info
 
702
                if not tlvs.has_key(0xcb):
 
703
                    continue # this happens with ICQ
 
704
                permitMode = {1:'permitall',2:'denyall',3:'permitsome',4:'denysome',5:'permitbuddies'}[ord(tlvs[0xca])]
 
705
                visibility = {'\xff\xff\xff\xff':'all','\x00\x00\x00\x04':'notaim'}[tlvs[0xcb]]
 
706
            elif itemType == 5: # unknown (perhaps idle data)?
 
707
                pass
 
708
            else:
 
709
                log.msg('%s %s %s %s %s' % (name, groupID, buddyID, itemType, tlvs))
 
710
        timestamp = struct.unpack('!L',itemdata)[0]
 
711
        if not timestamp: # we've got more packets coming
 
712
            # which means add some deferred stuff
 
713
            d = defer.Deferred()
 
714
            self.requestCallbacks[snac[4]] = d
 
715
            d.addCallback(self._cbRequestSSI, (revision, groups, permit, deny, permitMode, visibility))
 
716
            return d
 
717
        return (groups[0].users,permit,deny,permitMode,visibility,timestamp,revision)
 
718
 
 
719
    def activateSSI(self):
 
720
        """
 
721
        active the data stored on the server (use buddy list, permit deny settings, etc.)
 
722
        """
 
723
        self.sendSNACnr(0x13,0x07,'')
 
724
 
 
725
    def startModifySSI(self):
 
726
        """
 
727
        tell the OSCAR server to be on the lookout for SSI modifications
 
728
        """
 
729
        self.sendSNACnr(0x13,0x11,'')
 
730
 
 
731
    def addItemSSI(self, item, groupID = None, buddyID = None):
 
732
        """
 
733
        add an item to the SSI server.  if buddyID == 0, then this should be a group.
 
734
        this gets a callback when it's finished, but you can probably ignore it.
 
735
        """
 
736
        if groupID is None:
 
737
            if isinstance(item, SSIGroup):
 
738
                groupID = 0
 
739
            else:
 
740
                groupID = item.group.group.findIDFor(item.group)
 
741
        if buddyID is None:
 
742
            buddyID = item.group.findIDFor(item)
 
743
        return self.sendSNAC(0x13,0x08, item.oscarRep(groupID, buddyID))
 
744
 
 
745
    def modifyItemSSI(self, item, groupID = None, buddyID = None):
 
746
        if groupID is None:
 
747
            if isinstance(item, SSIGroup):
 
748
                groupID = 0
 
749
            else:
 
750
                groupID = item.group.group.findIDFor(item.group)
 
751
        if buddyID is None:
 
752
            buddyID = item.group.findIDFor(item)
 
753
        return self.sendSNAC(0x13,0x09, item.oscarRep(groupID, buddyID))
 
754
 
 
755
    def delItemSSI(self, item, groupID = None, buddyID = None):
 
756
        if groupID is None:
 
757
            if isinstance(item, SSIGroup):
 
758
                groupID = 0
 
759
            else:
 
760
                groupID = item.group.group.findIDFor(item.group)
 
761
        if buddyID is None:
 
762
            buddyID = item.group.findIDFor(item)
 
763
        return self.sendSNAC(0x13,0x0A, item.oscarRep(groupID, buddyID))
 
764
 
 
765
    def endModifySSI(self):
 
766
        self.sendSNACnr(0x13,0x12,'')
 
767
 
 
768
    def setProfile(self, profile):
 
769
        """
 
770
        set the profile.
 
771
        send None to not set a profile (different from '' for a blank one)
 
772
        """
 
773
        self.profile = profile
 
774
        tlvs = ''
 
775
        if self.profile is not None:
 
776
            tlvs =  TLV(1,'text/aolrtf; charset="us-ascii"') + \
 
777
                    TLV(2,self.profile)
 
778
 
 
779
        tlvs = tlvs + TLV(5, ''.join(self.capabilities))
 
780
        self.sendSNACnr(0x02, 0x04, tlvs)
 
781
 
 
782
    def setAway(self, away = None):
 
783
        """
 
784
        set the away message, or return (if away == None)
 
785
        """
 
786
        self.awayMessage = away
 
787
        tlvs = TLV(3,'text/aolrtf; charset="us-ascii"') + \
 
788
               TLV(4,away or '')
 
789
        self.sendSNACnr(0x02, 0x04, tlvs)
 
790
 
 
791
    def setIdleTime(self, idleTime):
 
792
        """
 
793
        set our idle time.  don't call more than once with a non-0 idle time.
 
794
        """
 
795
        self.sendSNACnr(0x01, 0x11, struct.pack('!L',idleTime))
 
796
 
 
797
    def sendMessage(self, user, message, wantAck = 0, autoResponse = 0, offline = 0 ):  \
 
798
                    #haveIcon = 0, ):
 
799
        """
 
800
        send a message to user (not an OSCARUseR).
 
801
        message can be a string, or a multipart tuple.
 
802
        if wantAck, we return a Deferred that gets a callback when the message is sent.
 
803
        if autoResponse, this message is an autoResponse, as if from an away message.
 
804
        if offline, this is an offline message (ICQ only, I think)
 
805
        """
 
806
        data = ''.join([chr(random.randrange(0, 127)) for i in range(8)]) # cookie
 
807
        data = data + '\x00\x01' + chr(len(user)) + user
 
808
        if not type(message) in (types.TupleType, types.ListType):
 
809
            message = [[message,]]
 
810
            if type(message[0][0]) == types.UnicodeType:
 
811
                message[0].append('unicode')
 
812
        messageData = ''
 
813
        for part in message:
 
814
            charSet = 0
 
815
            if 'unicode' in part[1:]:
 
816
                charSet = 2
 
817
                part[0] = part[0].encode('utf-8')
 
818
            elif 'iso-8859-1' in part[1:]:
 
819
                charSet = 3
 
820
                part[0] = part[0].encode('iso-8859-1')
 
821
            elif 'none' in part[1:]:
 
822
                charSet = 0xffff
 
823
            if 'macintosh' in part[1:]:
 
824
                charSubSet = 0xb
 
825
            else:
 
826
                charSubSet = 0
 
827
            messageData = messageData + '\x01\x01' + \
 
828
                          struct.pack('!3H',len(part[0])+4,charSet,charSubSet)
 
829
            messageData = messageData + part[0]
 
830
        data = data + TLV(2, '\x05\x01\x00\x03\x01\x01\x02'+messageData)
 
831
        if wantAck:
 
832
            data = data + TLV(3,'')
 
833
        if autoResponse:
 
834
            data = data + TLV(4,'')
 
835
        if offline:
 
836
            data = data + TLV(6,'')
 
837
        if wantAck:
 
838
            return self.sendSNAC(0x04, 0x06, data).addCallback(self._cbSendMessageAck, user, message)
 
839
        self.sendSNACnr(0x04, 0x06, data)
 
840
 
 
841
    def _cbSendMessageAck(self, snac, user, message):
 
842
        return user, message
 
843
 
 
844
    def connectService(self, service, wantCallback = 0, extraData = ''):
 
845
        """
 
846
        connect to another service
 
847
        if wantCallback, we return a Deferred that gets called back when the service is online.
 
848
        if extraData, append that to our request.
 
849
        """
 
850
        if wantCallback:
 
851
            d = defer.Deferred()
 
852
            self.sendSNAC(0x01,0x04,struct.pack('!H',service) + extraData).addCallback(self._cbConnectService, d)
 
853
            return d
 
854
        else:
 
855
            self.sendSNACnr(0x01,0x04,struct.pack('!H',service))
 
856
 
 
857
    def _cbConnectService(self, snac, d):
 
858
        self.oscar_01_05(snac[2:], d)
 
859
 
 
860
    def createChat(self, shortName):
 
861
        """
 
862
        create a chat room
 
863
        """
 
864
        if self.services.has_key(SERVICE_CHATNAV):
 
865
            return self.services[SERVICE_CHATNAV].createChat(shortName)
 
866
        else:
 
867
            return self.connectService(SERVICE_CHATNAV,1).addCallback(lambda s: s.createChat(shortName))
 
868
 
 
869
 
 
870
    def joinChat(self, exchange, fullName, instance):
 
871
        """
 
872
        join a chat room
 
873
        """
 
874
        #d = defer.Deferred()
 
875
        return self.connectService(0x0e, 1, TLV(0x01, struct.pack('!HB',exchange, len(fullName)) + fullName +
 
876
                          struct.pack('!H', instance))).addCallback(self._cbJoinChat) #, d)
 
877
        #return d
 
878
 
 
879
    def _cbJoinChat(self, chat):
 
880
        del self.services[SERVICE_CHAT]
 
881
        return chat
 
882
 
 
883
    def warnUser(self, user, anon = 0):
 
884
        return self.sendSNAC(0x04, 0x08, '\x00'+chr(anon)+chr(len(user))+user).addCallback(self._cbWarnUser)
 
885
 
 
886
    def _cbWarnUser(self, snac):
 
887
        oldLevel, newLevel = struct.unpack('!2H', snac[5])
 
888
        return oldLevel, newLevel
 
889
 
 
890
    def getInfo(self, user):
 
891
        #if user.
 
892
        return self.sendSNAC(0x02, 0x05, '\x00\x01'+chr(len(user))+user).addCallback(self._cbGetInfo)
 
893
 
 
894
    def _cbGetInfo(self, snac):
 
895
        user, rest = self.parseUser(snac[5],1)
 
896
        tlvs = readTLVs(rest)
 
897
        return tlvs.get(0x02,None)
 
898
 
 
899
    def getAway(self, user):
 
900
        return self.sendSNAC(0x02, 0x05, '\x00\x03'+chr(len(user))+user).addCallback(self._cbGetAway)
 
901
 
 
902
    def _cbGetAway(self, snac):
 
903
        user, rest = self.parseUser(snac[5],1)
 
904
        tlvs = readTLVs(rest)
 
905
        return tlvs.get(0x04,None) # return None if there is no away message
 
906
 
 
907
    #def acceptSendFileRequest(self,
 
908
 
 
909
    # methods to be overriden by the client
 
910
    def initDone(self):
 
911
        """
 
912
        called when we get the rate information, which means we should do other init. stuff.
 
913
        """
 
914
        log.msg('%s initDone' % self)
 
915
        pass
 
916
 
 
917
    def updateBuddy(self, user):
 
918
        """
 
919
        called when a buddy changes status, with the OSCARUser for that buddy.
 
920
        """
 
921
        log.msg('%s updateBuddy %s' % (self, user))
 
922
        pass
 
923
 
 
924
    def offlineBuddy(self, user):
 
925
        """
 
926
        called when a buddy goes offline
 
927
        """
 
928
        log.msg('%s offlineBuddy %s' % (self, user))
 
929
        pass
 
930
 
 
931
    def receiveMessage(self, user, multiparts, flags):
 
932
        """
 
933
        called when someone sends us a message
 
934
        """
 
935
        pass
 
936
 
 
937
    def receiveWarning(self, newLevel, user):
 
938
        """
 
939
        called when someone warns us.
 
940
        user is either None (if it was anonymous) or an OSCARUser
 
941
        """
 
942
        pass
 
943
 
 
944
    def receiveChatInvite(self, user, message, exchange, fullName, instance, shortName, inviteTime):
 
945
        """
 
946
        called when someone invites us to a chat room
 
947
        """
 
948
        pass
 
949
 
 
950
    def chatReceiveMessage(self, chat, user, message):
 
951
        """
 
952
        called when someone in a chatroom sends us a message in the chat
 
953
        """
 
954
        pass
 
955
 
 
956
    def chatMemberJoined(self, chat, member):
 
957
        """
 
958
        called when a member joins the chat
 
959
        """
 
960
        pass
 
961
 
 
962
    def chatMemberLeft(self, chat, member):
 
963
        """
 
964
        called when a member leaves the chat
 
965
        """
 
966
        pass
 
967
 
 
968
    def receiveSendFileRequest(self, user, file, description, cookie):
 
969
        """
 
970
        called when someone tries to send a file to us
 
971
        """
 
972
        pass
 
973
 
 
974
class OSCARService(SNACBased):
 
975
    def __init__(self, bos, cookie, d = None):
 
976
        SNACBased.__init__(self, cookie)
 
977
        self.bos = bos
 
978
        self.d = d
 
979
 
 
980
    def connectionLost(self, reason):
 
981
        for k,v in self.bos.services.items():
 
982
            if v == self:
 
983
                del self.bos.services[k]
 
984
                return
 
985
 
 
986
    def clientReady(self):
 
987
        SNACBased.clientReady(self)
 
988
        if self.d:
 
989
            self.d.callback(self)
 
990
            self.d = None
 
991
 
 
992
class ChatNavService(OSCARService):
 
993
    snacFamilies = {
 
994
        0x01:(3, 0x0010, 0x059b),
 
995
        0x0d:(1, 0x0010, 0x059b)
 
996
    }
 
997
    def oscar_01_07(self, snac):
 
998
        # rate info
 
999
        self.sendSNACnr(0x01, 0x08, '\000\001\000\002\000\003\000\004\000\005')
 
1000
        self.sendSNACnr(0x0d, 0x02, '')
 
1001
 
 
1002
    def oscar_0D_09(self, snac):
 
1003
        self.clientReady()
 
1004
 
 
1005
    def getChatInfo(self, exchange, name, instance):
 
1006
        d = defer.Deferred()
 
1007
        self.sendSNAC(0x0d,0x04,struct.pack('!HB',exchange,len(name)) + \
 
1008
                      name + struct.pack('!HB',instance,2)). \
 
1009
            addCallback(self._cbGetChatInfo, d)
 
1010
        return d
 
1011
 
 
1012
    def _cbGetChatInfo(self, snac, d):
 
1013
        data = snac[5][4:]
 
1014
        exchange, length = struct.unpack('!HB',data[:3])
 
1015
        fullName = data[3:3+length]
 
1016
        instance = struct.unpack('!H',data[3+length:5+length])[0]
 
1017
        tlvs = readTLVs(data[8+length:])
 
1018
        shortName = tlvs[0x6a]
 
1019
        inviteTime = struct.unpack('!L',tlvs[0xca])[0]
 
1020
        info = (exchange,fullName,instance,shortName,inviteTime)
 
1021
        d.callback(info)
 
1022
 
 
1023
    def createChat(self, shortName):
 
1024
        #d = defer.Deferred()
 
1025
        data = '\x00\x04\x06create\xff\xff\x01\x00\x03'
 
1026
        data = data + TLV(0xd7, 'en')
 
1027
        data = data + TLV(0xd6, 'us-ascii')
 
1028
        data = data + TLV(0xd3, shortName)
 
1029
        return self.sendSNAC(0x0d, 0x08, data).addCallback(self._cbCreateChat)
 
1030
        #return d
 
1031
 
 
1032
    def _cbCreateChat(self, snac): #d):
 
1033
        exchange, length = struct.unpack('!HB',snac[5][4:7])
 
1034
        fullName = snac[5][7:7+length]
 
1035
        instance = struct.unpack('!H',snac[5][7+length:9+length])[0]
 
1036
        #d.callback((exchange, fullName, instance))
 
1037
        return exchange, fullName, instance
 
1038
 
 
1039
class ChatService(OSCARService):
 
1040
    snacFamilies = {
 
1041
        0x01:(3, 0x0010, 0x059b),
 
1042
        0x0E:(1, 0x0010, 0x059b)
 
1043
    }
 
1044
    def __init__(self,bos,cookie, d = None):
 
1045
        OSCARService.__init__(self,bos,cookie,d)
 
1046
        self.exchange = None
 
1047
        self.fullName = None
 
1048
        self.instance = None
 
1049
        self.name = None
 
1050
        self.members = None
 
1051
 
 
1052
    clientReady = SNACBased.clientReady # we'll do our own callback
 
1053
 
 
1054
    def oscar_01_07(self,snac):
 
1055
        self.sendSNAC(0x01,0x08,"\000\001\000\002\000\003\000\004\000\005")
 
1056
        self.clientReady()
 
1057
 
 
1058
    def oscar_0E_02(self, snac):
 
1059
#        try: # this is EVIL
 
1060
#            data = snac[3][4:]
 
1061
#            self.exchange, length = struct.unpack('!HB',data[:3])
 
1062
#            self.fullName = data[3:3+length]
 
1063
#            self.instance = struct.unpack('!H',data[3+length:5+length])[0]
 
1064
#            tlvs = readTLVs(data[8+length:])
 
1065
#            self.name = tlvs[0xd3]
 
1066
#            self.d.callback(self)
 
1067
#        except KeyError:
 
1068
        data = snac[3]
 
1069
        self.exchange, length = struct.unpack('!HB',data[:3])
 
1070
        self.fullName = data[3:3+length]
 
1071
        self.instance = struct.unpack('!H',data[3+length:5+length])[0]
 
1072
        tlvs = readTLVs(data[8+length:])
 
1073
        self.name = tlvs[0xd3]
 
1074
        self.d.callback(self)
 
1075
 
 
1076
    def oscar_0E_03(self,snac):
 
1077
        users=[]
 
1078
        rest=snac[3]
 
1079
        while rest:
 
1080
            user, rest = self.bos.parseUser(rest, 1)
 
1081
            users.append(user)
 
1082
        if not self.fullName:
 
1083
            self.members = users
 
1084
        else:
 
1085
            self.members.append(users[0])
 
1086
            self.bos.chatMemberJoined(self,users[0])
 
1087
 
 
1088
    def oscar_0E_04(self,snac):
 
1089
        user=self.bos.parseUser(snac[3])
 
1090
        for u in self.members:
 
1091
            if u.name == user.name: # same person!
 
1092
                self.members.remove(u)
 
1093
        self.bos.chatMemberLeft(self,user)
 
1094
 
 
1095
    def oscar_0E_06(self,snac):
 
1096
        data = snac[3]
 
1097
        user,rest=self.bos.parseUser(snac[3][14:],1)
 
1098
        tlvs = readTLVs(rest[8:])
 
1099
        message=tlvs[1]
 
1100
        self.bos.chatReceiveMessage(self,user,message)
 
1101
 
 
1102
    def sendMessage(self,message):
 
1103
        tlvs=TLV(0x02,"us-ascii")+TLV(0x03,"en")+TLV(0x01,message)
 
1104
        self.sendSNAC(0x0e,0x05,
 
1105
                      "\x46\x30\x38\x30\x44\x00\x63\x00\x00\x03\x00\x01\x00\x00\x00\x06\x00\x00\x00\x05"+
 
1106
                      struct.pack("!H",len(tlvs))+
 
1107
                      tlvs)
 
1108
 
 
1109
    def leaveChat(self):
 
1110
        self.disconnect()
 
1111
 
 
1112
class OscarAuthenticator(OscarConnection):
 
1113
    BOSClass = BOSConnection
 
1114
    def __init__(self,username,password,deferred=None,icq=0):
 
1115
        self.username=username
 
1116
        self.password=password
 
1117
        self.deferred=deferred
 
1118
        self.icq=icq # icq mode is disabled
 
1119
        #if icq and self.BOSClass==BOSConnection:
 
1120
        #    self.BOSClass=ICQConnection
 
1121
 
 
1122
    def oscar_(self,flap):
 
1123
        if not self.icq:
 
1124
            self.sendFLAP("\000\000\000\001", 0x01)
 
1125
            self.sendFLAP(SNAC(0x17,0x06,0,
 
1126
                               TLV(TLV_USERNAME,self.username)+
 
1127
                               TLV(0x004B,'')))
 
1128
            self.state="Key"
 
1129
        else:
 
1130
            encpass=encryptPasswordICQ(self.password)
 
1131
            self.sendFLAP('\000\000\000\001'+
 
1132
                          TLV(0x01,self.username)+
 
1133
                          TLV(0x02,encpass)+
 
1134
                          TLV(0x03,'ICQ Inc. - Product of ICQ (TM).2001b.5.18.1.3659.85')+
 
1135
                          TLV(0x16,"\x01\x0a")+
 
1136
                          TLV(0x17,"\x00\x05")+
 
1137
                          TLV(0x18,"\x00\x12")+
 
1138
                          TLV(0x19,"\000\001")+
 
1139
                          TLV(0x1a,"\x0eK")+
 
1140
                          TLV(0x14,"\x00\x00\x00U")+
 
1141
                          TLV(0x0f,"en")+
 
1142
                          TLV(0x0e,"us"),0x01)
 
1143
            self.state="Cookie"
 
1144
 
 
1145
    def oscar_Key(self,data):
 
1146
        snac=readSNAC(data[1])
 
1147
        key=snac[5][2:]
 
1148
        encpass=encryptPasswordMD5(self.password,key)
 
1149
        self.sendFLAP(SNAC(0x17,0x02,0,
 
1150
                           TLV(TLV_USERNAME,self.username)+
 
1151
                           TLV(TLV_PASSWORD,encpass)+
 
1152
                           TLV(0x004C, '')+ # unknown
 
1153
                           TLV(TLV_CLIENTNAME,"AOL Instant Messenger (SM), version 4.8.2790/WIN32")+
 
1154
                           TLV(0x0016,"\x01\x09")+
 
1155
                           TLV(TLV_CLIENTMAJOR,"\000\004")+
 
1156
                           TLV(TLV_CLIENTMINOR,"\000\010")+
 
1157
                           TLV(0x0019,"\000\000")+
 
1158
                           TLV(TLV_CLIENTSUB,"\x0A\xE6")+
 
1159
                           TLV(0x0014,"\x00\x00\x00\xBB")+
 
1160
                           TLV(TLV_LANG,"en")+
 
1161
                           TLV(TLV_COUNTRY,"us")+
 
1162
                           TLV(TLV_USESSI,"\001")))
 
1163
        return "Cookie"
 
1164
 
 
1165
    def oscar_Cookie(self,data):
 
1166
        snac=readSNAC(data[1])
 
1167
        if self.icq:
 
1168
            i=snac[5].find("\000")
 
1169
            snac[5]=snac[5][i:]
 
1170
        tlvs=readTLVs(snac[5])
 
1171
        if tlvs.has_key(6):
 
1172
            self.cookie=tlvs[6]
 
1173
            server,port=string.split(tlvs[5],":")
 
1174
            d = self.connectToBOS(server, int(port))
 
1175
            d.addErrback(lambda x: log.msg("Connection Failed! Reason: %s" % x))
 
1176
            if self.deferred:
 
1177
                d.chainDeferred(self.deferred)
 
1178
            self.disconnect()
 
1179
        elif tlvs.has_key(8):
 
1180
            errorcode=tlvs[8]
 
1181
            errorurl=tlvs[4]
 
1182
            if errorcode=='\000\030':
 
1183
                error="You are attempting to sign on again too soon.  Please try again later."
 
1184
            elif errorcode=='\000\005':
 
1185
                error="Invalid Username or Password."
 
1186
            else: error=repr(errorcode)
 
1187
            self.error(error,errorurl)
 
1188
        else:
 
1189
            log.msg('hmm, weird tlvs for %s cookie packet' % str(self))
 
1190
            log.msg(tlvs)
 
1191
            log.msg('snac')
 
1192
            log.msg(str(snac))
 
1193
        return "None"
 
1194
 
 
1195
    def oscar_None(self,data): pass
 
1196
 
 
1197
    def connectToBOS(self, server, port):
 
1198
        c = protocol.ClientCreator(reactor, self.BOSClass, self.username, self.cookie)
 
1199
        return c.connectTCP(server, int(port))
 
1200
 
 
1201
    def error(self,error,url):
 
1202
        log.msg("ERROR! %s %s" % (error,url))
 
1203
        if self.deferred: self.deferred.errback((error,url))
 
1204
        self.transport.loseConnection()
 
1205
 
 
1206
FLAP_CHANNEL_NEW_CONNECTION = 0x01
 
1207
FLAP_CHANNEL_DATA = 0x02
 
1208
FLAP_CHANNEL_ERROR = 0x03
 
1209
FLAP_CHANNEL_CLOSE_CONNECTION = 0x04
 
1210
 
 
1211
SERVICE_CHATNAV = 0x0d
 
1212
SERVICE_CHAT = 0x0e
 
1213
serviceClasses = {
 
1214
    SERVICE_CHATNAV:ChatNavService,
 
1215
    SERVICE_CHAT:ChatService
 
1216
}
 
1217
TLV_USERNAME = 0x0001
 
1218
TLV_CLIENTNAME = 0x0003
 
1219
TLV_COUNTRY = 0x000E
 
1220
TLV_LANG = 0x000F
 
1221
TLV_CLIENTMAJOR = 0x0017
 
1222
TLV_CLIENTMINOR = 0x0018
 
1223
TLV_CLIENTSUB = 0x001A
 
1224
TLV_PASSWORD = 0x0025
 
1225
TLV_USESSI = 0x004A
 
1226
 
 
1227
CAP_ICON = '\011F\023FL\177\021\321\202"DEST\000\000'
 
1228
CAP_VOICE = '\011F\023AL\177\021\321\202"DEST\000\000'
 
1229
CAP_IMAGE = '\011F\023EL\177\021\321\202"DEST\000\000'
 
1230
CAP_CHAT = 't\217$ b\207\021\321\202"DEST\000\000'
 
1231
CAP_GET_FILE = '\011F\023HL\177\021\321\202"DEST\000\000'
 
1232
CAP_SEND_FILE = '\011F\023CL\177\021\321\202"DEST\000\000'
 
1233
CAP_GAMES = '\011F\023GL\177\021\321\202"DEST\000\000'
 
1234
CAP_SEND_LIST = '\011F\023KL\177\021\321\202"DEST\000\000'
 
1235
CAP_SERV_REL = '\011F\023IL\177\021\321\202"DEST\000\000'