Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.google.gerrit.server.change.FileContentUtil Maven / Gradle / Ivy
package com.google.gerrit.server.change;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import com.google.common.base.Strings;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.PatchScript.FileMode;
import com.google.gerrit.entities.Patch;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.BinaryResult;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.mime.FileTypeRegistry;
import com.google.gerrit.server.project.ProjectState;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import eu.medsea.mimeutil.MimeType;
import java.io.IOException;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.NB;
@Singleton
public class FileContentUtil {
public static final String TEXT_X_GERRIT_COMMIT_MESSAGE = "text/x-gerrit-commit-message" ;
public static final String TEXT_X_GERRIT_MERGE_LIST = "text/x-gerrit-merge-list" ;
private static final String X_GIT_SYMLINK = "x-git/symlink" ;
private static final String X_GIT_GITLINK = "x-git/gitlink" ;
private static final int MAX_SIZE = 5 << 20 ;
private static final String ZIP_TYPE = "application/zip" ;
private static final SecureRandom rng = new SecureRandom();
private final GitRepositoryManager repoManager;
private final FileTypeRegistry registry;
@Inject
FileContentUtil(GitRepositoryManager repoManager, FileTypeRegistry ftr) {
this .repoManager = repoManager;
this .registry = ftr;
}
public BinaryResult getContent (
ProjectState project, ObjectId revstr, String path, @Nullable Integer parent)
throws BadRequestException, ResourceNotFoundException, IOException {
try (Repository repo = openRepository(project);
RevWalk rw = new RevWalk(repo)) {
if (parent != null ) {
RevCommit revCommit = rw.parseCommit(revstr);
if (revCommit == null ) {
throw new ResourceNotFoundException("commit not found" );
}
if (parent > revCommit.getParentCount()) {
throw new BadRequestException("invalid parent" );
}
revstr = rw.parseCommit(revstr).getParent(Integer.max(0 , parent - 1 )).toObjectId();
}
return getContent(repo, project, revstr, path);
}
}
public BinaryResult getContent (
Repository repo, ProjectState project, ObjectId revstr, String path)
throws IOException, ResourceNotFoundException, BadRequestException {
try (RevWalk rw = new RevWalk(repo)) {
RevCommit commit = rw.parseCommit(revstr);
try (TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), path, commit.getTree())) {
if (tw == null ) {
throw new ResourceNotFoundException();
}
org.eclipse.jgit.lib.FileMode mode = tw.getFileMode(0 );
ObjectId id = tw.getObjectId(0 );
if (mode == org.eclipse.jgit.lib.FileMode.GITLINK) {
return BinaryResult.create(id.name()).setContentType(X_GIT_GITLINK).base64();
}
if (mode == org.eclipse.jgit.lib.FileMode.TREE) {
throw new BadRequestException("cannot retrieve content of directories" );
}
ObjectLoader obj = repo.open(id, OBJ_BLOB);
byte [] raw;
try {
raw = obj.getCachedBytes(MAX_SIZE);
} catch (LargeObjectException e) {
raw = null ;
}
String type;
if (mode == org.eclipse.jgit.lib.FileMode.SYMLINK) {
type = X_GIT_SYMLINK;
} else {
type = registry.getMimeType(path, raw).toString();
type = resolveContentType(project, path, FileMode.FILE, type);
}
return asBinaryResult(raw, obj).setContentType(type).base64();
}
}
}
private static BinaryResult asBinaryResult (byte [] raw, ObjectLoader obj) {
if (raw != null ) {
return BinaryResult.create(raw);
}
BinaryResult result =
new BinaryResult() {
@Override
public void writeTo (OutputStream os) throws IOException {
obj.copyTo(os);
}
};
result.setContentLength(obj.getSize());
return result;
}
public BinaryResult downloadContent (
ProjectState project, ObjectId revstr, String path, @Nullable Integer parent)
throws ResourceNotFoundException, IOException {
try (Repository repo = openRepository(project);
RevWalk rw = new RevWalk(repo)) {
String suffix = "new" ;
RevCommit commit = rw.parseCommit(revstr);
if (parent != null && parent > 0 ) {
if (commit.getParentCount() == 1 ) {
suffix = "old" ;
} else {
suffix = "old" + parent;
}
commit = rw.parseCommit(commit.getParent(parent - 1 ));
}
try (TreeWalk tw = TreeWalk.forPath(rw.getObjectReader(), path, commit.getTree())) {
if (tw == null ) {
throw new ResourceNotFoundException();
}
int mode = tw.getFileMode(0 ).getObjectType();
if (mode != Constants.OBJ_BLOB) {
throw new ResourceNotFoundException();
}
ObjectId id = tw.getObjectId(0 );
ObjectLoader obj = repo.open(id, OBJ_BLOB);
byte [] raw;
try {
raw = obj.getCachedBytes(MAX_SIZE);
} catch (LargeObjectException e) {
raw = null ;
}
MimeType contentType = registry.getMimeType(path, raw);
return registry.isSafeInline(contentType)
? wrapBlob(path, obj, raw, contentType, suffix)
: zipBlob(path, obj, commit, suffix);
}
}
}
private BinaryResult wrapBlob (
String path,
final ObjectLoader obj,
byte [] raw,
MimeType contentType,
@Nullable String suffix) {
return asBinaryResult(raw, obj)
.setContentType(contentType.toString())
.setAttachmentName(safeFileName(path, suffix));
}
@SuppressWarnings ("resource" )
private BinaryResult zipBlob (
final String path, ObjectLoader obj, RevCommit commit, @Nullable final String suffix) {
final String commitName = commit.getName();
final long when = commit.getCommitTime() * 1000L ;
return new BinaryResult() {
@Override
public void writeTo (OutputStream os) throws IOException {
try (ZipOutputStream zipOut = new ZipOutputStream(os)) {
String decoration = randSuffix();
if (!Strings.isNullOrEmpty(suffix)) {
decoration = suffix + '-' + decoration;
}
ZipEntry e = new ZipEntry(safeFileName(path, decoration));
e.setComment(commitName + ":" + path);
e.setSize(obj.getSize());
e.setTime(when);
zipOut.putNextEntry(e);
obj.copyTo(zipOut);
zipOut.closeEntry();
}
}
}.setContentType(ZIP_TYPE).setAttachmentName(safeFileName(path, suffix) + ".zip" ).disableGzip();
}
private static String safeFileName (String fileName, @Nullable String suffix) {
int slash = fileName.lastIndexOf('/' );
if (slash >= 0 ) {
fileName = fileName.substring(slash + 1 );
}
StringBuilder r = new StringBuilder(fileName.length());
for (int i = 0 ; i < fileName.length(); i++) {
final char c = fileName.charAt(i);
if (c == '_' || c == '-' || c == '.' || c == '@' ) {
r.append(c);
} else if ('0' <= c && c <= '9' ) {
r.append(c);
} else if ('A' <= c && c <= 'Z' ) {
r.append(c);
} else if ('a' <= c && c <= 'z' ) {
r.append(c);
} else if (c == ' ' || c == '\n' || c == '\r' || c == '\t' ) {
r.append('-' );
} else {
r.append('_' );
}
}
fileName = r.toString();
int ext = fileName.lastIndexOf('.' );
if (suffix == null ) {
return fileName;
} else if (ext <= 0 ) {
return fileName + "_" + suffix;
} else {
return fileName.substring(0 , ext) + "_" + suffix + fileName.substring(ext);
}
}
private static String randSuffix () {
Hasher h = Hashing.murmur3_128().newHasher();
byte [] buf = new byte [8 ];
NB.encodeInt64(buf, 0 , TimeUtil.nowMs());
h.putBytes(buf);
rng.nextBytes(buf);
h.putBytes(buf);
return h.hash().toString();
}
public static String resolveContentType (
ProjectState project, String path, FileMode fileMode, String mimeType) {
switch (fileMode) {
case FILE:
if (Patch.COMMIT_MSG.equals(path)) {
return TEXT_X_GERRIT_COMMIT_MESSAGE;
}
if (Patch.MERGE_LIST.equals(path)) {
return TEXT_X_GERRIT_MERGE_LIST;
}
if (project != null ) {
for (ProjectState p : project.tree()) {
String t = p.getConfig().getMimeTypes().getMimeType(path);
if (t != null ) {
return t;
}
}
}
return mimeType;
case GITLINK:
return X_GIT_GITLINK;
case SYMLINK:
return X_GIT_SYMLINK;
default :
throw new IllegalStateException("file mode: " + fileMode);
}
}
private Repository openRepository (ProjectState project)
throws RepositoryNotFoundException, IOException {
return repoManager.openRepository(project.getNameKey());
}
}