# -*- coding: utf-8 -*- # imports import StringIO import json import stat import logging from cherrypy.lib import file_generator, cptools, httputil import cherrypy import rpyc from vispa import AjaxException from vispa.controller import AbstractController logger = logging.getLogger(__name__) class FSController(AbstractController): def __init__(self): AbstractController.__init__(self, mount_static=False) @staticmethod def _stream_remote_file(fs, path): offset = 0 buffer_size = 2 ** 20 while True: try: data = fs.get_file_content(path, offset, buffer_size) except: logger.exception("get content") raise l = len(data) if l <= 0: break else: offset += l yield data @cherrypy.expose @cherrypy.tools.ajax(on=False) @cherrypy.tools.method(accept="GET") def getfile(self, path, download=None, **kwargs): fs = self.get('fs') stats = fs.stat(path) if not stat.S_ISREG(stats.st_mode): raise cherrypy.HTTPError(404, "Not a File!") # Set the Last-Modified response header, so that # modified-since validation code can work. headers = cherrypy.response.headers headers[ 'Cache-Control'] = 'max-age=1, private, must-revalidate, no-cache' headers['Pragma'] = 'no-cache' headers['Last-Modified'] = httputil.HTTPDate(stats.st_mtime) headers['Expires'] = httputil.HTTPDate(stats.st_mtime + 1) headers['Content-Length'] = stats.st_size cptools.validate_since() if download and download.lower() in ['true', '1', 'yes']: disposition = 'attachment; filename=%s' % path.split('/')[-1] else: disposition = 'inline; filename=%s' % path.split('/')[-1] headers['Content-Disposition'] = disposition mimetype = fs.get_mime_type(path) if mimetype is not None: headers['Content-Type'] = mimetype else: headers['Content-Type'] = "application/octet-stream" self.release() return FSController._stream_remote_file(fs, path) getfile._cp_config = {'response.stream': True} @cherrypy.expose @cherrypy.tools.ajax(on=False) @cherrypy.tools.method(accept="GET") def thumbnail(self, path, width=100, height=100, **kwargs): self.release_session() fs = self.get('fs') self.release_database() contents = fs.thumbnail(path, int(width), int(height)) cherrypy.response.headers['Content-Type'] = "image/jpeg" contents = StringIO.StringIO(contents) return file_generator(contents) class FSAjaxController(AbstractController): @cherrypy.expose @cherrypy.tools.method(accept="GET") def exists(self, path, filetype=None): self.release_session() fs = self.get('fs') self.release_database() # type = type if type else type target_type = fs.exists(path, type=filetype) if target_type: return target_type else: return "Failed" @cherrypy.expose @cherrypy.tools.method(accept="GET") def filecount(self, path, watch_id=None): self.release_session() fs = self.get('fs') self.release_database() count = fs.get_file_count(path, window_id=self.get('window_id'), view_id=self.get('view_id'), watch_id=watch_id) if count == -1: raise AjaxException("%s does not exist" % path) # instead of raising an exception we return -2 as count to account for # the fact that the user does not have the permission to read the path # in the GUI elif count == -2: # raise AjaxException("You do not have rights to read %s." % path) elif not isinstance(count, int): raise AjaxException(count) else: return {"count": count} @cherrypy.expose @cherrypy.tools.ajax(encoded=True) @cherrypy.tools.method(accept="GET") def filelist(self, path, filefilter=None, reverse=False, watch_id=None): self.release_session() fs = self.get('fs') self.release_database() reverse = self.convert(reverse, bool) # get the files with the filter return fs.get_file_list(path, filter=filefilter, reverse=reverse, encode_json=True, window_id=self.get('window_id'), view_id=self.get('view_id'), watch_id=watch_id) @cherrypy.expose @cherrypy.tools.method(accept="POST") def createfolder(self, path, name): self.release_session() fs = self.get('fs') self.release_database() fs.create_folder(path, name) @cherrypy.expose @cherrypy.tools.method(accept="POST") def createfile(self, path, name): self.release_session() fs = self.get('fs') self.release_database() fs.create_file(path, name) @cherrypy.expose @cherrypy.tools.method(accept="POST") def rename(self, path, name, new_name): self.release_session() fs = self.get('fs') self.release_database() try: fs.rename(path, name, new_name) except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="POST") def remove(self, path): self.release_session() fs = self.get('fs') self.release_database() path = json.loads(path) # 'path' can be a unicode/string or list of unicodes/strings # so convert it with the convert function try: async_remove = rpyc.async(fs.remove) async_remove(path) return except: return @cherrypy.expose @cherrypy.tools.method(accept="POST") def move(self, source, destination): self.release_session() fs = self.get('fs') self.release_database() source = json.loads(source) destination = json.loads(destination) # 'source' and 'destination' can be a unicode/string or list of unicodes/strings # so convert it with the convert function try: fs.move(source, destination) except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="POST") def compress(self, path, paths, name): self.release_session() fs = self.get('fs') self.release_database() # 'paths' can be a unicode/string or list of unicodes/strings # so convert it with the convert function paths = json.loads(paths) try: fs.compress(path, paths, name) except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="POST") def decompress(self, file): self.release_session() fs = self.get('fs') self.release_database() try: fs.decompress(file) except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="POST") def paste(self, path, paths, cut): self.release_session() fs = self.get('fs') self.release_database() paths = json.loads(paths) # path = fs.expand(path.encode("utf-8")) # 'paths' can be a unicode/string or list of unicodes/strings # so convert it with the convert function try: fs.paste(path, paths, self.convert(cut, bool)) except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="POST") def upload(self, *args, **kwargs): self.release_session() fs = self.get('fs') self.release_database() # the html5 uploader provides following kwargs: # index, type, name, size, files[] # Since "files[]" ends with "[]"-brackets # we have to use kwargs instead of args # extract the path # prepare the parts parts = kwargs['files[]'] # force parts to be a list if not isinstance(parts, list): parts = [parts] # get the byterange if chunked chunked = False if 'Content-Range' in cherrypy.request.headers: chunked = True tmp = cherrypy.request.headers['Content-Range'] tmp = tmp.split()[1].split('/') maxbytes = int(tmp[1]) tmp = tmp[0].split('-') firstbyte = int(tmp[0]) lastbyte = int(tmp[1]) path = kwargs['path'] try: for part in parts: filename = fs.handle_file_name_collision(part.filename, path) if chunked: filename = '~' + filename if chunked and (firstbyte != 0): # check correct size of previous chunks for file in fs.get_file_list(path, encode_json=False)['filelist']: if file['name'] == filename and file['size'] != firstbyte: raise AjaxException("Chunked upload failed") append = True else: append = False while True: data = part.file.read(1024 ** 2) if len(data) <= 0: break success, msg = fs.save_file_content(filename, data, path=path, force=True, append=append) if not success: raise AjaxException(msg) if not append: append = True if chunked and (lastbyte + 1 == maxbytes): fs.rename(path, filename, filename.lstrip('~')) except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="GET") def isbrowserfile(self, path): self.release_session() fs = self.get('fs') self.release_database() try: # return fs.is_browser_file(str(path)) if fs.is_browser_file(path): return else: return "File can not be opened in browser." except Exception as e: raise AjaxException(str(e).split("\n")[0]) @cherrypy.expose @cherrypy.tools.method(accept="GET") def getsuggestions(self, path, length=10, append_hidden=True): self.release_session() fs = self.get('fs') self.release_database() try: length = length or 1 suggestions = fs.get_suggestions(path, length=int( length), append_hidden=self.convert(append_hidden, bool), encode_json=True) return self.success(suggestions=suggestions, encode_json=True) except Exception as e: return self.fail(msg=str(e), encode_json=True) @cherrypy.expose @cherrypy.tools.ajax(encoded=True) @cherrypy.tools.method(accept="POST") def savefile(self, path, content, watch_id=None, utf8=False): self.release_session() fs = self.get('fs') self.release_database() return fs.save_file(path, content, utf8=utf8, window_id=self.get('window_id'), view_id=self.get('view_id'), watch_id=watch_id) @cherrypy.expose @cherrypy.tools.ajax(encoded=True) @cherrypy.tools.method(accept="GET") def getfile(self, path, watch_id=None, utf8=False): self.release_session() fs = self.get('fs') self.release_database() return fs.get_file(path, utf8=utf8, window_id=self.get('window_id'), view_id=self.get('view_id'), watch_id=watch_id) @cherrypy.expose @cherrypy.tools.method(accept="POST") def watch(self, path, watch_id): self.release_session() fs = self.get('fs') self.release_database() err = fs.watch(path, window_id=self.get('window_id'), view_id=self.get('view_id'), watch_id=watch_id) if err: raise AjaxException(err) return {"success": not err} @cherrypy.expose @cherrypy.tools.method(accept="POST") def unwatch(self, watch_id=None): self.release_session() fs = self.get('fs') self.release_database() err = fs.unwatch(window_id=self.get('window_id'), view_id=self.get('view_id'), watch_id=watch_id) if err: raise AjaxException(err) return {"success": not err} @cherrypy.expose @cherrypy.tools.ajax(encoded=True) @cherrypy.tools.method(accept="GET") def getworkspaceini(self, request, fail_on_missing=False): self.release_session() fs = self.get('fs') self.release_database() fail_on_missing = self.convert(fail_on_missing, bool) return fs.get_workspaceini(request, fail_on_missing=fail_on_missing) @cherrypy.expose @cherrypy.tools.method(accept="POST") def setworkspaceini(self, request): self.release_session() fs = self.get('fs') self.release_database() err = fs.set_workspaceini(request) if err: raise AjaxException(err) return {"success": not err} @cherrypy.expose @cherrypy.tools.method(accept="GET") def expand(self, path): self.release_session() fs = self.get('fs') self.release_database() path = json.loads(path) # 'path' can be a unicode/string or list of unicodes/strings # so convert it with the convert function return fs.expand(path) @cherrypy.expose @cherrypy.tools.method(accept="GET") def checkpermissions(self, path): self.release_session() fs = self.get('fs') self.release_database() path = json.loads(path) path = fs.expand(path) return {"permission": fs.checkPermissions(path)}