diff options
4 files changed, 160 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java index d55fb4467b..b814984935 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java @@ -486,4 +486,36 @@ public class RevCommitParseTest extends RepositoryTestCase { private static ObjectId id(String str) { return ObjectId.fromString(str); } + + @Test + public void testParse_gpgSig() throws Exception { + String commit = "tree e3a1035abd2b319bb01e57d69b0ba6cab289297e\n" + + "parent 54e895b87c0768d2317a2b17062e3ad9f76a8105\n" + + "committer A U Thor <author@xample.com 1528968566 +0200\n" + + "gpgsig -----BEGIN PGP SIGNATURE-----\n" + + " \n" + + " wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n" + + " U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n" + + " znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n" + + " wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n" + + " SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n" + + " xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n" + + " =TClh\n" + + " -----END PGP SIGNATURE-----\n" + + "some other header\n\n" + + "commit message"; + + final RevCommit c; + c = new RevCommit(id("9473095c4cb2f12aefe1db8a355fe3fafba42f67")); + c.parseCanonical(new RevWalk(db), commit.getBytes(UTF_8)); + String gpgSig = new String(c.getRawGpgSignature(), UTF_8); + assertTrue(gpgSig.startsWith("-----BEGIN")); + assertTrue(gpgSig.endsWith("END PGP SIGNATURE-----")); + } + + @Test + public void testParse_NoGpgSig() throws Exception { + final RevCommit c = create("a message"); + assertNull(c.getRawGpgSignature()); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtilsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtilsTest.java index 7bd9adb90c..3aae59e908 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtilsTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/util/RawParseUtilsTest.java @@ -52,7 +52,24 @@ import java.nio.charset.UnsupportedCharsetException; import org.eclipse.jgit.lib.Constants; import org.junit.Test; +import static java.nio.charset.StandardCharsets.UTF_8; + public class RawParseUtilsTest { + String commit = "tree e3a1035abd2b319bb01e57d69b0ba6cab289297e\n" + + "parent 54e895b87c0768d2317a2b17062e3ad9f76a8105\n" + + "committer A U Thor <author@xample.com 1528968566 +0200\n" + + "gpgsig -----BEGIN PGP SIGNATURE-----\n" + + " \n" + + " wsBcBAABCAAQBQJbGB4pCRBK7hj4Ov3rIwAAdHIIAENrvz23867ZgqrmyPemBEZP\n" + + " U24B1Tlq/DWvce2buaxmbNQngKZ0pv2s8VMc11916WfTIC9EKvioatmpjduWvhqj\n" + + " znQTFyiMor30pyYsfrqFuQZvqBW01o8GEWqLg8zjf9Rf0R3LlOEw86aT8CdHRlm6\n" + + " wlb22xb8qoX4RB+LYfz7MhK5F+yLOPXZdJnAVbuyoMGRnDpwdzjL5Hj671+XJxN5\n" + + " SasRdhxkkfw/ZnHxaKEc4juMz8Nziz27elRwhOQqlTYoXNJnsV//wy5Losd7aKi1\n" + + " xXXyUpndEOmT0CIcKHrN/kbYoVL28OJaxoBuva3WYQaRrzEe3X02NMxZe9gkSqA=\n" + + " =TClh\n" + + " -----END PGP SIGNATURE-----\n" + + "some other header\n\n" + + "commit message"; @Test public void testParseEncoding_ISO8859_1_encoding() { @@ -79,4 +96,30 @@ public class RawParseUtilsTest { } } + @Test + public void testHeaderStart() { + byte[] headerName = "some".getBytes(UTF_8); + byte[] commitBytes = commit.getBytes(UTF_8); + assertEquals(625, RawParseUtils.headerStart(headerName, commitBytes, 0)); + assertEquals(625, RawParseUtils.headerStart(headerName, commitBytes, 4)); + + byte[] missingHeaderName = "missing".getBytes(UTF_8); + assertEquals(-1, RawParseUtils.headerStart(missingHeaderName, + commitBytes, 0)); + + byte[] fauxHeaderName = "other".getBytes(UTF_8); + assertEquals(-1, RawParseUtils.headerStart(fauxHeaderName, commitBytes, 625 + 4)); + } + + @Test + public void testHeaderEnd() { + byte[] commitBytes = commit.getBytes(UTF_8); + int[] expected = new int[] {45, 93, 148, 619, 637}; + int start = 0; + for (int i = 0; i < expected.length; i++) { + start = RawParseUtils.headerEnd(commitBytes, start); + assertEquals(expected[i], start); + start += 1; + } + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java index 5bd5dd6368..86ecd8eaee 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java @@ -51,6 +51,7 @@ import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -390,6 +391,34 @@ public class RevCommit extends RevObject { } /** + * Parse the gpg signature from the raw buffer. + * <p> + * This method parses and returns the raw content of the gpgsig lines. This + * method is fairly expensive and produces a new byte[] instance on each + * invocation. Callers should invoke this method only if they are certain + * they will need, and should cache the return value for as long as + * necessary to use all information from it. + * <p> + * RevFilter implementations should try to use + * {@link org.eclipse.jgit.util.RawParseUtils} to scan the + * {@link #getRawBuffer()} instead, as this will allow faster evaluation of + * commits. + * + * @return contents of the gpg signature; null if the commit was not signed. + * @since 5.1 + */ + public final @Nullable byte[] getRawGpgSignature() { + final byte[] raw = buffer; + final byte[] header = {'g', 'p', 'g', 's', 'i', 'g'}; + final int start = RawParseUtils.headerStart(header, raw, 0); + if (start < 0) { + return null; + } + final int end = RawParseUtils.headerEnd(raw, start); + return Arrays.copyOfRange(raw, start, end); + } + + /** * Parse the author identity from the raw buffer. * <p> * This method parses and returns the content of the author line, after diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java index c22c8de5d1..28f406a49e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java @@ -549,6 +549,62 @@ public final class RawParseUtils { } /** + * Locate the end of the header. Note that headers may be + * more than one line long. + * @param b + * buffer to scan. + * @param ptr + * position within buffer to start looking for the end-of-header. + * @return new position just after the header. This is either + * b.length, or the index of the header's terminating newline. + * @since 5.1 + */ + public static final int headerEnd(final byte[] b, int ptr) { + final int sz = b.length; + while (ptr < sz) { + final byte c = b[ptr++]; + if (c == '\n' && (ptr == sz || b[ptr] != ' ')) { + return ptr - 1; + } + } + return ptr - 1; + } + + /** + * Find the start of the contents of a given header. + * + * @param b + * buffer to scan. + * @param headerName + * header to search for + * @param ptr + * position within buffer to start looking for header at. + * @return new position at the start of the header's contents, -1 for + * not found + * @since 5.1 + */ + public static final int headerStart(byte[] headerName, byte[] b, int ptr) { + // Start by advancing to just past a LF or buffer start + if (ptr != 0) { + ptr = nextLF(b, ptr - 1); + } + while (ptr < b.length - (headerName.length + 1)) { + boolean found = true; + for (int i = 0; i < headerName.length; i++) { + if (headerName[i] != b[ptr++]) { + found = false; + break; + } + } + if (found && b[ptr++] == ' ') { + return ptr; + } + ptr = nextLF(b, ptr); + } + return -1; + } + + /** * Locate the first position before a given character. * * @param b |