Skip to content

Commit 252dc07

Browse files
committed
Merge pull request gitblit-org#988 from gitblit/976-raw-download-filestore-item
Fix for gitblit-org#976 - Filestore links via browser
2 parents 0400915 + 46f33f8 commit 252dc07

24 files changed

+468
-131
lines changed

src/main/java/com/gitblit/Constants.java

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ public class Constants {
9494
public static final int LEN_SHORTLOG = 78;
9595

9696
public static final int LEN_SHORTLOG_REFS = 60;
97+
98+
public static final int LEN_FILESTORE_META_MIN = 125;
99+
100+
public static final int LEN_FILESTORE_META_MAX = 146;
97101

98102
public static final String DEFAULT_BRANCH = "default";
99103

src/main/java/com/gitblit/models/FilestoreModel.java

+60-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
import java.util.Date;
2121
import java.util.List;
2222
import java.util.NoSuchElementException;
23+
import java.util.regex.Matcher;
24+
import java.util.regex.Pattern;
25+
26+
import com.gitblit.Constants;
2327

2428
/**
2529
* A FilestoreModel represents a file stored outside a repository but referenced by the repository using a unique objectID
@@ -31,6 +35,18 @@ public class FilestoreModel implements Serializable {
3135

3236
private static final long serialVersionUID = 1L;
3337

38+
private static final String metaRegexText = new StringBuilder()
39+
.append("version\\shttps://git-lfs.github.com/spec/v1\\s+")
40+
.append("oid\\ssha256:(" + Constants.REGEX_SHA256 + ")\\s+")
41+
.append("size\\s([0-9]+)")
42+
.toString();
43+
44+
private static final Pattern metaRegex = Pattern.compile(metaRegexText);
45+
46+
private static final int metaRegexIndexSHA = 1;
47+
48+
private static final int metaRegexIndexSize = 2;
49+
3450
public final String oid;
3551

3652
private Long size;
@@ -43,6 +59,12 @@ public class FilestoreModel implements Serializable {
4359
//Access Control
4460
private List<String> repositories;
4561

62+
public FilestoreModel(String id, long definedSize) {
63+
oid = id;
64+
size = definedSize;
65+
status = Status.ReferenceOnly;
66+
}
67+
4668
public FilestoreModel(String id, long expectedSize, UserModel user, String repo) {
4769
oid = id;
4870
size = expectedSize;
@@ -53,6 +75,29 @@ public FilestoreModel(String id, long expectedSize, UserModel user, String repo)
5375
repositories.add(repo);
5476
}
5577

78+
/*
79+
* Attempts to create a FilestoreModel from the given meta string
80+
*
81+
* @return A valid FilestoreModel if successful, otherwise null
82+
*/
83+
public static FilestoreModel fromMetaString(String meta) {
84+
85+
Matcher m = metaRegex.matcher(meta);
86+
87+
if (m.find()) {
88+
try
89+
{
90+
final Long size = Long.parseLong(m.group(metaRegexIndexSize));
91+
final String sha = m.group(metaRegexIndexSHA);
92+
return new FilestoreModel(sha, size);
93+
} catch (Exception e) {
94+
//Fail silent - it is not a valid filestore item
95+
}
96+
}
97+
98+
return null;
99+
}
100+
56101
public synchronized long getSize() {
57102
return size;
58103
}
@@ -102,26 +147,34 @@ public synchronized boolean isInErrorState() {
102147
}
103148

104149
public synchronized void addRepository(String repo) {
105-
if (!repositories.contains(repo)) {
106-
repositories.add(repo);
107-
}
150+
if (status != Status.ReferenceOnly) {
151+
if (!repositories.contains(repo)) {
152+
repositories.add(repo);
153+
}
154+
}
108155
}
109156

110157
public synchronized void removeRepository(String repo) {
111-
repositories.remove(repo);
158+
if (status != Status.ReferenceOnly) {
159+
repositories.remove(repo);
160+
}
112161
}
113162

114163
public synchronized boolean isInRepositoryList(List<String> repoList) {
115-
for (String name : repositories) {
116-
if (repoList.contains(name)) {
117-
return true;
164+
if (status != Status.ReferenceOnly) {
165+
for (String name : repositories) {
166+
if (repoList.contains(name)) {
167+
return true;
168+
}
118169
}
119170
}
120171
return false;
121172
}
122173

123174
public static enum Status {
124175

176+
ReferenceOnly(-42),
177+
125178
Deleted(-30),
126179
AuthenticationRequired(-20),
127180

src/main/java/com/gitblit/models/PathModel.java

+47-9
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,20 @@
1515
*/
1616
package com.gitblit.models;
1717

18+
import java.io.IOException;
1819
import java.io.Serializable;
1920

2021
import org.eclipse.jgit.diff.DiffEntry;
2122
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
23+
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
24+
import org.eclipse.jgit.errors.MissingObjectException;
25+
import org.eclipse.jgit.lib.Constants;
2226
import org.eclipse.jgit.lib.FileMode;
27+
import org.eclipse.jgit.lib.Repository;
28+
import org.eclipse.jgit.revwalk.RevWalk;
29+
30+
import com.gitblit.manager.FilestoreManager;
31+
import com.gitblit.utils.JGitUtils;
2332

2433
/**
2534
* PathModel is a serializable model class that represents a file or a folder,
@@ -34,16 +43,18 @@ public class PathModel implements Serializable, Comparable<PathModel> {
3443

3544
public final String name;
3645
public final String path;
46+
private final FilestoreModel filestoreItem;
3747
public final long size;
3848
public final int mode;
3949
public final String objectId;
4050
public final String commitId;
4151
public boolean isParentPath;
42-
43-
public PathModel(String name, String path, long size, int mode, String objectId, String commitId) {
52+
53+
public PathModel(String name, String path, FilestoreModel filestoreItem, long size, int mode, String objectId, String commitId) {
4454
this.name = name;
4555
this.path = path;
46-
this.size = size;
56+
this.filestoreItem = filestoreItem;
57+
this.size = (filestoreItem == null) ? size : filestoreItem.getSize();
4758
this.mode = mode;
4859
this.objectId = objectId;
4960
this.commitId = commitId;
@@ -66,6 +77,18 @@ public boolean isFile() {
6677
|| FileMode.EXECUTABLE_FILE.equals(mode)
6778
|| (FileMode.MISSING.equals(mode) && !isSymlink() && !isSubmodule() && !isTree());
6879
}
80+
81+
public boolean isFilestoreItem() {
82+
return filestoreItem != null;
83+
}
84+
85+
public String getFilestoreOid() {
86+
if (filestoreItem != null) {
87+
return filestoreItem.oid;
88+
}
89+
90+
return null;
91+
}
6992

7093
@Override
7194
public int hashCode() {
@@ -119,9 +142,9 @@ public static class PathChangeModel extends PathModel {
119142

120143
public int deletions;
121144

122-
public PathChangeModel(String name, String path, long size, int mode, String objectId,
145+
public PathChangeModel(String name, String path, FilestoreModel filestoreItem, long size, int mode, String objectId,
123146
String commitId, ChangeType type) {
124-
super(name, path, size, mode, objectId, commitId);
147+
super(name, path, filestoreItem, size, mode, objectId, commitId);
125148
this.changeType = type;
126149
}
127150

@@ -148,18 +171,33 @@ public boolean equals(Object o) {
148171
return super.equals(o);
149172
}
150173

151-
public static PathChangeModel from(DiffEntry diff, String commitId) {
174+
public static PathChangeModel from(DiffEntry diff, String commitId, Repository repository) {
152175
PathChangeModel pcm;
176+
FilestoreModel filestoreItem = null;
177+
long size = 0;
178+
179+
if (repository != null) {
180+
try (RevWalk revWalk = new RevWalk(repository)) {
181+
size = revWalk.getObjectReader().getObjectSize(diff.getNewId().toObjectId(), Constants.OBJ_BLOB);
182+
183+
if (JGitUtils.isPossibleFilestoreItem(size)) {
184+
filestoreItem = JGitUtils.getFilestoreItem(revWalk.getObjectReader().open(diff.getNewId().toObjectId()));
185+
}
186+
} catch (Exception e) {
187+
e.printStackTrace();
188+
}
189+
}
190+
153191
if (diff.getChangeType().equals(ChangeType.DELETE)) {
154-
pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), 0, diff
192+
pcm = new PathChangeModel(diff.getOldPath(), diff.getOldPath(), filestoreItem, size, diff
155193
.getNewMode().getBits(), diff.getOldId().name(), commitId, diff
156194
.getChangeType());
157195
} else if (diff.getChangeType().equals(ChangeType.RENAME)) {
158-
pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), 0, diff
196+
pcm = new PathChangeModel(diff.getOldPath(), diff.getNewPath(), filestoreItem, size, diff
159197
.getNewMode().getBits(), diff.getNewId().name(), commitId, diff
160198
.getChangeType());
161199
} else {
162-
pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), 0, diff
200+
pcm = new PathChangeModel(diff.getNewPath(), diff.getNewPath(), filestoreItem, size, diff
163201
.getNewMode().getBits(), diff.getNewId().name(), commitId, diff
164202
.getChangeType());
165203
}

src/main/java/com/gitblit/servlet/AccessRestrictionFilter.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,11 @@ protected RepositoryModel createRepository(UserModel user, String repository, St
133133
/**
134134
* Allows authentication header to be altered based on the action requested
135135
* Default is WWW-Authenticate
136+
* @param httpRequest
136137
* @param action
137138
* @return authentication type header
138139
*/
139-
protected String getAuthenticationHeader(String action) {
140+
protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) {
140141
return "WWW-Authenticate";
141142
}
142143

@@ -192,7 +193,7 @@ public void doFilter(final ServletRequest request, final ServletResponse respons
192193
logger.info(MessageFormat.format("ARF: CREATE CHALLENGE {0}", fullUrl));
193194
}
194195

195-
httpResponse.setHeader(getAuthenticationHeader(urlRequestType), CHALLENGE);
196+
httpResponse.setHeader(getAuthenticationHeader(httpRequest, urlRequestType), CHALLENGE);
196197
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
197198
return;
198199
} else {
@@ -239,7 +240,7 @@ public void doFilter(final ServletRequest request, final ServletResponse respons
239240
if (runtimeManager.isDebugMode()) {
240241
logger.info(MessageFormat.format("ARF: CHALLENGE {0}", fullUrl));
241242
}
242-
httpResponse.setHeader(getAuthenticationHeader(urlRequestType), CHALLENGE);
243+
httpResponse.setHeader(getAuthenticationHeader(httpRequest, urlRequestType), CHALLENGE);
243244
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
244245
return;
245246
} else {

src/main/java/com/gitblit/servlet/DownloadZipServlet.java

+12-6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.google.inject.Inject;
2424
import com.google.inject.Singleton;
25+
2526
import javax.servlet.ServletException;
2627
import javax.servlet.http.HttpServlet;
2728
import javax.servlet.http.HttpServletResponse;
@@ -34,6 +35,7 @@
3435
import com.gitblit.Constants;
3536
import com.gitblit.IStoredSettings;
3637
import com.gitblit.Keys;
38+
import com.gitblit.manager.IFilestoreManager;
3739
import com.gitblit.manager.IRepositoryManager;
3840
import com.gitblit.utils.CompressionUtils;
3941
import com.gitblit.utils.JGitUtils;
@@ -57,6 +59,8 @@ public class DownloadZipServlet extends HttpServlet {
5759
private IStoredSettings settings;
5860

5961
private IRepositoryManager repositoryManager;
62+
63+
private IFilestoreManager filestoreManager;
6064

6165
public static enum Format {
6266
zip(".zip"), tar(".tar"), gz(".tar.gz"), xz(".tar.xz"), bzip2(".tar.bzip2");
@@ -78,9 +82,10 @@ public static Format fromName(String name) {
7882
}
7983

8084
@Inject
81-
public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager) {
85+
public DownloadZipServlet(IStoredSettings settings, IRepositoryManager repositoryManager, IFilestoreManager filestoreManager) {
8286
this.settings = settings;
8387
this.repositoryManager = repositoryManager;
88+
this.filestoreManager = filestoreManager;
8489
}
8590

8691
/**
@@ -169,22 +174,23 @@ private void processRequest(javax.servlet.http.HttpServletRequest request,
169174
response.setHeader("Pragma", "no-cache");
170175
response.setDateHeader("Expires", 0);
171176

177+
172178
try {
173179
switch (format) {
174180
case zip:
175-
CompressionUtils.zip(r, basePath, objectId, response.getOutputStream());
181+
CompressionUtils.zip(r, filestoreManager, basePath, objectId, response.getOutputStream());
176182
break;
177183
case tar:
178-
CompressionUtils.tar(r, basePath, objectId, response.getOutputStream());
184+
CompressionUtils.tar(r, filestoreManager, basePath, objectId, response.getOutputStream());
179185
break;
180186
case gz:
181-
CompressionUtils.gz(r, basePath, objectId, response.getOutputStream());
187+
CompressionUtils.gz(r, filestoreManager, basePath, objectId, response.getOutputStream());
182188
break;
183189
case xz:
184-
CompressionUtils.xz(r, basePath, objectId, response.getOutputStream());
190+
CompressionUtils.xz(r, filestoreManager, basePath, objectId, response.getOutputStream());
185191
break;
186192
case bzip2:
187-
CompressionUtils.bzip2(r, basePath, objectId, response.getOutputStream());
193+
CompressionUtils.bzip2(r, filestoreManager, basePath, objectId, response.getOutputStream());
188194
break;
189195
}
190196

src/main/java/com/gitblit/servlet/FilestoreServlet.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,10 @@ protected void doGet(HttpServletRequest request,
238238
}
239239
} else {
240240
response.setStatus(responseObject.error.code);
241-
serialize(response, responseObject.error);
241+
242+
if (isMetaRequest) {
243+
serialize(response, responseObject.error);
244+
}
242245
}
243246
};
244247

src/main/java/com/gitblit/servlet/GitFilter.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,8 @@ protected String extractRepositoryName(String url) {
102102
}
103103

104104
/**
105-
* Analyze the url and returns the action of the request. Return values are
106-
* either "/git-receive-pack" or "/git-upload-pack".
105+
* Analyze the url and returns the action of the request. Return values are:
106+
* "/git-receive-pack", "/git-upload-pack" or "/info/lfs".
107107
*
108108
* @param serverUrl
109109
* @return action of the request
@@ -316,18 +316,22 @@ protected RepositoryModel createRepository(UserModel user, String repository, St
316316

317317
/**
318318
* Git lfs action uses an alternative authentication header,
319+
* dependent on the viewing method.
319320
*
321+
* @param httpRequest
320322
* @param action
321323
* @return
322324
*/
323325
@Override
324-
protected String getAuthenticationHeader(String action) {
326+
protected String getAuthenticationHeader(HttpServletRequest httpRequest, String action) {
325327

326328
if (action.equals(gitLfs)) {
327-
return "LFS-Authenticate";
329+
if (hasContentInRequestHeader(httpRequest, "Accept", FilestoreServlet.GIT_LFS_META_MIME)) {
330+
return "LFS-Authenticate";
331+
}
328332
}
329333

330-
return super.getAuthenticationHeader(action);
334+
return super.getAuthenticationHeader(httpRequest, action);
331335
}
332336

333337
/**

src/main/java/com/gitblit/servlet/RawServlet.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ private void processRequest(HttpServletRequest request, HttpServletResponse resp
364364
if (pathEntries.get(0).path.indexOf('/') > -1) {
365365
// we are in a subdirectory, add parent directory link
366366
String pp = URLEncoder.encode(requestedPath, Constants.ENCODING);
367-
pathEntries.add(0, new PathModel("..", pp + "/..", 0, FileMode.TREE.getBits(), null, null));
367+
pathEntries.add(0, new PathModel("..", pp + "/..", null, 0, FileMode.TREE.getBits(), null, null));
368368
}
369369
}
370370

0 commit comments

Comments
 (0)