# If web.authenticate=false, any user can execute the aforementioned functions. \r
web.allowAdministration = true\r
\r
+# Allow dyanamic zip downloads. \r
+web.allowZipDownloads = true\r
+\r
# This is the message display above the repositories table.\r
# This can point to a file with Markdown content.\r
# Specifying "gitblit" uses the internal welcome message.\r
public final static String ADMIN_ROLE = "#admin";\r
\r
public final static String PROPERTIES_FILE = "gitblit.properties";\r
+ \r
+ public final static String GIT_SERVLET_PATH = "/git/";\r
+ \r
+ public final static String ZIP_SERVLET_PATH = "/zip/";\r
\r
public static enum AccessRestrictionType {\r
NONE, PUSH, CLONE, VIEW;\r
--- /dev/null
+package com.gitblit;\r
+\r
+import java.util.Date;\r
+\r
+import javax.servlet.http.HttpServlet;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.eclipse.jgit.lib.Repository;\r
+import org.eclipse.jgit.revwalk.RevCommit;\r
+import org.slf4j.Logger;\r
+import org.slf4j.LoggerFactory;\r
+\r
+import com.gitblit.Constants.AccessRestrictionType;\r
+import com.gitblit.utils.JGitUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.wicket.models.RepositoryModel;\r
+\r
+public class DownloadZipServlet extends HttpServlet {\r
+\r
+ public static String asLink(String baseURL, String repository, String objectId, String path) {\r
+ return baseURL + (baseURL.endsWith("/") ? "" : "/") + "zip?r=" + repository + (path == null ? "" : ("&p=" + path)) + (objectId == null ? "" : ("&h=" + objectId));\r
+ }\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private final static Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);\r
+\r
+ public DownloadZipServlet() {\r
+ super();\r
+ }\r
+\r
+ @Override\r
+ protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ @Override\r
+ protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {\r
+ processRequest(request, response);\r
+ }\r
+\r
+ private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException {\r
+ if (!GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)) {\r
+ logger.warn("Zip downloads are disabled");\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+\r
+ }\r
+ String repository = request.getParameter("r");\r
+ String basePath = request.getParameter("p");\r
+ String objectId = request.getParameter("h");\r
+\r
+ try {\r
+ String name = repository;\r
+ if (name.indexOf('/') > -1) {\r
+ name = name.substring(name.lastIndexOf('/') + 1);\r
+ }\r
+\r
+ // check roles first\r
+ boolean authorized = request.isUserInRole(Constants.ADMIN_ROLE);\r
+ authorized |= request.isUserInRole(repository);\r
+\r
+ if (!authorized) {\r
+ RepositoryModel model = GitBlit.self().getRepositoryModel(repository);\r
+ if (model.accessRestriction.atLeast(AccessRestrictionType.VIEW)) {\r
+ logger.warn("Unauthorized access via zip servlet for " + model.name);\r
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);\r
+ return;\r
+ }\r
+ }\r
+ if (!StringUtils.isEmpty(basePath)) {\r
+ name += "-" + basePath.replace('/', '_');\r
+ }\r
+ if (!StringUtils.isEmpty(objectId)) {\r
+ name += "-" + objectId;\r
+ }\r
+\r
+ Repository r = GitBlit.self().getRepository(repository);\r
+ RevCommit commit = JGitUtils.getCommit(r, objectId);\r
+ Date date = JGitUtils.getCommitDate(commit);\r
+ String contentType = "application/octet-stream";\r
+ response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());\r
+ // response.setContentLength(attachment.getFileSize());\r
+ response.setHeader("Content-Disposition", "attachment; filename=\"" + name + ".zip" + "\"");\r
+ response.setDateHeader("Last-Modified", date.getTime());\r
+ response.setHeader("Cache-Control", "no-cache");\r
+ response.setHeader("Pragma", "no-cache");\r
+ response.setDateHeader("Expires", 0);\r
+\r
+ try {\r
+ JGitUtils.zip(r, basePath, objectId, response.getOutputStream());\r
+ response.flushBuffer();\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write attachment to client", t);\r
+ }\r
+ } catch (Throwable t) {\r
+ logger.error("Failed to write attachment to client", t);\r
+ }\r
+ }\r
+}\r
wicketFilter.setInitParameter(WicketFilter.FILTER_MAPPING_PARAM, wicketPathSpec);\r
wicketFilter.setInitParameter(WicketFilter.IGNORE_PATHS_PARAM, "git/");\r
rootContext.addFilter(wicketFilter, wicketPathSpec, FilterMapping.DEFAULT);\r
-\r
+ \r
+ // Zip Servlet\r
+ rootContext.addServlet(DownloadZipServlet.class, Constants.ZIP_SERVLET_PATH + "*");\r
+ \r
// Git Servlet\r
ServletHolder gitServlet = null;\r
- String gitServletPathSpec = "/git/*";\r
+ String gitServletPathSpec = Constants.GIT_SERVLET_PATH + "*";\r
if (fileSettings.getBoolean(Keys.git.enableGitServlet, true)) {\r
gitServlet = rootContext.addServlet(GitBlitServlet.class, gitServletPathSpec);\r
gitServlet.setInitParameter("base-path", params.repositoriesFolder);\r
package com.gitblit.tests;\r
\r
import java.io.File;\r
+import java.io.FileOutputStream;\r
import java.util.Date;\r
import java.util.List;\r
\r
r.close();\r
System.out.println(diff);\r
}\r
+ \r
+ public void testZip() throws Exception {\r
+ Repository r = new FileRepository(new File(repositoriesFolder, "gitblit.git/" + Constants.DOT_GIT));\r
+ FileOutputStream fos = null;\r
+ try {\r
+ File zipFile = new File("c:/output.zip");\r
+ zipFile.delete();\r
+ fos = new FileOutputStream(zipFile);\r
+ if (JGitUtils.zip(r, "src", Constants.HEAD, fos)) {\r
+ System.out.println("zip = " + zipFile.length() + " bytes");\r
+ } else {\r
+ System.err.println("failed to generate zip file?!");\r
+ }\r
+ } finally {\r
+ if (fos != null) {\r
+ try {\r
+ fos.close();\r
+ } catch (Throwable t) {\r
+ }\r
+ }\r
+ }\r
+ }\r
\r
}\r
import java.io.File;\r
import java.io.IOException;\r
import java.io.InputStream;\r
+import java.io.OutputStream;\r
import java.nio.charset.Charset;\r
import java.text.DateFormat;\r
import java.text.ParseException;\r
import java.util.Map;\r
import java.util.Set;\r
import java.util.concurrent.atomic.AtomicInteger;\r
+import java.util.zip.ZipEntry;\r
+import java.util.zip.ZipOutputStream;\r
\r
import org.eclipse.jgit.api.Git;\r
import org.eclipse.jgit.diff.DiffEntry;\r
return null;\r
}\r
\r
+ public static boolean zip(Repository r, String basePath, String objectId, OutputStream os) throws Exception {\r
+ RevCommit commit = getCommit(r, objectId);\r
+ if (commit == null) {\r
+ return false;\r
+ }\r
+ final RevWalk rw = new RevWalk(r);\r
+ final TreeWalk walk = new TreeWalk(r);\r
+ try {\r
+ walk.addTree(commit.getTree());\r
+ ZipOutputStream zos = new ZipOutputStream(os);\r
+ zos.setComment("Generated by Git:Blit");\r
+ if (basePath != null && basePath.length() > 0) {\r
+ PathFilter f = PathFilter.create(basePath);\r
+ walk.setFilter(f);\r
+ }\r
+ walk.setRecursive(true);\r
+ while (walk.next()) {\r
+ ZipEntry entry = new ZipEntry(walk.getPathString());\r
+ entry.setSize(walk.getObjectReader().getObjectSize(walk.getObjectId(0), Constants.OBJ_BLOB));\r
+ entry.setComment(commit.getName());\r
+ zos.putNextEntry(entry);\r
+\r
+ ObjectId entid = walk.getObjectId(0);\r
+ FileMode entmode = walk.getFileMode(0);\r
+ RevBlob blob = (RevBlob) rw.lookupAny(entid, entmode.getObjectType());\r
+ rw.parseBody(blob);\r
+\r
+ ObjectLoader ldr = r.open(blob.getId(), Constants.OBJ_BLOB);\r
+ byte[] tmp = new byte[4096];\r
+ InputStream in = ldr.openStream();\r
+ int n;\r
+ while ((n = in.read(tmp)) > 0) {\r
+ zos.write(tmp, 0, n);\r
+ }\r
+ in.close();\r
+ }\r
+ zos.finish();\r
+ return true;\r
+ } catch (IOException e) {\r
+ LOGGER.error("Failed to zip files from commit " + commit.getName(), e);\r
+ } finally {\r
+ walk.release();\r
+ rw.dispose();\r
+ }\r
+ return false;\r
+ }\r
+\r
public static List<Metric> getDateMetrics(Repository r) {\r
Metric total = new Metric("TOTAL");\r
final Map<String, Metric> metricMap = new HashMap<String, Metric>();\r
- \r
+\r
if (hasCommits(r)) {\r
final List<RefModel> tags = getTags(r, -1);\r
final Map<ObjectId, RefModel> tagMap = new HashMap<ObjectId, RefModel>();\r
gb.canAdminDescription = can administer Git:Blit server\r
gb.permittedUsers = permitted users\r
gb.isFrozen = is frozen\r
-gb.isFrozenDescription = deny push operations
\ No newline at end of file
+gb.isFrozenDescription = deny push operations\r
+gb.zip = zip
\ No newline at end of file
<tr><th><wicket:message key="gb.committer">committer</wicket:message></th><td><span class="sha1" wicket:id="commitCommitter">[committer]</span></td></tr>\r
<tr><th></th><td><span class="sha1" wicket:id="commitCommitterDate">[commit date]</span></td></tr>\r
<tr><th><wicket:message key="gb.commit">commit</wicket:message></th><td><span class="sha1" wicket:id="commitId">[commit id]</span></td></tr>\r
- <tr><th><wicket:message key="gb.tree">tree</wicket:message></th><td><span class="sha1" wicket:id="commitTree">[commit tree]</span></td></tr>\r
+ <tr><th><wicket:message key="gb.tree">tree</wicket:message></th>\r
+ <td><span class="sha1" wicket:id="commitTree">[commit tree]</span>\r
+ <span class="link">\r
+ <a wicket:id="treeLink"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>\r
+ </span>\r
+ </td></tr>\r
<tr><th valign="top"><wicket:message key="gb.parent">parent</wicket:message></th>\r
<td>\r
<span wicket:id="commitParents">\r
import org.apache.wicket.PageParameters;\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
import org.apache.wicket.markup.repeater.Item;\r
import org.apache.wicket.markup.repeater.data.DataView;\r
import org.apache.wicket.markup.repeater.data.ListDataProvider;\r
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
\r
+import com.gitblit.DownloadZipServlet;\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.JGitUtils.SearchType;\r
import com.gitblit.wicket.LinkPanel;\r
add(new Label("commitId", c.getName()));\r
\r
add(new LinkPanel("commitTree", "list", c.getTree().getName(), TreePage.class, newCommitParameter()));\r
+ add(new BookmarkablePageLink<Void>("treeLink", TreePage.class, newCommitParameter()));\r
+ add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, null)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));\r
\r
// Parent Commits\r
ListDataProvider<String> parentsDp = new ListDataProvider<String>(parents);\r
\r
<!-- blob nav links --> \r
<div class="page_nav2">\r
- <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a>\r
+ <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="headLink"><wicket:message key="gb.head"></wicket:message></a> | <a wicket:id="zipLink"><wicket:message key="gb.zip"></wicket:message></a>\r
</div> \r
\r
<!-- commit header -->\r
<!-- tree links -->\r
<wicket:fragment wicket:id="treeLinks">\r
<span class="link">\r
- <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>\r
+ <a wicket:id="tree"><wicket:message key="gb.tree"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="zip"><wicket:message key="gb.zip"></wicket:message></a>\r
</span>\r
</wicket:fragment>\r
\r
import org.apache.wicket.PageParameters;\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.link.BookmarkablePageLink;\r
+import org.apache.wicket.markup.html.link.ExternalLink;\r
import org.apache.wicket.markup.html.panel.Fragment;\r
import org.apache.wicket.markup.repeater.Item;\r
import org.apache.wicket.markup.repeater.data.DataView;\r
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
\r
+import com.gitblit.DownloadZipServlet;\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
import com.gitblit.utils.ByteFormat;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.wicket.LinkPanel;\r
// tree page links\r
add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, objectId, path)));\r
add(new BookmarkablePageLink<Void>("headLink", TreePage.class, WicketUtils.newPathParameter(repositoryName, Constants.HEAD, path)));\r
+ add(new ExternalLink("zipLink", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));\r
\r
add(new CommitHeaderPanel("commitHeader", repositoryName, commit));\r
\r
Fragment links = new Fragment("pathLinks", "treeLinks", this);\r
links.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));\r
links.add(new BookmarkablePageLink<Void>("history", HistoryPage.class, WicketUtils.newPathParameter(repositoryName, entry.commitId, entry.path)));\r
+ links.add(new ExternalLink("zip", DownloadZipServlet.asLink(getRequest().getRelativePathPrefixToContextRoot(), repositoryName, objectId, entry.path)).setVisible(GitBlit.self().settings().getBoolean(Keys.web.allowZipDownloads, true)));\r
item.add(links);\r
} else {\r
// blob link\r
color: #008000;\r
}\r
\r
+span.link {\r
+ color: #888;\r
+}\r
+\r
span.link, span.link a {\r
font-family: sans-serif;\r
- font-size: 11px;\r
+ font-size: 11px; \r
}\r
\r
span.link em, div.link span em {\r
font-style: normal;\r
font-family: sans-serif;\r
- font-size: 11px;\r
+ font-size: 11px; \r
}\r
\r
div.page_header {\r