comparison sat/plugins/plugin_xep_0446.py @ 3896:dbf0c7faaf49

plugin XEP-0446: File Metadata implementation: rel 379
author Goffi <goffi@goffi.org>
date Wed, 21 Sep 2022 22:27:28 +0200
parents
children 0ff265725489
comparison
equal deleted inserted replaced
3895:eb0a77bea363 3896:dbf0c7faaf49
1 #!/usr/bin/env python3
2
3 # Copyright (C) 2009-2022 Jérôme Poisson (goffi@goffi.org)
4
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 from logging import exception
19 from typing import Optional, Union, Tuple, Dict, Any
20 from pathlib import Path
21
22 from twisted.words.xish import domish
23
24 from sat.core.constants import Const as C
25 from sat.core.i18n import _
26 from sat.core.log import getLogger
27 from sat.core import exceptions
28 from sat.tools import utils
29
30 log = getLogger(__name__)
31
32
33 PLUGIN_INFO = {
34 C.PI_NAME: "File Metadata Element",
35 C.PI_IMPORT_NAME: "XEP-0446",
36 C.PI_TYPE: "XEP",
37 C.PI_MODES: C.PLUG_MODE_BOTH,
38 C.PI_PROTOCOLS: ["XEP-0446"],
39 C.PI_DEPENDENCIES: ["XEP-0300"],
40 C.PI_MAIN: "XEP_0446",
41 C.PI_HANDLER: "no",
42 C.PI_DESCRIPTION: _("""Implementation of XEP-0446 (File Metadata Element)"""),
43 }
44
45 NS_FILE_METADATA = "urn:xmpp:file:metadata:0"
46
47
48 class XEP_0446:
49
50 def __init__(self, host):
51 log.info(_("XEP-0446 (File Metadata Element) plugin initialization"))
52 host.registerNamespace("file-metadata", NS_FILE_METADATA)
53 self._hash = host.plugins["XEP-0300"]
54
55 def get_file_metadata_elt(
56 self,
57 name: Optional[str] = None,
58 media_type: Optional[str] = None,
59 desc: Optional[str] = None,
60 size: Optional[int] = None,
61 file_hash: Optional[Tuple[str, str]] = None,
62 date: Optional[Union[float, int]] = None,
63 width: Optional[int] = None,
64 height: Optional[int] = None,
65 length: Optional[int] = None,
66 thumbnail: Optional[str] = None,
67 ) -> domish.Element:
68 """Generate the element describing a file
69
70 @param name: name of the file
71 @param media_type: media-type
72 @param desc: description
73 @param size: size in bytes
74 @param file_hash: (algo, hash)
75 @param date: timestamp of the last modification datetime
76 @param width: image width in pixels
77 @param height: image height in pixels
78 @param length: video length in seconds
79 @param thumbnail: URL to a thumbnail
80 @return: ``<file/>`` element
81 """
82 if name:
83 name = Path(name).name
84 file_elt = domish.Element((NS_FILE_METADATA, "file"))
85 for name, value in (
86 ("name", name),
87 ("media-type", media_type),
88 ("desc", desc),
89 ("size", size),
90 ("width", width),
91 ("height", height),
92 ("length", length),
93 ):
94 if value is not None:
95 file_elt.addElement(name, content=str(value))
96 if file_hash is not None:
97 hash_algo, hash_ = file_hash
98 file_elt.addChild(self._hash.buildHashElt(hash_, hash_algo))
99 if date is not None:
100 file_elt.addElement("date", utils.xmpp_date(date))
101 if thumbnail is not None:
102 # TODO: implement thumbnails
103 log.warning("thumbnail is not implemented yet")
104 return file_elt
105
106 def parse_file_metadata_elt(
107 self,
108 file_metadata_elt: domish.Element
109 ) -> Dict[str, Any]:
110 """Parse <file-metadata/> element
111
112 @param file_metadata_elt: <file-metadata/> element
113 a parent element can also be used
114 @return: file-metadata data. It's a dict whose keys correspond to
115 [get_file_metadata_elt] parameters
116 @raise exceptions.NotFound: no <file-metadata/> element has been found
117 """
118
119 if file_metadata_elt.name != "file-metadata":
120 try:
121 file_metadata_elt = next(
122 file_metadata_elt.elements(NS_FILE_METADATA, "file-metadata")
123 )
124 except StopIteration:
125 raise exceptions.NotFound
126 data: Dict[str, Any] = {}
127
128 for key, type_ in (
129 ("name", str),
130 ("media-type", str),
131 ("desc", str),
132 ("size", int),
133 ("date", "timestamp"),
134 ("width", int),
135 ("height", int),
136 ("length", int),
137 ):
138 elt = next(file_metadata_elt.elements(NS_FILE_METADATA, key), None)
139 if elt is not None:
140 if type_ in (str, int):
141 content = str(elt)
142 if key == "name":
143 # we avoid malformed names or names containing path elements
144 content = Path(content).name
145 elif key == "media-type":
146 key = "media_type"
147 data[key] = type_(content)
148 elif type == "timestamp":
149 data[key] = utils.parse_xmpp_date(str(elt))
150 else:
151 raise exceptions.InternalError
152
153 try:
154 algo, hash_ = self._hash.parseHashElt(file_metadata_elt)
155 except exceptions.NotFound:
156 pass
157 except exceptions.DataError:
158 from sat.tools.xml_tools import pFmtElt
159 log.warning("invalid <hash/> element:\n{pFmtElt(file_metadata_elt)}")
160 else:
161 data["file_hash"] = (algo, hash_.decode())
162
163 # TODO: thumbnails
164
165 return data