comparison src/plugins/plugin_xep_0234.py @ 1574:babd97d80049

plugins XEP-0234, file: moved file request dialog to file plugin
author Goffi <goffi@goffi.org>
date Wed, 11 Nov 2015 18:19:47 +0100
parents c668081eba1c
children 833bdb227b16
comparison
equal deleted inserted replaced
1573:6338677f3a89 1574:babd97d80049
15 # GNU Affero General Public License for more details. 15 # GNU Affero General Public License for more details.
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _, D_ 20 from sat.core.i18n import _
21 from sat.core.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23 log = getLogger(__name__) 23 log = getLogger(__name__)
24 from sat.core import exceptions 24 from sat.core import exceptions
25 from sat.tools import xml_tools
26 from wokkel import disco, iwokkel 25 from wokkel import disco, iwokkel
27 from zope.interface import implements 26 from zope.interface import implements
28 from sat.tools import utils 27 from sat.tools import utils
29 import os.path 28 import os.path
30 from twisted.words.xish import domish 29 from twisted.words.xish import domish
33 from twisted.words.protocols.jabber.xmlstream import XMPPHandler 32 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
34 from twisted.internet import defer 33 from twisted.internet import defer
35 34
36 35
37 NS_JINGLE_FT = 'urn:xmpp:jingle:apps:file-transfer:4' 36 NS_JINGLE_FT = 'urn:xmpp:jingle:apps:file-transfer:4'
38 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 ?')
39 CONFIRM_TITLE = D_(u'Confirm file transfer')
40 CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?')
41 CONFIRM_OVERWRITE_TITLE = D_(u'File exists')
42 37
43 PLUGIN_INFO = { 38 PLUGIN_INFO = {
44 "name": "Jingle File Transfer", 39 "name": "Jingle File Transfer",
45 "import_name": "XEP-0234", 40 "import_name": "XEP-0234",
46 "type": "XEP", 41 "type": "XEP",
76 'app_kwargs': {'filepath': filepath, 71 'app_kwargs': {'filepath': filepath,
77 'name': name, 72 'name': name,
78 'file_desc': file_desc}, 73 'file_desc': file_desc},
79 }], 74 }],
80 profile=profile) 75 profile=profile)
81
82
83 # Dialogs with user
84 # the overwrite check is done here
85
86 def _getDestDir(self, session, content_name, content_data, profile):
87 """Request confirmation and destination dir to user
88
89 if transfer is confirmed, session is filled
90 @param session(dict): jingle session data
91 @param content_name(unicode): name of the jingle content
92 @param content_data(dict): content informations
93 @param profile: %(doc_profile)s
94 return (defer.Deferred): True if transfer is accepted
95 """
96 application_data = content_data['application_data']
97 file_data = application_data['file_data']
98 d = xml_tools.deferDialog(self.host,
99 _(CONFIRM).format(entity=session['peer_jid'].full(), **file_data),
100 _(CONFIRM_TITLE),
101 type_=C.XMLUI_DIALOG_FILE,
102 options={C.XMLUI_DATA_FILETYPE: C.XMLUI_DATA_FILETYPE_DIR},
103 profile=profile)
104 d.addCallback(self._gotConfirmation, session, content_name, content_data, application_data, profile)
105 return d
106
107 def _openFileWrite(self, session, content_name, content_data, file_path, file_data, profile):
108 assert 'file_obj' not in content_data
109 file_obj = content_data['file_obj'] = self._f.File(
110 self.host,
111 file_path,
112 'w',
113 size=file_data['size'],
114 profile=profile,
115 )
116 finished_d = content_data['finished_d'] = defer.Deferred()
117 args = [file_obj, session, content_name, content_data, profile]
118 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args)
119
120 def _gotConfirmation(self, data, session, content_name, content_data, application_data, profile):
121 """Called when the permission and dest path have been received
122
123 @param data(dict): xmlui data received from file dialog
124 @param session(dict): jingle session data
125 @param content_name(unicode): name of the jingle content
126 @param content_data(dict): content session_data
127 @param content_data(dict): application session data
128 @param profile: %(doc_profile)s
129 return (bool): True if copy is wanted and OK
130 False if user wants to cancel
131 if fill exists ask confirmation and call again self._getDestDir if needed
132 """
133 if data.get('cancelled', False):
134 return False
135 file_data = application_data['file_data']
136 path = data['path']
137 file_data['file_path'] = file_path = os.path.join(path, file_data['name'])
138 log.debug(u'destination file path set to {}'.format(file_path))
139
140 # we manage case where file already exists
141 if os.path.exists(file_path):
142 def check_overwrite(overwrite):
143 if overwrite:
144 self._openFileWrite(session, content_name, content_data, file_path, file_data, profile)
145 return True
146 else:
147 return self._getDestDir(session, content_name, content_data, profile)
148
149 exists_d = xml_tools.deferConfirm(
150 self.host,
151 _(CONFIRM_OVERWRITE).format(file_path),
152 _(CONFIRM_OVERWRITE_TITLE),
153 profile=profile)
154 exists_d.addCallback(check_overwrite)
155 return exists_d
156
157 self._openFileWrite(session, content_name, content_data, file_path, file_data, profile)
158 return True
159 76
160 # jingle callbacks 77 # jingle callbacks
161 78
162 def jingleSessionInit(self, session, content_name, filepath, name=None, file_desc=None, profile=C.PROF_KEY_NONE): 79 def jingleSessionInit(self, session, content_name, filepath, name=None, file_desc=None, profile=C.PROF_KEY_NONE):
163 content_data = session['contents'][content_name] 80 content_data = session['contents'][content_name]
212 # TODO: parse hash using plugin XEP-0300 129 # TODO: parse hash using plugin XEP-0300
213 130
214 content_data['application_data']['file_data'] = file_data 131 content_data['application_data']['file_data'] = file_data
215 132
216 # now we actualy request permission to user 133 # now we actualy request permission to user
217 return self._getDestDir(session, content_name, content_data, profile) 134 def gotConfirmation(confirmed):
135 if confirmed:
136 finished_d = content_data['finished_d'] = defer.Deferred()
137 args = [session, content_name, content_data, profile]
138 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args)
139 return confirmed
140
141 d = self._f.getDestDir(session['peer_jid'], content_data, file_data, profile)
142 d.addCallback(gotConfirmation)
143 return d
218 144
219 145
220 def jingleHandler(self, action, session, content_name, desc_elt, profile): 146 def jingleHandler(self, action, session, content_name, desc_elt, profile):
221 content_data = session['contents'][content_name] 147 content_data = session['contents'][content_name]
222 application_data = content_data['application_data'] 148 application_data = content_data['application_data']
232 file_elt.addElement('range') 158 file_elt.addElement('range')
233 elif action == self._j.A_SESSION_ACCEPT: 159 elif action == self._j.A_SESSION_ACCEPT:
234 assert not 'file_obj' in content_data 160 assert not 'file_obj' in content_data
235 file_path = application_data['file_path'] 161 file_path = application_data['file_path']
236 size = application_data['file_data']['size'] 162 size = application_data['file_data']['size']
237 file_obj = content_data['file_obj'] = self._f.File(self.host, 163 content_data['file_obj'] = self._f.File(self.host,
238 file_path, 164 file_path,
239 size=size, 165 size=size,
240 profile=profile 166 profile=profile
241 ) 167 )
242 finished_d = content_data['finished_d'] = defer.Deferred() 168 finished_d = content_data['finished_d'] = defer.Deferred()
243 args = [file_obj, session, content_name, content_data, profile] 169 args = [session, content_name, content_data, profile]
244 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) 170 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args)
245 else: 171 else:
246 log.warning(u"FIXME: unmanaged action {}".format(action)) 172 log.warning(u"FIXME: unmanaged action {}".format(action))
247 return desc_elt 173 return desc_elt
248 174
249 def _finishedCb(self, dummy, file_obj, session, content_name, content_data, profile): 175 def _finishedCb(self, dummy, session, content_name, content_data, profile):
250 log.debug(u"File transfer completed successfuly") 176 log.debug(u"File transfer completed successfuly")
251 if content_data['senders'] != session['role']: 177 if content_data['senders'] != session['role']:
252 # we terminate the session only if we are the received, 178 # we terminate the session only if we are the received,
253 # as recommanded in XEP-0234 §2 (after example 6) 179 # as recommanded in XEP-0234 §2 (after example 6)
254 self._j.contentTerminate(session, content_name, profile=profile) 180 self._j.contentTerminate(session, content_name, profile=profile)
255 file_obj.close() 181 content_data['file_obj'].close()
256 182
257 def _finishedEb(self, failure, file_obj, session, content_name, content_data, profile): 183 def _finishedEb(self, failure, session, content_name, content_data, profile):
258 log.warning(u"Error while streaming through s5b: {}".format(failure)) 184 log.warning(u"Error while streaming through s5b: {}".format(failure))
259 file_obj.close() 185 content_data['file_obj'].close()
260 self._j.contentTerminate(session, content_name, reason=self._j.REASON_FAILED_TRANSPORT, profile=profile) 186 self._j.contentTerminate(session, content_name, reason=self._j.REASON_FAILED_TRANSPORT, profile=profile)
261 187
262 188
263 class XEP_0234_handler(XMPPHandler): 189 class XEP_0234_handler(XMPPHandler):
264 implements(iwokkel.IDisco) 190 implements(iwokkel.IDisco)