Download patch file with /patch?zip or /patch?download
For ?zip compress the patch file text inside of a ZIP archive. The inner file name is "commitsha1.diff". Modern UI shells on Windows, Mac OS X and Linux make it easy to unpack the compressed ZIP file to get access to the raw text. For ?download a filename is suggested in the Content-Dispostion response header, suggesting the browser to download the base64 encoded stream to the local drive as "commitsha1.diff.base64". Encoding the patch is necessary to prevent XSS attacks made against the Gerrit site. The ZIP wrapping does not allow an attacker to make a valid Java applet; the filename ending in ".diff" is not a valid Java class file name. The base64 wrapping can only be treated as plain text by a browser as it does not contain HTML special characters. Change-Id: Ia4c41e51c5f57607c45e2588629a88b47e1d9d09
This commit is contained in:
parent
888161bb20
commit
973f38bc4a
@ -1533,6 +1533,15 @@ The formatted patch is returned as text encoded inside base64:
|
||||
RnJvbSA3ZGFkY2MxNTNmZGVhMTdhYTg0ZmYzMmE2ZTI0NWRiYjY...
|
||||
----
|
||||
|
||||
Adding query parameter `zip` (for example `/changes/.../patch?zip`)
|
||||
returns the patch as a single file inside of a ZIP archive. Clients
|
||||
can expand the ZIP to obtain the plain text patch, avoiding the
|
||||
need for a base64 decoding step. This option implies `download`.
|
||||
|
||||
Query parameter `download` (e.g. `/changes/.../patch?download`)
|
||||
will suggest the browser save the patch as `commitsha1.diff.base64`,
|
||||
for later processing by command line tools.
|
||||
|
||||
[[get-mergeable]]
|
||||
Get Mergeable
|
||||
~~~~~~~~~~~~~
|
||||
|
@ -62,6 +62,7 @@ public abstract class BinaryResult implements Closeable {
|
||||
private long contentLength = -1;
|
||||
private boolean gzip = true;
|
||||
private boolean base64 = false;
|
||||
private String attachmentName;
|
||||
|
||||
/** @return the MIME type of the result, for HTTP clients. */
|
||||
public String getContentType() {
|
||||
@ -89,6 +90,17 @@ public abstract class BinaryResult implements Closeable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Get the attachment file name; null if not set. */
|
||||
public String getAttachmentName() {
|
||||
return attachmentName;
|
||||
}
|
||||
|
||||
/** Set the attachment file name and return {@code this}. */
|
||||
public BinaryResult setAttachmentName(String attachmentName) {
|
||||
this.attachmentName = attachmentName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @return length in bytes of the result; -1 if not known. */
|
||||
public long getContentLength() {
|
||||
return contentLength;
|
||||
|
@ -695,6 +695,11 @@ public class RestApiServlet extends HttpServlet {
|
||||
BinaryResult bin) throws IOException {
|
||||
final BinaryResult appResult = bin;
|
||||
try {
|
||||
if (bin.getAttachmentName() != null) {
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"" + bin.getAttachmentName() + "\"");
|
||||
}
|
||||
if (bin.isBase64()) {
|
||||
bin = stackBase64(res, bin);
|
||||
}
|
||||
|
@ -25,21 +25,31 @@ import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import org.eclipse.jgit.lib.AbbreviatedObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.PersonIdent;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.kohsuke.args4j.Option;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class GetPatch implements RestReadView<RevisionResource> {
|
||||
private final GitRepositoryManager repoManager;
|
||||
|
||||
@Option(name = "--zip")
|
||||
private boolean zip;
|
||||
|
||||
@Option(name = "--download")
|
||||
private boolean download;
|
||||
|
||||
@Inject
|
||||
GetPatch(GitRepositoryManager repoManager) {
|
||||
this.repoManager = repoManager;
|
||||
@ -71,6 +81,20 @@ public class GetPatch implements RestReadView<RevisionResource> {
|
||||
BinaryResult bin = new BinaryResult() {
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
if (zip) {
|
||||
ZipOutputStream zos = new ZipOutputStream(out);
|
||||
ZipEntry e = new ZipEntry(fileName(rw, commit));
|
||||
e.setTime(commit.getCommitTime() * 1000L);
|
||||
zos.putNextEntry(e);
|
||||
format(zos);
|
||||
zos.closeEntry();
|
||||
zos.finish();
|
||||
} else {
|
||||
format(out);
|
||||
}
|
||||
}
|
||||
|
||||
private void format(OutputStream out) throws IOException {
|
||||
out.write(formatEmailHeader(commit).getBytes(UTF_8));
|
||||
DiffFormatter fmt = new DiffFormatter(out);
|
||||
fmt.setRepository(repo);
|
||||
@ -83,8 +107,20 @@ public class GetPatch implements RestReadView<RevisionResource> {
|
||||
rw.release();
|
||||
repo.close();
|
||||
}
|
||||
}.setContentType("application/mbox")
|
||||
.base64();
|
||||
};
|
||||
|
||||
if (zip) {
|
||||
bin.disableGzip()
|
||||
.setContentType("application/zip")
|
||||
.setAttachmentName(fileName(rw, commit) + ".zip");
|
||||
} else {
|
||||
bin.base64()
|
||||
.setContentType("application/mbox")
|
||||
.setAttachmentName(download
|
||||
? fileName(rw, commit) + ".base64"
|
||||
: null);
|
||||
}
|
||||
|
||||
close = false;
|
||||
return bin;
|
||||
} finally {
|
||||
@ -132,4 +168,10 @@ public class GetPatch implements RestReadView<RevisionResource> {
|
||||
df.setCalendar(Calendar.getInstance(author.getTimeZone(), Locale.US));
|
||||
return df.format(author.getWhen());
|
||||
}
|
||||
|
||||
private static String fileName(RevWalk rw, RevCommit commit)
|
||||
throws IOException {
|
||||
AbbreviatedObjectId id = rw.getObjectReader().abbreviate(commit, 8);
|
||||
return id.name() + ".diff";
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user