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