home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2012 January / maximum-cd-2012-01.iso / DiscContents / digsby_setup.exe / lib / msn / MSNBuddy.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2011-10-05  |  40.1 KB  |  797 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.6)
  3.  
  4. import common
  5. import contacts
  6. from util import Storage, to_storage, dyn_dispatch, odict, threaded
  7. from util.primitives.funcs import get
  8. from util.xml_tag import tag
  9. from util.cacheable import urlcacheopen, cproperty
  10. from util.observe import ObservableProperty
  11. from common.sms import validate_sms, normalize_sms
  12. from util.callbacks import callsback
  13. from MSNUtil import url_decode
  14. import time
  15. import datetime
  16. import logging
  17. log = logging.getLogger('msn.buddy')
  18.  
  19. yesterday = lambda : datetime.datetime.today() - datetime.timedelta(1)
  20. import sys
  21. thismod = sys.modules[__name__]
  22. del sys
  23.  
  24. try:
  25.     _
  26. except:
  27.     
  28.     _ = lambda s: s
  29.  
  30. statuses = Storage(brb = _('Be Right Back'), phone = _('On the Phone'), lunch = _('Out to Lunch'))
  31.  
  32. class MSNBuddy(common.buddy):
  33.     __slots__ = '\n    status_message\n    msn_obj\n    _idle_start\n    pending_auth\n    phone_home\n    phone_work\n    phone_mobile\n    allow_mobile\n    enable_wireless\n    remote_alias\n    has_blog\n    client_id\n    role_ids\n    get_profile\n    _space\n    membersoap\n    contactsoap\n    '.split()
  34.     
  35.     def __init__(self, msn, name = None):
  36.         self._status = 'unknown'
  37.         self._status_message = ''
  38.         self._got_presence = False
  39.         self.phone_home = None
  40.         self.phone_work = None
  41.         self.phone_mobile = None
  42.         self.allow_mobile = None
  43.         self.enable_wireless = None
  44.         self.remote_alias = None
  45.         self.has_blog = None
  46.         self.msn_obj = None
  47.         self._idle_start = 0
  48.         self.info = { }
  49.         self.role_ids = { }
  50.         self.pending_auth = False
  51.         fixedname = ''.join(url_decode(name).split())
  52.         common.buddy.__init__(self, fixedname, msn)
  53.         if self is self.protocol.self_buddy:
  54.             profile = profile
  55.             import common
  56.             profile.account_manager.buddywatcher.unregister(self)
  57.         
  58.         self.CID = 0
  59.         self.space = None
  60.         if self._space:
  61.             
  62.             try:
  63.                 self.update_contact_card(tag(self._space))
  64.             self._space = None
  65.  
  66.         
  67.         self.mships = { }
  68.         self.contactsoap = None
  69.         self.membersoap = None
  70.         self._btype = 'im'
  71.  
  72.     
  73.     def __hash__(self):
  74.         return common.buddy.__hash__(self)
  75.  
  76.     
  77.     def sms(self):
  78.         if validate_sms(self.phone_mobile):
  79.             return self.phone_mobile
  80.         return False
  81.  
  82.     sms = property(sms)
  83.     
  84.     def _get_phone_mobile(self):
  85.         return getattr(self, '_phone_mobile', None)
  86.  
  87.     
  88.     def _set_phone_mobile(self, val):
  89.         if val and val.startswith('tel:'):
  90.             val = val[4:]
  91.         
  92.         self._phone_mobile = val
  93.  
  94.     phone_mobile = property(_get_phone_mobile, _set_phone_mobile)
  95.     
  96.     def id(self):
  97.         if self.contactsoap is not None:
  98.             v = self.contactsoap.ContactId
  99.             if v:
  100.                 return v
  101.         
  102.         if not self.guid:
  103.             pass
  104.         return self.name
  105.  
  106.     id = property(id)
  107.     
  108.     def _get_guid(self):
  109.         return getattr(self, '_guid', None)
  110.  
  111.     
  112.     def _set_guid(self, val):
  113.         if not isinstance(val, self.protocol.ns.cid_class):
  114.             raise TypeError('Was expecting type %r for guid, got %r (type=%r)', self.protocol.cid_class, val, type(val))
  115.         isinstance(val, self.protocol.ns.cid_class)
  116.         self._guid = val
  117.  
  118.     guid = property(_get_guid, _set_guid, doc = 'Contact ID, which takes the form of a UUID in modern versions of MSNP.In the past, it was simply the passport name of the contact.')
  119.     
  120.     def get_caps(self):
  121.         caps = caps
  122.         import common
  123.         buddy_caps = set(self.protocol.caps)
  124.         if not self.online:
  125.             buddy_caps.discard(caps.FILES)
  126.         
  127.         return buddy_caps
  128.  
  129.     caps = property(get_caps)
  130.     
  131.     def _set_contactsoap(self, val):
  132.         self._contactsoap = val
  133.         if self.contactsoap is not None:
  134.             phs = self.contactsoap.ContactInfo.Phones
  135.             if phs is not None and phs.ContactPhone is not None:
  136.                 for phone in phs.ContactPhone:
  137.                     if phone.ContactPhoneType == 'ContactPhoneMobile':
  138.                         
  139.                         try:
  140.                             num = normalize_sms(phone.Number)
  141.                         except:
  142.                             continue
  143.  
  144.                         if not validate_sms(num):
  145.                             continue
  146.                         
  147.                         self.phone_mobile = num
  148.                         continue
  149.                 
  150.             
  151.         
  152.  
  153.     
  154.     def _get_contactsoap(self):
  155.         return self._contactsoap
  156.  
  157.     contactsoap = property(_get_contactsoap, _set_contactsoap)
  158.     
  159.     def online(self):
  160.         if self.mobile:
  161.             return True
  162.         if self._got_presence:
  163.             pass
  164.         return self._status not in ('offline', 'unknown')
  165.  
  166.     online = property(online)
  167.     
  168.     def set_status(self, newval):
  169.         oldstatus = self._status
  170.         self._status = newval
  171.         if oldstatus != newval and self._got_presence:
  172.             self.notify('status', oldstatus, newval)
  173.         
  174.  
  175.     
  176.     def get_status(self):
  177.         if not self._got_presence:
  178.             return 'unknown'
  179.         if self._status == 'idle':
  180.             return 'idle'
  181.         if self.away:
  182.             return 'away'
  183.         if self.mobile:
  184.             return 'mobile'
  185.         return self._status
  186.  
  187.     status = ObservableProperty(get_status, set_status, observe = ('_status',))
  188.     
  189.     def profile(self):
  190.         pass
  191.  
  192.     profile = property(profile)
  193.     
  194.     def away(self):
  195.         return self._status in ('busy', 'brb', 'away', 'phone', 'lunch')
  196.  
  197.     away = property(away)
  198.     
  199.     def blocked(self):
  200.         return self in self.protocol.block_list
  201.  
  202.     blocked = property(blocked)
  203.     
  204.     def _set_status_message(self, val):
  205.         self._got_presence = True
  206.         self._status_message = val
  207.  
  208.     
  209.     def _get_status_message(self):
  210.         return self._status_message
  211.  
  212.     status_message = property(_get_status_message, _set_status_message)
  213.     
  214.     def mobile(self):
  215.         if self.sms and self.allow_mobile == 'Y':
  216.             pass
  217.         return self._status == 'offline'
  218.  
  219.     mobile = property(mobile)
  220.     
  221.     def get_profile(self):
  222.         self.protocol.get_profile(self)
  223.  
  224.     get_profile = common.action((lambda self: True))(get_profile)
  225.     
  226.     def sightly_status(self):
  227.         if self.status == 'mobile':
  228.             return _('Mobile')
  229.         return statuses.get(self._status, self._status.title())
  230.  
  231.     sightly_status = property(sightly_status)
  232.     
  233.     def block(self, _block = True, callback = None):
  234.         self.protocol.block_buddy(self, _block, callback = callback)
  235.  
  236.     block = callsback(block)
  237.     
  238.     def unblock(self, callback = None):
  239.         self.protocol.block_buddy(self, False, callback = callback)
  240.  
  241.     unblock = callsback(unblock)
  242.     
  243.     def service(self):
  244.         num_or_str = get(self, '_btype', 1)
  245.         num = get(dict(msn = 1, mob = 4, fed = 32), num_or_str, num_or_str)
  246.         if num == 32 and self.name.endswith('yahoo.com'):
  247.             prot_name = 'yahoo'
  248.         else:
  249.             prot_name = self.protocol.name
  250.         return prot_name
  251.  
  252.     service = property(service)
  253.     
  254.     def __repr__(self):
  255.         return '<MSNBuddy %s>' % self.name
  256.  
  257.     
  258.     def __str__(self):
  259.         return repr(self)
  260.  
  261.     
  262.     def get_idle(self):
  263.         return self.status == 'idle'
  264.  
  265.     
  266.     def set_idle(self, val):
  267.         pass
  268.  
  269.     idle = ObservableProperty(get_idle, set_idle, observe = ('status',))
  270.     
  271.     def update(self, new):
  272.         if not new:
  273.             return None
  274.         for field in new.__dict__:
  275.             newval = getattr(new, field)
  276.             if newval:
  277.                 setattr(self, field, newval)
  278.                 self.setnotifyif()
  279.                 continue
  280.             new
  281.         
  282.  
  283.     
  284.     def update_contact_card(self, card):
  285.         if not card:
  286.             return None
  287.         self._space = card._to_xml(pretty = False)
  288.         self.space = MSNSpace(self, card)
  289.  
  290.     
  291.     def _update_ccard_elt(self, elt, kind):
  292.         return dyn_dispatch(self, '_update_ccard_%s' % kind.lower(), elt)
  293.  
  294.     
  295.     def _update_ccard_spacetitle(self, elt):
  296.         self.space.update((lambda .0: for e in .0:
  297. if 'type' not in e._attrs:
  298. (e._name, e._cdata)continue)(elt))
  299.  
  300.     
  301.     def _update_ccard_album(self, elt):
  302.         photos = []
  303.         for subel in elt:
  304.             if subel._attrs.get('type', '') == 'Photo':
  305.                 photos.append(Storage((lambda .0: for e in .0:
  306. (e._name, e._cdata))(subel)))
  307.                 continue
  308.         
  309.         album = self.space.setdefault('album', Storage())
  310.         album.update((lambda .0: for e in .0:
  311. if 'type' not in e._attrs:
  312. (e._name, e._cdata)continue)(elt))
  313.         album.photos = photos
  314.  
  315.     _space = cproperty('')
  316.     
  317.     def _update_ccard_musiclist(self, elt):
  318.         musiclist = self.space.setdefault('musiclist', Storage())
  319.         songs = []
  320.         for song in elt:
  321.             if song._attrs.get('type', '') == 'MusicListEntry':
  322.                 songs.append(Storage((lambda .0: for e in .0:
  323. (e._name, e._cdata))(song)))
  324.                 continue
  325.         
  326.         musiclist.update((lambda .0: for e in .0:
  327. if 'type' not in e._attrs:
  328. (e._name, e._cdata)continue)(elt))
  329.         musiclist.songs = songs
  330.  
  331.     
  332.     def _update_ccard_booklist(self, elt):
  333.         booklist = self.space.setdefault('booklist', Storage())
  334.         books = []
  335.         for book in elt:
  336.             if book._attrs.get('type', '') == 'BookListEntry':
  337.                 books.append(Storage((lambda .0: for e in .0:
  338. (e._name, e._cdata))(book)))
  339.                 continue
  340.         
  341.         booklist.update((lambda .0: for e in .0:
  342. if 'type' not in e._attrs:
  343. (e._name, e._cdata)continue)(elt))
  344.         booklist.books = books
  345.  
  346.     
  347.     def _update_ccard_genericlist(self, elt):
  348.         gen_lists = self.space.setdefault('gen_lists', [])
  349.         entries = []
  350.         for entry in elt:
  351.             if entry._attrs.get('type', '') == 'GenericListEntry':
  352.                 entries.append(Storage((lambda .0: for e in .0:
  353. (e._name, e._cdata))(entry)))
  354.                 continue
  355.         
  356.         new_list = Storage((lambda .0: for e in .0:
  357. if 'type' not in e._attrs:
  358. (e._name, e._cdata)continue)(elt))
  359.         new_list.entries = entries
  360.         gen_lists.append(new_list)
  361.  
  362.     
  363.     def _update_ccard_blog(self, elt):
  364.         blog = self.space.setdefault('blog', Storage())
  365.         posts = []
  366.         for post in elt:
  367.             if post._attrs.get('type', '') == 'Post':
  368.                 posts.append(Storage((lambda .0: for e in .0:
  369. (e._name, e._cdata))(post)))
  370.                 continue
  371.         
  372.         blog.update((lambda .0: for e in .0:
  373. if 'type' not in e._attrs:
  374. (e._name, e._cdata)continue)(elt))
  375.         blog.posts = posts
  376.  
  377.     
  378.     def _update_ccard_profile(self, elt):
  379.         profiles = self.space.setdefault('profiles', Storage())
  380.         for profile in elt:
  381.             p_type = profile._attrs.get('type', '')
  382.             if p_type.endswith('Profile'):
  383.                 prof = profiles.setdefault(p_type.lower()[:-7], Storage())
  384.                 prof.update((lambda .0: for e in .0:
  385. (e._name, e._cdata))(profile))
  386.                 continue
  387.         
  388.         profiles.update((lambda .0: for e in .0:
  389. if 'type' not in e._attrs:
  390. (e._name, e._cdata)continue)(elt))
  391.  
  392.     
  393.     def _update_ccard_livecontact(self, elt):
  394.         pass
  395.  
  396.     
  397.     def __cmp__(self, other):
  398.         
  399.         try:
  400.             if other is self:
  401.                 return 0
  402.             return cmp((self.name, self.protocol), (other.name, other.protocol))
  403.         except:
  404.             return -1
  405.  
  406.  
  407.     
  408.     def pretty_profile(self):
  409.         d = { }
  410.         if self.remote_alias and self.alias != self.remote_alias:
  411.             d[_('Display Name:')] = self.remote_alias
  412.         
  413.         if self.space is None:
  414.             return d
  415.         p = self.space.pretty_profile
  416.         for validkey in p.keys():
  417.             if p[validkey] and filter(None, p[validkey]):
  418.                 continue
  419.             _[1][validkey]
  420.         
  421.         return p
  422.  
  423.     pretty_profile = property(pretty_profile)
  424.  
  425.  
  426. class MSNSpaceElement(object):
  427.     
  428.     def __init__(self, elt):
  429.         object.__init__(self)
  430.         for attr in ('title', 'url', 'description', 'tooltip'):
  431.             setattr(self, attr, str(getattr(elt, attr, '')).decode('utf-8'))
  432.         
  433.         self.last_check = yesterday()
  434.         self.last_update = yesterday()
  435.  
  436.     
  437.     def __repr__(self):
  438.         res = [
  439.             '<%s ' % type(self).__name__]
  440.         for attr in ('title', 'url', 'description', 'tooltip'):
  441.             myval = getattr(self, attr)
  442.             if myval:
  443.                 res.append('%s=%s, ' % (attr.capitalize(), myval))
  444.                 continue
  445.         
  446.         res[-1] = res[-1][:-2] + '>'
  447.         return ''.join(res)
  448.  
  449.     
  450.     def __iter__(self):
  451.         
  452.         def attrs():
  453.             for attr in ('title', 'url', 'description', 'tooltip'):
  454.                 val = getattr(self, attr)
  455.                 if val:
  456.                     yield (attr, val)
  457.                     continue
  458.             
  459.  
  460.         import itertools
  461.         return itertools.chain(attrs(), iter(self.contents))
  462.  
  463.     
  464.     def pretty_profile(self, p = None):
  465.         if not p:
  466.             pass
  467.         p = odict()
  468.         p[self.title + ':'] = [
  469.             '\n',
  470.             (self.url, self.description)]
  471.         return p
  472.  
  473.     pretty_profile = property(pretty_profile)
  474.     
  475.     def to_tag(self):
  476.         table = tag('table')
  477.         tr = tag('tr')
  478.         a = tag('a', href = self.url)
  479.         a._add_child(tag('b', self.title + ':'))
  480.         tr._add_child(a)
  481.         tr._add_child(tag('td', self.description))
  482.         table._add_child(tr)
  483.         return table
  484.  
  485.  
  486.  
  487. class SpaceTitleElt(MSNSpaceElement):
  488.     
  489.     def to_tag(self):
  490.         a = tag('a', href = self.url)
  491.         a._add_child('b', self.title)
  492.         return a
  493.  
  494.     
  495.     def pretty_profile(self):
  496.         return { }
  497.  
  498.     pretty_profile = property(pretty_profile)
  499.  
  500.  
  501. class GenericListElt(list, MSNSpaceElement):
  502.     
  503.     def __init__(self, elt):
  504.         MSNSpaceElement.__init__(self, elt)
  505.         []([], _[1])
  506.  
  507.     
  508.     def __repr__(self):
  509.         return '<%s: %s>' % (type(self).__name__, list.__repr__(self))
  510.  
  511.     
  512.     def __iter__(self):
  513.         return list.__iter__(self)
  514.  
  515.     
  516.     def pretty_profile(self):
  517.         return odict(MSNSpaceElement.pretty_profile.fget(self).items())
  518.  
  519.     pretty_profile = property(pretty_profile)
  520.     
  521.     def to_tag(self):
  522.         p = MSNSpaceElement.to_tag(self)
  523.         for thing in self:
  524.             p._add_child(thing.to_tag())
  525.         
  526.         return p
  527.  
  528.  
  529.  
  530. class MSNSpace(MSNSpaceElement):
  531.     _name = 'MSN Space'
  532.     
  533.     def __init__(self, buddy, contact_card):
  534.         MSNSpaceElement.__init__(self, contact_card)
  535.         self.last_update = str(contact_card.lastUpdate)
  536.         self.title = contact_card._attrs.get('displayName', buddy.name).strip().decode('fuzzy utf-8')
  537.         self.buddy = buddy
  538.         self.dp_url = contact_card._attrs.get('displayPictureUrl', '')
  539.         self.contents = []
  540.         for element in contact_card.elements:
  541.             type_ = element['type']
  542.             cls = getattr(thismod, '%sElt' % type_, MSNSpaceElement)
  543.             self.contents.append(cls(element))
  544.         
  545.  
  546.     
  547.     def pretty_profile(self):
  548.         p = odict()
  549.         for thing in self.contents:
  550.             p.update(thing.pretty_profile.items())
  551.         
  552.         url = self.contents[0].url
  553.         if not url and self.buddy.CID:
  554.             url = 'http://spaces.live.com/Profile.aspx?cid=%s' % self.buddy.CID
  555.         elif not self.buddy.CID:
  556.             return p
  557.         p['Profile URL:'] = [
  558.             '\n',
  559.             (url, url)]
  560.         return p
  561.  
  562.     pretty_profile = property(pretty_profile)
  563.     
  564.     def to_tag(self):
  565.         p = tag('p')
  566.         for thing in self.contents:
  567.             p._add_child(thing.to_tag())
  568.         
  569.         return p
  570.  
  571.  
  572.  
  573. class GenericListEntryElt(MSNSpaceElement):
  574.     
  575.     def __init__(self, elt):
  576.         MSNSpaceElement.__init__(self, elt)
  577.         self.last_update = elt['lastUpdated']
  578.         self.title = str(elt.title).strip().decode('utf-8')
  579.  
  580.     
  581.     def pretty_profile(self):
  582.         return odict({
  583.             self.title: '\n' })
  584.  
  585.     pretty_profile = property(pretty_profile)
  586.  
  587.  
  588. class MusicListElt(GenericListElt):
  589.     
  590.     def pretty_profile(self):
  591.         songlist = [ x.pretty_profile for x in self ]
  592.         songlist.insert(0, '\n')
  593.         return odict({
  594.             'Songs:': songlist })
  595.  
  596.     pretty_profile = property(pretty_profile)
  597.  
  598.  
  599. class MusicListEntryElt(GenericListEntryElt):
  600.     
  601.     def __init__(self, elt):
  602.         GenericListEntryElt.__init__(self, elt)
  603.         self.artist = str(elt.artist).decode('utf-8')
  604.         self.song = str(elt.song).decode('utf-8')
  605.  
  606.     
  607.     def pretty_profile(self):
  608.         return (self.url, _(u'%s by %s') % (self.song, self.artist + u'\n'))
  609.  
  610.     pretty_profile = property(pretty_profile)
  611.  
  612.  
  613. class BookListElt(GenericListElt):
  614.     
  615.     def to_tag(self):
  616.         p = tag('p')
  617.         p._add_child(tag('a', self.title, href = self.url))
  618.         for thing in self:
  619.             p._add_child(thing.to_tag())
  620.         
  621.         return p
  622.  
  623.  
  624.  
  625. class BookListEntryElt(GenericListEntryElt):
  626.     
  627.     def to_tag(self):
  628.         return tag('p', '%s<br />%s' % (self.title, self.description))
  629.  
  630.  
  631.  
  632. class BlogElt(GenericListElt):
  633.     
  634.     def to_tag(self):
  635.         p = tag('p')
  636.         for thing in self:
  637.             p._add_child(thing.to_tag())
  638.         
  639.         return p
  640.  
  641.     
  642.     def pretty_profile(self):
  643.         return []([ post.pretty_profile for post in self ])
  644.  
  645.     pretty_profile = property(pretty_profile)
  646.  
  647.  
  648. class PostElt(GenericListEntryElt):
  649.     
  650.     def to_tag(self):
  651.         link = tag('a', self.title, href = self.url)
  652.         return tag('p', 'New post: %s<br />%s' % (link._to_xml(), self.description))
  653.  
  654.     
  655.     def pretty_profile(self):
  656.         return (self.title + ':', [
  657.             '\n',
  658.             (self.url, self.description)])
  659.  
  660.     pretty_profile = property(pretty_profile)
  661.  
  662.  
  663. class AlbumElt(GenericListElt):
  664.     
  665.     def to_tag(self):
  666.         p = tag('p')
  667.         tr = tag('b', 'Photos Album:')
  668.         for photo in self:
  669.             tr._add_child(photo.to_tag())
  670.         
  671.         p._add_child(tr)
  672.         return p
  673.  
  674.     
  675.     def pretty_profile(self):
  676.         piclist = [ x.pretty_profile for x in self ]
  677.         piclist.insert(0, '\n')
  678.         return odict({
  679.             'Photos:': piclist })
  680.  
  681.     pretty_profile = property(pretty_profile)
  682.  
  683.  
  684. class PhotoElt(GenericListEntryElt):
  685.     (WIDTH, HEIGHT) = (20, 20)
  686.     
  687.     def __init__(self, elt):
  688.         GenericListEntryElt.__init__(self, elt)
  689.         self.thumbnail_url = str(elt.thumbnailUrl)
  690.         self.web_url = str(elt.webReadyUrl)
  691.         self.album_name = str(elt.albumName).decode('utf-8')
  692.         self.img_sm = None
  693.         self.img_lg = None
  694.         self.loadimages()
  695.  
  696.     
  697.     def to_tag(self):
  698.         a = tag('a', href = self.url)
  699.         a._add_child(tag('img', src = self.web_url, alt = self.tooltip, width = self.WIDTH, height = self.HEIGHT))
  700.         return a
  701.  
  702.     
  703.     def pretty_profile(self):
  704.         pref = pref
  705.         import common
  706.         sz = pref('msn.spaces.photosize', 20)
  707.         if self.img_sm is None:
  708.             return (self.url, u'%s - %s' % (self.album_name, self.title + u'\n'))
  709.         return dict(data = self.img_sm, alt = self.tooltip, height = sz, width = sz, href = self.url)
  710.  
  711.     pretty_profile = property(pretty_profile)
  712.     
  713.     def loadimages(self):
  714.         
  715.         try:
  716.             self.img_sm = urlcacheopen(self.thumbnail_url)
  717.         except:
  718.             self.imb_sm = 'javascript'
  719.  
  720.         if self.img_sm and 'javascript' in self.img_sm:
  721.             self.img_sm = None
  722.         
  723.         
  724.         try:
  725.             self.img_lg = urlcacheopen(self.web_url)
  726.         except:
  727.             self.img_lg = 'javascript'
  728.  
  729.         if self.img_lg and 'javascript' in self.img_lg:
  730.             self.img_lg = None
  731.         
  732.  
  733.     loadimages = threaded(loadimages)
  734.  
  735.  
  736. class MSNContact(contacts.Contact):
  737.     _renderer = 'Contact'
  738.     inherited_actions = [
  739.         MSNBuddy]
  740.     
  741.     def __init__(self, buddy, group_obj_or_id):
  742.         group_id = getattr(group_obj_or_id, 'name', group_obj_or_id)
  743.         contacts.Contact.__init__(self, buddy, (buddy.name, group_id))
  744.  
  745.     
  746.     def __repr__(self):
  747.         return '<MSN' + contacts.Contact.__repr__(self)[4:]
  748.  
  749.     
  750.     def block(self, *a, **k):
  751.         return contacts.Contact.block(self, *a, **k)
  752.  
  753.     block = common.action(contacts.Contact._block_pred)(block)
  754.     
  755.     def unblock(self, *a, **k):
  756.         return contacts.Contact.unblock(self, *a, **k)
  757.  
  758.     unblock = common.action(contacts.Contact._unblock_pred)(unblock)
  759.  
  760.  
  761. def from_mime(mime_info, email, msn, friendlyname = None):
  762.     b = msn.get_buddy(email)
  763.     if friendlyname:
  764.         if not friendlyname.decode('url').decode('fuzzy utf8'):
  765.             pass
  766.         b.remote_alias = None
  767.     
  768.     info = to_storage(mime_info)
  769.     for k, v in info.items():
  770.         k = k.replace(' ', '_').replace('-', '_').lower()
  771.         setattr(b, k, v)
  772.     
  773.     b.info = dict(info.items())
  774.     return b
  775.  
  776.  
  777. def from_lst(msn, N, **kwargs):
  778.     email = N
  779.     kwargs = to_storage(kwargs)
  780.     b = msn.get_buddy(email)
  781.     if 'F' in kwargs:
  782.         if not kwargs['F'].decode('url').decode('fuzzy utf8'):
  783.             pass
  784.         b.remote_alias = None
  785.     
  786.     if 'C' in kwargs:
  787.         b.guid = msn.cid_class(kwargs['C'])
  788.     
  789.     return b
  790.  
  791. CONTACT_CARD = '\n\n<contactCard>\n  <storageAuthCache>\n    1pqFlR36RzjW-X1jmTbKjKRsUpCe4cq9KHls4whqQIXlnjXVMidNbandYSmy0QEqm17M7xLIb2Fvo\n  </storageAuthCache>\n  <elements returnedMatches="3" displayName="shaps" totalMatches="3" displayPictureUrl="http://shared.live.com/jdIfE-NNCwZHMseiLFdi12c3U3v1VRQA5Wr8zoyj4Q0IBYQYt0pq5GcGiVCgriAz-mQjoThX0wVn7RxL!igd-A/base/3379/Controls/img/ContactControl/WLXLarge_default.gif">\n    <element type="SpaceTitle">\n      <title>\n        Steve\'s Space\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/?owner=1\n      </url>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="BookList">\n      <subElement type="BookListEntry" lastUpdated="0001-01-01T00:00:00">\n        <description>\n          stuff for description\n        </description>\n        <title>\n          book title 3\n        </title>\n        <tooltip>\n          Title: book title 3\n          Author: author\n          Description: stuff for description\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Lists/cns!2DB0770EAE61F13!106?owner=1\n        </url>\n      </subElement>\n      <subElement type="BookListEntry" lastUpdated="0001-01-01T00:00:00">\n        <description>\n          lkj lkj l\n        </description>\n        <title>\n          book title 2\n        </title>\n        <tooltip>\n          Title: book title 2Author: author 2Description: lkj lkj l\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Lists/cns!2DB0770EAE61F13!106?owner=1\n        </url>\n      </subElement>\n      <title>\n        Book List\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Lists/cns!2DB0770EAE61F13!106?owner=1\n      </url>\n      <description>\n        book title 3\n      </description>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="Album">\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:32.31-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          10.png\n        </title>\n        <tooltip>\n          January 1710.png\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!151?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxszOikmaP3r3dXbFG7ToyO0hMKW-WHHHJ2D9Lmff30X2Jo-2STreLVcNjaEogTUoBTZHMxhbwHv0ZBeHMwOnEdJcPytZWhE0nfg\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxszOikmaP3r3dlq-_0irNxa_2G2Gzp9ZgHOfSTUGN79t1IYnjSY5DKTXfP4ADxmBKN0Z5m0I7P6TUjGRafqU4NPz05iaCqzwJRA\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:32.107-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          9.gif\n        </title>\n        <tooltip>\n          January 179.gif\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!150?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxswE_zjzklXeXVqyaBoOfjid5rjfn8g0bSHcgQmQ5AoqTuNtwMzT-XNFSFv_IKj8mcZZ-ENJcOYB3XWJYb3U0wC4RSwlFUqeGfw\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxswE_zjzklXeXVJfE1Gdeox-zkKou9QwXud_dSI32qXsAnPABY4K57fs1bpWbe7JSZdhHJwEQ0psi7lGPBDLpdW0_3sUHaRDjuA\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:31.967-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          8.gif\n        </title>\n        <tooltip>\n          January 178.gif\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!149?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs1s-Updfv0-X7Rk9c9exySfJ-2PozaK6BKKyP9v8DAcv5xxyZSZ0OK9wirfRd2yWEEX7VzZS2mvvBkUWbtz0VXbLead2Ybs5OA\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs1s-Updfv0-XLAU_ZUH4P33y-NaJII-4uupQ0uoxjOCQJwxrL6sA1Xa9X3mdKUNYrj95_CVyrr5QVP7BXWFFyd2avflir6OhOA\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:08.043-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          7.png\n        </title>\n        <tooltip>\n          January 177.png\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!148?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxsyrdesm1gzDOdiW3uJUFNjo3H7f9H4uLtkj7u-akJEORhy2lLsKpM01wmbQvBUA-OEZdfQLdJi-NouMArPt5N0CW4nYHxW51UA\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxsyrdesm1gzDO9TYtPpGT2JsdnYIUZw7Lo5-yTazqwfIGHa6vu-Ku9OIMA0gLptXjy6IMXfr-CV5cag2FJKcqo_rVuPRT2t_t7w\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:07.933-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          6.jpg\n        </title>\n        <tooltip>\n          January 176.jpg\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!147?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs-wyD9TggUJ_4XM7tDqJ6CINNBLXvtT4Lfwr7ikxskuBzMQNerpK-oV8CvyWk9BbashoZg1H9afsvyl576NJp4I0FWfrI4hwBw\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs-wyD9TggUJ_C3BxBY1m6WsjbKXjpxDULsHHHEg8yAl2WLKDzSqb99OBxXaSetCdU-p8o6ScLb807p2QFAWr4gtH6OvBof2EBg\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <subElement type="Photo" lastUpdated="2007-04-17T09:35:07.81-07:00">\n        <description>\n          Photos\n        </description>\n        <title>\n          5.jpg\n        </title>\n        <tooltip>\n          January 175.jpg\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/photos/cns!2DB0770EAE61F13!138/cns!2DB0770EAE61F13!146?owner=1\n        </url>\n        <thumbnailUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs49JgAmSpKyqet5yR2Upi4Nn_X5ewL5gr6Tst7QFxHUmmjxQJpMpldLBUCWxs5dXLddQfHtjnSJTjycfX0vvZYvb9vVzlON9OA\n        </thumbnailUrl>\n        <webReadyUrl>\n          http://blufiles.storage.msn.com/x1pPHu2K6HCG6qh_ATHNyPxs49JgAmSpKyq-H9kk6kls9jjKFGLEr9AWb3NdQAxCcI6ta_72H6Ct25Dzjm1lkz7_xgUUuKboUy37TR0hveNy5ZRjhJ1eMUzcw\n        </webReadyUrl>\n        <albumName>\n          January 17\n        </albumName>\n      </subElement>\n      <title>\n        Photos:\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Photos/?owner=1\n      </url>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="Blog">\n      <subElement type="Post" lastUpdated="2007-01-16T13:55:01.013-08:00">\n        <description>\n          sfljks dflkjsdflkjsdflkjs dflkjsad flks jdaflsa dkjflsadkfjsadlfkj\n        </description>\n        <title>\n          NEWNEWNEW\n        </title>\n        <tooltip>\n          sfljks dflkjsdflkjsdflkjs dflkjsad flks jdaflsa dkjflsadkfjsadlfkj\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Blog/cns!2DB0770EAE61F13!130.entry?owner=1\n        </url>\n      </subElement>\n      <title>\n        Blog:\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/?owner=1\n      </url>\n      <totalNewItems>\n        0\n      </totalNewItems>\n    </element>\n    <element type="Profile">\n      <subElement type="GeneralProfile" lastUpdated="2007-01-16T09:01:51.533-08:00">\n        <description />\n        <title>\n          General profile info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated General profile information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n        </url>\n      </subElement>\n      <subElement type="PublicProfile" lastUpdated="2006-12-21T16:06:47.47-08:00">\n        <description />\n        <title>\n          Public profile info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Public profile information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n        </url>\n      </subElement>\n      <subElement type="SocialProfile" lastUpdated="2006-05-31T09:54:52.59-07:00">\n        <description />\n        <title>\n          Social profile info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Social profile information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n        </url>\n      </subElement>\n      <title>\n        Profile\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view\n      </url>\n      <description>\n        General profile info updated\n      </description>\n      <totalNewItems>\n        3\n      </totalNewItems>\n    </element>\n    <element type="LiveContact">\n      <subElement type="ProfessionalContactProfile" lastUpdated="2006-12-21T16:06:47.47-08:00">\n        <description />\n        <title>\n          Business contact info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Business contact information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view&mode=activecontacts\n        </url>\n      </subElement>\n      <subElement type="PersonalContactProfile" lastUpdated="2006-12-21T16:06:47.47-08:00">\n        <description />\n        <title>\n          Personal contact info updated\n        </title>\n        <tooltip>\n          This person has recently added or updated Personal contact information.\n        </tooltip>\n        <url>\n          http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view&mode=activecontacts\n        </url>\n      </subElement>\n      <title>\n        Contact Info\n      </title>\n      <url>\n        http://shaps776.spaces.live.com/Profile.aspx?cid=205766389534170899&mkt=en-us&action=view&mode=activecontacts\n      </url>\n      <description>\n        Business contact info updated\n      </description>\n      <totalNewItems>\n        2\n      </totalNewItems>\n    </element>\n  </elements>\n  <lastUpdate>\n    2007-04-18T08:20:01.167-07:00\n  </lastUpdate>\n  <theme>\n    <name>\n      personalspacegree\n    </name>\n    <titleBar foreground="333333" fontFace="" background="f4fbf7" />\n    <clientArea foreground="444444" backgroundImage="http://shared.live.com/jdIfE-NNCwb0XZTwVBd6PpCWk!2k7FEfmc0OX5OC0rZ-I0WjzyccY5aYuUiTkMo2blaFQRxUooU/personalspacegree/3379/img/green_card_bkgd.gif" fontFace="" background="FFFFFF" />\n    <toolbar foreground="333333" fontFace="" background="f4fbf7" />\n    <border topLeftImage="http://sc1.sclive.net/11.01.3810.0000/Web/Contacts/images/card_ul.gif" bottomLeftImage="http://sc2.sclive.net/11.01.3810.0000/Web/Contacts/images/card_ll.gif" bottomRightImage="http://sc4.sclive.net/11.01.3810.0000/Web/Contacts/images/card_lr.gif" outline="7F7F7F" topRightImage="http://sc3.sclive.net/11.01.3810.0000/Web/Contacts/images/card_ur.gif" />\n  </theme>\n  <liveTheme>\n    <themeName>\n      personalspacegree\n    </themeName>\n    <head backgroundImage="http://shared.live.com/jdIfE-NNCwb0XZTwVBd6PpCWk!2k7FEfmc0OX5OC0rZ-I0WjzyccY5aYuUiTkMo2blaFQRxUooU/personalspacegree/3379/img/SmallBannerImage.jpg" textColor="333333" linkColor="006629" backgroundColor="f4fbf7" />\n    <body accordionHoverColor="aad2ba" secondaryLinkColor="7F7F7F" dividerColor="8ed4ab" backgroundImage="" backgroundColor="f4fbf7" linkColor="006629" textColor="333333" />\n    <actions linkColor="333333" backgroundColor="f4fbf7" />\n  </liveTheme>\n</contactCard>\n'.strip().replace('&', '&')
  792. if __name__ == '__main__':
  793.     b = Storage(name = 'shaps776@hotmail.com')
  794.     card = MSNSpace(b, tag(CONTACT_CARD))
  795.     print card.to_tag()._to_xml()
  796.  
  797.