123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- import os
- import re
- import gevent
- from Plugin import PluginManager
- from Config import config
- from Debug import Debug
- # 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, file_obj=None):
- if archive_path not in archive_cache:
- if archive_path.endswith("tar.gz"):
- import tarfile
- archive_cache[archive_path] = tarfile.open(archive_path, fileobj=file_obj, mode="r:gz")
- else:
- import zipfile
- archive_cache[archive_path] = zipfile.ZipFile(file_obj or 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, file_obj=None):
- archive = openArchive(archive_path, file_obj=file_obj)
- if archive_path.endswith(".zip"):
- return archive.open(path_within)
- else:
- return archive.extractfile(path_within)
- @PluginManager.registerTo("UiRequest")
- class UiRequestPlugin(object):
- def actionSiteMedia(self, path, **kwargs):
- if ".zip/" in path or ".tar.gz/" in path:
- file_obj = None
- path_parts = self.parsePath(path)
- file_path = "%s/%s/%s" % (config.data_dir, path_parts["address"], path_parts["inner_path"])
- match = re.match("^(.*\.(?:tar.gz|zip))/(.*)", file_path)
- archive_path, path_within = match.groups()
- if archive_path not in archive_cache:
- site = self.server.site_manager.get(path_parts["address"])
- if not site:
- return self.actionSiteAddPrompt(path)
- archive_inner_path = site.storage.getInnerPath(archive_path)
- if not os.path.isfile(archive_path):
- # Wait until file downloads
- result = site.needFile(archive_inner_path, priority=10)
- # Send virutal file path download finished event to remove loading screen
- site.updateWebsocket(file_done=archive_inner_path)
- if not result:
- return self.error404(archive_inner_path)
- file_obj = site.storage.openBigfile(archive_inner_path)
- if file_obj == False:
- file_obj = None
- header_allow_ajax = False
- if self.get.get("ajax_key"):
- requester_site = self.server.site_manager.get(path_parts["request_address"])
- if self.get["ajax_key"] == requester_site.settings["ajax_key"]:
- header_allow_ajax = True
- else:
- return self.error403("Invalid ajax_key")
- try:
- file = openArchiveFile(archive_path, path_within, file_obj=file_obj)
- content_type = self.getContentType(file_path)
- self.sendHeader(200, content_type=content_type, noscript=kwargs.get("header_noscript", False), allow_ajax=header_allow_ajax)
- return self.streamFile(file)
- except Exception as err:
- self.log.debug("Error opening archive file: %s" % Debug.formatException(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|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 openArchive(self, inner_path):
- archive_path = self.getPath(inner_path)
- file_obj = None
- if archive_path not in archive_cache:
- if not os.path.isfile(archive_path):
- result = self.site.needFile(inner_path, priority=10)
- self.site.updateWebsocket(file_done=inner_path)
- if not result:
- raise Exception("Unable to download file")
- file_obj = self.site.storage.openBigfile(inner_path)
- if file_obj == False:
- file_obj = None
- try:
- archive = openArchive(archive_path, file_obj=file_obj)
- except Exception as err:
- raise Exception("Unable to download file: %s" % Debug.formatException(err))
- return archive
- def walk(self, inner_path, *args, **kwags):
- if ".zip" in inner_path or ".tar.gz" in inner_path:
- match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
- archive_inner_path, path_within = match.groups()
- archive = self.openArchive(archive_inner_path)
- path_within = path_within.lstrip("/")
- 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()]
- namelist_relative = []
- for name in namelist:
- if not name.startswith(path_within):
- continue
- name_relative = name.replace(path_within, "", 1).rstrip("/")
- namelist_relative.append(name_relative)
- return namelist_relative
- else:
- return super(SiteStoragePlugin, self).walk(inner_path, *args, **kwags)
- def list(self, inner_path, *args, **kwags):
- if ".zip" in inner_path or ".tar.gz" in inner_path:
- match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
- archive_inner_path, path_within = match.groups()
- archive = self.openArchive(archive_inner_path)
- path_within = path_within.lstrip("/")
- if archive_inner_path.endswith(".zip"):
- namelist = [name for name in archive.namelist()]
- else:
- namelist = [item.name for item in archive.getmembers()]
- namelist_relative = []
- for name in namelist:
- if not name.startswith(path_within):
- continue
- name_relative = name.replace(path_within, "", 1).rstrip("/")
- if "/" in name_relative: # File is in sub-directory
- continue
- namelist_relative.append(name_relative)
- return namelist_relative
- else:
- return super(SiteStoragePlugin, self).list(inner_path, *args, **kwags)
- def read(self, inner_path, mode="rb", **kwargs):
- if ".zip/" in inner_path or ".tar.gz/" in inner_path:
- match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
- archive_inner_path, path_within = match.groups()
- archive = self.openArchive(archive_inner_path)
- path_within = path_within.lstrip("/")
- if archive_inner_path.endswith(".zip"):
- return archive.open(path_within).read()
- else:
- return archive.extractfile(path_within).read()
- else:
- return super(SiteStoragePlugin, self).read(inner_path, mode, **kwargs)
|