Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0234.py @ 1571:c668081eba1c
plugins XEP-0234, XEP-0260, XEP-0261: jingle session termination is managed by application (XEP-0234) instead of transport
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 08 Nov 2015 14:48:04 +0100 |
parents | 1f7a34d499e0 |
children | babd97d80049 |
comparison
equal
deleted
inserted
replaced
1570:37d4be4a9fed | 1571:c668081eba1c |
---|---|
29 import os.path | 29 import os.path |
30 from twisted.words.xish import domish | 30 from twisted.words.xish import domish |
31 from twisted.words.protocols.jabber import jid | 31 from twisted.words.protocols.jabber import jid |
32 from twisted.python import failure | 32 from twisted.python import failure |
33 from twisted.words.protocols.jabber.xmlstream import XMPPHandler | 33 from twisted.words.protocols.jabber.xmlstream import XMPPHandler |
34 from twisted.internet import defer | |
34 | 35 |
35 | 36 |
36 NS_JINGLE_FT = 'urn:xmpp:jingle:apps:file-transfer:4' | 37 NS_JINGLE_FT = 'urn:xmpp:jingle:apps:file-transfer:4' |
37 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 ?') | 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 ?') |
38 CONFIRM_TITLE = D_(u'Confirm file transfer') | 39 CONFIRM_TITLE = D_(u'Confirm file transfer') |
50 "description": _("""Implementation of Jingle File Transfer""") | 51 "description": _("""Implementation of Jingle File Transfer""") |
51 } | 52 } |
52 | 53 |
53 | 54 |
54 class XEP_0234(object): | 55 class XEP_0234(object): |
56 # TODO: assure everything is closed when file is sent or session terminate is received | |
55 | 57 |
56 def __init__(self, host): | 58 def __init__(self, host): |
57 log.info(_("plugin Jingle File Transfer initialization")) | 59 log.info(_("plugin Jingle File Transfer initialization")) |
58 self.host = host | 60 self.host = host |
59 self._j = host.plugins["XEP-0166"] # shortcut to access jingle | 61 self._j = host.plugins["XEP-0166"] # shortcut to access jingle |
79 | 81 |
80 | 82 |
81 # Dialogs with user | 83 # Dialogs with user |
82 # the overwrite check is done here | 84 # the overwrite check is done here |
83 | 85 |
84 def _getDestDir(self, session, content_data, profile): | 86 def _getDestDir(self, session, content_name, content_data, profile): |
85 """Request confirmation and destination dir to user | 87 """Request confirmation and destination dir to user |
86 | 88 |
87 if transfer is confirmed, session is filled | 89 if transfer is confirmed, session is filled |
88 @param session(dict): jingle session data | 90 @param session(dict): jingle session data |
91 @param content_name(unicode): name of the jingle content | |
89 @param content_data(dict): content informations | 92 @param content_data(dict): content informations |
90 @param profile: %(doc_profile)s | 93 @param profile: %(doc_profile)s |
91 return (defer.Deferred): True if transfer is accepted | 94 return (defer.Deferred): True if transfer is accepted |
92 """ | 95 """ |
93 application_data = content_data['application_data'] | 96 application_data = content_data['application_data'] |
96 _(CONFIRM).format(entity=session['peer_jid'].full(), **file_data), | 99 _(CONFIRM).format(entity=session['peer_jid'].full(), **file_data), |
97 _(CONFIRM_TITLE), | 100 _(CONFIRM_TITLE), |
98 type_=C.XMLUI_DIALOG_FILE, | 101 type_=C.XMLUI_DIALOG_FILE, |
99 options={C.XMLUI_DATA_FILETYPE: C.XMLUI_DATA_FILETYPE_DIR}, | 102 options={C.XMLUI_DATA_FILETYPE: C.XMLUI_DATA_FILETYPE_DIR}, |
100 profile=profile) | 103 profile=profile) |
101 d.addCallback(self._gotConfirmation, session, content_data, application_data, profile) | 104 d.addCallback(self._gotConfirmation, session, content_name, content_data, application_data, profile) |
102 return d | 105 return d |
103 | 106 |
104 def _openFileWrite(self, content_data, file_path, file_data, profile): | 107 def _openFileWrite(self, session, content_name, content_data, file_path, file_data, profile): |
105 assert 'file_obj' not in content_data | 108 assert 'file_obj' not in content_data |
106 content_data['file_obj'] = self._f.File( | 109 file_obj = content_data['file_obj'] = self._f.File( |
107 self.host, | 110 self.host, |
108 file_path, | 111 file_path, |
109 'w', | 112 'w', |
110 size=file_data['size'], | 113 size=file_data['size'], |
111 profile=profile, | 114 profile=profile, |
112 ) | 115 ) |
113 | 116 finished_d = content_data['finished_d'] = defer.Deferred() |
114 def _gotConfirmation(self, data, session, content_data, application_data, profile): | 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): | |
115 """Called when the permission and dest path have been received | 121 """Called when the permission and dest path have been received |
116 | 122 |
117 @param data(dict): xmlui data received from file dialog | 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 | |
118 return (bool): True if copy is wanted and OK | 129 return (bool): True if copy is wanted and OK |
119 False if user wants to cancel | 130 False if user wants to cancel |
120 if fill exists ask confirmation and call again self._getDestDir if needed | 131 if fill exists ask confirmation and call again self._getDestDir if needed |
121 """ | 132 """ |
122 if data.get('cancelled', False): | 133 if data.get('cancelled', False): |
128 | 139 |
129 # we manage case where file already exists | 140 # we manage case where file already exists |
130 if os.path.exists(file_path): | 141 if os.path.exists(file_path): |
131 def check_overwrite(overwrite): | 142 def check_overwrite(overwrite): |
132 if overwrite: | 143 if overwrite: |
133 self._openFileWrite(content_data, file_path, file_data, profile) | 144 self._openFileWrite(session, content_name, content_data, file_path, file_data, profile) |
134 return True | 145 return True |
135 else: | 146 else: |
136 return self._getDestDir(session, content_data, profile) | 147 return self._getDestDir(session, content_name, content_data, profile) |
137 | 148 |
138 exists_d = xml_tools.deferConfirm( | 149 exists_d = xml_tools.deferConfirm( |
139 self.host, | 150 self.host, |
140 _(CONFIRM_OVERWRITE).format(file_path), | 151 _(CONFIRM_OVERWRITE).format(file_path), |
141 _(CONFIRM_OVERWRITE_TITLE), | 152 _(CONFIRM_OVERWRITE_TITLE), |
142 profile=profile) | 153 profile=profile) |
143 exists_d.addCallback(check_overwrite) | 154 exists_d.addCallback(check_overwrite) |
144 return exists_d | 155 return exists_d |
145 | 156 |
146 self._openFileWrite(content_data, file_path, file_data, profile) | 157 self._openFileWrite(session, content_name, content_data, file_path, file_data, profile) |
147 return True | 158 return True |
148 | 159 |
149 # jingle callbacks | 160 # jingle callbacks |
150 | 161 |
151 def jingleSessionInit(self, session, content_name, filepath, name=None, file_desc=None, profile=C.PROF_KEY_NONE): | 162 def jingleSessionInit(self, session, content_name, filepath, name=None, file_desc=None, profile=C.PROF_KEY_NONE): |
201 # TODO: parse hash using plugin XEP-0300 | 212 # TODO: parse hash using plugin XEP-0300 |
202 | 213 |
203 content_data['application_data']['file_data'] = file_data | 214 content_data['application_data']['file_data'] = file_data |
204 | 215 |
205 # now we actualy request permission to user | 216 # now we actualy request permission to user |
206 return self._getDestDir(session, content_data, profile) | 217 return self._getDestDir(session, content_name, content_data, profile) |
207 | 218 |
208 | 219 |
209 def jingleHandler(self, action, session, content_name, desc_elt, profile): | 220 def jingleHandler(self, action, session, content_name, desc_elt, profile): |
210 content_data = session['contents'][content_name] | 221 content_data = session['contents'][content_name] |
211 application_data = content_data['application_data'] | 222 application_data = content_data['application_data'] |
226 file_obj = content_data['file_obj'] = self._f.File(self.host, | 237 file_obj = content_data['file_obj'] = self._f.File(self.host, |
227 file_path, | 238 file_path, |
228 size=size, | 239 size=size, |
229 profile=profile | 240 profile=profile |
230 ) | 241 ) |
231 file_obj.eof.addCallback(lambda dummy: file_obj.close()) | 242 finished_d = content_data['finished_d'] = defer.Deferred() |
243 args = [file_obj, session, content_name, content_data, profile] | |
244 finished_d.addCallbacks(self._finishedCb, self._finishedEb, args, None, args) | |
232 else: | 245 else: |
233 log.warning(u"FIXME: unmanaged action {}".format(action)) | 246 log.warning(u"FIXME: unmanaged action {}".format(action)) |
234 return desc_elt | 247 return desc_elt |
235 | 248 |
249 def _finishedCb(self, dummy, file_obj, session, content_name, content_data, profile): | |
250 log.debug(u"File transfer completed successfuly") | |
251 if content_data['senders'] != session['role']: | |
252 # we terminate the session only if we are the received, | |
253 # as recommanded in XEP-0234 ยง2 (after example 6) | |
254 self._j.contentTerminate(session, content_name, profile=profile) | |
255 file_obj.close() | |
256 | |
257 def _finishedEb(self, failure, file_obj, session, content_name, content_data, profile): | |
258 log.warning(u"Error while streaming through s5b: {}".format(failure)) | |
259 file_obj.close() | |
260 self._j.contentTerminate(session, content_name, reason=self._j.REASON_FAILED_TRANSPORT, profile=profile) | |
261 | |
236 | 262 |
237 class XEP_0234_handler(XMPPHandler): | 263 class XEP_0234_handler(XMPPHandler): |
238 implements(iwokkel.IDisco) | 264 implements(iwokkel.IDisco) |
239 | 265 |
240 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 266 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): |