]>
code.delx.au - pymsnt/blob - src/legacy/glue.py
17474924cabe93dd0981c76bfa6192498997a535
1 # Copyright 2004 James Bunton <james@delx.cjb.net>
2 # Licensed for distribution under the GPL version 2, check COPYING for details
5 from twisted
. internet
import task
6 if ( utils
. checkTwisted ()):
7 from twisted
. xish
. domish
import Element
9 from tlib
. domish
import Element
20 name
= "MSN Transport" # The name of the transport
21 version
= "0.9.5" # The transport version
22 mangle
= True # XDB '@' -> '%' mangling
23 id = "msn" # The transport identifier
28 """ Returns True if the JID passed is a valid groupchat JID (for MSN, does not contain '%') """
29 return ( jid
. find ( '%' ) == - 1 )
33 # This should be set to the name space the registration entries are in, in the xdb spool
34 namespace
= "jabber:iq:register"
37 def formRegEntry ( username
, password
, nickname
):
38 """ Returns a domish.Element representation of the data passed. This element will be written to the XDB spool file """
39 reginfo
= Element (( None , "query" ))
40 reginfo
. attributes
[ "xmlns" ] = "jabber:iq:register"
42 userEl
= reginfo
. addElement ( "username" )
43 userEl
. addContent ( username
)
45 passEl
= reginfo
. addElement ( "password" )
46 passEl
. addContent ( password
)
48 nickEl
= reginfo
. addElement ( "nick" )
49 if ( nickname
): nickEl
. addContent ( nickname
)
56 def getAttributes ( base
):
57 """ This function should, given a spool domish.Element, pull the username, password,
58 and nickname out of it and return them """
62 for child
in base
. elements ():
64 if ( child
. name
== "username" ):
65 username
= child
.__ str
__ ()
66 elif ( child
. name
== "password" ):
67 password
= child
.__ str
__ ()
68 elif ( child
. name
== "nick" ):
69 nickname
= child
.__ str
__ ()
70 except AttributeError :
73 return username
, password
, nickname
80 """ Converts a MSN passport into a JID representation to be used with the transport """
81 return msnid
. replace ( '@' , '%' ) + "@" + config
. jid
83 translateAccount
= msn2jid
# Marks this as the function to be used in jabber:iq:gateway (Service ID Translation)
86 """ Converts a JID representation of a MSN passport into the original MSN passport """
87 return unicode ( jid
[: jid
. find ( '@' )]. replace ( '%' , '@' ))
90 def presence2state ( show
, ptype
):
91 """ Converts a Jabber presence into an MSN status code """
92 if ( ptype
== "unavailable" ):
93 return msn
. STATUS_OFFLINE
94 elif ( show
in [ None , "online" , "chat" ]):
95 return msn
. STATUS_ONLINE
96 elif ( show
in [ "dnd" ]):
97 return msn
. STATUS_BUSY
98 elif ( show
in [ "away" , "xa" ]):
99 return msn
. STATUS_AWAY
102 def state2presence ( state
):
103 """ Converts a MSN status code into a Jabber presence """
104 if ( state
== msn
. STATUS_ONLINE
):
106 elif ( state
== msn
. STATUS_BUSY
):
108 elif ( state
== msn
. STATUS_AWAY
):
109 return ( "away" , None )
110 elif ( state
== msn
. STATUS_IDLE
):
111 return ( "away" , None )
112 elif ( state
== msn
. STATUS_BRB
):
113 return ( "away" , None )
114 elif ( state
== msn
. STATUS_PHONE
):
116 elif ( state
== msn
. STATUS_LUNCH
):
117 return ( "away" , None )
119 return ( None , "unavailable" )
124 # This class handles groupchats with the legacy protocol
125 class LegacyGroupchat ( groupchat
. BaseGroupchat
):
126 def __init__ ( self
, session
, resource
, ID
= None , existing
= False , switchboardSession
= None ):
127 """ Possible entry points for groupchat
128 - User starts an empty switchboard session by sending presence to a blank room
129 - An existing switchboard session is joined by another MSN user
130 - User invited to an existing switchboard session with more than one user
132 groupchat
. BaseGroupchat
.__ init
__ ( self
, session
, resource
, ID
)
134 self
. switchboardSession
= msnw
. GroupchatSwitchboardSession ( self
, makeSwitchboard
= True )
136 self
. switchboardSession
= switchboardSession
138 assert ( self
. switchboardSession
!= None )
140 debug
. log ( "LegacyGroupchat: \" %s \" created" % ( self
. roomJID ()))
143 self
. switchboardSession
. removeMe ()
144 self
. switchboardSession
= None
145 groupchat
. BaseGroupchat
. removeMe ( self
)
146 debug
. log ( "LegacyGroupchat: \" %s \" destroyed" % ( self
. roomJID ()))
147 utils
. mutilateMe ( self
)
149 def sendLegacyMessage ( self
, message
, noerror
):
150 debug
. log ( "LegacyGroupchat: \" %s \" sendLegacyMessage( \" %s \" )" % ( self
. roomJID (), message
))
151 self
. switchboardSession
. sendMessage ( message
, noerror
)
153 def sendContactInvite ( self
, contactJID
):
154 debug
. log ( "LegacyGroupchat: \" %s \" sendContactInvite( \" %s \" )" % ( self
. roomJID (), contactJID
))
155 userHandle
= jid2msn ( contactJID
)
156 self
. switchboardSession
. inviteUser ( userHandle
)
160 # This class handles most interaction with the legacy protocol
161 class LegacyConnection ( msnw
. MSNConnection
):
162 """ A glue class that connects to the legacy network """
163 def __init__ ( self
, username
, password
, session
):
164 self
. session
= session
165 self
. listSynced
= False
166 self
. initialListVersion
= 0
168 # Get the latest listVersion to pass to MSNConnection
169 result
= self
. session
. pytrans
. xdb
. request ( self
. session
. jabberID
, "msn:listVersion" )
171 self
. initialListVersion
= int ( str ( result
))
174 msnw
. MSNConnection
.__ init
__ ( self
, username
, password
)
176 # User typing notification stuff
177 self
. userTyping
= dict () # Indexed by contact MSN ID, stores whether the user is typing to this contact
178 # Contact typing notification stuff
179 self
. contactTyping
= dict () # Indexed by contact MSN ID, stores an integer that is incremented at 5 second intervals. If it reaches 3 then the contact has stopped typing. It is set to zero whenever MSN typing notification messages are received
181 self
. userTypingSend
= task
. LoopingCall ( self
. sendTypingNotifications
)
182 self
. userTypingSend
. start ( 5.0 )
184 import subscription
# Is in here to prevent an ImportError loop
185 self
. subscriptions
= subscription
. SubscriptionManager ( self
. session
)
187 debug
. log ( "LegacyConnection: \" %s \" - created" % ( self
. session
. jabberID
))
190 debug
. log ( "LegacyConnection: \" %s \" - being deleted" % ( self
. session
. jabberID
))
192 self
. userTypingSend
. stop ()
194 if ( self
. getContacts ()):
195 for userHandle
in self
. getContacts (). getContacts ():
196 msnContact
= self
. getContacts (). getContact ( userHandle
)
197 if ( msnContact
. status
and msnContact
. status
!= msn
. STATUS_OFFLINE
):
198 msnContact
. status
= msn
. STATUS_OFFLINE
199 self
. sendMSNContactPresence ( msnContact
)
201 # msnw.MSNConnection.changeStatus(self, msn.STATUS_OFFLINE, self.session.nickname) # Change our nickname on the MSN network (stops users from appearing offline with a nickname "james - Online")
203 # Save to XDB the current list version (for fast logins)
204 if ( self
. notificationFactory
and self
. notificationFactory
. contacts
):
205 listVersion
= self
. notificationFactory
. contacts
. version
206 el
= Element (( None , "query" ))
207 el
. addContent ( str ( listVersion
))
208 self
. session
. pytrans
. xdb
. set ( self
. session
. jabberID
, "msn:listVersion" , el
)
210 msnw
. MSNConnection
. removeMe ( self
)
211 self
. subscriptions
. removeMe ()
212 self
. subscriptions
= None
215 utils
. mutilateMe ( self
)
217 def jidRes ( self
, resource
):
218 to
= self
. session
. jabberID
224 def highestResource ( self
):
225 """ Returns highest priority resource """
226 return self
. session
. highestResource ()
228 def sendMessage ( self
, dest
, resource
, body
, noerror
):
230 if ( self
. userTyping
. has_key ( dest
)):
231 del self
. userTyping
[ dest
]
232 msnw
. MSNConnection
. sendMessage ( self
, dest
, resource
, body
, noerror
)
234 def buildFriendly ( self
, status
):
235 """ Constructs a friendly name from the user's registered nick, and their status message """
237 if ( not config
. fancyFriendly
):
238 if ( self
. session
. nickname
and len ( self
. session
. nickname
) > 0 ):
239 return self
. session
. nickname
241 return self
. session
. jabberID
[: self
. session
. jabberID
. find ( '@' )]
243 if ( self
. session
. nickname
and len ( self
. session
. nickname
) > 0 ):
244 friendly
= self
. session
. nickname
246 friendly
= self
. session
. jabberID
[: self
. session
. jabberID
. find ( '@' )]
247 if ( status
and len ( status
) > 0 ):
250 if ( len ( friendly
) > 127 ):
251 friendly
= friendly
[: 124 ] + "..."
252 debug
. log ( "LegacyConnection: buildFriendly( %s ) returning \" %s \" " % ( self
. session
. jabberID
, friendly
))
255 def msnAlert ( self
, text
, actionurl
, subscrurl
):
256 el
= Element (( None , "message" ))
257 el
. attributes
[ "to" ] = self
. session
. jabberID
258 el
. attributes
[ "from" ] = config
. jid
259 el
. attributes
[ "type" ] = "headline"
260 body
= el
. addElement ( "body" )
261 body
. addContent ( text
)
263 x
= el
. addElement ( "x" )
264 x
. attributes
[ "xmlns" ] = "jabber:x:oob"
265 x
. addElement ( "desc" ). addContent ( "More information on this notice." )
266 x
. addElement ( "url" ). addContent ( actionurl
)
268 x
= el
. addElement ( "x" )
269 x
. attributes
[ "xmlns" ] = "jabber:x:oob"
270 x
. addElement ( "desc" ). addContent ( "Manage subscriptions to alerts." )
271 x
. addElement ( "url" ). addContent ( subscrurl
)
273 self
. session
. pytrans
. send ( el
)
275 def setStatus ( self
, show
, status
):
276 statusCode
= presence2state ( show
, None )
277 msnw
. MSNConnection
. changeStatus ( self
, statusCode
, self
. buildFriendly ( status
))
279 def newResourceOnline ( self
, resource
):
280 self
. sendLists ( resource
)
282 def jabberSubscriptionReceived ( self
, source
, subtype
):
283 self
. subscriptions
. jabberSubscriptionReceived ( source
, subtype
)
285 def sendTypingNotifications ( self
):
286 # Send any typing notification messages to the user's contacts
287 for contact
in self
. userTyping
. keys ():
288 if ( self
. userTyping
[ contact
]):
289 self
. sendTypingToContact ( contact
)
291 # Send any typing notification messages from contacts to the user
292 for contact
, resource
in self
. contactTyping
. keys ():
293 self
. contactTyping
[( contact
, resource
)] += 1
294 if ( self
. contactTyping
[( contact
, resource
)] >= 3 ):
295 self
. session
. sendTypingNotification ( self
. jidRes ( resource
), msn2jid ( contact
), False )
296 del self
. contactTyping
[( contact
, resource
)]
298 def gotContactTyping ( self
, contact
, resource
):
299 # Check if the contact has only just started typing
300 if ( not self
. contactTyping
. has_key (( contact
, resource
))):
301 self
. session
. sendTypingNotification ( self
. jidRes ( resource
), msn2jid ( contact
), True )
304 self
. contactTyping
[( contact
, resource
)] = 0
306 def userTypingNotification ( self
, dest
, resource
, composing
):
308 self
. userTyping
[ dest
] = composing
309 if ( composing
): # Make it instant
310 self
. sendTypingToContact ( dest
)
313 def sendMSNContactPresence ( self
, msnContact
, to
= None ):
315 to
= self
. session
. jabberID
316 source
= msn2jid ( msnContact
. userHandle
)
317 show
, ptype
= state2presence ( msnContact
. status
)
318 status
= msnContact
. screenName
. decode ( "utf-8" )
319 self
. session
. sendPresence ( to
= to
, fro
= source
, show
= show
, status
= status
, ptype
= ptype
)
321 def sendMSNUserPresence ( self
, userHandle
, to
= None ):
322 msnContact
= self
. getContacts (). getContact ( userHandle
)
324 self
. sendMSNContactPresence ( msnContact
, to
)
326 def sendLists ( self
, resource
):
327 """ Sends a copy of the MSN contact presences to this resource """
328 debug
. log ( "LegacyConnection: \" %s \" - sendLists( \" %s \" )" % ( self
. session
. jabberID
, resource
))
329 fulljid
= self
. session
. jabberID
331 fulljid
+= "/" + resource
332 if ( self
. getContacts ()):
333 for userHandle
in self
. getContacts (). getContacts ():
334 self
. sendMSNUserPresence ( userHandle
, fulljid
)
336 def listSynchronized ( self
):
338 self
. session
. sendPresence ( to
= self
. session
. jabberID
, fro
= config
. jid
)
339 self
. subscriptions
. syncJabberLegacyLists ()
340 self
. listSynced
= True
341 self
. subscriptions
. flushSubscriptionBuffer ()
343 def gotMessage ( self
, remoteUser
, resource
, text
):
344 source
= msn2jid ( remoteUser
)
345 self
. session
. sendMessage ( self
. jidRes ( resource
), fro
= source
, body
= text
, mtype
= "chat" )
349 debug
. log ( "LegacyConnection: \" %s \" - loggedIn()" % ( self
. session
. jabberID
))
350 self
. session
. ready
= True
352 def contactStatusChanged ( self
, remoteUser
):
353 if ( self
. session
): # Make sure the transport isn't shutting down
354 debug
. log ( "LegacyConnection: \" %s \" - contactStatusChanged( \" %s \" )" % ( self
. session
. jabberID
, remoteUser
))
355 self
. sendMSNUserPresence ( remoteUser
)
357 def ourStatusChanged ( self
, statusCode
):
358 # Send out a new presence packet to the Jabber user so that the MSN-t icon changes
361 to
= self
. session
. jabberID
362 show
, ptype
= state2presence ( statusCode
)
363 debug
. log ( "LegacyConnection: \" %s \" - ourStatusChanged( \" %s \" )" % ( self
. session
. jabberID
, statusCode
))
364 self
. session
. sendPresence ( to
= to
, fro
= source
, show
= show
)
366 def userMapping ( self
, passport
, jid
):
367 text
= lang
. get ( self
. session
. lang
). userMapping
% ( passport
, jid
)
368 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= msn2jid ( passport
), body
= text
)
370 def userAddedMe ( self
, userHandle
):
371 self
. subscriptions
. msnContactAddedMe ( userHandle
)
373 def userRemovedMe ( self
, userHandle
):
374 self
. subscriptions
. msnContactRemovedMe ( userHandle
)
376 def serverGoingDown ( self
):
378 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, body
= lang
. get ( self
. session
. lang
). msnMaintenance
)
380 def multipleLogin ( self
):
382 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, body
= lang
. get ( self
. session
. lang
). msnMultipleLogin
)
383 self
. session
. removeMe ()
385 def accountNotVerified ( self
):
387 text
= lang
. get ( self
. session
. lang
). msnNotVerified
% ( self
. session
. username
)
388 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, body
= text
)
390 def loginFailure ( self
, message
):
392 text
= lang
. get ( self
. session
. lang
). msnLoginFailure
% ( self
. session
. username
)
393 self
. session
. sendErrorMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, etype
= "auth" , condition
= "not-authorized" , explanation
= text
, body
= "Login Failure" )
395 def failedMessage ( self
, remoteUser
, message
):
397 fro
= msn2jid ( remoteUser
)
398 self
. session
. sendErrorMessage ( to
= self
. session
. jabberID
, fro
= fro
, etype
= "wait" , condition
= "recipient-unavailable" , explanation
= lang
. get ( self
. session
. lang
). msnFailedMessage
, body
= message
)
400 def initialEmailNotification ( self
, inboxunread
, foldersunread
):
401 text
= lang
. get ( self
. session
. lang
). msnInitialMail
% ( inboxunread
, foldersunread
)
402 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, body
= text
, mtype
= "headline" )
404 def realtimeEmailNotification ( self
, mailfrom
, fromaddr
, subject
):
405 text
= lang
. get ( self
. session
. lang
). msnRealtimeMail
% ( mailfrom
, fromaddr
, subject
)
406 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, body
= text
, mtype
= "headline" )
408 def connectionLost ( self
, reason
):
410 debug
. log ( "LegacyConnection: \" %s \" - connectionLost( \" %s \" )" % ( self
. session
. jabberID
, reason
))
411 text
= lang
. get ( self
. session
. lang
). msnDisconnected
% ( "Error" ) # FIXME, a better error would be nice =P
412 self
. session
. sendMessage ( to
= self
. session
. jabberID
, fro
= config
. jid
, body
= text
)
413 self
. session
. removeMe () # Tear down the session