1 # Copyright 2004-2005 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
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
21 """ Manages all the Twisted factories, etc """
22 def __init__(self
, username
, password
):
23 self
.username
= username
24 self
.password
= password
28 LogEvent(INFO
, self
.session
.jabberID
)
32 MSNConnection
.removeMe(self
)
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
45 self
.savedEvents
= SavedEvents()
49 LogEvent(INFO
, self
.session
.jabberID
)
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
= {}
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()
69 def getContacts(self
):
70 if self
.notificationFactory
:
71 return self
.notificationFactory
.contacts
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
)
84 self
.failedMessage(remoteUser
, text
)
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
)
93 self
.switchboardSessions
[userHandle
].requestAvatar()
95 if self
.switchboardSessions
.has_key(userHandle
): # Only request avatars for open switchboards
96 self
.switchboardSessions
[userHandle
].requestAvatar()
98 def sendTypingToContact(self
, remoteUser
):
99 if self
.switchboardSessions
.has_key(remoteUser
):
100 self
.switchboardSessions
[remoteUser
].sendTypingNofication()
102 def notificationProtocolReady(self
, notificationProtocol
):
103 self
.notificationProtocol
= notificationProtocol
107 def sendSavedEvents(self
):
108 # Hack for events sent before we're logged in
109 self
.savedEvents
.send(self
)
110 self
.savedEvents
= None
112 def changeAvatar(self
, imageData
):
113 if self
.notificationProtocol
:
114 self
.notificationProtocol
.changeAvatar(imageData
, push
=True)
116 self
.savedEvents
.avatarImageData
= imageData
118 def changeStatus(self
, statusCode
, screenName
, personal
):
119 if self
.notificationProtocol
:
121 self
.ourStatusChanged(arg
[0])
123 self
.ourNickChanged(arg
[0])
125 self
.ourPersonalChanged(personal
)
126 LogEvent(INFO
, self
.session
.jabberID
)
128 statusCode
= str(statusCode
.encode("utf-8"))
129 self
.notificationProtocol
.changeStatus(statusCode
).addCallback(cb1
)
131 screenName
= str(screenName
.encode("utf-8"))
132 self
.notificationProtocol
.changeScreenName(screenName
).addCallback(cb2
)
134 personal
= str(personal
.encode("utf-8"))
137 self
.notificationProtocol
.changePersonalMessage(personal
).addCallback(cb3
)
139 self
.savedEvents
.statusCode
= statusCode
140 self
.savedEvents
.screenName
= screenName
141 self
.savedEvents
.personal
= personal
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
)
149 self
.connectionLost(self
)
151 def addContact(self
, listType
, userHandle
):
152 if self
.notificationProtocol
:
153 return self
.notificationProtocol
.addContact(listType
, str(userHandle
))
155 self
.savedEvents
.addContacts
.append((listType
, str(userHandle
)))
157 def remContact(self
, listType
, userHandle
, groupID
=0):
158 if self
.notificationProtocol
:
159 return self
.notificationProtocol
.remContact(listType
, str(userHandle
))
161 self
.savedEvents
.remContacts
.append((listType
, str(userHandle
)))
165 def initialEmailNotification(self
, inboxunread
, foldersunread
):
168 def realtimeEmailNotification(self
, mailfrom
, fromaddr
, subject
):
174 def loginFailure(self
, message
):
177 def multipleLogin(self
):
180 def gotMessage(self
, remoteUser
, resource
, text
):
183 def avatarHashChanged(self
, userHandle
, hash):
186 def gotAvatarImage(self
, to
, image
):
189 def gotSendRequest(self
, fileReceive
):
192 def listSynchronized(self
):
195 def contactStatusChanged(self
, remoteUser
):
198 def ourStatusChanged(self
, statusCode
):
201 def ourNickChanged(self
, nick
):
204 def ourPersonalChanged(self
, personal
):
207 def userMapping(self
, passport
, jid
):
210 def gotContactTyping(self
, remoteUser
, resource
):
213 def serverGoingDown(self
):
216 def accountNotVerified(self
):
219 def userAddedMe(self
, userHandle
):
222 def userRemovedMe(self
, userHandle
):
225 def failedMessage(self
, remoteUser
, message
):
228 def connectionLost(self
):
238 self
.avatarImageData
= ""
239 self
.addContacts
= []
240 self
.remContacts
= []
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
)
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
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
)
271 class SwitchboardSessionBase
:
272 def sendMessage(self
, message
, noerror
):
274 def failedMessage(ignored
):
275 if self
.__class
__ == GroupchatSwitchboardSession
:
276 tempmsncon
.failedMessage(self
.groupchat
.roomJID(), message
)
278 tempmsncon
.failedMessage(self
.remoteUser
, message
)
280 tempmsncon
= self
.msncon
# In case MSN tells us the message failed after removeMe()
282 LogEvent(INFO
, self
.ident
)
283 message
= str(message
.encode("utf-8"))
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
290 d
= self
.switchboard
.sendMessage(msnmessage
)
292 d
.addCallback(failedMessage
)
294 chunks
= int(math
.ceil(len(message
) / float(MAXMESSAGESIZE
)))
296 guid
= utils
.random_guid()
297 while chunk
< chunks
:
298 offset
= chunk
* MAXMESSAGESIZE
299 text
= message
[offset
: offset
+ MAXMESSAGESIZE
]
301 msnmessage
= msn
.MSNMessage(message
=text
)
302 msnmessage
.setHeader("Message-ID", guid
)
304 msnmessage
.setHeader("Content-Type", "text/plain; charset=UTF-8")
305 msnmessage
.setHeader("Chunks", str(chunks
))
307 msnmessage
.setHeader("Chunk", str(chunk
))
308 msnmessage
.ack
= msn
.MSNMessage
.MESSAGE_NACK
310 d
= self
.switchboard
.sendMessage(msnmessage
)
312 d
.addCallback(failedMessage
)
317 self
.messageBuffer
.append((message
, noerror
))
319 def gotAvatarImage(self
, to
, image
):
320 self
.msncon
.gotAvatarImage(to
, image
)
322 def gotSendRequest(self
, fileReceive
):
323 self
.msncon
.gotSendRequest(fileReceive
)
325 def switchboardReady(self
, switchboard
):
326 LogEvent(INFO
, self
.ident
)
328 self
.switchboard
= switchboard
331 def resetTimer(self
):
335 class GroupchatSwitchboardSession(SwitchboardSessionBase
):
336 def __init__(self
, groupchat
=None, makeSwitchboard
=False):
341 self
.ident
= groupchat
.roomJID()
342 self
.groupchat
= groupchat
343 self
.msncon
= self
.groupchat
.session
.legacycon
345 self
.ident
= str(self
)
346 self
.groupchat
= None
347 self
.switchboard
= None
349 self
.messageBuffer
= []
350 self
.invitedUsers
= []
351 self
.oneUserHasJoined
= False
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
)
360 LogEvent(INFO
, self
.ident
, "Created groupchat for " + self
.msncon
.username
)
364 log
.err("removeMe called more than once!")
368 LogEvent(INFO
, self
.ident
)
371 self
.switchboard
.removeMe()
372 self
.switchboard
= None
373 self
.groupchat
= None
376 utils
.mutilateMe(self
)
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
))
383 def sendMessage(self
, message
, noerror
):
384 if self
.oneUserHasJoined
:
385 SwitchboardSessionBase
.sendMessage(self
, message
, noerror
)
387 self
.messageBuffer
.append((message
, noerror
))
389 def inviteUser(self
, userHandle
):
390 userHandle
= str(userHandle
)
392 LogEvent(INFO
, self
.ident
)
393 self
.switchboard
.inviteUser(userHandle
)
395 self
.invitedUsers
.append(userHandle
)
397 def gotMessage(self
, message
):
398 self
.groupchat
.messageReceived(message
.userHandle
, message
.getMessage())
400 def flushBuffer(self
):
401 for m
, noerror
in self
.messageBuffer
[:]:
402 self
.messageBuffer
.remove((m
, noerror
))
403 self
.sendMessage(m
, noerror
)
405 for i
in self
.invitedUsers
[:]:
406 self
.invitedUsers
.remove(i
)
409 def userJoined(self
, userHandle
):
410 LogEvent(INFO
, self
.ident
)
411 self
.oneUserHasJoined
= True
413 self
.groupchat
.contactJoined(userHandle
)
415 def userLeft(self
, userHandle
):
416 LogEvent(INFO
, self
.ident
)
417 self
.groupchat
.contactLeft(userHandle
)
421 class SwitchboardSession(SwitchboardSessionBase
):
422 def __init__(self
, msncon
, remoteUser
, resource
, reply
=False, host
=None, port
=None, key
=None, sessionID
=None):
425 self
.ident
= (msncon
.session
.jabberID
, remoteUser
)
427 self
.remoteUser
= str(remoteUser
)
428 self
.resource
= str(resource
)
430 self
.killTimer
= reactor
.callLater(30.0*60.0, self
.removeMe
)
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
437 # Request a switchboard
438 d
= self
.msncon
.notificationProtocol
.requestSwitchboardServer()
439 d
.addCallback(self
.sbRequestAccepted
)
440 d
.addErrback(self
.removeMe
)
442 reactor
.connectTCP(host
, port
, SwitchboardFactory(self
, key
, sessionID
, reply
))
444 LogEvent(INFO
, self
.ident
)
446 def removeMe(self
, sbflag
=True):
448 log
.err("removeMe called more than once!")
452 LogEvent(INFO
, self
.ident
)
453 for message
, noerror
in self
.messageBuffer
:
455 self
.msncon
.failedMessage(self
.remoteUser
, message
)
456 self
.messageBuffer
= []
458 del self
.msncon
.switchboardSessions
[self
.remoteUser
]
460 if sbflag
and self
.switchboard
:
461 self
.switchboard
.removeMe()
462 self
.switchboard
= None
464 if self
.killTimer
and not self
.killTimer
.called
:
465 self
.killTimer
.cancel()
466 self
.killTimer
= None
468 utils
.mutilateMe(self
)
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
)
475 def sbRequestAccepted(self
, (host
, port
, key
)):
476 # Connect to the switchboard server
477 reactor
.connectTCP(host
, port
, SwitchboardFactory(self
, key
))
479 def sendTypingNofication(self
):
481 self
.switchboard
.sendTypingNotification()
483 def contactTyping(self
):
484 self
.msncon
.gotContactTyping(self
.remoteUser
, self
.resource
)
486 def flushBuffer(self
):
487 for m
, noerror
in self
.messageBuffer
[:]:
488 self
.messageBuffer
.remove((m
, noerror
))
489 self
.sendMessage(m
, noerror
)
491 def gotMessage(self
, message
):
492 self
.msncon
.gotMessage(self
.remoteUser
, self
.resource
, message
.getMessage())
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
)
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
)
513 def userLeft(self
, userHandle
):
514 if userHandle
== self
.remoteUser
:
519 class DispatchFactory(ClientFactory
):
520 def __init__(self
, msncon
):
523 def buildProtocol(self
, addr
):
524 p
= Dispatch(self
.msncon
)
525 del self
.msncon
# No longer needed
528 def clientConnectionFailed(self
, connector
, reason
):
529 self
.msncon
.connectionLostBase(reason
)
532 class Dispatch(msn
.DispatchClient
):
533 def __init__(self
, msncon
):
534 msn
.DispatchClient
.__init
__(self
)
536 self
.userHandle
= self
.msncon
.username
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
)
549 class Notification(msn
.NotificationClient
):
553 msn
.NotificationClient
.__init
__(self
)
557 log
.err("removeMe called more than once!")
562 self
.transport
.loseConnection()
563 if self
.factory
.msncon
:
564 self
.factory
.msncon
.notificationProtocol
= None
565 self
.factory
.msncon
= None
568 utils
.mutilateMe(self
)
570 def badConditions(self
):
571 if not (self
.factory
and self
.factory
.msncon
and self
.factory
.msncon
.session
and self
.factory
.msncon
.session
.alive
):
578 def loginFailure(self
, message
):
579 if self
.badConditions(): return
580 self
.factory
.msncon
.loginFailure(message
)
582 def loggedIn(self
, userHandle
, verified
):
583 if self
.badConditions(): return
585 self
.factory
.msncon
.notificationProtocolReady(self
)
587 self
.factory
.msncon
.accountNotVerified()
589 msn
.NotificationClient
.loggedIn(self
, userHandle
, verified
)
591 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
593 def msnAlertReceived(self
, body
, action
, subscr
):
594 if self
.badConditions(): return
595 self
.factory
.msncon
.msnAlert(body
, action
, subscr
)
597 def initialEmailNotification(self
, inboxunread
, foldersunread
):
598 if self
.badConditions() or not config
.mailNotifications
: return
599 self
.factory
.msncon
.initialEmailNotification(inboxunread
, foldersunread
)
601 def realtimeEmailNotification(self
, mailfrom
, fromaddr
, subject
):
602 if self
.badConditions() or not config
.mailNotifications
: return
603 self
.factory
.msncon
.realtimeEmailNotification(mailfrom
, fromaddr
, subject
)
605 def connectionLost(self
, reason
):
606 if self
.badConditions(): return
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
)
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)
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
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)
637 def contactStatusChanged(self
, statusCode
, userHandle
, screenName
):
638 if self
.badConditions(): return
639 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
641 self
.factory
.msncon
.contactStatusChanged(userHandle
)
643 def contactPersonalChanged(self
, userHandle
, personal
):
644 if self
.badConditions(): return
645 msn
.NotificationClient
.contactPersonalChanged(self
, userHandle
, personal
)
646 self
.factory
.msncon
.contactStatusChanged(userHandle
)
648 def contactOffline(self
, userHandle
):
649 if self
.badConditions(): return
650 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
651 msn
.NotificationClient
.contactOffline(self
, userHandle
)
653 self
.factory
.msncon
.contactStatusChanged(userHandle
)
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
)
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
)
667 def multipleLogin(self
):
668 if self
.badConditions(): return
669 LogEvent(INFO
, self
.factory
.msncon
.session
.jabberID
)
670 self
.factory
.msncon
.multipleLogin()
674 class SwitchboardFactory(ClientFactory
):
675 def __init__(self
, switchboardSession
, key
, sessionID
=None, reply
=False):
676 self
.switchboardSession
= switchboardSession
678 self
.sessionID
= sessionID
681 def buildProtocol(self
, addr
):
682 p
= Switchboard(self
.switchboardSession
)
683 if p
.badConditions(): return p
685 p
.sessionID
= self
.sessionID
687 p
.userHandle
= self
.switchboardSession
.msncon
.username
691 class Switchboard(msn
.SwitchboardClient
):
692 def __init__(self
, switchboardSession
):
695 self
.switchboardSession
= switchboardSession
696 self
.chattingUsers
= []
698 msn
.SwitchboardClient
.__init
__(self
)
699 if self
.badConditions(): return
700 self
.msnobj
= self
.switchboardSession
.msncon
.notificationProtocol
.msnobj
701 LogEvent(INFO
, self
.switchboardSession
.ident
)
705 log
.err("removeMe called more than once!")
709 self
.transport
.loseConnection()
710 LogEvent(INFO
, self
.switchboardSession
.ident
)
711 self
.switchboardSession
= None
712 self
.factory
.switchboardSession
= None
715 if self
.callid
and not self
.callid
.called
:
716 self
.callid
.cancel() # Cancel the invite fail message
719 utils
.mutilateMe(self
)
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
:
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
)
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
)
744 self
.readySwitchboardSession()
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)
754 def gotChattingUsers(self
, users
):
756 self
.chattingUsers
.append(user
)
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
)
766 def userLeft(self
, userHandle
):
767 if self
.badConditions(): return
768 LogEvent(INFO
, self
.switchboardSession
.ident
)
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
)
775 def gotMessage(self
, message
):
776 if self
.badConditions():
777 LogEvent(WARN
, self
.switchboardSession
.ident
, "gotMessage() called too late. Dropped a message!")
780 LogEvent(INFO
, self
.switchboardSession
.ident
)
781 cTypes
= [s
.lstrip() for s
in message
.getHeader("Content-Type").split(';')]
782 if "text/plain" in cTypes
:
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
)
788 self
.switchboardSession
.gotMessage(lang
.get(self
.switchboardSession
.msncon
.session
.lang
).msnDroppedMessage
) # FIXME, this is a little deep
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
)
796 LogEvent(INFO
, self
.switchboardSession
.ident
, "Discarding unknown message type.")
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()
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
)
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
)
819 return msn
.SwitchboardClient
.sendMessage(self
, message
)
821 def gotAvatarImage(self
, to
, image
):
822 if self
.badConditions(): return
823 self
.switchboardSession
.gotAvatarImage(to
, image
)
825 def gotSendRequest(self
, fileReceive
):
826 if self
.badConditions():
827 fileReceive
.accept(False)
829 LogEvent(INFO
, self
.switchboardSession
.ident
)
830 self
.switchboardSession
.gotSendRequest(fileReceive
)