]> code.delx.au - pymsnt/blob - src/legacy/glue.py
c00be73b341226805a33fa4659c545b5973e24f3
[pymsnt] / src / legacy / glue.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 import os.path
5 import utils
6 from twisted.internet import task, error
7 from tlib.xmlw import Element
8 from tlib import msn
9 from debug import LogEvent, INFO, WARN, ERROR
10 import disco
11 import groupchat
12 import ft
13 import avatar
14 import config
15 import lang
16
17
18
19
20 url = "http://msn-transport.jabberstudio.org"
21 version = "0.11-dev" # The transport version
22 mangle = True # XDB '@' -> '%' mangling
23 id = "msn" # The transport identifier
24
25
26 # Load the default avatars
27 f = open(os.path.join("data", "defaultJabberAvatar.png"), "rb")
28 defaultJabberAvatarData = f.read()
29 f.close()
30
31 f = open(os.path.join("data", "defaultMSNAvatar.png"), "rb")
32 defaultAvatarData = f.read()
33 f.close()
34 defaultAvatar = avatar.AvatarCache().setAvatar(defaultAvatarData)
35
36
37 def reloadConfig():
38 msn.MSNConnection.GETALLAVATARS = config.getAllAvatars
39
40 def isGroupJID(jid):
41 """ Returns True if the JID passed is a valid groupchat JID (for MSN, does not contain '%') """
42 return (jid.find('%') == -1)
43
44
45
46 # This should be set to the name space the registration entries are in, in the xdb spool
47 namespace = "jabber:iq:register"
48
49
50 def formRegEntry(username, password):
51 """ Returns a domish.Element representation of the data passed. This element will be written to the XDB spool file """
52 reginfo = Element((None, "query"))
53 reginfo.attributes["xmlns"] = "jabber:iq:register"
54
55 userEl = reginfo.addElement("username")
56 userEl.addContent(username)
57
58 passEl = reginfo.addElement("password")
59 passEl.addContent(password)
60
61 return reginfo
62
63
64
65
66 def getAttributes(base):
67 """ This function should, given a spool domish.Element, pull the username, password,
68 and out of it and return them """
69 username = ""
70 password = ""
71 for child in base.elements():
72 try:
73 if child.name == "username":
74 username = child.__str__()
75 elif child.name == "password":
76 password = child.__str__()
77 except AttributeError:
78 continue
79
80 return username, password
81
82
83 def startStats(statistics):
84 stats = statistics.stats
85 stats["MessageCount"] = 0
86 stats["FailedMessageCount"] = 0
87 stats["AvatarCount"] = 0
88 stats["FailedAvatarCount"] = 0
89
90 def updateStats(statistics):
91 stats = statistics.stats
92 # FIXME
93 #stats["AvatarCount"] = msnp2p.MSNP2P_Avatar.TRANSFER_COUNT
94 #stats["FailedAvatarCount"] = msnp2p.MSNP2P_Avatar.ERROR_COUNT
95
96
97 msn2jid_cache = {}
98 def msn2jid(msnid, withResource):
99 """ Converts a MSN passport into a JID representation to be used with the transport """
100 global msn2jid_cache
101 global jid2msn_cache
102
103 if msn2jid_cache.has_key(msnid):
104 jid = msn2jid_cache[msnid]
105 if withResource:
106 jid += "/msn"
107 return jid
108 else:
109 if msnid.startswith("tel:+"):
110 msnid = msnid.replace("tel:+", "") + "%tel"
111 jid = msnid.replace('@', '%') + "@" + config.jid + (withResource and "/msn" or "")
112 msn2jid_cache[msnid] = jid
113 jid2msn_cache[jid] = msnid
114 return jid
115
116 # Marks this as the function to be used in jabber:iq:gateway (Service ID Translation)
117 def translateAccount(msnid):
118 return msn2jid(msnid, False)
119
120 jid2msn_cache = {}
121 def jid2msn(jid):
122 """ Converts a JID representation of a MSN passport into the original MSN passport """
123 global jid2msn_cache
124 global msn2jid_cache
125
126 if jid2msn_cache.has_key(jid):
127 msnid = jid2msn_cache[jid]
128 return msnid
129 else:
130 if jid.find("%tel@") > 0:
131 jid = "tel:+" + jid.replace("%tel@", "@")
132 msnid = unicode(jid[:jid.find('@')].replace('%', '@')).split("/")[0]
133 jid2msn_cache[jid] = msnid
134 msn2jid_cache[msnid] = jid
135 return msnid
136
137
138 def presence2state(show, ptype):
139 """ Converts a Jabber presence into an MSN status code """
140 if ptype == "unavailable":
141 return msn.STATUS_OFFLINE
142 elif not show or show == "online" or show == "chat":
143 return msn.STATUS_ONLINE
144 elif show == "dnd":
145 return msn.STATUS_BUSY
146 elif show == "away" or show == "xa":
147 return msn.STATUS_AWAY
148 return msn.STATUS_ONLINE
149
150
151 def state2presence(state):
152 """ Converts a MSN status code into a Jabber presence """
153 if state == msn.STATUS_ONLINE:
154 return (None, None)
155 elif state == msn.STATUS_BUSY:
156 return ("dnd", None)
157 elif state == msn.STATUS_AWAY:
158 return ("away", None)
159 elif state == msn.STATUS_IDLE:
160 return ("away", None)
161 elif state == msn.STATUS_BRB:
162 return ("away", None)
163 elif state == msn.STATUS_PHONE:
164 return ("dnd", None)
165 elif state == msn.STATUS_LUNCH:
166 return ("away", None)
167 else:
168 return (None, "unavailable")
169
170
171 def getGroupNames(msnContact, msnContactList):
172 """ Gets a list of groups that this contact is in """
173 groups = []
174 for groupGUID in msnContact.groups:
175 try:
176 groups.append(msnContactList.groups[groupGUID])
177 except KeyError:
178 pass
179 return groups
180
181 def msnlist2jabsub(lists):
182 """ Converts MSN contact lists ORed together into the corresponding Jabber subscription state """
183 if lists & msn.FORWARD_LIST and lists & msn.REVERSE_LIST:
184 return "both"
185 elif lists & msn.REVERSE_LIST:
186 return "from"
187 elif lists & msn.FORWARD_LIST:
188 return "to"
189 else:
190 return "none"
191
192
193 def jabsub2msnlist(sub):
194 """ Converts a Jabber subscription state into the corresponding MSN contact lists ORed together """
195 if sub == "to":
196 return msn.FORWARD_LIST
197 elif sub == "from":
198 return msn.REVERSE_LIST
199 elif sub == "both":
200 return (msn.FORWARD_LIST | msn.REVERSE_LIST)
201 else:
202 return 0
203
204
205
206
207
208 # This class handles groupchats with the legacy protocol
209 class LegacyGroupchat(groupchat.BaseGroupchat):
210 def __init__(self, session, resource=None, ID=None, switchboardSession=None):
211 """ Possible entry points for groupchat
212 - User starts an empty switchboard session by sending presence to a blank room
213 - An existing switchboard session is joined by another MSN user
214 - User invited to an existing switchboard session with more than one user
215 """
216 groupchat.BaseGroupchat.__init__(self, session, resource, ID)
217 if switchboardSession:
218 self.switchboardSession = switchboardSession
219 else:
220 self.switchboardSession = msn.MultiSwitchboardSession(self.session.legacycon)
221 self.switchboardSession.groupchat = self
222
223 LogEvent(INFO, self.roomJID())
224
225 def removeMe(self):
226 if self.switchboardSession.transport:
227 self.switchboardSession.transport.loseConnection()
228 self.switchboardSession.groupchat = None
229 del self.switchboardSession
230 groupchat.BaseGroupchat.removeMe(self)
231 LogEvent(INFO, self.roomJID())
232
233 def sendLegacyMessage(self, message, noerror):
234 LogEvent(INFO, self.roomJID())
235 self.switchboardSession.sendMessage(message.replace("\n", "\r\n"), noerror)
236
237 def sendContactInvite(self, contactJID):
238 LogEvent(INFO, self.roomJID())
239 userHandle = jid2msn(contactJID)
240 self.switchboardSession.inviteUser(userHandle)
241
242 def gotMessage(self, userHandle, text):
243 LogEvent(INFO, self.roomJID())
244 self.messageReceived(userHandle, text)
245
246
247
248 # This class handles most interaction with the legacy protocol
249 class LegacyConnection(msn.MSNConnection):
250 """ A glue class that connects to the legacy network """
251 def __init__(self, username, password, session):
252 self.jabberID = session.jabberID
253
254 self.session = session
255 self.listSynced = False
256 self.initialListVersion = 0
257
258 self.remoteShow = ""
259 self.remoteStatus = ""
260 self.remoteNick = ""
261
262 # Init the MSN bits
263 msn.MSNConnection.__init__(self, username, password, self.jabberID)
264
265 # User typing notification stuff
266 self.userTyping = dict() # Indexed by contact MSN ID, stores whether the user is typing to this contact
267 # Contact typing notification stuff
268 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
269 # Looping function
270 self.userTypingSend = task.LoopingCall(self.sendTypingNotifications)
271 self.userTypingSend.start(5.0)
272
273 self.legacyList = LegacyList(self.session)
274
275 LogEvent(INFO, self.jabberID)
276
277 def removeMe(self):
278 LogEvent(INFO, self.jabberID)
279
280 self.userTypingSend.stop()
281
282 self.legacyList.removeMe()
283 self.legacyList = None
284 self.session = None
285 self.logOut()
286
287
288 # Implemented from baseproto
289 def sendShowStatus(self, jid=None):
290 if not self.session: return
291 source = config.jid
292 if not jid:
293 jid = self.jabberID
294 self.session.sendPresence(to=jid, fro=source, show=self.remoteShow, status=self.remoteStatus, nickname=self.remoteNick)
295
296 def resourceOffline(self, resource):
297 pass
298
299 def highestResource(self):
300 """ Returns highest priority resource """
301 return self.session.highestResource()
302
303 def sendMessage(self, dest, resource, body, noerror):
304 dest = jid2msn(dest)
305 if self.userTyping.has_key(dest):
306 del self.userTyping[dest]
307 try:
308 msn.MSNConnection.sendMessage(self, dest, body, noerror)
309 self.session.pytrans.statistics.stats["MessageCount"] += 1
310 except:
311 self.failedMessage(dest, body)
312 raise
313
314 def sendFile(self, dest, ftSend):
315 dest = jid2msn(dest)
316 def continueSendFile1((msnFileSend, d)):
317 def continueSendFile2((success, )):
318 if success:
319 ftSend.accept(msnFileSend)
320 else:
321 sendFileFail()
322 d.addCallbacks(continueSendFile2, sendFileFail)
323
324 def sendFileFail():
325 ftSend.reject()
326
327 d = msn.MSNConnection.sendFile(self, dest, ftSend.filename, ftSend.filesize)
328 d.addCallbacks(continueSendFile1, sendFileFail)
329
330 def setStatus(self, nickname, show, status):
331 statusCode = presence2state(show, None)
332 msn.MSNConnection.changeStatus(self, statusCode, nickname, status)
333
334 def updateAvatar(self, av=None):
335 global defaultJabberAvatarData
336
337 if av:
338 msn.MSNConnection.changeAvatar(self, av.getImageData())
339 else:
340 msn.MSNConnection.changeAvatar(self, defaultJabberAvatarData)
341
342 def sendTypingNotifications(self):
343 if not self.session: return
344
345 # Send any typing notification messages to the user's contacts
346 for contact in self.userTyping.keys():
347 if self.userTyping[contact]:
348 self.sendTypingToContact(contact)
349
350 # Send any typing notification messages from contacts to the user
351 for contact in self.contactTyping.keys():
352 self.contactTyping[contact] += 1
353 if self.contactTyping[contact] >= 3:
354 self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), False)
355 del self.contactTyping[contact]
356
357 def userTypingNotification(self, dest, resource, composing):
358 if not self.session: return
359 dest = jid2msn(dest)
360 self.userTyping[dest] = composing
361 if composing: # Make it instant
362 self.sendTypingToContact(dest)
363
364
365
366 # Implement callbacks from msn.MSNConnection
367 def connectionFailed(self, reason):
368 LogEvent(INFO, self.jabberID)
369 text = lang.get(self.session.lang).msnConnectFailed % reason
370 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
371 self.session.removeMe()
372
373 def loginFailed(self, reason):
374 LogEvent(INFO, self.jabberID)
375 text = lang.get(self.session.lang).msnLoginFailure % (self.session.username)
376 self.session.sendErrorMessage(to=self.jabberID, fro=config.jid, etype="auth", condition="not-authorized", explanation=text, body="Login Failure")
377 self.session.removeMe()
378
379 def connectionLost(self, reason):
380 LogEvent(INFO, self.jabberID)
381 if reason.type != error.ConnectionDone:
382 text = lang.get(self.session.lang).msnDisconnected % reason
383 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
384 self.session.removeMe() # Tear down the session
385
386 def multipleLogin(self):
387 LogEvent(INFO, self.jabberID)
388 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
389 self.session.removeMe()
390
391 def serverGoingDown(self):
392 LogEvent(INFO, self.jabberID)
393 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
394
395 def accountNotVerified(self):
396 LogEvent(INFO, self.jabberID)
397 text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
398 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
399
400 def userMapping(self, passport, jid):
401 LogEvent(INFO, self.jabberID)
402 text = lang.get(self.session.lang).userMapping % (passport, jid)
403 self.session.sendMessage(to=self.jabberID, fro=msn2jid(passport, True), body=text)
404
405 def loggedIn(self):
406 LogEvent(INFO, self.jabberID)
407 self.session.ready = True
408
409 def listSynchronized(self):
410 LogEvent(INFO, self.jabberID)
411 self.session.sendPresence(to=self.jabberID, fro=config.jid)
412 self.legacyList.syncJabberLegacyLists()
413 self.listSynced = True
414 #self.legacyList.flushSubscriptionBuffer()
415
416 def ourStatusChanged(self, statusCode, screenName, personal):
417 # Send out a new presence packet to the Jabber user so that the transport icon changes
418 LogEvent(INFO, self.jabberID)
419 self.remoteShow, ptype = state2presence(statusCode)
420 self.remoteStatus = personal
421 self.remoteNick = screenName
422 self.sendShowStatus()
423
424 def gotMessage(self, remoteUser, text):
425 LogEvent(INFO, self.jabberID)
426 source = msn2jid(remoteUser, True)
427 if self.contactTyping.has_key(remoteUser):
428 del self.contactTyping[remoteUser]
429 self.session.sendMessage(self.jabberID, fro=source, body=text, mtype="chat")
430 self.session.pytrans.statistics.stats["MessageCount"] += 1
431
432 def gotGroupchat(self, msnGroupchat, userHandle):
433 LogEvent(INFO, self.jabberID)
434 msnGroupchat.groupchat = LegacyGroupchat(self.session, switchboardSession=msnGroupchat)
435 msnGroupchat.groupchat.sendUserInvite(msn2jid(userHandle, True))
436
437 def gotContactTyping(self, contact):
438 LogEvent(INFO, self.jabberID)
439 # Check if the contact has only just started typing
440 if not self.contactTyping.has_key(contact):
441 self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), True)
442
443 # Reset the counter
444 self.contactTyping[contact] = 0
445
446 def failedMessage(self, remoteUser, message):
447 LogEvent(INFO, self.jabberID)
448 self.session.pytrans.statistics.stats["FailedMessageCount"] += 1
449 fro = msn2jid(remoteUser, True)
450 self.session.sendErrorMessage(to=self.jabberID, fro=fro, etype="wait", condition="recipient-unavailable", explanation=lang.get(self.session.lang).msnFailedMessage, body=message)
451
452 def contactAvatarChanged(self, userHandle, hash):
453 LogEvent(INFO, self.jabberID)
454 jid = msn2jid(userHandle, False)
455 c = self.session.contactList.findContact(jid)
456 if not c: return
457
458 if hash:
459 # New avatar
460 av = self.session.pytrans.avatarCache.getAvatar(hash)
461 if av:
462 msnContact = self.getContacts().getContact(userHandle)
463 msnContact.msnobjGot = True
464 c.updateAvatar(av)
465 else:
466 def updateAvatarCB((imageData,)):
467 av = self.session.pytrans.avatarCache.setAvatar(imageData)
468 c.updateAvatar(av)
469 d = self.sendAvatarRequest(userHandle)
470 if d:
471 d.addCallback(updateAvatarCB)
472 else:
473 # They've turned off their avatar
474 global defaultAvatar
475 c.updateAvatar(defaultAvatar)
476
477 def contactStatusChanged(self, remoteUser):
478 LogEvent(INFO, self.jabberID)
479
480 msnContact = self.getContacts().getContact(remoteUser)
481 c = self.session.contactList.findContact(msn2jid(remoteUser, False))
482 if not (c and msnContact): return
483
484 show, ptype = state2presence(msnContact.status)
485 status = msnContact.personal.decode("utf-8")
486 screenName = msnContact.screenName.decode("utf-8")
487
488 c.updateNickname(screenName, push=False)
489 c.updatePresence(show, status, ptype, force=True)
490
491 def gotFileReceive(self, fileReceive):
492 LogEvent(INFO, self.jabberID)
493 # FIXME
494 ft.FTReceive(self.session, msn2jid(fileReceive.userHandle, True), fileReceive)
495
496 def contactAddedMe(self, userHandle):
497 LogEvent(INFO, self.jabberID)
498 self.session.contactList.getContact(msn2jid(userHandle, False)).contactRequestsAuth()
499
500 def contactRemovedMe(self, userHandle):
501 LogEvent(INFO, self.jabberID)
502 c = self.session.contactList.getContact(msn2jid(userHandle, True))
503 c.contactDerequestsAuth()
504 c.contactRemovesAuth()
505
506 def gotInitialEmailNotification(self, inboxunread, foldersunread):
507 if config.mailNotifications:
508 LogEvent(INFO, self.jabberID)
509 text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread)
510 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text, mtype="headline")
511
512 def gotRealtimeEmailNotification(self, mailfrom, fromaddr, subject):
513 if config.mailNotifications:
514 LogEvent(INFO, self.jabberID)
515 text = lang.get(self.session.lang).msnRealtimeMail % (mailfrom, fromaddr, subject)
516 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text, mtype="headline")
517
518 def gotMSNAlert(self, text, actionurl, subscrurl):
519 LogEvent(INFO, self.jabberID)
520
521 el = Element((None, "message"))
522 el.attributes["to"] = self.jabberID
523 el.attributes["from"] = config.jid
524 el.attributes["type"] = "headline"
525 body = el.addElement("body")
526 body.addContent(text)
527
528 x = el.addElement("x")
529 x.attributes["xmlns"] = "jabber:x:oob"
530 x.addElement("desc").addContent("More information on this notice.")
531 x.addElement("url").addContent(actionurl)
532
533 x = el.addElement("x")
534 x.attributes["xmlns"] = "jabber:x:oob"
535 x.addElement("desc").addContent("Manage subscriptions to alerts.")
536 x.addElement("url").addContent(subscrurl)
537
538 self.session.pytrans.send(el)
539
540 def gotAvatarImageData(self, userHandle, imageData):
541 LogEvent(INFO, self.jabberID)
542 av = self.session.pytrans.avatarCache.setAvatar(imageData)
543 jid = msn2jid(userHandle, False)
544 c = self.session.contactList.findContact(jid)
545 c.updateAvatar(av)
546
547
548
549
550 class LegacyList:
551 def __init__(self, session):
552 self.jabberID = session.jabberID
553 self.session = session
554 self.subscriptionBuffer = []
555
556 def removeMe(self):
557 self.subscriptionBuffer = None
558 self.session = None
559
560 def addContact(self, jid):
561 LogEvent(INFO, self.jabberID)
562 userHandle = jid2msn(jid)
563 self.session.legacycon.addContact(msn.FORWARD_LIST, userHandle)
564
565 # Handle adding a contact that has previously been removed
566 msnContact = self.session.legacycon.getContacts().getContact(userHandle)
567 if msnContact and msnContact.lists & msn.REVERSE_LIST:
568 self.session.legacycon.contactAddedMe(userHandle)
569 self.authContact(jid)
570 self.session.contactList.getContact(jid).contactGrantsAuth()
571
572 def removeContact(self, jid):
573 LogEvent(INFO, self.jabberID)
574 jid = jid2msn(jid)
575 self.session.legacycon.remContact(msn.FORWARD_LIST, jid)
576
577
578 def authContact(self, jid):
579 LogEvent(INFO, self.jabberID)
580 userHandle = jid2msn(jid)
581 d = self.session.legacycon.remContact(msn.PENDING_LIST, userHandle)
582 if d:
583 self.session.legacycon.addContact(msn.REVERSE_LIST, userHandle)
584 self.session.legacycon.remContact(msn.BLOCK_LIST, userHandle)
585 self.session.legacycon.addContact(msn.ALLOW_LIST, userHandle)
586
587 def deauthContact(self, jid):
588 LogEvent(INFO, self.jabberID)
589 jid = jid2msn(jid)
590 self.session.legacycon.remContact(msn.ALLOW_LIST, jid)
591 self.session.legacycon.remContact(msn.PENDING_LIST, jid)
592 self.session.legacycon.addContact(msn.BLOCK_LIST, jid)
593
594
595
596 def syncJabberLegacyLists(self):
597 """ Synchronises the MSN contact list on server with the Jabber contact list """
598
599 global defaultAvatar
600
601 # We have to make an MSNContactList from the XDB data, then compare it with the one the server sent
602 # Any subscription changes must be sent to the client, as well as changed in the XDB
603 LogEvent(INFO, self.jabberID, "Start.")
604 result = self.session.pytrans.xdb.request(self.jabberID, disco.IQROSTER)
605 oldContactList = msn.MSNContactList()
606 if result:
607 for item in result.elements():
608 user = item.getAttribute("jid")
609 sub = item.getAttribute("subscription")
610 lists = item.getAttribute("lists")
611 if not lists:
612 lists = jabsub2msnlist(sub) # Backwards compatible
613 lists = int(lists)
614 contact = msn.MSNContact(userHandle=user, screenName="", lists=lists)
615 oldContactList.addContact(contact)
616
617 newXDB = Element((None, "query"))
618 newXDB.attributes["xmlns"] = disco.IQROSTER
619
620 contactList = self.session.legacycon.getContacts()
621
622
623 # Convienence functions
624 def addedToList(num):
625 return (not (oldLists & num) and (lists & num))
626 def removedFromList(num):
627 return ((oldLists & num) and not (lists & num))
628
629 for contact in contactList.contacts.values():
630 # Compare with the XDB <item/> entry
631 oldContact = oldContactList.getContact(contact.userHandle)
632 if oldContact == None:
633 oldLists = 0
634 else:
635 oldLists = oldContact.lists
636 lists = contact.lists
637
638 # Create the Jabber representation of the
639 # contact base on the old list data and then
640 # sync it with current
641 jabContact = self.session.contactList.createContact(msn2jid(contact.userHandle, False), msnlist2jabsub(oldLists))
642 jabContact.updateAvatar(defaultAvatar, push=False)
643
644 if addedToList(msn.FORWARD_LIST):
645 jabContact.syncGroups(getGroupNames(contact, contactList), push=False)
646 jabContact.syncContactGrantedAuth()
647
648 if removedFromList(msn.FORWARD_LIST):
649 jabContact.syncContactRemovedAuth()
650
651 if addedToList(msn.ALLOW_LIST):
652 jabContact.syncUserGrantedAuth()
653
654 if addedToList(msn.BLOCK_LIST) or removedFromList(msn.ALLOW_LIST):
655 jabContact.syncUserRemovedAuth()
656
657 if (not (lists & msn.ALLOW_LIST) and not (lists & msn.BLOCK_LIST) and (lists & msn.REVERSE_LIST)) or (lists & msn.PENDING_LIST):
658 jabContact.contactRequestsAuth()
659
660 if removedFromList(msn.REVERSE_LIST):
661 jabContact.contactDerequestsAuth()
662
663 jabContact.syncRoster()
664
665 item = newXDB.addElement("item")
666 item.attributes["jid"] = contact.userHandle
667 item.attributes["subscription"] = msnlist2jabsub(lists)
668 item.attributes["lists"] = str(lists)
669
670 # Update the XDB
671 self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
672 LogEvent(INFO, self.jabberID, "End.")
673
674 def saveLegacyList(self):
675 contactList = self.session.legacycon.getContacts()
676 if not contactList: return
677
678 newXDB = Element((None, "query"))
679 newXDB.attributes["xmlns"] = disco.IQROSTER
680
681 for contact in contactList.contacts.values():
682 item = newXDB.addElement("item")
683 item.attributes["jid"] = contact.userHandle
684 item.attributes["subscription"] = msnlist2jabsub(contact.lists) # Backwards compat
685 item.attributes["lists"] = str(contact.lists)
686
687 self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
688 LogEvent(INFO, self.jabberID, "Finished saving list.")
689
690