# HG changeset patch # User Goffi # Date 1447262387 -3600 # Node ID babd97d80049317757e2de5e6a12a7df3aad136c # Parent 6338677f3a8946c5da4aad03548c0d7528608bb5 plugins XEP-0234, file: moved file request dialog to file plugin diff -r 6338677f3a89 -r babd97d80049 src/plugins/plugin_misc_file.py --- a/src/plugins/plugin_misc_file.py Wed Nov 11 14:56:05 2015 +0100 +++ b/src/plugins/plugin_misc_file.py Wed Nov 11 18:19:47 2015 +0100 @@ -17,12 +17,13 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from sat.core.i18n import _ +from sat.core.i18n import _, D_ from sat.core.constants import Const as C from sat.core.log import getLogger log = getLogger(__name__) +from sat.tools import xml_tools +from twisted.internet import defer import os -from twisted.internet import defer import uuid @@ -37,6 +38,12 @@ } +CONFIRM = D_(u'{peer} wants to send the file "{name}" to you:\n{desc}\n\nThe file has a size of {size_human}\n\nDo you accept ?') +CONFIRM_TITLE = D_(u'Confirm file transfer') +CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?') +CONFIRM_OVERWRITE_TITLE = D_(u'File exists') + + class SatFile(object): """A file-like object to have high level files manipulation""" # TODO: manage "with" statement @@ -94,4 +101,85 @@ log.info(_("plugin File initialization")) self.host = host + # Dialogs with user + # the overwrite check is done here + def _openFileWrite(self, file_path, transfer_data, file_data, profile): + assert 'file_obj' not in transfer_data + transfer_data['file_obj'] = SatFile( + self.host, + file_path, + 'w', + size=file_data['size'], + profile=profile, + ) + + def _gotConfirmation(self, data, peer_jid, transfer_data, file_data, profile): + """Called when the permission and dest path have been received + + @param peer_jid(jid.JID): jid of the file sender + @param transfer_data(dict): same as for [self.getDestDir] + @param file_data(dict): same as for [self.getDestDir] + @param profile: %(doc_profile)s + return (bool): True if copy is wanted and OK + False if user wants to cancel + if file exists ask confirmation and call again self._getDestDir if needed + """ + if data.get('cancelled', False): + return False + path = data['path'] + file_data['file_path'] = file_path = os.path.join(path, file_data['name']) + log.debug(u'destination file path set to {}'.format(file_path)) + + # we manage case where file already exists + if os.path.exists(file_path): + def check_overwrite(overwrite): + if overwrite: + self._openFileWrite(file_path, transfer_data, file_data, profile) + return True + else: + return self.getDestDir(peer_jid, transfer_data, file_data, profile) + + exists_d = xml_tools.deferConfirm( + self.host, + _(CONFIRM_OVERWRITE).format(file_path), + _(CONFIRM_OVERWRITE_TITLE), + profile=profile) + exists_d.addCallback(check_overwrite) + return exists_d + + self._openFileWrite(file_path, transfer_data, file_data, profile) + return True + + def getDestDir(self, peer_jid, transfer_data, file_data, profile): + """Request confirmation and destination dir to user + + Overwrite confirmation is managed. + if transfer is confirmed, 'file_obj' is added to transfer_data + @param peer_jid(jid.JID): jid of the file sender + @param filename(unicode): name of the file + @param transfer_data(dict): data of the transfer session, + it will be only used to store the file_obj. + "file_obj" key *MUST NOT* exist before using getDestDir + @param file_data(dict): information about the file to be transfered + It MUST contain the following keys: + - peer_jid (jid.JID): other peer jid + - name (unicode): name of the file to trasnsfer + the name must not be empty or contain a "/" character + - size (int): size of the file + It may content the key used in CONFIRM constant + It *MUST NOT* contain the "peer" key + "file_path" will be added to this dict once destination selected + @param profile: %(doc_profile)s + return (defer.Deferred): True if transfer is accepted + """ + filename = file_data['name'] + assert filename and not '/' in filename + d = xml_tools.deferDialog(self.host, + _(CONFIRM).format(peer=peer_jid.full(), **file_data), + _(CONFIRM_TITLE), + type_=C.XMLUI_DIALOG_FILE, + options={C.XMLUI_DATA_FILETYPE: C.XMLUI_DATA_FILETYPE_DIR}, + profile=profile) + d.addCallback(self._gotConfirmation, peer_jid, transfer_data, file_data, profile) + return d diff -r 6338677f3a89 -r babd97d80049 src/plugins/plugin_xep_0234.py --- a/src/plugins/plugin_xep_0234.py Wed Nov 11 14:56:05 2015 +0100 +++ b/src/plugins/plugin_xep_0234.py Wed Nov 11 18:19:47 2015 +0100 @@ -17,12 +17,11 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from sat.core.i18n import _, D_ +from sat.core.i18n import _ from sat.core.constants import Const as C from sat.core.log import getLogger log = getLogger(__name__) from sat.core import exceptions -from sat.tools import xml_tools from wokkel import disco, iwokkel from zope.interface import implements from sat.tools import utils @@ -35,10 +34,6 @@ NS_JINGLE_FT = 'urn:xmpp:jingle:apps:file-transfer:4' -CONFIRM = D_(u'{entity} wants to send the file "{name}" to you:\n{desc}\n\nThe file has a size of {size_human}\n\nDo you accept ?') -CONFIRM_TITLE = D_(u'Confirm file transfer') -CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?') -CONFIRM_OVERWRITE_TITLE = D_(u'File exists') PLUGIN_INFO = { "name": "Jingle File Transfer", @@ -79,84 +74,6 @@ }], profile=profile) - - # Dialogs with user - # the overwrite check is done here - - def _getDestDir(self, session, content_name, content_data, profile): - """Request confirmation and destination dir to user - - if transfer is confirmed, session is filled - @param session(dict): jingle session data - @param content_name(unicode): name of the jingle content - @param content_data(dict): content informations - @param profile: %(doc_profile)s - return (defer.Deferred): True if transfer is accepted - """ - application_data = content_data['application_data'] - file_data = application_data['file_data'] - d = xml_tools.deferDialog(self.host, - _(CONFIRM).format(entity=session['peer_jid'].full(), **file_data), - _(CONFIRM_TITLE), - type_=C.XMLUI_DIALOG_FILE, - options={C.XMLUI_DATA_FILETYPE: C.XMLUI_DATA_FILETYPE_DIR}, - profile=profile) - d.addCallback(self._gotConfirmation, session, content_name, content_data, application_data, profile) - return d - - def _openFileWrite(self, session, content_name, content_data, file_path, file_data, profile): - assert 'file_obj' not in content_data - file_obj = content_data['file_obj'] = self._f.File( - self.host, - file_path, - 'w', - size=file_data['size'], - profile=profile, - ) - finished_d = content_data['finished_d'] = defer.Deferred() - args = [file_obj, session, content_name, content_data, profile] - finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) - - def _gotConfirmation(self, data, session, content_name, content_data, application_data, profile): - """Called when the permission and dest path have been received - - @param data(dict): xmlui data received from file dialog - @param session(dict): jingle session data - @param content_name(unicode): name of the jingle content - @param content_data(dict): content session_data - @param content_data(dict): application session data - @param profile: %(doc_profile)s - return (bool): True if copy is wanted and OK - False if user wants to cancel - if fill exists ask confirmation and call again self._getDestDir if needed - """ - if data.get('cancelled', False): - return False - file_data = application_data['file_data'] - path = data['path'] - file_data['file_path'] = file_path = os.path.join(path, file_data['name']) - log.debug(u'destination file path set to {}'.format(file_path)) - - # we manage case where file already exists - if os.path.exists(file_path): - def check_overwrite(overwrite): - if overwrite: - self._openFileWrite(session, content_name, content_data, file_path, file_data, profile) - return True - else: - return self._getDestDir(session, content_name, content_data, profile) - - exists_d = xml_tools.deferConfirm( - self.host, - _(CONFIRM_OVERWRITE).format(file_path), - _(CONFIRM_OVERWRITE_TITLE), - profile=profile) - exists_d.addCallback(check_overwrite) - return exists_d - - self._openFileWrite(session, content_name, content_data, file_path, file_data, profile) - return True - # jingle callbacks def jingleSessionInit(self, session, content_name, filepath, name=None, file_desc=None, profile=C.PROF_KEY_NONE): @@ -214,7 +131,16 @@ content_data['application_data']['file_data'] = file_data # now we actualy request permission to user - return self._getDestDir(session, content_name, content_data, profile) + def gotConfirmation(confirmed): + if confirmed: + finished_d = content_data['finished_d'] = defer.Deferred() + args = [session, content_name, content_data, profile] + finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) + return confirmed + + d = self._f.getDestDir(session['peer_jid'], content_data, file_data, profile) + d.addCallback(gotConfirmation) + return d def jingleHandler(self, action, session, content_name, desc_elt, profile): @@ -234,29 +160,29 @@ assert not 'file_obj' in content_data file_path = application_data['file_path'] size = application_data['file_data']['size'] - file_obj = content_data['file_obj'] = self._f.File(self.host, - file_path, - size=size, - profile=profile - ) + content_data['file_obj'] = self._f.File(self.host, + file_path, + size=size, + profile=profile + ) finished_d = content_data['finished_d'] = defer.Deferred() - args = [file_obj, session, content_name, content_data, profile] + args = [session, content_name, content_data, profile] finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) else: log.warning(u"FIXME: unmanaged action {}".format(action)) return desc_elt - def _finishedCb(self, dummy, file_obj, session, content_name, content_data, profile): + def _finishedCb(self, dummy, session, content_name, content_data, profile): log.debug(u"File transfer completed successfuly") if content_data['senders'] != session['role']: # we terminate the session only if we are the received, # as recommanded in XEP-0234 ยง2 (after example 6) self._j.contentTerminate(session, content_name, profile=profile) - file_obj.close() + content_data['file_obj'].close() - def _finishedEb(self, failure, file_obj, session, content_name, content_data, profile): + def _finishedEb(self, failure, session, content_name, content_data, profile): log.warning(u"Error while streaming through s5b: {}".format(failure)) - file_obj.close() + content_data['file_obj'].close() self._j.contentTerminate(session, content_name, reason=self._j.REASON_FAILED_TRANSPORT, profile=profile)