193 lines
7.6 KiB
Python
193 lines
7.6 KiB
Python
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 = config.data_dir / path_parts["address"] / path_parts["inner_path"]
|
|
match = re.match(r"^(.*\.(?: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(r"^(.*\.(?: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(r"^(.*\.(?: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(r"^(.*\.(?: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(r"^(.*\.(?: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)
|
|
|