home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2009 June / maximum-cd-2009-06.iso / DiscContents / digsby_setup.exe / lib / msn / MSNBuddy.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-02-26  |  40.1 KB  |  803 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.5)
  3.  
  4. import common
  5. import contacts
  6. from util import Storage, to_storage, dyn_dispatch, odict, threaded, get
  7. from util.xml_tag import tag
  8. from util.cacheable import urlcacheopen, cproperty
  9. from util.observe import ObservableProperty
  10. from common.sms import validate_sms, normalize_sms
  11. from util.callbacks import callsback
  12. from MSNUtil import url_decode
  13. import time
  14. import datetime
  15. import logging
  16. log = logging.getLogger('msn.buddy')
  17.  
  18. yesterday = lambda : datetime.datetime.today() - datetime.timedelta(1)
  19. import sys
  20. thismod = sys.modules[__name__]
  21. del sys
  22.  
  23. try:
  24.     _
  25. except:
  26.     
  27.     _ = lambda s: s
  28.  
  29. statuses = Storage(brb = _('Be Right Back'), phone = _('On the Phone'), lunch = _('Out to Lunch'))
  30.  
  31. class MSNBuddy(common.buddy):
  32.     __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()
  33.     
  34.     def __init__(self, msn, name = None):
  35.         self._status = 'unknown'
  36.         self._status_message = ''
  37.         self._got_presence = False
  38.         self.phone_home = None
  39.         self.phone_work = None
  40.         self.phone_mobile = None
  41.         self.allow_mobile = None
  42.         self.enable_wireless = None
  43.         self.remote_alias = None
  44.         self.has_blog = None
  45.         self.msn_obj = None
  46.         self._idle_start = 0
  47.         self.info = { }
  48.         self.role_ids = { }
  49.         self.pending_auth = False
  50.         common.buddy.__init__(self, url_decode(name), msn)
  51.         if self is self.protocol.self_buddy:
  52.             profile = profile
  53.             import common
  54.             profile.account_manager.buddywatcher.unregister(self)
  55.         
  56.         self.CID = 0
  57.         self.space = None
  58.         if self._space:
  59.             
  60.             try:
  61.                 self.update_contact_card(tag(self._space))
  62.             self._space = None
  63.  
  64.         
  65.         self.mships = { }
  66.         self.contactsoap = None
  67.         self.membersoap = None
  68.         self._btype = 'im'
  69.  
  70.     
  71.     def __hash__(self):
  72.         return common.buddy.__hash__(self)
  73.  
  74.     
  75.     def sms(self):
  76.         if validate_sms(self.phone_mobile):
  77.             return self.phone_mobile
  78.         else:
  79.             return False
  80.  
  81.     sms = property(sms)
  82.     
  83.     def _get_phone_mobile(self):
  84.         return getattr(self, '_phone_mobile', None)
  85.  
  86.     
  87.     def _set_phone_mobile(self, val):
  88.         if val and val.startswith('tel:'):
  89.             val = val[4:]
  90.         
  91.         self._phone_mobile = val
  92.  
  93.     phone_mobile = property(_get_phone_mobile, _set_phone_mobile)
  94.     
  95.     def id(self):
  96.         if self.contactsoap is not None:
  97.             v = self.contactsoap.ContactId
  98.             if v:
  99.                 return v
  100.             
  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.         
  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.         
  163.         if self._got_presence:
  164.             pass
  165.         return self._status not in ('offline', 'unknown')
  166.  
  167.     online = property(online)
  168.     
  169.     def set_status(self, newval):
  170.         oldstatus = self._status
  171.         self._status = newval
  172.         if oldstatus != newval and self._got_presence:
  173.             self.notify('status', oldstatus, newval)
  174.         
  175.  
  176.     
  177.     def get_status(self):
  178.         if not self._got_presence:
  179.             return 'unknown'
  180.         
  181.         if self._status == 'idle':
  182.             return 'idle'
  183.         
  184.         if self.away:
  185.             return 'away'
  186.         
  187.         if self.mobile:
  188.             return 'mobile'
  189.         
  190.         return self._status
  191.  
  192.     status = ObservableProperty(get_status, set_status, observe = ('_status',))
  193.     
  194.     def profile(self):
  195.         pass
  196.  
  197.     profile = property(profile)
  198.     
  199.     def away(self):
  200.         return self._status in ('busy', 'brb', 'away', 'phone', 'lunch')
  201.  
  202.     away = property(away)
  203.     
  204.     def blocked(self):
  205.         return self in self.protocol.block_list
  206.  
  207.     blocked = property(blocked)
  208.     
  209.     def stripped_msg(self):
  210.         return self.status_message
  211.  
  212.     stripped_msg = property(stripped_msg)
  213.     
  214.     def _set_status_message(self, val):
  215.         self._got_presence = True
  216.         self._status_message = val
  217.  
  218.     
  219.     def _get_status_message(self):
  220.         return self._status_message
  221.  
  222.     status_message = property(_get_status_message, _set_status_message)
  223.     
  224.     def mobile(self):
  225.         if self.sms or self.allow_mobile == 'Y':
  226.             pass
  227.         return self._status == 'offline'
  228.  
  229.     mobile = property(mobile)
  230.     
  231.     def get_profile(self):
  232.         self.protocol.get_profile(self)
  233.  
  234.     get_profile = common.action((lambda self: True))(get_profile)
  235.     
  236.     def sightly_status(self):
  237.         if self.status == 'mobile':
  238.             return _('Mobile')
  239.         else:
  240.             return statuses.get(self._status, self._status.title())
  241.  
  242.     sightly_status = property(sightly_status)
  243.     
  244.     def block(self, _block = True, callback = None):
  245.         self.protocol.block_buddy(self, _block, callback = callback)
  246.  
  247.     block = callsback(block)
  248.     
  249.     def unblock(self, callback = None):
  250.         self.protocol.block_buddy(self, False, callback = callback)
  251.  
  252.     unblock = callsback(unblock)
  253.     
  254.     def service(self):
  255.         num_or_str = get(self, '_btype', 1)
  256.         num = get(dict(msn = 1, mob = 4, fed = 32), num_or_str, num_or_str)
  257.         if num == 32 and self.name.endswith('yahoo.com'):
  258.             prot_name = 'yahoo'
  259.         else:
  260.             prot_name = self.protocol.name
  261.         return prot_name
  262.  
  263.     service = property(service)
  264.     
  265.     def __repr__(self):
  266.         return '<MSNBuddy %s>' % self.name
  267.  
  268.     
  269.     def __str__(self):
  270.         return repr(self)
  271.  
  272.     
  273.     def get_idle(self):
  274.         return self.status == 'idle'
  275.  
  276.     
  277.     def set_idle(self, val):
  278.         pass
  279.  
  280.     idle = ObservableProperty(get_idle, set_idle, observe = ('status',))
  281.     
  282.     def update(self, new):
  283.         if not new:
  284.             return None
  285.         
  286.         for field in new.__dict__:
  287.             newval = getattr(new, field)
  288.             if newval:
  289.                 setattr(self, field, newval)
  290.                 self.setnotifyif()
  291.                 continue
  292.         
  293.  
  294.     
  295.     def update_contact_card(self, card):
  296.         if not card:
  297.             return None
  298.         
  299.         self._space = card._to_xml(pretty = False)
  300.         self.space = MSNSpace(self, card)
  301.  
  302.     
  303.     def _update_ccard_elt(self, elt, kind):
  304.         return dyn_dispatch(self, '_update_ccard_%s' % kind.lower(), elt)
  305.  
  306.     
  307.     def _update_ccard_spacetitle(self, elt):
  308.         self.space.update((lambda .0: for e in .0:
  309. if 'type' not in e._attrs:
  310. (e._name, e._cdata)continue)(elt))
  311.  
  312.     
  313.     def _update_ccard_album(self, elt):
  314.         photos = []
  315.         for subel in elt:
  316.             if subel._attrs.get('type', '') == 'Photo':
  317.                 photos.append(Storage((lambda .0: for e in .0:
  318. (e._name, e._cdata))(subel)))
  319.                 continue
  320.         
  321.         album = self.space.setdefault('album', Storage())
  322.         album.update((lambda .0: for e in .0:
  323. if 'type' not in e._attrs:
  324. (e._name, e._cdata)continue)(elt))
  325.         album.photos = photos
  326.  
  327.     _space = cproperty('')
  328.     
  329.     def _update_ccard_musiclist(self, elt):
  330.         musiclist = self.space.setdefault('musiclist', Storage())
  331.         songs = []
  332.         for song in elt:
  333.             if song._attrs.get('type', '') == 'MusicListEntry':
  334.                 songs.append(Storage((lambda .0: for e in .0:
  335. (e._name, e._cdata))(song)))
  336.                 continue
  337.         
  338.         musiclist.update((lambda .0: for e in .0:
  339. if 'type' not in e._attrs:
  340. (e._name, e._cdata)continue)(elt))
  341.         musiclist.songs = songs
  342.  
  343.     
  344.     def _update_ccard_booklist(self, elt):
  345.         booklist = self.space.setdefault('booklist', Storage())
  346.         books = []
  347.         for book in elt:
  348.             if book._attrs.get('type', '') == 'BookListEntry':
  349.                 books.append(Storage((lambda .0: for e in .0:
  350. (e._name, e._cdata))(book)))
  351.                 continue
  352.         
  353.         booklist.update((lambda .0: for e in .0:
  354. if 'type' not in e._attrs:
  355. (e._name, e._cdata)continue)(elt))
  356.         booklist.books = books
  357.  
  358.     
  359.     def _update_ccard_genericlist(self, elt):
  360.         gen_lists = self.space.setdefault('gen_lists', [])
  361.         entries = []
  362.         for entry in elt:
  363.             if entry._attrs.get('type', '') == 'GenericListEntry':
  364.                 entries.append(Storage((lambda .0: for e in .0:
  365. (e._name, e._cdata))(entry)))
  366.                 continue
  367.         
  368.         new_list = Storage((lambda .0: for e in .0:
  369. if 'type' not in e._attrs:
  370. (e._name, e._cdata)continue)(elt))
  371.         new_list.entries = entries
  372.         gen_lists.append(new_list)
  373.  
  374.     
  375.     def _update_ccard_blog(self, elt):
  376.         blog = self.space.setdefault('blog', Storage())
  377.         posts = []
  378.         for post in elt:
  379.             if post._attrs.get('type', '') == 'Post':
  380.                 posts.append(Storage((lambda .0: for e in .0:
  381. (e._name, e._cdata))(post)))
  382.                 continue
  383.         
  384.         blog.update((lambda .0: for e in .0:
  385. if 'type' not in e._attrs:
  386. (e._name, e._cdata)continue)(elt))
  387.         blog.posts = posts
  388.  
  389.     
  390.     def _update_ccard_profile(self, elt):
  391.         profiles = self.space.setdefault('profiles', Storage())
  392.         for profile in elt:
  393.             p_type = profile._attrs.get('type', '')
  394.             if p_type.endswith('Profile'):
  395.                 prof = profiles.setdefault(p_type.lower()[:-7], Storage())
  396.                 prof.update((lambda .0: for e in .0:
  397. (e._name, e._cdata))(profile))
  398.                 continue
  399.         
  400.         profiles.update((lambda .0: for e in .0:
  401. if 'type' not in e._attrs:
  402. (e._name, e._cdata)continue)(elt))
  403.  
  404.     
  405.     def _update_ccard_livecontact(self, elt):
  406.         pass
  407.  
  408.     
  409.     def __cmp__(self, other):
  410.         
  411.         try:
  412.             if other is self:
  413.                 return 0
  414.             
  415.             return cmp((self.name, self.protocol), (other.name, other.protocol))
  416.         except:
  417.             return -1
  418.  
  419.  
  420.     
  421.     def pretty_profile(self):
  422.         d = { }
  423.         if self.remote_alias and self.alias != self.remote_alias:
  424.             d[_('Display Name:')] = self.remote_alias
  425.         
  426.  
  427.     pretty_profile = property(pretty_profile)
  428.  
  429.  
  430. class MSNSpaceElement(object):
  431.     
  432.     def __init__(self, elt):
  433.         object.__init__(self)
  434.         for attr in ('title', 'url', 'description', 'tooltip'):
  435.             setattr(self, attr, str(getattr(elt, attr, '')).decode('utf-8'))
  436.         
  437.         self.last_check = yesterday()
  438.         self.last_update = yesterday()
  439.  
  440.     
  441.     def __repr__(self):
  442.         res = [
  443.             '<%s ' % type(self).__name__]
  444.         for attr in ('title', 'url', 'description', 'tooltip'):
  445.             myval = getattr(self, attr)
  446.             if myval:
  447.                 res.append('%s=%s, ' % (attr.capitalize(), myval))
  448.                 continue
  449.         
  450.         res[-1] = res[-1][:-2] + '>'
  451.         return ''.join(res)
  452.  
  453.     
  454.     def __iter__(self):
  455.         
  456.         def attrs():
  457.             for attr in ('title', 'url', 'description', 'tooltip'):
  458.                 val = getattr(self, attr)
  459.                 if val:
  460.                     yield (attr, val)
  461.                     continue
  462.             
  463.  
  464.         import itertools
  465.         return itertools.chain(attrs(), iter(self.contents))
  466.  
  467.     
  468.     def pretty_profile(self, p = None):
  469.         if not p:
  470.             pass
  471.         p = odict()
  472.         p[self.title + ':'] = [
  473.             '\n',
  474.             (self.url, self.description)]
  475.         return p
  476.  
  477.     pretty_profile = property(pretty_profile)
  478.     
  479.     def to_tag(self):
  480.         table = tag('table')
  481.         tr = tag('tr')
  482.         a = tag('a', href = self.url)
  483.         a._add_child(tag('b', self.title + ':'))
  484.         tr._add_child(a)
  485.         tr._add_child(tag('td', self.description))
  486.         table._add_child(tr)
  487.         return table
  488.  
  489.  
  490.  
  491. class SpaceTitleElt(MSNSpaceElement):
  492.     
  493.     def to_tag(self):
  494.         a = tag('a', href = self.url)
  495.         a._add_child('b', self.title)
  496.         return a
  497.  
  498.     
  499.     def pretty_profile(self):
  500.         return { }
  501.  
  502.     pretty_profile = property(pretty_profile)
  503.  
  504.  
  505. class GenericListElt(list, MSNSpaceElement):
  506.     
  507.     def __init__(self, elt):
  508.         MSNSpaceElement.__init__(self, elt)
  509.         []([], _[1])
  510.  
  511.     
  512.     def __repr__(self):
  513.         return '<%s: %s>' % (type(self).__name__, list.__repr__(self))
  514.  
  515.     
  516.     def __iter__(self):
  517.         return list.__iter__(self)
  518.  
  519.     
  520.     def pretty_profile(self):
  521.         return odict(MSNSpaceElement.pretty_profile.fget(self).items())
  522.  
  523.     pretty_profile = property(pretty_profile)
  524.     
  525.     def to_tag(self):
  526.         p = MSNSpaceElement.to_tag(self)
  527.         for thing in self:
  528.             p._add_child(thing.to_tag())
  529.         
  530.         return p
  531.  
  532.  
  533.  
  534. class MSNSpace(MSNSpaceElement):
  535.     _name = 'MSN Space'
  536.     
  537.     def __init__(self, buddy, contact_card):
  538.         MSNSpaceElement.__init__(self, contact_card)
  539.         self.last_update = str(contact_card.lastUpdate)
  540.         self.title = contact_card._attrs.get('displayName', buddy.name).strip().decode('utf-8')
  541.         self.buddy = buddy
  542.         self.dp_url = contact_card._attrs.get('displayPictureUrl', '')
  543.         self.contents = []
  544.         for element in contact_card.elements:
  545.             type_ = element['type']
  546.             cls = getattr(thismod, '%sElt' % type_, MSNSpaceElement)
  547.             self.contents.append(cls(element))
  548.         
  549.  
  550.     
  551.     def pretty_profile(self):
  552.         p = odict()
  553.         for thing in self.contents:
  554.             p.update(thing.pretty_profile.items())
  555.         
  556.         url = self.contents[0].url
  557.         if not url and self.buddy.CID:
  558.             url = 'http://spaces.live.com/Profile.aspx?cid=%s' % self.buddy.CID
  559.         elif not self.buddy.CID:
  560.             return p
  561.         
  562.         p['Profile URL:'] = [
  563.             '\n',
  564.             (url, url)]
  565.         return p
  566.  
  567.     pretty_profile = property(pretty_profile)
  568.     
  569.     def to_tag(self):
  570.         p = tag('p')
  571.         for thing in self.contents:
  572.             p._add_child(thing.to_tag())
  573.         
  574.         return p
  575.  
  576.  
  577.  
  578. class GenericListEntryElt(MSNSpaceElement):
  579.     
  580.     def __init__(self, elt):
  581.         MSNSpaceElement.__init__(self, elt)
  582.         self.last_update = elt['lastUpdated']
  583.         self.title = str(elt.title).strip().decode('utf-8')
  584.  
  585.     
  586.     def pretty_profile(self):
  587.         return odict({
  588.             self.title: '\n' })
  589.  
  590.     pretty_profile = property(pretty_profile)
  591.  
  592.  
  593. class MusicListElt(GenericListElt):
  594.     
  595.     def pretty_profile(self):
  596.         songlist = [ x.pretty_profile for x in self ]
  597.         songlist.insert(0, '\n')
  598.         return odict({
  599.             'Songs:': songlist })
  600.  
  601.     pretty_profile = property(pretty_profile)
  602.  
  603.  
  604. class MusicListEntryElt(GenericListEntryElt):
  605.     
  606.     def __init__(self, elt):
  607.         GenericListEntryElt.__init__(self, elt)
  608.         self.artist = str(elt.artist).decode('utf-8')
  609.         self.song = str(elt.song).decode('utf-8')
  610.  
  611.     
  612.     def pretty_profile(self):
  613.         return (self.url, _(u'%s by %s') % (self.song, self.artist + u'\n'))
  614.  
  615.     pretty_profile = property(pretty_profile)
  616.  
  617.  
  618. class BookListElt(GenericListElt):
  619.     
  620.     def to_tag(self):
  621.         p = tag('p')
  622.         p._add_child(tag('a', self.title, href = self.url))
  623.         for thing in self:
  624.             p._add_child(thing.to_tag())
  625.         
  626.         return p
  627.  
  628.  
  629.  
  630. class BookListEntryElt(GenericListEntryElt):
  631.     
  632.     def to_tag(self):
  633.         return tag('p', '%s<br />%s' % (self.title, self.description))
  634.  
  635.  
  636.  
  637. class BlogElt(GenericListElt):
  638.     
  639.     def to_tag(self):
  640.         p = tag('p')
  641.         for thing in self:
  642.             p._add_child(thing.to_tag())
  643.         
  644.         return p
  645.  
  646.     
  647.     def pretty_profile(self):
  648.         return []([ post.pretty_profile for post in self ])
  649.  
  650.     pretty_profile = property(pretty_profile)
  651.  
  652.  
  653. class PostElt(GenericListEntryElt):
  654.     
  655.     def to_tag(self):
  656.         link = tag('a', self.title, href = self.url)
  657.         return tag('p', 'New post: %s<br />%s' % (link._to_xml(), self.description))
  658.  
  659.     
  660.     def pretty_profile(self):
  661.         return (self.title + ':', [
  662.             '\n',
  663.             (self.url, self.description)])
  664.  
  665.     pretty_profile = property(pretty_profile)
  666.  
  667.  
  668. class AlbumElt(GenericListElt):
  669.     
  670.     def to_tag(self):
  671.         p = tag('p')
  672.         tr = tag('b', 'Photos Album:')
  673.         for photo in self:
  674.             tr._add_child(photo.to_tag())
  675.         
  676.         p._add_child(tr)
  677.         return p
  678.  
  679.     
  680.     def pretty_profile(self):
  681.         piclist = [ x.pretty_profile for x in self ]
  682.         piclist.insert(0, '\n')
  683.         return odict({
  684.             'Photos:': piclist })
  685.  
  686.     pretty_profile = property(pretty_profile)
  687.  
  688.  
  689. class PhotoElt(GenericListEntryElt):
  690.     (WIDTH, HEIGHT) = (20, 20)
  691.     
  692.     def __init__(self, elt):
  693.         GenericListEntryElt.__init__(self, elt)
  694.         self.thumbnail_url = str(elt.thumbnailUrl)
  695.         self.web_url = str(elt.webReadyUrl)
  696.         self.album_name = str(elt.albumName).decode('utf-8')
  697.         self.img_sm = None
  698.         self.img_lg = None
  699.         self.loadimages()
  700.  
  701.     
  702.     def to_tag(self):
  703.         a = tag('a', href = self.url)
  704.         a._add_child(tag('img', src = self.web_url, alt = self.tooltip, width = self.WIDTH, height = self.HEIGHT))
  705.         return a
  706.  
  707.     
  708.     def pretty_profile(self):
  709.         pref = pref
  710.         import common
  711.         sz = pref('msn.spaces.photosize', 20)
  712.         if self.img_sm is None:
  713.             return (self.url, u'%s - %s' % (self.album_name, self.title + u'\n'))
  714.         
  715.         return dict(data = self.img_sm, alt = self.tooltip, height = sz, width = sz, href = self.url)
  716.  
  717.     pretty_profile = property(pretty_profile)
  718.     
  719.     def loadimages(self):
  720.         
  721.         try:
  722.             self.img_sm = urlcacheopen(self.thumbnail_url)
  723.         except:
  724.             self.imb_sm = 'javascript'
  725.  
  726.         if self.img_sm and 'javascript' in self.img_sm:
  727.             self.img_sm = None
  728.         
  729.         
  730.         try:
  731.             self.img_lg = urlcacheopen(self.web_url)
  732.         except:
  733.             self.img_lg = 'javascript'
  734.  
  735.         if self.img_lg and 'javascript' in self.img_lg:
  736.             self.img_lg = None
  737.         
  738.  
  739.     loadimages = threaded(loadimages)
  740.  
  741.  
  742. class MSNContact(contacts.Contact):
  743.     _renderer = 'Contact'
  744.     inherited_actions = [
  745.         MSNBuddy]
  746.     
  747.     def __init__(self, buddy, group_obj_or_id):
  748.         group_id = getattr(group_obj_or_id, 'name', group_obj_or_id)
  749.         contacts.Contact.__init__(self, buddy, (buddy.name, group_id))
  750.  
  751.     
  752.     def __repr__(self):
  753.         return '<MSN' + contacts.Contact.__repr__(self)[4:]
  754.  
  755.     
  756.     def block(self, *a, **k):
  757.         return contacts.Contact.block(self, *a, **k)
  758.  
  759.     block = common.action(contacts.Contact._block_pred)(block)
  760.     
  761.     def unblock(self, *a, **k):
  762.         return contacts.Contact.unblock(self, *a, **k)
  763.  
  764.     unblock = common.action(contacts.Contact._unblock_pred)(unblock)
  765.  
  766.  
  767. def from_mime(mime_info, email, msn, friendlyname = None):
  768.     b = msn.get_buddy(email)
  769.     if friendlyname:
  770.         if not url_decode(friendlyname).decode('utf-8'):
  771.             pass
  772.         b.remote_alias = None
  773.     
  774.     info = to_storage(mime_info)
  775.     for k, v in info.items():
  776.         k = k.replace(' ', '_').replace('-', '_').lower()
  777.         setattr(b, k, v)
  778.     
  779.     b.info = dict(info.items())
  780.     return b
  781.  
  782.  
  783. def from_lst(msn, N, **kwargs):
  784.     email = N
  785.     kwargs = to_storage(kwargs)
  786.     b = msn.get_buddy(email)
  787.     if 'F' in kwargs:
  788.         if not url_decode(kwargs['F']).decode('utf-8'):
  789.             pass
  790.         b.remote_alias = None
  791.     
  792.     if 'C' in kwargs:
  793.         b.guid = msn.cid_class(kwargs['C'])
  794.     
  795.     return b
  796.  
  797. 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('&', '&')
  798. if __name__ == '__main__':
  799.     b = Storage(name = 'shaps776@hotmail.com')
  800.     card = MSNSpace(b, tag(CONTACT_CARD))
  801.     print card.to_tag()._to_xml()
  802.  
  803.