Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0329.py @ 2562:26edcf3a30eb
core, setup: huge cleaning:
- moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention
- move twisted directory to root
- removed all hacks from setup.py, and added missing dependencies, it is now clean
- use https URL for website in setup.py
- removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed
- renamed sat.sh to sat and fixed its installation
- added python_requires to specify Python version needed
- replaced glib2reactor which use deprecated code by gtk3reactor
sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 02 Apr 2018 19:44:50 +0200 |
parents | src/plugins/plugin_xep_0329.py@8d82a62fa098 |
children | 282d1314d574 |
comparison
equal
deleted
inserted
replaced
2561:bd30dc3ffe5a | 2562:26edcf3a30eb |
---|---|
1 #!/usr/bin/env python2 | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT plugin for File Information Sharing (XEP-0329) | |
5 # Copyright (C) 2009-2018 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
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/>. | |
19 | |
20 from sat.core.i18n import _ | |
21 from sat.core import exceptions | |
22 from sat.core.constants import Const as C | |
23 from sat.core.log import getLogger | |
24 log = getLogger(__name__) | |
25 from sat.tools import stream | |
26 from sat.tools.common import regex | |
27 from wokkel import disco, iwokkel | |
28 from zope.interface import implements | |
29 from twisted.words.protocols.jabber import xmlstream | |
30 from twisted.words.protocols.jabber import jid | |
31 from twisted.words.protocols.jabber import error as jabber_error | |
32 from twisted.internet import defer | |
33 import mimetypes | |
34 import json | |
35 import os | |
36 | |
37 | |
38 PLUGIN_INFO = { | |
39 C.PI_NAME: "File Information Sharing", | |
40 C.PI_IMPORT_NAME: "XEP-0329", | |
41 C.PI_TYPE: "XEP", | |
42 C.PI_MODES: C.PLUG_MODE_BOTH, | |
43 C.PI_PROTOCOLS: ["XEP-0329"], | |
44 C.PI_DEPENDENCIES: ["XEP-0234", "XEP-0300"], | |
45 C.PI_MAIN: "XEP_0329", | |
46 C.PI_HANDLER: "yes", | |
47 C.PI_DESCRIPTION: _(u"""Implementation of File Information Sharing""") | |
48 } | |
49 | |
50 NS_FIS = 'urn:xmpp:fis:0' | |
51 | |
52 IQ_FIS_REQUEST = C.IQ_GET + '/query[@xmlns="' + NS_FIS + '"]' | |
53 SINGLE_FILES_DIR = u"files" | |
54 TYPE_VIRTUAL= u'virtual' | |
55 TYPE_PATH = u'path' | |
56 SHARE_TYPES = (TYPE_PATH, TYPE_VIRTUAL) | |
57 KEY_TYPE = u'type' | |
58 | |
59 | |
60 class ShareNode(object): | |
61 """node containing directory or files to share, virtual or real""" | |
62 host = None | |
63 | |
64 def __init__(self, name, parent, type_, access, path=None): | |
65 assert type_ in SHARE_TYPES | |
66 if name is not None: | |
67 if name == u'..' or u'/' in name or u'\\' in name: | |
68 log.warning(_(u'path change chars found in name [{name}], hack attempt?').format(name=name)) | |
69 if name == u'..': | |
70 name = u'--' | |
71 else: | |
72 name = regex.pathEscape(name) | |
73 self.name = name | |
74 self.children = {} | |
75 self.type = type_ | |
76 self.access = {} if access is None else access | |
77 assert isinstance(self.access, dict) | |
78 self.parent = None | |
79 if parent is not None: | |
80 assert name | |
81 parent.addChild(self) | |
82 else: | |
83 assert name is None | |
84 if path is not None: | |
85 if type_ != TYPE_PATH: | |
86 raise exceptions.InternalError(_(u"path can only be set on path nodes")) | |
87 self._path = path | |
88 | |
89 @property | |
90 def path(self): | |
91 return self._path | |
92 | |
93 def __getitem__(self, key): | |
94 return self.children[key] | |
95 | |
96 def __contains__(self, item): | |
97 return self.children.__contains__(item) | |
98 | |
99 def __iter__(self): | |
100 return self.children.__iter__ | |
101 | |
102 def iteritems(self): | |
103 return self.children.iteritems() | |
104 | |
105 def getOrCreate(self, name, type_=TYPE_VIRTUAL, access=None): | |
106 """get a node or create a virtual one and return it""" | |
107 if access is None: | |
108 access = {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}} | |
109 try: | |
110 return self.children[name] | |
111 except KeyError: | |
112 node = ShareNode(name, self, type_=type_, access=access) | |
113 return node | |
114 | |
115 def addChild(self, node): | |
116 if node.parent is not None: | |
117 raise exceptions.ConflictError(_(u"a node can't have several parents")) | |
118 node.parent = self | |
119 self.children[node.name] = node | |
120 | |
121 def _checkNodePermission(self, client, node, perms, peer_jid): | |
122 """Check access to this node for peer_jid | |
123 | |
124 @param node(SharedNode): node to check access | |
125 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* | |
126 @param peer_jid(jid.JID): entity which try to access the node | |
127 @return (bool): True if entity can access | |
128 """ | |
129 file_data = {u'access':self.access, u'owner': client.jid.userhostJID()} | |
130 try: | |
131 self.host.memory.checkFilePermission(file_data, peer_jid, perms) | |
132 except exceptions.PermissionError: | |
133 return False | |
134 else: | |
135 return True | |
136 | |
137 def checkPermissions(self, client, peer_jid, perms=(C.ACCESS_PERM_READ,), check_parents=True): | |
138 """check that peer_jid can access this node and all its parents | |
139 | |
140 @param peer_jid(jid.JID): entrity trying to access the node | |
141 @param perms(unicode): permissions to check, iterable of C.ACCESS_PERM_* | |
142 @param check_parents(bool): if True, access of all parents of this node will be checked too | |
143 @return (bool): True if entity can access this node | |
144 """ | |
145 peer_jid = peer_jid.userhostJID() | |
146 if peer_jid == client.jid.userhostJID(): | |
147 return True | |
148 | |
149 parent = self | |
150 while parent != None: | |
151 if not self._checkNodePermission(client, parent, perms, peer_jid): | |
152 return False | |
153 parent = parent.parent | |
154 | |
155 return True | |
156 | |
157 @staticmethod | |
158 def find(client, path, peer_jid, perms=(C.ACCESS_PERM_READ,)): | |
159 """find node corresponding to a path | |
160 | |
161 @param path(unicode): path to the requested file or directory | |
162 @param peer_jid(jid.JID): entity trying to find the node | |
163 used to check permission | |
164 @return (dict, unicode): shared data, remaining path | |
165 @raise exceptions.PermissionError: user can't access this file | |
166 @raise exceptions.DataError: path is invalid | |
167 @raise NotFound: path lead to a non existing file/directory | |
168 """ | |
169 path_elts = filter(None, path.split(u'/')) | |
170 | |
171 if u'..' in path_elts: | |
172 log.warning(_(u'parent dir ("..") found in path, hack attempt? path is {path} [{profile}]').format( | |
173 path=path, profile=client.profile)) | |
174 raise exceptions.PermissionError(u"illegal path elements") | |
175 | |
176 if not path_elts: | |
177 raise exceptions.DataError(_(u'path is invalid: {path}').format(path=path)) | |
178 | |
179 node = client._XEP_0329_root_node | |
180 | |
181 while path_elts: | |
182 if node.type == TYPE_VIRTUAL: | |
183 try: | |
184 node = node[path_elts.pop(0)] | |
185 except KeyError: | |
186 raise exceptions.NotFound | |
187 elif node.type == TYPE_PATH: | |
188 break | |
189 | |
190 if not node.checkPermissions(client, peer_jid, perms = perms): | |
191 raise exceptions.PermissionError(u"permission denied") | |
192 | |
193 return node, u'/'.join(path_elts) | |
194 | |
195 | |
196 class XEP_0329(object): | |
197 | |
198 def __init__(self, host): | |
199 log.info(_("File Information Sharing initialization")) | |
200 self.host = host | |
201 ShareNode.host = host | |
202 self._h = host.plugins['XEP-0300'] | |
203 self._jf = host.plugins['XEP-0234'] | |
204 host.bridge.addMethod("FISList", ".plugin", in_sign='ssa{ss}s', out_sign='aa{ss}', method=self._listFiles, async=True) | |
205 host.bridge.addMethod("FISSharePath", ".plugin", in_sign='ssss', out_sign='s', method=self._sharePath) | |
206 host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger) | |
207 host.registerNamespace('fis', NS_FIS) | |
208 | |
209 def getHandler(self, client): | |
210 return XEP_0329_handler(self) | |
211 | |
212 def profileConnected(self, client): | |
213 if not client.is_component: | |
214 client._XEP_0329_root_node = ShareNode(None, None, TYPE_VIRTUAL, {C.ACCESS_PERM_READ: {KEY_TYPE: C.ACCESS_TYPE_PUBLIC}}) | |
215 client._XEP_0329_names_data = {} # name to share map | |
216 | |
217 def _fileSendingRequestTrigger(self, client, session, content_data, content_name, file_data, file_elt): | |
218 """this trigger check that a requested file is available, and fill suitable data if so | |
219 | |
220 path and name are used to retrieve the file. If path is missing, we try our luck with known names | |
221 """ | |
222 if client.is_component: | |
223 return True, None | |
224 | |
225 try: | |
226 name = file_data[u'name'] | |
227 except KeyError: | |
228 return True, None | |
229 assert u'/' not in name | |
230 | |
231 path = file_data.get(u'path') | |
232 if path is not None: | |
233 # we have a path, we can follow it to find node | |
234 try: | |
235 node, rem_path = ShareNode.find(client, path, session[u'peer_jid']) | |
236 except (exceptions.PermissionError, exceptions.NotFound): | |
237 # no file, or file not allowed, we continue normal workflow | |
238 return True, None | |
239 except exceptions.DataError: | |
240 log.warning(_(u'invalid path: {path}').format(path=path)) | |
241 return True, None | |
242 | |
243 if node.type == TYPE_VIRTUAL: | |
244 # we have a virtual node, so name must link to a path node | |
245 try: | |
246 path = node[name].path | |
247 except KeyError: | |
248 return True, None | |
249 elif node.type == TYPE_PATH: | |
250 # we have a path node, so we can retrieve the full path now | |
251 path = os.path.join(node.path, rem_path, name) | |
252 else: | |
253 raise exceptions.InternalError(u'unknown type: {type}'.format(type=node.type)) | |
254 if not os.path.exists(path): | |
255 return True, None | |
256 size = os.path.getsize(path) | |
257 else: | |
258 # we don't have the path, we try to find the file by its name | |
259 try: | |
260 name_data = client._XEP_0329_names_data[name] | |
261 except KeyError: | |
262 return True, None | |
263 | |
264 for path, shared_file in name_data.iteritems(): | |
265 if True: # FIXME: filters are here | |
266 break | |
267 else: | |
268 return True, None | |
269 parent_node = shared_file[u'parent'] | |
270 if not parent_node.checkPermissions(client, session[u'peer_jid']): | |
271 log.warning(_(u"{peer_jid} requested a file (s)he can't access [{profile}]").format( | |
272 peer_jid = session[u'peer_jid'], profile = client.profile)) | |
273 return True, None | |
274 size = shared_file[u'size'] | |
275 | |
276 file_data[u'size'] = size | |
277 file_elt.addElement(u'size', content=unicode(size)) | |
278 hash_algo = file_data[u'hash_algo'] = self._h.getDefaultAlgo() | |
279 hasher = file_data[u'hash_hasher'] = self._h.getHasher(hash_algo) | |
280 file_elt.addChild(self._h.buildHashUsedElt(hash_algo)) | |
281 content_data['stream_object'] = stream.FileStreamObject( | |
282 self.host, | |
283 client, | |
284 path, | |
285 uid=self._jf.getProgressId(session, content_name), | |
286 size=size, | |
287 data_cb=lambda data: hasher.update(data), | |
288 ) | |
289 return False, True | |
290 | |
291 # common methods | |
292 | |
293 def _requestHandler(self, client, iq_elt, root_nodes_cb, files_from_node_cb): | |
294 iq_elt.handled = True | |
295 owner = jid.JID(iq_elt['from']).userhostJID() | |
296 node = iq_elt.query.getAttribute('node') | |
297 if not node: | |
298 d = defer.maybeDeferred(root_nodes_cb, client, iq_elt, owner) | |
299 else: | |
300 d = defer.maybeDeferred(files_from_node_cb, client, iq_elt, owner, node) | |
301 d.addErrback(lambda failure_: log.error(_(u"error while retrieving files: {msg}").format(msg=failure_))) | |
302 | |
303 def _iqError(self, client, iq_elt, condition="item-not-found"): | |
304 error_elt = jabber_error.StanzaError(condition).toResponse(iq_elt) | |
305 client.send(error_elt) | |
306 | |
307 # client | |
308 | |
309 def _addPathData(self, client, query_elt, path, parent_node): | |
310 """Fill query_elt with files/directories found in path""" | |
311 name = os.path.basename(path) | |
312 if os.path.isfile(path): | |
313 size = os.path.getsize(path) | |
314 mime_type = mimetypes.guess_type(path, strict=False)[0] | |
315 file_elt = self._jf.buildFileElement(name = name, | |
316 size = size, | |
317 mime_type = mime_type, | |
318 modified = os.path.getmtime(path)) | |
319 | |
320 query_elt.addChild(file_elt) | |
321 # we don't specify hash as it would be too resource intensive to calculate it for all files | |
322 # we add file to name_data, so users can request it later | |
323 name_data = client._XEP_0329_names_data.setdefault(name, {}) | |
324 if path not in name_data: | |
325 name_data[path] = {'size': size, | |
326 'mime_type': mime_type, | |
327 'parent': parent_node} | |
328 else: | |
329 # we have a directory | |
330 directory_elt = query_elt.addElement('directory') | |
331 directory_elt['name'] = name | |
332 | |
333 def _pathNodeHandler(self, client, iq_elt, query_elt, node, path): | |
334 """Fill query_elt for path nodes, i.e. physical directories""" | |
335 path = os.path.join(node.path, path) | |
336 | |
337 if not os.path.exists(path): | |
338 # path may have been moved since it has been shared | |
339 return self._iqError(client, iq_elt) | |
340 elif os.path.isfile(path): | |
341 self._addPathData(client, query_elt, path, node) | |
342 else: | |
343 for name in sorted(os.listdir(path.encode('utf-8')), key=lambda n: n.lower()): | |
344 try: | |
345 name = name.decode('utf-8', 'strict') | |
346 except UnicodeDecodeError as e: | |
347 log.warning(_(u"ignoring invalid unicode name ({name}): {msg}").format( | |
348 name = name.decode('utf-8', 'replace'), | |
349 msg = e)) | |
350 continue | |
351 full_path = os.path.join(path, name) | |
352 self._addPathData(client, query_elt, full_path, node) | |
353 | |
354 def _virtualNodeHandler(self, client, peer_jid, iq_elt, query_elt, node): | |
355 """Fill query_elt for virtual nodes""" | |
356 for name, child_node in node.iteritems(): | |
357 if not child_node.checkPermissions(client, peer_jid, check_parents=False): | |
358 continue | |
359 node_type = child_node.type | |
360 if node_type == TYPE_VIRTUAL: | |
361 directory_elt = query_elt.addElement('directory') | |
362 directory_elt['name'] = name | |
363 elif node_type == TYPE_PATH: | |
364 self._addPathData(client, query_elt, child_node.path, child_node) | |
365 else: | |
366 raise exceptions.InternalError(_(u'unexpected type: {type}').format(type=node_type)) | |
367 | |
368 def _getRootNodesCb(self, client, iq_elt, owner): | |
369 peer_jid = jid.JID(iq_elt['from']) | |
370 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | |
371 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | |
372 for name, node in client._XEP_0329_root_node.iteritems(): | |
373 if not node.checkPermissions(client, peer_jid, check_parents=False): | |
374 continue | |
375 directory_elt = query_elt.addElement('directory') | |
376 directory_elt['name'] = name | |
377 client.send(iq_result_elt) | |
378 | |
379 def _getFilesFromNodeCb(self, client, iq_elt, owner, node_path): | |
380 """Main method to retrieve files/directories from a node_path""" | |
381 peer_jid = jid.JID(iq_elt[u'from']) | |
382 try: | |
383 node, path = ShareNode.find(client, node_path, peer_jid) | |
384 except (exceptions.PermissionError, exceptions.NotFound): | |
385 return self._iqError(client, iq_elt) | |
386 except exceptions.DataError: | |
387 return self._iqError(client, iq_elt, condition='not-acceptable') | |
388 | |
389 node_type = node.type | |
390 peer_jid = jid.JID(iq_elt['from']) | |
391 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | |
392 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | |
393 query_elt[u'node'] = node_path | |
394 | |
395 # we now fill query_elt according to node_type | |
396 if node_type == TYPE_PATH: | |
397 # it's a physical path | |
398 self._pathNodeHandler(client, iq_elt, query_elt, node, path) | |
399 elif node_type == TYPE_VIRTUAL: | |
400 assert not path | |
401 self._virtualNodeHandler(client, peer_jid, iq_elt, query_elt, node) | |
402 else: | |
403 raise exceptions.InternalError(_(u'unknown node type: {type}').format(type=node_type)) | |
404 | |
405 client.send(iq_result_elt) | |
406 | |
407 def onRequest(self, iq_elt, client): | |
408 return self._requestHandler(client, iq_elt, self._getRootNodesCb, self._getFilesFromNodeCb) | |
409 | |
410 # Component | |
411 | |
412 @defer.inlineCallbacks | |
413 def _compGetRootNodesCb(self, client, iq_elt, owner): | |
414 peer_jid = jid.JID(iq_elt['from']) | |
415 files_data = yield self.host.memory.getFiles(client, peer_jid=peer_jid, parent=u'', | |
416 type_=C.FILE_TYPE_DIRECTORY, owner=owner) | |
417 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | |
418 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | |
419 for file_data in files_data: | |
420 name = file_data[u'name'] | |
421 directory_elt = query_elt.addElement(u'directory') | |
422 directory_elt[u'name'] = name | |
423 client.send(iq_result_elt) | |
424 | |
425 @defer.inlineCallbacks | |
426 def _compGetFilesFromNodeCb(self, client, iq_elt, owner, node_path): | |
427 """retrieve files from local files repository according to permissions | |
428 | |
429 result stanza is then built and sent to requestor | |
430 @trigger XEP-0329_compGetFilesFromNode(client, iq_elt, owner, node_path, files_data): can be used to add data/elements | |
431 """ | |
432 peer_jid = jid.JID(iq_elt['from']) | |
433 try: | |
434 files_data = yield self.host.memory.getFiles(client, peer_jid=peer_jid, path=node_path, owner=owner) | |
435 except exceptions.NotFound: | |
436 self._iqError(client, iq_elt) | |
437 return | |
438 iq_result_elt = xmlstream.toResponse(iq_elt, 'result') | |
439 query_elt = iq_result_elt.addElement((NS_FIS, 'query')) | |
440 query_elt[u'node'] = node_path | |
441 if not self.host.trigger.point(u'XEP-0329_compGetFilesFromNode', client, iq_elt, owner, node_path, files_data): | |
442 return | |
443 for file_data in files_data: | |
444 file_elt = self._jf.buildFileElementFromDict(file_data, | |
445 modified=file_data.get(u'modified', file_data[u'created'])) | |
446 query_elt.addChild(file_elt) | |
447 client.send(iq_result_elt) | |
448 | |
449 def onComponentRequest(self, iq_elt, client): | |
450 return self._requestHandler(client, iq_elt, self._compGetRootNodesCb, self._compGetFilesFromNodeCb) | |
451 | |
452 def _parseResult(self, iq_elt): | |
453 query_elt = next(iq_elt.elements(NS_FIS, 'query')) | |
454 files = [] | |
455 | |
456 for elt in query_elt.elements(): | |
457 if elt.name == 'file': | |
458 # we have a file | |
459 try: | |
460 file_data = self._jf.parseFileElement(elt) | |
461 except exceptions.DataError: | |
462 continue | |
463 file_data[u'type'] = C.FILE_TYPE_FILE | |
464 elif elt.name == 'directory' and elt.uri == NS_FIS: | |
465 # we have a directory | |
466 | |
467 file_data = {'name': elt['name'], 'type': C.FILE_TYPE_DIRECTORY} | |
468 else: | |
469 log.warning(_(u"unexpected element, ignoring: {elt}").format(elt=elt.toXml())) | |
470 continue | |
471 files.append(file_data) | |
472 return files | |
473 | |
474 # file methods # | |
475 | |
476 def _serializeData(self, files_data): | |
477 for file_data in files_data: | |
478 for key, value in file_data.iteritems(): | |
479 file_data[key] = json.dumps(value) if key in ('extra',) else unicode(value) | |
480 return files_data | |
481 | |
482 def _listFiles(self, target_jid, path, extra, profile): | |
483 client = self.host.getClient(profile) | |
484 target_jid = client.jid.userhostJID() if not target_jid else jid.JID(target_jid) | |
485 d = self.listFiles(client, target_jid, path or None) | |
486 d.addCallback(self._serializeData) | |
487 return d | |
488 | |
489 def listFiles(self, client, target_jid, path=None, extra=None): | |
490 """List file shared by an entity | |
491 | |
492 @param target_jid(jid.JID): jid of the sharing entity | |
493 @param path(unicode, None): path to the directory containing shared files | |
494 None to get root directories | |
495 @param extra(dict, None): extra data | |
496 @return list(dict): shared files | |
497 """ | |
498 iq_elt = client.IQ('get') | |
499 iq_elt['to'] = target_jid.full() | |
500 query_elt = iq_elt.addElement((NS_FIS, 'query')) | |
501 if path: | |
502 query_elt['node'] = path | |
503 d = iq_elt.send() | |
504 d.addCallback(self._parseResult) | |
505 return d | |
506 | |
507 def _sharePath(self, name, path, access, profile): | |
508 client = self.host.getClient(profile) | |
509 access= json.loads(access) | |
510 return self.sharePath(client, name or None, path, access) | |
511 | |
512 def sharePath(self, client, name, path, access): | |
513 if client.is_component: | |
514 raise exceptions.ClientTypeError | |
515 if not os.path.exists(path): | |
516 raise ValueError(_(u"This path doesn't exist!")) | |
517 if not path or not path.strip(u' /'): | |
518 raise ValueError(_(u"A path need to be specified")) | |
519 | |
520 node = client._XEP_0329_root_node | |
521 node_type = TYPE_PATH | |
522 if os.path.isfile(path): | |
523 # we have a single file, the workflow is diferrent as we store all single files in the same dir | |
524 node = node.getOrCreate(SINGLE_FILES_DIR) | |
525 | |
526 if not name: | |
527 name = os.path.basename(path.rstrip(u' /')) | |
528 if not name: | |
529 raise exceptions.InternalError(_(u"Can't find a proper name")) | |
530 | |
531 if not isinstance(access, dict): | |
532 raise ValueError(_(u'access must be a dict')) | |
533 | |
534 if name in node or name == SINGLE_FILES_DIR: | |
535 idx = 1 | |
536 new_name = name + '_' + unicode(idx) | |
537 while new_name in node: | |
538 idx += 1 | |
539 new_name = name + '_' + unicode(idx) | |
540 name = new_name | |
541 log.info(_(u"A directory with this name is already shared, renamed to {new_name} [{profile}]".format( | |
542 new_name=new_name, profile=client.profile))) | |
543 | |
544 ShareNode(name=name, parent=node, type_=node_type, access=access, path=path) | |
545 return name | |
546 | |
547 | |
548 class XEP_0329_handler(xmlstream.XMPPHandler): | |
549 implements(iwokkel.IDisco) | |
550 | |
551 def __init__(self, plugin_parent): | |
552 self.plugin_parent = plugin_parent | |
553 self.host = plugin_parent.host | |
554 | |
555 def connectionInitialized(self): | |
556 if self.parent.is_component: | |
557 self.xmlstream.addObserver(IQ_FIS_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent) | |
558 else: | |
559 self.xmlstream.addObserver(IQ_FIS_REQUEST, self.plugin_parent.onRequest, client=self.parent) | |
560 | |
561 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
562 return [disco.DiscoFeature(NS_FIS)] | |
563 | |
564 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
565 return [] |