From 9197d340db81a245193dff1ecb44889b8e0dfe31 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 24 May 2011 17:39:38 -0400 Subject: Download zip feature. --- src/com/gitblit/Constants.java | 4 + src/com/gitblit/DownloadZipServlet.java | 100 ++++++++++++++++++++++++ src/com/gitblit/GitBlitServer.java | 7 +- src/com/gitblit/tests/JGitUtilsTest.java | 23 ++++++ src/com/gitblit/utils/JGitUtils.java | 52 +++++++++++- src/com/gitblit/wicket/GitBlitWebApp.properties | 3 +- src/com/gitblit/wicket/pages/CommitPage.html | 7 +- src/com/gitblit/wicket/pages/CommitPage.java | 6 ++ src/com/gitblit/wicket/pages/TreePage.html | 4 +- src/com/gitblit/wicket/pages/TreePage.java | 6 ++ src/com/gitblit/wicket/resources/gitblit.css | 8 +- 11 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 src/com/gitblit/DownloadZipServlet.java (limited to 'src/com') diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index 46f32084..b84ab7da 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -17,6 +17,10 @@ public class Constants { public final static String ADMIN_ROLE = "#admin"; public final static String PROPERTIES_FILE = "gitblit.properties"; + + public final static String GIT_SERVLET_PATH = "/git/"; + + public final static String ZIP_SERVLET_PATH = "/zip/"; public static enum AccessRestrictionType { NONE, PUSH, CLONE, VIEW; diff --git a/src/com/gitblit/DownloadZipServlet.java b/src/com/gitblit/DownloadZipServlet.java new file mode 100644 index 00000000..87fda907 --- /dev/null +++ b/src/com/gitblit/DownloadZipServlet.java @@ -0,0 +1,100 @@ +package com.gitblit; + +import java.util.Date; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.utils.JGitUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.models.RepositoryModel; + +public class DownloadZipServlet extends HttpServlet { + + public static String asLink(String baseURL, String repository, String objectId, String path) { + return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository + (path == null ? "" : ("&p=" + path)) + (objectId == null ? "" : ("&h=" + objectId)); + } + + private static final long serialVersionUID = 1L; + + private final static Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class); + + public DownloadZipServlet() { + super(); + } + + @Override + protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { + processRequest(request, response); + } + + @Override + protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { + processRequest(request, response); + } + + private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { + if (!GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)) { + logger.warn("Zip downloads are disabled"); + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + + } + String repository = request.getParameter("r"); + String basePath = request.getParameter("p"); + String objectId = request.getParameter("h"); + + try { + String name = repository; + if (name.indexOf('/') > -1) { + name = name.substring(name.lastIndexOf('/') + 1); + } + + // check roles first + boolean authorized = request.isUserInRole(Constants.ADMIN_ROLE); + authorized |= request.isUserInRole(repository); + + if (!authorized) { + RepositoryModel model = GitBlit.self().getRepositoryModel(repository); + if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) { + logger.warn("Unauthorized access via zip servlet for " + model.name); + response.sendError(HttpServletResponse.SC_FORBIDDEN); + return; + } + } + if (!StringUtils.isEmpty(basePath)) { + name += "-" + basePath.replace('/', '_'); + } + if (!StringUtils.isEmpty(objectId)) { + name += "-" + objectId; + } + + Repository r = GitBlit.self().getRepository(repository); + RevCommit commit = JGitUtils.getCommit(r, objectId); + Date date = JGitUtils.getCommitDate(commit); + String contentType = "application/octet-stream"; + response.setContentType(contentType + "; charset=" + response.getCharacterEncoding()); + // response.setContentLength(attachment.getFileSize()); + response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" + "\""); + response.setDateHeader("Last-Modified", date.getTime()); + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + + try { + JGitUtils.zip(r, basePath, objectId, response.getOutputStream()); + response.flushBuffer(); + } catch (Throwable t) { + logger.error("Failed to write attachment to client", t); + } + } catch (Throwable t) { + logger.error("Failed to write attachment to client", t); + } + } +} diff --git a/src/com/gitblit/GitBlitServer.java b/src/com/gitblit/GitBlitServer.java index e9e44637..a7b15385 100644 --- a/src/com/gitblit/GitBlitServer.java +++ b/src/com/gitblit/GitBlitServer.java @@ -211,10 +211,13 @@ public class GitBlitServer { wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec); wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/"); rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT); - + + // Zip Servlet + rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*"); + // Git Servlet ServletHolder gitServlet = null; - String gitServletPathSpec = "/git/*"; + String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*"; if (fileSettings.getBoolean(Keys.git.enableGitServlet, true)) { gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec); gitServlet.setInitParameter("base-path", params.repositoriesFolder); diff --git a/src/com/gitblit/tests/JGitUtilsTest.java b/src/com/gitblit/tests/JGitUtilsTest.java index 196058c5..11b77127 100644 --- a/src/com/gitblit/tests/JGitUtilsTest.java +++ b/src/com/gitblit/tests/JGitUtilsTest.java @@ -1,6 +1,7 @@ package com.gitblit.tests; import java.io.File; +import java.io.FileOutputStream; import java.util.Date; import java.util.List; @@ -103,5 +104,27 @@ public class JGitUtilsTest extends TestCase { r.close(); System.out.println(diff); } + + public void testZip() throws Exception { + Repository r = new FileRepository(new File(repositoriesFolder, "gitblit.git/" + Constants.DOT_GIT)); + FileOutputStream fos = null; + try { + File zipFile = new File("c:/output.zip"); + zipFile.delete(); + fos = new FileOutputStream(zipFile); + if (JGitUtils.zip(r, "src", Constants.HEAD, fos)) { + System.out.println("zip = " + zipFile.length() + " bytes"); + } else { + System.err.println("failed to generate zip file?!"); + } + } finally { + if (fos != null) { + try { + fos.close(); + } catch (Throwable t) { + } + } + } + } } diff --git a/src/com/gitblit/utils/JGitUtils.java b/src/com/gitblit/utils/JGitUtils.java index c9c13c56..b153c0c6 100644 --- a/src/com/gitblit/utils/JGitUtils.java +++ b/src/com/gitblit/utils/JGitUtils.java @@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.charset.Charset; import java.text.DateFormat; import java.text.ParseException; @@ -17,6 +18,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.diff.DiffEntry; @@ -808,10 +811,57 @@ public class JGitUtils { return null; } + public static boolean zip(Repository r, String basePath, String objectId, OutputStream os) throws Exception { + RevCommit commit = getCommit(r, objectId); + if (commit == null) { + return false; + } + final RevWalk rw = new RevWalk(r); + final TreeWalk walk = new TreeWalk(r); + try { + walk.addTree(commit.getTree()); + ZipOutputStream zos = new ZipOutputStream(os); + zos.setComment("Generated by Git:Blit"); + if (basePath != null && basePath.length() > 0) { + PathFilter f = PathFilter.create(basePath); + walk.setFilter(f); + } + walk.setRecursive(true); + while (walk.next()) { + ZipEntry entry = new ZipEntry(walk.getPathString()); + entry.setSize(walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB)); + entry.setComment(commit.getName()); + zos.putNextEntry(entry); + + ObjectId entid = walk.getObjectId(0); + FileMode entmode = walk.getFileMode(0); + RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType()); + rw.parseBody(blob); + + ObjectLoader ldr = r.open(blob.getId(), Constants.OBJ_BLOB); + byte[] tmp = new byte[4096]; + InputStream in = ldr.openStream(); + int n; + while ((n = in.read(tmp)) > 0) { + zos.write(tmp, 0, n); + } + in.close(); + } + zos.finish(); + return true; + } catch (IOException e) { + LOGGER.error("Failed to zip files from commit " + commit.getName(), e); + } finally { + walk.release(); + rw.dispose(); + } + return false; + } + public static List getDateMetrics(Repository r) { Metric total = new Metric("TOTAL"); final Map metricMap = new HashMap(); - + if (hasCommits(r)) { final List tags = getTags(r, -1); final Map tagMap = new HashMap(); diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index b6dbc117..0c4c350f 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -92,4 +92,5 @@ gb.showRemoteBranchesDescription = show remote branches gb.canAdminDescription = can administer Git:Blit server gb.permittedUsers = permitted users gb.isFrozen = is frozen -gb.isFrozenDescription = deny push operations \ No newline at end of file +gb.isFrozenDescription = deny push operations +gb.zip = zip \ No newline at end of file diff --git a/src/com/gitblit/wicket/pages/CommitPage.html b/src/com/gitblit/wicket/pages/CommitPage.html index 059d77e4..1b5fffed 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.html +++ b/src/com/gitblit/wicket/pages/CommitPage.html @@ -23,7 +23,12 @@ committer[committer] [commit date] commit[commit id] - tree[commit tree] + tree + [commit tree] + + | + + parent diff --git a/src/com/gitblit/wicket/pages/CommitPage.java b/src/com/gitblit/wicket/pages/CommitPage.java index 5396e821..c3c3fa96 100644 --- a/src/com/gitblit/wicket/pages/CommitPage.java +++ b/src/com/gitblit/wicket/pages/CommitPage.java @@ -6,6 +6,7 @@ import java.util.List; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; import org.apache.wicket.markup.repeater.data.ListDataProvider; @@ -13,6 +14,9 @@ import org.apache.wicket.model.StringResourceModel; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import com.gitblit.DownloadZipServlet; +import com.gitblit.GitBlit; +import com.gitblit.Keys; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.JGitUtils.SearchType; import com.gitblit.wicket.LinkPanel; @@ -62,6 +66,8 @@ public class CommitPage extends RepositoryPage { add(new Label("commitId", c.getName())); add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class, newCommitParameter())); + add(new BookmarkablePageLink("treeLink", TreePage.class, newCommitParameter())); + add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, null)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true))); // Parent Commits ListDataProvider parentsDp = new ListDataProvider(parents); diff --git a/src/com/gitblit/wicket/pages/TreePage.html b/src/com/gitblit/wicket/pages/TreePage.html index 35eac299..8d706ee2 100644 --- a/src/com/gitblit/wicket/pages/TreePage.html +++ b/src/com/gitblit/wicket/pages/TreePage.html @@ -9,7 +9,7 @@ @@ -32,7 +32,7 @@ - | + | | diff --git a/src/com/gitblit/wicket/pages/TreePage.java b/src/com/gitblit/wicket/pages/TreePage.java index ea5bd530..e385fab0 100644 --- a/src/com/gitblit/wicket/pages/TreePage.java +++ b/src/com/gitblit/wicket/pages/TreePage.java @@ -5,6 +5,7 @@ import java.util.List; import org.apache.wicket.PageParameters; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; +import org.apache.wicket.markup.html.link.ExternalLink; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.data.DataView; @@ -13,6 +14,9 @@ import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import com.gitblit.DownloadZipServlet; +import com.gitblit.GitBlit; +import com.gitblit.Keys; import com.gitblit.utils.ByteFormat; import com.gitblit.utils.JGitUtils; import com.gitblit.wicket.LinkPanel; @@ -36,6 +40,7 @@ public class TreePage extends RepositoryPage { // tree page links add(new BookmarkablePageLink("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path))); add(new BookmarkablePageLink("headLink", TreePage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path))); + add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true))); add(new CommitHeaderPanel("commitHeader", repositoryName, commit)); @@ -73,6 +78,7 @@ public class TreePage extends RepositoryPage { Fragment links = new Fragment("pathLinks", "treeLinks", this); links.add(new BookmarkablePageLink("tree", TreePage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path))); links.add(new BookmarkablePageLink("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path))); + links.add(new ExternalLink("zip", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, entry.path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true))); item.add(links); } else { // blob link diff --git a/src/com/gitblit/wicket/resources/gitblit.css b/src/com/gitblit/wicket/resources/gitblit.css index 4a971a6f..64484d27 100644 --- a/src/com/gitblit/wicket/resources/gitblit.css +++ b/src/com/gitblit/wicket/resources/gitblit.css @@ -114,15 +114,19 @@ span.empty { color: #008000; } +span.link { + color: #888; +} + span.link, span.link a { font-family: sans-serif; - font-size: 11px; + font-size: 11px; } span.link em, div.link span em { font-style: normal; font-family: sans-serif; - font-size: 11px; + font-size: 11px; } div.page_header { -- cgit v1.2.3