implements XEP-0292 (vCard4 over XMPP)

This commit is contained in:
nicoco
2022-11-04 06:20:11 +01:00
parent 7a0fb97083
commit 65636b8cce
6 changed files with 419 additions and 0 deletions

View File

@@ -79,6 +79,7 @@ __all__ = [
# 'xep_0270', # XMPP Compliance Suites 2010. Dont automatically load
'xep_0279', # Server IP Check
'xep_0280', # Message Carbons
'xep_0292', # vCard4 Over XMPP
'xep_0297', # Stanza Forwarding
'xep_0300', # Use of Cryptographic Hash Functions in XMPP
# 'xep_0302', # XMPP Compliance Suites 2012. Dont automatically load

View File

@@ -0,0 +1,5 @@
from slixmpp.plugins.base import register_plugin
from . import stanza, vcard4
register_plugin(vcard4.XEP_0292)

View File

@@ -0,0 +1,167 @@
import datetime
from typing import Optional
from slixmpp import ElementBase, Iq, register_stanza_plugin
NS = "urn:ietf:params:xml:ns:vcard-4.0"
class _VCardElementBase(ElementBase):
namespace = NS
class VCard4(_VCardElementBase):
name = plugin_attrib = "vcard"
interfaces = {"full_name", "given", "surname", "birthday"}
def set_full_name(self, full_name: str):
self["fn"]["text"] = full_name
def get_full_name(self):
return self["fn"]["text"]
def set_given(self, given: str):
self["n"]["given"] = given
def get_given(self):
return self["n"]["given"]
def set_surname(self, surname: str):
self["n"]["surname"] = surname
def get_surname(self):
return self["n"]["surname"]
def set_birthday(self, birthday: datetime.date):
self["bday"]["date"] = birthday
def get_birthday(self):
return self["bday"]["date"]
def add_tel(self, number: str, name: Optional[str] = None):
tel = Tel()
if name:
tel["parameters"]["type_"]["text"] = name
tel["uri"] = f"tel:{number}"
self.append(tel)
def add_address(
self, country: Optional[str] = None, locality: Optional[str] = None
):
adr = Adr()
if locality:
adr["locality"] = locality
if country:
adr["country"] = country
self.append(adr)
def add_nickname(self, nick: str):
el = Nickname()
el["text"] = nick
self.append(el)
def add_note(self, note: str):
el = Note()
el["text"] = note
self.append(el)
def add_impp(self, impp: str):
el = Impp()
el["uri"] = impp
self.append(el)
def add_url(self, url: str):
el = Url()
el["uri"] = url
self.append(el)
def add_email(self, email: str):
el = Email()
el["text"] = email
self.append(el)
class _VCardTextElementBase(_VCardElementBase):
interfaces = {"text"}
sub_interfaces = {"text"}
class Fn(_VCardTextElementBase):
name = plugin_attrib = "fn"
class Nickname(_VCardTextElementBase):
name = plugin_attrib = "nickname"
class Note(_VCardTextElementBase):
name = plugin_attrib = "note"
class _VCardUriElementBase(_VCardElementBase):
interfaces = {"uri"}
sub_interfaces = {"uri"}
class Url(_VCardUriElementBase):
name = plugin_attrib = "url"
class Impp(_VCardUriElementBase):
name = plugin_attrib = "impp"
class Email(_VCardTextElementBase):
name = plugin_attrib = "email"
class N(_VCardElementBase):
name = "n"
plugin_attrib = "n"
interfaces = sub_interfaces = {"given", "surname", "additional"}
class BDay(_VCardElementBase):
name = plugin_attrib = "bday"
interfaces = {"date"}
def set_date(self, date: datetime.date):
d = Date()
d.xml.text = date.strftime("%Y-%m-%d")
self.append(d)
def get_date(self):
for elem in self.xml:
try:
return datetime.date.fromisoformat(elem.text)
except ValueError:
return None
class Date(_VCardElementBase):
name = "date"
class Tel(_VCardUriElementBase):
name = plugin_attrib = "tel"
class Parameters(_VCardElementBase):
name = plugin_attrib = "parameters"
class Type(_VCardTextElementBase):
name = "type"
plugin_attrib = "type_"
class Adr(_VCardElementBase):
name = plugin_attrib = "adr"
interfaces = sub_interfaces = {"locality", "country"}
register_stanza_plugin(Parameters, Type)
register_stanza_plugin(Tel, Parameters)
for p in N, Fn, Nickname, Note, Url, Impp, Email, BDay, Tel, Adr:
register_stanza_plugin(VCard4, p, iterable=True)
register_stanza_plugin(Iq, VCard4)

View File

@@ -0,0 +1,111 @@
import logging
from datetime import date
from typing import Optional
from slixmpp import (
JID,
ComponentXMPP,
register_stanza_plugin,
)
from slixmpp.plugins.base import BasePlugin
from . import stanza
class XEP_0292(BasePlugin):
"""
vCard4 over XMPP
Does not implement the IQ semantics that neither movim does gajim implement,
cf https://xmpp.org/extensions/xep-0292.html#self-iq-retrieval and
https://xmpp.org/extensions/xep-0292.html#self-iq-publication
Does not implement the "empty pubsub event item" as a notification mechanism,
that neither gajim nor movim implement
https://xmpp.org/extensions/xep-0292.html#sect-idm45744791178720
Relies on classic pubsub semantics instead.
"""
xmpp: ComponentXMPP
name = "xep_0292"
description = "vCard4 Over XMPP"
dependencies = {"xep_0163", "xep_0060", "xep_0030"}
stanza = stanza
def plugin_init(self):
pubsub_stanza = self.xmpp["xep_0060"].stanza
register_stanza_plugin(pubsub_stanza.Item, stanza.VCard4)
register_stanza_plugin(pubsub_stanza.EventItem, stanza.VCard4)
self.xmpp['xep_0060'].map_node_event(stanza.NS, 'vcard4')
def plugin_end(self):
self.xmpp['xep_0030'].del_feature(feature=stanza.NS)
self.xmpp['xep_0163'].remove_interest(stanza.NS)
def session_bind(self, jid):
self.xmpp['xep_0163'].register_pep('vcard4', stanza.VCard4)
def publish_vcard(
self,
full_name: Optional[str] = None,
given: Optional[str] = None,
surname: Optional[str] = None,
birthday: Optional[date] = None,
nickname: Optional[str] = None,
phone: Optional[str] = None,
note: Optional[str] = None,
url: Optional[str] = None,
email: Optional[str] = None,
country: Optional[str] = None,
locality: Optional[str] = None,
impp: Optional[str] = None,
**pubsubkwargs,
):
"""
Publish a vcard using PEP
"""
vcard = stanza.VCard4()
if impp:
vcard.add_impp(impp)
if nickname:
vcard.add_nickname(nickname)
if full_name:
vcard["full_name"] = full_name
if given:
vcard["given"] = given
if surname:
vcard["surname"] = surname
if birthday:
vcard["birthday"] = birthday
if note:
vcard.add_note(note)
if url:
vcard.add_url(url)
if email:
vcard.add_email(email)
if phone:
vcard.add_tel(phone)
if country and locality:
vcard.add_address(country, locality)
elif country:
vcard.add_address(country, locality)
return self.xmpp["xep_0163"].publish(vcard, id="current", **pubsubkwargs)
def retrieve_vcard(self, jid: JID, **pubsubkwargs):
"""
Retrieve a vcard using PEP
"""
return self.xmpp["xep_0060"].get_item(
jid, stanza.VCard4.namespace, "current", **pubsubkwargs
)
log = logging.getLogger(__name__)