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