]> code.delx.au - pymsnt/blob - src/legacy/glue.py
Applied patch from the old svn memorytest tree
[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 from twisted.internet import task, error
6 from twisted.words.xish.domish import Element
7
8 from debug import LogEvent, INFO, WARN, ERROR
9 from legacy import msn
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, lambda: 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