commit | author | age
|
f13c4c
|
1 |
/*
|
JM |
2 |
* Copyright 2011 gitblit.com.
|
|
3 |
*
|
|
4 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5 |
* you may not use this file except in compliance with the License.
|
|
6 |
* You may obtain a copy of the License at
|
|
7 |
*
|
|
8 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9 |
*
|
|
10 |
* Unless required by applicable law or agreed to in writing, software
|
|
11 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13 |
* See the License for the specific language governing permissions and
|
|
14 |
* limitations under the License.
|
|
15 |
*/
|
7bf6e1
|
16 |
package com.gitblit.servlet;
|
9197d3
|
17 |
|
6d874a
|
18 |
import java.io.IOException;
|
JM |
19 |
import java.text.MessageFormat;
|
|
20 |
import java.text.ParseException;
|
9197d3
|
21 |
import java.util.Date;
|
JM |
22 |
|
cdb2fe
|
23 |
import com.google.inject.Inject;
|
JM |
24 |
import com.google.inject.Singleton;
|
6d874a
|
25 |
import javax.servlet.ServletException;
|
1b34b0
|
26 |
import javax.servlet.http.HttpServlet;
|
9197d3
|
27 |
import javax.servlet.http.HttpServletResponse;
|
JM |
28 |
|
|
29 |
import org.eclipse.jgit.lib.Repository;
|
|
30 |
import org.eclipse.jgit.revwalk.RevCommit;
|
|
31 |
import org.slf4j.Logger;
|
|
32 |
import org.slf4j.LoggerFactory;
|
|
33 |
|
7bf6e1
|
34 |
import com.gitblit.Constants;
|
JM |
35 |
import com.gitblit.IStoredSettings;
|
|
36 |
import com.gitblit.Keys;
|
db4f6b
|
37 |
import com.gitblit.manager.IRepositoryManager;
|
59b817
|
38 |
import com.gitblit.utils.CompressionUtils;
|
9197d3
|
39 |
import com.gitblit.utils.JGitUtils;
|
6d874a
|
40 |
import com.gitblit.utils.MarkdownUtils;
|
9197d3
|
41 |
import com.gitblit.utils.StringUtils;
|
JM |
42 |
|
892570
|
43 |
/**
|
JM |
44 |
* Streams out a zip file from the specified repository for any tree path at any
|
|
45 |
* revision.
|
699e71
|
46 |
*
|
892570
|
47 |
* @author James Moger
|
699e71
|
48 |
*
|
892570
|
49 |
*/
|
1b34b0
|
50 |
@Singleton
|
JM |
51 |
public class DownloadZipServlet extends HttpServlet {
|
9197d3
|
52 |
|
JM |
53 |
private static final long serialVersionUID = 1L;
|
|
54 |
|
2a7306
|
55 |
private transient Logger logger = LoggerFactory.getLogger(DownloadZipServlet.class);
|
cacf8b
|
56 |
|
65d5bb
|
57 |
private IStoredSettings settings;
|
cacf8b
|
58 |
|
65d5bb
|
59 |
private IRepositoryManager repositoryManager;
|
699e71
|
60 |
|
59b817
|
61 |
public static enum Format {
|
JM |
62 |
zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");
|
699e71
|
63 |
|
59b817
|
64 |
public final String extension;
|
699e71
|
65 |
|
59b817
|
66 |
Format(String ext) {
|
JM |
67 |
this.extension = ext;
|
|
68 |
}
|
699e71
|
69 |
|
59b817
|
70 |
public static Format fromName(String name) {
|
JM |
71 |
for (Format format : values()) {
|
|
72 |
if (format.name().equalsIgnoreCase(name)) {
|
|
73 |
return format;
|
|
74 |
}
|
|
75 |
}
|
|
76 |
return zip;
|
|
77 |
}
|
|
78 |
}
|
9197d3
|
79 |
|
1b34b0
|
80 |
@Inject
|
JM |
81 |
public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager) {
|
|
82 |
this.settings = settings;
|
|
83 |
this.repositoryManager = repositoryManager;
|
9197d3
|
84 |
}
|
JM |
85 |
|
892570
|
86 |
/**
|
JM |
87 |
* Returns an url to this servlet for the specified parameters.
|
699e71
|
88 |
*
|
892570
|
89 |
* @param baseURL
|
JM |
90 |
* @param repository
|
|
91 |
* @param objectId
|
|
92 |
* @param path
|
59b817
|
93 |
* @param format
|
892570
|
94 |
* @return an url
|
JM |
95 |
*/
|
59b817
|
96 |
public static String asLink(String baseURL, String repository, String objectId, String path, Format format) {
|
8c9a20
|
97 |
if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
|
c22722
|
98 |
baseURL = baseURL.substring(0, baseURL.length() - 1);
|
JM |
99 |
}
|
5450d0
|
100 |
return baseURL + Constants.ZIP_PATH + "?r=" + repository
|
2a7306
|
101 |
+ (path == null ? "" : ("&p=" + path))
|
59b817
|
102 |
+ (objectId == null ? "" : ("&h=" + objectId))
|
JM |
103 |
+ (format == null ? "" : ("&format=" + format.name()));
|
9197d3
|
104 |
}
|
JM |
105 |
|
892570
|
106 |
/**
|
78753b
|
107 |
* Creates a zip stream from the repository of the requested data.
|
699e71
|
108 |
*
|
892570
|
109 |
* @param request
|
JM |
110 |
* @param response
|
|
111 |
* @throws javax.servlet.ServletException
|
|
112 |
* @throws java.io.IOException
|
|
113 |
*/
|
2a7306
|
114 |
private void processRequest(javax.servlet.http.HttpServletRequest request,
|
JM |
115 |
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
|
|
116 |
java.io.IOException {
|
db4f6b
|
117 |
if (!settings.getBoolean(Keys.web.allowZipDownloads, true)) {
|
9197d3
|
118 |
logger.warn("Zip downloads are disabled");
|
JM |
119 |
response.sendError(HttpServletResponse.SC_FORBIDDEN);
|
|
120 |
return;
|
|
121 |
}
|
699e71
|
122 |
|
59b817
|
123 |
Format format = Format.zip;
|
9197d3
|
124 |
String repository = request.getParameter("r");
|
JM |
125 |
String basePath = request.getParameter("p");
|
|
126 |
String objectId = request.getParameter("h");
|
59b817
|
127 |
String f = request.getParameter("format");
|
JM |
128 |
if (!StringUtils.isEmpty(f)) {
|
|
129 |
format = Format.fromName(f);
|
|
130 |
}
|
699e71
|
131 |
|
9197d3
|
132 |
try {
|
JM |
133 |
String name = repository;
|
|
134 |
if (name.indexOf('/') > -1) {
|
|
135 |
name = name.substring(name.lastIndexOf('/') + 1);
|
|
136 |
}
|
59b817
|
137 |
name = StringUtils.stripDotGit(name);
|
9197d3
|
138 |
|
JM |
139 |
if (!StringUtils.isEmpty(basePath)) {
|
|
140 |
name += "-" + basePath.replace('/', '_');
|
|
141 |
}
|
|
142 |
if (!StringUtils.isEmpty(objectId)) {
|
|
143 |
name += "-" + objectId;
|
|
144 |
}
|
699e71
|
145 |
|
db4f6b
|
146 |
Repository r = repositoryManager.getRepository(repository);
|
6d874a
|
147 |
if (r == null) {
|
db4f6b
|
148 |
if (repositoryManager.isCollectingGarbage(repository)) {
|
e92c6d
|
149 |
error(response, MessageFormat.format("# Error\nGitblit is busy collecting garbage in {0}", repository));
|
JM |
150 |
return;
|
|
151 |
} else {
|
|
152 |
error(response, MessageFormat.format("# Error\nFailed to find repository {0}", repository));
|
|
153 |
return;
|
|
154 |
}
|
6d874a
|
155 |
}
|
9197d3
|
156 |
RevCommit commit = JGitUtils.getCommit(r, objectId);
|
6d874a
|
157 |
if (commit == null) {
|
JM |
158 |
error(response, MessageFormat.format("# Error\nFailed to find commit {0}", objectId));
|
|
159 |
r.close();
|
|
160 |
return;
|
|
161 |
}
|
9197d3
|
162 |
Date date = JGitUtils.getCommitDate(commit);
|
6d874a
|
163 |
|
9197d3
|
164 |
String contentType = "application/octet-stream";
|
JM |
165 |
response.setContentType(contentType + "; charset=" + response.getCharacterEncoding());
|
59b817
|
166 |
response.setHeader("Content-Disposition", "attachment; filename=\"" + name + format.extension + "\"");
|
9197d3
|
167 |
response.setDateHeader("Last-Modified", date.getTime());
|
JM |
168 |
response.setHeader("Cache-Control", "no-cache");
|
|
169 |
response.setHeader("Pragma", "no-cache");
|
|
170 |
response.setDateHeader("Expires", 0);
|
|
171 |
|
|
172 |
try {
|
59b817
|
173 |
switch (format) {
|
JM |
174 |
case zip:
|
|
175 |
CompressionUtils.zip(r, basePath, objectId, response.getOutputStream());
|
|
176 |
break;
|
|
177 |
case tar:
|
|
178 |
CompressionUtils.tar(r, basePath, objectId, response.getOutputStream());
|
|
179 |
break;
|
|
180 |
case gz:
|
|
181 |
CompressionUtils.gz(r, basePath, objectId, response.getOutputStream());
|
|
182 |
break;
|
|
183 |
case xz:
|
|
184 |
CompressionUtils.xz(r, basePath, objectId, response.getOutputStream());
|
|
185 |
break;
|
|
186 |
case bzip2:
|
|
187 |
CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());
|
|
188 |
break;
|
|
189 |
}
|
699e71
|
190 |
|
9197d3
|
191 |
response.flushBuffer();
|
52fc94
|
192 |
} catch (IOException t) {
|
9a23de
|
193 |
String message = t.getMessage() == null ? "" : t.getMessage().toLowerCase();
|
JM |
194 |
if (message.contains("reset") || message.contains("broken pipe")) {
|
|
195 |
logger.error("Client aborted zip download: " + message);
|
|
196 |
} else {
|
699e71
|
197 |
logger.error("Failed to write attachment to client", t);
|
9a23de
|
198 |
}
|
9197d3
|
199 |
} catch (Throwable t) {
|
JM |
200 |
logger.error("Failed to write attachment to client", t);
|
|
201 |
}
|
6d874a
|
202 |
|
JM |
203 |
// close the repository
|
|
204 |
r.close();
|
9197d3
|
205 |
} catch (Throwable t) {
|
JM |
206 |
logger.error("Failed to write attachment to client", t);
|
|
207 |
}
|
|
208 |
}
|
2a7306
|
209 |
|
6d874a
|
210 |
private void error(HttpServletResponse response, String mkd) throws ServletException,
|
JM |
211 |
IOException, ParseException {
|
|
212 |
String content = MarkdownUtils.transformMarkdown(mkd);
|
|
213 |
response.setContentType("text/html; charset=" + Constants.ENCODING);
|
|
214 |
response.getWriter().write(content);
|
|
215 |
}
|
|
216 |
|
2a7306
|
217 |
@Override
|
JM |
218 |
protected void doPost(javax.servlet.http.HttpServletRequest request,
|
|
219 |
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
|
|
220 |
java.io.IOException {
|
|
221 |
processRequest(request, response);
|
|
222 |
}
|
|
223 |
|
|
224 |
@Override
|
|
225 |
protected void doGet(javax.servlet.http.HttpServletRequest request,
|
|
226 |
javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
|
|
227 |
java.io.IOException {
|
|
228 |
processRequest(request, response);
|
|
229 |
}
|
9197d3
|
230 |
}
|