import os import re import gevent from Plugin import PluginManager from Config import config # Keep archive open for faster reponse times for large sites archive_cache = {} def closeArchive(archive_path): if archive_path in archive_cache: del archive_cache[archive_path] def openArchive(archive_path): if archive_path not in archive_cache: if archive_path.endswith("tar.gz"): import tarfile archive_cache[archive_path] = tarfile.open(archive_path, "r:gz") elif archive_path.endswith("tar.bz2"): import tarfile archive_cache[archive_path] = tarfile.open(archive_path, "r:bz2") else: import zipfile archive_cache[archive_path] = zipfile.ZipFile(archive_path) gevent.spawn_later(5, lambda: closeArchive(archive_path)) # Close after 5 sec archive = archive_cache[archive_path] return archive def openArchiveFile(archive_path, path_within): archive = openArchive(archive_path) if archive_path.endswith(".zip"): return archive.open(path_within) else: return archive.extractfile(path_within.encode("utf8")) @PluginManager.registerTo("UiRequest") class UiRequestPlugin(object): def actionSiteMedia(self, path, **kwargs): if ".zip/" in path or ".tar.gz/" in path: path_parts = self.parsePath(path) file_path = u"%s/%s/%s" % (config.data_dir, path_parts["address"], path_parts["inner_path"].decode("utf8")) match = re.match("^(.*\.(?:tar.gz|tar.bz2|zip))/(.*)", file_path) archive_path, path_within = match.groups() if not os.path.isfile(archive_path): site = self.server.site_manager.get(path_parts["address"]) if not site: return self.actionSiteAddPrompt(path) # Wait until file downloads result = site.needFile(site.storage.getInnerPath(archive_path), priority=10) # Send virutal file path download finished event to remove loading screen site.updateWebsocket(file_done=site.storage.getInnerPath(file_path)) if not result: return self.error404(path) try: file = openArchiveFile(archive_path, path_within) content_type = self.getContentType(file_path) self.sendHeader(200, content_type=content_type, noscript=kwargs.get("header_noscript", False)) return self.streamFile(file) except Exception as err: self.log.debug("Error opening archive file: %s" % err) return self.error404(path) return super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs) def streamFile(self, file): for i in range(100): # Read max 6MB try: block = file.read(60 * 1024) if block: yield block else: raise StopIteration except StopIteration: file.close() break @PluginManager.registerTo("SiteStorage") class SiteStoragePlugin(object): def isFile(self, inner_path): if ".zip/" in inner_path or ".tar.gz/" in inner_path: match = re.match("^(.*\.(?:tar.gz|tar.bz2|zip))/(.*)", inner_path) archive_inner_path, path_within = match.groups() return super(SiteStoragePlugin, self).isFile(archive_inner_path) else: return super(SiteStoragePlugin, self).isFile(inner_path) def walk(self, inner_path, *args, **kwags): if ".zip" in inner_path or ".tar.gz" in inner_path: match = re.match("^(.*\.(?:tar.gz|tar.bz2|zip))(.*)", inner_path) archive_inner_path, path_within = match.groups() archive = openArchive(self.getPath(archive_inner_path)) if archive_inner_path.endswith(".zip"): namelist = [name for name in archive.namelist() if not name.endswith("/")] else: namelist = [item.name for item in archive.getmembers() if not item.isdir()] return namelist else: return super(SiteStoragePlugin, self).walk(inner_path, *args, **kwags)