BUGZILLA_PATTERN = r"(bug\s?|#)(\d+)"
CVE_PATTERN = r"(CVE)(\s|\-)(\d{4}\-\d{4})"
+FILE_EXTENSIONS_VIEWABLE = (
+ ".c",
+ ".cfg",
+ ".conf",
+ ".config",
+ ".h",
+ ".nm",
+ ".patch",
+ ".pl",
+ ".pm",
+ ".py",
+ ".S",
+ ".s",
+ ".sh",
+ ".txt",
+ "Kconfig",
+ "Makefile",
+)
+
N_ = lambda x: x
MSG_BUILD_FAILED_SUBJECT = N_("[%(build_name)s] Build job failed.")
@property
def filelist(self):
if self._filelist is None:
- self._filelist = \
- self.db.query("SELECT * FROM filelists WHERE pkg_id = %s ORDER BY name",
- self.id)
+ self._filelist = []
+
+ for f in self.db.query("SELECT * FROM filelists WHERE pkg_id = %s ORDER BY name", self.id):
+ f = File(self.pakfire, f)
+ self._filelist.append(f)
return self._filelist
def get_file(self):
- if os.path.exists(self.path):
- return pakfire.packages.open(self.path)
+ path = os.path.join(PACKAGES_DIR, self.path)
+
+ if os.path.exists(path):
+ return pakfire.packages.open(None, None, path)
## properties
return self.properties.get("critical_path", "N") == "Y"
+class File(base.Object):
+ def __init__(self, pakfire, data):
+ base.Object.__init__(self, pakfire)
+
+ self.data = data
+
+ def __getattr__(self, attr):
+ try:
+ return self.data[attr]
+ except KeyError:
+ raise AttributeError, attr
+
+ @property
+ def downloadable(self):
+ # All regular files are downloadable.
+ return self.type == 0
+
+ @property
+ def viewable(self):
+ for ext in FILE_EXTENSIONS_VIEWABLE:
+ if self.name.endswith(ext):
+ return True
+
+ return False
+
+
# XXX DEAD CODE
-class File(base.Object):
+class __File(base.Object):
def __init__(self, pakfire, path):
base.Object.__init__(self, pakfire)
{{ file.name }}
</td>
<td>
- <div class="btn-toolbar">
- <div class="btn-group">
- <a class="btn btn-mini" href="#">V</a>
- <a class="btn btn-mini" href="#">D</a>
- </div>
+ <div class="btn-group">
+ {% if file.viewable %}
+ <a class="btn btn-mini" href="#">
+ <i class="icon-file"></i>
+ </a>
+ {% end %}
+ {% if file.downloadable %}
+ <a class="btn btn-mini" href="/package/{{ pkg.uuid }}/download{{ file.name }}">
+ <i class="icon-download"></i>
+ </a>
+ {% end %}
</div>
</td>
</tr>
(r"/package/([\w\-\+]+)/properties", PackagePropertiesHandler),
(r"/package/([\w\-\+]+)", PackageNameHandler),
(r"/package/([\w\-\+]+)/changelog", PackageChangelogHandler),
+ (r"/package/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})/download(.*)", PackageFileDownloadHandler),
# Files
(r"/file/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})", FileDetailHandler),
#!/usr/bin/python
+import mimetypes
+import os.path
import tornado.web
from handlers_base import BaseHandler
+from backend.constants import BUFFER_SIZE
+
class PackageIDDetailHandler(BaseHandler):
def get(self, id):
package = self.packages.get_by_id(id)
else:
critical_path = False
build.pkg.update_property("critical_path", critical_path)
+
+
+class PackageFileDownloadHandler(BaseHandler):
+ def get(self, pkg_uuid, filename):
+ # Fetch package.
+ pkg = self.pakfire.packages.get_by_uuid(pkg_uuid)
+ if not pkg:
+ raise tornado.web.HTTPError(404, "Package not found: %s" % pkg_uuid)
+
+ # Check if the package has got a file with the given name.
+ if not filename in [f.name for f in pkg.filelist]:
+ raise tornado.web.HTTPError(404, "Package %s does not contain file %s" % (pkg, filename))
+
+ # Open the package in the filesystem.
+ pkg_file = pkg.get_file()
+ if not pkg_file:
+ raise torando.web.HTTPError(404, "Could not open package %s" % pkg.path)
+
+ # Open the file to transfer it to the client.
+ f = pkg_file.open_file(filename)
+ if not f:
+ raise tornado.web.HTTPError(404, "Package %s does not contain file %s" % (pkg_file, filename))
+
+ # Send the filename in header.
+ self.set_header("Content-Disposition", "attachment; filename=%s" % os.path.basename(filename))
+
+ # Guess the MIME type of the file.
+ (type, encoding) = mimetypes.guess_type(filename)
+ if not type:
+ type = "text/plain"
+ self.set_header("Content-Type", type)
+
+ # Transfer the content chunk by chunk.
+ while True:
+ buf = f.read(BUFFER_SIZE)
+ if not buf:
+ break
+
+ self.write(buf)
+
+ f.close()
+
+ # Done.
+ self.finish()