]> code.delx.au - pymsnt/blob - src/legacy/msnw.py
File transfer nearly working...
[pymsnt] / src / legacy / msnw.py
1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
3
4 from twisted.internet import reactor
5 from twisted.internet.protocol import ClientFactory
6 from twisted.python import log
7 from debug import LogEvent, INFO, WARN, ERROR
8 from tlib import msn
9 import math
10 import base64
11 import binascii
12 import math
13 import config
14 import utils
15 import lang
16
17 MAXMESSAGESIZE = 1400
18
19
20 class MSNConnection:
21 """ Manages all the Twisted factories, etc """
22 def __init__(self, username, password):
23 self.username = username
24 self.password = password
25 self.inited = False
26 self.tries = 0
27 self.initMe()
28 LogEvent(INFO, self.session.jabberID)
29
30 def initMe(self):
31 if self.inited:
32 MSNConnection.removeMe(self)
33
34 self.switchboardSessions = {}
35 dispatchFactory = DispatchFactory(self)
36 reactor.connectTCP('messenger.hotmail.com', 1863, dispatchFactory)
37 self.notificationFactory = msn.NotificationFactory()
38 self.notificationFactory.userHandle = self.username
39 self.notificationFactory.password = self.password
40 self.notificationFactory.msncon = self
41 self.notificationFactory.protocol = Notification
42 self.notificationFactory.initialListVersion = self.initialListVersion
43 self.notificationProtocol = None
44
45 self.savedEvents = SavedEvents()
46
47 self.inited = True
48
49 LogEvent(INFO, self.session.jabberID)
50
51 def removeMe(self):
52 LogEvent(INFO, self.session.jabberID)
53 if self.notificationProtocol:
54 self.notificationProtocol.removeMe()
55 if self.notificationFactory:
56 self.notificationFactory.msncon = None
57 self.notificationFactory = None
58 self.notificationProtocol = None
59 self.savedEvents = SavedEvents()
60 for userHandle in self.switchboardSessions.copy():
61 self.switchboardSessions[userHandle].removeMe()
62 self.switchboardSessions = {}
63
64 def resourceOffline(self, offlineResource):
65 for contact in self.switchboardSessions.keys():
66 if self.switchboardSessions[contact].resource == offlineResource:
67 self.switchboardSessions[contact].resource = self.highestResource()
68
69 def getContacts(self):
70 if self.notificationFactory:
71 return self.notificationFactory.contacts
72 else:
73 return None
74
75
76 def sendMessage(self, remoteUser, resource, text, noerror):
77 LogEvent(INFO, self.session.jabberID)
78 if self.notificationProtocol:
79 if not self.switchboardSessions.has_key(remoteUser):
80 self.switchboardSessions[remoteUser] = SwitchboardSession(self, remoteUser, resource)
81 self.switchboardSessions[remoteUser].resource = resource
82 self.switchboardSessions[remoteUser].sendMessage(text.replace("\n", "\r\n"), noerror)
83 elif not noerror:
84 self.failedMessage(remoteUser, text)
85
86 def requestAvatar(self, userHandle):
87 LogEvent(INFO, self.session.jabberID)
88 resource = self.session.highestResource()
89 if config.getAllAvatars:
90 if not self.switchboardSessions.has_key(userHandle):
91 self.switchboardSessions[userHandle] = SwitchboardSession(self, userHandle, resource)
92 else:
93 self.switchboardSessions[userHandle].requestAvatar()
94 else:
95 if self.switchboardSessions.has_key(userHandle): # Only request avatars for open switchboards
96 self.switchboardSessions[userHandle].requestAvatar()
97
98 def sendTypingToContact(self, remoteUser):
99 if self.switchboardSessions.has_key(remoteUser):
100 self.switchboardSessions[remoteUser].sendTypingNofication()
101
102 def notificationProtocolReady(self, notificationProtocol):
103 self.notificationProtocol = notificationProtocol
104 self.loggedIn()
105 self.tries = 0
106
107 def sendSavedEvents(self):
108 # Hack for events sent before we're logged in
109 self.savedEvents.send(self)
110 self.savedEvents = None
111
112 def changeAvatar(self, imageData):
113 if self.notificationProtocol:
114 self.notificationProtocol.changeAvatar(imageData, push=True)
115 else:
116 self.savedEvents.avatarImageData = imageData
117
118 def changeStatus(self, statusCode, screenName, personal):
119 if self.notificationProtocol:
120 def cb1(arg):
121 self.ourStatusChanged(arg[0])
122 def cb2(arg):
123 self.ourNickChanged(arg[0])
124 def cb3(arg):
125 self.ourPersonalChanged(personal)
126 LogEvent(INFO, self.session.jabberID)
127 if statusCode:
128 statusCode = str(statusCode.encode("utf-8"))
129 self.notificationProtocol.changeStatus(statusCode).addCallback(cb1)
130 if screenName:
131 screenName = str(screenName.encode("utf-8"))
132 self.notificationProtocol.changeScreenName(screenName).addCallback(cb2)
133 if personal:
134 personal = str(personal.encode("utf-8"))
135 else:
136 personal = ""
137 self.notificationProtocol.changePersonalMessage(personal).addCallback(cb3)
138 else:
139 self.savedEvents.statusCode = statusCode
140 self.savedEvents.screenName = screenName
141 self.savedEvents.personal = personal
142
143 def connectionLostBase(self, reason):
144 # Attempts to reconnect
145 if self.tries < 5 and self.session:
146 reactor.callLater(2 ** self.tries, self.initMe)
147 self.tries += 1
148 else:
149 self.connectionLost(self)
150
151 def addContact(self, listType, userHandle):
152 if self.notificationProtocol:
153 return self.notificationProtocol.addContact(listType, str(userHandle))
154 else:
155 self.savedEvents.addContacts.append((listType, str(userHandle)))
156
157 def remContact(self, listType, userHandle, groupID=0):
158 if self.notificationProtocol:
159 return self.notificationProtocol.remContact(listType, str(userHandle))
160 else:
161 self.savedEvents.remContacts.append((listType, str(userHandle)))
162
163
164
165 def initialEmailNotification(self, inboxunread, foldersunread):
166 pass
167
168 def realtimeEmailNotification(self, mailfrom, fromaddr, subject):
169 pass
170
171 def loggedIn(self):
172 pass
173
174 def loginFailure(self, message):
175 pass
176
177 def multipleLogin(self):
178 pass
179
180 def gotMessage(self, remoteUser, resource, text):
181 pass
182
183 def avatarHashChanged(self, userHandle, hash):
184 pass
185
186 def gotAvatarImage(self, to, image):
187 pass
188
189 def gotSendRequest(self, fileReceive):
190 pass
191
192 def listSynchronized(self):
193 pass
194
195 def contactStatusChanged(self, remoteUser):
196 pass
197
198 def ourStatusChanged(self, statusCode):
199 pass
200
201 def ourNickChanged(self, nick):
202 pass
203
204 def ourPersonalChanged(self, personal):
205 pass
206
207 def userMapping(self, passport, jid):
208 pass
209
210 def gotContactTyping(self, remoteUser, resource):
211 pass
212
213 def serverGoingDown(self):
214 pass
215
216 def accountNotVerified(self):
217 pass
218
219 def userAddedMe(self, userHandle):
220 pass
221
222 def userRemovedMe(self, userHandle):
223 pass
224
225 def failedMessage(self, remoteUser, message):
226 pass
227
228 def connectionLost(self):
229 pass
230
231
232
233 class SavedEvents:
234 def __init__(self):
235 self.nickname = ""
236 self.statusCode = ""
237 self.personal = ""
238 self.avatarImageData = ""
239 self.addContacts = []
240 self.remContacts = []
241
242 def send(self, msncon):
243 if self.avatarImageData:
244 msncon.notificationProtocol.changeAvatar(self.avatarImageData, push=False)
245 if self.nickname or self.statusCode or self.personal:
246 msncon.changeStatus(self.statusCode, self.nickname, self.personal)
247 for listType, userHandle in self.addContacts:
248 msncon.addContact(listType, userHandle)
249 for listType, userHandle in self.remContacts:
250 msncon.remContact(listType, userHandle)
251
252
253
254 def switchToGroupchat(switchboardSession, user1, user2):
255 gcsbs = GroupchatSwitchboardSession()
256 from glue import LegacyGroupchat, msn2jid
257 groupchat = LegacyGroupchat(session=switchboardSession.msncon.session, resource=None, existing=True, switchboardSession=gcsbs)
258 gcsbs.groupchat = groupchat
259 gcsbs.msncon = switchboardSession.msncon
260 gcsbs.switchboard = switchboardSession.switchboard
261 gcsbs.switchboard.switchboardSession = gcsbs
262 gcsbs.ready = True
263 gcsbs.userJoined(user1)
264 gcsbs.userJoined(user2)
265 groupchat.sendUserInvite(msn2jid(switchboardSession.remoteUser))
266 switchboardSession.removeMe(False)
267 LogEvent(INFO, gcsbs.msncon.session.jabberID)
268 return gcsbs
269
270
271 class SwitchboardSessionBase:
272 def sendMessage(self, message, noerror):
273 if self.ready:
274 def failedMessage(ignored):
275 if self.__class__ == GroupchatSwitchboardSession:
276 tempmsncon.failedMessage(self.groupchat.roomJID(), message)
277 else:
278 tempmsncon.failedMessage(self.remoteUser, message)
279
280 tempmsncon = self.msncon # In case MSN tells us the message failed after removeMe()
281
282 LogEvent(INFO, self.ident)
283 message = str(message.encode("utf-8"))
284
285 if len(message) < MAXMESSAGESIZE:
286 msnmessage = msn.MSNMessage(message=message)
287 msnmessage.setHeader("Content-Type", "text/plain; charset=UTF-8")
288 msnmessage.ack = msn.MSNMessage.MESSAGE_NACK
289
290 d = self.switchboard.sendMessage(msnmessage)
291 if not noerror:
292 d.addCallback(failedMessage)
293 else:
294 chunks = int(math.ceil(len(message) / float(MAXMESSAGESIZE)))
295 chunk = 0
296 guid = utils.random_guid()
297 while chunk < chunks:
298 offset = chunk * MAXMESSAGESIZE
299 text = message[offset : offset + MAXMESSAGESIZE]
300
301 msnmessage = msn.MSNMessage(message=text)
302 msnmessage.setHeader("Message-ID", guid)
303 if chunk == 0:
304 msnmessage.setHeader("Content-Type", "text/plain; charset=UTF-8")
305 msnmessage.setHeader("Chunks", str(chunks))
306 else:
307 msnmessage.setHeader("Chunk", str(chunk))
308 msnmessage.ack = msn.MSNMessage.MESSAGE_NACK
309
310 d = self.switchboard.sendMessage(msnmessage)
311 if not noerror:
312 d.addCallback(failedMessage)
313 chunk += 1
314
315 self.resetTimer()
316 else:
317 self.messageBuffer.append((message, noerror))
318
319 def gotAvatarImage(self, to, image):
320 self.msncon.gotAvatarImage(to, image)
321
322 def gotSendRequest(self, fileReceive):
323 self.msncon.gotSendRequest(fileReceive)
324
325 def switchboardReady(self, switchboard):
326 LogEvent(INFO, self.ident)
327 self.ready = True
328 self.switchboard = switchboard
329 self.flushBuffer()
330
331 def resetTimer(self):
332 pass
333
334
335 class GroupchatSwitchboardSession(SwitchboardSessionBase):
336 def __init__(self, groupchat=None, makeSwitchboard=False):
337 self.removed = False
338
339 self.msncon = None
340 if groupchat:
341 self.ident = groupchat.roomJID()
342 self.groupchat = groupchat
343 self.msncon = self.groupchat.session.legacycon
344 else:
345 self.ident = str(self)
346 self.groupchat = None
347 self.switchboard = None
348 self.ready = False
349 self.messageBuffer = []
350 self.invitedUsers = []
351 self.oneUserHasJoined = False
352
353 if makeSwitchboard and groupchat:
354 LogEvent(INFO, self.ident, "Requesting switchboard.")
355 d = self.msncon.notificationProtocol.requestSwitchboardServer()
356 d.addCallback(self.sbRequestAccepted)
357 d.addErrback(self.removeMe)
358
359 if self.msncon:
360 LogEvent(INFO, self.ident, "Created groupchat for " + self.msncon.username)
361
362 def removeMe(self):
363 if self.removed:
364 log.err("removeMe called more than once!")
365 return
366 self.removed = True
367
368 LogEvent(INFO, self.ident)
369 self.msncon = None
370 if self.switchboard:
371 self.switchboard.removeMe()
372 self.switchboard = None
373 self.groupchat = None
374 self.ready = False
375
376 utils.mutilateMe(self)
377
378 def sbRequestAccepted(self, (host, port, key)):
379 # Connect to the switchboard server
380 LogEvent(INFO, self.ident)
381 reactor.connectTCP(host, port, SwitchboardFactory(self, key))
382
383 def sendMessage(self, message, noerror):
384 if self.oneUserHasJoined:
385 SwitchboardSessionBase.sendMessage(self, message, noerror)
386 else:
387 self.messageBuffer.append((message, noerror))
388
389 def inviteUser(self, userHandle):
390 userHandle = str(userHandle)
391 if self.ready:
392 LogEvent(INFO, self.ident)
393 self.switchboard.inviteUser(userHandle)
394 else:
395 self.invitedUsers.append(userHandle)
396
397 def gotMessage(self, message):
398 self.groupchat.messageReceived(message.userHandle, message.getMessage())
399
400 def flushBuffer(self):
401 for m, noerror in self.messageBuffer[:]:
402 self.messageBuffer.remove((m, noerror))
403 self.sendMessage(m, noerror)
404
405 for i in self.invitedUsers[:]:
406 self.invitedUsers.remove(i)
407 self.inviteUser(i)
408
409 def userJoined(self, userHandle):
410 LogEvent(INFO, self.ident)
411 self.oneUserHasJoined = True
412 self.flushBuffer()
413 self.groupchat.contactJoined(userHandle)
414
415 def userLeft(self, userHandle):
416 LogEvent(INFO, self.ident)
417 self.groupchat.contactLeft(userHandle)
418
419
420
421 class SwitchboardSession(SwitchboardSessionBase):
422 def __init__(self, msncon, remoteUser, resource, reply=False, host=None, port=None, key=None, sessionID=None):
423 self.removed = False
424
425 self.ident = (msncon.session.jabberID, remoteUser)
426 self.msncon = msncon
427 self.remoteUser = str(remoteUser)
428 self.resource = str(resource)
429
430 self.killTimer = reactor.callLater(30.0*60.0, self.removeMe)
431
432 self.switchboard = None # The SwitchboardClient class
433 self.messageBuffer = [] # Any messages sent before the switchboard is ready are buffered
434 self.ready = False # Is True when we are connected to the switchboard, and the remote user has accepted our invite
435
436 if not reply:
437 # Request a switchboard
438 d = self.msncon.notificationProtocol.requestSwitchboardServer()
439 d.addCallback(self.sbRequestAccepted)
440 d.addErrback(self.removeMe)
441 else:
442 reactor.connectTCP(host, port, SwitchboardFactory(self, key, sessionID, reply))
443
444 LogEvent(INFO, self.ident)
445
446 def removeMe(self, sbflag=True):
447 if self.removed:
448 log.err("removeMe called more than once!")
449 return
450 self.removed = True
451
452 LogEvent(INFO, self.ident)
453 for message, noerror in self.messageBuffer:
454 if not noerror:
455 self.msncon.failedMessage(self.remoteUser, message)
456 self.messageBuffer = []
457
458 del self.msncon.switchboardSessions[self.remoteUser]
459 self.msncon = None
460 if sbflag and self.switchboard:
461 self.switchboard.removeMe()
462 self.switchboard = None
463 self.ready = False
464 if self.killTimer and not self.killTimer.called:
465 self.killTimer.cancel()
466 self.killTimer = None
467
468 utils.mutilateMe(self)
469
470 def resetTimer(self):
471 # Sets a count down timer to kill this switchboard session in 30 minutes
472 self.killTimer.cancel()
473 self.killTimer = reactor.callLater(30.0*60.0, self.removeMe)
474
475 def sbRequestAccepted(self, (host, port, key)):
476 # Connect to the switchboard server
477 reactor.connectTCP(host, port, SwitchboardFactory(self, key))
478
479 def sendTypingNofication(self):
480 if self.ready:
481 self.switchboard.sendTypingNotification()
482
483 def contactTyping(self):
484 self.msncon.gotContactTyping(self.remoteUser, self.resource)
485
486 def flushBuffer(self):
487 for m, noerror in self.messageBuffer[:]:
488 self.messageBuffer.remove((m, noerror))
489 self.sendMessage(m, noerror)
490
491 def gotMessage(self, message):
492 self.msncon.gotMessage(self.remoteUser, self.resource, message.getMessage())
493 self.resetTimer()
494
495 CAPS = msn.MSNContact.MSNC1 | msn.MSNContact.MSNC2 | msn.MSNContact.MSNC3 | msn.MSNContact.MSNC4
496 def requestAvatar(self):
497 if not self.switchboard: return
498 msnContacts = self.msncon.getContacts()
499 if not msnContacts: return
500 msnContact = msnContacts.getContact(self.remoteUser)
501 if not (msnContact and msnContact.caps & self.CAPS and msnContact.msnobj): return
502 if msnContact.msnobjGot: return
503 msnContact.msnobjGot = True # This is deliberately set before we get the avatar. So that we don't try to reget failed avatars over & over
504 self.switchboard.sendAvatarRequest(self.remoteUser, msnContact.msnobj)
505
506 def userJoined(self, userHandle):
507 if userHandle != self.remoteUser:
508 # Another user has joined, so we now have three participants (these two and ourself)
509 switchToGroupchat(self, self.remoteUser, userHandle)
510 else:
511 self.requestAvatar()
512
513 def userLeft(self, userHandle):
514 if userHandle == self.remoteUser:
515 self.removeMe()
516
517
518
519 class DispatchFactory(ClientFactory):
520 def __init__(self, msncon):
521 self.msncon = msncon
522
523 def buildProtocol(self, addr):
524 p = Dispatch(self.msncon)
525 del self.msncon # No longer needed
526 return p
527
528 def clientConnectionFailed(self, connector, reason):
529 self.msncon.connectionLostBase(reason)
530
531
532 class Dispatch(msn.DispatchClient):
533 def __init__(self, msncon):
534 msn.DispatchClient.__init__(self)
535 self.msncon = msncon
536 self.userHandle = self.msncon.username
537
538 def __del__(self):
539 self.factory = None
540 self.msncon = None
541
542 def gotNotificationReferral(self, host, port):
543 self.transport.loseConnection()
544 if self.msncon and self.msncon.session and self.msncon.session.alive:
545 reactor.connectTCP(host, port, self.msncon.notificationFactory)
546
547
548
549 class Notification(msn.NotificationClient):
550 def __init__(self):
551 self.removed = False
552
553 msn.NotificationClient.__init__(self)
554
555 def removeMe(self):
556 if self.removed:
557 log.err("removeMe called more than once!")
558 return
559 self.removed = True
560
561 self.logOut()
562 self.transport.loseConnection()
563 if self.factory.msncon:
564 self.factory.msncon.notificationProtocol = None
565 self.factory.msncon = None
566 self.factory = None
567
568 utils.mutilateMe(self)
569
570 def badConditions(self):
571 if not (self.factory and self.factory.msncon and self.factory.msncon.session and self.factory.msncon.session.alive):
572 if not self.removed:
573 self.removeMe()
574 return True
575 return False
576
577
578 def loginFailure(self, message):
579 if self.badConditions(): return
580 self.factory.msncon.loginFailure(message)
581
582 def loggedIn(self, userHandle, verified):
583 if self.badConditions(): return
584
585 self.factory.msncon.notificationProtocolReady(self)
586 if not verified:
587 self.factory.msncon.accountNotVerified()
588
589 msn.NotificationClient.loggedIn(self, userHandle, verified)
590
591 LogEvent(INFO, self.factory.msncon.session.jabberID)
592
593 def msnAlertReceived(self, body, action, subscr):
594 if self.badConditions(): return
595 self.factory.msncon.msnAlert(body, action, subscr)
596
597 def initialEmailNotification(self, inboxunread, foldersunread):
598 if self.badConditions() or not config.mailNotifications: return
599 self.factory.msncon.initialEmailNotification(inboxunread, foldersunread)
600
601 def realtimeEmailNotification(self, mailfrom, fromaddr, subject):
602 if self.badConditions() or not config.mailNotifications: return
603 self.factory.msncon.realtimeEmailNotification(mailfrom, fromaddr, subject)
604
605 def connectionLost(self, reason):
606 if self.badConditions(): return
607 def wait():
608 LogEvent(INFO, self.factory.msncon.session.jabberID)
609 msn.NotificationClient.connectionLost(self, reason)
610 self.factory.msncon.connectionLostBase(reason)
611 # Make sure this event is handled after any others
612 reactor.callLater(0, wait)
613
614 def listSynchronized(self, *args):
615 if self.badConditions(): return
616 LogEvent(INFO, self.factory.msncon.session.jabberID)
617 self.factory.msncon.listSynchronized()
618 if self.badConditions(): return # Just in case the session is deregistered
619 self.factory.msncon.sendSavedEvents()
620 self.setPrivacyMode(False)
621
622 def gotSwitchboardInvitation(self, sessionID, host, port, key, remoteUser, screenName):
623 if self.badConditions(): return
624 LogEvent(INFO, self.factory.msncon.session.jabberID)
625 sbs = SwitchboardSession(self.factory.msncon, remoteUser, self.factory.msncon.session.highestResource(), True, host, port, key, sessionID)
626 if self.factory.msncon.switchboardSessions.has_key(remoteUser):
627 self.factory.msncon.switchboardSessions[remoteUser].removeMe()
628 self.factory.msncon.switchboardSessions[remoteUser] = sbs
629
630 def avatarHashChanged(self, userHandle, hash):
631 if self.badConditions(): return
632 LogEvent(INFO, self.factory.msncon.session.jabberID)
633 hash = base64.decodestring(hash)
634 hash = binascii.hexlify(hash)
635 self.factory.msncon.avatarHashChanged(userHandle, hash)
636
637 def contactStatusChanged(self, statusCode, userHandle, screenName):
638 if self.badConditions(): return
639 LogEvent(INFO, self.factory.msncon.session.jabberID)
640
641 self.factory.msncon.contactStatusChanged(userHandle)
642
643 def contactPersonalChanged(self, userHandle, personal):
644 if self.badConditions(): return
645 msn.NotificationClient.contactPersonalChanged(self, userHandle, personal)
646 self.factory.msncon.contactStatusChanged(userHandle)
647
648 def contactOffline(self, userHandle):
649 if self.badConditions(): return
650 LogEvent(INFO, self.factory.msncon.session.jabberID)
651 msn.NotificationClient.contactOffline(self, userHandle)
652
653 self.factory.msncon.contactStatusChanged(userHandle)
654
655 def userAddedMe(self, userGuid, userHandle, screenName):
656 if self.badConditions(): return
657 LogEvent(INFO, self.factory.msncon.session.jabberID)
658 msn.NotificationClient.userAddedMe(self, userGuid, userHandle, screenName)
659 self.factory.msncon.userAddedMe(userHandle)
660
661 def userRemovedMe(self, userGuid, userHandle):
662 if self.badConditions(): return
663 LogEvent(INFO, self.factory.msncon.session.jabberID)
664 msn.NotificationClient.userRemovedMe(self, userGuid, userHandle)
665 self.factory.msncon.userRemovedMe(userHandle)
666
667 def multipleLogin(self):
668 if self.badConditions(): return
669 LogEvent(INFO, self.factory.msncon.session.jabberID)
670 self.factory.msncon.multipleLogin()
671
672
673
674 class SwitchboardFactory(ClientFactory):
675 def __init__(self, switchboardSession, key, sessionID=None, reply=False):
676 self.switchboardSession = switchboardSession
677 self.key = key
678 self.sessionID = sessionID
679 self.reply = reply
680
681 def buildProtocol(self, addr):
682 p = Switchboard(self.switchboardSession)
683 if p.badConditions(): return p
684 p.key = self.key
685 p.sessionID = self.sessionID
686 p.reply = self.reply
687 p.userHandle = self.switchboardSession.msncon.username
688 p.factory = self
689 return p
690
691 class Switchboard(msn.SwitchboardClient):
692 def __init__(self, switchboardSession):
693 self.removed = False
694
695 self.switchboardSession = switchboardSession
696 self.chattingUsers = []
697 self.callid = None
698 msn.SwitchboardClient.__init__(self)
699 if self.badConditions(): return
700 self.msnobj = self.switchboardSession.msncon.notificationProtocol.msnobj
701 LogEvent(INFO, self.switchboardSession.ident)
702
703 def removeMe(self):
704 if self.removed:
705 log.err("removeMe called more than once!")
706 return
707 self.removed = True
708
709 self.transport.loseConnection()
710 LogEvent(INFO, self.switchboardSession.ident)
711 self.switchboardSession = None
712 self.factory.switchboardSession = None
713 self.factory = None
714
715 if self.callid and not self.callid.called:
716 self.callid.cancel() # Cancel the invite fail message
717 self.callid = None
718
719 utils.mutilateMe(self)
720
721 def badConditions(self):
722 if not (self.switchboardSession and self.switchboardSession.msncon and self.switchboardSession.msncon.session and self.switchboardSession.msncon.session.alive):
723 if self.switchboardSession:
724 if not self.switchboardSession.removed:
725 self.switchboardSession.removeMe()
726 elif not self.removed:
727 self.removeMe()
728 return True
729 return False
730
731 def loggedIn(self):
732 if self.badConditions(): return
733 if (not self.reply) and self.switchboardSession.__class__ == SwitchboardSession:
734 def failCB(arg=None):
735 LogEvent(INFO, ident, "User has not joined after 30 seconds.")
736 self.switchboardSession.removeMe()
737 d = self.inviteUser(self.switchboardSession.remoteUser)
738 d.addErrback(failCB)
739 ident = self.switchboardSession.ident
740 # If the user doesn't join then we want to tear down the SwitchboardSession
741 self.callid = reactor.callLater(30.0, failCB)
742
743 else:
744 self.readySwitchboardSession()
745
746 def readySwitchboardSession(self):
747 self.switchboardSession.switchboardReady(self)
748 for user in self.chattingUsers:
749 self.switchboardSession.userJoined(user)
750 if self.callid and not self.callid.called:
751 self.callid.cancel() # Cancel the invite fail message (only applies if we needed to invite the user)
752 self.callid = None
753
754 def gotChattingUsers(self, users):
755 for user in users:
756 self.chattingUsers.append(user)
757
758 def userJoined(self, userHandle, screenName):
759 if self.badConditions(): return
760 # FIXME - check this is correct
761 if (not self.reply) and isinstance(self.switchboardSession, SwitchboardSession):
762 self.readySwitchboardSession()
763 LogEvent(INFO, self.switchboardSession.ident)
764 self.switchboardSession.userJoined(userHandle)
765
766 def userLeft(self, userHandle):
767 if self.badConditions(): return
768 LogEvent(INFO, self.switchboardSession.ident)
769 def wait():
770 if self.badConditions(): return
771 self.switchboardSession.userLeft(userHandle)
772 # Make sure this event is handled after any others (eg, gotMessage)
773 reactor.callLater(0, wait)
774
775 def gotMessage(self, message):
776 if self.badConditions():
777 LogEvent(WARN, self.switchboardSession.ident, "gotMessage() called too late. Dropped a message!")
778 return
779
780 LogEvent(INFO, self.switchboardSession.ident)
781 cTypes = [s.lstrip() for s in message.getHeader("Content-Type").split(';')]
782 if "text/plain" in cTypes:
783 try:
784 if len(cTypes) > 1 and cTypes[1].find("UTF-8") >= 0:
785 message.message = message.message.decode("utf-8")
786 self.switchboardSession.gotMessage(message)
787 except:
788 self.switchboardSession.gotMessage(lang.get(self.switchboardSession.msncon.session.lang).msnDroppedMessage) # FIXME, this is a little deep
789 raise
790 return
791 if "text/x-clientcaps" in cTypes:
792 if message.hasHeader("JabberID"):
793 jid = message.getHeader("JabberID")
794 self.switchboardSession.msncon.userMapping(message.userHandle, jid)
795 return
796 LogEvent(INFO, self.switchboardSession.ident, "Discarding unknown message type.")
797
798 def userTyping(self, message):
799 if self.badConditions(): return
800 if self.switchboardSession.__class__ == SwitchboardSession: # Ignore typing in groupchats
801 if message.userHandle == self.switchboardSession.remoteUser:
802 self.switchboardSession.contactTyping()
803
804 def sendClientCaps(self):
805 message = msn.MSNMessage()
806 message.setHeader("Content-Type", "text/x-clientcaps")
807 message.setHeader("Client-Name", "PyMSNt")
808 message.setHeader("JabberID", str(self.switchboardSession.msncon.session.jabberID)) # FIXME, this is a little deep
809 self.sendMessage(message)
810
811 def sendMessage(self, message):
812 # A little bit of fancyness to make sure that clientcaps
813 # only gets sent after the first text message.
814 if message.getHeader("Content-Type").startswith("text"):
815 self.sendMessage = type(self.sendMessage)(msn.SwitchboardClient.sendMessage, self, Switchboard)
816 self.sendClientCaps()
817 return self.sendMessage(message)
818 else:
819 return msn.SwitchboardClient.sendMessage(self, message)
820
821 def gotAvatarImage(self, to, image):
822 if self.badConditions(): return
823 self.switchboardSession.gotAvatarImage(to, image)
824
825 def gotSendRequest(self, fileReceive):
826 if self.badConditions():
827 fileReceive.accept(False)
828 return
829 LogEvent(INFO, self.switchboardSession.ident)
830 self.switchboardSession.gotSendRequest(fileReceive)
831
832