Merge pull request #3599 from libgit2/gpgsign Introduce git_commit_extract_signature
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 34d29ed..a922774 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -264,6 +264,18 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field);
/**
+ * Extract the signature from a commit
+ *
+ * @param signature the signature block
+ * @param signed_data signed data; this is the commit contents minus the signature block
+ * @param repo the repository in which the commit exists
+ * @param commit_id the commit from which to extract the data
+ * @param field the name of the header field containing the signature
+ * block; pass `NULL` to extract the default 'gpgsig'
+ */
+GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);
+
+/**
* Create new commit in the repository from a list of `git_object` pointers
*
* The message will **not** be cleaned up automatically. You can do that
diff --git a/src/commit.c b/src/commit.c
index 87301e0..453506d 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -621,3 +621,89 @@ oom:
giterr_set_oom();
return -1;
}
+
+int git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field)
+{
+ git_odb_object *obj;
+ git_odb *odb;
+ const char *buf;
+ const char *h, *eol;
+ int error;
+
+ git_buf_sanitize(signature);
+ git_buf_sanitize(signed_data);
+
+ if (!field)
+ field = "gpgsig";
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ return error;
+
+ if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
+ return error;
+
+ buf = git_odb_object_data(obj);
+
+ while ((h = strchr(buf, '\n')) && h[1] != '\0' && h[1] != '\n') {
+ h++;
+ if (git__prefixcmp(buf, field)) {
+ if (git_buf_put(signed_data, buf, h - buf) < 0)
+ return -1;
+
+ buf = h;
+ continue;
+ }
+
+ h = buf;
+ h += strlen(field);
+ eol = strchr(h, '\n');
+ if (h[0] != ' ') {
+ buf = h;
+ continue;
+ }
+ if (!eol)
+ goto malformed;
+
+ h++; /* skip the SP */
+
+ git_buf_put(signature, h, eol - h);
+ if (git_buf_oom(signature))
+ goto oom;
+
+ /* If the next line starts with SP, it's multi-line, we must continue */
+ while (eol[1] == ' ') {
+ git_buf_putc(signature, '\n');
+ h = eol + 2;
+ eol = strchr(h, '\n');
+ if (!eol)
+ goto malformed;
+
+ git_buf_put(signature, h, eol - h);
+ }
+
+ if (git_buf_oom(signature))
+ goto oom;
+
+ git_odb_object_free(obj);
+ return git_buf_puts(signed_data, eol+1);
+ }
+
+ giterr_set(GITERR_INVALID, "this commit is not signed");
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+
+malformed:
+ giterr_set(GITERR_OBJECT, "malformed header");
+ error = -1;
+ goto cleanup;
+oom:
+ giterr_set_oom();
+ error = -1;
+ goto cleanup;
+
+cleanup:
+ git_odb_object_free(obj);
+ git_buf_clear(signature);
+ git_buf_clear(signed_data);
+ return error;
+}
diff --git a/tests/commit/parse.c b/tests/commit/parse.c
index 7c7264b..733fbae 100644
--- a/tests/commit/parse.c
+++ b/tests/commit/parse.c
@@ -460,3 +460,52 @@ cpxtDQQMGYFpXK/71stq\n\
git_buf_free(&buf);
git_commit__free(commit);
}
+
+void test_commit_parse__extract_signature(void)
+{
+ git_odb *odb;
+ git_oid commit_id;
+ git_buf signature = GIT_BUF_INIT, signed_data = GIT_BUF_INIT;
+ const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
+Version: GnuPG v1.4.12 (Darwin)\n\
+\n\
+iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+cpxtDQQMGYFpXK/71stq\n\
+=ozeK\n\
+-----END PGP SIGNATURE-----";
+
+ const char *data = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
+parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+\n\
+a simple commit which works\n";
+
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
+ cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJ_COMMIT));
+
+ cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+ cl_assert_equal_s(gpgsig, signature.ptr);
+ cl_assert_equal_s(data, signed_data.ptr);
+
+ git_buf_clear(&signature);
+ git_buf_clear(&signed_data);
+
+ cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, "gpgsig"));
+ cl_assert_equal_s(gpgsig, signature.ptr);
+ cl_assert_equal_s(data, signed_data.ptr);
+
+ git_buf_free(&signature);
+ git_buf_free(&signed_data);
+}