Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
1import os
2import re
3
4import gevent
5
6from Plugin import PluginManager
7from Config import config
8from Debug import Debug
9
10# Keep archive open for faster reponse times for large sites
11archive_cache = {}
12
13
14def closeArchive(archive_path):
15 if archive_path in archive_cache:
16 del archive_cache[archive_path]
17
18
19def openArchive(archive_path, file_obj=None):
20 if archive_path not in archive_cache:
21 if archive_path.endswith("tar.gz"):
22 import tarfile
23 archive_cache[archive_path] = tarfile.open(archive_path, fileobj=file_obj, mode="r:gz")
24 else:
25 import zipfile
26 archive_cache[archive_path] = zipfile.ZipFile(file_obj or archive_path)
27 gevent.spawn_later(5, lambda: closeArchive(archive_path)) # Close after 5 sec
28
29 archive = archive_cache[archive_path]
30 return archive
31
32
33def openArchiveFile(archive_path, path_within, file_obj=None):
34 archive = openArchive(archive_path, file_obj=file_obj)
35 if archive_path.endswith(".zip"):
36 return archive.open(path_within)
37 else:
38 return archive.extractfile(path_within)
39
40
41@PluginManager.registerTo("UiRequest")
42class UiRequestPlugin(object):
43 def actionSiteMedia(self, path, **kwargs):
44 if ".zip/" in path or ".tar.gz/" in path:
45 file_obj = None
46 path_parts = self.parsePath(path)
47 file_path = "%s/%s/%s" % (config.data_dir, path_parts["address"], path_parts["inner_path"])
48 match = re.match("^(.*\.(?:tar.gz|zip))/(.*)", file_path)
49 archive_path, path_within = match.groups()
50 if archive_path not in archive_cache:
51 site = self.server.site_manager.get(path_parts["address"])
52 if not site:
53 return self.actionSiteAddPrompt(path)
54 archive_inner_path = site.storage.getInnerPath(archive_path)
55 if not os.path.isfile(archive_path):
56 # Wait until file downloads
57 result = site.needFile(archive_inner_path, priority=10)
58 # Send virutal file path download finished event to remove loading screen
59 site.updateWebsocket(file_done=archive_inner_path)
60 if not result:
61 return self.error404(archive_inner_path)
62 file_obj = site.storage.openBigfile(archive_inner_path)
63 if file_obj == False:
64 file_obj = None
65
66 header_allow_ajax = False
67 if self.get.get("ajax_key"):
68 requester_site = self.server.site_manager.get(path_parts["request_address"])
69 if self.get["ajax_key"] == requester_site.settings["ajax_key"]:
70 header_allow_ajax = True
71 else:
72 return self.error403("Invalid ajax_key")
73
74 try:
75 file = openArchiveFile(archive_path, path_within, file_obj=file_obj)
76 content_type = self.getContentType(file_path)
77 self.sendHeader(200, content_type=content_type, noscript=kwargs.get("header_noscript", False), allow_ajax=header_allow_ajax)
78 return self.streamFile(file)
79 except Exception as err:
80 self.log.debug("Error opening archive file: %s" % Debug.formatException(err))
81 return self.error404(path)
82
83 return super(UiRequestPlugin, self).actionSiteMedia(path, **kwargs)
84
85 def streamFile(self, file):
86 for i in range(100): # Read max 6MB
87 try:
88 block = file.read(60 * 1024)
89 if block:
90 yield block
91 else:
92 raise StopIteration
93 except StopIteration:
94 file.close()
95 break
96
97
98@PluginManager.registerTo("SiteStorage")
99class SiteStoragePlugin(object):
100 def isFile(self, inner_path):
101 if ".zip/" in inner_path or ".tar.gz/" in inner_path:
102 match = re.match("^(.*\.(?:tar.gz|zip))/(.*)", inner_path)
103 archive_inner_path, path_within = match.groups()
104 return super(SiteStoragePlugin, self).isFile(archive_inner_path)
105 else:
106 return super(SiteStoragePlugin, self).isFile(inner_path)
107
108 def openArchive(self, inner_path):
109 archive_path = self.getPath(inner_path)
110 file_obj = None
111 if archive_path not in archive_cache:
112 if not os.path.isfile(archive_path):
113 result = self.site.needFile(inner_path, priority=10)
114 self.site.updateWebsocket(file_done=inner_path)
115 if not result:
116 raise Exception("Unable to download file")
117 file_obj = self.site.storage.openBigfile(inner_path)
118 if file_obj == False:
119 file_obj = None
120
121 try:
122 archive = openArchive(archive_path, file_obj=file_obj)
123 except Exception as err:
124 raise Exception("Unable to download file: %s" % Debug.formatException(err))
125
126 return archive
127
128 def walk(self, inner_path, *args, **kwags):
129 if ".zip" in inner_path or ".tar.gz" in inner_path:
130 match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
131 archive_inner_path, path_within = match.groups()
132 archive = self.openArchive(archive_inner_path)
133 path_within = path_within.lstrip("/")
134
135 if archive_inner_path.endswith(".zip"):
136 namelist = [name for name in archive.namelist() if not name.endswith("/")]
137 else:
138 namelist = [item.name for item in archive.getmembers() if not item.isdir()]
139
140 namelist_relative = []
141 for name in namelist:
142 if not name.startswith(path_within):
143 continue
144 name_relative = name.replace(path_within, "", 1).rstrip("/")
145 namelist_relative.append(name_relative)
146
147 return namelist_relative
148
149 else:
150 return super(SiteStoragePlugin, self).walk(inner_path, *args, **kwags)
151
152 def list(self, inner_path, *args, **kwags):
153 if ".zip" in inner_path or ".tar.gz" in inner_path:
154 match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
155 archive_inner_path, path_within = match.groups()
156 archive = self.openArchive(archive_inner_path)
157 path_within = path_within.lstrip("/")
158
159 if archive_inner_path.endswith(".zip"):
160 namelist = [name for name in archive.namelist()]
161 else:
162 namelist = [item.name for item in archive.getmembers()]
163
164 namelist_relative = []
165 for name in namelist:
166 if not name.startswith(path_within):
167 continue
168 name_relative = name.replace(path_within, "", 1).rstrip("/")
169
170 if "/" in name_relative: # File is in sub-directory
171 continue
172
173 namelist_relative.append(name_relative)
174 return namelist_relative
175
176 else:
177 return super(SiteStoragePlugin, self).list(inner_path, *args, **kwags)
178
179 def read(self, inner_path, mode="rb", **kwargs):
180 if ".zip/" in inner_path or ".tar.gz/" in inner_path:
181 match = re.match("^(.*\.(?:tar.gz|zip))(.*)", inner_path)
182 archive_inner_path, path_within = match.groups()
183 archive = self.openArchive(archive_inner_path)
184 path_within = path_within.lstrip("/")
185
186 if archive_inner_path.endswith(".zip"):
187 return archive.open(path_within).read()
188 else:
189 return archive.extractfile(path_within).read()
190
191 else:
192 return super(SiteStoragePlugin, self).read(inner_path, mode, **kwargs)
193