Mercurial > libervia-backend
view src/plugins/plugin_comp_file_sharing.py @ 2504:67cc54b01a12
plugin file sharing component: first draft:
this component act as an endpoint where a user can store and retrieve its files using Jingle.
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 28 Feb 2018 18:28:39 +0100 |
parents | |
children | 4440ea7047bd |
line wrap: on
line source
#!/usr/bin/env python2 # -*- coding: utf-8 -*- # SAT plugin for parrot mode (experimental) # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from sat.core.i18n import _ from sat.core.constants import Const as C from sat.core import exceptions from sat.core.log import getLogger log = getLogger(__name__) from sat.tools.common import regex from sat.tools import stream from twisted.internet import defer import os import os.path PLUGIN_INFO = { C.PI_NAME: "File sharing component", C.PI_IMPORT_NAME: "file_sharing", C.PI_MODES: [C.PLUG_MODE_COMPONENT], C.PI_TYPE: C.PLUG_TYPE_ENTRY_POINT, C.PI_PROTOCOLS: [], C.PI_DEPENDENCIES: ["FILE", "XEP-0234", "XEP-0260", "XEP-0261", "XEP-0329"], C.PI_RECOMMENDATIONS: [], C.PI_MAIN: "FileSharing", C.PI_HANDLER: "no", C.PI_DESCRIPTION: _(u"""Component hosting and sharing files""") } PROGRESS_ID_KEY = 'progress_id' HASH_ALGO = u'sha-256' class FileSharing(object): def __init__(self, host): log.info(_(u"File Sharing initialization")) self.host = host self._f = host.plugins['FILE'] self._jf = host.plugins['XEP-0234'] self._h = host.plugins['XEP-0300'] host.trigger.add("FILE_getDestDir", self._getDestDirTrigger) host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger, priority=1000) self.files_path = host.getLocalPath(None, C.FILES_DIR, profile=False) def profileConnected(self, client): path = client.file_tmp_dir = os.path.join( self.host.memory.getConfig('', 'local_dir'), C.FILES_TMP_DIR, regex.pathEscape(client.profile)) if not os.path.exists(path): os.makedirs(path) @defer.inlineCallbacks def _fileTransferedCb(self, dummy, client, peer_jid, file_data, file_path): if file_data[u'hash_algo'] == HASH_ALGO: log.debug(_(u"Reusing already generated hash")) file_hash = file_data[u'hash_hasher'].hexdigest() else: hasher = self._h.getHasher(HASH_ALGO) with open('file_path') as f: file_hash = yield self._h.calculateHash(f, hasher) final_path = os.path.join(self.files_path, file_hash) if os.path.isfile(final_path): log.debug(u"file [{file_hash}] already exists, we can remove temporary one".format(file_hash = file_hash)) os.unlink(file_path) else: os.rename(file_path, final_path) log.debug(u"file [{file_hash}] moved to {files_path}".format(file_hash=file_hash, files_path=self.files_path)) self.host.memory.setFile(client, name=file_data[u'name'], version=u'', file_hash=file_hash, hash_algo=HASH_ALGO, size=file_data[u'size'], path=file_data.get(u'path'), namespace=file_data.get(u'namespace'), owner=peer_jid) def _getDestDirTrigger(self, client, peer_jid, transfer_data, file_data, stream_object): if not client.is_component: return True, None assert stream_object assert 'stream_object' not in transfer_data assert PROGRESS_ID_KEY in file_data filename = file_data['name'] assert filename and not '/' in filename file_tmp_dir = self.host.getLocalPath(client, C.FILES_TMP_DIR, peer_jid.userhost(), component=True, profile=False) file_tmp_path = file_data['file_path'] = os.path.join(file_tmp_dir, file_data['name']) transfer_data['finished_d'].addCallback(self._fileTransferedCb, client, peer_jid, file_data, file_tmp_path) self._f.openFileWrite(client, file_tmp_path, transfer_data, file_data, stream_object) return False, defer.succeed(True) @defer.inlineCallbacks def _retrieveFiles(self, client, session, content_data, content_name, file_data, file_elt): peer_jid = session[u'peer_jid'] try: found_files = yield self.host.memory.getFiles(client, peer_jid=peer_jid, name=file_data.get(u'name'), file_hash=file_data.get(u'hash'), hash_algo=file_data.get(u'hash_algo'), path=file_data.get(u'path'), namespace=file_data.get(u'namespace')) except exceptions.NotFound: found_files = None except exceptions.PermissionError: log.warning(_(u"{peer_jid} is trying to access an unauthorized file: {name}").format( peer_jid=peer_jid, name=file_data.get(u'name'))) defer.returnValue(False) if not found_files: log.warning(_(u"no matching file found ({file_data})").format(file_data=file_data)) defer.returnValue(False) # we only use the first found file found_file = found_files[0] file_hash = found_file[u'hash'] file_path = os.path.join(self.files_path, file_hash) file_data[u'hash_hasher'] = hasher = self._h.getHasher(found_file[u'hash_algo']) size = file_data[u'size'] = found_file[u'size'] file_data[u'file_hash'] = file_hash file_data[u'hash_algo'] = found_file[u'hash_algo'] # we complete file_elt so peer can have some details on the file if u'name' not in file_data: file_elt.addElement(u'name', content=found_file[u'name']) file_elt.addElement(u'size', content=unicode(size)) content_data['stream_object'] = stream.FileStreamObject( self.host, client, file_path, uid=self._jf.getProgressId(session, content_name), size=size, data_cb=lambda data: hasher.update(data), ) defer.returnValue(True) def _fileSendingRequestTrigger(self, client, session, content_data, content_name, file_data, file_elt): if not client.is_component: return True, None else: return False, self._retrieveFiles(client, session, content_data, content_name, file_data, file_elt)