Mercurial > libervia-backend
diff src/plugins/plugin_xep_0234.py @ 2512:025afb04c10b
plugin XEP-0234: some cleaning + added triggers to allow plugins to change parsing/generation of <file> element
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Mar 2018 17:53:19 +0100 |
parents | 7ad5f2c4e34a |
children | a19b2c43e719 |
line wrap: on
line diff
--- a/src/plugins/plugin_xep_0234.py Fri Mar 02 17:45:23 2018 +0100 +++ b/src/plugins/plugin_xep_0234.py Fri Mar 02 17:53:19 2018 +0100 @@ -36,6 +36,7 @@ from twisted.internet import error as internet_error from collections import namedtuple import regex +import mimetypes NS_JINGLE_FT = 'urn:xmpp:jingle:apps:file-transfer:5' @@ -59,6 +60,7 @@ class XEP_0234(object): # TODO: assure everything is closed when file is sent or session terminate is received # TODO: call self._f.unregister when unloading order will be managing (i.e. when dependencies will be unloaded at the end) + Range = Range # we copy the class here, so it can be used by other plugins def __init__(self, host): log.info(_("plugin Jingle File Transfer initialization")) @@ -86,41 +88,46 @@ # generic methods - def buildFileElement(self, name, file_hash=None, hash_algo=None, size=None, media_type=None, desc=None, - date=None, range_offset=None, range_length=None, path=None, namespace=None, file_elt=None): + def buildFileElement(self, name, file_hash=None, hash_algo=None, size=None, mime_type=None, desc=None, + modified=None, transfer_range=None, path=None, namespace=None, file_elt=None, **kwargs): """Generate a <file> element with available metadata @param file_hash(unicode, None): hash of the file empty string to set <hash-used/> element @param hash_algo(unicode, None): hash algorithm used if file_hash is None and hash_algo is set, a <hash-used/> element will be generated - @param range_offset(int, None): offset where transfer must start - use -1 to add an empty <range/> element - @param date(int, unicode, None): date of last modification + @param transfer_range(Range, None): where transfer must start/stop + @param modified(int, unicode, None): date of last modification 0 to use current date int to use an unix timestamp else must be an unicode string which will be used as it (it must be an XMPP time) @param file_elt(domish.Element, None): element to use None to create a new one + @param **kwargs: data for plugin extension (ignored by default) @return (domish.Element): generated element + @trigger XEP-0234_buildFileElement(file_elt, extra_args): can be used to extend elements to add """ if file_elt is None: file_elt = domish.Element((NS_JINGLE_FT, u'file')) - for name, value in ((u'name', name), (u'size', size), ('media-type', media_type), + for name, value in ((u'name', name), (u'size', size), ('media-type', mime_type), (u'desc', desc), (u'path', path), (u'namespace', namespace)): if value is not None: file_elt.addElement(name, content=unicode(value)) - if date is not None: - if isinstance(date, int): - file_elt.addElement(u'date', utils.xmpp_date(date or None)) + + if modified is not None: + if isinstance(modified, int): + file_elt.addElement(u'date', utils.xmpp_date(modified or None)) else: - file_elt.addElement(u'date', date) - if range_offset or range_length: - range_elt = file_elt.addElement(u'range') - if range_offset is not None and range_offset != -1: - range_elt[u'offset'] = range_offset - if range_length is not None: - range_elt[u'length'] = range_length + file_elt.addElement(u'date', modified) + elif 'created' in kwargs: + file_elt.addElement(u'date', utils.xmpp_date(kwargs.pop('created'))) + + range_elt = file_elt.addElement(u'range') + if transfer_range is not None: + if transfer_range.offset is not None: + range_elt[u'offset'] = transfer_range.offset + if transfer_range.length is not None: + range_elt[u'length'] = transfer_range.length if file_hash is not None: if not file_hash: file_elt.addChild(self._hash.buildHashUsedElt()) @@ -128,9 +135,13 @@ file_elt.addChild(self._hash.buildHashElt(file_hash, hash_algo)) elif hash_algo is not None: file_elt.addChild(self._hash.buildHashUsedElt(hash_algo)) + self.host.trigger.point(u'XEP-0234_buildFileElement', file_elt, extra_args=kwargs) + if kwargs: + for kw in kwargs: + log.debug('ignored keyword: {}'.format(kw)) return file_elt - def buildFileElementFromDict(self, file_data, file_elt = None, **kwargs): + def buildFileElementFromDict(self, file_data, **kwargs): """like buildFileElement but get values from a file_data dict @param file_data(dict): metadata to use @@ -139,37 +150,23 @@ if kwargs: file_data = file_data.copy() file_data.update(kwargs) - (name, file_hash, hash_algo, size, media_type, - desc, date, path, namespace) = (file_data.get(u'name'), - file_data.get(u'file_hash'), - file_data.get(u'hash_algo'), - file_data.get(u'size'), - file_data.get(u'media-type'), - file_data.get(u'desc'), - file_data.get(u'date'), - file_data.get(u'path'), - file_data.get(u'namespace')) - try: - range_offset, range_length = file_data[u'range'] - except KeyError: - range_offset = range_length = None - return self. buildFileElement(name = name, file_hash = file_hash, hash_algo = hash_algo, size = size, - media_type = media_type, desc = desc, date = date, - range_offset = range_offset, range_length = range_length, path = path, - namespace = namespace, file_elt = file_elt) + return self. buildFileElement(**file_data) - def parseFileElement(self, file_elt, file_data=None, given=False, parent_elt=None): + def parseFileElement(self, file_elt, file_data=None, given=False, parent_elt=None, keep_empty_range=False): """Parse a <file> element and file dictionary accordingly @param file_data(dict, None): dict where the data will be set following keys will be set (and overwritten if they already exist): - name, file_hash, hash_algo, size, media-type, desc, path, namespace, range + name, file_hash, hash_algo, size, mime_type, desc, path, namespace, range if None, a new dict is created @param given(bool): if True, prefix hash key with "given_" @param parent_elt(domish.Element, None): parent of the file element if set, file_elt must not be set + @param keep_empty_range(bool): if True, keep empty range (i.e. range when offset and length are None) + empty range are useful to know if a peer_jid can handle range @return (dict): file_data + @trigger XEP-0234_parseFileElement(file_elt, file_data): can be used to parse new elements @raise exceptions.NotFound: there is not <file> element in parent_elt @raise exceptions.DataError: if file_elt uri is not NS_JINGLE_FT """ @@ -187,12 +184,13 @@ if file_data is None: file_data = {} - for name in (u'name', u'media-type', u'desc', u'path', u'namespace'): + for name in (u'name', u'desc', u'path', u'namespace'): try: - file_data[name] = unicode(file_elt.elements(NS_JINGLE_FT, name).next()) + file_data[name] = unicode(next(file_elt.elements(NS_JINGLE_FT, name))) except StopIteration: pass + name = file_data.get(u'name') if name == u'..': # we don't want to go to parent dir when joining to a path @@ -202,7 +200,17 @@ file_data[u'name'] = regex.pathEscape(name) try: - file_data[u'size'] = int(unicode(file_elt.elements(NS_JINGLE_FT, u'size').next())) + file_data[u'mime_type'] = unicode(next(file_elt.elements(NS_JINGLE_FT, u'media-type'))) + except StopIteration: + pass + + try: + file_data[u'size'] = int(unicode(next(file_elt.elements(NS_JINGLE_FT, u'size')))) + except StopIteration: + pass + + try: + file_data[u'modified'] = utils.date_parse(next(file_elt.elements(NS_JINGLE_FT, u'date'))) except StopIteration: pass @@ -213,7 +221,8 @@ else: offset = range_elt.getAttribute('offset') length = range_elt.getAttribute('length') - file_data[u'range'] = Range(offset=offset, length=length) + if offset or length or keep_empty_range: + file_data[u'transfer_range'] = Range(offset=offset, length=length) prefix = u'given_' if given else u'' hash_algo_key, hash_key = u'hash_algo', prefix + u'file_hash' @@ -222,6 +231,8 @@ except exceptions.NotFound: pass + self.host.trigger.point(u'XEP-0234_parseFileElement', file_elt, file_data) + return file_data # bridge methods @@ -314,17 +325,20 @@ if content_data[u'senders'] == self._j.ROLE_INITIATOR: # we send a file + if name is None: + name = os.path.basename(filepath) file_data[u'date'] = utils.xmpp_date() file_data[u'desc'] = extra.pop(u'file_desc', u'') - file_data[u'media-type'] = "application/octet-stream" # TODO - file_data[u'name'] = os.path.basename(filepath) if name is None else name + file_data[u'name'] = name + mime_type = mimetypes.guess_type(name, strict=False)[0] + if mime_type is not None: + file_data[u'mime_type'] = mime_type file_data[u'size'] = os.path.getsize(filepath) if u'namespace' in extra: file_data[u'namespace'] = extra[u'namespace'] if u'path' in extra: file_data[u'path'] = extra[u'path'] self.buildFileElementFromDict(file_data, file_elt=file_elt, file_hash=u'') - file_elt.addElement("range") # TODO else: # we request a file file_hash = extra.pop(u'file_hash', u'')