From 259c84e99a314fa610b824ff5c92d73b7f3ad64f Mon Sep 17 00:00:00 2001
From: Lance Stout <lancestout@gmail.com>
Date: Sat, 7 Apr 2012 20:50:02 -0400
Subject: Add initial XEP-0054 plugin.

---
 sleekxmpp/plugins/__init__.py            |   1 +
 sleekxmpp/plugins/xep_0054/__init__.py   |  15 +
 sleekxmpp/plugins/xep_0054/stanza.py     | 512 +++++++++++++++++++++++++++++++
 sleekxmpp/plugins/xep_0054/vcard_temp.py | 118 +++++++
 4 files changed, 646 insertions(+)
 create mode 100644 sleekxmpp/plugins/xep_0054/__init__.py
 create mode 100644 sleekxmpp/plugins/xep_0054/stanza.py
 create mode 100644 sleekxmpp/plugins/xep_0054/vcard_temp.py

(limited to 'sleekxmpp/plugins')

diff --git a/sleekxmpp/plugins/__init__.py b/sleekxmpp/plugins/__init__.py
index 6913a49a..1c6c89a2 100644
--- a/sleekxmpp/plugins/__init__.py
+++ b/sleekxmpp/plugins/__init__.py
@@ -24,6 +24,7 @@ __all__ = [
     'xep_0045',  # Multi-User Chat (Client)
     'xep_0047',  # In-Band Bytestreams
     'xep_0050',  # Ad-hoc Commands
+    'xep_0054',  # vcard-temp
     'xep_0059',  # Result Set Management
     'xep_0060',  # Pubsub (Client)
     'xep_0066',  # Out of Band Data
diff --git a/sleekxmpp/plugins/xep_0054/__init__.py b/sleekxmpp/plugins/xep_0054/__init__.py
new file mode 100644
index 00000000..d460cc8a
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0054/__init__.py
@@ -0,0 +1,15 @@
+"""
+    SleekXMPP: The Sleek XMPP Library
+    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+    This file is part of SleekXMPP.
+
+    See the file LICENSE for copying permission.
+"""
+
+from sleekxmpp.plugins.base import register_plugin
+
+from sleekxmpp.plugins.xep_0054.stanza import VCardTemp
+from sleekxmpp.plugins.xep_0054.vcard_temp import XEP_0054
+
+
+register_plugin(XEP_0054)
diff --git a/sleekxmpp/plugins/xep_0054/stanza.py b/sleekxmpp/plugins/xep_0054/stanza.py
new file mode 100644
index 00000000..38f8d43b
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0054/stanza.py
@@ -0,0 +1,512 @@
+import base64
+import datetime as dt
+
+from sleekxmpp.thirdparty.suelta.util import bytes
+
+from sleekxmpp.xmlstream import ElementBase, ET, register_stanza_plugin, JID
+from sleekxmpp.plugins import xep_0082
+
+
+class VCardTemp(ElementBase):
+    name = 'vCard'
+    namespace = 'vcard-temp'
+    plugin_attrib = 'vcard_temp'
+    interfaces = set(['FN', 'VERSION'])
+    sub_interfaces = set(['FN', 'VERSION'])
+
+
+class Name(ElementBase):
+    name = 'N'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['FAMILY', 'GIVEN', 'MIDDLE', 'PREFIX', 'SUFFIX'])
+    sub_interfaces = interfaces
+
+    def _set_component(self, name, value):
+        if isinstance(value, list):
+            value = ','.join(value)
+        if value is not None:
+            self._set_sub_text(name, value, keep=True)
+        else:
+            self._del_sub(name)
+
+    def _get_component(self, name):
+        value = self._get_sub_text(name, '')
+        if ',' in value:
+            value = [v.strip() for v in value.split(',')]
+        return value
+
+    def set_family(self, value):
+        self._set_component('FAMILY', value)
+
+    def get_family(self):
+        return self._get_component('FAMILY')
+
+    def set_given(self, value):
+        self._set_component('GIVEN', value)
+
+    def get_given(self):
+        return self._get_component('GIVEN')
+
+    def set_middle(self, value):
+        print(value)
+        self._set_component('MIDDLE', value)
+
+    def get_middle(self):
+        return self._get_component('MIDDLE')
+
+    def set_prefix(self, value):
+        self._set_component('PREFIX', value)
+
+    def get_prefix(self):
+        return self._get_component('PREFIX')
+
+    def set_suffix(self, value):
+        self._set_component('SUFFIX', value)
+
+    def get_suffix(self):
+        return self._get_component('SUFFIX')
+
+
+class Email(ElementBase):
+    name = 'EMAIL'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400', 'USERID'])
+    sub_interfaces = set(['USERID'])
+    bool_interfaces = set(['HOME', 'WORK', 'INTERNET', 'PREF', 'X400'])
+
+
+class Address(ElementBase):
+    name = 'ADR'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INTL', 
+                      'PREF', 'POBOX', 'EXTADD', 'STREET', 'LOCALITY', 
+                      'REGION', 'PCODE', 'CTRY'])
+    sub_interfaces = set(['POBOX', 'EXTADD', 'STREET', 'LOCALITY',
+                          'REGION', 'PCODE', 'CTRY'])
+    bool_interfaces = set(['HOME', 'WORK', 'DOM', 'INTL', 'PREF'])
+
+
+class Telephone(ElementBase):
+    name = 'TEL'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER', 'MSG',
+                      'CELL', 'VIDEO', 'BBS', 'MODEM', 'ISDN', 'PCS',
+                      'PREF', 'NUMBER'])
+    sub_interfaces = set(['NUMBER'])
+    bool_interfaces = set(['HOME', 'WORK', 'VOICE', 'FAX', 'PAGER', 
+                           'MSG', 'CELL', 'VIDEO', 'BBS', 'MODEM', 
+                           'ISDN', 'PCS', 'PREF'])
+
+    def setup(self, xml=None):
+        super(Telephone, self).setup(xml=xml)
+        self._set_sub_text('NUMBER', '', keep=True)
+
+    def set_number(self, value):
+        self._set_sub_text('NUMBER', value, keep=True)
+
+    def del_number(self):
+        self._set_sub_text('NUMBER', '', keep=True)
+
+
+class Label(ElementBase):
+    name = 'LABEL'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 'INT',
+                      'PREF', 'lines'])
+    bool_interfaces = set(['HOME', 'WORK', 'POSTAL', 'PARCEL', 'DOM', 
+                           'INT', 'PREF'])
+
+    def add_line(self, value):
+        line = ET.Element('{%s}LINE' % self.namespace)
+        line.text = value
+        self.xml.append(line)
+
+    def get_lines(self):
+        lines = self.xml.find('{%s}LINE' % self.namespace)
+        if lines is None:
+            return []
+        return [line.text for line in lines]
+
+    def set_lines(self, values):
+        self.del_lines()
+        for line in values:
+            self.add_line(line)
+
+    def del_lines(self):
+        lines = self.xml.find('{%s}LINE' % self.namespace)
+        if lines is None:
+            return
+        for line in lines:
+            self.xml.remove(line)
+
+
+class Geo(ElementBase):
+    name = 'GEO'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['LAT', 'LON'])
+    sub_interfaces = interfaces
+
+
+class Org(ElementBase):
+    name = 'ORG'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['ORGNAME', 'ORGUNIT', 'orgunits'])
+    sub_interfaces = set(['ORGNAME', 'ORGUNIT'])
+
+    def add_orgunit(self, value):
+        orgunit = ET.Element('{%s}ORGUNIT' % self.namespace)
+        orgunit.text = value
+        self.xml.append(orgunit)
+
+    def get_orgunits(self):
+        orgunits = self.xml.find('{%s}ORGUNIT' % self.namespace)
+        if orgunits is None:
+            return []
+        return [orgunit.text for orgunit in orgunits]
+
+    def set_orgunits(self, values):
+        self.del_orgunits()
+        for orgunit in values:
+            self.add_orgunit(orgunit)
+
+    def del_orgunits(self):
+        orgunits = self.xml.find('{%s}ORGUNIT' % self.namespace)
+        if orgunits is None:
+            return
+        for orgunit in orgunits:
+            self.xml.remove(orgunit)
+
+
+class Photo(ElementBase):
+    name = 'PHOTO'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['TYPE', 'EXTVAL'])
+    sub_interfaces = interfaces
+
+
+class Logo(ElementBase):
+    name = 'LOGO'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['TYPE', 'EXTVAL'])
+    sub_interfaces = interfaces
+
+
+class Sound(ElementBase):
+    name = 'LOGO'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['PHONETC', 'EXTVAL'])
+    sub_interfaces = interfaces
+
+
+class BinVal(ElementBase):
+    name = 'BINVAL'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['BINVAL'])
+    is_extension = True
+
+    def setup(self, xml=None):
+        self.xml = ET.Element('')
+        return True
+
+    def set_binval(self, value):
+        self.del_binval()
+        parent = self.parent()
+        if value:
+            xml = ET.Element('{%s}BINVAL' % self.namespace)
+            xml.text = bytes(base64.b64encode(value)).decode('utf-8')
+            parent.append(xml)
+
+    def get_binval(self):
+        parent = self.parent()
+        xml = parent.find('{%s}BINVAL' % self.namespace)
+        if xml is not None:
+            return base64.b64decode(bytes(xml.text))
+        return b''
+
+    def del_binval(self):
+        self.parent()._del_sub('{%s}BINVAL' % self.namespace)
+
+
+class Classification(ElementBase):
+    name = 'CLASS'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['PUBLIC', 'PRIVATE', 'CONFIDENTIAL'])
+    bool_interfaces = interfaces
+
+
+class Categories(ElementBase):
+    name = 'CATEGORIES'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_categories(self, values):
+        self.del_categories()
+        for keyword in values:
+            item = ET.Element('{%s}KEYWORD' % self.namespace)
+            item.text = keyword
+            self.xml.append(item)
+
+    def get_categories(self):
+        items = self.xml.findall('{%s}KEYWORD' % self.namespace)
+        if items is None:
+            return []
+        keywords = []
+        for item in items:
+            keywords.append(item.text)
+        return keywords
+
+    def del_categories(self):
+        items = self.xml.findall('{%s}KEYWORD' % self.namespace)
+        for item in items:
+            self.xml.remove(item)
+
+
+class Birthday(ElementBase):
+    name = 'BDAY'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_bday(self, value):
+        if isinstance(value, dt.datetime):
+            value = xep_0082.format_datetime(value)
+        self.xml.text = value
+
+    def get_bday(self):
+        if not self.xml.text:
+            return None
+        return xep_0082.parse(self.xml.text)
+
+
+class Rev(ElementBase):
+    name = 'REV'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_rev(self, value):
+        if isinstance(value, dt.datetime):
+            value = xep_0082.format_datetime(value)
+        self.xml.text = value
+
+    def get_rev(self):
+        if not self.xml.text:
+            return None
+        return xep_0082.parse(self.xml.text)
+
+
+class Title(ElementBase):
+    name = 'TITLE'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_title(self, value):
+        self.xml.text = value
+
+    def get_title(self):
+        return self.xml.text
+
+
+class Role(ElementBase):
+    name = 'ROLE'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_role(self, value):
+        self.xml.text = value
+
+    def get_role(self):
+        return self.xml.text
+
+
+class Note(ElementBase):
+    name = 'NOTE'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_note(self, value):
+        self.xml.text = value
+
+    def get_note(self):
+        return self.xml.text
+
+
+class Desc(ElementBase):
+    name = 'DESC'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_desc(self, value):
+        self.xml.text = value
+
+    def get_desc(self):
+        return self.xml.text
+
+
+class URL(ElementBase):
+    name = 'URL'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_url(self, value):
+        self.xml.text = value
+
+    def get_url(self):
+        return self.xml.text
+
+
+class UID(ElementBase):
+    name = 'UID'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_uid(self, value):
+        self.xml.text = value
+
+    def get_uid(self):
+        return self.xml.text
+
+
+class ProdID(ElementBase):
+    name = 'PRODID'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_prodid(self, value):
+        self.xml.text = value
+
+    def get_prodid(self):
+        return self.xml.text
+
+
+class Mailer(ElementBase):
+    name = 'MAILER'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_mailer(self, value):
+        self.xml.text = value
+
+    def get_mailer(self):
+        return self.xml.text
+
+
+class SortString(ElementBase):
+    name = 'SORT-STRING'
+    namespace = 'vcard-temp'
+    plugin_attrib = 'SORT_STRING'
+    interfaces = set([name])
+    is_extension = True
+
+    def set_sort_string(self, value):
+        self.xml.text = value
+
+    def get_sort_string(self):
+        return self.xml.text
+
+
+class Agent(ElementBase):
+    name = 'AGENT'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set(['EXTVAL'])
+    sub_interfaces = interfaces
+
+
+class JabberID(ElementBase):
+    name = 'JABBERID'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_jabberid(self, value):
+        self.xml.text = JID(value).bare
+
+    def get_jabberid(self):
+        return JID(self.xml.text)
+
+
+class TimeZone(ElementBase):
+    name = 'TZ'
+    namespace = 'vcard-temp'
+    plugin_attrib = name
+    interfaces = set([name])
+    is_extension = True
+
+    def set_tz(self, value):
+        time = xep_0082.time(offset=value) 
+        if time[-1] == 'Z':
+            self.xml.text = 'Z'
+        else:
+            self.xml.text = time[-6:]
+
+    def get_tz(self):
+        if not self.xml.text:
+            return xep_0082.tzutc()
+        time = xep_0082.parse('00:00:00%s' % self.xml.text)
+        return time.tzinfo
+
+
+register_stanza_plugin(VCardTemp, Name)
+register_stanza_plugin(VCardTemp, Address, iterable=True)
+register_stanza_plugin(VCardTemp, Agent, iterable=True)
+register_stanza_plugin(VCardTemp, Birthday, iterable=True)
+register_stanza_plugin(VCardTemp, Categories, iterable=True)
+register_stanza_plugin(VCardTemp, Desc, iterable=True)
+register_stanza_plugin(VCardTemp, Email, iterable=True)
+register_stanza_plugin(VCardTemp, Geo, iterable=True)
+register_stanza_plugin(VCardTemp, JabberID, iterable=True)
+register_stanza_plugin(VCardTemp, Label, iterable=True)
+register_stanza_plugin(VCardTemp, Logo, iterable=True)
+register_stanza_plugin(VCardTemp, Mailer, iterable=True)
+register_stanza_plugin(VCardTemp, Note, iterable=True)
+register_stanza_plugin(VCardTemp, Photo, iterable=True)
+register_stanza_plugin(VCardTemp, ProdID, iterable=True)
+register_stanza_plugin(VCardTemp, Rev, iterable=True)
+register_stanza_plugin(VCardTemp, Role, iterable=True)
+register_stanza_plugin(VCardTemp, SortString, iterable=True)
+register_stanza_plugin(VCardTemp, Sound, iterable=True)
+register_stanza_plugin(VCardTemp, Telephone, iterable=True)
+register_stanza_plugin(VCardTemp, Title, iterable=True)
+register_stanza_plugin(VCardTemp, TimeZone, iterable=True)
+register_stanza_plugin(VCardTemp, UID, iterable=True)
+register_stanza_plugin(VCardTemp, URL, iterable=True)
+
+register_stanza_plugin(Photo, BinVal)
+register_stanza_plugin(Logo, BinVal)
+register_stanza_plugin(Sound, BinVal)
+
+register_stanza_plugin(Agent, VCardTemp)
diff --git a/sleekxmpp/plugins/xep_0054/vcard_temp.py b/sleekxmpp/plugins/xep_0054/vcard_temp.py
new file mode 100644
index 00000000..9be4c098
--- /dev/null
+++ b/sleekxmpp/plugins/xep_0054/vcard_temp.py
@@ -0,0 +1,118 @@
+"""
+    SleekXMPP: The Sleek XMPP Library
+    Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
+    This file is part of SleekXMPP.
+
+    See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp import Iq
+from sleekxmpp.exceptions import XMPPError
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+from sleekxmpp.plugins import BasePlugin
+from sleekxmpp.plugins.xep_0054 import VCardTemp, stanza
+
+
+log = logging.getLogger(__name__)
+
+
+class XEP_0054(BasePlugin):
+
+    """
+    XEP-0054: vcard-temp
+    """
+
+    name = 'xep_0054'
+    description = 'XEP-0054: vcard-temp'
+    dependencies = set(['xep_0030', 'xep_0082'])
+    stanza = stanza
+
+    def plugin_init(self):
+        """
+        Start the XEP-0054 plugin.
+        """
+        register_stanza_plugin(Iq, VCardTemp)
+
+        self.xmpp['xep_0030'].add_feature('vcard-temp')
+
+        self.api.register(self._set_vcard, 'set_vcard', default=True)
+        self.api.register(self._get_vcard, 'get_vcard', default=True)
+        self.api.register(self._del_vcard, 'del_vcard', default=True)
+
+        self._vcard_cache = {}
+
+        self.xmpp.register_handler(
+                Callback('VCardTemp',
+                    StanzaPath('iq/vcard_temp'),
+                    self._handle_get_vcard))
+
+    def make_vcard(self):
+        return VCardTemp()
+
+    def get_vcard(self, jid=None, ifrom=None, local=False, cached=False, 
+                  block=True, callback=None, timeout=None):
+        if self.xmpp.is_component and jid.bare == self.xmpp.boundjid.bare:
+            local = True
+
+        if local or cached:
+            vcard = self.api['get_vcard'](jid, None, ifrom)
+            if not isinstance(vcard, Iq):
+                iq = self.xmpp.Iq()
+                iq.append(vcard)
+                return iq
+            return vcard
+
+        iq = self.xmpp.Iq()
+        iq['to'] = jid
+        iq['from'] = ifrom
+        iq['type'] = 'get'
+        iq.enable('vcard_temp')
+
+        vcard = iq.send(block=block, callback=callback, timeout=timeout)
+
+        self.api['set_vcard'](vcard['from'], args=vcard['vcard_temp'])
+        return vcard
+
+    def publish_vcard(self, vcard=None, jid=None, block=True, ifrom=None, 
+                      callback=None, timeout=None):
+        if self.xmpp.is_component:
+            self.api['set_vcard'](jid, None, ifrom, vcard)
+            return
+
+        iq = self.xmpp.Iq()
+        iq['to'] = jid
+        iq['from'] = ifrom
+        iq['type'] = 'set'
+        iq.append(vcard)
+        return iq.send(block=block, callback=callback, timeout=timeout)
+
+    def _handle_get_vcard(self, iq):
+        if iq['type'] == 'result':
+            self.api['set_vcard'](jid=iq['from'], args=iq['vcard_temp'])
+            return
+        elif iq['type'] == 'get':
+            vcard = self.api['get_vard'](iq['from'].bare)
+            if isinstance(vcard, Iq):
+                vcard.send()
+            else:
+                iq.reply()
+                iq.append(vcard)
+                iq.send()
+        else:
+            raise XMPPError('service-unavailable')
+
+    # =================================================================
+
+    def _set_vcard(self, jid, node, ifrom, vcard):
+        self._vcard_cache[jid.bare] = vcard
+
+    def _get_vcard(self, jid, node, ifrom, vcard):
+        return self._vcard_cache.get(jid.bare, VCardTemp())
+
+    def _del_vcard(self, jid, node, ifrom, vcard):
+        if jid.bare in self._vcard_cache:
+            del self._vcard_cache[jid.bare]
-- 
cgit v1.2.3