]> code.delx.au - pymsnt/blob - src/legacy/glue.py
Sensible messages are now sent to both participants if a file is rejected for being...
[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
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 text = lang.get(self.session.lang).msnDisconnected % reason
382 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
383 self.session.removeMe() # Tear down the session
384
385 def multipleLogin(self):
386 LogEvent(INFO, self.jabberID)
387 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMultipleLogin)
388 self.session.removeMe()
389
390 def serverGoingDown(self):
391 LogEvent(INFO, self.jabberID)
392 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=lang.get(self.session.lang).msnMaintenance)
393
394 def accountNotVerified(self):
395 LogEvent(INFO, self.jabberID)
396 text = lang.get(self.session.lang).msnNotVerified % (self.session.username)
397 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text)
398
399 def userMapping(self, passport, jid):
400 LogEvent(INFO, self.jabberID)
401 text = lang.get(self.session.lang).userMapping % (passport, jid)
402 self.session.sendMessage(to=self.jabberID, fro=msn2jid(passport, True), body=text)
403
404 def loggedIn(self):
405 LogEvent(INFO, self.jabberID)
406 self.session.ready = True
407
408 def listSynchronized(self):
409 LogEvent(INFO, self.jabberID)
410 self.session.sendPresence(to=self.jabberID, fro=config.jid)
411 self.legacyList.syncJabberLegacyLists()
412 self.listSynced = True
413 #self.legacyList.flushSubscriptionBuffer()
414
415 def ourStatusChanged(self, statusCode, screenName, personal):
416 # Send out a new presence packet to the Jabber user so that the transport icon changes
417 LogEvent(INFO, self.jabberID)
418 self.remoteShow, ptype = state2presence(statusCode)
419 self.remoteStatus = personal
420 self.remoteNick = screenName
421 self.sendShowStatus()
422
423 def gotMessage(self, remoteUser, text):
424 LogEvent(INFO, self.jabberID)
425 source = msn2jid(remoteUser, True)
426 if self.contactTyping.has_key(remoteUser):
427 del self.contactTyping[remoteUser]
428 self.session.sendMessage(self.jabberID, fro=source, body=text, mtype="chat")
429 self.session.pytrans.statistics.stats["MessageCount"] += 1
430
431 def gotGroupchat(self, msnGroupchat, userHandle):
432 LogEvent(INFO, self.jabberID)
433 msnGroupchat.groupchat = LegacyGroupchat(self.session, switchboardSession=msnGroupchat)
434 msnGroupchat.groupchat.sendUserInvite(msn2jid(userHandle, True))
435
436 def gotContactTyping(self, contact):
437 LogEvent(INFO, self.jabberID)
438 # Check if the contact has only just started typing
439 if not self.contactTyping.has_key(contact):
440 self.session.sendTypingNotification(self.jabberID, msn2jid(contact, True), True)
441
442 # Reset the counter
443 self.contactTyping[contact] = 0
444
445 def failedMessage(self, remoteUser, message):
446 LogEvent(INFO, self.jabberID)
447 self.session.pytrans.statistics.stats["FailedMessageCount"] += 1
448 fro = msn2jid(remoteUser, True)
449 self.session.sendErrorMessage(to=self.jabberID, fro=fro, etype="wait", condition="recipient-unavailable", explanation=lang.get(self.session.lang).msnFailedMessage, body=message)
450
451 def contactAvatarChanged(self, userHandle, hash):
452 LogEvent(INFO, self.jabberID)
453 jid = msn2jid(userHandle, False)
454 c = self.session.contactList.findContact(jid)
455 if not c: return
456
457 if hash:
458 # New avatar
459 av = self.session.pytrans.avatarCache.getAvatar(hash)
460 if av:
461 msnContact = self.getContacts().getContact(userHandle)
462 msnContact.msnobjGot = True
463 c.updateAvatar(av)
464 else:
465 def updateAvatarCB((imageData,)):
466 av = self.session.pytrans.avatarCache.setAvatar(imageData)
467 c.updateAvatar(av)
468 d = self.sendAvatarRequest(userHandle)
469 if d:
470 d.addCallback(updateAvatarCB)
471 else:
472 # They've turned off their avatar
473 global defaultAvatar
474 c.updateAvatar(defaultAvatar)
475
476 def contactStatusChanged(self, remoteUser):
477 LogEvent(INFO, self.jabberID)
478
479 msnContact = self.getContacts().getContact(remoteUser)
480 c = self.session.contactList.findContact(msn2jid(remoteUser, False))
481 if not (c and msnContact): return
482
483 show, ptype = state2presence(msnContact.status)
484 status = msnContact.personal.decode("utf-8")
485 screenName = msnContact.screenName.decode("utf-8")
486
487 c.updateNickname(screenName, push=False)
488 c.updatePresence(show, status, ptype, force=True)
489
490 def gotFileReceive(self, fileReceive):
491 LogEvent(INFO, self.jabberID)
492 # FIXME
493 ft.FTReceive(self.session, msn2jid(fileReceive.userHandle, True), fileReceive)
494
495 def contactAddedMe(self, userHandle):
496 LogEvent(INFO, self.jabberID)
497 self.session.contactList.getContact(msn2jid(userHandle, False)).contactRequestsAuth()
498
499 def contactRemovedMe(self, userHandle):
500 LogEvent(INFO, self.jabberID)
501 c = self.session.contactList.getContact(msn2jid(userHandle, True))
502 c.contactDerequestsAuth()
503 c.contactRemovesAuth()
504
505 def gotInitialEmailNotification(self, inboxunread, foldersunread):
506 if config.mailNotifications:
507 LogEvent(INFO, self.jabberID)
508 text = lang.get(self.session.lang).msnInitialMail % (inboxunread, foldersunread)
509 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text, mtype="headline")
510
511 def gotRealtimeEmailNotification(self, mailfrom, fromaddr, subject):
512 if config.mailNotifications:
513 LogEvent(INFO, self.jabberID)
514 text = lang.get(self.session.lang).msnRealtimeMail % (mailfrom, fromaddr, subject)
515 self.session.sendMessage(to=self.jabberID, fro=config.jid, body=text, mtype="headline")
516
517 def gotMSNAlert(self, text, actionurl, subscrurl):
518 LogEvent(INFO, self.jabberID)
519
520 el = Element((None, "message"))
521 el.attributes["to"] = self.jabberID
522 el.attributes["from"] = config.jid
523 el.attributes["type"] = "headline"
524 body = el.addElement("body")
525 body.addContent(text)
526
527 x = el.addElement("x")
528 x.attributes["xmlns"] = "jabber:x:oob"
529 x.addElement("desc").addContent("More information on this notice.")
530 x.addElement("url").addContent(actionurl)
531
532 x = el.addElement("x")
533 x.attributes["xmlns"] = "jabber:x:oob"
534 x.addElement("desc").addContent("Manage subscriptions to alerts.")
535 x.addElement("url").addContent(subscrurl)
536
537 self.session.pytrans.send(el)
538
539 def gotAvatarImageData(self, userHandle, imageData):
540 LogEvent(INFO, self.jabberID)
541 av = self.session.pytrans.avatarCache.setAvatar(imageData)
542 jid = msn2jid(userHandle, False)
543 c = self.session.contactList.findContact(jid)
544 c.updateAvatar(av)
545
546
547
548
549 class LegacyList:
550 def __init__(self, session):
551 self.jabberID = session.jabberID
552 self.session = session
553 self.subscriptionBuffer = []
554
555 def removeMe(self):
556 self.subscriptionBuffer = None
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 self.session.contactList.getContact(jid).contactGrantsAuth()
564
565 def removeContact(self, jid):
566 LogEvent(INFO, self.jabberID)
567 jid = jid2msn(jid)
568 self.session.legacycon.remContact(msn.FORWARD_LIST, jid)
569
570
571 def authContact(self, jid):
572 LogEvent(INFO, self.jabberID)
573 jid = jid2msn(jid)
574 d = self.session.legacycon.remContact(msn.PENDING_LIST, jid)
575 if d:
576 self.session.legacycon.addContact(msn.REVERSE_LIST, jid)
577 self.session.legacycon.remContact(msn.BLOCK_LIST, jid)
578 self.session.legacycon.addContact(msn.ALLOW_LIST, jid)
579
580 def deauthContact(self, jid):
581 LogEvent(INFO, self.jabberID)
582 jid = jid2msn(jid)
583 self.session.legacycon.remContact(msn.ALLOW_LIST, jid)
584 self.session.legacycon.remContact(msn.PENDING_LIST, jid)
585 self.session.legacycon.addContact(msn.BLOCK_LIST, jid)
586
587
588
589 def syncJabberLegacyLists(self):
590 """ Synchronises the MSN contact list on server with the Jabber contact list """
591
592 global defaultAvatar
593
594 # We have to make an MSNContactList from the XDB data, then compare it with the one the server sent
595 # Any subscription changes must be sent to the client, as well as changed in the XDB
596 LogEvent(INFO, self.jabberID, "Start.")
597 result = self.session.pytrans.xdb.request(self.jabberID, disco.IQROSTER)
598 oldContactList = msn.MSNContactList()
599 if result:
600 for item in result.elements():
601 user = item.getAttribute("jid")
602 sub = item.getAttribute("subscription")
603 lists = item.getAttribute("lists")
604 if not lists:
605 lists = jabsub2msnlist(sub) # Backwards compatible
606 lists = int(lists)
607 contact = msn.MSNContact(userHandle=user, screenName="", lists=lists)
608 oldContactList.addContact(contact)
609
610 newXDB = Element((None, "query"))
611 newXDB.attributes["xmlns"] = disco.IQROSTER
612
613 contactList = self.session.legacycon.getContacts()
614
615
616 # Convienence functions
617 def addedToList(num):
618 return (not (oldLists & num) and (lists & num))
619 def removedFromList(num):
620 return ((oldLists & num) and not (lists & num))
621
622 for contact in contactList.contacts.values():
623 # Compare with the XDB <item/> entry
624 oldContact = oldContactList.getContact(contact.userHandle)
625 if oldContact == None:
626 oldLists = 0
627 else:
628 oldLists = oldContact.lists
629 lists = contact.lists
630
631 # Create the Jabber representation of the
632 # contact base on the old list data and then
633 # sync it with current
634 jabContact = self.session.contactList.createContact(msn2jid(contact.userHandle, False), msnlist2jabsub(oldLists))
635 jabContact.updateAvatar(defaultAvatar, push=False)
636
637 if addedToList(msn.FORWARD_LIST):
638 jabContact.syncGroups(getGroupNames(contact, contactList), push=False)
639 jabContact.syncContactGrantedAuth()
640
641 if removedFromList(msn.FORWARD_LIST):
642 jabContact.syncContactRemovedAuth()
643
644 if addedToList(msn.ALLOW_LIST):
645 jabContact.syncUserGrantedAuth()
646
647 if addedToList(msn.BLOCK_LIST) or removedFromList(msn.ALLOW_LIST):
648 jabContact.syncUserRemovedAuth()
649
650 if (not (lists & msn.ALLOW_LIST) and not (lists & msn.BLOCK_LIST) and (lists & msn.REVERSE_LIST)) or (lists & msn.PENDING_LIST):
651 jabContact.contactRequestsAuth()
652
653 if removedFromList(msn.REVERSE_LIST):
654 jabContact.contactDerequestsAuth()
655
656 jabContact.syncRoster()
657
658 item = newXDB.addElement("item")
659 item.attributes["jid"] = contact.userHandle
660 item.attributes["subscription"] = msnlist2jabsub(lists)
661 item.attributes["lists"] = str(lists)
662
663 # Update the XDB
664 self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
665 LogEvent(INFO, self.jabberID, "End.")
666
667 def saveLegacyList(self):
668 contactList = self.session.legacycon.getContacts()
669 if not contactList: return
670
671 newXDB = Element((None, "query"))
672 newXDB.attributes["xmlns"] = disco.IQROSTER
673
674 for contact in contactList.contacts.values():
675 item = newXDB.addElement("item")
676 item.attributes["jid"] = contact.userHandle
677 item.attributes["subscription"] = msnlist2jabsub(contact.lists) # Backwards compat
678 item.attributes["lists"] = str(contact.lists)
679
680 self.session.pytrans.xdb.set(self.jabberID, disco.IQROSTER, newXDB)
681 LogEvent(INFO, self.jabberID, "Finished saving list.")
682
683