comparison src/plugins/plugin_misc_file.py @ 1585:846a39900fa6

plugins XEP-0096, XEP-0260, file: sendFile method is managed by file plugin, which choose the best available method + progress_id fix
author Goffi <goffi@goffi.org>
date Sat, 14 Nov 2015 19:18:05 +0100
parents 833bdb227b16
children b144babc2658
comparison
equal deleted inserted replaced
1584:b57b4683dc33 1585:846a39900fa6
19 19
20 from sat.core.i18n import _, D_ 20 from sat.core.i18n import _, D_
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.tools import xml_tools 25 from sat.tools import xml_tools
25 from twisted.internet import defer 26 from twisted.internet import defer
27 from twisted.words.protocols.jabber import jid
26 import os 28 import os
29 import os.path
27 import uuid 30 import uuid
28 31
29 32
30 PLUGIN_INFO = { 33 PLUGIN_INFO = {
31 "name": "File Tansfer", 34 "name": "File Tansfer",
41 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 ?') 44 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 ?')
42 CONFIRM_TITLE = D_(u'Confirm file transfer') 45 CONFIRM_TITLE = D_(u'Confirm file transfer')
43 CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?') 46 CONFIRM_OVERWRITE = D_(u'File {} already exists, are you sure you want to overwrite ?')
44 CONFIRM_OVERWRITE_TITLE = D_(u'File exists') 47 CONFIRM_OVERWRITE_TITLE = D_(u'File exists')
45 48
49 PROGRESS_ID_KEY = 'progress_id'
50
46 51
47 class SatFile(object): 52 class SatFile(object):
48 """A file-like object to have high level files manipulation""" 53 """A file-like object to have high level files manipulation"""
49 # TODO: manage "with" statement 54 # TODO: manage "with" statement
50 55
52 """ 57 """
53 @param host: %(doc_host)s 58 @param host: %(doc_host)s
54 @param path(str): path of the file to get 59 @param path(str): path of the file to get
55 @param mode(str): same as for built-in "open" function 60 @param mode(str): same as for built-in "open" function
56 @param uid(unicode, None): unique id identifing this progressing element 61 @param uid(unicode, None): unique id identifing this progressing element
62 This uid will be used with self.host.progressGet
57 will be automaticaly generated if None 63 will be automaticaly generated if None
58 @param size(None, int): size of the file 64 @param size(None, int): size of the file
59 """ 65 """
60 self.host = host 66 self.host = host
61 self.uid = uid or unicode(uuid.uuid4()) 67 self.uid = uid or unicode(uuid.uuid4())
98 File=SatFile 104 File=SatFile
99 105
100 def __init__(self, host): 106 def __init__(self, host):
101 log.info(_("plugin File initialization")) 107 log.info(_("plugin File initialization"))
102 self.host = host 108 self.host = host
109 host.bridge.addMethod("fileSend", ".plugin", in_sign='sssss', out_sign='a{ss}', method=self._fileSend, async=True)
110 self._file_callbacks = []
111
112 def _fileSend(self, peer_jid_s, filepath, name="", file_desc="", profile=C.PROF_KEY_NONE):
113 return self.fileSend(jid.JID(peer_jid_s), filepath, name or None, file_desc or None, profile)
114
115 @defer.inlineCallbacks
116 def fileSend(self, peer_jid, filepath, filename=None, file_desc=None, profile=C.PROF_KEY_NONE):
117 """Send a file using best available method
118
119 @param peer_jid(jid.JID): jid of the destinee
120 @param filepath(str): absolute path to the file
121 @param filename(unicode, None): name to use, or None to find it from filepath
122 @param file_desc(unicode, None): description of the file
123 @param profile: %(doc_profile)s
124 @return (dict): action dictionary, with progress id in case of success, else xmlui message
125 """
126 if not os.path.isfile(filepath):
127 raise exceptions.DataError(u"The given path doesn't link to a file")
128 if not filename:
129 filename = os.path.basename(filepath) or '_'
130 for namespace, callback, priority, method_name in self._file_callbacks:
131 has_feature = yield self.host.hasFeature(namespace, peer_jid, profile)
132 if has_feature:
133 log.info(u"{name} method will be used to send the file".format(name=method_name))
134 progress_id = yield defer.maybeDeferred(callback, peer_jid, filepath, filename, file_desc, profile)
135 defer.returnValue({'progress': progress_id})
136 msg = u"Can't find any method to send file to {jid}".format(jid=peer_jid.full())
137 log.warning(msg)
138 defer.returnValue({'xmlui': xml_tools.note(u"Can't transfer file", msg, C.XMLUI_DATA_LVL_WARNING).toXml()})
139
140 def register(self, namespace, callback, priority=0, method_name=None):
141 """Register a fileSending method
142
143 @param namespace(unicode): XEP namespace
144 @param callback(callable): method to call (must have the same signature as [fileSend])
145 @param priority(int): pririoty of this method, the higher available will be used
146 @param method_name(unicode): short name for the method, namespace will be used if None
147 """
148 for data in self._file_callbacks:
149 if namespace == data[0]:
150 raise exceptions.ConflictError(u'A method with this namespace is already registered')
151 self._file_callbacks.append((namespace, callback, priority, method_name or namespace))
152 self._file_callbacks.sort(key=lambda data: data[2], reverse=True)
153
154 def unregister(self, namespace):
155 for idx, data in enumerate(self._file_callbacks):
156 if data[0] == namespace:
157 del [idx]
158 return
159 raise exceptions.NotFound(u"The namespace to unregister doesn't exist")
103 160
104 # Dialogs with user 161 # Dialogs with user
105 # the overwrite check is done here 162 # the overwrite check is done here
106 163
107 def _openFileWrite(self, file_path, transfer_data, file_data, profile): 164 def _openFileWrite(self, file_path, transfer_data, file_data, profile):
108 assert 'file_obj' not in transfer_data 165 assert 'file_obj' not in transfer_data
109 transfer_data['file_obj'] = SatFile( 166 transfer_data['file_obj'] = SatFile(
110 self.host, 167 self.host,
111 file_path, 168 file_path,
112 'w', 169 'w',
170 uid=file_data[PROGRESS_ID_KEY],
113 size=file_data['size'], 171 size=file_data['size'],
114 profile=profile, 172 profile=profile,
115 ) 173 )
116 174
117 def _gotConfirmation(self, data, peer_jid, transfer_data, file_data, profile): 175 def _gotConfirmation(self, data, peer_jid, transfer_data, file_data, profile):
165 It MUST contain the following keys: 223 It MUST contain the following keys:
166 - peer_jid (jid.JID): other peer jid 224 - peer_jid (jid.JID): other peer jid
167 - name (unicode): name of the file to trasnsfer 225 - name (unicode): name of the file to trasnsfer
168 the name must not be empty or contain a "/" character 226 the name must not be empty or contain a "/" character
169 - size (int): size of the file 227 - size (int): size of the file
228 - progress_id (unicode): id to use for progression
170 It may content the key used in CONFIRM constant 229 It may content the key used in CONFIRM constant
171 It *MUST NOT* contain the "peer" key 230 It *MUST NOT* contain the "peer" key
172 "file_path" will be added to this dict once destination selected 231 "file_path" will be added to this dict once destination selected
173 "size_human" will also be added with human readable file size 232 "size_human" will also be added with human readable file size
174 @param profile: %(doc_profile)s 233 @param profile: %(doc_profile)s
175 return (defer.Deferred): True if transfer is accepted 234 return (defer.Deferred): True if transfer is accepted
176 """ 235 """
177 filename = file_data['name'] 236 filename = file_data['name']
178 assert filename and not '/' in filename 237 assert filename and not '/' in filename
238 assert PROGRESS_ID_KEY in file_data
179 # human readable size 239 # human readable size
180 file_data['size_human'] = u'{:.6n} Mio'.format(float(file_data['size'])/(1024**2)) 240 file_data['size_human'] = u'{:.6n} Mio'.format(float(file_data['size'])/(1024**2))
181 d = xml_tools.deferDialog(self.host, 241 d = xml_tools.deferDialog(self.host,
182 _(CONFIRM).format(peer=peer_jid.full(), **file_data), 242 _(CONFIRM).format(peer=peer_jid.full(), **file_data),
183 _(CONFIRM_TITLE), 243 _(CONFIRM_TITLE),