XEP-0385: minimal support
- includes bits of other required XEPs - only implements 'outgoing' SIMS
This commit is contained in:
parent
1f934d375c
commit
b4b1efe058
10 changed files with 254 additions and 0 deletions
6
slixmpp/plugins/xep_0234/__init__.py
Normal file
6
slixmpp/plugins/xep_0234/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from slixmpp.plugins.base import register_plugin
|
||||
|
||||
from . import stanza
|
||||
from .jingle_file_transfer import XEP_0234
|
||||
|
||||
register_plugin(XEP_0234)
|
21
slixmpp/plugins/xep_0234/jingle_file_transfer.py
Normal file
21
slixmpp/plugins/xep_0234/jingle_file_transfer.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import logging
|
||||
|
||||
from slixmpp.plugins import BasePlugin
|
||||
|
||||
from . import stanza
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0234(BasePlugin):
|
||||
|
||||
"""
|
||||
XEP-0234: Jingle File Transfer
|
||||
|
||||
Minimum needed for xep 0385 (Stateless inline media sharing)
|
||||
"""
|
||||
|
||||
name = "xep_0234"
|
||||
description = "XEP-0234: Jingle File Transfer"
|
||||
dependencies = {"xep_0082", "xep_0300"}
|
||||
stanza = stanza
|
38
slixmpp/plugins/xep_0234/stanza.py
Normal file
38
slixmpp/plugins/xep_0234/stanza.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
from datetime import datetime
|
||||
|
||||
from slixmpp.plugins.xep_0082 import format_datetime, parse
|
||||
from slixmpp.xmlstream import ElementBase
|
||||
|
||||
NS = "urn:xmpp:jingle:apps:file-transfer:5"
|
||||
|
||||
|
||||
class File(ElementBase):
|
||||
name = "file"
|
||||
namespace = NS
|
||||
plugin_attrib = "file"
|
||||
interfaces = sub_interfaces = {"media-type", "name", "date", "size", "hash", "desc"}
|
||||
|
||||
def set_size(self, size: int):
|
||||
self._set_sub_text("size", str(size))
|
||||
|
||||
def get_size(self):
|
||||
return _int_or_none(self._get_sub_text("size"))
|
||||
|
||||
def get_date(self):
|
||||
try:
|
||||
return parse(self._get_sub_text("date"))
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
def set_date(self, stamp: datetime):
|
||||
try:
|
||||
self._set_sub_text("date", format_datetime(stamp))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
def _int_or_none(v):
|
||||
try:
|
||||
return int(v)
|
||||
except ValueError:
|
||||
return None
|
6
slixmpp/plugins/xep_0372/__init__.py
Normal file
6
slixmpp/plugins/xep_0372/__init__.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from slixmpp.plugins.base import register_plugin
|
||||
|
||||
from . import stanza
|
||||
from .references import XEP_0372
|
||||
|
||||
register_plugin(XEP_0372)
|
23
slixmpp/plugins/xep_0372/references.py
Normal file
23
slixmpp/plugins/xep_0372/references.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
import logging
|
||||
|
||||
from slixmpp import Message, register_stanza_plugin
|
||||
from slixmpp.plugins import BasePlugin
|
||||
|
||||
from . import stanza
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0372(BasePlugin):
|
||||
"""
|
||||
XEP-0372: References
|
||||
|
||||
Minimum needed for xep 0385 (Stateless inline media sharing)
|
||||
"""
|
||||
|
||||
name = "xep_0372"
|
||||
description = "XEP-0372: References"
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(Message, stanza.Reference)
|
9
slixmpp/plugins/xep_0372/stanza.py
Normal file
9
slixmpp/plugins/xep_0372/stanza.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from slixmpp.xmlstream import ElementBase
|
||||
|
||||
NAMESPACE = "urn:xmpp:reference:0"
|
||||
|
||||
|
||||
class Reference(ElementBase):
|
||||
name = plugin_attrib = "reference"
|
||||
namespace = NAMESPACE
|
||||
interfaces = {"type", "uri", "id", "begin", "end"}
|
11
slixmpp/plugins/xep_0385/__init__.py
Normal file
11
slixmpp/plugins/xep_0385/__init__.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
# Slixmpp: The Slick XMPP Library
|
||||
# Copyright (C) 2012 Nathanael C. Fritz, Lance J.T. Stout
|
||||
# This file is part of Slixmpp.
|
||||
# See the file LICENSE for copying permission
|
||||
from slixmpp.plugins.base import register_plugin
|
||||
|
||||
from . import stanza
|
||||
from .sims import XEP_0385
|
||||
|
||||
register_plugin(XEP_0385)
|
66
slixmpp/plugins/xep_0385/sims.py
Normal file
66
slixmpp/plugins/xep_0385/sims.py
Normal file
|
@ -0,0 +1,66 @@
|
|||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Optional
|
||||
|
||||
from slixmpp.plugins import BasePlugin
|
||||
from slixmpp.stanza import Message
|
||||
from slixmpp.xmlstream import register_stanza_plugin
|
||||
|
||||
from . import stanza
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class XEP_0385(BasePlugin):
|
||||
|
||||
"""
|
||||
XEP-0385: Stateless Inline Media Sharing (SIMS)
|
||||
|
||||
Only support outgoing SIMS, incoming is not handled at all.
|
||||
"""
|
||||
|
||||
name = "xep_0385"
|
||||
description = "XEP-0385: Stateless Inline Media Sharing (SIMS)"
|
||||
dependencies = {"xep_0234", "xep_0300", "xep_0372"}
|
||||
stanza = stanza
|
||||
|
||||
def plugin_init(self):
|
||||
register_stanza_plugin(self.xmpp["xep_0372"].stanza.Reference, stanza.Sims)
|
||||
register_stanza_plugin(Message, stanza.Sims)
|
||||
|
||||
register_stanza_plugin(stanza.Sims, stanza.Sources)
|
||||
register_stanza_plugin(stanza.Sims, self.xmpp["xep_0234"].stanza.File)
|
||||
register_stanza_plugin(stanza.Sources, self.xmpp["xep_0372"].stanza.Reference)
|
||||
|
||||
def get_sims(
|
||||
self,
|
||||
path: Path,
|
||||
uris: Iterable[str],
|
||||
media_type: Optional[str],
|
||||
desc: Optional[str],
|
||||
):
|
||||
sims = stanza.Sims()
|
||||
for uri in uris:
|
||||
ref = self.xmpp["xep_0372"].stanza.Reference()
|
||||
ref["uri"] = uri
|
||||
ref["type"] = "data"
|
||||
sims["sources"].append(ref)
|
||||
if media_type:
|
||||
sims["file"]["media-type"] = media_type
|
||||
if desc:
|
||||
sims["file"]["desc"] = desc
|
||||
sims["file"]["name"] = path.name
|
||||
|
||||
stat = path.stat()
|
||||
sims["file"]["size"] = stat.st_size
|
||||
sims["file"]["date"] = datetime.fromtimestamp(stat.st_mtime)
|
||||
|
||||
h = self.xmpp.plugin["xep_0300"].compute_hash(path)
|
||||
h["value"] = h["value"].decode()
|
||||
sims["file"].append(h)
|
||||
|
||||
ref = self.xmpp["xep_0372"].stanza.Reference()
|
||||
ref.append(sims)
|
||||
ref["type"] = "data"
|
||||
return ref
|
14
slixmpp/plugins/xep_0385/stanza.py
Normal file
14
slixmpp/plugins/xep_0385/stanza.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
from slixmpp.xmlstream import ElementBase
|
||||
|
||||
NAMESPACE = "urn:xmpp:sims:1"
|
||||
|
||||
|
||||
class Sims(ElementBase):
|
||||
name = "media-sharing"
|
||||
plugin_attrib = "sims"
|
||||
namespace = NAMESPACE
|
||||
|
||||
|
||||
class Sources(ElementBase):
|
||||
name = plugin_attrib = "sources"
|
||||
namespace = NAMESPACE
|
60
tests/test_stream_xep_0385.py
Normal file
60
tests/test_stream_xep_0385.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
import unittest
|
||||
from base64 import b64encode
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from tempfile import NamedTemporaryFile
|
||||
from hashlib import sha256
|
||||
|
||||
from slixmpp.plugins.xep_0082 import format_datetime
|
||||
from slixmpp.test import SlixTest
|
||||
|
||||
class TestSIMS(SlixTest):
|
||||
def setUp(self):
|
||||
self.stream_start(
|
||||
mode="component", jid="whatevs.shakespeare.lit", plugins={"xep_0385"}
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
self.stream_close()
|
||||
|
||||
def test_set_file(self):
|
||||
with NamedTemporaryFile("wb+") as f:
|
||||
n = 10
|
||||
size = 0
|
||||
for i in range(n):
|
||||
size += len(bytes(i))
|
||||
f.write(bytes(i))
|
||||
|
||||
f.seek(0)
|
||||
h = b64encode(sha256(f.read()).digest()).decode()
|
||||
sims = self.xmpp["xep_0385"].get_sims(
|
||||
Path(f.name),
|
||||
["https://xxx.com"],
|
||||
media_type="MEDIA",
|
||||
desc="DESCRIPTION",
|
||||
)
|
||||
|
||||
self.check(
|
||||
sims,
|
||||
f"""
|
||||
<reference xmlns='urn:xmpp:reference:0' type='data'>
|
||||
<media-sharing xmlns='urn:xmpp:sims:1'>
|
||||
<file xmlns='urn:xmpp:jingle:apps:file-transfer:5'>
|
||||
<media-type>MEDIA</media-type>
|
||||
<name>{Path(f.name).name}</name>
|
||||
<size>{size}</size>
|
||||
<hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>{h}</hash>
|
||||
<desc>DESCRIPTION</desc>
|
||||
<date>{format_datetime(datetime.fromtimestamp(Path(f.name).stat().st_mtime))}</date>
|
||||
</file>
|
||||
<sources>
|
||||
<reference xmlns='urn:xmpp:reference:0' type='data' uri='https://xxx.com' />
|
||||
</sources>
|
||||
</media-sharing>
|
||||
</reference>
|
||||
""",
|
||||
use_values=False,
|
||||
)
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestSIMS)
|
Loading…
Reference in a new issue